use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
use chrono::{DateTime, Utc};
use std::path::PathBuf;
use super::hardcode_config::HardcodeConfig;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FocusPoint {
pub id: String,
pub topic: String,
pub keywords: Vec<String>,
pub entities: Vec<String>,
pub core_question: Option<String>,
pub status: FocusStatus,
pub created_at: DateTime<Utc>,
pub last_active: DateTime<Utc>,
pub importance: f32,
pub message_range: MessageRange,
pub sub_foci: Vec<String>,
pub semantic_summary: Option<String>,
pub related_files: Vec<PathBuf>,
pub confidence: f32,
pub dynamic_switch_threshold: f32,
pub focus_type: FocusType,
pub feedback_history: Vec<FocusFeedback>,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum FocusType {
ProblemSolving,
TaskExecution,
KnowledgeExploration,
DecisionMaking,
CodeOptimization,
General,
}
impl Default for FocusType {
fn default() -> Self {
FocusType::General
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FocusFeedback {
pub timestamp: DateTime<Utc>,
pub feedback_type: FocusFeedbackType,
pub rating: Option<u8>,
pub message_index: usize,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum FocusFeedbackType {
Confirmed,
AutoSwitched,
Completed,
Rejected,
TooBroad,
TooNarrow,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq)]
pub enum FocusStatus {
Active, Suspended, Completed, Abandoned, }
impl std::fmt::Display for FocusStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
FocusStatus::Active => write!(f, "Active"),
FocusStatus::Suspended => write!(f, "Suspended"),
FocusStatus::Completed => write!(f, "Completed"),
FocusStatus::Abandoned => write!(f, "Abandoned"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct MessageRange {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FocusManager {
pub current_focus_id: Option<String>,
pub foci: HashMap<String, FocusPoint>,
pub focus_history: Vec<String>,
pub config: FocusConfig,
pub focus_stack: Vec<String>,
pub active_foci: HashSet<String>,
pub focus_graph: HashMap<String, Vec<(String, f32)>>,
pub focus_tree: HashMap<String, Vec<String>>,
pub relevance_cache: HashMap<String, HashMap<String, f32>>,
pub cache_capacity: usize,
pub hardcode_config: HardcodeConfig,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FocusConfig {
pub max_active_foci: usize,
pub switch_threshold: f32,
pub max_history_foci: usize,
pub auto_suspend_after: usize,
}
impl Default for FocusConfig {
fn default() -> Self {
Self {
max_active_foci: 3,
switch_threshold: 0.3,
max_history_foci: 10,
auto_suspend_after: 5,
}
}
}
impl FocusPoint {
pub fn new(
id: String,
topic: String,
keywords: Vec<String>,
entities: Vec<String>,
core_question: Option<String>,
message_index: usize,
) -> Self {
Self {
id,
topic,
keywords,
entities,
core_question,
status: FocusStatus::Active,
created_at: Utc::now(),
last_active: Utc::now(),
importance: 0.5,
message_range: MessageRange {
start: message_index,
end: message_index,
},
sub_foci: Vec::new(),
semantic_summary: None,
related_files: Vec::new(),
confidence: 0.5,
dynamic_switch_threshold: 0.3, focus_type: FocusType::default(),
feedback_history: Vec::new(),
}
}
pub fn new_with_ai(
id: String,
topic: String,
keywords: Vec<String>,
entities: Vec<String>,
core_question: Option<String>,
semantic_summary: Option<String>,
related_files: Vec<PathBuf>,
confidence: f32,
focus_type: FocusType,
message_index: usize,
) -> Self {
Self {
id,
topic,
keywords,
entities,
core_question,
status: FocusStatus::Active,
created_at: Utc::now(),
last_active: Utc::now(),
importance: confidence, message_range: MessageRange {
start: message_index,
end: message_index,
},
sub_foci: Vec::new(),
semantic_summary,
related_files,
confidence,
dynamic_switch_threshold: Self::calculate_initial_threshold(confidence, focus_type),
focus_type,
feedback_history: Vec::new(),
}
}
fn calculate_initial_threshold(confidence: f32, focus_type: FocusType) -> f32 {
let base_threshold = 0.3;
let confidence_modifier = confidence * 0.2;
let type_modifier = match focus_type {
FocusType::ProblemSolving => 0.15, FocusType::TaskExecution => 0.1, FocusType::DecisionMaking => 0.2, FocusType::CodeOptimization => 0.15,
FocusType::KnowledgeExploration => 0.0, FocusType::General => 0.0,
};
base_threshold + confidence_modifier + type_modifier
}
pub fn adjust_threshold_from_feedback(&mut self) {
if self.feedback_history.is_empty() {
return;
}
let recent_feedbacks = self.feedback_history.iter().rev().take(10);
let mut confirmed_count = 0;
let mut rejected_count = 0;
for feedback in recent_feedbacks {
match feedback.feedback_type {
FocusFeedbackType::Confirmed => confirmed_count += 1,
FocusFeedbackType::Rejected => rejected_count += 1,
FocusFeedbackType::TooBroad => self.dynamic_switch_threshold *= 0.9, FocusFeedbackType::TooNarrow => self.dynamic_switch_threshold *= 1.1, _ => {}
}
}
if confirmed_count > rejected_count + 3 {
self.dynamic_switch_threshold = (self.dynamic_switch_threshold * 1.15).min(0.6);
}
if rejected_count > confirmed_count + 2 {
self.dynamic_switch_threshold = (self.dynamic_switch_threshold * 0.85).max(0.1);
}
log::debug!(
"Focus '{}' threshold adjusted to {:.2} based on {} feedbacks",
self.topic,
self.dynamic_switch_threshold,
self.feedback_history.len()
);
}
pub fn add_feedback(&mut self, feedback_type: FocusFeedbackType, rating: Option<u8>, message_index: usize) {
self.feedback_history.push(FocusFeedback {
timestamp: Utc::now(),
feedback_type,
rating,
message_index,
});
self.adjust_threshold_from_feedback();
}
pub fn should_switch(&self, relevance_score: f32) -> bool {
relevance_score < self.dynamic_switch_threshold
}
pub fn with_importance(mut self, importance: f32) -> Self {
self.importance = importance;
self
}
pub fn with_confidence(mut self, confidence: f32) -> Self {
self.confidence = confidence;
self.dynamic_switch_threshold = Self::calculate_initial_threshold(confidence, self.focus_type);
self
}
pub fn with_type(mut self, focus_type: FocusType) -> Self {
self.focus_type = focus_type;
self.dynamic_switch_threshold = Self::calculate_initial_threshold(self.confidence, focus_type);
self
}
pub fn effective_importance(&self) -> f32 {
let elapsed_minutes = (Utc::now() - self.last_active).num_seconds() as f32 / 60.0;
if elapsed_minutes < 0.0 {
return self.importance;
}
let half_life = 30.0;
let decay_factor = (-elapsed_minutes / half_life).exp();
self.importance * decay_factor
}
pub fn effective_confidence(&self) -> f32 {
if self.feedback_history.is_empty() {
return self.confidence;
}
let recent_feedbacks = self.feedback_history.iter().rev().take(10);
let mut positive_count = 0;
let mut negative_count = 0;
for feedback in recent_feedbacks {
match feedback.feedback_type {
FocusFeedbackType::Confirmed | FocusFeedbackType::Completed => positive_count += 1,
FocusFeedbackType::Rejected => negative_count += 1,
_ => {}
}
}
let adjustment = (positive_count as f32 - negative_count as f32) * 0.05;
(self.confidence + adjustment).clamp(0.1, 1.0)
}
pub fn wake_up(&mut self) {
self.last_active = Utc::now();
self.importance = (self.importance + 0.1).min(1.0); }
pub fn update_message_range(&mut self, message_index: usize) {
self.message_range.end = message_index;
}
pub fn add_keywords(&mut self, new_keywords: &[String]) {
for kw in new_keywords {
if !self.keywords.contains(kw) {
self.keywords.push(kw.clone());
}
}
}
pub fn add_entities(&mut self, new_entities: &[String]) {
for entity in new_entities {
if !self.entities.contains(entity) {
self.entities.push(entity.clone());
}
}
}
}
impl FocusManager {
pub fn new() -> Self {
let hardcode_config = HardcodeConfig::default();
Self {
current_focus_id: None,
foci: HashMap::new(),
focus_history: Vec::new(),
config: FocusConfig::default(),
focus_stack: Vec::new(),
active_foci: HashSet::new(),
focus_graph: HashMap::new(),
focus_tree: HashMap::new(),
relevance_cache: HashMap::new(),
cache_capacity: hardcode_config.focus_cache_capacity,
hardcode_config,
}
}
pub fn current_focus(&self) -> Option<&FocusPoint> {
self.current_focus_id
.as_ref()
.and_then(|id| self.foci.get(id))
}
pub fn current_focus_mut(&mut self) -> Option<&mut FocusPoint> {
self.current_focus_id
.as_ref()
.and_then(|id| self.foci.get_mut(id))
}
pub fn add_focus(&mut self, focus: FocusPoint) {
if self.foci.len() >= self.config.max_active_foci {
self.suspend_oldest_focus();
}
let focus_id = focus.id.clone();
self.foci.insert(focus_id.clone(), focus);
self.current_focus_id = Some(focus_id.clone());
self.focus_history.push(focus_id.clone());
self.focus_stack.push(focus_id.clone());
self.active_foci.insert(focus_id.clone());
if self.focus_history.len() > self.config.max_history_foci {
let removed = self.focus_history.remove(0);
if let Some(f) = self.foci.get_mut(&removed) {
f.status = FocusStatus::Abandoned;
}
self.focus_stack.retain(|id| id != &removed);
self.active_foci.remove(&removed);
}
}
pub fn switch_focus(&mut self, focus_id: &str) -> Option<()> {
if !self.foci.contains_key(focus_id) {
return None;
}
if let Some(current_id) = self.current_focus_id.clone() {
self.add_focus_transition(¤t_id, focus_id, 1.0);
}
if let Some(current_id) = &self.current_focus_id {
if let Some(current) = self.foci.get_mut(current_id) {
current.status = FocusStatus::Suspended;
}
if let Some(pos) = self.focus_stack.iter().position(|id| id == current_id) {
if pos == self.focus_stack.len() - 1 {
self.focus_stack.pop();
}
}
self.active_foci.remove(current_id);
}
if let Some(new_focus) = self.foci.get_mut(focus_id) {
new_focus.status = FocusStatus::Active;
new_focus.last_active = Utc::now();
self.current_focus_id = Some(focus_id.to_string());
self.focus_stack.retain(|id| id != focus_id);
self.focus_stack.push(focus_id.to_string());
self.active_foci.insert(focus_id.to_string());
}
Some(())
}
fn suspend_oldest_focus(&mut self) {
let oldest_active = self.foci.iter()
.filter(|(_, f)| f.status == FocusStatus::Active)
.min_by_key(|(_, f)| f.last_active)
.map(|(id, _)| id.clone());
if let Some(id) = oldest_active {
if let Some(focus) = self.foci.get_mut(&id) {
focus.status = FocusStatus::Suspended;
}
}
}
pub fn calculate_relevance(&self, user_input: &str) -> HashMap<String, f32> {
let mut relevance = HashMap::new();
for (id, focus) in &self.foci {
if focus.status != FocusStatus::Abandoned {
let score = self.calculate_focus_relevance(user_input, focus);
relevance.insert(id.clone(), score);
}
}
relevance
}
fn calculate_focus_relevance(&self, input: &str, focus: &FocusPoint) -> f32 {
let input_lower = input.to_lowercase();
let mut score = 0.0;
let keyword_matches = focus.keywords.iter()
.filter(|kw| input_lower.contains(&kw.to_lowercase()))
.count();
score += keyword_matches as f32 * 0.2;
let entity_matches = focus.entities.iter()
.filter(|ent| input_lower.contains(&ent.to_lowercase()))
.count();
score += entity_matches as f32 * 0.3;
if let Some(question) = &focus.core_question {
if self.question_similar(input, question) {
score += 0.4;
}
}
score *= focus.importance;
let hours_since_active = (Utc::now() - focus.last_active).num_hours() as f32;
let time_decay = 1.0 / (1.0 + hours_since_active * 0.1);
score *= time_decay;
score.min(1.0)
}
fn question_similar(&self, input: &str, question: &str) -> bool {
let input_words = self.extract_words(input);
let question_words = self.extract_words(question);
let common = input_words.intersection(&question_words).count();
let total = question_words.len();
common as f32 / total as f32 > 0.5
}
fn extract_words(&self, text: &str) -> std::collections::HashSet<String> {
text.to_lowercase()
.split_whitespace()
.map(|s| s.to_string())
.collect()
}
pub fn should_create_new_focus(&self, user_input: &str) -> bool {
let relevance = self.calculate_relevance(user_input);
if relevance.is_empty() {
return true;
}
if let Some(current_focus) = self.current_focus() {
let current_relevance = relevance.get(¤t_focus.id).copied().unwrap_or(0.0);
if current_focus.should_switch(current_relevance) {
log::debug!(
"Focus '{}' relevance {:.2} below dynamic threshold {:.2}, suggesting new focus",
current_focus.topic,
current_relevance,
current_focus.dynamic_switch_threshold
);
return true;
}
}
false
}
pub fn get_most_relevant_focus(&self, user_input: &str) -> Option<String> {
let relevance = self.calculate_relevance(user_input);
relevance.iter()
.max_by(|(_, score_a), (_, score_b)| score_a.partial_cmp(score_b).unwrap_or(std::cmp::Ordering::Equal))
.map(|(id, _)| id.clone())
}
pub fn update_focus_range(&mut self, focus_id: &str, message_index: usize) {
if let Some(focus) = self.foci.get_mut(focus_id) {
focus.message_range.end = message_index;
focus.last_active = Utc::now();
}
}
pub fn complete_focus(&mut self, focus_id: &str) {
if let Some(focus) = self.foci.get_mut(focus_id) {
focus.status = FocusStatus::Completed;
}
if self.current_focus_id.as_ref() == Some(&focus_id.to_string()) {
self.current_focus_id = None;
}
}
pub fn create_focus_message(&self) -> Option<String> {
let current = self.current_focus()?;
let mut message = format!(
"🎯 **Current Focus: {}**\n\n",
current.topic
);
if let Some(question) = ¤t.core_question {
message.push_str(&format!("**Core Question:** {}\n\n", question));
}
if !current.keywords.is_empty() {
message.push_str(&format!(
"**Related Keywords:** {}\n\n",
current.keywords.join(", ")
));
}
if !current.entities.is_empty() {
message.push_str(&format!(
"**Related Entities:** {}\n\n",
current.entities.join(", ")
));
}
if self.focus_history.len() > 1 {
message.push_str("**Previous Focuses:**\n");
for (idx, focus_id) in self.focus_history.iter().rev().take(3).enumerate() {
if let Some(f) = self.foci.get(focus_id) {
if f.id != current.id {
message.push_str(&format!(
"{}. {} ({})\n",
idx + 1,
f.topic,
f.status
));
}
}
}
}
if self.focus_stack.len() > 1 {
message.push_str("\n**Active Focus Stack:**\n");
for (idx, focus_id) in self.focus_stack.iter().rev().enumerate() {
if let Some(f) = self.foci.get(focus_id) {
message.push_str(&format!(
"{}. {} (importance: {:.2})\n",
idx + 1,
f.topic,
f.effective_importance()
));
}
}
}
Some(message)
}
pub fn primary_focus(&self) -> Option<&FocusPoint> {
self.focus_stack.last()
.and_then(|id| self.foci.get(id))
}
pub fn get_active_foci(&self) -> Vec<&FocusPoint> {
self.active_foci.iter()
.filter_map(|id| self.foci.get(id))
.collect()
}
pub fn switch_to_previous_focus(&mut self) -> Option<()> {
if self.focus_stack.len() < 2 {
return None;
}
let current_id = self.focus_stack.pop()?;
let previous_id = self.focus_stack.last()?.clone();
self.switch_focus(&previous_id)?;
self.focus_stack.insert(self.focus_stack.len() - 1, current_id);
Some(())
}
pub fn activate_multiple_foci(&mut self, focus_ids: &[String]) {
for id in focus_ids {
if self.foci.contains_key(id) {
self.active_foci.insert(id.clone());
if let Some(focus) = self.foci.get_mut(id) {
focus.status = FocusStatus::Active;
}
}
}
}
pub fn add_focus_transition(&mut self, from_id: &str, to_id: &str, strength: f32) {
self.focus_graph
.entry(from_id.to_string())
.or_default()
.push((to_id.to_string(), strength));
if let Some(transitions) = self.focus_graph.get_mut(from_id) {
for (id, s) in transitions.iter_mut() {
if id == to_id {
*s = (*s + strength).min(1.0);
return;
}
}
}
}
pub fn wake_related_foci(&mut self, focus_id: &str, min_strength: f32) {
if let Some(related) = self.focus_graph.get(focus_id) {
for (related_id, strength) in related {
if *strength >= min_strength {
if let Some(focus) = self.foci.get_mut(related_id) {
focus.wake_up();
focus.status = FocusStatus::Active;
self.active_foci.insert(related_id.clone());
}
}
}
}
}
pub fn get_related_foci(&self, focus_id: &str) -> Vec<(String, f32)> {
self.focus_graph.get(focus_id)
.map(|relations| relations.clone())
.unwrap_or_default()
}
pub fn split_focus(&mut self, parent_id: &str, child: FocusPoint) {
let child_id = child.id.clone();
self.foci.insert(child_id.clone(), child);
self.focus_tree
.entry(parent_id.to_string())
.or_default()
.push(child_id.clone());
if let Some(parent) = self.foci.get_mut(parent_id) {
parent.sub_foci.push(child_id.clone());
}
self.active_foci.insert(child_id);
}
pub fn merge_focus_to_parent(&mut self, parent_id: &str, child_id: &str) {
if let (Some(parent), Some(child)) =
(self.foci.get(parent_id), self.foci.get(child_id).cloned())
{
let mut parent = parent.clone();
parent.add_keywords(&child.keywords);
parent.add_entities(&child.entities);
parent.importance = (parent.importance + 0.1).min(1.0);
if let Some(child_focus) = self.foci.get_mut(child_id) {
child_focus.status = FocusStatus::Completed;
}
if let Some(children) = self.focus_tree.get_mut(parent_id) {
children.retain(|id| id != child_id);
}
self.foci.insert(parent_id.to_string(), parent);
}
}
pub fn get_focus_tree_depth(&self, focus_id: &str) -> usize {
let empty_children = Vec::new();
let children = self.focus_tree.get(focus_id).unwrap_or(&empty_children);
if children.is_empty() {
return 1;
}
1 + children.iter()
.map(|child_id| self.get_focus_tree_depth(child_id))
.max()
.unwrap_or(0)
}
pub fn record_negative_feedback(&mut self, focus_id: &str, message_index: usize) {
if let Some(focus) = self.foci.get_mut(focus_id) {
focus.add_feedback(FocusFeedbackType::Rejected, None, message_index);
focus.confidence *= 0.8;
focus.dynamic_switch_threshold = FocusPoint::calculate_initial_threshold(
focus.confidence,
focus.focus_type
);
log::info!(
"Negative feedback recorded for focus '{}', confidence reduced to {:.2}",
focus.topic,
focus.confidence
);
}
}
pub fn record_positive_feedback(&mut self, focus_id: &str, message_index: usize) {
if let Some(focus) = self.foci.get_mut(focus_id) {
focus.add_feedback(FocusFeedbackType::Confirmed, None, message_index);
focus.confidence = (focus.confidence + 0.1).min(1.0);
focus.dynamic_switch_threshold = FocusPoint::calculate_initial_threshold(
focus.confidence,
focus.focus_type
);
}
}
pub fn predict_next_focus(&self) -> Option<String> {
if let Some(current_id) = &self.current_focus_id {
if let Some(transitions) = self.focus_graph.get(current_id) {
return transitions.iter()
.max_by(|a, b| a.1.partial_cmp(&b.1).unwrap_or(std::cmp::Ordering::Equal))
.map(|(id, _)| id.clone());
}
}
None
}
pub fn predict_by_keywords(&self, keywords: &[String]) -> Option<String> {
let mut best_match: Option<(String, f32)> = None;
for (focus_id, focus) in &self.foci {
if focus.status == FocusStatus::Active {
let overlap = keywords.iter()
.filter(|kw| focus.keywords.contains(kw))
.count() as f32;
let score = overlap / focus.keywords.len() as f32;
if score > 0.3 {
if best_match.is_none() || score > best_match.as_ref().unwrap().1 {
best_match = Some((focus_id.clone(), score));
}
}
}
}
best_match.map(|(id, _)| id)
}
fn compute_input_hash(&self, input: &str) -> String {
input.chars().take(50).collect()
}
pub fn get_cached_relevance(&mut self, user_input: &str) -> Option<HashMap<String, f32>> {
let hash = self.compute_input_hash(user_input);
if let Some(cached) = self.relevance_cache.get(&hash) {
return Some(cached.clone());
}
None
}
pub fn cache_relevance(&mut self, user_input: &str, relevance: HashMap<String, f32>) {
let hash = self.compute_input_hash(user_input);
if self.relevance_cache.len() >= self.cache_capacity {
let keys_to_remove = self.relevance_cache.keys()
.take(self.cache_capacity / 2)
.cloned()
.collect::<Vec<_>>();
for key in keys_to_remove {
self.relevance_cache.remove(&key);
}
}
self.relevance_cache.insert(hash, relevance);
}
pub fn update_active_relevance(&mut self, user_input: &str) -> HashMap<String, f32> {
let mut relevance = HashMap::new();
if let Some(cached) = self.get_cached_relevance(user_input) {
return cached;
}
for focus_id in &self.active_foci {
if let Some(focus) = self.foci.get(focus_id) {
let score = self.calculate_focus_relevance(user_input, focus);
relevance.insert(focus_id.clone(), score);
}
}
self.cache_relevance(user_input, relevance.clone());
relevance
}
pub fn calculate_relevance_with_decay(&self, user_input: &str) -> HashMap<String, f32> {
let mut relevance = HashMap::new();
for (id, focus) in &self.foci {
if focus.status != FocusStatus::Abandoned {
let effective_importance = focus.effective_importance();
let score = self.calculate_focus_relevance(user_input, focus) * effective_importance;
relevance.insert(id.clone(), score);
}
}
relevance
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_focus_manager() {
let mut manager = FocusManager::new();
let focus = FocusPoint::new(
"focus-1".to_string(),
"Optimizing Rust performance".to_string(),
vec!["performance".to_string(), "rust".to_string()],
vec!["main.rs".to_string()],
Some("How to reduce memory usage?".to_string()),
0,
).with_importance(0.8);
manager.add_focus(focus);
assert!(manager.current_focus().is_some());
assert_eq!(manager.current_focus().unwrap().topic, "Optimizing Rust performance");
}
#[test]
fn test_relevance_calculation() {
let mut manager = FocusManager::new();
let focus = FocusPoint::new(
"focus-1".to_string(),
"Database optimization".to_string(),
vec!["database".to_string(), "sql".to_string()],
vec!["db.rs".to_string()],
Some("Why is query slow?".to_string()),
0,
).with_importance(0.8);
manager.add_focus(focus);
let relevance = manager.calculate_relevance("The database query is still slow with SQL");
assert!(relevance["focus-1"] >= 0.15);
let relevance = manager.calculate_relevance("Let's talk about UI design");
assert!(relevance["focus-1"] < 0.1);
}
#[test]
fn test_should_create_new_focus() {
let mut manager = FocusManager::new();
assert!(manager.should_create_new_focus("any input"));
let focus = FocusPoint::new(
"focus-1".to_string(),
"API design".to_string(),
vec!["api".to_string()],
vec![],
None,
0,
).with_importance(0.5);
manager.add_focus(focus);
let relevance = manager.calculate_relevance("How to improve API response time?");
println!("API relevance: {}", relevance["focus-1"]);
assert!(manager.should_create_new_focus("I want to change the color scheme"));
}
}