You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Cmd/Ctrl +/-/0 did nothing while focus was inside the Amicode chat webview (see #266). The chords were wired to route through the extension bridge up to top-level VS Code (workbench.action.zoom*), but top-level VS Code can never see keydowns that originate inside the app iframe — so the forward path could not fire in the only place that mattered, and where it did fire, it double-fired.
V2 (alternative) — workbench-owned zoom: same in-document capture, intents forwarded up the bridge to workbench.action.zoom*. PR: none yet (may be dropped if V1 suffices)
Why the previous approach failed
The previous wiring had three layers — the workbench keybinding service, the webview host page, and the app iframe (plus split-frame panes, each a full app instance) — with zoom intents posted as amicode bridge envelopes ("zoom" kind) and executed as workbench zoom actions. Round-3 remote instrumentation (a raw-capture probe in the app documents, an eager "Amicode — webview diag" output channel, and a host-page keydown listener) established the failure modes:
Top-level VS Code is blind inside the webview. Keydown events are routed within the document that owns focus; an iframe's document is a separate document, so chords typed with focus inside the app never reach the host page or the workbench keybinding service. (Ctrl/Cmd+Shift+P "working" from the webview is the app's own bridge forward, not keybinding-service forwarding.) No extension keybinding or command can fire while focus is inside the app — the envelope route had no capture point there.
The host page is the wrong capture layer. With focus inside the app iframe, the host document never receives the chords (frame-local event routing) — total silence at every layer. With focus on the webview's own host document, the webview forwards host-document keydowns to the workbench regardless of preventDefault, so the host-page listener fired in addition to the workbench's native handling: one keypress produced two zoom steps (observed: tab focus → double zoom per press, one log line, two presses to reverse).
Layout-sensitive matching in the app registry. The in-app command registry matched exact (normalized-key, modifier-mask) pairs with no fallback, and only "mod+=" / "mod+-" were registered. Ctrl+Plus on a US layout IS Ctrl+Shift+"=" — arriving as key "+" with the shift bit (wrong key, wrong mask); the numpad "+" arrives unshifted; on DE/FR/Nordic layouts "=" itself is shifted. The canonical chord matched exactly one layout's one key.
The relay chain amplified fragility without fixing capture. Pane envelopes had to hop pane → parent frame → host page → extension with source-pinning and consume semantics; every hop was spoof/loop surface, and none of it mattered — with panel focus the chords never reached any forward-capable layer.
V1 — in-app CSS zoom, no extension machinery (implemented)
Every app document (main frame and each split pane) registers a raw capture-phase keydown listener matching Ctrl/Cmd + = / + / - / _ / 0by the key the layout produces (the shift bit is ignored, so US Ctrl+Plus, the numpad "+", and shifted-"="/"-" layouts all match) and applies the existing per-window persisted CSS zoom (web-zoom.ts) to its own document. Matched chords are consumed (preventDefault + stopPropagation).
Removed: the extension zoom lane, the diag lane + eager probe, the host-page keydown listener, the zoom / diag-log allowlist entries and the "webview diag" output channel; the view.zoom* registry entries and their layout-sensitive chord sets.
Behavior: focus inside the webview → the webview content zooms (CSS, persisted per window, platform.webviewZoom signal intact for the titlebar/terminal); focus on an editor tab → the native single window zoom, unchanged. No doubles, no bridge.
Trade-off: zooms the webview content, not the whole VS Code window (that is V2's scope).
Verification: 9/9 app tests (raw-listener chord matrix + signal behavior), 17/17 extension bridge tests, tsc --noEmit clean on both sides.
Same in-document raw capture, but instead of applying CSS locally the document posts a zoom intent up the relay chain (app document → parent frame → host page → extension), which executes the matching workbench.action.zoomIn/Out/Reset.
This fixes both failure modes of the previous approach: capture moves into the iframe documents — the only layer that sees in-panel chords — and the host-page listener (the source of the tab-focus double-fire) is removed; with tab focus the app documents never see the chords, so only the native workbench zoom fires, once.
Cost: retains the relay/lane/allowlist machinery and its spoof/loop surface; zooms the whole window.
Not implemented; dropped if V1 proves sufficient.
Resolution (to be updated)
As of this stage V1 is the implemented disposition — minimal, zero extension machinery, consistent with the webview as a self-contained surface — with draft PRs #321 and harmoniqs/opencode#161 open. V2 remains the fallback should window-level zoom be desired. The final call and the V2 PR reference (if any) will be recorded here once the V1 PRs land.
Suggested labels
zoom, webview, keybindings, bug / enhancement per the final call.
Webview zoom keybindings: capture/forwarding problems and disposition (V1 implemented, V2 pending)
Summary
Cmd/Ctrl
+/-/0did nothing while focus was inside the Amicode chat webview (see #266). The chords were wired to route through the extension bridge up to top-level VS Code (workbench.action.zoom*), but top-level VS Code can never see keydowns that originate inside the app iframe — so the forward path could not fire in the only place that mattered, and where it did fire, it double-fired.Disposition:
workbench.action.zoom*. PR: none yet (may be dropped if V1 suffices)Why the previous approach failed
The previous wiring had three layers — the workbench keybinding service, the webview host page, and the app iframe (plus split-frame panes, each a full app instance) — with zoom intents posted as
amicodebridge envelopes ("zoom" kind) and executed as workbench zoom actions. Round-3 remote instrumentation (a raw-capture probe in the app documents, an eager "Amicode — webview diag" output channel, and a host-page keydown listener) established the failure modes:preventDefault, so the host-page listener fired in addition to the workbench's native handling: one keypress produced two zoom steps (observed: tab focus → double zoom per press, one log line, two presses to reverse).V1 — in-app CSS zoom, no extension machinery (implemented)
keydownlistener matching Ctrl/Cmd +=/+/-/_/0by the key the layout produces (the shift bit is ignored, so US Ctrl+Plus, the numpad "+", and shifted-"="/"-" layouts all match) and applies the existing per-window persisted CSS zoom (web-zoom.ts) to its own document. Matched chords are consumed (preventDefault+stopPropagation).zoom/diag-logallowlist entries and the "webview diag" output channel; theview.zoom*registry entries and their layout-sensitive chord sets.platform.webviewZoomsignal intact for the titlebar/terminal); focus on an editor tab → the native single window zoom, unchanged. No doubles, no bridge.tsc --noEmitclean on both sides.V2 — workbench-owned zoom via in-document capture + forwarded intents (alternative)
workbench.action.zoomIn/Out/Reset.Resolution (to be updated)
As of this stage V1 is the implemented disposition — minimal, zero extension machinery, consistent with the webview as a self-contained surface — with draft PRs #321 and harmoniqs/opencode#161 open. V2 remains the fallback should window-level zoom be desired. The final call and the V2 PR reference (if any) will be recorded here once the V1 PRs land.
Suggested labels
zoom,webview,keybindings,bug/enhancementper the final call.