diff --git a/assets/wechat-group-qr.png b/assets/wechat-group-qr.png index 1324996a..f9fb7547 100644 Binary files a/assets/wechat-group-qr.png and b/assets/wechat-group-qr.png differ diff --git a/ccw/frontend/src/components/codexlens/EnvSettingsTab.tsx b/ccw/frontend/src/components/codexlens/EnvSettingsTab.tsx index 0d718485..ef76537b 100644 --- a/ccw/frontend/src/components/codexlens/EnvSettingsTab.tsx +++ b/ccw/frontend/src/components/codexlens/EnvSettingsTab.tsx @@ -11,6 +11,8 @@ import { Button } from '@/components/ui/Button'; import { Input } from '@/components/ui/Input'; import { useCodexLensEnv, useSaveCodexLensEnv } from '@/hooks/useCodexLens'; +type EmbedMode = 'local' | 'api'; + // ======================================== // ENV group definitions // ======================================== @@ -71,6 +73,29 @@ const ENV_GROUPS: EnvGroup[] = [ }, ]; +// Fields that are only relevant in API mode +const API_ONLY_KEYS = new Set([ + 'CODEXLENS_EMBED_API_URL', + 'CODEXLENS_EMBED_API_KEY', + 'CODEXLENS_EMBED_API_ENDPOINTS', + 'CODEXLENS_EMBED_API_CONCURRENCY', +]); + +// Default placeholder values +const FIELD_DEFAULTS: Record = { + CODEXLENS_EMBED_API_MODEL: 'text-embedding-3-small', + CODEXLENS_EMBED_DIM: '1536', + CODEXLENS_EMBED_BATCH_SIZE: '512', + CODEXLENS_EMBED_API_CONCURRENCY: '4', + CODEXLENS_BINARY_TOP_K: '200', + CODEXLENS_ANN_TOP_K: '50', + CODEXLENS_FTS_TOP_K: '50', + CODEXLENS_FUSION_K: '60', + CODEXLENS_RERANKER_TOP_K: '20', + CODEXLENS_RERANKER_BATCH_SIZE: '32', + CODEXLENS_INDEX_WORKERS: '4', +}; + // Collect all keys const ALL_KEYS = ENV_GROUPS.flatMap((g) => g.fields.map((f) => f.key)); @@ -120,9 +145,10 @@ export function EnvSettingsTab() { const { data: serverEnv, isLoading } = useCodexLensEnv(); const { saveEnv, isSaving } = useSaveCodexLensEnv(); + const [embedMode, setEmbedMode] = useState('local'); const [localEnv, setLocalEnv] = useState>(buildEmptyEnv); - // Sync server state into local when loaded + // Sync server state into local when loaded and detect embed mode useEffect(() => { if (serverEnv) { setLocalEnv((prev) => { @@ -132,6 +158,10 @@ export function EnvSettingsTab() { }); return next; }); + // Auto-detect mode from saved env + if (serverEnv.CODEXLENS_EMBED_API_URL) { + setEmbedMode('api'); + } } }, [serverEnv]); @@ -157,13 +187,53 @@ export function EnvSettingsTab() { return (
- {ENV_GROUPS.map((group) => ( + {/* Mode toggle */} +
+ {formatMessage({ id: 'codexlens.env.mode' })}: +
+ + +
+ + {embedMode === 'local' + ? formatMessage({ id: 'codexlens.env.localModeDesc' }) + : formatMessage({ id: 'codexlens.env.apiModeDesc' })} + +
+ + {ENV_GROUPS.map((group) => { + // In local mode, filter out API-only fields from embed group + const visibleFields = embedMode === 'local' + ? group.fields.filter((f) => !API_ONLY_KEYS.has(f.key)) + : group.fields; + if (visibleFields.length === 0) return null; + return ( {formatMessage({ id: `codexlens.env.sections.${group.title}` })} - {group.fields.map((field) => ( + {visibleFields.map((field) => (