From d0d03bed636f8364ec65970d2d80d949e337d3d1 Mon Sep 17 00:00:00 2001
From: Wibias <37517432+Wibias@users.noreply.github.com>
Date: Sun, 26 Jul 2026 03:53:29 +0200
Subject: [PATCH 1/7] fix(gui): re-split ClaudeCode and ApiKeys after
foundations rebase
Rebase onto upstream/dev and re-apply the react-doctor module split against current stacked pages, including API key load/create/delete failure UI state.
---
gui/src/i18n/de.ts | 3 +
gui/src/i18n/en.ts | 3 +
gui/src/i18n/ja.ts | 3 +
gui/src/i18n/ko.ts | 3 +
gui/src/i18n/ru.ts | 3 +
gui/src/i18n/zh.ts | 3 +
gui/src/pages/ApiKeys.tsx | 382 +++++++---------------
gui/src/pages/ClaudeCode.tsx | 422 +++----------------------
gui/src/pages/api-keys-panels.tsx | 332 +++++++++++++++++++
gui/src/pages/api-keys-utils.ts | 41 +++
gui/src/pages/claude-code-sections.tsx | 265 ++++++++++++++++
gui/src/pages/claude-code-settings.tsx | 106 +++++++
gui/src/pages/claude-code-types.ts | 31 ++
gui/tests/apikeys-layout.test.ts | 20 +-
gui/tests/claudecode-layout.test.ts | 10 +-
15 files changed, 963 insertions(+), 664 deletions(-)
create mode 100644 gui/src/pages/api-keys-panels.tsx
create mode 100644 gui/src/pages/api-keys-utils.ts
create mode 100644 gui/src/pages/claude-code-sections.tsx
create mode 100644 gui/src/pages/claude-code-settings.tsx
create mode 100644 gui/src/pages/claude-code-types.ts
diff --git a/gui/src/i18n/de.ts b/gui/src/i18n/de.ts
index 76639a7f8..e74c3599e 100644
--- a/gui/src/i18n/de.ts
+++ b/gui/src/i18n/de.ts
@@ -765,6 +765,9 @@ export const de: Record
{endpoints.baseUrl}
- {endpoints.responses}
- {endpoints.chatCompletions}
- {endpoints.messages}
- {endpoints.models}
- {t("api.endpointNote")}
-{t("api.authBaseUrlNote")}
-{t("api.newKeyNote")}
-{newKey}
-
- {t("api.noKeys")}
- ) : ( -| {t("api.colName")} | {t("api.colKey")} | {t("api.colCreated")} | |
|---|---|---|---|
| {k.name} | -{k.prefix} |
- {formatCreatedDate(k.createdAt, localeTag)} | -- {confirmDelete === k.id ? ( - - - - - ) : ( - - )} - | -
{t("api.modelsSubtitle")}
- setModelQuery(event.target.value)} - placeholder={t("api.modelsSearch")} - aria-label={t("api.modelsSearch")} - /> - {modelsLoading ? ( -{t("api.modelsLoading")}
- ) : modelsLoadFailed ? ( -{t("api.modelsLoadFailed")}
- ) : filteredModels.length === 0 ? ( -{t("api.modelsEmpty")}
- ) : ( -| {t("api.colModel")} | -{t("api.colSource")} | -{t("api.colProtocols")} | -- |
|---|---|---|---|
|
-
-
- {modelId}
- {model.displayName !== model.id && {model.displayName}}
- |
- {sourceLabel(model)} | -{gatewayInboundProtocols(claudeCodeEnabled).map(protocolLabel).join(", ")} | -
-
-
-
-
- {testState === "ok" && {t("api.testSucceeded")} } - {testState === "error" &&{modelTests[modelId]?.detail ?? t("api.testFailed")} } - |
-
{`curl ${endpoints.chatCompletions} \\
- -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
- -H "Content-Type: application/json" \\
- -d '{
- "model": "gpt-5.4",
- "messages": [{"role": "user", "content": ${JSON.stringify(t("api.usageSampleInput"))}}]
- }'`}
- {`curl ${endpoints.responses} \\
- -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
- -H "Content-Type: application/json" \\
- -d '{
- "model": "gpt-5.4",
- "input": ${JSON.stringify(t("api.usageSampleInput"))}
- }'`}
- {`curl ${endpoints.messages} \\
- -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
- -H "Content-Type: application/json" \\
- -d '{
- "model": "claude-sonnet-4-6",
- "max_tokens": 64,
- "messages": [{"role": "user", "content": ${JSON.stringify(t("api.usageSampleInput"))}}]
- }'`}
- - {t("claude.smallFastModelAccurateHint")} -
- - {effectiveHelperModel === "" && ( -- {t("claude.smallFastModelNativeWarning")} -
- )} - > - ); -} +import { buildManualEnv } from "./claude-manual-env"; +import { + ClaudeCodeAliasesSection, + ClaudeCodeModelMapSection, + ClaudeCodeQuickstartSection, + ClaudeCodeSettingsCard, +} from "./claude-code-sections"; +import { formatCompactWindow, type ClaudeCodeState, type MapRow } from "./claude-code-types"; +import { SmallFastModelSetting } from "./claude-code-settings"; + +export { AutoConnectSetting, SmallFastModelSetting } from "./claude-code-settings"; export default function ClaudeCode({ apiBase }: { apiBase: string }) { const t = useT(); @@ -147,7 +26,16 @@ export default function ClaudeCode({ apiBase }: { apiBase: string }) { const load = useCallback(async () => { try { - const r = await fetch(`${apiBase}/api/claude-code`).then(res => res.json()); + const res = await fetch(`${apiBase}/api/claude-code`); + const r = await readJsonOrThrowocx claude-
{manualEnv}
- {t("claude.modelMapHint")}
-{t("claude.aliasesHint")}
- {state.aliases.length === 0 ? ( -{a.id}
- {a.display_name}
- {t("claude.subtitle")}
{status &&{endpoints.baseUrl}
+ {endpoints.responses}
+ {endpoints.chatCompletions}
+ {endpoints.messages}
+ {endpoints.models}
+ {t("api.endpointNote")}
+{t("api.authBaseUrlNote")}
+{t("api.newKeyNote")}
+{newKey}
+
+ {t("api.keysLoadFailed")}
+ ) : keys.length === 0 ? ( +{t("api.noKeys")}
+ ) : ( +| {t("api.colName")} | {t("api.colKey")} | {t("api.colCreated")} | |
|---|---|---|---|
| {k.name} | +{k.prefix} |
+ {formatCreatedDate(k.createdAt, localeTag)} | ++ {confirmDelete === k.id ? ( + + + + + ) : ( + + )} + | +
{t("api.modelsSubtitle")}
+ onModelQueryChange(event.target.value)} + placeholder={t("api.modelsSearch")} + aria-label={t("api.modelsSearch")} + /> + {modelsLoading ? ( +{t("api.modelsLoading")}
+ ) : modelsLoadFailed ? ( +{t("api.modelsLoadFailed")}
+ ) : filteredModels.length === 0 ? ( +{t("api.modelsEmpty")}
+ ) : ( +| {t("api.colModel")} | +{t("api.colSource")} | +{t("api.colProtocols")} | ++ |
|---|---|---|---|
|
+
+
+ {modelId}
+ {model.displayName !== model.id && {model.displayName}}
+ |
+ {sourceLabel(model)} | +{gatewayInboundProtocols(claudeCodeEnabled).map(protocolLabel).join(", ")} | +
+
+
+
+
+ {testState === "ok" && {t("api.testSucceeded")} } + {testState === "error" &&{modelTests[modelId]?.detail ?? t("api.testFailed")} } + |
+
{`curl ${endpoints.chatCompletions} \\
+ -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
+ -H "Content-Type: application/json" \\
+ -d '{
+ "model": "gpt-5.4",
+ "messages": [{"role": "user", "content": ${sampleInput}}]
+ }'`}
+ {`curl ${endpoints.responses} \\
+ -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
+ -H "Content-Type: application/json" \\
+ -d '{
+ "model": "gpt-5.4",
+ "input": ${sampleInput}
+ }'`}
+ {`curl ${endpoints.messages} \\
+ -H "x-opencodex-api-key: ocx_YOUR_KEY_HERE" \\
+ -H "Content-Type: application/json" \\
+ -d '{
+ "model": "claude-sonnet-4-6",
+ "max_tokens": 64,
+ "messages": [{"role": "user", "content": ${sampleInput}}]
+ }'`}
+ ocx claude+
{manualEnv}
+ {t("claude.modelMapHint")}
+{t("claude.aliasesHint")}
+ {aliases.length === 0 ? ( +{a.id}
+ {a.display_name}
+ + {t("claude.smallFastModelAccurateHint")} +
+ + {effectiveHelperModel === "" && ( ++ {t("claude.smallFastModelNativeWarning")} +
+ )} + > + ); +} diff --git a/gui/src/pages/claude-code-types.ts b/gui/src/pages/claude-code-types.ts new file mode 100644 index 000000000..231e505e7 --- /dev/null +++ b/gui/src/pages/claude-code-types.ts @@ -0,0 +1,31 @@ +import type { SidecarOverride } from "./claude-manual-env"; + +export interface MapRow { + from: string; + to: string; +} + +export interface ClaudeCodeState { + enabled: boolean; + authMode: "subscription" | "proxy"; + autoConnectSupported: boolean; + systemEnv: boolean; + fastMode: boolean | null; + /** Legacy config override (no GUI control anymore) — still disables auto-context when hand-set. */ + maxContextTokens: number | null; + autoContext: boolean; + autoCompactWindow: number | null; + injectAgents: boolean; + smallFastModel: string; + tierModels?: { haiku?: string }; + effectiveModelEnv: Record{t("api.keysLoadFailed")}
- ) : keys.length === 0 ? ( -{t("api.noKeys")}
- ) : ( + {keys.length > 0 ? ({t("api.keysLoadFailed")}
+ ) : ( +{t("api.noKeys")}
)}