diff --git a/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx new file mode 100644 index 000000000..7126322e0 --- /dev/null +++ b/gui/src/components/subagents-workspace/SubagentsWorkspace.tsx @@ -0,0 +1,234 @@ +/** + * SubagentsWorkspace — rail + main workspace for the Subagents tab, mirroring the + * Providers/Combos workspace DNA. Left rail lists Featured and Available models with + * add/remove toggles; the main pane shows either the featured roster (reorder + save) + * or a per-model detail view. + */ +import { useMemo, useState } from "react"; +import { + IconArrowDown, + IconArrowUp, + IconBot, + IconCheck, + IconChevron, + IconInfo, + IconPlus, + IconX, +} from "../../icons"; +import { useT } from "../../i18n/shared"; +import { Trans } from "../../i18n/provider"; +import { modelLabel } from "../../model-display"; + +export interface SubagentsWorkspaceProps { + available: string[]; + chosen: string[]; + busy?: boolean; + onToggle: (m: string) => void; + onMove: (i: number, dir: -1 | 1) => void; + onSave: () => void; +} + +const FEATURED_MAX = 5; + +export default function SubagentsWorkspace({ + available, + chosen, + busy = false, + onToggle, + onMove, + onSave, +}: SubagentsWorkspaceProps) { + const t = useT(); + const [query, setQuery] = useState(""); + const [selected, setSelected] = useState(null); + + const chosenSet = useMemo(() => new Set(chosen), [chosen]); + const full = chosen.length >= FEATURED_MAX; + + const featuredFiltered = useMemo(() => { + const q = query.trim().toLowerCase(); + return chosen.filter(m => !q || m.toLowerCase().includes(q)); + }, [chosen, query]); + + const availableFiltered = useMemo(() => { + const q = query.trim().toLowerCase(); + return available.filter(m => !chosenSet.has(m) && (!q || m.toLowerCase().includes(q))); + }, [available, chosenSet, query]); + + const selectedIndex = selected ? chosen.indexOf(selected) : -1; + const selectedIsFeatured = selectedIndex !== -1; + + return ( +
+
+ + +
+ {selected ? ( +
+ +
+ +

{selected}

+
+ +
+
+
+
{t("sub.workspace.selector")}
+
{selected}
+
+
+
{t("sub.workspace.priority")}
+
{selectedIsFeatured ? selectedIndex + 1 : t("sub.workspace.notFeatured")}
+
+
+
+ +
+

{t("sub.featured")}

+
+ {selectedIsFeatured ? ( + + ) : ( + + )} +
+
+
+ ) : ( + <> +
+

{t("sub.featured")}

+ {chosen.length}/{FEATURED_MAX} +
+

+

+ + {chosen.length === 0 ? ( +
{t("sub.noneSelected")}
+ ) : ( +
+ {chosen.map((m, i) => ( +
+ {i + 1} + {modelLabel(m)} + + + + + +
+ ))} +
+ )} + +
+ +
+ + )} +
+
+
+ ); +} diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts index 11167fc3b..fc0a2346b 100644 --- a/gui/src/i18n/de.ts +++ b/gui/src/i18n/de.ts @@ -450,6 +450,16 @@ export const de: Record = { "sub.moveUp": "{m} nach oben", "sub.moveDown": "{m} nach unten", "sub.removeAria": "{m} entfernen", + "sub.workspace.allModels": "Alle Modelle", + "sub.workspace.selector": "Öffentlicher Selektor", + "sub.workspace.priority": "Priorität", + "sub.workspace.notFeatured": "Nicht hervorgehoben", + "sub.workspace.addToFeatured": "{m} zu Hervorgehobenen hinzufügen", + "sub.workspace.removeFromFeatured": "{m} aus Hervorgehobenen entfernen", + "sub.workspace.featuredFull": "Hervorgehobene Liste ist voll (max. 5)", + "sub.workspace.selectModel": "Modell auswählen", + "sub.workspace.selectModelDesc": "Wählen Sie ein Modell aus der Liste, um Details anzuzeigen und es für spawn_agent hervorzuheben.", + "sub.workspace.mainAria": "Subagent-Modelldetails", "logs.title": "Anfrage-Protokolle", "logs.tabLogs": "Protokolle", "logs.tabDebug": "Diagnose", @@ -585,6 +595,8 @@ export const de: Record = { "usage.section.models": "Modelle", "usage.section.providers": "Anbieter", "usage.section.coverage": "Abdeckungs-Aufschlüsselung", + "usage.workspace.sections": "Nutzungsabschnitte", + "usage.workspace.report": "Nutzungsbericht", "usage.coverage.measured": "Gemessen", "usage.coverage.reported": "Anbieter gemeldet", "usage.coverage.estimated": "Geschätzt", @@ -1286,6 +1298,7 @@ export const de: Record = { "codexAuth.addIdPlaceholder": "codex-work, codex-alt, team…", "codexAuth.resetCreditsAria": "{count} Reset-Guthaben", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "Einstellungen", // Combos workspace "cws.loading": "Combos werden geladen…", diff --git a/gui/src/i18n/en.ts b/gui/src/i18n/en.ts index edb244f48..7dec5a5d3 100644 --- a/gui/src/i18n/en.ts +++ b/gui/src/i18n/en.ts @@ -466,6 +466,16 @@ export const en = { "sub.moveUp": "Move {m} up", "sub.moveDown": "Move {m} down", "sub.removeAria": "Remove {m}", + "sub.workspace.allModels": "All models", + "sub.workspace.selector": "Public selector", + "sub.workspace.priority": "Priority", + "sub.workspace.notFeatured": "Not featured", + "sub.workspace.addToFeatured": "Add {m} to featured", + "sub.workspace.removeFromFeatured": "Remove {m} from featured", + "sub.workspace.featuredFull": "Featured list is full (max 5)", + "sub.workspace.selectModel": "Select a model", + "sub.workspace.selectModelDesc": "Pick a model from the list to see details and feature it for spawn_agent.", + "sub.workspace.mainAria": "Subagent model details", // logs "logs.title": "Request Logs", @@ -607,6 +617,8 @@ export const en = { "usage.section.models": "Models", "usage.section.providers": "Providers", "usage.section.coverage": "Coverage breakdown", + "usage.workspace.sections": "Usage sections", + "usage.workspace.report": "Usage report", "usage.coverage.measured": "Measured", "usage.coverage.reported": "Provider reported", "usage.coverage.estimated": "Estimated", @@ -1243,6 +1255,7 @@ export const en = { "nav.claude": "Claude", "claude.subtitle": "Use GPT, Gemini, and other models inside Claude Code.", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "Settings", "claude.enabledLabel": "Claude connection", "claude.enabledHint": "When off, Claude Code cannot use this proxy.", "claude.authMode": "Auth Mode", diff --git a/gui/src/i18n/ja.ts b/gui/src/i18n/ja.ts index 3a6981701..f66ff096c 100644 --- a/gui/src/i18n/ja.ts +++ b/gui/src/i18n/ja.ts @@ -433,6 +433,16 @@ export const ja: Record = { "sub.moveUp": "{m} を上へ移動", "sub.moveDown": "{m} を下へ移動", "sub.removeAria": "{m} を削除", + "sub.workspace.allModels": "すべてのモデル", + "sub.workspace.selector": "公開セレクター", + "sub.workspace.priority": "優先度", + "sub.workspace.notFeatured": "おすすめ未設定", + "sub.workspace.addToFeatured": "{m} をおすすめに追加", + "sub.workspace.removeFromFeatured": "{m} をおすすめから削除", + "sub.workspace.featuredFull": "おすすめリストがいっぱいです(最大 5)", + "sub.workspace.selectModel": "モデルを選択", + "sub.workspace.selectModelDesc": "一覧からモデルを選んで詳細を確認し、spawn_agent のおすすめに設定します。", + "sub.workspace.mainAria": "サブエージェントのモデル詳細", // logs "logs.title": "リクエストログ", @@ -574,6 +584,8 @@ export const ja: Record = { "usage.section.models": "モデル", "usage.section.providers": "プロバイダー", "usage.section.coverage": "カバレッジ内訳", + "usage.workspace.sections": "使用量セクション", + "usage.workspace.report": "使用量レポート", "usage.coverage.measured": "計測", "usage.coverage.reported": "プロバイダー報告", "usage.coverage.estimated": "推定", @@ -1198,6 +1210,7 @@ export const ja: Record = { "nav.claude": "Claude", "claude.subtitle": "Claude Code 内で GPT、Gemini などのモデルを使用します。", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "設定", "claude.enabledLabel": "Claude 接続", "claude.enabledHint": "オフにすると Claude Code はこのプロキシを使用できません。", "claude.authMode": "認証モード", diff --git a/gui/src/i18n/ko.ts b/gui/src/i18n/ko.ts index 5e206ab31..d29038b12 100644 --- a/gui/src/i18n/ko.ts +++ b/gui/src/i18n/ko.ts @@ -460,6 +460,16 @@ export const ko: Record = { "sub.moveUp": "{m} 위로 이동", "sub.moveDown": "{m} 아래로 이동", "sub.removeAria": "{m} 삭제", + "sub.workspace.allModels": "모든 모델", + "sub.workspace.selector": "공개 셀렉터", + "sub.workspace.priority": "우선순위", + "sub.workspace.notFeatured": "추천되지 않음", + "sub.workspace.addToFeatured": "{m}을(를) 추천에 추가", + "sub.workspace.removeFromFeatured": "{m}을(를) 추천에서 제거", + "sub.workspace.featuredFull": "추천 목록이 가득 찼습니다 (최대 5개)", + "sub.workspace.selectModel": "모델 선택", + "sub.workspace.selectModelDesc": "목록에서 모델을 선택하여 세부 정보를 확인하고 spawn_agent에 추천하세요.", + "sub.workspace.mainAria": "서브에이전트 모델 세부 정보", // logs "logs.title": "요청 로그", @@ -600,6 +610,8 @@ export const ko: Record = { "usage.section.models": "모델", "usage.section.providers": "프로바이더", "usage.section.coverage": "커버리지 상세", + "usage.workspace.sections": "사용량 섹션", + "usage.workspace.report": "사용량 보고서", "usage.coverage.measured": "측정됨", "usage.coverage.reported": "제공자 보고", "usage.coverage.estimated": "추정", @@ -1306,6 +1318,7 @@ export const ko: Record = { "codexAuth.addIdPlaceholder": "codex-work, codex-alt, team…", "codexAuth.resetCreditsAria": "리셋 크레딧 {count}개", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "설정", // Combos workspace "cws.loading": "콤보 불러오는 중…", diff --git a/gui/src/i18n/ru.ts b/gui/src/i18n/ru.ts index 97e1d68da..271018c1c 100644 --- a/gui/src/i18n/ru.ts +++ b/gui/src/i18n/ru.ts @@ -465,6 +465,16 @@ export const ru: Record = { "sub.moveUp": "Переместить {m} вверх", "sub.moveDown": "Переместить {m} вниз", "sub.removeAria": "Убрать {m}", + "sub.workspace.allModels": "Все модели", + "sub.workspace.selector": "Публичный селектор", + "sub.workspace.priority": "Приоритет", + "sub.workspace.notFeatured": "Не в избранных", + "sub.workspace.addToFeatured": "Добавить {m} в избранные", + "sub.workspace.removeFromFeatured": "Убрать {m} из избранных", + "sub.workspace.featuredFull": "Список избранных заполнен (макс. 5)", + "sub.workspace.selectModel": "Выберите модель", + "sub.workspace.selectModelDesc": "Выберите модель из списка, чтобы увидеть детали и добавить её в избранные для spawn_agent.", + "sub.workspace.mainAria": "Сведения о модели субагента", // logs "logs.title": "Журнал запросов", @@ -606,6 +616,8 @@ export const ru: Record = { "usage.section.models": "Модели", "usage.section.providers": "Провайдеры", "usage.section.coverage": "Детализация покрытия", + "usage.workspace.sections": "Разделы использования", + "usage.workspace.report": "Отчёт об использовании", "usage.coverage.measured": "Измерено", "usage.coverage.reported": "Сообщено провайдером", "usage.coverage.estimated": "Оценено", @@ -1240,6 +1252,7 @@ export const ru: Record = { "nav.claude": "Claude", "claude.subtitle": "Используйте GPT, Gemini и другие модели внутри Claude Code.", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "Настройки", "claude.enabledLabel": "Подключение Claude", "claude.enabledHint": "Если выключено, Claude Code не сможет использовать этот прокси.", "claude.authMode": "Режим аутентификации", diff --git a/gui/src/i18n/zh.ts b/gui/src/i18n/zh.ts index ee16b561c..776016d0b 100644 --- a/gui/src/i18n/zh.ts +++ b/gui/src/i18n/zh.ts @@ -460,6 +460,16 @@ export const zh: Record = { "sub.moveUp": "上移 {m}", "sub.moveDown": "下移 {m}", "sub.removeAria": "移除 {m}", + "sub.workspace.allModels": "所有模型", + "sub.workspace.selector": "公开选择器", + "sub.workspace.priority": "优先级", + "sub.workspace.notFeatured": "未设为精选", + "sub.workspace.addToFeatured": "将 {m} 添加到精选", + "sub.workspace.removeFromFeatured": "将 {m} 从精选中移除", + "sub.workspace.featuredFull": "精选列表已满(最多 5 个)", + "sub.workspace.selectModel": "选择模型", + "sub.workspace.selectModelDesc": "从列表中选择一个模型以查看详情,并将其设为 spawn_agent 的精选模型。", + "sub.workspace.mainAria": "子代理模型详情", // logs "logs.title": "请求日志", @@ -600,6 +610,8 @@ export const zh: Record = { "usage.section.models": "模型", "usage.section.providers": "提供方", "usage.section.coverage": "覆盖率明细", + "usage.workspace.sections": "用量分区", + "usage.workspace.report": "用量报告", "usage.coverage.measured": "已计量", "usage.coverage.reported": "提供方上报", "usage.coverage.estimated": "估算", @@ -1306,6 +1318,7 @@ export const zh: Record = { "codexAuth.addIdPlaceholder": "codex-work, codex-alt, team…", "codexAuth.resetCreditsAria": "{count} 个重置额度", "claude.pageTitle": "Claude Code", + "claude.workspace.settings": "设置", // Combos workspace "cws.loading": "正在加载组合…", diff --git a/gui/src/pages/ClaudeCode.tsx b/gui/src/pages/ClaudeCode.tsx index 03e439e61..2c83423cb 100644 --- a/gui/src/pages/ClaudeCode.tsx +++ b/gui/src/pages/ClaudeCode.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useState } from "react"; +import { useCallback, useEffect, useMemo, useState, type ReactNode } from "react"; import { Notice } from "../ui"; import { useI18n, useT, LOCALES } from "../i18n/shared"; import { modelLabel } from "../model-display"; @@ -26,6 +26,7 @@ export default function ClaudeCode({ apiBase }: { apiBase: string }) { const [status, setStatus] = useState(""); const [ok, setOk] = useState(false); const [loading, setLoading] = useState(true); + const [selectedSection, setSelectedSection] = useState("settings"); const load = useCallback(async () => { try { @@ -69,7 +70,9 @@ export default function ClaudeCode({ apiBase }: { apiBase: string }) { }, [load]); const modelOptions = useMemo(() => { - const options = (state?.available ?? []).map(m => ({ value: m, label: String(modelLabel(m)) })); + // modelLabel returns ReactNode (icons for the 5.6 trio) — pass through as Select labels. + // String(modelLabel(...)) produced "[object Object]" for those first entries. + const options = (state?.available ?? []).map(m => ({ value: m, label: modelLabel(m) })); return [{ value: "", label: t("claude.smallFastModelUnsetOption") }, ...options]; }, [state?.available, t]); @@ -124,21 +127,74 @@ export default function ClaudeCode({ apiBase }: { apiBase: string }) { if (loading) return
{t("claude.loading")}
; if (!state) return {status || t("claude.loadFail")}; + const sections: Array<{ id: string; label: string; body: ReactNode }> = [ + { + id: "settings", + label: t("claude.workspace.settings"), + body: ( + + ), + }, + { + id: "quickstart", + label: t("claude.quickstart"), + body: , + }, + { + id: "smallFast", + label: t("claude.smallFastModel"), + body: ( + setState({ ...state, smallFastModel })} + /> + ), + }, + { + id: "modelMap", + label: t("claude.modelMap"), + body: { void save(); }} />, + }, + { + id: "aliases", + label: t("claude.aliases"), + body: , + }, + ]; + const selected = sections.find(s => s.id === selectedSection) ?? sections[0]!; + return ( - <> +

{t("claude.pageTitle")}

{t("claude.subtitle")}

{status && {status}} - - - setState({ ...state, smallFastModel })} - /> - { void save(); }} /> - - +
+ +
+
{selected.body}
+
+
+
); } diff --git a/gui/src/pages/Subagents.tsx b/gui/src/pages/Subagents.tsx index 6adcd5e30..9032d6626 100644 --- a/gui/src/pages/Subagents.tsx +++ b/gui/src/pages/Subagents.tsx @@ -1,16 +1,13 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useRef, useState } from "react"; import { readJsonOrThrow } from "../fetch-json"; -import { Notice, EmptyState } from "../ui"; -import { IconArrowUp, IconArrowDown, IconX, IconCheck, IconSearch, IconBot, IconInfo } from "../icons"; +import { Notice } from "../ui"; import { useT } from "../i18n/shared"; -import { Trans } from "../i18n/provider"; -import { modelLabel } from "../model-display"; +import SubagentsWorkspace from "../components/subagents-workspace/SubagentsWorkspace"; export default function Subagents({ apiBase }: { apiBase: string }) { const t = useT(); const [available, setAvailable] = useState([]); const [chosen, setChosen] = useState([]); - const [query, setQuery] = useState(""); const [status, setStatus] = useState(""); const [ok, setOk] = useState(false); const [loading, setLoading] = useState(true); @@ -18,8 +15,6 @@ export default function Subagents({ apiBase }: { apiBase: string }) { /** Sync guard: state-only `busy` can miss clicks before the disabled re-render commits. */ const saveInFlight = useRef(false); - const chosenSet = useMemo(() => new Set(chosen), [chosen]); - const load = useCallback(async () => { try { const res = await fetch(`${apiBase}/api/subagent-models`); @@ -83,11 +78,6 @@ export default function Subagents({ apiBase }: { apiBase: string }) { } }; - const filtered = useMemo(() => { - const q = query.trim().toLowerCase(); - return available.filter(m => !q || m.toLowerCase().includes(q)); - }, [available, query]); - if (loading) return
{t("sub.loading")}
; return ( @@ -95,79 +85,15 @@ export default function Subagents({ apiBase }: { apiBase: string }) {

{t("nav.subagents")}

-

- {status && {status}} - -
{t("sub.featured")} {chosen.length}/5
-
-
- {chosen.length === 0 ? ( - - ) : ( -
- {chosen.map((m, i) => ( -
- {i + 1} - {modelLabel(m)} - - - -
- ))} -
- )} - -
- -
- -
{t("sub.models")} {filtered.length}
-
-
-
- {filtered.map(m => { - const sel = chosenSet.has(m); - const full = !sel && chosen.length >= 5; - return ( - - ); - })} - {filtered.length === 0 && ( - - )} -
+ { void save(); }} + /> ); } diff --git a/gui/src/pages/Usage.tsx b/gui/src/pages/Usage.tsx index 85865a65a..b45ebe087 100644 --- a/gui/src/pages/Usage.tsx +++ b/gui/src/pages/Usage.tsx @@ -1,4 +1,4 @@ -import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react"; import { useI18n, type TFn, type Locale } from "../i18n/shared"; import { formatTokens } from "../format-tokens"; import { formatEstimatedUsdValue as formatUsdEstimate } from "../intl-formatters"; @@ -439,18 +439,37 @@ function UsageHeatmapPanel({ ); } +function UsageWorkspaceSection({ + title, + titleId, + children, +}: { + title: string; + titleId: string; + children: ReactNode; +}) { + return ( +
+

{title}

+ {children} +
+ ); +} + function UsageModelsTable({ models, modelQuery, onModelQuery, locale, t, + workspace = false, }: { models: UsageModel[]; modelQuery: string; onModelQuery: (query: string) => void; locale: Locale; t: TFn; + workspace?: boolean; }) { const searchLabel = t("usage.search.models"); const sectionLabel = t("usage.section.models"); @@ -493,6 +512,15 @@ function UsageModelsTable({ ); + if (workspace) { + return ( + +
{searchInput}
+ {table} +
+ ); + } + return (
@@ -508,10 +536,12 @@ function UsageProvidersTable({ providers, locale, t, + workspace = false, }: { providers: UsageProvider[]; locale: Locale; t: TFn; + workspace?: boolean; }) { const sectionLabel = t("usage.section.providers"); const titleId = "usage-providers-title"; @@ -542,6 +572,14 @@ function UsageProvidersTable({
); + if (workspace) { + return ( + + {table} + + ); + } + return (

{sectionLabel}

@@ -553,9 +591,11 @@ function UsageProvidersTable({ function UsageCoveragePanel({ summary, t, + workspace = false, }: { summary: UsageSummaryTotals; t: TFn; + workspace?: boolean; }) { const sectionLabel = t("usage.section.coverage"); const titleId = "usage-coverage-title"; @@ -572,6 +612,14 @@ function UsageCoveragePanel({ ); + if (workspace) { + return ( + + {body} + + ); + } + return (

{sectionLabel}

@@ -580,6 +628,114 @@ function UsageCoveragePanel({ ); } +/** + * Workspace layout for Usage: left rail picks one report section so Overview / + * Models / Providers / Coverage do not stack into a long scroll. + */ +function UsageWorkspaceBody({ + data, + loading, + heatmap, + weekBars, + activeDays, + filteredModels, + modelQuery, + onModelQuery, + sortedProviders, + range, + selectedSection, + onSelectSection, + locale, + t, +}: { + data: UsageResponse | null; + loading: boolean; + heatmap: ReturnType; + weekBars: UsageDay[]; + activeDays: number; + filteredModels: UsageModel[]; + modelQuery: string; + onModelQuery: (query: string) => void; + sortedProviders: UsageProvider[]; + range: Range; + selectedSection: string; + onSelectSection: (id: string) => void; + locale: Locale; + t: TFn; +}) { + const empty = !!data && data.summary.requests === 0; + const sections = [ + { + id: "overview", + label: t("usage.section.overview"), + meta: data ? `${data.summary.requests}` : "—", + body: data ? ( + <> + + + + ) : null, + }, + { + id: "models", + label: t("usage.section.models"), + meta: data ? `${data.models.length}` : "—", + body: data + ? + : null, + }, + { + id: "providers", + label: t("usage.section.providers"), + meta: data ? `${data.providers.length}` : "—", + body: data + ? + : null, + }, + { + id: "coverage", + label: t("usage.section.coverage"), + meta: data ? formatPct(data.summary.coverageRatio) : "—", + body: data ? : null, + }, + ]; + const selected = sections.find(s => s.id === selectedSection) ?? sections[0]; + const mainBody = loading && !data + ? + : empty + ? + : selected.body; + + return ( +
+
+ +
+
{mainBody}
+
+
+
+ ); +} + export default function Usage({ apiBase }: { apiBase: string }) { const { t, locale } = useI18n(); const [range, setRange] = useState("30d"); @@ -588,6 +744,7 @@ export default function Usage({ apiBase }: { apiBase: string }) { const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [modelQuery, setModelQuery] = useState(""); + const [selectedSection, setSelectedSection] = useState("overview"); const loadGenerationRef = useRef(0); const fetchUsage = useCallback(async (nextRange: Range, nextSurface: UsageSurface, signal: AbortSignal) => { @@ -666,19 +823,24 @@ export default function Usage({ apiBase }: { apiBase: string }) { {t("common.retry")} - ) : loading && !data ? ( - - ) : data?.summary.requests === 0 ? ( - - ) : data ? ( - <> - - - - - - - ) : null} + ) : ( + + )} ); } diff --git a/gui/src/pages/claude-code-sections.tsx b/gui/src/pages/claude-code-sections.tsx index c2a5966d5..5e31ce211 100644 --- a/gui/src/pages/claude-code-sections.tsx +++ b/gui/src/pages/claude-code-sections.tsx @@ -27,10 +27,12 @@ function authSourceLabel(source: string | undefined, t: TFn): string { export function ClaudeCodeSettingsCard({ state, autoCompactOptions, + availableModels, onStateChange, }: { state: ClaudeCodeState; autoCompactOptions: { value: string; label: string }[]; + availableModels: string[]; onStateChange: (next: ClaudeCodeState) => void; }) { const t = useT(); @@ -50,37 +52,38 @@ export function ClaudeCodeSettingsCard({ {t("claude.authMode")} {t("claude.authModeHint")} - onStateChange({ ...state, authMode: v as ClaudeCodeState["authMode"] })} + label={t("claude.authMode")} + style={{ minWidth: 220 }} + align="right" + portal + /> + {state.authModeOrigin && ( -
-
- {t("claude.effectiveMode.label")} - - {state.authModeOrigin === "manual" - ? t("claude.effectiveMode.manual", { - mode: state.markerMode === "proxy" ? t("claude.authModeProxy") : t("claude.authModeSubscription"), - }) - : state.authModeOrigin === "auto-present" - ? t("claude.effectiveMode.autoPresent", { source: authSourceLabel(state.authFoundBy, t) }) - : state.authModeOrigin === "auto-absent" - ? t("claude.effectiveMode.autoAbsent") - : t("claude.effectiveMode.autoUnknown")} - {state.admissionKeyActive === true ? ` ${t("claude.effectiveMode.admissionKey")}` : ""} - -
+
+ {t("claude.effectiveMode.label")} + + {state.authModeOrigin === "manual" + ? t("claude.effectiveMode.manual", { + mode: state.markerMode === "proxy" ? t("claude.authModeProxy") : t("claude.authModeSubscription"), + }) + : state.authModeOrigin === "auto-present" + ? t("claude.effectiveMode.autoPresent", { source: authSourceLabel(state.authFoundBy, t) }) + : state.authModeOrigin === "auto-absent" + ? t("claude.effectiveMode.autoAbsent") + : t("claude.effectiveMode.autoUnknown")} + {state.admissionKeyActive === true ? ` ${t("claude.effectiveMode.admissionKey")}` : ""} +
)} @@ -95,20 +98,21 @@ export function ClaudeCodeSettingsCard({ {t("claude.fastMode")} {t("claude.fastModeDesc")}
- +
+ onStateChange({ ...state, autoCompactWindow: v === "" ? null : Number(v) })} - label={t("claude.autoCompactWindow")} - style={{ minWidth: 130 }} - portal - /> +
+