From eea71403c5da705c3360d75fc78346ff5d02ac3a Mon Sep 17 00:00:00 2001 From: Gennadi Ryan Date: Mon, 24 Aug 2026 22:15:33 -0400 Subject: [PATCH 1/4] Adding devcontainer/vsix build path to developer tools panel (currently marked experimental) --- .../settings-v2/developer-tools-controller.ts | 44 ++++++++++++++ .../settings-v2/developer-tools.tsx | 59 +++++++++++++++++++ packages/app/src/context/settings.tsx | 12 ++++ 3 files changed, 115 insertions(+) diff --git a/packages/app/src/components/settings-v2/developer-tools-controller.ts b/packages/app/src/components/settings-v2/developer-tools-controller.ts index 12a6e867b..76ada90f9 100644 --- a/packages/app/src/components/settings-v2/developer-tools-controller.ts +++ b/packages/app/src/components/settings-v2/developer-tools-controller.ts @@ -25,6 +25,9 @@ export function createDeveloperToolsController() { const [pending, setPending] = createSignal(false) const [rebuildState, setRebuildState] = createSignal("idle") const [rebuildError, setRebuildError] = createSignal(undefined) + const [vsixBuildState, setVsixBuildState] = createSignal("idle") + const [vsixBuildError, setVsixBuildError] = createSignal(undefined) + const [vsixPath, setVsixPath] = createSignal(undefined) // On mount, check if we just came back from a rebuild (successful or in-progress). // The "rebuilding" flag survives iframe reloads caused by file-watcher churn @@ -102,6 +105,21 @@ export function createDeveloperToolsController() { // The window reload follows shortly — "rebuilt" flag is read on next mount } } + + // Devcontainer VSIX build status messages + if (d && d.source === "amicode" && d.kind === "dev-tools-build-vsix-status") { + if (d.state === "building") { + setVsixBuildState("rebuilding") + setVsixBuildError(undefined) + setVsixPath(undefined) + } else if (d.state === "failed") { + setVsixBuildState("failed") + setVsixBuildError(d.error ?? "Unknown error") + } else if (d.state === "done") { + setVsixBuildState("rebuilt") + setVsixPath(d.vsixPath) + } + } } if (typeof window !== "undefined") { @@ -201,6 +219,32 @@ export function createDeveloperToolsController() { pending, rebuildState, rebuildError, + // Devcontainer VSIX build + devcontainerMode: settings.developer.devcontainerMode, + setDevcontainerMode: (value: boolean) => { + settings.developer.setDevcontainerMode(value) + }, + vsixOutputPath: settings.developer.vsixOutputPath, + setVsixOutputPath: (value: string) => { + settings.developer.setVsixOutputPath(value) + }, + buildVsix: () => { + if (!inAmicode()) return + if (vsixBuildState() === "rebuilding") return + setVsixBuildState("rebuilding") + setVsixBuildError(undefined) + setVsixPath(undefined) + window.parent.postMessage({ + source: "amicode", + kind: "dev-tools-build-vsix", + opencodePath: settings.developer.opencodePath(), + amicodePath: settings.developer.amicodePath(), + outputPath: settings.developer.vsixOutputPath(), + }, "*") + }, + vsixBuildState, + vsixBuildError, + vsixPath, } } diff --git a/packages/app/src/components/settings-v2/developer-tools.tsx b/packages/app/src/components/settings-v2/developer-tools.tsx index 5773b900a..18582ec57 100644 --- a/packages/app/src/components/settings-v2/developer-tools.tsx +++ b/packages/app/src/components/settings-v2/developer-tools.tsx @@ -160,6 +160,65 @@ const DeveloperToolsContent: Component<{ controller: DeveloperToolsController }> /> + + {/* Devcontainer VSIX build (experimental) */} + + + props.controller.setDevcontainerMode(checked)} + /> + + + + +
+ props.controller.setVsixOutputPath(e.currentTarget.value)} + placeholder="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/workspaces/artifacts/" + spellcheck={false} + autocorrect="off" + autocomplete="off" + autocapitalize="off" + aria-label="VSIX output path" + /> +
+
+ +
+ props.controller.buildVsix()} + disabled={props.controller.vsixBuildState() === "rebuilding"} + > + {props.controller.vsixBuildState() === "rebuilding" ? "Building VSIX..." : "Build VSIX"} + +
+ + +
+ + VSIX built: {props.controller.vsixPath()} +
+
+ +
+ + {props.controller.vsixBuildError()} +
+
+
+
) } diff --git a/packages/app/src/context/settings.tsx b/packages/app/src/context/settings.tsx index 0443ed28b..121cdcb9f 100644 --- a/packages/app/src/context/settings.tsx +++ b/packages/app/src/context/settings.tsx @@ -55,6 +55,8 @@ export interface Settings { enabled: boolean opencodePath: string amicodePath: string + devcontainerMode: boolean + vsixOutputPath: string } keybinds: Record permissions: { @@ -220,6 +222,8 @@ const defaultSettings: Settings = { enabled: false, opencodePath: "", amicodePath: "", + devcontainerMode: false, + vsixOutputPath: "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/workspaces/artifacts/", }, keybinds: {}, permissions: { @@ -575,6 +579,14 @@ export const { use: useSettings, provider: SettingsProvider } = createSimpleCont setAmicodePath(value: string) { setStore("developer", "amicodePath", value) }, + devcontainerMode: withFallback(() => store.developer?.devcontainerMode, defaultSettings.developer.devcontainerMode), + setDevcontainerMode(value: boolean) { + setStore("developer", "devcontainerMode", value) + }, + vsixOutputPath: withFallback(() => store.developer?.vsixOutputPath, defaultSettings.developer.vsixOutputPath), + setVsixOutputPath(value: string) { + setStore("developer", "vsixOutputPath", value) + }, }, } }, From 2c1fc9b8b1b214ca70fb3e83c8f1b92135ded7aa Mon Sep 17 00:00:00 2001 From: Gennadi Ryan Date: Tue, 25 Aug 2026 00:31:05 -0400 Subject: [PATCH 2/4] Developer tools experimental feature isolation (previously was hidden behind developer mode gate, causing undesirable eager build-time races) --- .../settings-v2/developer-tools-controller.ts | 10 +- .../settings-v2/developer-tools.tsx | 237 +++++++++--------- 2 files changed, 126 insertions(+), 121 deletions(-) diff --git a/packages/app/src/components/settings-v2/developer-tools-controller.ts b/packages/app/src/components/settings-v2/developer-tools-controller.ts index 76ada90f9..faf19b747 100644 --- a/packages/app/src/components/settings-v2/developer-tools-controller.ts +++ b/packages/app/src/components/settings-v2/developer-tools-controller.ts @@ -210,9 +210,13 @@ export function createDeveloperToolsController() { setAmicodePath: (value: string) => { settings.developer.setAmicodePath(value) }, - /** Trigger validation + apply on blur */ - commitOpencodePath: () => sendUpdate(), - commitAmicodePath: () => sendUpdate(), + /** Trigger validation + apply on blur — only in developer mode (not devcontainer-only mode) */ + commitOpencodePath: () => { + if (settings.developer.enabled()) sendUpdate() + }, + commitAmicodePath: () => { + if (settings.developer.enabled()) sendUpdate() + }, /** Trigger a full rebuild (local = from disk, remote = git pull first) */ rebuild, status, diff --git a/packages/app/src/components/settings-v2/developer-tools.tsx b/packages/app/src/components/settings-v2/developer-tools.tsx index 18582ec57..590de7f68 100644 --- a/packages/app/src/components/settings-v2/developer-tools.tsx +++ b/packages/app/src/components/settings-v2/developer-tools.tsx @@ -57,7 +57,7 @@ const DeveloperToolsContent: Component<{ controller: DeveloperToolsController }> return ( - {/* Rebuild buttons */} + {/* Action buttons row — at the top for discoverability */}
+ +
+ props.controller.buildVsix()} + disabled={props.controller.vsixBuildState() === "rebuilding"} + > + {props.controller.vsixBuildState() === "rebuilding" ? "Building VSIX..." : "Build VSIX"} + +
+ +
+ + VSIX built: {props.controller.vsixPath()} +
+
+ +
+ + {props.controller.vsixBuildError()} +
+
+
+ {/* Toggle 1: Developer mode (hot-reload, eager actions) */} + {/* Toggle 2: Devcontainer mode (vsix build, no eager actions) — independent */} - {language.t("settings.general.row.opencodePath.description")} - - {opencodeError()} - - - } - > -
- props.controller.setOpencodePath(event.currentTarget.value)} - onBlur={() => props.controller.commitOpencodePath()} - placeholder={language.t("settings.general.row.opencodePath.placeholder")} - disabled={!props.controller.enabled()} - spellcheck={false} - autocorrect="off" - autocomplete="off" - autocapitalize="off" - aria-label={language.t("settings.general.row.opencodePath.title")} - /> -
-
- - - {language.t("settings.general.row.amicodePath.description")} - - - {language.t("settings.general.row.amicodePath.building")} - - - - {amicodeError()} - - - - {language.t("settings.general.row.amicodePath.reloadNeeded")} - - - - } + title="Devcontainer mode (experimental)" + description="Build a .vsix for manual installation instead of hot-reloading" > -
- props.controller.setAmicodePath(event.currentTarget.value)} - onBlur={() => props.controller.commitAmicodePath()} - placeholder={language.t("settings.general.row.amicodePath.placeholder")} - disabled={!props.controller.enabled()} - spellcheck={false} - autocorrect="off" - autocomplete="off" - autocapitalize="off" - aria-label={language.t("settings.general.row.amicodePath.title")} - /> -
+ props.controller.setDevcontainerMode(checked)} + />
- {/* Devcontainer VSIX build (experimental) */} - + {/* Path inputs — visible when EITHER mode is ON */} + + {language.t("settings.general.row.opencodePath.description")} + + {opencodeError()} + + + } > - props.controller.setDevcontainerMode(checked)} - /> +
+ props.controller.setOpencodePath(event.currentTarget.value)} + onBlur={() => props.controller.commitOpencodePath()} + placeholder={language.t("settings.general.row.opencodePath.placeholder")} + spellcheck={false} + autocorrect="off" + autocomplete="off" + autocapitalize="off" + aria-label={language.t("settings.general.row.opencodePath.title")} + /> +
- - -
- props.controller.setVsixOutputPath(e.currentTarget.value)} - placeholder="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/workspaces/artifacts/" - spellcheck={false} - autocorrect="off" - autocomplete="off" - autocapitalize="off" - aria-label="VSIX output path" - /> -
-
- -
- props.controller.buildVsix()} - disabled={props.controller.vsixBuildState() === "rebuilding"} - > - {props.controller.vsixBuildState() === "rebuilding" ? "Building VSIX..." : "Build VSIX"} - + + {language.t("settings.general.row.amicodePath.description")} + + + {language.t("settings.general.row.amicodePath.building")} + + + + {amicodeError()} + + + + {language.t("settings.general.row.amicodePath.reloadNeeded")} + + + + } + > +
+ props.controller.setAmicodePath(event.currentTarget.value)} + onBlur={() => props.controller.commitAmicodePath()} + placeholder={language.t("settings.general.row.amicodePath.placeholder")} + spellcheck={false} + autocorrect="off" + autocomplete="off" + autocapitalize="off" + aria-label={language.t("settings.general.row.amicodePath.title")} + />
+
+ - -
- - VSIX built: {props.controller.vsixPath()} -
-
- -
- - {props.controller.vsixBuildError()} -
-
- + {/* VSIX output path — only when devcontainer mode is ON */} + + +
+ props.controller.setVsixOutputPath(e.currentTarget.value)} + placeholder="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/workspaces/artifacts/" + spellcheck={false} + autocorrect="off" + autocomplete="off" + autocapitalize="off" + aria-label="VSIX output path" + /> +
+
) From 0ff0ce737b9b9322c6286258300cb61e1d38eb6d Mon Sep 17 00:00:00 2001 From: Gennadi Ryan Date: Tue, 25 Aug 2026 03:26:37 -0400 Subject: [PATCH 3/4] One-line typecheck fix --- packages/app/src/components/settings-v2/developer-tools.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/app/src/components/settings-v2/developer-tools.tsx b/packages/app/src/components/settings-v2/developer-tools.tsx index 590de7f68..904244708 100644 --- a/packages/app/src/components/settings-v2/developer-tools.tsx +++ b/packages/app/src/components/settings-v2/developer-tools.tsx @@ -92,7 +92,7 @@ const DeveloperToolsContent: Component<{ controller: DeveloperToolsController }>
- VSIX built: {props.controller.vsixPath()} + VSIX built: {props.controller.vsixPath() ?? ""}
From 935a3e544935c336cd539af2a9d5138590c18762 Mon Sep 17 00:00:00 2001 From: Gennadi Ryan Date: Tue, 25 Aug 2026 03:54:40 -0400 Subject: [PATCH 4/4] Adding coderabbit-suggested bad build guard (failure fallback on no valid vsix output path) --- .../components/settings-v2/developer-tools-controller.ts | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/packages/app/src/components/settings-v2/developer-tools-controller.ts b/packages/app/src/components/settings-v2/developer-tools-controller.ts index faf19b747..b15414690 100644 --- a/packages/app/src/components/settings-v2/developer-tools-controller.ts +++ b/packages/app/src/components/settings-v2/developer-tools-controller.ts @@ -116,8 +116,13 @@ export function createDeveloperToolsController() { setVsixBuildState("failed") setVsixBuildError(d.error ?? "Unknown error") } else if (d.state === "done") { - setVsixBuildState("rebuilt") - setVsixPath(d.vsixPath) + if (typeof d.vsixPath === "string" && d.vsixPath.trim() !== "") { + setVsixBuildState("rebuilt") + setVsixPath(d.vsixPath) + } else { + setVsixBuildState("failed") + setVsixBuildError("Build completed but no output path was reported") + } } } }