From c3e777e3e12c30637bcf893a6eeeed9222a4abb1 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 25 Jan 2026 02:42:31 +0000 Subject: [PATCH] fix: keep raw config edits scoped to config view (#1673) (thanks @Glucksberg) --- CHANGELOG.md | 1 + ...s-writing-models-json-no-env-token.test.ts | 28 +++++++++++++++++++ ui/src/ui/app-render.ts | 1 - ui/src/ui/views/config.browser.test.ts | 28 +++++++++++++++++++ 4 files changed, 57 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6661a5a3d..281ea1e06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -21,6 +21,7 @@ Docs: https://docs.clawd.bot - BlueBubbles: keep part-index GUIDs in reply tags when short IDs are missing. - Web UI: hide internal `message_id` hints in chat bubbles. - Web UI: show Stop button during active runs, swap back to New session when idle. (#1664) Thanks @ndbroadbent. +- Web UI: keep raw config edits from toggling channel save state; enable save/apply on raw changes only. (#1673) Thanks @Glucksberg. - Heartbeat: normalize target identifiers for consistent routing. - TUI: reload history after gateway reconnect to restore session state. (#1663) - Telegram: use wrapped fetch for long-polling on Node to normalize AbortSignal handling. (#1639) diff --git a/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts b/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts index 29ebe3265..221410316 100644 --- a/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts +++ b/src/agents/models-config.skips-writing-models-json-no-env-token.test.ts @@ -48,15 +48,29 @@ describe("models-config", () => { const previous = process.env.COPILOT_GITHUB_TOKEN; const previousGh = process.env.GH_TOKEN; const previousGithub = process.env.GITHUB_TOKEN; + const previousVenice = process.env.VENICE_API_KEY; + const previousKimiCode = process.env.KIMICODE_API_KEY; + const previousKimiCodeAlt = process.env.KIMI_CODE_API_KEY; const previousMinimax = process.env.MINIMAX_API_KEY; const previousMoonshot = process.env.MOONSHOT_API_KEY; const previousSynthetic = process.env.SYNTHETIC_API_KEY; + const previousAwsAccessKey = process.env.AWS_ACCESS_KEY_ID; + const previousAwsSecretKey = process.env.AWS_SECRET_ACCESS_KEY; + const previousAwsProfile = process.env.AWS_PROFILE; + const previousAwsBearer = process.env.AWS_BEARER_TOKEN; delete process.env.COPILOT_GITHUB_TOKEN; delete process.env.GH_TOKEN; delete process.env.GITHUB_TOKEN; + delete process.env.VENICE_API_KEY; + delete process.env.KIMICODE_API_KEY; + delete process.env.KIMI_CODE_API_KEY; delete process.env.MINIMAX_API_KEY; delete process.env.MOONSHOT_API_KEY; delete process.env.SYNTHETIC_API_KEY; + delete process.env.AWS_ACCESS_KEY_ID; + delete process.env.AWS_SECRET_ACCESS_KEY; + delete process.env.AWS_PROFILE; + delete process.env.AWS_BEARER_TOKEN; try { vi.resetModules(); @@ -79,12 +93,26 @@ describe("models-config", () => { else process.env.GH_TOKEN = previousGh; if (previousGithub === undefined) delete process.env.GITHUB_TOKEN; else process.env.GITHUB_TOKEN = previousGithub; + if (previousVenice === undefined) delete process.env.VENICE_API_KEY; + else process.env.VENICE_API_KEY = previousVenice; + if (previousKimiCode === undefined) delete process.env.KIMICODE_API_KEY; + else process.env.KIMICODE_API_KEY = previousKimiCode; + if (previousKimiCodeAlt === undefined) delete process.env.KIMI_CODE_API_KEY; + else process.env.KIMI_CODE_API_KEY = previousKimiCodeAlt; if (previousMinimax === undefined) delete process.env.MINIMAX_API_KEY; else process.env.MINIMAX_API_KEY = previousMinimax; if (previousMoonshot === undefined) delete process.env.MOONSHOT_API_KEY; else process.env.MOONSHOT_API_KEY = previousMoonshot; if (previousSynthetic === undefined) delete process.env.SYNTHETIC_API_KEY; else process.env.SYNTHETIC_API_KEY = previousSynthetic; + if (previousAwsAccessKey === undefined) delete process.env.AWS_ACCESS_KEY_ID; + else process.env.AWS_ACCESS_KEY_ID = previousAwsAccessKey; + if (previousAwsSecretKey === undefined) delete process.env.AWS_SECRET_ACCESS_KEY; + else process.env.AWS_SECRET_ACCESS_KEY = previousAwsSecretKey; + if (previousAwsProfile === undefined) delete process.env.AWS_PROFILE; + else process.env.AWS_PROFILE = previousAwsProfile; + if (previousAwsBearer === undefined) delete process.env.AWS_BEARER_TOKEN; + else process.env.AWS_BEARER_TOKEN = previousAwsBearer; } }); }); diff --git a/ui/src/ui/app-render.ts b/ui/src/ui/app-render.ts index d9834d0ec..0543950a3 100644 --- a/ui/src/ui/app-render.ts +++ b/ui/src/ui/app-render.ts @@ -512,7 +512,6 @@ export function renderApp(state: AppViewState) { activeSubsection: state.configActiveSubsection, onRawChange: (next) => { state.configRaw = next; - state.configFormDirty = true; }, onFormModeChange: (mode) => (state.configFormMode = mode), onFormPatch: (path, value) => updateConfigFormValue(state, path, value), diff --git a/ui/src/ui/views/config.browser.test.ts b/ui/src/ui/views/config.browser.test.ts index c64a4c788..bcd240fbb 100644 --- a/ui/src/ui/views/config.browser.test.ts +++ b/ui/src/ui/views/config.browser.test.ts @@ -96,6 +96,34 @@ describe("config view", () => { expect(applyButton?.disabled).toBe(true); }); + it("enables save and apply when raw changes", () => { + const container = document.createElement("div"); + render( + renderConfig({ + ...baseProps(), + formMode: "raw", + raw: "{\n gateway: { mode: \"local\" }\n}\n", + originalRaw: "{\n}\n", + }), + container, + ); + + const saveButton = Array.from( + container.querySelectorAll("button"), + ).find((btn) => btn.textContent?.trim() === "Save") as + | HTMLButtonElement + | undefined; + const applyButton = Array.from( + container.querySelectorAll("button"), + ).find((btn) => btn.textContent?.trim() === "Apply") as + | HTMLButtonElement + | undefined; + expect(saveButton).not.toBeUndefined(); + expect(applyButton).not.toBeUndefined(); + expect(saveButton?.disabled).toBe(false); + expect(applyButton?.disabled).toBe(false); + }); + it("switches mode via the sidebar toggle", () => { const container = document.createElement("div"); const onFormModeChange = vi.fn();