From 9d30fd0dac941d268be9cd7f94b936d3ddbfbea9 Mon Sep 17 00:00:00 2001 From: YoVinchen Date: Thu, 16 Oct 2025 11:34:10 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E7=9B=B4=E6=8E=A5=E5=88=9B?= =?UTF-8?q?=E5=BB=BA=E4=BC=9A=E8=AF=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/commands/mod.rs | 1 + src-tauri/src/commands/smart_sessions.rs | 446 ++++++++++++++++++ src-tauri/src/main.rs | 12 + .../src/templates/smart_session_claude.md | 30 ++ src/App.tsx | 63 ++- src/components/ClaudeCodeSession.tsx | 26 +- src/components/TabContent.tsx | 21 + src/components/TabManager.tsx | 20 +- src/components/WelcomePage.tsx | 64 ++- src/lib/analytics/types.ts | 2 +- src/lib/api.ts | 148 ++++++ src/locales/en/common.json | 5 + src/locales/zh/common.json | 5 + 13 files changed, 818 insertions(+), 25 deletions(-) create mode 100644 src-tauri/src/commands/smart_sessions.rs create mode 100644 src-tauri/src/templates/smart_session_claude.md diff --git a/src-tauri/src/commands/mod.rs b/src-tauri/src/commands/mod.rs index 3d47866..a787985 100644 --- a/src-tauri/src/commands/mod.rs +++ b/src-tauri/src/commands/mod.rs @@ -16,3 +16,4 @@ pub mod git; pub mod terminal; pub mod ccr; pub mod system; +pub mod smart_sessions; diff --git a/src-tauri/src/commands/smart_sessions.rs b/src-tauri/src/commands/smart_sessions.rs new file mode 100644 index 0000000..7d41594 --- /dev/null +++ b/src-tauri/src/commands/smart_sessions.rs @@ -0,0 +1,446 @@ +use anyhow::{Context, Result}; +use serde::{Deserialize, Serialize}; +use std::fs; +use std::path::PathBuf; +use tauri::AppHandle; +use chrono::{DateTime, Utc}; +use uuid::Uuid; + +/// 智能会话结果 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SmartSessionResult { + /// 会话ID + pub session_id: String, + /// 项目路径 + pub project_path: String, + /// 显示名称 + pub display_name: String, + /// 创建时间 + pub created_at: DateTime, + /// 会话类型 + pub session_type: String, +} + +/// 智能会话配置 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SmartSessionConfig { + /// 是否启用智能会话 + pub enabled: bool, + /// 基础目录 + pub base_directory: PathBuf, + /// 命名模式 + pub naming_pattern: String, + /// 是否启用自动清理 + pub auto_cleanup_enabled: bool, + /// 自动清理天数 + pub auto_cleanup_days: u32, + /// 模板文件 + pub template_files: Vec, +} + +/// 模板文件定义 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct TemplateFile { + /// 文件路径 + pub path: String, + /// 文件内容 + pub content: String, + /// 是否可执行 + pub executable: bool, +} + +/// 智能会话记录 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct SmartSession { + /// 会话ID + pub id: String, + /// 显示名称 + pub display_name: String, + /// 项目路径 + pub project_path: String, + /// 创建时间 + pub created_at: DateTime, + /// 最后访问时间 + pub last_accessed: DateTime, + /// 会话类型 + pub session_type: String, +} + +impl Default for SmartSessionConfig { + fn default() -> Self { + let base_directory = dirs::home_dir() + .unwrap_or_default() + .join(".claudia") + .join("smart-sessions"); + + Self { + enabled: true, + base_directory, + naming_pattern: "chat-{timestamp}".to_string(), + auto_cleanup_enabled: true, + auto_cleanup_days: 30, + template_files: vec![ + TemplateFile { + path: "CLAUDE.md".to_string(), + content: include_str!("../templates/smart_session_claude.md").to_string(), + executable: false, + }, + TemplateFile { + path: "README.md".to_string(), + content: "# Smart Quick Start Session\n\nThis is an automatically created workspace by Claudia.\n\nCreated at: {created_at}\nSession ID: {session_id}\n".to_string(), + executable: false, + }, + TemplateFile { + path: ".gitignore".to_string(), + content: "# Claudia Smart Session\n*.log\n.DS_Store\n.env\nnode_modules/\n".to_string(), + executable: false, + }, + ], + } + } +} + +/// 获取智能会话配置文件路径 +fn get_config_path() -> Result { + let claudia_dir = dirs::home_dir() + .context("Failed to get home directory")? + .join(".claudia"); + + fs::create_dir_all(&claudia_dir) + .context("Failed to create .claudia directory")?; + + Ok(claudia_dir.join("smart_sessions_config.json")) +} + +/// 加载智能会话配置 +pub fn load_smart_session_config() -> Result { + let config_path = get_config_path()?; + + if !config_path.exists() { + let default_config = SmartSessionConfig::default(); + save_smart_session_config(&default_config)?; + return Ok(default_config); + } + + let config_content = fs::read_to_string(&config_path) + .context("Failed to read smart session config")?; + + let config: SmartSessionConfig = serde_json::from_str(&config_content) + .context("Failed to parse smart session config")?; + + Ok(config) +} + +/// 保存智能会话配置 +pub fn save_smart_session_config(config: &SmartSessionConfig) -> Result<()> { + let config_path = get_config_path()?; + + let config_content = serde_json::to_string_pretty(config) + .context("Failed to serialize smart session config")?; + + fs::write(&config_path, config_content) + .context("Failed to write smart session config")?; + + Ok(()) +} + +/// 生成智能会话路径 +pub fn generate_smart_session_path( + config: &SmartSessionConfig, + session_name: Option, +) -> Result { + let timestamp = chrono::Utc::now(); + + let session_name = session_name.unwrap_or_else(|| { + match config.naming_pattern.as_str() { + "chat-{timestamp}" => format!("chat-{}", timestamp.format("%Y-%m-%d-%H%M%S")), + "session-{date}" => format!("session-{}", timestamp.format("%Y-%m-%d")), + "conversation-{datetime}" => format!("conversation-{}", timestamp.format("%Y%m%d_%H%M%S")), + _ => format!("chat-{}", timestamp.format("%Y-%m-%d-%H%M%S")), + } + }); + + let session_path = config.base_directory.join(&session_name); + + // 确保路径唯一 + if session_path.exists() { + let uuid = Uuid::new_v4().to_string()[..8].to_string(); + let unique_name = format!("{}-{}", session_name, uuid); + Ok(config.base_directory.join(unique_name)) + } else { + Ok(session_path) + } +} + +/// 创建智能会话环境 +pub fn create_smart_session_environment(session_path: &PathBuf) -> Result<()> { + let config = load_smart_session_config()?; + + // 创建主目录 + fs::create_dir_all(session_path) + .context("Failed to create smart session directory")?; + + // 创建 .claude 子目录 + let claude_dir = session_path.join(".claude"); + fs::create_dir_all(&claude_dir) + .context("Failed to create .claude directory")?; + + // 创建基础 Claude 设置文件 + let claude_settings = serde_json::json!({ + "smart_session": true, + "created_by": "claudia", + "created_at": chrono::Utc::now().to_rfc3339(), + "session_path": session_path.to_string_lossy() + }); + + let settings_path = claude_dir.join("settings.json"); + fs::write(&settings_path, serde_json::to_string_pretty(&claude_settings)?) + .context("Failed to write Claude settings")?; + + // 创建模板文件 + let session_id = Uuid::new_v4().to_string(); + let created_at = chrono::Utc::now().to_rfc3339(); + + for template in &config.template_files { + let file_path = session_path.join(&template.path); + + // 创建父目录(如果需要) + if let Some(parent) = file_path.parent() { + fs::create_dir_all(parent) + .context("Failed to create template file parent directory")?; + } + + // 替换模板变量 + let content = template.content + .replace("{session_id}", &session_id) + .replace("{created_at}", &created_at) + .replace("{project_path}", &session_path.to_string_lossy()); + + fs::write(&file_path, content) + .context(format!("Failed to write template file: {}", template.path))?; + + // 设置可执行权限(如果需要) + #[cfg(unix)] + if template.executable { + use std::os::unix::fs::PermissionsExt; + let mut perms = fs::metadata(&file_path)?.permissions(); + perms.set_mode(0o755); + fs::set_permissions(&file_path, perms)?; + } + } + + log::info!("Created smart session environment at: {}", session_path.display()); + Ok(()) +} + +/// 获取智能会话历史文件路径 +fn get_sessions_history_path() -> Result { + let claudia_dir = dirs::home_dir() + .context("Failed to get home directory")? + .join(".claudia"); + + fs::create_dir_all(&claudia_dir) + .context("Failed to create .claudia directory")?; + + Ok(claudia_dir.join("smart_sessions_history.json")) +} + +/// 保存智能会话记录 +pub fn save_smart_session_record(session_path: &PathBuf) -> Result { + let session_id = Uuid::new_v4().to_string(); + let now = chrono::Utc::now(); + + let session = SmartSession { + id: session_id.clone(), + display_name: session_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("Unnamed Session") + .to_string(), + project_path: session_path.to_string_lossy().to_string(), + created_at: now, + last_accessed: now, + session_type: "smart".to_string(), + }; + + let history_path = get_sessions_history_path()?; + + let mut sessions: Vec = if history_path.exists() { + let content = fs::read_to_string(&history_path) + .context("Failed to read sessions history")?; + serde_json::from_str(&content).unwrap_or_default() + } else { + Vec::new() + }; + + sessions.push(session); + + let history_content = serde_json::to_string_pretty(&sessions) + .context("Failed to serialize sessions history")?; + + fs::write(&history_path, history_content) + .context("Failed to write sessions history")?; + + Ok(session_id) +} + +/// 列出所有智能会话 +pub fn list_smart_sessions() -> Result> { + let history_path = get_sessions_history_path()?; + + if !history_path.exists() { + return Ok(Vec::new()); + } + + let content = fs::read_to_string(&history_path) + .context("Failed to read sessions history")?; + + let sessions: Vec = serde_json::from_str(&content) + .context("Failed to parse sessions history")?; + + // 过滤仍然存在的会话 + let existing_sessions: Vec = sessions + .into_iter() + .filter(|session| { + let path = PathBuf::from(&session.project_path); + path.exists() + }) + .collect(); + + Ok(existing_sessions) +} + +/// 清理过期的智能会话 +pub fn cleanup_old_smart_sessions(days: u32) -> Result { + let config = load_smart_session_config()?; + if !config.auto_cleanup_enabled { + return Ok(0); + } + + let cutoff_time = chrono::Utc::now() - chrono::Duration::days(days as i64); + let sessions = list_smart_sessions()?; + let mut cleaned_count = 0u32; + + let mut remaining_sessions = Vec::new(); + + for session in sessions { + if session.last_accessed < cutoff_time { + // 删除会话目录 + let session_path = PathBuf::from(&session.project_path); + if session_path.exists() { + if let Err(e) = fs::remove_dir_all(&session_path) { + log::warn!("Failed to remove session directory {}: {}", session_path.display(), e); + } else { + cleaned_count += 1; + log::info!("Cleaned up expired session: {}", session.display_name); + } + } + } else { + remaining_sessions.push(session); + } + } + + // 更新历史记录 + if cleaned_count > 0 { + let history_path = get_sessions_history_path()?; + let history_content = serde_json::to_string_pretty(&remaining_sessions) + .context("Failed to serialize updated sessions history")?; + + fs::write(&history_path, history_content) + .context("Failed to write updated sessions history")?; + } + + Ok(cleaned_count) +} + +// Tauri 命令实现 + +/// 创建智能快速开始会话 +#[tauri::command] +pub async fn create_smart_quick_start_session( + _app: AppHandle, + session_name: Option, +) -> Result { + log::info!("Creating smart quick start session: {:?}", session_name); + + let config = load_smart_session_config() + .map_err(|e| format!("Failed to load config: {}", e))?; + + if !config.enabled { + return Err("Smart sessions are disabled".to_string()); + } + + // 1. 生成唯一的会话路径 + let session_path = generate_smart_session_path(&config, session_name) + .map_err(|e| format!("Failed to generate session path: {}", e))?; + + // 2. 创建目录结构和环境 + create_smart_session_environment(&session_path) + .map_err(|e| format!("Failed to create session environment: {}", e))?; + + // 3. 保存到历史记录 + let session_id = save_smart_session_record(&session_path) + .map_err(|e| format!("Failed to save session record: {}", e))?; + + let display_name = session_path + .file_name() + .and_then(|n| n.to_str()) + .unwrap_or("Smart Session") + .to_string(); + + let result = SmartSessionResult { + session_id, + project_path: session_path.to_string_lossy().to_string(), + display_name, + created_at: chrono::Utc::now(), + session_type: "smart".to_string(), + }; + + log::info!("Smart session created successfully: {}", result.project_path); + Ok(result) +} + +/// 获取智能会话配置 +#[tauri::command] +pub async fn get_smart_session_config() -> Result { + load_smart_session_config() + .map_err(|e| format!("Failed to load smart session config: {}", e)) +} + +/// 更新智能会话配置 +#[tauri::command] +pub async fn update_smart_session_config( + config: SmartSessionConfig, +) -> Result<(), String> { + save_smart_session_config(&config) + .map_err(|e| format!("Failed to save smart session config: {}", e)) +} + +/// 列出智能会话 +#[tauri::command] +pub async fn list_smart_sessions_command() -> Result, String> { + list_smart_sessions() + .map_err(|e| format!("Failed to list smart sessions: {}", e)) +} + +/// 切换智能会话模式 +#[tauri::command] +pub async fn toggle_smart_session_mode(enabled: bool) -> Result<(), String> { + let mut config = load_smart_session_config() + .map_err(|e| format!("Failed to load config: {}", e))?; + + config.enabled = enabled; + + save_smart_session_config(&config) + .map_err(|e| format!("Failed to save config: {}", e))?; + + log::info!("Smart session mode toggled: {}", enabled); + Ok(()) +} + +/// 清理过期智能会话 +#[tauri::command] +pub async fn cleanup_old_smart_sessions_command(days: u32) -> Result { + cleanup_old_smart_sessions(days) + .map_err(|e| format!("Failed to cleanup old sessions: {}", e)) +} \ No newline at end of file diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 10b27b5..d98f7e6 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -86,6 +86,10 @@ use commands::ccr::{ stop_ccr_service, restart_ccr_service, open_ccr_ui, get_ccr_config_path, }; use commands::system::flush_dns; +use commands::smart_sessions::{ + create_smart_quick_start_session, get_smart_session_config, update_smart_session_config, + list_smart_sessions_command, toggle_smart_session_mode, cleanup_old_smart_sessions_command, +}; use process::ProcessRegistryState; use file_watcher::FileWatcherState; use std::sync::Mutex; @@ -425,6 +429,14 @@ fn main() { storage_execute_sql, storage_reset_database, + // Smart Sessions Management + create_smart_quick_start_session, + get_smart_session_config, + update_smart_session_config, + list_smart_sessions_command, + toggle_smart_session_mode, + cleanup_old_smart_sessions_command, + // Slash Commands commands::slash_commands::slash_commands_list, commands::slash_commands::slash_command_get, diff --git a/src-tauri/src/templates/smart_session_claude.md b/src-tauri/src/templates/smart_session_claude.md new file mode 100644 index 0000000..0eeca4d --- /dev/null +++ b/src-tauri/src/templates/smart_session_claude.md @@ -0,0 +1,30 @@ +# Smart Session Claude Configuration + +This is an automatically created smart session workspace by Claudia. + +## Session Information + +- **Created**: {created_at} +- **Session ID**: {session_id} +- **Workspace Path**: {project_path} + +## Quick Start + +This workspace is pre-configured for immediate use with Claude Code. You can start coding right away! + +## Available Tools + +- File editing and creation +- Terminal access +- Git integration +- And all Claude Code features + +## Notes + +- This is a temporary workspace created for quick experimentation +- Files will be automatically cleaned up based on your Claudia settings +- You can convert this to a permanent project anytime + +--- + +Generated by [Claudia](https://github.com/yovinchen/claudia) - Claude Code GUI \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 8062918..8a18b37 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -64,7 +64,9 @@ function AppContent() { createUsageTab, createMCPTab, createChatTab, - canAddTab + canAddTab, + updateTab, + switchToTab } = useTabState(); const [projects, setProjects] = useState([]); const [selectedProject, setSelectedProject] = useState(null); @@ -273,6 +275,64 @@ function AppContent() { } }; + /** + * Creates a smart quick start session and opens it in a new tab + * @author yovinchen + */ + const handleSmartQuickStart = async () => { + try { + if (!canAddTab()) { + setToast({ message: t('maximumTabsReached'), type: "error" }); + return; + } + + // Create smart session + const smartSession = await api.createSmartQuickStartSession(); + + // Create a new tab for the smart session + const newTabId = createChatTab(); + + // 直接更新新建标签的会话上下文,避免依赖事件时序 + updateTab(newTabId, { + type: 'chat', + title: smartSession.display_name || 'Smart Session', + initialProjectPath: smartSession.project_path, + sessionData: null, + status: 'active' + }); + + // 切换到标签页视图并激活新标签 + if (view !== "tabs") { + setView("tabs"); + } + switchToTab(newTabId); + + // Show success message + setToast({ + message: t('smartSessionCreated', { + name: smartSession.display_name, + path: smartSession.project_path + }), + type: "success" + }); + + trackEvent.journeyMilestone({ + journey_stage: 'smart_session', + milestone_reached: 'smart_session_created', + time_to_milestone_ms: Date.now() - performance.timing.navigationStart + }); + + } catch (error) { + console.error('Failed to create smart session:', error); + setToast({ + message: t('failedToCreateSmartSession', { + error: error instanceof Error ? error.message : String(error) + }), + type: "error" + }); + } + }; + /** * Returns to project list view */ @@ -321,6 +381,7 @@ function AppContent() { ); diff --git a/src/components/ClaudeCodeSession.tsx b/src/components/ClaudeCodeSession.tsx index 62297c5..211604f 100644 --- a/src/components/ClaudeCodeSession.tsx +++ b/src/components/ClaudeCodeSession.tsx @@ -354,6 +354,14 @@ export const ClaudeCodeSession: React.FC = ({ queuedPromptsRef.current = queuedPrompts; }, [queuedPrompts]); + // 当父组件通过智能会话或外部导航注入项目路径时,确保初次渲染即可进入工作区而非停留在创建页 + useEffect(() => { + if (!session && initialProjectPath && projectPath.trim().length === 0) { + setProjectPath(initialProjectPath); + setError(null); + } + }, [initialProjectPath, projectPath, session]); + // Get effective session info (from prop or extracted) - use useMemo to ensure it updates const effectiveSession = useMemo(() => { if (session) return session; @@ -474,6 +482,22 @@ export const ClaudeCodeSession: React.FC = ({ } }, [projectPath, tabId, session, updateTab]); + // Auto-start file monitoring when project path is available + useEffect(() => { + if (projectPath && !isFileWatching) { + // Auto-start file monitoring for smart sessions or when project path is set + const timeoutId = setTimeout(async () => { + try { + await startFileWatching(); + } catch (error) { + console.warn('[ClaudeCodeSession] Failed to auto-start file monitoring:', error); + } + }, 500); // Small delay to ensure component is fully mounted + + return () => clearTimeout(timeoutId); + } + }, [projectPath, isFileWatching, startFileWatching]); + // 滚动到顶部 const scrollToTop = useCallback(() => { if (parentRef.current) { @@ -1527,7 +1551,7 @@ export const ClaudeCodeSession: React.FC = ({ ); - const projectPathInput = !session && ( + const projectPathInput = !session && !projectPath && ( = ({ tab, isActive }) => { case 'chat': return ( { } }; + const handleCreateSmartSessionTab = (event: CustomEvent) => { + const { tabId, sessionData } = event.detail; + console.log('[TabContent] Handling create-smart-session-tab:', { tabId, sessionData }); + + // Update the existing tab with smart session data and switch immediately + updateTab(tabId, { + type: 'chat', + title: sessionData.display_name || 'Smart Session', + initialProjectPath: sessionData.project_path, + sessionData: null, // No existing session, this is a new session workspace + }); + + // Force immediate tab switch without delay + setTimeout(() => { + window.dispatchEvent(new CustomEvent('switch-to-tab', { detail: { tabId } })); + }, 0); + }; + window.addEventListener('open-session-in-tab', handleOpenSessionInTab as EventListener); window.addEventListener('open-claude-file', handleOpenClaudeFile as EventListener); window.addEventListener('open-agent-execution', handleOpenAgentExecution as EventListener); @@ -399,6 +418,7 @@ export const TabContent: React.FC = () => { window.addEventListener('open-import-agent-tab', handleOpenImportAgentTab); window.addEventListener('close-tab', handleCloseTab as EventListener); window.addEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener); + window.addEventListener('create-smart-session-tab', handleCreateSmartSessionTab as EventListener); return () => { window.removeEventListener('open-session-in-tab', handleOpenSessionInTab as EventListener); window.removeEventListener('open-claude-file', handleOpenClaudeFile as EventListener); @@ -407,6 +427,7 @@ export const TabContent: React.FC = () => { window.removeEventListener('open-import-agent-tab', handleOpenImportAgentTab); window.removeEventListener('close-tab', handleCloseTab as EventListener); window.removeEventListener('claude-session-selected', handleClaudeSessionSelected as EventListener); + window.removeEventListener('create-smart-session-tab', handleCreateSmartSessionTab as EventListener); }; }, [createChatTab, findTabBySessionId, createClaudeFileTab, createAgentExecutionTab, createCreateAgentTab, createImportAgentTab, closeTab, updateTab]); diff --git a/src/components/TabManager.tsx b/src/components/TabManager.tsx index 073cc92..6949d54 100644 --- a/src/components/TabManager.tsx +++ b/src/components/TabManager.tsx @@ -221,16 +221,16 @@ export const TabManager: React.FC = ({ className }) => { if (session && canAddTab()) { // Create a new chat tab with the session data const tabId = createChatTab(); - // Update the tab with session data - setTimeout(() => { - updateTab(tabId, { - type: 'chat', - title: session.project_path.split('/').pop() || 'Session', - sessionId: session.id, - sessionData: session, - initialProjectPath: projectPath || session.project_path, - }); - }, 100); + // Update the tab with session data immediately + updateTab(tabId, { + type: 'chat', + title: session.project_path.split('/').pop() || 'Session', + sessionId: session.id, + sessionData: session, + initialProjectPath: projectPath || session.project_path, + }); + // Switch to the new tab immediately + switchToTab(tabId); } }; diff --git a/src/components/WelcomePage.tsx b/src/components/WelcomePage.tsx index c8578c1..2669cab 100644 --- a/src/components/WelcomePage.tsx +++ b/src/components/WelcomePage.tsx @@ -1,16 +1,19 @@ import { motion } from "framer-motion"; -import { Bot, FolderCode, BarChart, ServerCog, FileText, Settings, Network, Router } from "lucide-react"; +import { Bot, FolderCode, BarChart, ServerCog, FileText, Settings, Network, Router, Zap, FolderOpen, Loader2 } from "lucide-react"; import { useTranslation } from "@/hooks/useTranslation"; import { Button } from "@/components/ui/button"; import { ClaudiaLogoMinimal } from "@/components/ClaudiaLogo"; +import { useState } from "react"; interface WelcomePageProps { onNavigate: (view: string) => void; onNewSession: () => void; + onSmartQuickStart?: () => void; } -export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) { +export function WelcomePage({ onNavigate, onNewSession, onSmartQuickStart }: WelcomePageProps) { const { t } = useTranslation(); + const [isCreatingSmartSession, setIsCreatingSmartSession] = useState(false); const mainFeatures = [ { @@ -98,6 +101,17 @@ export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) { onNewSession(); }; + const handleSmartQuickStartClick = async () => { + if (!onSmartQuickStart) return; + + setIsCreatingSmartSession(true); + try { + await onSmartQuickStart(); + } finally { + setIsCreatingSmartSession(false); + } + }; + return (
@@ -191,7 +205,7 @@ export function WelcomePage({ onNavigate, onNewSession }: WelcomePageProps) { ))}
- {/* Quick Action Button */} + {/* Quick Action Buttons */} + {/* 智能快速开始 - 新功能 */} + {onSmartQuickStart && ( + + )} + + {/* 传统快速开始 - 保持原功能 */} diff --git a/src/lib/analytics/types.ts b/src/lib/analytics/types.ts index 90d5d74..3c61476 100644 --- a/src/lib/analytics/types.ts +++ b/src/lib/analytics/types.ts @@ -313,7 +313,7 @@ export interface MemoryWarningProperties { // User Journey properties export interface UserJourneyProperties { - journey_stage: 'onboarding' | 'first_chat' | 'first_agent' | 'power_user'; + journey_stage: 'onboarding' | 'first_chat' | 'first_agent' | 'power_user' | 'smart_session'; milestone_reached?: string; time_to_milestone_ms?: number; } diff --git a/src/lib/api.ts b/src/lib/api.ts index 82d7d07..ea07fa5 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -2741,6 +2741,96 @@ export const api = { console.error("Failed to update model mapping:", error); throw error; } + }, + + // ============= Smart Sessions Management ============= + + /** + * 创建智能快速开始会话 + * @author yovinchen + * @param sessionName - 可选的会话名称 + * @returns Promise resolving to smart session result + */ + async createSmartQuickStartSession(sessionName?: string): Promise { + try { + return await invoke("create_smart_quick_start_session", { sessionName }); + } catch (error) { + console.error("Failed to create smart quick start session:", error); + throw error; + } + }, + + /** + * 获取智能会话配置 + * @author yovinchen + * @returns Promise resolving to smart session configuration + */ + async getSmartSessionConfig(): Promise { + try { + return await invoke("get_smart_session_config"); + } catch (error) { + console.error("Failed to get smart session config:", error); + throw error; + } + }, + + /** + * 更新智能会话配置 + * @author yovinchen + * @param config - 智能会话配置 + * @returns Promise resolving when configuration is updated + */ + async updateSmartSessionConfig(config: SmartSessionConfig): Promise { + try { + await invoke("update_smart_session_config", { config }); + } catch (error) { + console.error("Failed to update smart session config:", error); + throw error; + } + }, + + /** + * 列出所有智能会话 + * @author yovinchen + * @returns Promise resolving to array of smart sessions + */ + async listSmartSessions(): Promise { + try { + return await invoke("list_smart_sessions_command"); + } catch (error) { + console.error("Failed to list smart sessions:", error); + throw error; + } + }, + + /** + * 切换智能会话模式 + * @author yovinchen + * @param enabled - 是否启用智能会话 + * @returns Promise resolving when mode is toggled + */ + async toggleSmartSessionMode(enabled: boolean): Promise { + try { + await invoke("toggle_smart_session_mode", { enabled }); + } catch (error) { + console.error("Failed to toggle smart session mode:", error); + throw error; + } + }, + + /** + * 清理过期的智能会话 + * @author yovinchen + * @param days - 清理多少天前的会话 + * @returns Promise resolving to number of cleaned sessions + */ + async cleanupOldSmartSessions(days: number): Promise { + try { + return await invoke("cleanup_old_smart_sessions_command", { days }); + } catch (error) { + console.error("Failed to cleanup old smart sessions:", error); + throw error; + } } }; @@ -2868,3 +2958,61 @@ export interface ModelMapping { model_name: string; updated_at: string; } + +// ============= Smart Sessions Types ============= + +/** 智能会话结果 */ +export interface SmartSessionResult { + /** 会话ID */ + session_id: string; + /** 项目路径 */ + project_path: string; + /** 显示名称 */ + display_name: string; + /** 创建时间 */ + created_at: string; + /** 会话类型 */ + session_type: string; +} + +/** 智能会话配置 */ +export interface SmartSessionConfig { + /** 是否启用智能会话 */ + enabled: boolean; + /** 基础目录 */ + base_directory: string; + /** 命名模式 */ + naming_pattern: string; + /** 是否启用自动清理 */ + auto_cleanup_enabled: boolean; + /** 自动清理天数 */ + auto_cleanup_days: number; + /** 模板文件 */ + template_files: TemplateFile[]; +} + +/** 模板文件定义 */ +export interface TemplateFile { + /** 文件路径 */ + path: string; + /** 文件内容 */ + content: string; + /** 是否可执行 */ + executable: boolean; +} + +/** 智能会话记录 */ +export interface SmartSession { + /** 会话ID */ + id: string; + /** 显示名称 */ + display_name: string; + /** 项目路径 */ + project_path: string; + /** 创建时间 */ + created_at: string; + /** 最后访问时间 */ + last_accessed: string; + /** 会话类型 */ + session_type: string; +} diff --git a/src/locales/en/common.json b/src/locales/en/common.json index 3cf473f..f2eea20 100644 --- a/src/locales/en/common.json +++ b/src/locales/en/common.json @@ -141,6 +141,9 @@ "settings": "Settings", "settingsDesc": "App settings and configuration", "quickStartSession": "Quick Start New Session", + "smartQuickStart": "Smart Quick Start", + "choosePathStart": "Choose Path & Start", + "creatingSmartSession": "Creating smart session...", "ccrRouter": "CCR Router", "ccrRouterDesc": "Claude Code Router configuration management" }, @@ -763,6 +766,8 @@ "claudeCodeNotFound": "Claude Code not found", "selectClaudeInstallation": "Select Claude Installation", "installClaudeCode": "Install Claude Code", + "smartSessionCreated": "Smart session '{{name}}' created at: {{path}}", + "failedToCreateSmartSession": "Failed to create smart session: {{error}}", "noTabsOpen": "No tabs open", "clickPlusToStartChat": "Click the + button to start a new chat", "noAgentRunIdSpecified": "No agent run ID specified", diff --git a/src/locales/zh/common.json b/src/locales/zh/common.json index 247ba31..502b6a8 100644 --- a/src/locales/zh/common.json +++ b/src/locales/zh/common.json @@ -136,6 +136,9 @@ "settings": "设置", "settingsDesc": "应用设置和配置", "quickStartSession": "快速开始新会话", + "smartQuickStart": "智能快速开始", + "choosePathStart": "选择路径开始", + "creatingSmartSession": "正在创建智能会话...", "ccrRouter": "CCR 路由", "ccrRouterDesc": "Claude Code Router 配置管理" }, @@ -706,6 +709,8 @@ "claudeCodeNotFound": "未找到 Claude Code", "selectClaudeInstallation": "选择 Claude 安装", "installClaudeCode": "安装 Claude Code", + "smartSessionCreated": "智能会话 '{{name}}' 已创建在:{{path}}", + "failedToCreateSmartSession": "创建智能会话失败:{{error}}", "session": "会话", "letClaudeDecide": "让 Claude 决定", "basicReasoning": "基础推理",