feat(zoom): route Increase/Decrease Font Size across editors, terminals and WebViews (#2172)

Increase, Decrease and Reset Font Size only reached the active CodeMirror editor, so they did nothing with focus in a terminal or plugin WebView. They now apply to the focused surface through a shared workbench service: an editor tab, a terminal tab (which refits), or a plugin WebView. Wheel zoom stays pointer-based.

Co-authored-by: dengliu <[email protected]>
Цей коміт міститься в:
Deng Liuтаdengliu authored and GitHub committed 2026-11-09 02:22:51 +08:00
1 джерело ea8e04e448
коміт 62a39dadb7
9 files changed
+259 -9

No files matched your search

+25
Переглянути файл
@@ -102,6 +102,31 @@ describe("web view helpers", () => {
});
describe("frame bridge", () => {
it("accepts bounded zoom only from its parent with the bridge tag", () => {
const script = /<script>([\s\S]*)<\/script>/.exec(frameDocument("", { dark: true, vars: {} }))![1]!;
const parent = {};
const handlers = new Map<string, (e: { source: object; data: object }) => void>();
const root = document.createElement("html");
const frameWindow = {
parent,
addEventListener: (type: string, fn: (e: { source: object; data: object }) => void) => handlers.set(type, fn),
};
new Function("window", "document", script)(frameWindow, { documentElement: root });
const send = (source: object, data: object) => handlers.get("message")!({ source, data });
send({}, { tag: FRAME_TAG, fontZoomDelta: 1 });
send(parent, { tag: "other", fontZoomDelta: 1 });
send(parent, { tag: FRAME_TAG, fontZoomDelta: NaN });
send(parent, { tag: FRAME_TAG, fontZoomDelta: 100 });
expect(root.style.zoom).toBe("");
send(parent, { tag: FRAME_TAG, fontZoomDelta: 1 });
expect(root.style.zoom).toBe("1.08");
send(parent, { tag: FRAME_TAG, fontZoomDelta: -1 });
expect(root.style.zoom).toBe("");
send(parent, { tag: FRAME_TAG, fontZoomDelta: 1 });
send(parent, { tag: FRAME_TAG, fontZoomDelta: 0 });
expect(root.style.zoom).toBe("");
});
it("asks the workbench for the document and edits it, and hears of changes made elsewhere", async () => {
const script = /<script>([\s\S]*)<\/script>/.exec(frameDocument("", { dark: true, vars: {} }))![1]!;
const sent: Array<{ call: number; op: string; args: unknown }> = [];
+6 -1
Переглянути файл
@@ -20,7 +20,7 @@ import { tx } from "@/platform/i18n";
import { useProject } from "@/platform/project";
import { subscribeTheme, themeKey } from "@/platform/themeWatch";
import { EmptyState, Spinner } from "@/ui";
import { editorService } from "@/workbench/contributions";
import { editorService, registerFontZoomSurface } from "@/workbench/contributions";
import { ToolWindowCornerActions, useOwnToolWindowHeader } from "@/workbench/toolwindows/ToolWindows";
import { pluginsBackend, type FileFilter } from "../backend";
import { errorText, usePlugins } from "../store";
@@ -164,6 +164,10 @@ export function WebView({ pluginId, view, label, header = "default", document =
const post = useCallback((message: object, transfer: Transferable[] = []) => {
frame.current?.contentWindow?.postMessage({ tag: FRAME_TAG, ...message }, "*", transfer);
}, []);
useEffect(() => {
const host = frame.current;
if (host) return registerFontZoomSurface(host, (delta) => post({ fontZoomDelta: delta }));
}, [page, post]);
const postTheme = useCallback(() => {
const t = frameTheme();
if (ownHeader) {
@@ -360,6 +364,7 @@ export function WebView({ pluginId, view, label, header = "default", document =
<iframe
ref={frame}
className={styles.frame}
data-plugin-webview={pluginId}
title={label}
sandbox="allow-scripts"
allow={clipboard ? "clipboard-read; clipboard-write" : undefined}
+9 -1
Переглянути файл
@@ -60,12 +60,18 @@
if (initialTheme) applyTheme(initialTheme);
const stepFrameFontZoom = (delta) => {
const cur = Math.round((parseFloat(root.style.zoom || "1") || 1) * 100);
const next = delta === 0 ? 100 : Math.max(60, Math.min(200, cur + delta * 8));
root.style.zoom = next === 100 ? "" : (next / 100).toFixed(2);
};
// IDE shortcuts (Save, Close, Find Action…) pressed while the page has focus:
// the ones the page left alone go to the workbench. Editing keys stay here.
const EDITING_KEYS = new Set(["a", "c", "v", "x", "z", "y"]);
window.addEventListener("keydown", (e) => {
const fn = /^F\d{1,2}$/.test(e.key);
const mod = (e.metaKey || e.ctrlKey) && !EDITING_KEYS.has(e.key.toLowerCase());
const mod = (e.metaKey || e.ctrlKey || e.altKey) && !EDITING_KEYS.has(e.key.toLowerCase());
if (e.isComposing || (!fn && !mod)) return;
// After every handler of the page has seen it.
setTimeout(() => {
@@ -78,6 +84,8 @@
const m = e.data;
if (e.source !== window.parent || !m || m.tag !== TAG) return;
if (m.theme) applyTheme(m.theme);
else if (m.fontZoomDelta === -1 || m.fontZoomDelta === 0 || m.fontZoomDelta === 1)
stepFrameFontZoom(m.fontZoomDelta);
else if (typeof m.reply === "number") {
const p = pending.get(m.reply);
pending.delete(m.reply);