├── test_models.md ├── claudeCli ├── test_models.md ├── src-tauri │ ├── .mcp.json │ ├── build.rs │ ├── src │ │ ├── process │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── commands │ │ │ └── mod.rs │ ├── icons │ │ ├── 32x32.png │ │ ├── icon.icns │ │ ├── icon.ico │ │ ├── icon.png │ │ ├── 128x128.png │ │ └── 128x128@2x.png │ ├── .gitignore │ ├── Cargo.production.toml │ ├── capabilities │ │ └── default.json │ ├── Info.plist │ ├── entitlements.plist │ ├── tauri.conf.debug.json │ ├── Cargo.toml │ ├── tauri.conf.json │ └── tauri.conf.release.json ├── src │ ├── vite-env.d.ts │ ├── assets │ │ └── nfo │ │ │ ├── claudia-nfo.ogg │ │ │ └── asterisk-logo.png │ ├── constants │ │ └── agentIcons.ts │ ├── components │ │ ├── ToolWidgets.new.tsx │ │ ├── __tests__ │ │ │ ├── Button.test.tsx │ │ │ ├── TabManager.test.tsx │ │ │ └── ProjectList.test.tsx │ │ ├── ui │ │ │ ├── label.tsx │ │ │ ├── scroll-area.tsx │ │ │ ├── pagination.tsx │ │ │ ├── textarea.tsx │ │ │ ├── badge.tsx │ │ │ ├── tooltip.tsx │ │ │ ├── input.tsx │ │ │ └── radio-group.tsx │ │ ├── index.ts │ │ ├── widgets │ │ │ └── index.ts │ │ ├── TokenCounter.tsx │ │ ├── I18nProvider.tsx │ │ ├── AnalyticsErrorBoundary.tsx │ │ └── LanguageSelector.tsx │ ├── contexts │ │ ├── toastUtils.ts │ │ ├── tabUtils.ts │ │ ├── contextUtils.ts │ │ ├── contexts.ts │ │ └── hooks.ts │ ├── test │ │ └── setup.ts │ ├── lib │ │ ├── utils.ts │ │ ├── __tests__ │ │ │ ├── utils.test.ts │ │ │ └── api.test.ts │ │ ├── outputCacheHook.ts │ │ └── outputCacheUtils.ts │ ├── hooks │ │ ├── useTheme.ts │ │ ├── useMessageDisplayMode.ts │ │ ├── index.ts │ │ ├── useLoadingState.ts │ │ └── useDebounce.ts │ ├── main.tsx │ └── locales │ │ └── index.ts ├── .env.production ├── .cargo │ └── config.toml ├── .env.development ├── tsconfig.node.json ├── .editorconfig ├── index.html ├── vitest.config.ts ├── .gitignore ├── .prettierignore ├── .prettierrc ├── tsconfig.json ├── scripts │ └── bump-version.sh ├── .qoder │ └── rules │ │ └── rules.md ├── vite.config.ts ├── vite.config.production.ts └── cc_agents │ └── git-commit-bot.claudia.json ├── src-tauri ├── .mcp.json ├── build.rs ├── src │ ├── process │ │ └── mod.rs │ ├── lib.rs │ └── commands │ │ └── mod.rs ├── icons │ ├── 32x32.png │ ├── 64x64.png │ ├── icon.icns │ ├── icon.ico │ ├── icon.png │ ├── 128x128.png │ ├── 128x128@2x.png │ ├── StoreLogo.png │ ├── Square30x30Logo.png │ ├── Square44x44Logo.png │ ├── Square71x71Logo.png │ ├── Square89x89Logo.png │ ├── Square107x107Logo.png │ ├── Square142x142Logo.png │ ├── Square150x150Logo.png │ ├── Square284x284Logo.png │ ├── Square310x310Logo.png │ ├── ios │ │ ├── AppIcon-20x20@1x.png │ │ ├── AppIcon-20x20@2x.png │ │ ├── AppIcon-20x20@3x.png │ │ ├── AppIcon-29x29@1x.png │ │ ├── AppIcon-29x29@2x.png │ │ ├── AppIcon-29x29@3x.png │ │ ├── AppIcon-40x40@1x.png │ │ ├── AppIcon-40x40@2x.png │ │ ├── AppIcon-40x40@3x.png │ │ ├── AppIcon-512@2x.png │ │ ├── AppIcon-60x60@2x.png │ │ ├── AppIcon-60x60@3x.png │ │ ├── AppIcon-76x76@1x.png │ │ ├── AppIcon-76x76@2x.png │ │ ├── AppIcon-20x20@2x-1.png │ │ ├── AppIcon-29x29@2x-1.png │ │ ├── AppIcon-40x40@2x-1.png │ │ └── AppIcon-83.5x83.5@2x.png │ └── android │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png │ │ └── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_round.png │ │ └── ic_launcher_foreground.png ├── .gitignore ├── Cargo.production.toml ├── capabilities │ └── default.json ├── Info.plist ├── entitlements.plist ├── tauri.conf.debug.json ├── Cargo.toml └── tauri.conf.json ├── src ├── vite-env.d.ts ├── assets │ └── nfo │ │ ├── claudia-nfo.ogg │ │ └── asterisk-logo.png ├── constants │ └── agentIcons.ts ├── components │ ├── AnalyticsConsent.tsx │ ├── ToolWidgets.new.tsx │ ├── __tests__ │ │ ├── Button.test.tsx │ │ ├── TabManager.test.tsx │ │ └── ProjectList.test.tsx │ ├── ui │ │ ├── label.tsx │ │ ├── scroll-area.tsx │ │ ├── pagination.tsx │ │ ├── textarea.tsx │ │ ├── badge.tsx │ │ ├── tooltip.tsx │ │ ├── input.tsx │ │ └── radio-group.tsx │ ├── index.ts │ ├── widgets │ │ └── index.ts │ ├── TokenCounter.tsx │ ├── I18nProvider.tsx │ ├── AnalyticsErrorBoundary.tsx │ └── LanguageSelector.tsx ├── contexts │ ├── toastUtils.ts │ ├── tabUtils.ts │ ├── contextUtils.ts │ ├── hooks.ts │ └── contexts.ts ├── test │ └── setup.ts ├── hooks │ ├── useTheme.ts │ ├── useMessageDisplayMode.ts │ ├── index.ts │ ├── useLoadingState.ts │ ├── useConfigMonitor.ts │ └── useDebounce.ts ├── lib │ ├── __tests__ │ │ ├── utils.test.ts │ │ └── api.test.ts │ ├── outputCacheHook.ts │ ├── utils.ts │ └── outputCacheUtils.ts ├── main.tsx └── locales │ └── index.ts ├── app-icon.png ├── .env.production ├── .cargo └── config.toml ├── .env.development ├── tsconfig.node.json ├── .editorconfig ├── index.html ├── vitest.config.ts ├── .gitignore ├── .prettierignore ├── .prettierrc ├── tsconfig.json ├── scripts └── bump-version.sh ├── .qoder └── rules │ └── rules.md ├── vite.config.ts ├── vite.config.production.ts └── cc_agents └── git-commit-bot.claudia.json /test_models.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /claudeCli/test_models.md: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src-tauri/.mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": {} 3 | } -------------------------------------------------------------------------------- /claudeCli/src-tauri/.mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": {} 3 | } -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /claudeCli/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/app-icon.png -------------------------------------------------------------------------------- /claudeCli/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /src-tauri/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod registry; 2 | 3 | pub use registry::*; 4 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/src/process/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod registry; 2 | 3 | pub use registry::*; 4 | -------------------------------------------------------------------------------- /src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /src-tauri/icons/64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/64x64.png -------------------------------------------------------------------------------- /src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /src/assets/nfo/claudia-nfo.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src/assets/nfo/claudia-nfo.ogg -------------------------------------------------------------------------------- /src/assets/nfo/asterisk-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src/assets/nfo/asterisk-logo.png -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # 生产环境配置 2 | VITE_LOG_LEVEL=WARN 3 | VITE_ENABLE_CONSOLE_LOGS=false 4 | VITE_ENABLE_PERFORMANCE_LOGS=false -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /claudeCli/.env.production: -------------------------------------------------------------------------------- 1 | # 生产环境配置 2 | VITE_LOG_LEVEL=WARN 3 | VITE_ENABLE_CONSOLE_LOGS=false 4 | VITE_ENABLE_PERFORMANCE_LOGS=false -------------------------------------------------------------------------------- /claudeCli/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /claudeCli/src/assets/nfo/claudia-nfo.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src/assets/nfo/claudia-nfo.ogg -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-512@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /claudeCli/src/assets/nfo/asterisk-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/claudeCli/src/assets/nfo/asterisk-logo.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-gnu] 2 | linker = "aarch64-linux-gnu-gcc" 3 | 4 | [env] 5 | PKG_CONFIG_ALLOW_CROSS = "1" 6 | -------------------------------------------------------------------------------- /src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # 开发环境配置 2 | NODE_ENV=development 3 | VITE_LOG_LEVEL=DEBUG 4 | VITE_ENABLE_CONSOLE_LOGS=true 5 | VITE_ENABLE_PERFORMANCE_LOGS=true -------------------------------------------------------------------------------- /claudeCli/.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [target.aarch64-unknown-linux-gnu] 2 | linker = "aarch64-linux-gnu-gcc" 3 | 4 | [env] 5 | PKG_CONFIG_ALLOW_CROSS = "1" 6 | -------------------------------------------------------------------------------- /claudeCli/.env.development: -------------------------------------------------------------------------------- 1 | # 开发环境配置 2 | NODE_ENV=development 3 | VITE_LOG_LEVEL=DEBUG 4 | VITE_ENABLE_CONSOLE_LOGS=true 5 | VITE_ENABLE_PERFORMANCE_LOGS=true -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xuemk/termiClaude/HEAD/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /claudeCli/tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "bundler", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": ["vite.config.ts"] 10 | } 11 | -------------------------------------------------------------------------------- /src/constants/agentIcons.ts: -------------------------------------------------------------------------------- 1 | import { ICON_MAP } from "@/constants/iconConstants"; 2 | 3 | /** 4 | * Available icons for agents 5 | * 6 | * This constant is exported separately to avoid fast refresh warnings 7 | * when importing in React components. 8 | */ 9 | export const AGENT_ICONS = ICON_MAP; 10 | -------------------------------------------------------------------------------- /claudeCli/src/constants/agentIcons.ts: -------------------------------------------------------------------------------- 1 | import { ICON_MAP } from "@/constants/iconConstants"; 2 | 3 | /** 4 | * Available icons for agents 5 | * 6 | * This constant is exported separately to avoid fast refresh warnings 7 | * when importing in React components. 8 | */ 9 | export const AGENT_ICONS = ICON_MAP; 10 | -------------------------------------------------------------------------------- /src/components/AnalyticsConsent.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | interface AnalyticsConsentProps { 4 | open?: boolean; 5 | onOpenChange?: (open: boolean) => void; 6 | onComplete?: () => void; 7 | } 8 | 9 | export const AnalyticsConsent: React.FC = () => { 10 | return null; 11 | }; -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [*.rs] 18 | indent_size = 4 19 | 20 | [Makefile] 21 | indent_style = tab 22 | 23 | [*.{json,jsonc}] 24 | indent_size = 2 25 | 26 | [*.toml] 27 | indent_size = 2 -------------------------------------------------------------------------------- /claudeCli/.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | trim_trailing_whitespace = false 13 | 14 | [*.{yml,yaml}] 15 | indent_size = 2 16 | 17 | [*.rs] 18 | indent_size = 4 19 | 20 | [Makefile] 21 | indent_style = tab 22 | 23 | [*.{json,jsonc}] 24 | indent_size = 2 25 | 26 | [*.toml] 27 | indent_size = 2 -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | TermiClaude - Claude Code Session Browser 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /claudeCli/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Claudia - Claude Code Session Browser 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import react from "@vitejs/plugin-react"; 3 | import { fileURLToPath, URL } from "node:url"; 4 | 5 | export default defineConfig({ 6 | plugins: [react()], 7 | test: { 8 | environment: "jsdom", 9 | setupFiles: ["./src/test/setup.ts"], 10 | globals: true, 11 | include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], 12 | }, 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /claudeCli/vitest.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vitest/config"; 2 | import react from "@vitejs/plugin-react"; 3 | import { fileURLToPath, URL } from "node:url"; 4 | 5 | export default defineConfig({ 6 | plugins: [react()], 7 | test: { 8 | environment: "jsdom", 9 | setupFiles: ["./src/test/setup.ts"], 10 | globals: true, 11 | include: ["src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], 12 | }, 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | }); 19 | -------------------------------------------------------------------------------- /src/components/ToolWidgets.new.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Tool widgets re-export module 3 | * 4 | * This file re-exports all widgets from the widgets directory to maintain 5 | * backward compatibility with the original ToolWidgets.tsx structure. 6 | * Provides a clean import path for all tool-specific widget components. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { BashWidget, LSWidget, TodoWidget } from '@/components/ToolWidgets.new'; 11 | * ``` 12 | */ 13 | 14 | // eslint-disable-next-line react-refresh/only-export-components 15 | export * from "./widgets"; 16 | -------------------------------------------------------------------------------- /claudeCli/src/components/ToolWidgets.new.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Tool widgets re-export module 3 | * 4 | * This file re-exports all widgets from the widgets directory to maintain 5 | * backward compatibility with the original ToolWidgets.tsx structure. 6 | * Provides a clean import path for all tool-specific widget components. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { BashWidget, LSWidget, TodoWidget } from '@/components/ToolWidgets.new'; 11 | * ``` 12 | */ 13 | 14 | // eslint-disable-next-line react-refresh/only-export-components 15 | export * from "./widgets"; 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | *.bun-build 15 | 16 | # Tauri binaries (built executables) 17 | src-tauri/binaries/ 18 | 19 | # Editor directories and files 20 | .vscode/* 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | temp_lib/ 30 | 31 | .cursor/ 32 | AGENTS.md 33 | CLAUDE.md 34 | *_TASK.md 35 | 36 | # Claude project-specific files 37 | .claude/ 38 | 39 | .env -------------------------------------------------------------------------------- /claudeCli/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | *.bun-build 15 | 16 | # Tauri binaries (built executables) 17 | src-tauri/binaries/ 18 | 19 | # Editor directories and files 20 | .vscode/* 21 | !.vscode/extensions.json 22 | .idea 23 | .DS_Store 24 | *.suo 25 | *.ntvs* 26 | *.njsproj 27 | *.sln 28 | *.sw? 29 | temp_lib/ 30 | 31 | .cursor/ 32 | AGENTS.md 33 | CLAUDE.md 34 | *_TASK.md 35 | 36 | # Claude project-specific files 37 | .claude/ 38 | 39 | .env -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | # Build outputs 2 | dist/ 3 | build/ 4 | coverage/ 5 | 6 | # Tauri build artifacts 7 | src-tauri/target/ 8 | src-tauri/Cargo.lock 9 | 10 | # Node modules 11 | node_modules/ 12 | 13 | # Generated files 14 | *.generated.* 15 | *.min.js 16 | *.min.css 17 | 18 | # Lock files 19 | package-lock.json 20 | yarn.lock 21 | bun.lockb 22 | 23 | # IDE files 24 | .vscode/ 25 | .idea/ 26 | 27 | # OS files 28 | .DS_Store 29 | Thumbs.db 30 | 31 | # Logs 32 | *.log 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Temporary files 44 | *.tmp 45 | *.temp -------------------------------------------------------------------------------- /claudeCli/.prettierignore: -------------------------------------------------------------------------------- 1 | # Build outputs 2 | dist/ 3 | build/ 4 | coverage/ 5 | 6 | # Tauri build artifacts 7 | src-tauri/target/ 8 | src-tauri/Cargo.lock 9 | 10 | # Node modules 11 | node_modules/ 12 | 13 | # Generated files 14 | *.generated.* 15 | *.min.js 16 | *.min.css 17 | 18 | # Lock files 19 | package-lock.json 20 | yarn.lock 21 | bun.lockb 22 | 23 | # IDE files 24 | .vscode/ 25 | .idea/ 26 | 27 | # OS files 28 | .DS_Store 29 | Thumbs.db 30 | 31 | # Logs 32 | *.log 33 | npm-debug.log* 34 | yarn-debug.log* 35 | yarn-error.log* 36 | 37 | # Runtime data 38 | pids 39 | *.pid 40 | *.seed 41 | *.pid.lock 42 | 43 | # Temporary files 44 | *.tmp 45 | *.temp -------------------------------------------------------------------------------- /src/contexts/toastUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for toast management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Generate a unique identifier for toast notifications 8 | * 9 | * Creates a unique toast ID using timestamp and random characters 10 | * to ensure uniqueness across toast creation sessions. 11 | * 12 | * @returns Unique toast identifier string 13 | * 14 | * @example 15 | * ```typescript 16 | * const toastId = generateToastId(); 17 | * // Returns: 'toast_1703123456789_abc123def' 18 | * ``` 19 | */ 20 | export const generateToastId = (): string => { 21 | return `toast_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; 22 | }; 23 | -------------------------------------------------------------------------------- /claudeCli/src/contexts/toastUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for toast management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Generate a unique identifier for toast notifications 8 | * 9 | * Creates a unique toast ID using timestamp and random characters 10 | * to ensure uniqueness across toast creation sessions. 11 | * 12 | * @returns Unique toast identifier string 13 | * 14 | * @example 15 | * ```typescript 16 | * const toastId = generateToastId(); 17 | * // Returns: 'toast_1703123456789_abc123def' 18 | * ``` 19 | */ 20 | export const generateToastId = (): string => { 21 | return `toast_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`; 22 | }; 23 | -------------------------------------------------------------------------------- /src-tauri/Cargo.production.toml: -------------------------------------------------------------------------------- 1 | # 生产环境 Cargo 配置 2 | # 使用方法: CARGO_MANIFEST_PATH=Cargo.production.toml cargo build --release 3 | 4 | [profile.release] 5 | # 启用最大优化 6 | opt-level = 3 7 | # 启用 LTO (Link Time Optimization) 8 | lto = true 9 | # 减少代码大小 10 | codegen-units = 1 11 | # 启用 panic = "abort" 以减少二进制大小 12 | panic = "abort" 13 | # 去除调试符号 14 | debug = false 15 | # 启用所有优化 16 | overflow-checks = false 17 | 18 | [profile.release.package."*"] 19 | # 对所有依赖包也启用最大优化 20 | opt-level = 3 21 | 22 | # 生产环境特定的依赖配置 23 | [dependencies.log] 24 | version = "0.4" 25 | features = ["release_max_level_warn"] # 编译时移除 debug 和 info 日志 26 | 27 | [dependencies.env_logger] 28 | version = "0.11" 29 | default-features = false 30 | features = ["color"] -------------------------------------------------------------------------------- /claudeCli/src-tauri/Cargo.production.toml: -------------------------------------------------------------------------------- 1 | # 生产环境 Cargo 配置 2 | # 使用方法: CARGO_MANIFEST_PATH=Cargo.production.toml cargo build --release 3 | 4 | [profile.release] 5 | # 启用最大优化 6 | opt-level = 3 7 | # 启用 LTO (Link Time Optimization) 8 | lto = true 9 | # 减少代码大小 10 | codegen-units = 1 11 | # 启用 panic = "abort" 以减少二进制大小 12 | panic = "abort" 13 | # 去除调试符号 14 | debug = false 15 | # 启用所有优化 16 | overflow-checks = false 17 | 18 | [profile.release.package."*"] 19 | # 对所有依赖包也启用最大优化 20 | opt-level = 3 21 | 22 | # 生产环境特定的依赖配置 23 | [dependencies.log] 24 | version = "0.4" 25 | features = ["release_max_level_warn"] # 编译时移除 debug 和 info 日志 26 | 27 | [dependencies.env_logger] 28 | version = "0.11" 29 | default-features = false 30 | features = ["color"] -------------------------------------------------------------------------------- /src/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { afterEach } from "vitest"; 2 | import { cleanup } from "@testing-library/react"; 3 | import "@testing-library/jest-dom"; 4 | 5 | /** 6 | * Test setup configuration for Vitest and React Testing Library 7 | * 8 | * Configures the testing environment with Jest DOM matchers and automatic 9 | * cleanup after each test case to ensure test isolation. 10 | */ 11 | 12 | // Jest DOM matchers are automatically extended when importing '@testing-library/jest-dom' 13 | 14 | /** 15 | * Cleanup function that runs after each test case 16 | * 17 | * Ensures proper cleanup of the DOM and React components to prevent 18 | * test interference and memory leaks. 19 | */ 20 | afterEach(() => { 21 | cleanup(); 22 | }); 23 | -------------------------------------------------------------------------------- /src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ 2 | 3 | // Declare modules 4 | pub mod checkpoint; 5 | pub mod claude_binary; 6 | pub mod commands; 7 | pub mod logger; 8 | pub mod process; 9 | 10 | // Logger macros are automatically exported to crate root due to #[macro_export] 11 | // No need to re-export them manually 12 | 13 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 14 | pub fn run() -> Result<(), Box> { 15 | tauri::Builder::default() 16 | .run(tauri::generate_context!()) 17 | .map_err(|e| { 18 | log::error!("Failed to run Tauri application: {}", e); 19 | e 20 | })?; 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /claudeCli/src/test/setup.ts: -------------------------------------------------------------------------------- 1 | import { afterEach } from "vitest"; 2 | import { cleanup } from "@testing-library/react"; 3 | import "@testing-library/jest-dom"; 4 | 5 | /** 6 | * Test setup configuration for Vitest and React Testing Library 7 | * 8 | * Configures the testing environment with Jest DOM matchers and automatic 9 | * cleanup after each test case to ensure test isolation. 10 | */ 11 | 12 | // Jest DOM matchers are automatically extended when importing '@testing-library/jest-dom' 13 | 14 | /** 15 | * Cleanup function that runs after each test case 16 | * 17 | * Ensures proper cleanup of the DOM and React components to prevent 18 | * test interference and memory leaks. 19 | */ 20 | afterEach(() => { 21 | cleanup(); 22 | }); 23 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/src/lib.rs: -------------------------------------------------------------------------------- 1 | // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ 2 | 3 | // Declare modules 4 | pub mod checkpoint; 5 | pub mod claude_binary; 6 | pub mod commands; 7 | pub mod logger; 8 | pub mod process; 9 | 10 | // Logger macros are automatically exported to crate root due to #[macro_export] 11 | // No need to re-export them manually 12 | 13 | #[cfg_attr(mobile, tauri::mobile_entry_point)] 14 | pub fn run() -> Result<(), Box> { 15 | tauri::Builder::default() 16 | .run(tauri::generate_context!()) 17 | .map_err(|e| { 18 | log::error!("Failed to run Tauri application: {}", e); 19 | e 20 | })?; 21 | 22 | Ok(()) 23 | } 24 | -------------------------------------------------------------------------------- /claudeCli/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | /** 5 | * Combines multiple class values into a single string using clsx and tailwind-merge. 6 | * This utility function helps manage dynamic class names and prevents Tailwind CSS conflicts. 7 | * 8 | * @param inputs - Array of class values that can be strings, objects, arrays, etc. 9 | * @returns A merged string of class names with Tailwind conflicts resolved 10 | * 11 | * @example 12 | * cn("px-2 py-1", condition && "bg-blue-500", { "text-white": isActive }) 13 | * // Returns: "px-2 py-1 bg-blue-500 text-white" (when condition and isActive are true) 14 | */ 15 | export function cn(...inputs: ClassValue[]) { 16 | return twMerge(clsx(inputs)); 17 | } 18 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "singleQuote": false, 5 | "printWidth": 100, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "bracketSpacing": true, 11 | "bracketSameLine": false, 12 | "arrowParens": "always", 13 | "endOfLine": "lf", 14 | "embeddedLanguageFormatting": "auto", 15 | "htmlWhitespaceSensitivity": "css", 16 | "insertPragma": false, 17 | "proseWrap": "preserve", 18 | "requirePragma": false, 19 | "vueIndentScriptAndStyle": false, 20 | "overrides": [ 21 | { 22 | "files": "*.md", 23 | "options": { 24 | "printWidth": 80, 25 | "proseWrap": "always" 26 | } 27 | }, 28 | { 29 | "files": "*.json", 30 | "options": { 31 | "printWidth": 120 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /claudeCli/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "semi": true, 3 | "trailingComma": "es5", 4 | "singleQuote": false, 5 | "printWidth": 100, 6 | "tabWidth": 2, 7 | "useTabs": false, 8 | "quoteProps": "as-needed", 9 | "jsxSingleQuote": false, 10 | "bracketSpacing": true, 11 | "bracketSameLine": false, 12 | "arrowParens": "always", 13 | "endOfLine": "lf", 14 | "embeddedLanguageFormatting": "auto", 15 | "htmlWhitespaceSensitivity": "css", 16 | "insertPragma": false, 17 | "proseWrap": "preserve", 18 | "requirePragma": false, 19 | "vueIndentScriptAndStyle": false, 20 | "overrides": [ 21 | { 22 | "files": "*.md", 23 | "options": { 24 | "printWidth": 80, 25 | "proseWrap": "always" 26 | } 27 | }, 28 | { 29 | "files": "*.json", 30 | "options": { 31 | "printWidth": 120 32 | } 33 | } 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /src/contexts/tabUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for tab management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Generate a unique identifier for tabs 8 | * 9 | * Creates a unique tab ID using timestamp and random characters 10 | * to ensure uniqueness across tab creation sessions. 11 | * 12 | * @returns Unique tab identifier string 13 | * 14 | * @example 15 | * ```typescript 16 | * const tabId = generateTabId(); 17 | * // Returns: 'tab-1703123456789-abc123def' 18 | * ``` 19 | */ 20 | export const generateTabId = (): string => { 21 | return `tab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; 22 | }; 23 | 24 | /** 25 | * Maximum number of tabs allowed to be open simultaneously 26 | * 27 | * Prevents excessive memory usage and maintains UI performance 28 | * by limiting the number of concurrent tabs. 29 | */ 30 | export const MAX_TABS = 20; 31 | -------------------------------------------------------------------------------- /claudeCli/src/contexts/tabUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for tab management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Generate a unique identifier for tabs 8 | * 9 | * Creates a unique tab ID using timestamp and random characters 10 | * to ensure uniqueness across tab creation sessions. 11 | * 12 | * @returns Unique tab identifier string 13 | * 14 | * @example 15 | * ```typescript 16 | * const tabId = generateTabId(); 17 | * // Returns: 'tab-1703123456789-abc123def' 18 | * ``` 19 | */ 20 | export const generateTabId = (): string => { 21 | return `tab-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; 22 | }; 23 | 24 | /** 25 | * Maximum number of tabs allowed to be open simultaneously 26 | * 27 | * Prevents excessive memory usage and maintains UI performance 28 | * by limiting the number of concurrent tabs. 29 | */ 30 | export const MAX_TABS = 20; 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | /* Testing */ 24 | "types": ["vitest/globals", "@testing-library/jest-dom"], 25 | 26 | /* Path aliases */ 27 | "baseUrl": ".", 28 | "paths": { 29 | "@/*": ["./src/*"] 30 | } 31 | }, 32 | "include": ["src"], 33 | "references": [{ "path": "./tsconfig.node.json" }] 34 | } 35 | -------------------------------------------------------------------------------- /src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useThemeContext } from '../contexts/ThemeContext'; 2 | 3 | /** 4 | * Hook to access and control the theme system 5 | * 6 | * @returns {Object} Theme utilities and state 7 | * @returns {ThemeMode} theme - Current theme mode ('dark' | 'gray' | 'light' | 'custom') 8 | * @returns {CustomThemeColors} customColors - Custom theme color configuration 9 | * @returns {Function} setTheme - Function to change the theme mode 10 | * @returns {Function} setCustomColors - Function to update custom theme colors 11 | * @returns {boolean} isLoading - Whether theme operations are in progress 12 | * 13 | * @example 14 | * const { theme, setTheme } = useTheme(); 15 | * 16 | * // Change theme 17 | * await setTheme('light'); 18 | * 19 | * // Update custom colors 20 | * await setCustomColors({ background: 'oklch(0.98 0.01 240)' }); 21 | */ 22 | export const useTheme = () => { 23 | return useThemeContext(); 24 | }; -------------------------------------------------------------------------------- /claudeCli/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 6 | "module": "ESNext", 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "noEmit": true, 15 | "jsx": "react-jsx", 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true, 22 | 23 | /* Testing */ 24 | "types": ["vitest/globals", "@testing-library/jest-dom"], 25 | 26 | /* Path aliases */ 27 | "baseUrl": ".", 28 | "paths": { 29 | "@/*": ["./src/*"] 30 | } 31 | }, 32 | "include": ["src"], 33 | "references": [{ "path": "./tsconfig.node.json" }] 34 | } 35 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/useTheme.ts: -------------------------------------------------------------------------------- 1 | import { useThemeContext } from '../contexts/ThemeContext'; 2 | 3 | /** 4 | * Hook to access and control the theme system 5 | * 6 | * @returns {Object} Theme utilities and state 7 | * @returns {ThemeMode} theme - Current theme mode ('dark' | 'gray' | 'light' | 'custom') 8 | * @returns {CustomThemeColors} customColors - Custom theme color configuration 9 | * @returns {Function} setTheme - Function to change the theme mode 10 | * @returns {Function} setCustomColors - Function to update custom theme colors 11 | * @returns {boolean} isLoading - Whether theme operations are in progress 12 | * 13 | * @example 14 | * const { theme, setTheme } = useTheme(); 15 | * 16 | * // Change theme 17 | * await setTheme('light'); 18 | * 19 | * // Update custom colors 20 | * await setCustomColors({ background: 'oklch(0.98 0.01 240)' }); 21 | */ 22 | export const useTheme = () => { 23 | return useThemeContext(); 24 | }; -------------------------------------------------------------------------------- /claudeCli/src-tauri/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | /// # Commands Module 2 | /// 3 | /// This module contains all Tauri command handlers for the Claudia application. 4 | /// Commands are organized by functionality and provide the bridge between the 5 | /// frontend React application and the Rust backend. 6 | /// 7 | /// ## Module Structure 8 | /// 9 | /// - `agents` - Agent management and execution commands 10 | /// - `claude` - Claude Code integration and session management 11 | /// - `mcp` - Model Context Protocol server management 12 | /// - `slash_commands` - Slash command discovery and management 13 | /// - `storage` - Database operations and data management 14 | /// - `usage` - Usage statistics and cost tracking 15 | /// 16 | /// ## Security 17 | /// 18 | /// All commands implement proper input validation and use parameterized queries 19 | /// for database operations to prevent SQL injection attacks. 20 | 21 | pub mod agents; 22 | pub mod claude; 23 | pub mod mcp; 24 | pub mod usage; 25 | pub mod storage; 26 | pub mod slash_commands; 27 | pub mod proxy; 28 | -------------------------------------------------------------------------------- /src/lib/__tests__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | import { cn } from "../utils"; 3 | 4 | /** 5 | * Test suite for utility functions 6 | * 7 | * Tests general utility functions including class name merging, 8 | * conditional class handling, and Tailwind CSS integration. 9 | */ 10 | describe("Utils", () => { 11 | describe("cn function", () => { 12 | it("merges class names correctly", () => { 13 | expect(cn("class1", "class2")).toBe("class1 class2"); 14 | }); 15 | 16 | it("handles conditional classes", () => { 17 | const showConditional = true; 18 | const showHidden = false; 19 | expect(cn("base", showConditional && "conditional", showHidden && "hidden")).toBe( 20 | "base conditional" 21 | ); 22 | }); 23 | 24 | it("handles undefined and null values", () => { 25 | expect(cn("base", undefined, null, "end")).toBe("base end"); 26 | }); 27 | 28 | it("merges tailwind classes correctly", () => { 29 | expect(cn("px-2 py-1", "px-4")).toBe("py-1 px-4"); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /claudeCli/src/lib/__tests__/utils.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect } from "vitest"; 2 | import { cn } from "../utils"; 3 | 4 | /** 5 | * Test suite for utility functions 6 | * 7 | * Tests general utility functions including class name merging, 8 | * conditional class handling, and Tailwind CSS integration. 9 | */ 10 | describe("Utils", () => { 11 | describe("cn function", () => { 12 | it("merges class names correctly", () => { 13 | expect(cn("class1", "class2")).toBe("class1 class2"); 14 | }); 15 | 16 | it("handles conditional classes", () => { 17 | const showConditional = true; 18 | const showHidden = false; 19 | expect(cn("base", showConditional && "conditional", showHidden && "hidden")).toBe( 20 | "base conditional" 21 | ); 22 | }); 23 | 24 | it("handles undefined and null values", () => { 25 | expect(cn("base", undefined, null, "end")).toBe("base end"); 26 | }); 27 | 28 | it("merges tailwind classes correctly", () => { 29 | expect(cn("px-2 py-1", "px-4")).toBe("py-1 px-4"); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /src-tauri/src/commands/mod.rs: -------------------------------------------------------------------------------- 1 | /// # Commands Module 2 | /// 3 | /// This module contains all Tauri command handlers for the TermiClaude application. 4 | /// Commands are organized by functionality and provide the bridge between the 5 | /// frontend React application and the Rust backend. 6 | /// 7 | /// ## Module Structure 8 | /// 9 | /// - `agents` - Agent management and execution commands 10 | /// - `claude` - Claude Code integration and session management 11 | /// - `mcp` - Model Context Protocol server management 12 | /// - `settings_monitor` - Configuration monitoring and conflict detection 13 | /// - `slash_commands` - Slash command discovery and management 14 | /// - `storage` - Database operations and data management 15 | /// - `usage` - Usage statistics and cost tracking 16 | /// 17 | /// ## Security 18 | /// 19 | /// All commands implement proper input validation and use parameterized queries 20 | /// for database operations to prevent SQL injection attacks. 21 | 22 | pub mod agents; 23 | pub mod claude; 24 | pub mod mcp; 25 | pub mod settings_monitor; 26 | pub mod usage; 27 | pub mod storage; 28 | pub mod slash_commands; 29 | pub mod proxy; 30 | -------------------------------------------------------------------------------- /src/contexts/contextUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for context management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Create a standardized error for tab context usage outside provider 8 | * 9 | * @param hookName - Name of the hook that was called outside the provider 10 | * @returns Error object with descriptive message 11 | * 12 | * @example 13 | * ```typescript 14 | * if (!context) { 15 | * throw createTabContextError('useTabState'); 16 | * } 17 | * ``` 18 | */ 19 | export const createTabContextError = (hookName: string): Error => { 20 | return new Error(`${hookName} must be used within a TabProvider`); 21 | }; 22 | 23 | /** 24 | * Create a standardized error for toast context usage outside provider 25 | * 26 | * @param hookName - Name of the hook that was called outside the provider 27 | * @returns Error object with descriptive message 28 | * 29 | * @example 30 | * ```typescript 31 | * if (!context) { 32 | * throw createToastContextError('useToast'); 33 | * } 34 | * ``` 35 | */ 36 | export const createToastContextError = (hookName: string): Error => { 37 | return new Error(`${hookName} must be used within a ToastProvider`); 38 | }; 39 | -------------------------------------------------------------------------------- /claudeCli/src/contexts/contextUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Utility functions for context management 3 | * Separated to avoid fast refresh warnings 4 | */ 5 | 6 | /** 7 | * Create a standardized error for tab context usage outside provider 8 | * 9 | * @param hookName - Name of the hook that was called outside the provider 10 | * @returns Error object with descriptive message 11 | * 12 | * @example 13 | * ```typescript 14 | * if (!context) { 15 | * throw createTabContextError('useTabState'); 16 | * } 17 | * ``` 18 | */ 19 | export const createTabContextError = (hookName: string): Error => { 20 | return new Error(`${hookName} must be used within a TabProvider`); 21 | }; 22 | 23 | /** 24 | * Create a standardized error for toast context usage outside provider 25 | * 26 | * @param hookName - Name of the hook that was called outside the provider 27 | * @returns Error object with descriptive message 28 | * 29 | * @example 30 | * ```typescript 31 | * if (!context) { 32 | * throw createToastContextError('useToast'); 33 | * } 34 | * ``` 35 | */ 36 | export const createToastContextError = (hookName: string): Error => { 37 | return new Error(`${hookName} must be used within a ToastProvider`); 38 | }; 39 | -------------------------------------------------------------------------------- /scripts/bump-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to bump version across all files 4 | # Usage: ./scripts/bump-version.sh 1.0.0 5 | 6 | set -e 7 | 8 | if [ -z "$1" ]; then 9 | echo "Usage: $0 " 10 | echo "Example: $0 1.0.0" 11 | exit 1 12 | fi 13 | 14 | VERSION=$1 15 | 16 | echo "Bumping version to $VERSION..." 17 | 18 | # Update package.json 19 | sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" package.json && rm package.json.bak 20 | 21 | # Update Cargo.toml 22 | sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" src-tauri/Cargo.toml && rm src-tauri/Cargo.toml.bak 23 | 24 | # Update tauri.conf.json 25 | sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src-tauri/tauri.conf.json && rm src-tauri/tauri.conf.json.bak 26 | 27 | # Update Info.plist 28 | sed -i.bak "s/.*<\/string>/$VERSION<\/string>/" src-tauri/Info.plist && rm src-tauri/Info.plist.bak 29 | 30 | echo "✅ Version bumped to $VERSION in all files" 31 | echo "" 32 | echo "Next steps:" 33 | echo "1. Review the changes: git diff" 34 | echo "2. Commit: git commit -am \"chore: bump version to v$VERSION\"" 35 | echo "3. Tag: git tag -a v$VERSION -m \"Release v$VERSION\"" 36 | echo "4. Push: git push && git push --tags" 37 | -------------------------------------------------------------------------------- /claudeCli/scripts/bump-version.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Script to bump version across all files 4 | # Usage: ./scripts/bump-version.sh 1.0.0 5 | 6 | set -e 7 | 8 | if [ -z "$1" ]; then 9 | echo "Usage: $0 " 10 | echo "Example: $0 1.0.0" 11 | exit 1 12 | fi 13 | 14 | VERSION=$1 15 | 16 | echo "Bumping version to $VERSION..." 17 | 18 | # Update package.json 19 | sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" package.json && rm package.json.bak 20 | 21 | # Update Cargo.toml 22 | sed -i.bak "s/^version = \".*\"/version = \"$VERSION\"/" src-tauri/Cargo.toml && rm src-tauri/Cargo.toml.bak 23 | 24 | # Update tauri.conf.json 25 | sed -i.bak "s/\"version\": \".*\"/\"version\": \"$VERSION\"/" src-tauri/tauri.conf.json && rm src-tauri/tauri.conf.json.bak 26 | 27 | # Update Info.plist 28 | sed -i.bak "s/.*<\/string>/$VERSION<\/string>/" src-tauri/Info.plist && rm src-tauri/Info.plist.bak 29 | 30 | echo "✅ Version bumped to $VERSION in all files" 31 | echo "" 32 | echo "Next steps:" 33 | echo "1. Review the changes: git diff" 34 | echo "2. Commit: git commit -am \"chore: bump version to v$VERSION\"" 35 | echo "3. Tag: git tag -a v$VERSION -m \"Release v$VERSION\"" 36 | echo "4. Push: git push && git push --tags" 37 | -------------------------------------------------------------------------------- /src/components/__tests__/Button.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { Button } from "../ui/button"; 4 | 5 | /** 6 | * Test suite for Button component 7 | * 8 | * Tests button rendering, variants, sizes, states, and accessibility features. 9 | */ 10 | describe("Button Component", () => { 11 | it("renders correctly", () => { 12 | render(Test Button); 13 | expect(screen.getByRole("button")).toBeInTheDocument(); 14 | expect(screen.getByText("Test Button")).toBeInTheDocument(); 15 | }); 16 | 17 | it("handles click events", () => { 18 | const handleClick = vi.fn(); 19 | render(Click me); 20 | 21 | fireEvent.click(screen.getByRole("button")); 22 | expect(handleClick).toHaveBeenCalledTimes(1); 23 | }); 24 | 25 | it("applies variant classes correctly", () => { 26 | render(Destructive Button); 27 | const button = screen.getByRole("button"); 28 | expect(button).toHaveClass("bg-destructive"); 29 | }); 30 | 31 | it("can be disabled", () => { 32 | render(Disabled Button); 33 | const button = screen.getByRole("button"); 34 | expect(button).toBeDisabled(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Label component 6 | * 7 | * Extends standard HTML label attributes for form field labeling. 8 | */ 9 | export interface LabelProps extends React.LabelHTMLAttributes {} 10 | 11 | /** 12 | * Label component for form fields 13 | * 14 | * Provides consistent styling for form field labels with proper accessibility 15 | * support and disabled state handling. 16 | * 17 | * @param htmlFor - ID of the associated form control 18 | * @param className - Additional CSS classes 19 | * @param children - Label text content 20 | * 21 | * @example 22 | * ```tsx 23 | * Email Address 24 | * 25 | * 26 | * Description 27 | * 28 | * ``` 29 | */ 30 | const Label = React.forwardRef(({ className, ...props }, ref) => ( 31 | 39 | )); 40 | 41 | Label.displayName = "Label"; 42 | 43 | export { Label }; 44 | -------------------------------------------------------------------------------- /claudeCli/src/components/__tests__/Button.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { Button } from "../ui/button"; 4 | 5 | /** 6 | * Test suite for Button component 7 | * 8 | * Tests button rendering, variants, sizes, states, and accessibility features. 9 | */ 10 | describe("Button Component", () => { 11 | it("renders correctly", () => { 12 | render(Test Button); 13 | expect(screen.getByRole("button")).toBeInTheDocument(); 14 | expect(screen.getByText("Test Button")).toBeInTheDocument(); 15 | }); 16 | 17 | it("handles click events", () => { 18 | const handleClick = vi.fn(); 19 | render(Click me); 20 | 21 | fireEvent.click(screen.getByRole("button")); 22 | expect(handleClick).toHaveBeenCalledTimes(1); 23 | }); 24 | 25 | it("applies variant classes correctly", () => { 26 | render(Destructive Button); 27 | const button = screen.getByRole("button"); 28 | expect(button).toHaveClass("bg-destructive"); 29 | }); 30 | 31 | it("can be disabled", () => { 32 | render(Disabled Button); 33 | const button = screen.getByRole("button"); 34 | expect(button).toBeDisabled(); 35 | }); 36 | }); 37 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/label.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Label component 6 | * 7 | * Extends standard HTML label attributes for form field labeling. 8 | */ 9 | export interface LabelProps extends React.LabelHTMLAttributes {} 10 | 11 | /** 12 | * Label component for form fields 13 | * 14 | * Provides consistent styling for form field labels with proper accessibility 15 | * support and disabled state handling. 16 | * 17 | * @param htmlFor - ID of the associated form control 18 | * @param className - Additional CSS classes 19 | * @param children - Label text content 20 | * 21 | * @example 22 | * ```tsx 23 | * Email Address 24 | * 25 | * 26 | * Description 27 | * 28 | * ``` 29 | */ 30 | const Label = React.forwardRef(({ className, ...props }, ref) => ( 31 | 39 | )); 40 | 41 | Label.displayName = "Label"; 42 | 43 | export { Label }; 44 | -------------------------------------------------------------------------------- /src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "core:default", 8 | "dialog:default", 9 | "dialog:allow-open", 10 | "dialog:allow-save", 11 | "shell:allow-execute", 12 | "shell:allow-spawn", 13 | "shell:allow-open", 14 | { 15 | "identifier": "shell:allow-execute", 16 | "allow": [ 17 | { 18 | "name": "claude", 19 | "sidecar": false, 20 | "args": true 21 | } 22 | ] 23 | }, 24 | { 25 | "identifier": "shell:allow-spawn", 26 | "allow": [ 27 | { 28 | "name": "claude", 29 | "sidecar": false, 30 | "args": true 31 | } 32 | ] 33 | }, 34 | "fs:default", 35 | "fs:allow-mkdir", 36 | "fs:allow-read", 37 | "fs:allow-write", 38 | "fs:allow-remove", 39 | "fs:allow-rename", 40 | "fs:allow-exists", 41 | "fs:allow-copy-file", 42 | "fs:read-all", 43 | "fs:write-all", 44 | "fs:scope-app-recursive", 45 | "fs:scope-home-recursive", 46 | "http:default", 47 | "http:allow-fetch", 48 | "process:default", 49 | "notification:default", 50 | "clipboard-manager:default", 51 | "global-shortcut:default", 52 | "updater:default" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/capabilities/default.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "../gen/schemas/desktop-schema.json", 3 | "identifier": "default", 4 | "description": "Capability for the main window", 5 | "windows": ["main"], 6 | "permissions": [ 7 | "core:default", 8 | "dialog:default", 9 | "dialog:allow-open", 10 | "dialog:allow-save", 11 | "shell:allow-execute", 12 | "shell:allow-spawn", 13 | "shell:allow-open", 14 | { 15 | "identifier": "shell:allow-execute", 16 | "allow": [ 17 | { 18 | "name": "claude", 19 | "sidecar": false, 20 | "args": true 21 | } 22 | ] 23 | }, 24 | { 25 | "identifier": "shell:allow-spawn", 26 | "allow": [ 27 | { 28 | "name": "claude", 29 | "sidecar": false, 30 | "args": true 31 | } 32 | ] 33 | }, 34 | "fs:default", 35 | "fs:allow-mkdir", 36 | "fs:allow-read", 37 | "fs:allow-write", 38 | "fs:allow-remove", 39 | "fs:allow-rename", 40 | "fs:allow-exists", 41 | "fs:allow-copy-file", 42 | "fs:read-all", 43 | "fs:write-all", 44 | "fs:scope-app-recursive", 45 | "fs:scope-home-recursive", 46 | "http:default", 47 | "http:allow-fetch", 48 | "process:default", 49 | "notification:default", 50 | "clipboard-manager:default", 51 | "global-shortcut:default", 52 | "updater:default" 53 | ] 54 | } 55 | -------------------------------------------------------------------------------- /src/lib/__tests__/api.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { api } from "../api"; 3 | 4 | // Mock Tauri invoke 5 | vi.mock("@tauri-apps/api/core", () => ({ 6 | invoke: vi.fn(), 7 | })); 8 | 9 | /** 10 | * Test suite for API module 11 | * 12 | * Tests the main API interface including method availability, 13 | * function types, and module structure validation. 14 | */ 15 | describe("API Module", () => { 16 | it("should have all required API methods", () => { 17 | expect(api.listProjects).toBeDefined(); 18 | expect(api.getProjectSessions).toBeDefined(); 19 | expect(api.executeClaudeCode).toBeDefined(); 20 | expect(api.getAgent).toBeDefined(); 21 | expect(api.createAgent).toBeDefined(); 22 | }); 23 | 24 | it("should export API functions as expected", () => { 25 | expect(typeof api.listProjects).toBe("function"); 26 | expect(typeof api.getProjectSessions).toBe("function"); 27 | expect(typeof api.executeClaudeCode).toBe("function"); 28 | }); 29 | 30 | it("should have agent management methods", () => { 31 | expect(typeof api.getAgent).toBe("function"); 32 | expect(typeof api.createAgent).toBe("function"); 33 | expect(typeof api.updateAgent).toBe("function"); 34 | expect(typeof api.deleteAgent).toBe("function"); 35 | }); 36 | 37 | it("should have session management methods", () => { 38 | expect(typeof api.getProjectSessions).toBe("function"); 39 | // Note: deleteSession is not yet implemented in the API 40 | // expect(typeof api.deleteSession).toBe('function') 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import { ErrorBoundary } from "./components/ErrorBoundary"; 5 | import { I18nProvider } from "./components/I18nProvider"; 6 | import { AnalyticsErrorBoundary } from "./components/AnalyticsErrorBoundary"; 7 | import { replaceConsole } from "./lib/logger"; 8 | import { fontScaleManager } from "./lib/fontScale"; 9 | import { analytics, resourceMonitor } from "./lib/analytics"; 10 | import { PostHogProvider } from "posthog-js/react"; 11 | import "./assets/shimmer.css"; 12 | import "./styles.css"; 13 | // 初始化日志系统 14 | replaceConsole(); 15 | 16 | // 初始化字体缩放 17 | fontScaleManager; 18 | 19 | // Initialize analytics before rendering 20 | analytics.initialize(); 21 | 22 | // Start resource monitoring (check every 2 minutes) 23 | resourceMonitor.startMonitoring(120000); 24 | 25 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 26 | 27 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | ); 46 | -------------------------------------------------------------------------------- /claudeCli/src/lib/__tests__/api.test.ts: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { api } from "../api"; 3 | 4 | // Mock Tauri invoke 5 | vi.mock("@tauri-apps/api/core", () => ({ 6 | invoke: vi.fn(), 7 | })); 8 | 9 | /** 10 | * Test suite for API module 11 | * 12 | * Tests the main API interface including method availability, 13 | * function types, and module structure validation. 14 | */ 15 | describe("API Module", () => { 16 | it("should have all required API methods", () => { 17 | expect(api.listProjects).toBeDefined(); 18 | expect(api.getProjectSessions).toBeDefined(); 19 | expect(api.executeClaudeCode).toBeDefined(); 20 | expect(api.getAgent).toBeDefined(); 21 | expect(api.createAgent).toBeDefined(); 22 | }); 23 | 24 | it("should export API functions as expected", () => { 25 | expect(typeof api.listProjects).toBe("function"); 26 | expect(typeof api.getProjectSessions).toBe("function"); 27 | expect(typeof api.executeClaudeCode).toBe("function"); 28 | }); 29 | 30 | it("should have agent management methods", () => { 31 | expect(typeof api.getAgent).toBe("function"); 32 | expect(typeof api.createAgent).toBe("function"); 33 | expect(typeof api.updateAgent).toBe("function"); 34 | expect(typeof api.deleteAgent).toBe("function"); 35 | }); 36 | 37 | it("should have session management methods", () => { 38 | expect(typeof api.getProjectSessions).toBe("function"); 39 | // Note: deleteSession is not yet implemented in the API 40 | // expect(typeof api.deleteSession).toBe('function') 41 | }); 42 | }); 43 | -------------------------------------------------------------------------------- /claudeCli/src/main.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom/client"; 3 | import App from "./App"; 4 | import { ErrorBoundary } from "./components/ErrorBoundary"; 5 | import { I18nProvider } from "./components/I18nProvider"; 6 | import { AnalyticsErrorBoundary } from "./components/AnalyticsErrorBoundary"; 7 | import { replaceConsole } from "./lib/logger"; 8 | import { fontScaleManager } from "./lib/fontScale"; 9 | import { analytics, resourceMonitor } from "./lib/analytics"; 10 | import { PostHogProvider } from "posthog-js/react"; 11 | import "./assets/shimmer.css"; 12 | import "./styles.css"; 13 | 14 | // 初始化日志系统 15 | replaceConsole(); 16 | 17 | // 初始化字体缩放 18 | fontScaleManager; 19 | 20 | // Initialize analytics before rendering 21 | analytics.initialize(); 22 | 23 | // Start resource monitoring (check every 2 minutes) 24 | resourceMonitor.startMonitoring(120000); 25 | 26 | ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( 27 | 28 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | ); 47 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSRequiresAquaSystemAppearance 6 | 7 | LSMinimumSystemVersion 8 | 10.15 9 | CFBundleShortVersionString 10 | 0.1.0 11 | CFBundleName 12 | Claudia 13 | CFBundleDisplayName 14 | Claudia 15 | CFBundleIdentifier 16 | claudia.asterisk.so 17 | CFBundleDocumentTypes 18 | 19 | 20 | CFBundleTypeName 21 | Claudia Agent 22 | CFBundleTypeRole 23 | Editor 24 | CFBundleTypeExtensions 25 | 26 | claudia.json 27 | 28 | CFBundleTypeIconFile 29 | icon.icns 30 | LSHandlerRank 31 | Owner 32 | 33 | 34 | NSAppleEventsUsageDescription 35 | Claudia needs to send Apple Events to other applications. 36 | NSAppleScriptEnabled 37 | 38 | NSCameraUsageDescription 39 | Claudia needs camera access for capturing images for AI processing. 40 | NSMicrophoneUsageDescription 41 | Claudia needs microphone access for voice input features. 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import type { Language, Translations } from "@/lib/i18n"; 2 | import { en } from "./en"; 3 | import { zh } from "./zh"; 4 | import { ja } from "./ja"; 5 | import { es } from "./es"; 6 | import { ko } from "./ko"; 7 | import { fr } from "./fr"; 8 | import { de } from "./de"; 9 | import { ru } from "./ru"; 10 | import { pt } from "./pt"; 11 | import { it } from "./it"; 12 | import { ar } from "./ar"; 13 | import { hi } from "./hi"; 14 | 15 | /** 16 | * Complete translations mapping for all supported languages 17 | * 18 | * Contains translation objects for all supported languages in the application. 19 | * Used by the i18n system to provide localized content. 20 | */ 21 | export const translations: Record = { 22 | en, 23 | zh, 24 | ja, 25 | es, 26 | ko, 27 | fr, 28 | de, 29 | ru, 30 | pt, 31 | it, 32 | ar, 33 | hi, 34 | }; 35 | 36 | /** 37 | * Get translations for a specific language 38 | * 39 | * Retrieves the translation object for the specified language. 40 | * Falls back to English if the requested language is not available. 41 | * 42 | * @param language - Language code to get translations for 43 | * @returns Translation object for the specified language 44 | * 45 | * @example 46 | * ```typescript 47 | * const zhTranslations = getTranslations('zh'); 48 | * const fallbackTranslations = getTranslations('unsupported' as Language); 49 | * // Returns English translations as fallback 50 | * ``` 51 | */ 52 | export const getTranslations = (language: Language): Translations => { 53 | return translations[language] || translations.en; 54 | }; 55 | -------------------------------------------------------------------------------- /claudeCli/src/locales/index.ts: -------------------------------------------------------------------------------- 1 | import type { Language, Translations } from "@/lib/i18n"; 2 | import { en } from "./en"; 3 | import { zh } from "./zh"; 4 | import { ja } from "./ja"; 5 | import { es } from "./es"; 6 | import { ko } from "./ko"; 7 | import { fr } from "./fr"; 8 | import { de } from "./de"; 9 | import { ru } from "./ru"; 10 | import { pt } from "./pt"; 11 | import { it } from "./it"; 12 | import { ar } from "./ar"; 13 | import { hi } from "./hi"; 14 | 15 | /** 16 | * Complete translations mapping for all supported languages 17 | * 18 | * Contains translation objects for all supported languages in the application. 19 | * Used by the i18n system to provide localized content. 20 | */ 21 | export const translations: Record = { 22 | en, 23 | zh, 24 | ja, 25 | es, 26 | ko, 27 | fr, 28 | de, 29 | ru, 30 | pt, 31 | it, 32 | ar, 33 | hi, 34 | }; 35 | 36 | /** 37 | * Get translations for a specific language 38 | * 39 | * Retrieves the translation object for the specified language. 40 | * Falls back to English if the requested language is not available. 41 | * 42 | * @param language - Language code to get translations for 43 | * @returns Translation object for the specified language 44 | * 45 | * @example 46 | * ```typescript 47 | * const zhTranslations = getTranslations('zh'); 48 | * const fallbackTranslations = getTranslations('unsupported' as Language); 49 | * // Returns English translations as fallback 50 | * ``` 51 | */ 52 | export const getTranslations = (language: Language): Translations => { 53 | return translations[language] || translations.en; 54 | }; 55 | -------------------------------------------------------------------------------- /src-tauri/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSRequiresAquaSystemAppearance 6 | 7 | LSMinimumSystemVersion 8 | 10.15 9 | CFBundleShortVersionString 10 | 0.1.0 11 | CFBundleName 12 | TermiClaude 13 | CFBundleDisplayName 14 | TermiClaude 15 | CFBundleIdentifier 16 | termiclaude.asterisk.so 17 | CFBundleDocumentTypes 18 | 19 | 20 | CFBundleTypeName 21 | TermiClaude Agent 22 | CFBundleTypeRole 23 | Editor 24 | CFBundleTypeExtensions 25 | 26 | termiclaude.json 27 | 28 | CFBundleTypeIconFile 29 | icon.icns 30 | LSHandlerRank 31 | Owner 32 | 33 | 34 | NSAppleEventsUsageDescription 35 | TermiClaude needs to send Apple Events to other applications. 36 | NSAppleScriptEnabled 37 | 38 | NSCameraUsageDescription 39 | TermiClaude needs camera access for capturing images for AI processing. 40 | NSMicrophoneUsageDescription 41 | TermiClaude needs microphone access for voice input features. 42 | 43 | 44 | -------------------------------------------------------------------------------- /src/components/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Components index - Central export point for all application components 3 | * 4 | * Provides a single import location for all components used throughout 5 | * the application, including UI components, business logic components, 6 | * and specialized widgets. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { Button, Dialog, AgentExecutionDemo } from '@/components'; 11 | * ``` 12 | */ 13 | export * from "./AgentExecutionDemo"; 14 | export * from "./AgentRunOutputViewer"; 15 | export * from "./StreamMessage"; 16 | export * from "./ToolWidgets"; 17 | export * from "./NFOCredits"; 18 | export * from "./UsageDashboard"; 19 | export * from "./WebviewPreview"; 20 | export * from "./ImagePreview"; 21 | export * from "./MCPManager"; 22 | export * from "./MCPServerList"; 23 | export * from "./MCPAddServer"; 24 | export * from "./MCPImportExport"; 25 | export * from "./ClaudeVersionSelector"; 26 | export * from "./ui/badge"; 27 | export * from "./ui/button"; 28 | export * from "./ui/card"; 29 | export * from "./ui/dialog"; 30 | export * from "./ui/dropdown-menu"; 31 | export * from "./ui/input"; 32 | export * from "./ui/label"; 33 | export * from "./ui/select"; 34 | export * from "./ui/switch"; 35 | export * from "./ui/tabs"; 36 | export * from "./ui/textarea"; 37 | export * from "./ui/toast"; 38 | export * from "./ui/tooltip"; 39 | export * from "./SlashCommandPicker"; 40 | export * from "./SlashCommandsManager"; 41 | export * from "./ui/popover"; 42 | export * from "./ui/pagination"; 43 | export * from "./ui/split-pane"; 44 | export * from "./ui/scroll-area"; 45 | export * from "./RunningClaudeSessions"; 46 | -------------------------------------------------------------------------------- /claudeCli/src/components/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Components index - Central export point for all application components 3 | * 4 | * Provides a single import location for all components used throughout 5 | * the application, including UI components, business logic components, 6 | * and specialized widgets. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { Button, Dialog, AgentExecutionDemo } from '@/components'; 11 | * ``` 12 | */ 13 | export * from "./AgentExecutionDemo"; 14 | export * from "./AgentRunOutputViewer"; 15 | export * from "./StreamMessage"; 16 | export * from "./ToolWidgets"; 17 | export * from "./NFOCredits"; 18 | export * from "./UsageDashboard"; 19 | export * from "./WebviewPreview"; 20 | export * from "./ImagePreview"; 21 | export * from "./MCPManager"; 22 | export * from "./MCPServerList"; 23 | export * from "./MCPAddServer"; 24 | export * from "./MCPImportExport"; 25 | export * from "./ClaudeVersionSelector"; 26 | export * from "./ui/badge"; 27 | export * from "./ui/button"; 28 | export * from "./ui/card"; 29 | export * from "./ui/dialog"; 30 | export * from "./ui/dropdown-menu"; 31 | export * from "./ui/input"; 32 | export * from "./ui/label"; 33 | export * from "./ui/select"; 34 | export * from "./ui/switch"; 35 | export * from "./ui/tabs"; 36 | export * from "./ui/textarea"; 37 | export * from "./ui/toast"; 38 | export * from "./ui/tooltip"; 39 | export * from "./SlashCommandPicker"; 40 | export * from "./SlashCommandsManager"; 41 | export * from "./ui/popover"; 42 | export * from "./ui/pagination"; 43 | export * from "./ui/split-pane"; 44 | export * from "./ui/scroll-area"; 45 | export * from "./RunningClaudeSessions"; 46 | -------------------------------------------------------------------------------- /src/hooks/useMessageDisplayMode.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export type MessageDisplayMode = 'both' | 'tool_calls_only'; 4 | 5 | /** 6 | * Hook to manage message display mode setting 7 | * Controls how tool calls and tool results are displayed in the UI 8 | */ 9 | export const useMessageDisplayMode = () => { 10 | const [mode, setMode] = useState('both'); 11 | 12 | useEffect(() => { 13 | // Load from localStorage on mount 14 | const savedMode = localStorage.getItem('messageDisplayMode') as MessageDisplayMode; 15 | // Handle legacy 'tool_results_only' by converting to 'tool_calls_only' 16 | const normalizedMode = (savedMode as string) === 'tool_results_only' ? 'tool_calls_only' : savedMode; 17 | if (normalizedMode && ['both', 'tool_calls_only'].includes(normalizedMode)) { 18 | setMode(normalizedMode); 19 | } 20 | 21 | // Listen for storage changes (when updated from Settings) 22 | const handleStorageChange = (e: StorageEvent) => { 23 | if (e.key === 'messageDisplayMode' && e.newValue) { 24 | const newMode = e.newValue as MessageDisplayMode; 25 | if (['both', 'tool_calls_only'].includes(newMode)) { 26 | setMode(newMode); 27 | } 28 | } 29 | }; 30 | 31 | window.addEventListener('storage', handleStorageChange); 32 | return () => window.removeEventListener('storage', handleStorageChange); 33 | }, []); 34 | 35 | const setDisplayMode = (newMode: MessageDisplayMode) => { 36 | setMode(newMode); 37 | localStorage.setItem('messageDisplayMode', newMode); 38 | }; 39 | 40 | return { mode, setDisplayMode }; 41 | }; -------------------------------------------------------------------------------- /claudeCli/src/hooks/useMessageDisplayMode.ts: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export type MessageDisplayMode = 'both' | 'tool_calls_only'; 4 | 5 | /** 6 | * Hook to manage message display mode setting 7 | * Controls how tool calls and tool results are displayed in the UI 8 | */ 9 | export const useMessageDisplayMode = () => { 10 | const [mode, setMode] = useState('both'); 11 | 12 | useEffect(() => { 13 | // Load from localStorage on mount 14 | const savedMode = localStorage.getItem('messageDisplayMode') as MessageDisplayMode; 15 | // Handle legacy 'tool_results_only' by converting to 'tool_calls_only' 16 | const normalizedMode = (savedMode as string) === 'tool_results_only' ? 'tool_calls_only' : savedMode; 17 | if (normalizedMode && ['both', 'tool_calls_only'].includes(normalizedMode)) { 18 | setMode(normalizedMode); 19 | } 20 | 21 | // Listen for storage changes (when updated from Settings) 22 | const handleStorageChange = (e: StorageEvent) => { 23 | if (e.key === 'messageDisplayMode' && e.newValue) { 24 | const newMode = e.newValue as MessageDisplayMode; 25 | if (['both', 'tool_calls_only'].includes(newMode)) { 26 | setMode(newMode); 27 | } 28 | } 29 | }; 30 | 31 | window.addEventListener('storage', handleStorageChange); 32 | return () => window.removeEventListener('storage', handleStorageChange); 33 | }, []); 34 | 35 | const setDisplayMode = (newMode: MessageDisplayMode) => { 36 | setMode(newMode); 37 | localStorage.setItem('messageDisplayMode', newMode); 38 | }; 39 | 40 | return { mode, setDisplayMode }; 41 | }; -------------------------------------------------------------------------------- /src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom hooks index 3 | * 4 | * Central export point for all custom React hooks used throughout the application. 5 | * These hooks provide reusable logic for common patterns like API calls, 6 | * state management, and UI interactions. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { useLoadingState, useDebounce, useApiCall } from '@/hooks'; 11 | * 12 | * function MyComponent() { 13 | * const { data, isLoading, execute } = useLoadingState(fetchData); 14 | * const debouncedValue = useDebounce(searchTerm, 300); 15 | * const { call: apiCall } = useApiCall(api.getData); 16 | * 17 | * // Use hooks in your component logic 18 | * } 19 | * ``` 20 | */ 21 | export { useLoadingState } from "./useLoadingState"; 22 | export { useDebounce, useDebouncedCallback } from "./useDebounce"; 23 | export { useApiCall } from "./useApiCall"; 24 | export { usePagination } from "./usePagination"; 25 | export { useTheme } from './useTheme'; 26 | export { useMessageDisplayMode, type MessageDisplayMode } from './useMessageDisplayMode'; 27 | export { 28 | useAnalytics, 29 | useTrackEvent, 30 | usePageView, 31 | useAppLifecycle, 32 | useComponentMetrics, 33 | useInteractionTracking, 34 | useScreenTracking, 35 | useFeatureExperiment, 36 | usePathTracking, 37 | useFeatureAdoptionTracking, 38 | useWorkflowTracking, 39 | useAIInteractionTracking, 40 | useNetworkPerformanceTracking 41 | } from './useAnalytics'; 42 | export { 43 | usePerformanceMonitor, 44 | useAsyncPerformanceTracker 45 | } from './usePerformanceMonitor'; 46 | export { TAB_SCREEN_NAMES } from './useAnalytics'; 47 | export { useTabContext } from '../contexts/hooks'; 48 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Custom hooks index 3 | * 4 | * Central export point for all custom React hooks used throughout the application. 5 | * These hooks provide reusable logic for common patterns like API calls, 6 | * state management, and UI interactions. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { useLoadingState, useDebounce, useApiCall } from '@/hooks'; 11 | * 12 | * function MyComponent() { 13 | * const { data, isLoading, execute } = useLoadingState(fetchData); 14 | * const debouncedValue = useDebounce(searchTerm, 300); 15 | * const { call: apiCall } = useApiCall(api.getData); 16 | * 17 | * // Use hooks in your component logic 18 | * } 19 | * ``` 20 | */ 21 | export { useLoadingState } from "./useLoadingState"; 22 | export { useDebounce, useDebouncedCallback } from "./useDebounce"; 23 | export { useApiCall } from "./useApiCall"; 24 | export { usePagination } from "./usePagination"; 25 | export { useTheme } from './useTheme'; 26 | export { useMessageDisplayMode, type MessageDisplayMode } from './useMessageDisplayMode'; 27 | export { 28 | useAnalytics, 29 | useTrackEvent, 30 | usePageView, 31 | useAppLifecycle, 32 | useComponentMetrics, 33 | useInteractionTracking, 34 | useScreenTracking, 35 | useFeatureExperiment, 36 | usePathTracking, 37 | useFeatureAdoptionTracking, 38 | useWorkflowTracking, 39 | useAIInteractionTracking, 40 | useNetworkPerformanceTracking 41 | } from './useAnalytics'; 42 | export { 43 | usePerformanceMonitor, 44 | useAsyncPerformanceTracker 45 | } from './usePerformanceMonitor'; 46 | export { TAB_SCREEN_NAMES } from './useAnalytics'; 47 | export { useTabContext } from '../contexts/hooks'; 48 | -------------------------------------------------------------------------------- /src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the ScrollArea component 6 | */ 7 | interface ScrollAreaProps extends React.HTMLAttributes { 8 | /** 9 | * Optional className for styling 10 | */ 11 | className?: string; 12 | /** 13 | * Children to render inside the scroll area 14 | */ 15 | children: React.ReactNode; 16 | } 17 | 18 | /** 19 | * ScrollArea component for scrollable content with custom scrollbar styling 20 | * 21 | * Provides a scrollable container with custom-styled scrollbars that match 22 | * the application theme. Automatically handles overflow and provides smooth 23 | * scrolling experience across different browsers. 24 | * 25 | * @param className - Additional CSS classes for styling 26 | * @param children - Content to be made scrollable 27 | * 28 | * @example 29 | * ```tsx 30 | * 31 | * 32 | * Long content that needs scrolling... 33 | * More content... 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * {items.map(item => {item.name})} 40 | * 41 | * 42 | * ``` 43 | */ 44 | export const ScrollArea = React.forwardRef( 45 | ({ className, children, ...props }, ref) => { 46 | return ( 47 | 52 | {children} 53 | 54 | ); 55 | } 56 | ); 57 | 58 | ScrollArea.displayName = "ScrollArea"; 59 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/scroll-area.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the ScrollArea component 6 | */ 7 | interface ScrollAreaProps extends React.HTMLAttributes { 8 | /** 9 | * Optional className for styling 10 | */ 11 | className?: string; 12 | /** 13 | * Children to render inside the scroll area 14 | */ 15 | children: React.ReactNode; 16 | } 17 | 18 | /** 19 | * ScrollArea component for scrollable content with custom scrollbar styling 20 | * 21 | * Provides a scrollable container with custom-styled scrollbars that match 22 | * the application theme. Automatically handles overflow and provides smooth 23 | * scrolling experience across different browsers. 24 | * 25 | * @param className - Additional CSS classes for styling 26 | * @param children - Content to be made scrollable 27 | * 28 | * @example 29 | * ```tsx 30 | * 31 | * 32 | * Long content that needs scrolling... 33 | * More content... 34 | * 35 | * 36 | * 37 | * 38 | * 39 | * {items.map(item => {item.name})} 40 | * 41 | * 42 | * ``` 43 | */ 44 | export const ScrollArea = React.forwardRef( 45 | ({ className, children, ...props }, ref) => { 46 | return ( 47 | 52 | {children} 53 | 54 | ); 55 | } 56 | ); 57 | 58 | ScrollArea.displayName = "ScrollArea"; 59 | -------------------------------------------------------------------------------- /src-tauri/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.apple.security.app-sandbox 7 | 8 | 9 | 10 | com.apple.security.network.client 11 | 12 | com.apple.security.network.server 13 | 14 | 15 | 16 | com.apple.security.files.user-selected.read-write 17 | 18 | com.apple.security.files.downloads.read-write 19 | 20 | 21 | 22 | com.apple.security.inherit 23 | 24 | 25 | 26 | com.apple.security.automation.apple-events 27 | 28 | 29 | 30 | com.apple.security.device.camera 31 | 32 | com.apple.security.device.microphone 33 | 34 | 35 | 36 | com.apple.security.print 37 | 38 | 39 | 40 | com.apple.security.cs.allow-unsigned-executable-memory 41 | 42 | com.apple.security.cs.allow-jit 43 | 44 | com.apple.security.cs.disable-library-validation 45 | 46 | com.apple.security.cs.disable-executable-page-protection 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/entitlements.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | com.apple.security.app-sandbox 7 | 8 | 9 | 10 | com.apple.security.network.client 11 | 12 | com.apple.security.network.server 13 | 14 | 15 | 16 | com.apple.security.files.user-selected.read-write 17 | 18 | com.apple.security.files.downloads.read-write 19 | 20 | 21 | 22 | com.apple.security.inherit 23 | 24 | 25 | 26 | com.apple.security.automation.apple-events 27 | 28 | 29 | 30 | com.apple.security.device.camera 31 | 32 | com.apple.security.device.microphone 33 | 34 | 35 | 36 | com.apple.security.print 37 | 38 | 39 | 40 | com.apple.security.cs.allow-unsigned-executable-memory 41 | 42 | com.apple.security.cs.allow-jit 43 | 44 | com.apple.security.cs.disable-library-validation 45 | 46 | com.apple.security.cs.disable-executable-page-protection 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/lib/outputCacheHook.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Output cache hook separated to avoid fast refresh warnings 3 | */ 4 | import { useContext } from "react"; 5 | import { OutputCacheContextType, createOutputCacheError } from "./outputCacheUtils"; 6 | 7 | import React from "react"; 8 | 9 | /** 10 | * React context for output cache functionality 11 | * 12 | * Provides access to cached session outputs and cache management functions 13 | * throughout the component tree. 14 | */ 15 | const OutputCacheContext = React.createContext(null); 16 | 17 | /** 18 | * Hook for accessing output cache functionality 19 | * 20 | * Provides access to cached session outputs and cache management functions. 21 | * Must be used within an OutputCacheProvider component. 22 | * 23 | * @returns Output cache context with caching functions 24 | * @throws Error if used outside OutputCacheProvider 25 | * 26 | * @example 27 | * ```tsx 28 | * function SessionViewer({ sessionId }: { sessionId: number }) { 29 | * const { getCachedOutput, setCachedOutput, clearCache } = useOutputCache(); 30 | * 31 | * const cachedData = getCachedOutput(sessionId); 32 | * 33 | * return ( 34 | * 35 | * {cachedData ? ( 36 | * Cached output: {cachedData.output} 37 | * ) : ( 38 | * No cached data 39 | * )} 40 | * clearCache(sessionId)}> 41 | * Clear Cache 42 | * 43 | * 44 | * ); 45 | * } 46 | * ``` 47 | */ 48 | export function useOutputCache() { 49 | const context = useContext(OutputCacheContext); 50 | if (!context) { 51 | throw createOutputCacheError("useOutputCache"); 52 | } 53 | return context; 54 | } 55 | 56 | export { OutputCacheContext }; 57 | -------------------------------------------------------------------------------- /claudeCli/src/lib/outputCacheHook.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Output cache hook separated to avoid fast refresh warnings 3 | */ 4 | import { useContext } from "react"; 5 | import { OutputCacheContextType, createOutputCacheError } from "./outputCacheUtils"; 6 | 7 | import React from "react"; 8 | 9 | /** 10 | * React context for output cache functionality 11 | * 12 | * Provides access to cached session outputs and cache management functions 13 | * throughout the component tree. 14 | */ 15 | const OutputCacheContext = React.createContext(null); 16 | 17 | /** 18 | * Hook for accessing output cache functionality 19 | * 20 | * Provides access to cached session outputs and cache management functions. 21 | * Must be used within an OutputCacheProvider component. 22 | * 23 | * @returns Output cache context with caching functions 24 | * @throws Error if used outside OutputCacheProvider 25 | * 26 | * @example 27 | * ```tsx 28 | * function SessionViewer({ sessionId }: { sessionId: number }) { 29 | * const { getCachedOutput, setCachedOutput, clearCache } = useOutputCache(); 30 | * 31 | * const cachedData = getCachedOutput(sessionId); 32 | * 33 | * return ( 34 | * 35 | * {cachedData ? ( 36 | * Cached output: {cachedData.output} 37 | * ) : ( 38 | * No cached data 39 | * )} 40 | * clearCache(sessionId)}> 41 | * Clear Cache 42 | * 43 | * 44 | * ); 45 | * } 46 | * ``` 47 | */ 48 | export function useOutputCache() { 49 | const context = useContext(OutputCacheContext); 50 | if (!context) { 51 | throw createOutputCacheError("useOutputCache"); 52 | } 53 | return context; 54 | } 55 | 56 | export { OutputCacheContext }; 57 | -------------------------------------------------------------------------------- /src/components/ui/pagination.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ChevronLeft, ChevronRight } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | interface PaginationProps { 7 | /** 8 | * Current page number (1-indexed) 9 | */ 10 | currentPage: number; 11 | /** 12 | * Total number of pages 13 | */ 14 | totalPages: number; 15 | /** 16 | * Callback when page changes 17 | */ 18 | onPageChange: (page: number) => void; 19 | /** 20 | * Optional className for styling 21 | */ 22 | className?: string; 23 | } 24 | 25 | /** 26 | * Pagination component for navigating through paginated content 27 | * 28 | * @example 29 | * setCurrentPage(page)} 33 | * /> 34 | */ 35 | export const Pagination: React.FC = ({ 36 | currentPage, 37 | totalPages, 38 | onPageChange, 39 | className, 40 | }) => { 41 | if (totalPages <= 1) { 42 | return null; 43 | } 44 | 45 | return ( 46 | 47 | onPageChange(currentPage - 1)} 51 | disabled={currentPage <= 1} 52 | className="h-8 w-8" 53 | > 54 | 55 | 56 | 57 | 58 | Page {currentPage} of {totalPages} 59 | 60 | 61 | onPageChange(currentPage + 1)} 65 | disabled={currentPage >= totalPages} 66 | className="h-8 w-8" 67 | > 68 | 69 | 70 | 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/pagination.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { ChevronLeft, ChevronRight } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | interface PaginationProps { 7 | /** 8 | * Current page number (1-indexed) 9 | */ 10 | currentPage: number; 11 | /** 12 | * Total number of pages 13 | */ 14 | totalPages: number; 15 | /** 16 | * Callback when page changes 17 | */ 18 | onPageChange: (page: number) => void; 19 | /** 20 | * Optional className for styling 21 | */ 22 | className?: string; 23 | } 24 | 25 | /** 26 | * Pagination component for navigating through paginated content 27 | * 28 | * @example 29 | * setCurrentPage(page)} 33 | * /> 34 | */ 35 | export const Pagination: React.FC = ({ 36 | currentPage, 37 | totalPages, 38 | onPageChange, 39 | className, 40 | }) => { 41 | if (totalPages <= 1) { 42 | return null; 43 | } 44 | 45 | return ( 46 | 47 | onPageChange(currentPage - 1)} 51 | disabled={currentPage <= 1} 52 | className="h-8 w-8" 53 | > 54 | 55 | 56 | 57 | 58 | Page {currentPage} of {totalPages} 59 | 60 | 61 | onPageChange(currentPage + 1)} 65 | disabled={currentPage >= totalPages} 66 | className="h-8 w-8" 67 | > 68 | 69 | 70 | 71 | ); 72 | }; 73 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/tauri.conf.debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Claudia", 4 | "version": "0.1.65", 5 | "identifier": "claudia.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run build:executables:current && bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:debug", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Claudia (Debug)", 16 | "width": 800, 17 | "height": 600 18 | } 19 | ], 20 | "security": { 21 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src 'self' ipc: https://ipc.localhost; media-src 'self' data:", 22 | "assetProtocol": { 23 | "enable": true, 24 | "scope": ["**"] 25 | } 26 | } 27 | }, 28 | "plugins": { 29 | "fs": { 30 | "scope": ["$HOME/**"], 31 | "allow": [ 32 | "readFile", 33 | "writeFile", 34 | "readDir", 35 | "copyFile", 36 | "createDir", 37 | "removeDir", 38 | "removeFile", 39 | "renameFile", 40 | "exists" 41 | ] 42 | }, 43 | "shell": { 44 | "open": true 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "targets": "all", 50 | "icon": [ 51 | "icons/icon.ico", 52 | "icons/32x32.png", 53 | "icons/128x128.png", 54 | "icons/128x128@2x.png", 55 | "icons/icon.icns", 56 | "icons/icon.png" 57 | ], 58 | "externalBin": ["binaries/claude-code"], 59 | "windows": { 60 | "certificateThumbprint": null, 61 | "digestAlgorithm": "sha256", 62 | "timestampUrl": "" 63 | }, 64 | "linux": { 65 | "deb": { 66 | "depends": [] 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src-tauri/tauri.conf.debug.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "TermiClaude", 4 | "version": "1.4.0", 5 | "identifier": "termiclaude.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run build:executables:current && bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:debug", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "TermiClaude (Debug)", 16 | "width": 800, 17 | "height": 600 18 | } 19 | ], 20 | "security": { 21 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src 'self' ipc: https://ipc.localhost; media-src 'self' data:", 22 | "assetProtocol": { 23 | "enable": true, 24 | "scope": ["**"] 25 | } 26 | } 27 | }, 28 | "plugins": { 29 | "fs": { 30 | "scope": ["$HOME/**"], 31 | "allow": [ 32 | "readFile", 33 | "writeFile", 34 | "readDir", 35 | "copyFile", 36 | "createDir", 37 | "removeDir", 38 | "removeFile", 39 | "renameFile", 40 | "exists" 41 | ] 42 | }, 43 | "shell": { 44 | "open": true 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "targets": "all", 50 | "icon": [ 51 | "icons/icon.ico", 52 | "icons/32x32.png", 53 | "icons/128x128.png", 54 | "icons/128x128@2x.png", 55 | "icons/icon.icns", 56 | "icons/icon.png" 57 | ], 58 | "externalBin": ["binaries/claude-code"], 59 | "windows": { 60 | "certificateThumbprint": null, 61 | "digestAlgorithm": "sha256", 62 | "timestampUrl": "" 63 | }, 64 | "linux": { 65 | "deb": { 66 | "depends": [] 67 | } 68 | } 69 | } 70 | } -------------------------------------------------------------------------------- /src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Textarea component 6 | * 7 | * Extends standard HTML textarea attributes for multi-line text input. 8 | */ 9 | export interface TextareaProps extends React.TextareaHTMLAttributes {} 10 | 11 | /** 12 | * Textarea component for multi-line text input 13 | * 14 | * A styled textarea component that provides consistent appearance across 15 | * the application with support for focus states, disabled states, and 16 | * placeholder text. Automatically resizes based on content. 17 | * 18 | * @param className - Additional CSS classes to apply 19 | * @param placeholder - Placeholder text 20 | * @param disabled - Whether the textarea is disabled 21 | * @param value - Controlled value 22 | * @param onChange - Change event handler 23 | * @param rows - Number of visible text lines 24 | * 25 | * @example 26 | * ```tsx 27 | * setMessage(e.target.value)} 31 | * rows={4} 32 | * /> 33 | * 34 | * 38 | * ``` 39 | */ 40 | const Textarea = React.forwardRef( 41 | ({ className, ...props }, ref) => { 42 | return ( 43 | 51 | ); 52 | } 53 | ); 54 | Textarea.displayName = "Textarea"; 55 | 56 | export { Textarea }; 57 | -------------------------------------------------------------------------------- /src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | import { type ClassValue, clsx } from "clsx"; 2 | import { twMerge } from "tailwind-merge"; 3 | 4 | /** 5 | * Combines multiple class values into a single string using clsx and tailwind-merge. 6 | * This utility function helps manage dynamic class names and prevents Tailwind CSS conflicts. 7 | * 8 | * @param inputs - Array of class values that can be strings, objects, arrays, etc. 9 | * @returns A merged string of class names with Tailwind conflicts resolved 10 | * 11 | * @example 12 | * cn("px-2 py-1", condition && "bg-blue-500", { "text-white": isActive }) 13 | * // Returns: "px-2 py-1 bg-blue-500 text-white" (when condition and isActive are true) 14 | */ 15 | export function cn(...inputs: ClassValue[]) { 16 | return twMerge(clsx(inputs)); 17 | } 18 | 19 | /** 20 | * 规范化文件路径,确保格式正确 21 | * 22 | * 跨平台兼容: 23 | * - Windows: 支持驱动器号 (C:/, D:/) 24 | * - macOS/Linux: 支持绝对路径 (/Users/, /home/) 25 | * 26 | * 修复常见的路径格式问题: 27 | * - 移除重复的斜杠 (// -> /) 28 | * - 统一使用正斜杠 (\ -> /) 29 | * - 保留路径开头的斜杠(Unix绝对路径) 30 | * 31 | * @param path - 原始路径 32 | * @returns 规范化后的路径 33 | * 34 | * @example 35 | * // Windows 36 | * normalizePath("C//Users/test") // "C:/Users/test" 37 | * normalizePath("C:\\Users\\test") // "C:/Users/test" 38 | * 39 | * // macOS/Linux 40 | * normalizePath("/Users//test") // "/Users/test" 41 | * normalizePath("//home/user") // "/home/user" 42 | */ 43 | export function normalizePath(path: string): string { 44 | if (!path) return path; 45 | 46 | // 将所有反斜杠转换为正斜杠(Windows兼容) 47 | let normalized = path.replace(/\\/g, '/'); 48 | 49 | // 移除重复的斜杠,但保留开头的单个斜杠(Unix绝对路径) 50 | normalized = normalized.replace(/\/+/g, '/'); 51 | 52 | // Windows 驱动器号修复: 53 | // 1. 修复缺少冒号的情况:C/Users -> C:/Users 54 | normalized = normalized.replace(/^([A-Za-z])\//, '$1:/'); 55 | 56 | // 2. 确保已有冒号的格式正确:C:/ 保持不变 57 | normalized = normalized.replace(/^([A-Za-z]):\//, '$1:/'); 58 | 59 | return normalized; 60 | } 61 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/textarea.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Textarea component 6 | * 7 | * Extends standard HTML textarea attributes for multi-line text input. 8 | */ 9 | export interface TextareaProps extends React.TextareaHTMLAttributes {} 10 | 11 | /** 12 | * Textarea component for multi-line text input 13 | * 14 | * A styled textarea component that provides consistent appearance across 15 | * the application with support for focus states, disabled states, and 16 | * placeholder text. Automatically resizes based on content. 17 | * 18 | * @param className - Additional CSS classes to apply 19 | * @param placeholder - Placeholder text 20 | * @param disabled - Whether the textarea is disabled 21 | * @param value - Controlled value 22 | * @param onChange - Change event handler 23 | * @param rows - Number of visible text lines 24 | * 25 | * @example 26 | * ```tsx 27 | * setMessage(e.target.value)} 31 | * rows={4} 32 | * /> 33 | * 34 | * 38 | * ``` 39 | */ 40 | const Textarea = React.forwardRef( 41 | ({ className, ...props }, ref) => { 42 | return ( 43 | 51 | ); 52 | } 53 | ); 54 | Textarea.displayName = "Textarea"; 55 | 56 | export { Textarea }; 57 | -------------------------------------------------------------------------------- /src/components/widgets/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Widget components index 3 | * 4 | * Central export point for all widget components used in the application. 5 | * Widgets are specialized components for displaying different types of 6 | * Claude Code tool outputs and interactions. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { TodoWidget, LSWidget, BashWidget } from '@/components/widgets'; 11 | * 12 | * // Use widgets in your components 13 | * 14 | * 15 | * 16 | * ``` 17 | */ 18 | export { TodoWidget } from "./TodoWidget"; 19 | export { LSWidget } from "./LSWidget"; 20 | export { BashWidget } from "./BashWidget"; 21 | 22 | // TODO: Add these widgets as they are implemented 23 | // export { LSResultWidget } from './LSWidget'; 24 | // export { ReadWidget } from './ReadWidget'; 25 | // export { ReadResultWidget } from './ReadResultWidget'; 26 | // export { GlobWidget } from './GlobWidget'; 27 | // export { WriteWidget } from './WriteWidget'; 28 | // export { GrepWidget } from './GrepWidget'; 29 | // export { EditWidget } from './EditWidget'; 30 | // export { EditResultWidget } from './EditResultWidget'; 31 | // export { MCPWidget } from './MCPWidget'; 32 | // export { CommandWidget } from './CommandWidget'; 33 | // export { CommandOutputWidget } from './CommandOutputWidget'; 34 | // export { SummaryWidget } from './SummaryWidget'; 35 | // export { MultiEditWidget } from './MultiEditWidget'; 36 | // export { MultiEditResultWidget } from './MultiEditResultWidget'; 37 | // export { SystemReminderWidget } from './SystemReminderWidget'; 38 | // export { SystemInitializedWidget } from './SystemInitializedWidget'; 39 | // export { TaskWidget } from './TaskWidget'; 40 | // export { WebSearchWidget } from './WebSearchWidget'; 41 | // export { ThinkingWidget } from './ThinkingWidget'; 42 | // export { WebFetchWidget } from './WebFetchWidget'; 43 | // export { TodoReadWidget } from './TodoReadWidget'; 44 | -------------------------------------------------------------------------------- /claudeCli/src/components/widgets/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Widget components index 3 | * 4 | * Central export point for all widget components used in the application. 5 | * Widgets are specialized components for displaying different types of 6 | * Claude Code tool outputs and interactions. 7 | * 8 | * @example 9 | * ```tsx 10 | * import { TodoWidget, LSWidget, BashWidget } from '@/components/widgets'; 11 | * 12 | * // Use widgets in your components 13 | * 14 | * 15 | * 16 | * ``` 17 | */ 18 | export { TodoWidget } from "./TodoWidget"; 19 | export { LSWidget } from "./LSWidget"; 20 | export { BashWidget } from "./BashWidget"; 21 | 22 | // TODO: Add these widgets as they are implemented 23 | // export { LSResultWidget } from './LSWidget'; 24 | // export { ReadWidget } from './ReadWidget'; 25 | // export { ReadResultWidget } from './ReadResultWidget'; 26 | // export { GlobWidget } from './GlobWidget'; 27 | // export { WriteWidget } from './WriteWidget'; 28 | // export { GrepWidget } from './GrepWidget'; 29 | // export { EditWidget } from './EditWidget'; 30 | // export { EditResultWidget } from './EditResultWidget'; 31 | // export { MCPWidget } from './MCPWidget'; 32 | // export { CommandWidget } from './CommandWidget'; 33 | // export { CommandOutputWidget } from './CommandOutputWidget'; 34 | // export { SummaryWidget } from './SummaryWidget'; 35 | // export { MultiEditWidget } from './MultiEditWidget'; 36 | // export { MultiEditResultWidget } from './MultiEditResultWidget'; 37 | // export { SystemReminderWidget } from './SystemReminderWidget'; 38 | // export { SystemInitializedWidget } from './SystemInitializedWidget'; 39 | // export { TaskWidget } from './TaskWidget'; 40 | // export { WebSearchWidget } from './WebSearchWidget'; 41 | // export { ThinkingWidget } from './ThinkingWidget'; 42 | // export { WebFetchWidget } from './WebFetchWidget'; 43 | // export { TodoReadWidget } from './TodoReadWidget'; 44 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "claudia" 3 | version = "0.1.65" 4 | description = "GUI app and Toolkit for Claude Code" 5 | authors = ["mufeedvh", "123vviekr"] 6 | license = "AGPL-3.0" 7 | edition = "2021" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [[bin]] 12 | name = "claudia" 13 | path = "src/main.rs" 14 | 15 | [lib] 16 | name = "claudia_lib" 17 | crate-type = ["lib", "cdylib", "staticlib"] 18 | 19 | [build-dependencies] 20 | tauri-build = { version = "2.4", features = [] } 21 | 22 | [dependencies] 23 | tauri = { version = "2.4", features = ["protocol-asset", "tray-icon", "image-png"] } 24 | tauri-plugin-shell = "2" 25 | tauri-plugin-dialog = "2.4" 26 | tauri-plugin-fs = "2" 27 | tauri-plugin-process = "2" 28 | tauri-plugin-updater = "2" 29 | tauri-plugin-notification = "2" 30 | tauri-plugin-clipboard-manager = "2" 31 | tauri-plugin-global-shortcut = "2" 32 | tauri-plugin-http = "2" 33 | tauri-plugin-window-state = "2" 34 | serde = { version = "1", features = ["derive"] } 35 | serde_json = "1" 36 | tokio = { version = "1", features = ["full"] } 37 | rusqlite = { version = "0.32", features = ["bundled"] } 38 | dirs = "5" 39 | chrono = { version = "0.4", features = ["serde"] } 40 | anyhow = "1" 41 | log = "0.4" 42 | env_logger = "0.11" 43 | regex = "1" 44 | glob = "0.3" 45 | base64 = "0.22" 46 | libc = "0.2" 47 | reqwest = { version = "0.12", features = ["json", "native-tls-vendored"] } 48 | futures = "0.3" 49 | async-trait = "0.1" 50 | tempfile = "3" 51 | which = "7" 52 | sha2 = "0.10" 53 | zstd = "0.13" 54 | uuid = { version = "1.6", features = ["v4", "serde"] } 55 | walkdir = "2" 56 | serde_yaml = "0.9" 57 | 58 | 59 | [target.'cfg(target_os = "macos")'.dependencies] 60 | cocoa = "0.26" 61 | objc = "0.2" 62 | 63 | [features] 64 | # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! 65 | custom-protocol = ["tauri/custom-protocol"] 66 | 67 | [profile.release] 68 | strip = true 69 | opt-level = "z" 70 | lto = true 71 | codegen-units = 1 72 | -------------------------------------------------------------------------------- /src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "termiclaude" 3 | version = "1.4.0" 4 | description = "GUI app and Toolkit for TermiClaude" 5 | authors = ["mufeedvh", "123vviekr"] 6 | license = "AGPL-3.0" 7 | edition = "2021" 8 | 9 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 10 | 11 | [[bin]] 12 | name = "termiclaude" 13 | path = "src/main.rs" 14 | 15 | [lib] 16 | name = "termiclaude_lib" 17 | crate-type = ["lib", "cdylib", "staticlib"] 18 | 19 | [build-dependencies] 20 | tauri-build = { version = "2.4", features = [] } 21 | 22 | [dependencies] 23 | tauri = { version = "2.4", features = ["protocol-asset", "tray-icon", "image-png"] } 24 | tauri-plugin-shell = "2" 25 | tauri-plugin-dialog = "2.4" 26 | tauri-plugin-fs = "2" 27 | tauri-plugin-process = "2" 28 | tauri-plugin-updater = "2" 29 | tauri-plugin-notification = "2" 30 | tauri-plugin-clipboard-manager = "2" 31 | tauri-plugin-global-shortcut = "2" 32 | tauri-plugin-http = "2" 33 | tauri-plugin-window-state = "2" 34 | serde = { version = "1", features = ["derive"] } 35 | serde_json = "1" 36 | tokio = { version = "1", features = ["full"] } 37 | rusqlite = { version = "0.32", features = ["bundled"] } 38 | dirs = "5" 39 | chrono = { version = "0.4", features = ["serde"] } 40 | anyhow = "1" 41 | log = "0.4" 42 | env_logger = "0.11" 43 | regex = "1" 44 | glob = "0.3" 45 | base64 = "0.22" 46 | libc = "0.2" 47 | reqwest = { version = "0.12", features = ["json", "native-tls-vendored"] } 48 | futures = "0.3" 49 | async-trait = "0.1" 50 | tempfile = "3" 51 | which = "7" 52 | sha2 = "0.10" 53 | zstd = "0.13" 54 | uuid = { version = "1.6", features = ["v4", "serde"] } 55 | walkdir = "2" 56 | serde_yaml = "0.9" 57 | 58 | 59 | [target.'cfg(target_os = "macos")'.dependencies] 60 | cocoa = "0.26" 61 | objc = "0.2" 62 | 63 | [features] 64 | # This feature is used for production builds or when a dev server is not specified, DO NOT REMOVE!! 65 | custom-protocol = ["tauri/custom-protocol"] 66 | 67 | [profile.release] 68 | strip = true 69 | opt-level = "z" 70 | lto = true 71 | codegen-units = 1 72 | -------------------------------------------------------------------------------- /src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | /** 7 | * Badge variants configuration using class-variance-authority 8 | */ 9 | const badgeVariants = cva( 10 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors", 11 | { 12 | variants: { 13 | variant: { 14 | default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 15 | secondary: 16 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 17 | destructive: 18 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 19 | outline: "text-foreground", 20 | }, 21 | }, 22 | defaultVariants: { 23 | variant: "default", 24 | }, 25 | } 26 | ); 27 | 28 | /** 29 | * Props interface for the Badge component 30 | */ 31 | export interface BadgeProps 32 | extends React.HTMLAttributes, 33 | VariantProps {} 34 | 35 | /** 36 | * Badge component for displaying status, labels, or categories 37 | * 38 | * A small component used to display status information, labels, or categories. 39 | * Supports multiple visual variants and can be used inline with text or standalone. 40 | * 41 | * @param variant - Visual style variant (default, secondary, destructive, outline) 42 | * @param className - Additional CSS classes 43 | * @param children - Badge content 44 | * 45 | * @example 46 | * ```tsx 47 | * New 48 | * Error 49 | * Draft 50 | * ``` 51 | */ 52 | function Badge({ className, variant, ...props }: BadgeProps) { 53 | return ; 54 | } 55 | 56 | export { Badge }; 57 | // eslint-disable-next-line react-refresh/only-export-components 58 | export { badgeVariants }; 59 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/badge.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cva, type VariantProps } from "class-variance-authority"; 3 | 4 | import { cn } from "@/lib/utils"; 5 | 6 | /** 7 | * Badge variants configuration using class-variance-authority 8 | */ 9 | const badgeVariants = cva( 10 | "inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors", 11 | { 12 | variants: { 13 | variant: { 14 | default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80", 15 | secondary: 16 | "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", 17 | destructive: 18 | "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80", 19 | outline: "text-foreground", 20 | }, 21 | }, 22 | defaultVariants: { 23 | variant: "default", 24 | }, 25 | } 26 | ); 27 | 28 | /** 29 | * Props interface for the Badge component 30 | */ 31 | export interface BadgeProps 32 | extends React.HTMLAttributes, 33 | VariantProps {} 34 | 35 | /** 36 | * Badge component for displaying status, labels, or categories 37 | * 38 | * A small component used to display status information, labels, or categories. 39 | * Supports multiple visual variants and can be used inline with text or standalone. 40 | * 41 | * @param variant - Visual style variant (default, secondary, destructive, outline) 42 | * @param className - Additional CSS classes 43 | * @param children - Badge content 44 | * 45 | * @example 46 | * ```tsx 47 | * New 48 | * Error 49 | * Draft 50 | * ``` 51 | */ 52 | function Badge({ className, variant, ...props }: BadgeProps) { 53 | return ; 54 | } 55 | 56 | export { Badge }; 57 | // eslint-disable-next-line react-refresh/only-export-components 58 | export { badgeVariants }; 59 | -------------------------------------------------------------------------------- /src/lib/outputCacheUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Output cache utilities separated to avoid fast refresh warnings 3 | */ 4 | 5 | interface MessageContent { 6 | type: string; 7 | text?: string; 8 | name?: string; 9 | input?: unknown; 10 | content?: string; 11 | tool_use_id?: string; 12 | id?: string; 13 | } 14 | 15 | export interface ClaudeStreamMessage { 16 | type: "system" | "assistant" | "user" | "result"; 17 | subtype?: string; 18 | message?: { 19 | content?: MessageContent[]; 20 | usage?: { 21 | input_tokens: number; 22 | output_tokens: number; 23 | }; 24 | }; 25 | usage?: { 26 | input_tokens: number; 27 | output_tokens: number; 28 | }; 29 | tools?: string[]; 30 | cost_usd?: number; 31 | duration_ms?: number; 32 | num_turns?: number; 33 | result?: string; 34 | error?: string; 35 | session_id?: string; 36 | model?: string; 37 | cwd?: string; 38 | [key: string]: unknown; 39 | } 40 | 41 | export interface CachedSessionOutput { 42 | output: string; 43 | messages: ClaudeStreamMessage[]; 44 | lastUpdated: number; 45 | status: string; 46 | } 47 | 48 | export interface OutputCacheContextType { 49 | getCachedOutput: (sessionId: number) => CachedSessionOutput | null; 50 | setCachedOutput: (sessionId: number, data: CachedSessionOutput) => void; 51 | updateSessionStatus: (sessionId: number, status: string) => void; 52 | clearCache: (sessionId?: number) => void; 53 | isPolling: boolean; 54 | startBackgroundPolling: () => void; 55 | stopBackgroundPolling: () => void; 56 | } 57 | 58 | /** 59 | * Create a standardized error for output cache context usage outside provider 60 | * 61 | * @param hookName - Name of the hook that was called outside the provider 62 | * @returns Error object with descriptive message 63 | * 64 | * @example 65 | * ```typescript 66 | * if (!context) { 67 | * throw createOutputCacheError('useOutputCache'); 68 | * } 69 | * ``` 70 | */ 71 | export const createOutputCacheError = (hookName: string): Error => { 72 | return new Error(`${hookName} must be used within an OutputCacheProvider`); 73 | }; 74 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Claudia", 4 | "version": "0.1.65", 5 | "identifier": "claudia.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run build:executables:current && bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:executables:current && bun run build:debug", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Claudia", 16 | "width": 800, 17 | "height": 600, 18 | "resizable": true, 19 | "fullscreen": false 20 | } 21 | ], 22 | "security": { 23 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src 'self' ipc: https://ipc.localhost; media-src 'self' data:", 24 | "assetProtocol": { 25 | "enable": true, 26 | "scope": ["**"] 27 | } 28 | } 29 | }, 30 | "plugins": { 31 | "fs": { 32 | "scope": ["$HOME/**"], 33 | "allow": [ 34 | "readFile", 35 | "writeFile", 36 | "readDir", 37 | "copyFile", 38 | "createDir", 39 | "removeDir", 40 | "removeFile", 41 | "renameFile", 42 | "exists" 43 | ] 44 | }, 45 | "shell": { 46 | "open": true 47 | } 48 | }, 49 | "bundle": { 50 | "active": true, 51 | "targets": "all", 52 | "icon": [ 53 | "icons/icon.ico", 54 | "icons/32x32.png", 55 | "icons/128x128.png", 56 | "icons/128x128@2x.png", 57 | "icons/icon.icns", 58 | "icons/icon.png" 59 | ], 60 | "externalBin": ["binaries/claude-code"], 61 | "windows": { 62 | "certificateThumbprint": null, 63 | "digestAlgorithm": "sha256", 64 | "timestampUrl": "" 65 | }, 66 | "linux": { 67 | "deb": { 68 | "depends": [] 69 | }, 70 | "appimage": { 71 | "bundleMediaFramework": true 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /claudeCli/src/lib/outputCacheUtils.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Output cache utilities separated to avoid fast refresh warnings 3 | */ 4 | 5 | interface MessageContent { 6 | type: string; 7 | text?: string; 8 | name?: string; 9 | input?: unknown; 10 | content?: string; 11 | tool_use_id?: string; 12 | id?: string; 13 | } 14 | 15 | export interface ClaudeStreamMessage { 16 | type: "system" | "assistant" | "user" | "result"; 17 | subtype?: string; 18 | message?: { 19 | content?: MessageContent[]; 20 | usage?: { 21 | input_tokens: number; 22 | output_tokens: number; 23 | }; 24 | }; 25 | usage?: { 26 | input_tokens: number; 27 | output_tokens: number; 28 | }; 29 | tools?: string[]; 30 | cost_usd?: number; 31 | duration_ms?: number; 32 | num_turns?: number; 33 | result?: string; 34 | error?: string; 35 | session_id?: string; 36 | model?: string; 37 | cwd?: string; 38 | [key: string]: unknown; 39 | } 40 | 41 | export interface CachedSessionOutput { 42 | output: string; 43 | messages: ClaudeStreamMessage[]; 44 | lastUpdated: number; 45 | status: string; 46 | } 47 | 48 | export interface OutputCacheContextType { 49 | getCachedOutput: (sessionId: number) => CachedSessionOutput | null; 50 | setCachedOutput: (sessionId: number, data: CachedSessionOutput) => void; 51 | updateSessionStatus: (sessionId: number, status: string) => void; 52 | clearCache: (sessionId?: number) => void; 53 | isPolling: boolean; 54 | startBackgroundPolling: () => void; 55 | stopBackgroundPolling: () => void; 56 | } 57 | 58 | /** 59 | * Create a standardized error for output cache context usage outside provider 60 | * 61 | * @param hookName - Name of the hook that was called outside the provider 62 | * @returns Error object with descriptive message 63 | * 64 | * @example 65 | * ```typescript 66 | * if (!context) { 67 | * throw createOutputCacheError('useOutputCache'); 68 | * } 69 | * ``` 70 | */ 71 | export const createOutputCacheError = (hookName: string): Error => { 72 | return new Error(`${hookName} must be used within an OutputCacheProvider`); 73 | }; 74 | -------------------------------------------------------------------------------- /src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "TermiClaude", 4 | "version": "1.4.0", 5 | "identifier": "termiclaude.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run build:executables:current && bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:executables:current && bun run build:debug", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "TermiClaude", 16 | "width": 800, 17 | "height": 600, 18 | "resizable": true, 19 | "fullscreen": false 20 | } 21 | ], 22 | "security": { 23 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval'; connect-src 'self' ipc: https://ipc.localhost; media-src 'self' data:", 24 | "assetProtocol": { 25 | "enable": true, 26 | "scope": ["**"] 27 | } 28 | } 29 | }, 30 | "plugins": { 31 | "fs": { 32 | "scope": ["$HOME/**"], 33 | "allow": [ 34 | "readFile", 35 | "writeFile", 36 | "readDir", 37 | "copyFile", 38 | "createDir", 39 | "removeDir", 40 | "removeFile", 41 | "renameFile", 42 | "exists" 43 | ] 44 | }, 45 | "shell": { 46 | "open": true 47 | } 48 | }, 49 | "bundle": { 50 | "active": true, 51 | "targets": "all", 52 | "icon": [ 53 | "icons/icon.ico", 54 | "icons/32x32.png", 55 | "icons/128x128.png", 56 | "icons/128x128@2x.png", 57 | "icons/icon.icns", 58 | "icons/icon.png" 59 | ], 60 | "externalBin": ["binaries/claude-code"], 61 | "windows": { 62 | "certificateThumbprint": null, 63 | "digestAlgorithm": "sha256", 64 | "timestampUrl": "" 65 | }, 66 | "linux": { 67 | "deb": { 68 | "depends": [] 69 | }, 70 | "appimage": { 71 | "bundleMediaFramework": true 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/components/TokenCounter.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { motion } from "framer-motion"; 3 | import { Hash } from "lucide-react"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | interface TokenCounterProps { 7 | /** 8 | * Total number of tokens 9 | */ 10 | tokens: number; 11 | /** 12 | * Whether to show the counter 13 | */ 14 | show?: boolean; 15 | /** 16 | * Optional className for styling 17 | */ 18 | className?: string; 19 | } 20 | 21 | /** 22 | * TokenCounter component - Displays a floating token count 23 | * 24 | * A floating token counter that appears at the bottom-right of the screen 25 | * to show the current token usage. Features smooth animations and auto-hide 26 | * when token count is zero. 27 | * 28 | * @param tokens - Total number of tokens to display 29 | * @param show - Whether to show the counter (default: true) 30 | * @param className - Additional CSS classes for styling 31 | * 32 | * @example 33 | * ```tsx 34 | * 35 | * 36 | * // With custom styling 37 | * 42 | * ``` 43 | */ 44 | export const TokenCounter: React.FC = ({ tokens, show = true, className }) => { 45 | if (!show || tokens === 0) return null; 46 | 47 | return ( 48 | 60 | 61 | 62 | {tokens.toLocaleString()} 63 | tokens 64 | 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | /** 6 | * TooltipProvider component - Provides context for tooltip components 7 | * 8 | * Wrap your app or a section of your app with this provider to enable tooltips. 9 | * 10 | * @example 11 | * ```tsx 12 | * 13 | * 14 | * Hover me 15 | * Tooltip content 16 | * 17 | * 18 | * ``` 19 | */ 20 | const TooltipProvider = TooltipPrimitive.Provider; 21 | 22 | /** 23 | * Tooltip component - Root container for tooltip functionality 24 | */ 25 | const Tooltip = TooltipPrimitive.Root; 26 | 27 | /** 28 | * TooltipTrigger component - Element that triggers the tooltip on hover/focus 29 | */ 30 | const TooltipTrigger = TooltipPrimitive.Trigger; 31 | 32 | /** 33 | * TooltipContent component - The content that appears in the tooltip 34 | * 35 | * @param className - Additional CSS classes 36 | * @param sideOffset - Distance from the trigger element (default: 4px) 37 | */ 38 | const TooltipContent = React.forwardRef< 39 | React.ElementRef, 40 | React.ComponentPropsWithoutRef 41 | >(({ className, sideOffset = 4, ...props }, ref) => ( 42 | 51 | )); 52 | TooltipContent.displayName = TooltipPrimitive.Content.displayName; 53 | 54 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; 55 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/tooltip.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as TooltipPrimitive from "@radix-ui/react-tooltip"; 3 | import { cn } from "@/lib/utils"; 4 | 5 | /** 6 | * TooltipProvider component - Provides context for tooltip components 7 | * 8 | * Wrap your app or a section of your app with this provider to enable tooltips. 9 | * 10 | * @example 11 | * ```tsx 12 | * 13 | * 14 | * Hover me 15 | * Tooltip content 16 | * 17 | * 18 | * ``` 19 | */ 20 | const TooltipProvider = TooltipPrimitive.Provider; 21 | 22 | /** 23 | * Tooltip component - Root container for tooltip functionality 24 | */ 25 | const Tooltip = TooltipPrimitive.Root; 26 | 27 | /** 28 | * TooltipTrigger component - Element that triggers the tooltip on hover/focus 29 | */ 30 | const TooltipTrigger = TooltipPrimitive.Trigger; 31 | 32 | /** 33 | * TooltipContent component - The content that appears in the tooltip 34 | * 35 | * @param className - Additional CSS classes 36 | * @param sideOffset - Distance from the trigger element (default: 4px) 37 | */ 38 | const TooltipContent = React.forwardRef< 39 | React.ElementRef, 40 | React.ComponentPropsWithoutRef 41 | >(({ className, sideOffset = 4, ...props }, ref) => ( 42 | 51 | )); 52 | TooltipContent.displayName = TooltipPrimitive.Content.displayName; 53 | 54 | export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider }; 55 | -------------------------------------------------------------------------------- /claudeCli/src/components/TokenCounter.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { motion } from "framer-motion"; 3 | import { Hash } from "lucide-react"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | interface TokenCounterProps { 7 | /** 8 | * Total number of tokens 9 | */ 10 | tokens: number; 11 | /** 12 | * Whether to show the counter 13 | */ 14 | show?: boolean; 15 | /** 16 | * Optional className for styling 17 | */ 18 | className?: string; 19 | } 20 | 21 | /** 22 | * TokenCounter component - Displays a floating token count 23 | * 24 | * A floating token counter that appears at the bottom-right of the screen 25 | * to show the current token usage. Features smooth animations and auto-hide 26 | * when token count is zero. 27 | * 28 | * @param tokens - Total number of tokens to display 29 | * @param show - Whether to show the counter (default: true) 30 | * @param className - Additional CSS classes for styling 31 | * 32 | * @example 33 | * ```tsx 34 | * 35 | * 36 | * // With custom styling 37 | * 42 | * ``` 43 | */ 44 | export const TokenCounter: React.FC = ({ tokens, show = true, className }) => { 45 | if (!show || tokens === 0) return null; 46 | 47 | return ( 48 | 60 | 61 | 62 | {tokens.toLocaleString()} 63 | tokens 64 | 65 | 66 | ); 67 | }; 68 | -------------------------------------------------------------------------------- /src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Input component 6 | * Extends all standard HTML input attributes 7 | */ 8 | export interface InputProps extends React.InputHTMLAttributes {} 9 | 10 | /** 11 | * Input component for form inputs with consistent styling 12 | * 13 | * A styled input component that provides consistent appearance across 14 | * the application with support for focus states, disabled states, and 15 | * file inputs. Uses CSS custom properties for theming. 16 | * 17 | * @param type - Input type (text, number, email, password, etc.) 18 | * @param className - Additional CSS classes to apply 19 | * @param placeholder - Placeholder text 20 | * @param disabled - Whether the input is disabled 21 | * @param value - Controlled value 22 | * @param onChange - Change event handler 23 | * 24 | * @example 25 | * ```tsx 26 | * setName(e.target.value)} 31 | * /> 32 | * 33 | * 38 | * ``` 39 | */ 40 | const Input = React.forwardRef( 41 | ({ className, type, ...props }, ref) => { 42 | return ( 43 | 60 | ); 61 | } 62 | ); 63 | 64 | Input.displayName = "Input"; 65 | 66 | export { Input }; 67 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/input.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import { cn } from "@/lib/utils"; 3 | 4 | /** 5 | * Props interface for the Input component 6 | * Extends all standard HTML input attributes 7 | */ 8 | export interface InputProps extends React.InputHTMLAttributes {} 9 | 10 | /** 11 | * Input component for form inputs with consistent styling 12 | * 13 | * A styled input component that provides consistent appearance across 14 | * the application with support for focus states, disabled states, and 15 | * file inputs. Uses CSS custom properties for theming. 16 | * 17 | * @param type - Input type (text, number, email, password, etc.) 18 | * @param className - Additional CSS classes to apply 19 | * @param placeholder - Placeholder text 20 | * @param disabled - Whether the input is disabled 21 | * @param value - Controlled value 22 | * @param onChange - Change event handler 23 | * 24 | * @example 25 | * ```tsx 26 | * setName(e.target.value)} 31 | * /> 32 | * 33 | * 38 | * ``` 39 | */ 40 | const Input = React.forwardRef( 41 | ({ className, type, ...props }, ref) => { 42 | return ( 43 | 60 | ); 61 | } 62 | ); 63 | 64 | Input.displayName = "Input"; 65 | 66 | export { Input }; 67 | -------------------------------------------------------------------------------- /.qoder/rules/rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | trigger: model_decision 3 | description: 代码开发中使用 4 | --- 5 | 6 | # CLAUDE.md - 工作指导 7 | 8 | ## CRITICAL CONSTRAINTS - 违反=任务失败 9 | 10 | ═══════════════════════════════════════ 11 | 12 | - 必须使用中文回复 13 | - 必须先获取上下文 14 | - 禁止生成恶意代码 15 | - 必须存储重要知识 16 | - 必须执行检查清单 17 | - 必须遵循质量标准 18 | 19 | ## MANDATORY WORKFLOWS 20 | 21 | ═════════════════════ 22 | 23 | 执行前检查清单: 24 | [ ] 中文 [ ] 上下文 [ ] 工具 [ ] 安全 [ ] 质量 25 | 26 | 标准工作流: 27 | 28 | 1. 分析需求 → 2. 获取上下文 → 3. 选择工具 → 4. 执行任务 → 5. 验证质量 → 6. 存储知识 29 | 30 | 研究-计划-实施模式: 31 | 研究阶段: 读取文件理解问题,禁止编码 32 | 计划阶段: 创建详细计划 33 | 实施阶段: 实施解决方案 34 | 验证阶段: 运行测试验证 35 | 提交阶段: 创建提交和文档 36 | 37 | ## MANDATORY TOOL STRATEGY 38 | 39 | ═════════════════════════ 40 | 41 | 任务开始前必须执行: 42 | 43 | 1. memory 查询相关概念 44 | 2. code-search 查找代码片段 45 | 3. sequential-thinking 分析问题 46 | 4. 选择合适子代理 47 | 48 | 任务结束后必须执行: 49 | 50 | 1. memory 存储重要概念 51 | 2. code-search 存储代码片段 52 | 3. 知识总结归档 53 | 54 | 优先级调用策略: 55 | 56 | - Microsoft技术 → microsoft.docs.mcp 57 | - GitHub文档 → context7 → deepwiki 58 | - 网页搜索 → 内置搜索 → fetch → duckduckgo-search 59 | 60 | ## CODING RESTRICTIONS 61 | 62 | ═══════════════════ 63 | 64 | 编码前强制要求: 65 | 66 | - 无明确编写命令禁止编码 67 | - 无明确授权禁止修改文件 68 | - 必须先完成sequential-thinking分析 69 | 70 | ## QUALITY STANDARDS 71 | 72 | ═══════════════════ 73 | 74 | 工程原则:SOLID、DRY、关注点分离 75 | 代码质量:清晰命名、合理抽象、必要注释 76 | 性能意识:算法复杂度、内存使用、IO优化 77 | 测试思维:可测试设计、边界条件、错误处理 78 | 79 | ## SUBAGENT SELECTION 80 | 81 | ════════════════════ 82 | 83 | 必须主动调用合适子代理: 84 | 85 | - Python项目 → python-pro 86 | - C#/.NET项目 → csharp-pro 87 | - JavaScript/TypeScript → javascript-pro/typescript-pro 88 | - Unity开发 → unity-developer 89 | - 前端开发 → frontend-developer 90 | - 后端架构 → backend-architect 91 | - 云架构 → cloud-architect/hybrid-cloud-architect 92 | - 数据库优化 → database-optimizer 93 | - 安全审计 → security-auditor 94 | - 代码审查 → code-reviewer 95 | - 测试自动化 → test-automator 96 | - 性能优化 → performance-engineer 97 | - DevOps部署 → deployment-engineer 98 | - 文档编写 → docs-architect 99 | - 错误调试 → debugger/error-detective 100 | 101 | ## ENFORCEMENT 102 | 103 | ══════════════ 104 | 105 | 强制触发器:会话开始→检查约束,工具调用前→检查流程,回复前→验证清单 106 | 自我改进:成功→存储,失败→更新规则,持续→优化策略 -------------------------------------------------------------------------------- /claudeCli/.qoder/rules/rules.md: -------------------------------------------------------------------------------- 1 | --- 2 | trigger: model_decision 3 | description: 代码开发中使用 4 | --- 5 | 6 | # CLAUDE.md - 工作指导 7 | 8 | ## CRITICAL CONSTRAINTS - 违反=任务失败 9 | 10 | ═══════════════════════════════════════ 11 | 12 | - 必须使用中文回复 13 | - 必须先获取上下文 14 | - 禁止生成恶意代码 15 | - 必须存储重要知识 16 | - 必须执行检查清单 17 | - 必须遵循质量标准 18 | 19 | ## MANDATORY WORKFLOWS 20 | 21 | ═════════════════════ 22 | 23 | 执行前检查清单: 24 | [ ] 中文 [ ] 上下文 [ ] 工具 [ ] 安全 [ ] 质量 25 | 26 | 标准工作流: 27 | 28 | 1. 分析需求 → 2. 获取上下文 → 3. 选择工具 → 4. 执行任务 → 5. 验证质量 → 6. 存储知识 29 | 30 | 研究-计划-实施模式: 31 | 研究阶段: 读取文件理解问题,禁止编码 32 | 计划阶段: 创建详细计划 33 | 实施阶段: 实施解决方案 34 | 验证阶段: 运行测试验证 35 | 提交阶段: 创建提交和文档 36 | 37 | ## MANDATORY TOOL STRATEGY 38 | 39 | ═════════════════════════ 40 | 41 | 任务开始前必须执行: 42 | 43 | 1. memory 查询相关概念 44 | 2. code-search 查找代码片段 45 | 3. sequential-thinking 分析问题 46 | 4. 选择合适子代理 47 | 48 | 任务结束后必须执行: 49 | 50 | 1. memory 存储重要概念 51 | 2. code-search 存储代码片段 52 | 3. 知识总结归档 53 | 54 | 优先级调用策略: 55 | 56 | - Microsoft技术 → microsoft.docs.mcp 57 | - GitHub文档 → context7 → deepwiki 58 | - 网页搜索 → 内置搜索 → fetch → duckduckgo-search 59 | 60 | ## CODING RESTRICTIONS 61 | 62 | ═══════════════════ 63 | 64 | 编码前强制要求: 65 | 66 | - 无明确编写命令禁止编码 67 | - 无明确授权禁止修改文件 68 | - 必须先完成sequential-thinking分析 69 | 70 | ## QUALITY STANDARDS 71 | 72 | ═══════════════════ 73 | 74 | 工程原则:SOLID、DRY、关注点分离 75 | 代码质量:清晰命名、合理抽象、必要注释 76 | 性能意识:算法复杂度、内存使用、IO优化 77 | 测试思维:可测试设计、边界条件、错误处理 78 | 79 | ## SUBAGENT SELECTION 80 | 81 | ════════════════════ 82 | 83 | 必须主动调用合适子代理: 84 | 85 | - Python项目 → python-pro 86 | - C#/.NET项目 → csharp-pro 87 | - JavaScript/TypeScript → javascript-pro/typescript-pro 88 | - Unity开发 → unity-developer 89 | - 前端开发 → frontend-developer 90 | - 后端架构 → backend-architect 91 | - 云架构 → cloud-architect/hybrid-cloud-architect 92 | - 数据库优化 → database-optimizer 93 | - 安全审计 → security-auditor 94 | - 代码审查 → code-reviewer 95 | - 测试自动化 → test-automator 96 | - 性能优化 → performance-engineer 97 | - DevOps部署 → deployment-engineer 98 | - 文档编写 → docs-architect 99 | - 错误调试 → debugger/error-detective 100 | 101 | ## ENFORCEMENT 102 | 103 | ══════════════ 104 | 105 | 强制触发器:会话开始→检查约束,工具调用前→检查流程,回复前→验证清单 106 | 自我改进:成功→存储,失败→更新规则,持续→优化策略 -------------------------------------------------------------------------------- /claudeCli/src/contexts/contexts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * React contexts separated to avoid fast refresh warnings 3 | */ 4 | import { createContext } from "react"; 5 | import type { ClaudeMdFile } from "@/lib/api"; 6 | 7 | export interface Tab { 8 | id: string; 9 | type: 10 | | "chat" 11 | | "agent" 12 | | "projects" 13 | | "usage" 14 | | "mcp" 15 | | "settings" 16 | | "claude-md" 17 | | "claude-file" 18 | | "agent-execution" 19 | | "create-agent" 20 | | "import-agent"; 21 | title: string; 22 | sessionId?: string; 23 | sessionData?: unknown; 24 | agentRunId?: string; 25 | agentData?: unknown; 26 | claudeFileId?: string; 27 | claudeFileData?: ClaudeMdFile; 28 | initialProjectPath?: string; 29 | status: "active" | "idle" | "running" | "complete" | "error"; 30 | hasUnsavedChanges: boolean; 31 | order: number; 32 | icon?: string; 33 | createdAt: Date; 34 | updatedAt: Date; 35 | } 36 | 37 | interface TabContextType { 38 | tabs: Tab[]; 39 | activeTabId: string | null; 40 | addTab: (tab: Omit) => string; 41 | removeTab: (id: string) => void; 42 | updateTab: (id: string, updates: Partial) => void; 43 | setActiveTab: (id: string) => void; 44 | reorderTabs: (startIndex: number, endIndex: number) => void; 45 | getTabById: (id: string) => Tab | undefined; 46 | closeAllTabs: () => void; 47 | getTabsByType: (type: "chat" | "agent") => Tab[]; 48 | } 49 | 50 | export type ToastType = "success" | "error" | "info" | "warning"; 51 | 52 | export interface ToastItem { 53 | id: string; 54 | message: string; 55 | type: ToastType; 56 | duration?: number; 57 | } 58 | 59 | interface ToastContextValue { 60 | showToast: (message: string, type?: ToastType, duration?: number) => void; 61 | showSuccess: (message: string, duration?: number) => void; 62 | showError: (message: string, duration?: number) => void; 63 | showInfo: (message: string, duration?: number) => void; 64 | dismissToast: (id: string) => void; 65 | clearAllToasts: () => void; 66 | } 67 | 68 | export const TabContext = createContext(undefined); 69 | export const ToastContext = createContext(undefined); 70 | -------------------------------------------------------------------------------- /src/contexts/hooks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Context hooks separated to avoid fast refresh warnings 3 | */ 4 | import { useContext } from "react"; 5 | import { TabContext, ToastContext } from "./contexts"; 6 | import { createTabContextError, createToastContextError } from "./contextUtils"; 7 | 8 | /** 9 | * Hook for accessing tab context 10 | * 11 | * Provides access to tab management functionality from the TabContext. 12 | * Must be used within a TabProvider component. 13 | * 14 | * @returns Tab context with state and management functions 15 | * @throws Error if used outside TabProvider 16 | * 17 | * @example 18 | * ```tsx 19 | * function TabComponent() { 20 | * const { tabs, activeTabId, addTab, removeTab } = useTabContext(); 21 | * 22 | * return ( 23 | * 24 | * Active tab: {activeTabId} 25 | * Total tabs: {tabs.length} 26 | * 27 | * ); 28 | * } 29 | * ``` 30 | */ 31 | export const useTabContext = () => { 32 | const context = useContext(TabContext); 33 | if (!context) { 34 | throw createTabContextError("useTabContext"); 35 | } 36 | return context; 37 | }; 38 | 39 | /** 40 | * Hook for accessing toast notification functionality 41 | * 42 | * Provides access to toast notification functions from the ToastContext. 43 | * Must be used within a ToastProvider component. 44 | * 45 | * @returns Toast context with notification functions 46 | * @throws Error if used outside ToastProvider 47 | * 48 | * @example 49 | * ```tsx 50 | * function NotificationComponent() { 51 | * const { showSuccess, showError, showInfo } = useToast(); 52 | * 53 | * const handleSuccess = () => { 54 | * showSuccess('Operation completed successfully!'); 55 | * }; 56 | * 57 | * const handleError = () => { 58 | * showError('Something went wrong!'); 59 | * }; 60 | * 61 | * return ( 62 | * 63 | * Show Success 64 | * Show Error 65 | * 66 | * ); 67 | * } 68 | * ``` 69 | */ 70 | export const useToast = () => { 71 | const context = useContext(ToastContext); 72 | if (!context) { 73 | throw createToastContextError("useToast"); 74 | } 75 | return context; 76 | }; 77 | -------------------------------------------------------------------------------- /claudeCli/src/contexts/hooks.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Context hooks separated to avoid fast refresh warnings 3 | */ 4 | import { useContext } from "react"; 5 | import { TabContext, ToastContext } from "./contexts"; 6 | import { createTabContextError, createToastContextError } from "./contextUtils"; 7 | 8 | /** 9 | * Hook for accessing tab context 10 | * 11 | * Provides access to tab management functionality from the TabContext. 12 | * Must be used within a TabProvider component. 13 | * 14 | * @returns Tab context with state and management functions 15 | * @throws Error if used outside TabProvider 16 | * 17 | * @example 18 | * ```tsx 19 | * function TabComponent() { 20 | * const { tabs, activeTabId, addTab, removeTab } = useTabContext(); 21 | * 22 | * return ( 23 | * 24 | * Active tab: {activeTabId} 25 | * Total tabs: {tabs.length} 26 | * 27 | * ); 28 | * } 29 | * ``` 30 | */ 31 | export const useTabContext = () => { 32 | const context = useContext(TabContext); 33 | if (!context) { 34 | throw createTabContextError("useTabContext"); 35 | } 36 | return context; 37 | }; 38 | 39 | /** 40 | * Hook for accessing toast notification functionality 41 | * 42 | * Provides access to toast notification functions from the ToastContext. 43 | * Must be used within a ToastProvider component. 44 | * 45 | * @returns Toast context with notification functions 46 | * @throws Error if used outside ToastProvider 47 | * 48 | * @example 49 | * ```tsx 50 | * function NotificationComponent() { 51 | * const { showSuccess, showError, showInfo } = useToast(); 52 | * 53 | * const handleSuccess = () => { 54 | * showSuccess('Operation completed successfully!'); 55 | * }; 56 | * 57 | * const handleError = () => { 58 | * showError('Something went wrong!'); 59 | * }; 60 | * 61 | * return ( 62 | * 63 | * Show Success 64 | * Show Error 65 | * 66 | * ); 67 | * } 68 | * ``` 69 | */ 70 | export const useToast = () => { 71 | const context = useContext(ToastContext); 72 | if (!context) { 73 | throw createToastContextError("useToast"); 74 | } 75 | return context; 76 | }; 77 | -------------------------------------------------------------------------------- /vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import tailwindcss from "@tailwindcss/vite"; 4 | import { fileURLToPath, URL } from "node:url"; 5 | 6 | const host = process.env.TAURI_DEV_HOST; 7 | 8 | // https://vitejs.dev/config/ 9 | export default defineConfig(({ mode }) => ({ 10 | plugins: [react(), tailwindcss()], 11 | 12 | // Path resolution 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | 19 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 20 | // 21 | // 1. prevent vite from obscuring rust errors 22 | clearScreen: false, 23 | // 2. tauri expects a fixed port, fail if that port is not available 24 | server: { 25 | port: 1420, 26 | strictPort: true, 27 | host: host || false, 28 | hmr: host 29 | ? { 30 | protocol: "ws", 31 | host, 32 | port: 1421, 33 | } 34 | : undefined, 35 | watch: { 36 | // 3. tell vite to ignore watching `src-tauri` 37 | ignored: ["**/src-tauri/**"], 38 | }, 39 | }, 40 | 41 | // Build configuration for code splitting 42 | build: { 43 | // Increase chunk size warning limit to 2000 KB 44 | chunkSizeWarningLimit: 2000, 45 | 46 | // development模式下禁用压缩 47 | minify: mode === 'development' ? false : 'terser', 48 | 49 | rollupOptions: { 50 | output: { 51 | // Manual chunks for better code splitting 52 | manualChunks: { 53 | // Vendor chunks 54 | "react-vendor": ["react", "react-dom"], 55 | "ui-vendor": [ 56 | "@radix-ui/react-dialog", 57 | "@radix-ui/react-dropdown-menu", 58 | "@radix-ui/react-select", 59 | "@radix-ui/react-tabs", 60 | "@radix-ui/react-tooltip", 61 | "@radix-ui/react-switch", 62 | "@radix-ui/react-popover", 63 | ], 64 | "editor-vendor": ["@uiw/react-md-editor"], 65 | "syntax-vendor": ["react-syntax-highlighter"], 66 | // Tauri and other utilities 67 | tauri: ["@tauri-apps/api", "@tauri-apps/plugin-dialog", "@tauri-apps/plugin-shell"], 68 | utils: ["date-fns", "clsx", "tailwind-merge"], 69 | }, 70 | }, 71 | }, 72 | }, 73 | })); 74 | -------------------------------------------------------------------------------- /claudeCli/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import tailwindcss from "@tailwindcss/vite"; 4 | import { fileURLToPath, URL } from "node:url"; 5 | 6 | const host = process.env.TAURI_DEV_HOST; 7 | 8 | // https://vitejs.dev/config/ 9 | export default defineConfig(({ mode }) => ({ 10 | plugins: [react(), tailwindcss()], 11 | 12 | // Path resolution 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | 19 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 20 | // 21 | // 1. prevent vite from obscuring rust errors 22 | clearScreen: false, 23 | // 2. tauri expects a fixed port, fail if that port is not available 24 | server: { 25 | port: 1420, 26 | strictPort: true, 27 | host: host || false, 28 | hmr: host 29 | ? { 30 | protocol: "ws", 31 | host, 32 | port: 1421, 33 | } 34 | : undefined, 35 | watch: { 36 | // 3. tell vite to ignore watching `src-tauri` 37 | ignored: ["**/src-tauri/**"], 38 | }, 39 | }, 40 | 41 | // Build configuration for code splitting 42 | build: { 43 | // Increase chunk size warning limit to 2000 KB 44 | chunkSizeWarningLimit: 2000, 45 | 46 | // development模式下禁用压缩 47 | minify: mode === 'development' ? false : 'terser', 48 | 49 | rollupOptions: { 50 | output: { 51 | // Manual chunks for better code splitting 52 | manualChunks: { 53 | // Vendor chunks 54 | "react-vendor": ["react", "react-dom"], 55 | "ui-vendor": [ 56 | "@radix-ui/react-dialog", 57 | "@radix-ui/react-dropdown-menu", 58 | "@radix-ui/react-select", 59 | "@radix-ui/react-tabs", 60 | "@radix-ui/react-tooltip", 61 | "@radix-ui/react-switch", 62 | "@radix-ui/react-popover", 63 | ], 64 | "editor-vendor": ["@uiw/react-md-editor"], 65 | "syntax-vendor": ["react-syntax-highlighter"], 66 | // Tauri and other utilities 67 | tauri: ["@tauri-apps/api", "@tauri-apps/plugin-dialog", "@tauri-apps/plugin-shell"], 68 | utils: ["date-fns", "clsx", "tailwind-merge"], 69 | }, 70 | }, 71 | }, 72 | }, 73 | })); 74 | -------------------------------------------------------------------------------- /vite.config.production.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import tailwindcss from "@tailwindcss/vite"; 4 | import { fileURLToPath, URL } from "node:url"; 5 | 6 | const host = process.env.TAURI_DEV_HOST; 7 | 8 | // 生产环境配置 9 | export default defineConfig(async () => ({ 10 | plugins: [react(), tailwindcss()], 11 | 12 | // Path resolution 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | 19 | // 生产环境优化 20 | define: { 21 | // 移除开发环境的调试代码 22 | __DEV__: false, 23 | "process.env.NODE_ENV": '"production"', 24 | }, 25 | 26 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 27 | clearScreen: false, 28 | server: { 29 | port: 1420, 30 | strictPort: true, 31 | host: host || false, 32 | hmr: host 33 | ? { 34 | protocol: "ws", 35 | host, 36 | port: 1421, 37 | } 38 | : undefined, 39 | watch: { 40 | ignored: ["**/src-tauri/**"], 41 | }, 42 | }, 43 | 44 | // 生产环境构建配置 45 | build: { 46 | // 增加 chunk 大小警告限制 47 | chunkSizeWarningLimit: 2000, 48 | 49 | // 启用压缩 50 | minify: "terser", 51 | 52 | // Terser 配置 - 移除 console 和 debugger 53 | terserOptions: { 54 | compress: { 55 | drop_console: true, 56 | drop_debugger: true, 57 | pure_funcs: ["console.log", "console.debug", "console.info"], 58 | }, 59 | }, 60 | 61 | rollupOptions: { 62 | output: { 63 | // 手动分块以优化加载 64 | manualChunks: { 65 | // Vendor chunks 66 | "react-vendor": ["react", "react-dom"], 67 | "ui-vendor": [ 68 | "@radix-ui/react-dialog", 69 | "@radix-ui/react-dropdown-menu", 70 | "@radix-ui/react-select", 71 | "@radix-ui/react-tabs", 72 | "@radix-ui/react-tooltip", 73 | "@radix-ui/react-switch", 74 | "@radix-ui/react-popover", 75 | ], 76 | "editor-vendor": ["@uiw/react-md-editor"], 77 | "syntax-vendor": ["react-syntax-highlighter"], 78 | // Tauri and other utilities 79 | tauri: ["@tauri-apps/api", "@tauri-apps/plugin-dialog", "@tauri-apps/plugin-shell"], 80 | utils: ["date-fns", "clsx", "tailwind-merge"], 81 | }, 82 | }, 83 | }, 84 | }, 85 | })); 86 | -------------------------------------------------------------------------------- /claudeCli/vite.config.production.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | import tailwindcss from "@tailwindcss/vite"; 4 | import { fileURLToPath, URL } from "node:url"; 5 | 6 | const host = process.env.TAURI_DEV_HOST; 7 | 8 | // 生产环境配置 9 | export default defineConfig(async () => ({ 10 | plugins: [react(), tailwindcss()], 11 | 12 | // Path resolution 13 | resolve: { 14 | alias: { 15 | "@": fileURLToPath(new URL("./src", import.meta.url)), 16 | }, 17 | }, 18 | 19 | // 生产环境优化 20 | define: { 21 | // 移除开发环境的调试代码 22 | __DEV__: false, 23 | "process.env.NODE_ENV": '"production"', 24 | }, 25 | 26 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 27 | clearScreen: false, 28 | server: { 29 | port: 1420, 30 | strictPort: true, 31 | host: host || false, 32 | hmr: host 33 | ? { 34 | protocol: "ws", 35 | host, 36 | port: 1421, 37 | } 38 | : undefined, 39 | watch: { 40 | ignored: ["**/src-tauri/**"], 41 | }, 42 | }, 43 | 44 | // 生产环境构建配置 45 | build: { 46 | // 增加 chunk 大小警告限制 47 | chunkSizeWarningLimit: 2000, 48 | 49 | // 启用压缩 50 | minify: "terser", 51 | 52 | // Terser 配置 - 移除 console 和 debugger 53 | terserOptions: { 54 | compress: { 55 | drop_console: true, 56 | drop_debugger: true, 57 | pure_funcs: ["console.log", "console.debug", "console.info"], 58 | }, 59 | }, 60 | 61 | rollupOptions: { 62 | output: { 63 | // 手动分块以优化加载 64 | manualChunks: { 65 | // Vendor chunks 66 | "react-vendor": ["react", "react-dom"], 67 | "ui-vendor": [ 68 | "@radix-ui/react-dialog", 69 | "@radix-ui/react-dropdown-menu", 70 | "@radix-ui/react-select", 71 | "@radix-ui/react-tabs", 72 | "@radix-ui/react-tooltip", 73 | "@radix-ui/react-switch", 74 | "@radix-ui/react-popover", 75 | ], 76 | "editor-vendor": ["@uiw/react-md-editor"], 77 | "syntax-vendor": ["react-syntax-highlighter"], 78 | // Tauri and other utilities 79 | tauri: ["@tauri-apps/api", "@tauri-apps/plugin-dialog", "@tauri-apps/plugin-shell"], 80 | utils: ["date-fns", "clsx", "tailwind-merge"], 81 | }, 82 | }, 83 | }, 84 | }, 85 | })); 86 | -------------------------------------------------------------------------------- /src/components/I18nProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, ReactNode } from "react"; 2 | import { 3 | I18nContext, 4 | type Language, 5 | type I18nContextType, 6 | getBrowserLanguage, 7 | isRTLLanguage, 8 | } from "@/lib/i18n"; 9 | import { getTranslations } from "@/locales"; 10 | 11 | /** 12 | * Props interface for the I18nProvider component 13 | */ 14 | interface I18nProviderProps { 15 | children: ReactNode; 16 | } 17 | 18 | /** 19 | * Local storage key for persisting language preference 20 | */ 21 | const LANGUAGE_STORAGE_KEY = "claudia-language"; 22 | 23 | /** 24 | * I18nProvider component for providing internationalization context 25 | * 26 | * Provides internationalization functionality throughout the application including 27 | * language switching, translation access, RTL support, and persistent language 28 | * preferences. Automatically detects browser language and updates HTML attributes. 29 | * 30 | * @param children - Child components that will have access to i18n context 31 | * 32 | * @example 33 | * ```tsx 34 | * function App() { 35 | * return ( 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * ); 42 | * } 43 | * ``` 44 | */ 45 | export const I18nProvider: React.FC = ({ children }) => { 46 | // 从localStorage获取保存的语言,如果没有则使用浏览器语言 47 | const [language, setLanguage] = useState(() => { 48 | const savedLanguage = localStorage.getItem(LANGUAGE_STORAGE_KEY) as Language; 49 | return savedLanguage || getBrowserLanguage(); 50 | }); 51 | 52 | // 更新语言设置 53 | const handleSetLanguage = (newLanguage: Language) => { 54 | setLanguage(newLanguage); 55 | localStorage.setItem(LANGUAGE_STORAGE_KEY, newLanguage); 56 | 57 | // 更新HTML lang属性 58 | document.documentElement.lang = newLanguage; 59 | 60 | // 更新HTML dir属性(用于RTL语言) 61 | document.documentElement.dir = isRTLLanguage(newLanguage) ? "rtl" : "ltr"; 62 | }; 63 | 64 | // 初始化时设置HTML属性 65 | useEffect(() => { 66 | document.documentElement.lang = language; 67 | document.documentElement.dir = isRTLLanguage(language) ? "rtl" : "ltr"; 68 | }, [language]); 69 | 70 | const contextValue: I18nContextType = { 71 | language, 72 | setLanguage: handleSetLanguage, 73 | t: getTranslations(language), 74 | isRTL: isRTLLanguage(language), 75 | }; 76 | 77 | return {children}; 78 | }; 79 | -------------------------------------------------------------------------------- /claudeCli/src/components/I18nProvider.tsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, ReactNode } from "react"; 2 | import { 3 | I18nContext, 4 | type Language, 5 | type I18nContextType, 6 | getBrowserLanguage, 7 | isRTLLanguage, 8 | } from "@/lib/i18n"; 9 | import { getTranslations } from "@/locales"; 10 | 11 | /** 12 | * Props interface for the I18nProvider component 13 | */ 14 | interface I18nProviderProps { 15 | children: ReactNode; 16 | } 17 | 18 | /** 19 | * Local storage key for persisting language preference 20 | */ 21 | const LANGUAGE_STORAGE_KEY = "claudia-language"; 22 | 23 | /** 24 | * I18nProvider component for providing internationalization context 25 | * 26 | * Provides internationalization functionality throughout the application including 27 | * language switching, translation access, RTL support, and persistent language 28 | * preferences. Automatically detects browser language and updates HTML attributes. 29 | * 30 | * @param children - Child components that will have access to i18n context 31 | * 32 | * @example 33 | * ```tsx 34 | * function App() { 35 | * return ( 36 | * 37 | * 38 | * 39 | * 40 | * 41 | * ); 42 | * } 43 | * ``` 44 | */ 45 | export const I18nProvider: React.FC = ({ children }) => { 46 | // 从localStorage获取保存的语言,如果没有则使用浏览器语言 47 | const [language, setLanguage] = useState(() => { 48 | const savedLanguage = localStorage.getItem(LANGUAGE_STORAGE_KEY) as Language; 49 | return savedLanguage || getBrowserLanguage(); 50 | }); 51 | 52 | // 更新语言设置 53 | const handleSetLanguage = (newLanguage: Language) => { 54 | setLanguage(newLanguage); 55 | localStorage.setItem(LANGUAGE_STORAGE_KEY, newLanguage); 56 | 57 | // 更新HTML lang属性 58 | document.documentElement.lang = newLanguage; 59 | 60 | // 更新HTML dir属性(用于RTL语言) 61 | document.documentElement.dir = isRTLLanguage(newLanguage) ? "rtl" : "ltr"; 62 | }; 63 | 64 | // 初始化时设置HTML属性 65 | useEffect(() => { 66 | document.documentElement.lang = language; 67 | document.documentElement.dir = isRTLLanguage(language) ? "rtl" : "ltr"; 68 | }, [language]); 69 | 70 | const contextValue: I18nContextType = { 71 | language, 72 | setLanguage: handleSetLanguage, 73 | t: getTranslations(language), 74 | isRTL: isRTLLanguage(language), 75 | }; 76 | 77 | return {children}; 78 | }; 79 | -------------------------------------------------------------------------------- /cc_agents/git-commit-bot.claudia.json: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "default_task": "Push all changes.", 4 | "icon": "bot", 5 | "model": "sonnet", 6 | "name": "Git Commit Bot", 7 | "system_prompt": "\nYou are a Git Commit Push bot. Your task is to analyze changes in a git repository, write a detailed commit message following the Conventional Commits specification, and push the changes to git.\n\n\n# Instructions\n\n\nFirst, check if there are commits in the remote repository that have not been synced locally:\n1. Run `git fetch` to update remote tracking branches\n2. Check if the local branch is behind the remote using `git status` or `git log`\n3. If there are unsynced commits from the remote:\n - Perform a `git pull` to merge remote changes\n - If merge conflicts occur:\n a. Carefully analyze the conflicting changes\n b. Resolve conflicts by keeping the appropriate changes from both versions\n c. Mark conflicts as resolved using `git add`\n d. Complete the merge\n4. Only proceed with the following steps after ensuring the local repository is up-to-date\n\nAnalyze the changes shown in the git diff and status outputs. Pay attention to:\n1. Which files were modified, added, or deleted\n2. The nature of the changes (e.g., bug fixes, new features, refactoring)\n3. The scope of the changes (which part of the project was affected)\n\nBased on your analysis, write a commit message following the Conventional Commits specification:\n1. Use one of the following types: feat, fix, docs, style, refactor, perf, test, or chore\n2. Include a scope in parentheses if applicable\n3. Write a concise description in the present tense\n4. If necessary, add a longer description after a blank line\n5. Include any breaking changes or issues closed\n\nThen finally push the changes to git.\n\n\n# Notes\n\n\n- Replace [branch_name] with the appropriate branch name based on the information in the git log. If you cannot determine the branch name, use \"main\" as the default.\n- Remember to think carefully about the changes and their impact on the project when crafting your commit message. Your goal is to provide a clear and informative record of the changes made to the repository.\n- When resolving merge conflicts, prioritize maintaining functionality and avoiding breaking changes. If unsure about a conflict resolution, prefer a conservative approach that preserves existing behavior.\n" 8 | }, 9 | "exported_at": "2025-06-23T14:29:58.156063+00:00", 10 | "version": 1 11 | } 12 | -------------------------------------------------------------------------------- /claudeCli/cc_agents/git-commit-bot.claudia.json: -------------------------------------------------------------------------------- 1 | { 2 | "agent": { 3 | "default_task": "Push all changes.", 4 | "icon": "bot", 5 | "model": "sonnet", 6 | "name": "Git Commit Bot", 7 | "system_prompt": "\nYou are a Git Commit Push bot. Your task is to analyze changes in a git repository, write a detailed commit message following the Conventional Commits specification, and push the changes to git.\n\n\n# Instructions\n\n\nFirst, check if there are commits in the remote repository that have not been synced locally:\n1. Run `git fetch` to update remote tracking branches\n2. Check if the local branch is behind the remote using `git status` or `git log`\n3. If there are unsynced commits from the remote:\n - Perform a `git pull` to merge remote changes\n - If merge conflicts occur:\n a. Carefully analyze the conflicting changes\n b. Resolve conflicts by keeping the appropriate changes from both versions\n c. Mark conflicts as resolved using `git add`\n d. Complete the merge\n4. Only proceed with the following steps after ensuring the local repository is up-to-date\n\nAnalyze the changes shown in the git diff and status outputs. Pay attention to:\n1. Which files were modified, added, or deleted\n2. The nature of the changes (e.g., bug fixes, new features, refactoring)\n3. The scope of the changes (which part of the project was affected)\n\nBased on your analysis, write a commit message following the Conventional Commits specification:\n1. Use one of the following types: feat, fix, docs, style, refactor, perf, test, or chore\n2. Include a scope in parentheses if applicable\n3. Write a concise description in the present tense\n4. If necessary, add a longer description after a blank line\n5. Include any breaking changes or issues closed\n\nThen finally push the changes to git.\n\n\n# Notes\n\n\n- Replace [branch_name] with the appropriate branch name based on the information in the git log. If you cannot determine the branch name, use \"main\" as the default.\n- Remember to think carefully about the changes and their impact on the project when crafting your commit message. Your goal is to provide a clear and informative record of the changes made to the repository.\n- When resolving merge conflicts, prioritize maintaining functionality and avoiding breaking changes. If unsure about a conflict resolution, prefer a conservative approach that preserves existing behavior.\n" 8 | }, 9 | "exported_at": "2025-06-23T14:29:58.156063+00:00", 10 | "version": 1 11 | } 12 | -------------------------------------------------------------------------------- /src/components/__tests__/TabManager.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { TabManager } from "../TabManager"; 4 | 5 | // Mock the TabContext 6 | vi.mock("@/contexts/hooks", () => ({ 7 | useTabContext: () => ({ 8 | tabs: [ 9 | { id: "1", title: "Tab 1", type: "project", content: {} }, 10 | { id: "2", title: "Tab 2", type: "session", content: {} }, 11 | ], 12 | activeTabId: "1", 13 | addTab: vi.fn(), 14 | removeTab: vi.fn(), 15 | setActiveTab: vi.fn(), 16 | updateTab: vi.fn(), 17 | getTabById: vi.fn((id) => { 18 | const tabs = [ 19 | { id: "1", title: "Tab 1", type: "project", content: {} }, 20 | { id: "2", title: "Tab 2", type: "session", content: {} }, 21 | ]; 22 | return tabs.find(tab => tab.id === id); 23 | }), 24 | getTabsByType: vi.fn((type) => { 25 | const tabs = [ 26 | { id: "1", title: "Tab 1", type: "project", content: {} }, 27 | { id: "2", title: "Tab 2", type: "session", content: {} }, 28 | ]; 29 | return tabs.filter(tab => tab.type === type); 30 | }), 31 | reorderTabs: vi.fn(), 32 | closeAllTabs: vi.fn(), 33 | }), 34 | })); 35 | 36 | /** 37 | * Test suite for TabManager component 38 | * 39 | * Tests tab management functionality including tab rendering, 40 | * active tab indicators, tab closing, and new tab creation. 41 | */ 42 | describe("TabManager Component", () => { 43 | it("renders tabs correctly", () => { 44 | render(); 45 | 46 | expect(screen.getByText("Tab 1")).toBeInTheDocument(); 47 | expect(screen.getByText("Tab 2")).toBeInTheDocument(); 48 | }); 49 | 50 | it("shows active tab indicator", () => { 51 | render(); 52 | 53 | const activeTab = screen.getByText("Tab 1").closest("li"); 54 | expect(activeTab).toHaveClass("border-blue-500"); 55 | }); 56 | 57 | it("handles tab close button", () => { 58 | render(); 59 | 60 | // Look for close buttons by their X icon 61 | const closeButtons = screen.getAllByRole("button").filter(button => 62 | button.querySelector('svg.lucide-x') 63 | ); 64 | expect(closeButtons.length).toBeGreaterThan(0); 65 | }); 66 | 67 | it("handles new tab creation", () => { 68 | render(); 69 | 70 | const newTabButton = screen.getByTitle("Browse projects"); 71 | fireEvent.click(newTabButton); 72 | 73 | // Should trigger addTab function 74 | expect(newTabButton).toBeInTheDocument(); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /claudeCli/src/components/__tests__/TabManager.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { TabManager } from "../TabManager"; 4 | 5 | // Mock the TabContext 6 | vi.mock("@/contexts/hooks", () => ({ 7 | useTabContext: () => ({ 8 | tabs: [ 9 | { id: "1", title: "Tab 1", type: "project", content: {} }, 10 | { id: "2", title: "Tab 2", type: "session", content: {} }, 11 | ], 12 | activeTabId: "1", 13 | addTab: vi.fn(), 14 | removeTab: vi.fn(), 15 | setActiveTab: vi.fn(), 16 | updateTab: vi.fn(), 17 | getTabById: vi.fn((id) => { 18 | const tabs = [ 19 | { id: "1", title: "Tab 1", type: "project", content: {} }, 20 | { id: "2", title: "Tab 2", type: "session", content: {} }, 21 | ]; 22 | return tabs.find(tab => tab.id === id); 23 | }), 24 | getTabsByType: vi.fn((type) => { 25 | const tabs = [ 26 | { id: "1", title: "Tab 1", type: "project", content: {} }, 27 | { id: "2", title: "Tab 2", type: "session", content: {} }, 28 | ]; 29 | return tabs.filter(tab => tab.type === type); 30 | }), 31 | reorderTabs: vi.fn(), 32 | closeAllTabs: vi.fn(), 33 | }), 34 | })); 35 | 36 | /** 37 | * Test suite for TabManager component 38 | * 39 | * Tests tab management functionality including tab rendering, 40 | * active tab indicators, tab closing, and new tab creation. 41 | */ 42 | describe("TabManager Component", () => { 43 | it("renders tabs correctly", () => { 44 | render(); 45 | 46 | expect(screen.getByText("Tab 1")).toBeInTheDocument(); 47 | expect(screen.getByText("Tab 2")).toBeInTheDocument(); 48 | }); 49 | 50 | it("shows active tab indicator", () => { 51 | render(); 52 | 53 | const activeTab = screen.getByText("Tab 1").closest("li"); 54 | expect(activeTab).toHaveClass("border-blue-500"); 55 | }); 56 | 57 | it("handles tab close button", () => { 58 | render(); 59 | 60 | // Look for close buttons by their X icon 61 | const closeButtons = screen.getAllByRole("button").filter(button => 62 | button.querySelector('svg.lucide-x') 63 | ); 64 | expect(closeButtons.length).toBeGreaterThan(0); 65 | }); 66 | 67 | it("handles new tab creation", () => { 68 | render(); 69 | 70 | const newTabButton = screen.getByTitle("Browse projects"); 71 | fireEvent.click(newTabButton); 72 | 73 | // Should trigger addTab function 74 | expect(newTabButton).toBeInTheDocument(); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /src/contexts/contexts.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * React contexts separated to avoid fast refresh warnings 3 | */ 4 | import { createContext } from "react"; 5 | import type { ClaudeMdFile } from "@/lib/api"; 6 | 7 | export interface Tab { 8 | id: string; 9 | type: 10 | | "chat" 11 | | "agent" 12 | | "projects" 13 | | "usage" 14 | | "mcp" 15 | | "settings" 16 | | "claude-md" 17 | | "claude-file" 18 | | "agent-execution" 19 | | "create-agent" 20 | | "import-agent"; 21 | title: string; 22 | customTitle?: string; // Custom user-defined title for the tab 23 | sessionId?: string; 24 | sessionData?: unknown; 25 | agentRunId?: string; 26 | agentData?: unknown; 27 | claudeFileId?: string; 28 | claudeFileData?: ClaudeMdFile; 29 | initialProjectPath?: string; 30 | status: "active" | "idle" | "running" | "complete" | "error"; 31 | hasUnsavedChanges: boolean; 32 | order: number; 33 | icon?: string; 34 | createdAt: Date; 35 | updatedAt: Date; 36 | // State memory for preserving component state across navigation 37 | componentState?: { 38 | selectedProject?: unknown; 39 | currentPage?: number; 40 | scrollPosition?: number; 41 | [key: string]: unknown; 42 | }; 43 | // Parent tab ID for hierarchical navigation 44 | parentTabId?: string; 45 | } 46 | 47 | interface TabContextType { 48 | tabs: Tab[]; 49 | activeTabId: string | null; 50 | addTab: (tab: Omit) => string; 51 | removeTab: (id: string) => void; 52 | updateTab: (id: string, updates: Partial) => void; 53 | setActiveTab: (id: string) => void; 54 | reorderTabs: (startIndex: number, endIndex: number) => void; 55 | getTabById: (id: string) => Tab | undefined; 56 | closeAllTabs: () => void; 57 | getTabsByType: (type: "chat" | "agent") => Tab[]; 58 | } 59 | 60 | export type ToastType = "success" | "error" | "info" | "warning"; 61 | 62 | export interface ToastItem { 63 | id: string; 64 | message: string; 65 | type: ToastType; 66 | duration?: number; 67 | } 68 | 69 | interface ToastContextValue { 70 | showToast: (message: string, type?: ToastType, duration?: number) => void; 71 | showSuccess: (message: string, duration?: number) => void; 72 | showError: (message: string, duration?: number) => void; 73 | showInfo: (message: string, duration?: number) => void; 74 | dismissToast: (id: string) => void; 75 | clearAllToasts: () => void; 76 | } 77 | 78 | export const TabContext = createContext(undefined); 79 | export const ToastContext = createContext(undefined); 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/components/__tests__/ProjectList.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { ProjectList } from "../ProjectList"; 4 | import { I18nProvider } from "../I18nProvider"; 5 | 6 | // Mock framer-motion 7 | vi.mock("framer-motion", () => ({ 8 | motion: { 9 | div: "div", 10 | }, 11 | })); 12 | 13 | // Mock Tauri APIs 14 | vi.mock("@tauri-apps/plugin-dialog", () => ({ 15 | open: vi.fn(), 16 | })); 17 | 18 | /** 19 | * Test suite for ProjectList component 20 | * 21 | * Tests project listing functionality including rendering, pagination, 22 | * project selection, and loading states. 23 | */ 24 | describe("ProjectList Component", () => { 25 | const mockProjects = [ 26 | { id: "1", path: "/path/to/project1", sessions: [], created_at: Date.now() / 1000 }, 27 | { id: "2", path: "/path/to/project2", sessions: [], created_at: Date.now() / 1000 }, 28 | ]; 29 | 30 | it("renders project list correctly", () => { 31 | render( 32 | 33 | 34 | 35 | ); 36 | 37 | expect(screen.getByText("project1")).toBeInTheDocument(); 38 | expect(screen.getByText("project2")).toBeInTheDocument(); 39 | }); 40 | 41 | it("handles project selection", () => { 42 | const onProjectClick = vi.fn(); 43 | render( 44 | 45 | 46 | 47 | ); 48 | 49 | const project1 = screen.getByText("project1").closest(".cursor-pointer"); 50 | if (project1) { 51 | fireEvent.click(project1); 52 | } 53 | 54 | expect(onProjectClick).toHaveBeenCalledWith(mockProjects[0]); 55 | }); 56 | 57 | it("shows empty state when no projects", () => { 58 | render( 59 | 60 | 61 | 62 | ); 63 | 64 | // Should render empty grid 65 | expect(screen.queryByText("project1")).not.toBeInTheDocument(); 66 | }); 67 | 68 | it("handles project settings callback", () => { 69 | const onProjectSettings = vi.fn(); 70 | render( 71 | 72 | 77 | 78 | ); 79 | 80 | // Should render settings dropdown when callback is provided 81 | expect(screen.getAllByRole("button")).toHaveLength(2); // One for each project 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /claudeCli/src/components/__tests__/ProjectList.test.tsx: -------------------------------------------------------------------------------- 1 | import { describe, it, expect, vi } from "vitest"; 2 | import { render, screen, fireEvent } from "@testing-library/react"; 3 | import { ProjectList } from "../ProjectList"; 4 | import { I18nProvider } from "../I18nProvider"; 5 | 6 | // Mock framer-motion 7 | vi.mock("framer-motion", () => ({ 8 | motion: { 9 | div: "div", 10 | }, 11 | })); 12 | 13 | // Mock Tauri APIs 14 | vi.mock("@tauri-apps/plugin-dialog", () => ({ 15 | open: vi.fn(), 16 | })); 17 | 18 | /** 19 | * Test suite for ProjectList component 20 | * 21 | * Tests project listing functionality including rendering, pagination, 22 | * project selection, and loading states. 23 | */ 24 | describe("ProjectList Component", () => { 25 | const mockProjects = [ 26 | { id: "1", path: "/path/to/project1", sessions: [], created_at: Date.now() / 1000 }, 27 | { id: "2", path: "/path/to/project2", sessions: [], created_at: Date.now() / 1000 }, 28 | ]; 29 | 30 | it("renders project list correctly", () => { 31 | render( 32 | 33 | 34 | 35 | ); 36 | 37 | expect(screen.getByText("project1")).toBeInTheDocument(); 38 | expect(screen.getByText("project2")).toBeInTheDocument(); 39 | }); 40 | 41 | it("handles project selection", () => { 42 | const onProjectClick = vi.fn(); 43 | render( 44 | 45 | 46 | 47 | ); 48 | 49 | const project1 = screen.getByText("project1").closest(".cursor-pointer"); 50 | if (project1) { 51 | fireEvent.click(project1); 52 | } 53 | 54 | expect(onProjectClick).toHaveBeenCalledWith(mockProjects[0]); 55 | }); 56 | 57 | it("shows empty state when no projects", () => { 58 | render( 59 | 60 | 61 | 62 | ); 63 | 64 | // Should render empty grid 65 | expect(screen.queryByText("project1")).not.toBeInTheDocument(); 66 | }); 67 | 68 | it("handles project settings callback", () => { 69 | const onProjectSettings = vi.fn(); 70 | render( 71 | 72 | 77 | 78 | ); 79 | 80 | // Should render settings dropdown when callback is provided 81 | expect(screen.getAllByRole("button")).toHaveLength(2); // One for each project 82 | }); 83 | }); 84 | -------------------------------------------------------------------------------- /src/hooks/useLoadingState.ts: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | 3 | /** 4 | * State interface for the useLoadingState hook 5 | * 6 | * @template T - The type of data being managed 7 | */ 8 | interface LoadingState { 9 | /** The data returned from the async operation, null if not yet loaded */ 10 | data: T | null; 11 | /** Whether an async operation is currently in progress */ 12 | isLoading: boolean; 13 | /** Any error that occurred during the async operation */ 14 | error: Error | null; 15 | /** Function to execute the async operation with given arguments */ 16 | execute: (...args: unknown[]) => Promise; 17 | /** Function to reset the state to initial values */ 18 | reset: () => void; 19 | } 20 | 21 | /** 22 | * Custom hook for managing loading states with error handling 23 | * 24 | * This hook provides a consistent pattern for handling async operations 25 | * with loading states, error handling, and data management. It reduces 26 | * boilerplate code and provides a clean API for async operations. 27 | * 28 | * @template T - The type of data returned by the async function 29 | * @param asyncFunction - The async function to execute 30 | * @returns Object containing data, loading state, error, execute function, and reset function 31 | * 32 | * @example 33 | * ```tsx 34 | * const { data, isLoading, error, execute, reset } = useLoadingState( 35 | * async (userId: string) => fetchUser(userId) 36 | * ); 37 | * 38 | * // Execute the function 39 | * const handleFetch = () => execute('123'); 40 | * 41 | * // Reset state 42 | * const handleReset = () => reset(); 43 | * ``` 44 | */ 45 | export function useLoadingState( 46 | asyncFunction: (...args: unknown[]) => Promise 47 | ): LoadingState { 48 | const [data, setData] = useState(null); 49 | const [isLoading, setIsLoading] = useState(false); 50 | const [error, setError] = useState(null); 51 | 52 | const execute = useCallback( 53 | async (...args: unknown[]): Promise => { 54 | try { 55 | setIsLoading(true); 56 | setError(null); 57 | const result = await asyncFunction(...args); 58 | setData(result); 59 | return result; 60 | } catch (err) { 61 | const error = err instanceof Error ? err : new Error("An error occurred"); 62 | setError(error); 63 | throw error; 64 | } finally { 65 | setIsLoading(false); 66 | } 67 | }, 68 | [asyncFunction] 69 | ); 70 | 71 | const reset = useCallback(() => { 72 | setData(null); 73 | setError(null); 74 | setIsLoading(false); 75 | }, []); 76 | 77 | return { data, isLoading, error, execute, reset }; 78 | } 79 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/useLoadingState.ts: -------------------------------------------------------------------------------- 1 | import { useState, useCallback } from "react"; 2 | 3 | /** 4 | * State interface for the useLoadingState hook 5 | * 6 | * @template T - The type of data being managed 7 | */ 8 | interface LoadingState { 9 | /** The data returned from the async operation, null if not yet loaded */ 10 | data: T | null; 11 | /** Whether an async operation is currently in progress */ 12 | isLoading: boolean; 13 | /** Any error that occurred during the async operation */ 14 | error: Error | null; 15 | /** Function to execute the async operation with given arguments */ 16 | execute: (...args: unknown[]) => Promise; 17 | /** Function to reset the state to initial values */ 18 | reset: () => void; 19 | } 20 | 21 | /** 22 | * Custom hook for managing loading states with error handling 23 | * 24 | * This hook provides a consistent pattern for handling async operations 25 | * with loading states, error handling, and data management. It reduces 26 | * boilerplate code and provides a clean API for async operations. 27 | * 28 | * @template T - The type of data returned by the async function 29 | * @param asyncFunction - The async function to execute 30 | * @returns Object containing data, loading state, error, execute function, and reset function 31 | * 32 | * @example 33 | * ```tsx 34 | * const { data, isLoading, error, execute, reset } = useLoadingState( 35 | * async (userId: string) => fetchUser(userId) 36 | * ); 37 | * 38 | * // Execute the function 39 | * const handleFetch = () => execute('123'); 40 | * 41 | * // Reset state 42 | * const handleReset = () => reset(); 43 | * ``` 44 | */ 45 | export function useLoadingState( 46 | asyncFunction: (...args: unknown[]) => Promise 47 | ): LoadingState { 48 | const [data, setData] = useState(null); 49 | const [isLoading, setIsLoading] = useState(false); 50 | const [error, setError] = useState(null); 51 | 52 | const execute = useCallback( 53 | async (...args: unknown[]): Promise => { 54 | try { 55 | setIsLoading(true); 56 | setError(null); 57 | const result = await asyncFunction(...args); 58 | setData(result); 59 | return result; 60 | } catch (err) { 61 | const error = err instanceof Error ? err : new Error("An error occurred"); 62 | setError(error); 63 | throw error; 64 | } finally { 65 | setIsLoading(false); 66 | } 67 | }, 68 | [asyncFunction] 69 | ); 70 | 71 | const reset = useCallback(() => { 72 | setData(null); 73 | setError(null); 74 | setIsLoading(false); 75 | }, []); 76 | 77 | return { data, isLoading, error, execute, reset }; 78 | } 79 | -------------------------------------------------------------------------------- /src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"; 3 | import { Circle } from "lucide-react"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | /** 7 | * RadioGroup component for selecting one option from multiple choices 8 | * 9 | * A radio button group that allows users to select exactly one option 10 | * from a set of mutually exclusive choices. 11 | * 12 | * @param className - Additional CSS classes 13 | * @param value - Currently selected value 14 | * @param onValueChange - Callback when selection changes 15 | * 16 | * @example 17 | * ```tsx 18 | * 19 | * 20 | * 21 | * Option 1 22 | * 23 | * 24 | * 25 | * Option 2 26 | * 27 | * 28 | * ``` 29 | */ 30 | const RadioGroup = React.forwardRef< 31 | React.ElementRef, 32 | React.ComponentPropsWithoutRef 33 | >(({ className, ...props }, ref) => { 34 | return ; 35 | }); 36 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; 37 | 38 | /** 39 | * RadioGroupItem component - Individual radio button within a RadioGroup 40 | * 41 | * @param value - The value this radio button represents 42 | * @param className - Additional CSS classes 43 | * @param disabled - Whether this option is disabled 44 | */ 45 | const RadioGroupItem = React.forwardRef< 46 | React.ElementRef, 47 | React.ComponentPropsWithoutRef 48 | >(({ className, ...props }, ref) => { 49 | return ( 50 | 58 | 59 | 60 | 61 | 62 | ); 63 | }); 64 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; 65 | 66 | export { RadioGroup, RadioGroupItem }; 67 | -------------------------------------------------------------------------------- /claudeCli/src/components/ui/radio-group.tsx: -------------------------------------------------------------------------------- 1 | import * as React from "react"; 2 | import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"; 3 | import { Circle } from "lucide-react"; 4 | import { cn } from "@/lib/utils"; 5 | 6 | /** 7 | * RadioGroup component for selecting one option from multiple choices 8 | * 9 | * A radio button group that allows users to select exactly one option 10 | * from a set of mutually exclusive choices. 11 | * 12 | * @param className - Additional CSS classes 13 | * @param value - Currently selected value 14 | * @param onValueChange - Callback when selection changes 15 | * 16 | * @example 17 | * ```tsx 18 | * 19 | * 20 | * 21 | * Option 1 22 | * 23 | * 24 | * 25 | * Option 2 26 | * 27 | * 28 | * ``` 29 | */ 30 | const RadioGroup = React.forwardRef< 31 | React.ElementRef, 32 | React.ComponentPropsWithoutRef 33 | >(({ className, ...props }, ref) => { 34 | return ; 35 | }); 36 | RadioGroup.displayName = RadioGroupPrimitive.Root.displayName; 37 | 38 | /** 39 | * RadioGroupItem component - Individual radio button within a RadioGroup 40 | * 41 | * @param value - The value this radio button represents 42 | * @param className - Additional CSS classes 43 | * @param disabled - Whether this option is disabled 44 | */ 45 | const RadioGroupItem = React.forwardRef< 46 | React.ElementRef, 47 | React.ComponentPropsWithoutRef 48 | >(({ className, ...props }, ref) => { 49 | return ( 50 | 58 | 59 | 60 | 61 | 62 | ); 63 | }); 64 | RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName; 65 | 66 | export { RadioGroup, RadioGroupItem }; 67 | -------------------------------------------------------------------------------- /src/hooks/useConfigMonitor.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { listen } from '@tauri-apps/api/event'; 3 | import { invoke } from '@tauri-apps/api/core'; 4 | 5 | interface ConfigStatus { 6 | needs_refresh: boolean; 7 | message: string; 8 | } 9 | 10 | interface UseConfigMonitorReturn { 11 | status: ConfigStatus | null; 12 | isMonitoring: boolean; 13 | startMonitoring: () => Promise; 14 | clearStatus: () => void; 15 | checkConsistency: () => Promise; 16 | } 17 | 18 | export const useConfigMonitor = (): UseConfigMonitorReturn => { 19 | const [status, setStatus] = useState(null); 20 | const [isMonitoring, setIsMonitoring] = useState(false); 21 | 22 | const startMonitoring = async () => { 23 | try { 24 | // 启动后端监听器 25 | await invoke('start_settings_monitor'); 26 | setIsMonitoring(true); 27 | console.log('Config monitor started'); 28 | } catch (error) { 29 | console.error('Failed to start config monitor:', error); 30 | } 31 | }; 32 | 33 | const clearStatus = () => { 34 | setStatus(null); 35 | }; 36 | 37 | const checkConsistency = async (): Promise => { 38 | try { 39 | // 获取当前选择的模型 40 | const currentModel = localStorage.getItem('selected-model'); 41 | if (!currentModel) { 42 | console.log('No current model selected, skipping consistency check'); 43 | return null; 44 | } 45 | 46 | const result = await invoke('check_config_consistency_simple', { 47 | currentSelectedModel: currentModel 48 | }); 49 | return result; 50 | } catch (error) { 51 | console.error('Failed to check configuration consistency:', error); 52 | return null; 53 | } 54 | }; 55 | 56 | useEffect(() => { 57 | let unlisten: (() => void) | undefined; 58 | 59 | const setupListener = async () => { 60 | try { 61 | // 监听文件变化事件,主动检测一致性 62 | unlisten = await listen('settings-file-changed', async () => { 63 | console.log('Settings file changed, checking consistency...'); 64 | 65 | // 获取当前选择的模型并检测 66 | const result = await checkConsistency(); 67 | if (result && result.needs_refresh) { 68 | setStatus(result); 69 | } 70 | }); 71 | 72 | // 自动启动监听 73 | await startMonitoring(); 74 | } catch (error) { 75 | console.error('Failed to setup config monitor:', error); 76 | } 77 | }; 78 | 79 | setupListener(); 80 | 81 | return () => { 82 | if (unlisten) { 83 | unlisten(); 84 | } 85 | }; 86 | }, []); 87 | 88 | return { 89 | status, 90 | isMonitoring, 91 | startMonitoring, 92 | clearStatus, 93 | checkConsistency, 94 | }; 95 | }; -------------------------------------------------------------------------------- /src/components/AnalyticsErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ErrorInfo, ReactNode } from 'react'; 2 | import { eventBuilders, analytics } from '@/lib/analytics'; 3 | 4 | interface Props { 5 | children: ReactNode; 6 | fallback?: (error: Error, reset: () => void) => ReactNode; 7 | } 8 | 9 | interface State { 10 | hasError: boolean; 11 | error: Error | null; 12 | } 13 | 14 | /** 15 | * Error boundary component that tracks UI errors to analytics 16 | */ 17 | export class AnalyticsErrorBoundary extends Component { 18 | constructor(props: Props) { 19 | super(props); 20 | this.state = { hasError: false, error: null }; 21 | } 22 | 23 | static getDerivedStateFromError(error: Error): State { 24 | return { hasError: true, error }; 25 | } 26 | 27 | componentDidCatch(error: Error, errorInfo: ErrorInfo) { 28 | // Track UI error to analytics 29 | const event = eventBuilders.uiError({ 30 | component_name: errorInfo.componentStack?.split('\n')[0] || 'Unknown', 31 | error_type: error.name || 'UnknownError', 32 | user_action: undefined, // Could be enhanced with context 33 | }); 34 | 35 | analytics.track(event.event, event.properties); 36 | 37 | // Log to console for debugging 38 | console.error('UI Error caught by boundary:', error, errorInfo); 39 | } 40 | 41 | reset = () => { 42 | this.setState({ hasError: false, error: null }); 43 | }; 44 | 45 | render() { 46 | if (this.state.hasError && this.state.error) { 47 | // Use custom fallback if provided 48 | if (this.props.fallback) { 49 | return this.props.fallback(this.state.error, this.reset); 50 | } 51 | 52 | // Default fallback UI 53 | return ( 54 | 55 | 56 | Something went wrong 57 | 58 | 59 | {this.state.error.message} 60 | 61 | 65 | Try again 66 | 67 | 68 | ); 69 | } 70 | 71 | return this.props.children; 72 | } 73 | } 74 | 75 | /** 76 | * Hook to wrap components with analytics error tracking 77 | */ 78 | export function withAnalyticsErrorBoundary( 79 | Component: React.ComponentType, 80 | fallback?: (error: Error, reset: () => void) => ReactNode 81 | ) { 82 | return (props: P) => ( 83 | 84 | 85 | 86 | ); 87 | } -------------------------------------------------------------------------------- /src/components/LanguageSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Globe } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useI18n } from "@/lib/i18n"; 11 | import { SUPPORTED_LANGUAGES, type Language } from "@/lib/i18n"; 12 | 13 | interface LanguageSelectorProps { 14 | /** 15 | * 是否显示为紧凑模式(只显示图标) 16 | */ 17 | compact?: boolean; 18 | /** 19 | * 可选的className 20 | */ 21 | className?: string; 22 | } 23 | 24 | /** 25 | * LanguageSelector component for switching application language 26 | * 27 | * A dropdown component that allows users to switch between supported 28 | * languages. Features compact mode for icon-only display and full mode 29 | * with language name. Automatically persists language preference. 30 | * 31 | * @param compact - Whether to show in compact mode (icon only) 32 | * @param className - Optional CSS classes for styling 33 | * 34 | * @example 35 | * ```tsx 36 | * // Full mode with language name 37 | * 38 | * 39 | * // Compact mode (icon only) 40 | * 41 | * ``` 42 | */ 43 | export const LanguageSelector: React.FC = ({ 44 | compact = false, 45 | className, 46 | }) => { 47 | const { language, setLanguage, t } = useI18n(); 48 | 49 | /** 50 | * Handle language change selection 51 | * 52 | * @param newLanguage - The selected language code 53 | */ 54 | const handleLanguageChange = (newLanguage: Language) => { 55 | setLanguage(newLanguage); 56 | }; 57 | 58 | return ( 59 | 60 | 61 | 67 | 68 | {!compact && ( 69 | <> 70 | {SUPPORTED_LANGUAGES[language]} 71 | > 72 | )} 73 | 74 | 75 | 76 | {Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ( 77 | handleLanguageChange(code as Language)} 80 | className={`cursor-pointer ${language === code ? "bg-accent" : ""}`} 81 | > 82 | {name} 83 | {language === code && ✓} 84 | 85 | ))} 86 | 87 | 88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /claudeCli/src/components/AnalyticsErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ErrorInfo, ReactNode } from 'react'; 2 | import { eventBuilders, analytics } from '@/lib/analytics'; 3 | 4 | interface Props { 5 | children: ReactNode; 6 | fallback?: (error: Error, reset: () => void) => ReactNode; 7 | } 8 | 9 | interface State { 10 | hasError: boolean; 11 | error: Error | null; 12 | } 13 | 14 | /** 15 | * Error boundary component that tracks UI errors to analytics 16 | */ 17 | export class AnalyticsErrorBoundary extends Component { 18 | constructor(props: Props) { 19 | super(props); 20 | this.state = { hasError: false, error: null }; 21 | } 22 | 23 | static getDerivedStateFromError(error: Error): State { 24 | return { hasError: true, error }; 25 | } 26 | 27 | componentDidCatch(error: Error, errorInfo: ErrorInfo) { 28 | // Track UI error to analytics 29 | const event = eventBuilders.uiError({ 30 | component_name: errorInfo.componentStack?.split('\n')[0] || 'Unknown', 31 | error_type: error.name || 'UnknownError', 32 | user_action: undefined, // Could be enhanced with context 33 | }); 34 | 35 | analytics.track(event.event, event.properties); 36 | 37 | // Log to console for debugging 38 | console.error('UI Error caught by boundary:', error, errorInfo); 39 | } 40 | 41 | reset = () => { 42 | this.setState({ hasError: false, error: null }); 43 | }; 44 | 45 | render() { 46 | if (this.state.hasError && this.state.error) { 47 | // Use custom fallback if provided 48 | if (this.props.fallback) { 49 | return this.props.fallback(this.state.error, this.reset); 50 | } 51 | 52 | // Default fallback UI 53 | return ( 54 | 55 | 56 | Something went wrong 57 | 58 | 59 | {this.state.error.message} 60 | 61 | 65 | Try again 66 | 67 | 68 | ); 69 | } 70 | 71 | return this.props.children; 72 | } 73 | } 74 | 75 | /** 76 | * Hook to wrap components with analytics error tracking 77 | */ 78 | export function withAnalyticsErrorBoundary( 79 | Component: React.ComponentType, 80 | fallback?: (error: Error, reset: () => void) => ReactNode 81 | ) { 82 | return (props: P) => ( 83 | 84 | 85 | 86 | ); 87 | } -------------------------------------------------------------------------------- /claudeCli/src/components/LanguageSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Globe } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useI18n } from "@/lib/i18n"; 11 | import { SUPPORTED_LANGUAGES, type Language } from "@/lib/i18n"; 12 | 13 | interface LanguageSelectorProps { 14 | /** 15 | * 是否显示为紧凑模式(只显示图标) 16 | */ 17 | compact?: boolean; 18 | /** 19 | * 可选的className 20 | */ 21 | className?: string; 22 | } 23 | 24 | /** 25 | * LanguageSelector component for switching application language 26 | * 27 | * A dropdown component that allows users to switch between supported 28 | * languages. Features compact mode for icon-only display and full mode 29 | * with language name. Automatically persists language preference. 30 | * 31 | * @param compact - Whether to show in compact mode (icon only) 32 | * @param className - Optional CSS classes for styling 33 | * 34 | * @example 35 | * ```tsx 36 | * // Full mode with language name 37 | * 38 | * 39 | * // Compact mode (icon only) 40 | * 41 | * ``` 42 | */ 43 | export const LanguageSelector: React.FC = ({ 44 | compact = false, 45 | className, 46 | }) => { 47 | const { language, setLanguage, t } = useI18n(); 48 | 49 | /** 50 | * Handle language change selection 51 | * 52 | * @param newLanguage - The selected language code 53 | */ 54 | const handleLanguageChange = (newLanguage: Language) => { 55 | setLanguage(newLanguage); 56 | }; 57 | 58 | return ( 59 | 60 | 61 | 67 | 68 | {!compact && ( 69 | <> 70 | {SUPPORTED_LANGUAGES[language]} 71 | > 72 | )} 73 | 74 | 75 | 76 | {Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ( 77 | handleLanguageChange(code as Language)} 80 | className={`cursor-pointer ${language === code ? "bg-accent" : ""}`} 81 | > 82 | {name} 83 | {language === code && ✓} 84 | 85 | ))} 86 | 87 | 88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/tauri.conf.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Claudia", 4 | "version": "0.1.65", 5 | "identifier": "claudia.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:executables:current && bun run build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Claudia", 16 | "width": 800, 17 | "height": 600 18 | } 19 | ], 20 | "security": { 21 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' https://app.posthog.com https://*.posthog.com https://*.i.posthog.com https://*.assets.i.posthog.com; connect-src 'self' ipc: https://ipc.localhost https://app.posthog.com https://*.posthog.com https://*.i.posthog.com; media-src 'self' data:", 22 | "assetProtocol": { 23 | "enable": true, 24 | "scope": ["**"] 25 | } 26 | } 27 | }, 28 | "plugins": { 29 | "fs": { 30 | "scope": ["$HOME/**"], 31 | "allow": [ 32 | "readFile", 33 | "writeFile", 34 | "readDir", 35 | "copyFile", 36 | "createDir", 37 | "removeDir", 38 | "removeFile", 39 | "renameFile", 40 | "exists" 41 | ] 42 | }, 43 | "shell": { 44 | "open": true 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "targets": [ 50 | "deb", 51 | "rpm", 52 | "appimage", 53 | "app", 54 | "dmg", 55 | "msi", 56 | "nsis" 57 | ], 58 | "icon": [ 59 | "icons/icon.ico", 60 | "icons/32x32.png", 61 | "icons/128x128.png", 62 | "icons/128x128@2x.png", 63 | "icons/icon.icns" 64 | ], 65 | "externalBin": ["binaries/claude-code"], 66 | "resources": [], 67 | "copyright": "© 2025 Asterisk. All rights reserved.", 68 | "category": "DeveloperTool", 69 | "shortDescription": "GUI app and Toolkit for Claude Code", 70 | "longDescription": "Claudia is a comprehensive GUI application and toolkit for working with Claude Code, providing an intuitive interface for AI-assisted development.", 71 | "windows": { 72 | "certificateThumbprint": null, 73 | "digestAlgorithm": "sha256", 74 | "timestampUrl": "" 75 | }, 76 | "linux": { 77 | "appimage": { 78 | "bundleMediaFramework": true 79 | }, 80 | "deb": { 81 | "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"] 82 | }, 83 | "rpm": { 84 | "depends": ["webkit2gtk4.1", "gtk3"] 85 | } 86 | }, 87 | "macOS": { 88 | "frameworks": [], 89 | "minimumSystemVersion": "10.15", 90 | "exceptionDomain": "", 91 | "signingIdentity": null, 92 | "providerShortName": null, 93 | "entitlements": "entitlements.plist" 94 | } 95 | } 96 | } --------------------------------------------------------------------------------
Long content that needs scrolling...
More content...
59 | {this.state.error.message} 60 |
( 79 | Component: React.ComponentType
, 80 | fallback?: (error: Error, reset: () => void) => ReactNode 81 | ) { 82 | return (props: P) => ( 83 | 84 | 85 | 86 | ); 87 | } -------------------------------------------------------------------------------- /src/components/LanguageSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Globe } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useI18n } from "@/lib/i18n"; 11 | import { SUPPORTED_LANGUAGES, type Language } from "@/lib/i18n"; 12 | 13 | interface LanguageSelectorProps { 14 | /** 15 | * 是否显示为紧凑模式(只显示图标) 16 | */ 17 | compact?: boolean; 18 | /** 19 | * 可选的className 20 | */ 21 | className?: string; 22 | } 23 | 24 | /** 25 | * LanguageSelector component for switching application language 26 | * 27 | * A dropdown component that allows users to switch between supported 28 | * languages. Features compact mode for icon-only display and full mode 29 | * with language name. Automatically persists language preference. 30 | * 31 | * @param compact - Whether to show in compact mode (icon only) 32 | * @param className - Optional CSS classes for styling 33 | * 34 | * @example 35 | * ```tsx 36 | * // Full mode with language name 37 | * 38 | * 39 | * // Compact mode (icon only) 40 | * 41 | * ``` 42 | */ 43 | export const LanguageSelector: React.FC = ({ 44 | compact = false, 45 | className, 46 | }) => { 47 | const { language, setLanguage, t } = useI18n(); 48 | 49 | /** 50 | * Handle language change selection 51 | * 52 | * @param newLanguage - The selected language code 53 | */ 54 | const handleLanguageChange = (newLanguage: Language) => { 55 | setLanguage(newLanguage); 56 | }; 57 | 58 | return ( 59 | 60 | 61 | 67 | 68 | {!compact && ( 69 | <> 70 | {SUPPORTED_LANGUAGES[language]} 71 | > 72 | )} 73 | 74 | 75 | 76 | {Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ( 77 | handleLanguageChange(code as Language)} 80 | className={`cursor-pointer ${language === code ? "bg-accent" : ""}`} 81 | > 82 | {name} 83 | {language === code && ✓} 84 | 85 | ))} 86 | 87 | 88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /claudeCli/src/components/AnalyticsErrorBoundary.tsx: -------------------------------------------------------------------------------- 1 | import React, { Component, ErrorInfo, ReactNode } from 'react'; 2 | import { eventBuilders, analytics } from '@/lib/analytics'; 3 | 4 | interface Props { 5 | children: ReactNode; 6 | fallback?: (error: Error, reset: () => void) => ReactNode; 7 | } 8 | 9 | interface State { 10 | hasError: boolean; 11 | error: Error | null; 12 | } 13 | 14 | /** 15 | * Error boundary component that tracks UI errors to analytics 16 | */ 17 | export class AnalyticsErrorBoundary extends Component { 18 | constructor(props: Props) { 19 | super(props); 20 | this.state = { hasError: false, error: null }; 21 | } 22 | 23 | static getDerivedStateFromError(error: Error): State { 24 | return { hasError: true, error }; 25 | } 26 | 27 | componentDidCatch(error: Error, errorInfo: ErrorInfo) { 28 | // Track UI error to analytics 29 | const event = eventBuilders.uiError({ 30 | component_name: errorInfo.componentStack?.split('\n')[0] || 'Unknown', 31 | error_type: error.name || 'UnknownError', 32 | user_action: undefined, // Could be enhanced with context 33 | }); 34 | 35 | analytics.track(event.event, event.properties); 36 | 37 | // Log to console for debugging 38 | console.error('UI Error caught by boundary:', error, errorInfo); 39 | } 40 | 41 | reset = () => { 42 | this.setState({ hasError: false, error: null }); 43 | }; 44 | 45 | render() { 46 | if (this.state.hasError && this.state.error) { 47 | // Use custom fallback if provided 48 | if (this.props.fallback) { 49 | return this.props.fallback(this.state.error, this.reset); 50 | } 51 | 52 | // Default fallback UI 53 | return ( 54 | 55 | 56 | Something went wrong 57 | 58 | 59 | {this.state.error.message} 60 | 61 | 65 | Try again 66 | 67 | 68 | ); 69 | } 70 | 71 | return this.props.children; 72 | } 73 | } 74 | 75 | /** 76 | * Hook to wrap components with analytics error tracking 77 | */ 78 | export function withAnalyticsErrorBoundary( 79 | Component: React.ComponentType, 80 | fallback?: (error: Error, reset: () => void) => ReactNode 81 | ) { 82 | return (props: P) => ( 83 | 84 | 85 | 86 | ); 87 | } -------------------------------------------------------------------------------- /claudeCli/src/components/LanguageSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Globe } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useI18n } from "@/lib/i18n"; 11 | import { SUPPORTED_LANGUAGES, type Language } from "@/lib/i18n"; 12 | 13 | interface LanguageSelectorProps { 14 | /** 15 | * 是否显示为紧凑模式(只显示图标) 16 | */ 17 | compact?: boolean; 18 | /** 19 | * 可选的className 20 | */ 21 | className?: string; 22 | } 23 | 24 | /** 25 | * LanguageSelector component for switching application language 26 | * 27 | * A dropdown component that allows users to switch between supported 28 | * languages. Features compact mode for icon-only display and full mode 29 | * with language name. Automatically persists language preference. 30 | * 31 | * @param compact - Whether to show in compact mode (icon only) 32 | * @param className - Optional CSS classes for styling 33 | * 34 | * @example 35 | * ```tsx 36 | * // Full mode with language name 37 | * 38 | * 39 | * // Compact mode (icon only) 40 | * 41 | * ``` 42 | */ 43 | export const LanguageSelector: React.FC = ({ 44 | compact = false, 45 | className, 46 | }) => { 47 | const { language, setLanguage, t } = useI18n(); 48 | 49 | /** 50 | * Handle language change selection 51 | * 52 | * @param newLanguage - The selected language code 53 | */ 54 | const handleLanguageChange = (newLanguage: Language) => { 55 | setLanguage(newLanguage); 56 | }; 57 | 58 | return ( 59 | 60 | 61 | 67 | 68 | {!compact && ( 69 | <> 70 | {SUPPORTED_LANGUAGES[language]} 71 | > 72 | )} 73 | 74 | 75 | 76 | {Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ( 77 | handleLanguageChange(code as Language)} 80 | className={`cursor-pointer ${language === code ? "bg-accent" : ""}`} 81 | > 82 | {name} 83 | {language === code && ✓} 84 | 85 | ))} 86 | 87 | 88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/tauri.conf.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Claudia", 4 | "version": "0.1.65", 5 | "identifier": "claudia.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:executables:current && bun run build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Claudia", 16 | "width": 800, 17 | "height": 600 18 | } 19 | ], 20 | "security": { 21 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' https://app.posthog.com https://*.posthog.com https://*.i.posthog.com https://*.assets.i.posthog.com; connect-src 'self' ipc: https://ipc.localhost https://app.posthog.com https://*.posthog.com https://*.i.posthog.com; media-src 'self' data:", 22 | "assetProtocol": { 23 | "enable": true, 24 | "scope": ["**"] 25 | } 26 | } 27 | }, 28 | "plugins": { 29 | "fs": { 30 | "scope": ["$HOME/**"], 31 | "allow": [ 32 | "readFile", 33 | "writeFile", 34 | "readDir", 35 | "copyFile", 36 | "createDir", 37 | "removeDir", 38 | "removeFile", 39 | "renameFile", 40 | "exists" 41 | ] 42 | }, 43 | "shell": { 44 | "open": true 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "targets": [ 50 | "deb", 51 | "rpm", 52 | "appimage", 53 | "app", 54 | "dmg", 55 | "msi", 56 | "nsis" 57 | ], 58 | "icon": [ 59 | "icons/icon.ico", 60 | "icons/32x32.png", 61 | "icons/128x128.png", 62 | "icons/128x128@2x.png", 63 | "icons/icon.icns" 64 | ], 65 | "externalBin": ["binaries/claude-code"], 66 | "resources": [], 67 | "copyright": "© 2025 Asterisk. All rights reserved.", 68 | "category": "DeveloperTool", 69 | "shortDescription": "GUI app and Toolkit for Claude Code", 70 | "longDescription": "Claudia is a comprehensive GUI application and toolkit for working with Claude Code, providing an intuitive interface for AI-assisted development.", 71 | "windows": { 72 | "certificateThumbprint": null, 73 | "digestAlgorithm": "sha256", 74 | "timestampUrl": "" 75 | }, 76 | "linux": { 77 | "appimage": { 78 | "bundleMediaFramework": true 79 | }, 80 | "deb": { 81 | "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"] 82 | }, 83 | "rpm": { 84 | "depends": ["webkit2gtk4.1", "gtk3"] 85 | } 86 | }, 87 | "macOS": { 88 | "frameworks": [], 89 | "minimumSystemVersion": "10.15", 90 | "exceptionDomain": "", 91 | "signingIdentity": null, 92 | "providerShortName": null, 93 | "entitlements": "entitlements.plist" 94 | } 95 | } 96 | } --------------------------------------------------------------------------------
, 80 | fallback?: (error: Error, reset: () => void) => ReactNode 81 | ) { 82 | return (props: P) => ( 83 | 84 | 85 | 86 | ); 87 | } -------------------------------------------------------------------------------- /claudeCli/src/components/LanguageSelector.tsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Globe } from "lucide-react"; 3 | import { Button } from "@/components/ui/button"; 4 | import { 5 | DropdownMenu, 6 | DropdownMenuContent, 7 | DropdownMenuItem, 8 | DropdownMenuTrigger, 9 | } from "@/components/ui/dropdown-menu"; 10 | import { useI18n } from "@/lib/i18n"; 11 | import { SUPPORTED_LANGUAGES, type Language } from "@/lib/i18n"; 12 | 13 | interface LanguageSelectorProps { 14 | /** 15 | * 是否显示为紧凑模式(只显示图标) 16 | */ 17 | compact?: boolean; 18 | /** 19 | * 可选的className 20 | */ 21 | className?: string; 22 | } 23 | 24 | /** 25 | * LanguageSelector component for switching application language 26 | * 27 | * A dropdown component that allows users to switch between supported 28 | * languages. Features compact mode for icon-only display and full mode 29 | * with language name. Automatically persists language preference. 30 | * 31 | * @param compact - Whether to show in compact mode (icon only) 32 | * @param className - Optional CSS classes for styling 33 | * 34 | * @example 35 | * ```tsx 36 | * // Full mode with language name 37 | * 38 | * 39 | * // Compact mode (icon only) 40 | * 41 | * ``` 42 | */ 43 | export const LanguageSelector: React.FC = ({ 44 | compact = false, 45 | className, 46 | }) => { 47 | const { language, setLanguage, t } = useI18n(); 48 | 49 | /** 50 | * Handle language change selection 51 | * 52 | * @param newLanguage - The selected language code 53 | */ 54 | const handleLanguageChange = (newLanguage: Language) => { 55 | setLanguage(newLanguage); 56 | }; 57 | 58 | return ( 59 | 60 | 61 | 67 | 68 | {!compact && ( 69 | <> 70 | {SUPPORTED_LANGUAGES[language]} 71 | > 72 | )} 73 | 74 | 75 | 76 | {Object.entries(SUPPORTED_LANGUAGES).map(([code, name]) => ( 77 | handleLanguageChange(code as Language)} 80 | className={`cursor-pointer ${language === code ? "bg-accent" : ""}`} 81 | > 82 | {name} 83 | {language === code && ✓} 84 | 85 | ))} 86 | 87 | 88 | ); 89 | }; 90 | -------------------------------------------------------------------------------- /src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src/hooks/useDebounce.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState, useRef } from "react"; 2 | 3 | /** 4 | * Custom hook that debounces a value 5 | * 6 | * Delays updating the returned value until after the specified delay has passed 7 | * since the last time the input value changed. This is useful for search inputs, 8 | * API calls, and other scenarios where you want to reduce the frequency of updates. 9 | * 10 | * @template T - The type of the value being debounced 11 | * @param value - The value to debounce 12 | * @param delay - The delay in milliseconds 13 | * @returns The debounced value 14 | * 15 | * @example 16 | * ```tsx 17 | * const [searchTerm, setSearchTerm] = useState(''); 18 | * const debouncedSearchTerm = useDebounce(searchTerm, 300); 19 | * 20 | * useEffect(() => { 21 | * if (debouncedSearchTerm) { 22 | * performSearch(debouncedSearchTerm); 23 | * } 24 | * }, [debouncedSearchTerm]); 25 | * ``` 26 | */ 27 | export function useDebounce(value: T, delay: number): T { 28 | const [debouncedValue, setDebouncedValue] = useState(value); 29 | 30 | useEffect(() => { 31 | const handler = setTimeout(() => { 32 | setDebouncedValue(value); 33 | }, delay); 34 | 35 | return () => { 36 | clearTimeout(handler); 37 | }; 38 | }, [value, delay]); 39 | 40 | return debouncedValue; 41 | } 42 | 43 | /** 44 | * Custom hook that returns a debounced callback function 45 | * 46 | * The returned callback will only be invoked after the specified delay has passed 47 | * since the last call. This prevents rapid successive calls and is useful for 48 | * expensive operations like API calls or complex calculations. 49 | * 50 | * @template T - The type of the callback function 51 | * @param callback - The function to debounce 52 | * @param delay - The delay in milliseconds 53 | * @returns A debounced version of the callback 54 | * 55 | * @example 56 | * ```tsx 57 | * const debouncedSave = useDebouncedCallback((data: FormData) => { 58 | * saveToServer(data); 59 | * }, 1000); 60 | * 61 | * // This will only call saveToServer once, 1 second after the last call 62 | * debouncedSave(formData); 63 | * debouncedSave(formData); // Cancels previous call 64 | * debouncedSave(formData); // Only this call will execute 65 | * ``` 66 | */ 67 | export function useDebouncedCallback unknown>( 68 | callback: T, 69 | delay: number 70 | ): T { 71 | const timeoutRef = useRef | null>(null); 72 | const callbackRef = useRef(callback); 73 | 74 | // Update callback ref on each render to avoid stale closures 75 | callbackRef.current = callback; 76 | 77 | return useRef(((...args: Parameters) => { 78 | if (timeoutRef.current) { 79 | clearTimeout(timeoutRef.current); 80 | } 81 | 82 | timeoutRef.current = setTimeout(() => { 83 | callbackRef.current(...args); 84 | }, delay); 85 | }) as T).current; 86 | } 87 | -------------------------------------------------------------------------------- /claudeCli/src-tauri/tauri.conf.release.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://schema.tauri.app/config/2", 3 | "productName": "Claudia", 4 | "version": "0.1.65", 5 | "identifier": "claudia.asterisk.so", 6 | "build": { 7 | "beforeDevCommand": "bun run dev", 8 | "devUrl": "http://localhost:1420", 9 | "beforeBuildCommand": "bun run build:executables:current && bun run build", 10 | "frontendDist": "../dist" 11 | }, 12 | "app": { 13 | "windows": [ 14 | { 15 | "title": "Claudia", 16 | "width": 800, 17 | "height": 600 18 | } 19 | ], 20 | "security": { 21 | "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost blob: data:; style-src 'self' 'unsafe-inline'; script-src 'self' 'unsafe-eval' https://app.posthog.com https://*.posthog.com https://*.i.posthog.com https://*.assets.i.posthog.com; connect-src 'self' ipc: https://ipc.localhost https://app.posthog.com https://*.posthog.com https://*.i.posthog.com; media-src 'self' data:", 22 | "assetProtocol": { 23 | "enable": true, 24 | "scope": ["**"] 25 | } 26 | } 27 | }, 28 | "plugins": { 29 | "fs": { 30 | "scope": ["$HOME/**"], 31 | "allow": [ 32 | "readFile", 33 | "writeFile", 34 | "readDir", 35 | "copyFile", 36 | "createDir", 37 | "removeDir", 38 | "removeFile", 39 | "renameFile", 40 | "exists" 41 | ] 42 | }, 43 | "shell": { 44 | "open": true 45 | } 46 | }, 47 | "bundle": { 48 | "active": true, 49 | "targets": [ 50 | "deb", 51 | "rpm", 52 | "appimage", 53 | "app", 54 | "dmg", 55 | "msi", 56 | "nsis" 57 | ], 58 | "icon": [ 59 | "icons/icon.ico", 60 | "icons/32x32.png", 61 | "icons/128x128.png", 62 | "icons/128x128@2x.png", 63 | "icons/icon.icns" 64 | ], 65 | "externalBin": ["binaries/claude-code"], 66 | "resources": [], 67 | "copyright": "© 2025 Asterisk. All rights reserved.", 68 | "category": "DeveloperTool", 69 | "shortDescription": "GUI app and Toolkit for Claude Code", 70 | "longDescription": "Claudia is a comprehensive GUI application and toolkit for working with Claude Code, providing an intuitive interface for AI-assisted development.", 71 | "windows": { 72 | "certificateThumbprint": null, 73 | "digestAlgorithm": "sha256", 74 | "timestampUrl": "" 75 | }, 76 | "linux": { 77 | "appimage": { 78 | "bundleMediaFramework": true 79 | }, 80 | "deb": { 81 | "depends": ["libwebkit2gtk-4.1-0", "libgtk-3-0"] 82 | }, 83 | "rpm": { 84 | "depends": ["webkit2gtk4.1", "gtk3"] 85 | } 86 | }, 87 | "macOS": { 88 | "frameworks": [], 89 | "minimumSystemVersion": "10.15", 90 | "exceptionDomain": "", 91 | "signingIdentity": null, 92 | "providerShortName": null, 93 | "entitlements": "entitlements.plist" 94 | } 95 | } 96 | } --------------------------------------------------------------------------------