diff --git a/DESIGN.md b/DESIGN.md index d91fdc91b..3e8328c0d 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -95,6 +95,12 @@ App-level glue that remains local: selectors as a version-pinned integration contract: kit-ui dependency bumps that touch settings must pass the settings browser coverage in CI before adoption. +- Fixed overlay rules in `frontend/src/app.css` keep kit-ui positioning shells + in viewport coordinates and restore interface zoom on their direct children. + These class selectors and child boundaries are a version-pinned integration + contract. Dependency updates must pass the zoom menu geometry coverage in + `frontend/e2e/appearance-a11y.spec.ts`. The date picker retains its current + scale until kit-ui can size its fixed-width panel for zoom. Relative date ranges follow kit-ui semantics: "Last N days" spans N calendar days inclusive of today. `presetRange()` (dateRangeSelector.ts) and diff --git a/docs/configuration.md b/docs/configuration.md index 29de6dccf..5f49bb59d 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -183,6 +183,7 @@ require_auth = true cursor_admin_api_key = "key_xxxxx" daemon_idle_timeout = "20m" chart_palette = "agentsview" +zoom_level = 120 ``` | Field | Description | @@ -203,6 +204,7 @@ chart_palette = "agentsview" | `public_origins` | Additional trusted origins for request Host/Origin checks | | `daemon_idle_timeout` | Idle timeout for detached writable daemons; set to `"0s"` to keep them alive | | `chart_palette` | Server-wide categorical chart colors: `"agentsview"` (default) or `"matplotlib"`; also configurable under **Settings > Appearance** | +| `zoom_level` | Default interface zoom for clients without a local preference: `67`, `75`, `80`, `90`, `100`, `110`, `120`, `125`, `130`, `150`, `175`, or `200`. Defaults to `100` when omitted. Restart the daemon after manual edits | | `disabled_agents` | Session providers to exclude from local filesystem scanning; changes require a daemon restart — see [Disabling Session Providers](#disabling-session-providers) | | `[proxy]` | Managed proxy configuration table — see [Remote Access](/docs/remote-access/) | | `disable_update_check` | Disable the automatic update check (see [Privacy](#privacy-and-telemetry)) | diff --git a/docs/usage.md b/docs/usage.md index 10d6c872e..f9a83c6ea 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -1164,7 +1164,7 @@ organized into sections: | Section | What You Can Configure | | ----------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | Language | Interface language (English, French, Japanese, Korean, Simplified Chinese, or Traditional Chinese) | -| Appearance | Theme (light/dark), high-contrast mode, chart colors, message layout, text size, block visibility, desktop zoom level | +| Appearance | Theme (light/dark), high-contrast mode, chart colors, message layout, zoom, block visibility | | Date ranges | Browser-local checkbox for linking date selections across Sessions, Usage, Activity, Trends, and Quality | | Agent Directories | Custom paths for each agent's session files. For Devin CLI, point at the local root that contains `cli/` (for example a redacted `.../Application Support/devin` path), not copied config or OAuth files. | | Terminal | Default terminal emulator for session resume | @@ -1178,15 +1178,17 @@ organized into sections: ![Chart color palette setting](/docs/assets/generated/screenshots/settings-chart-colors.png) -Language, theme, high contrast, message layout, text size, block visibility, -desktop zoom, and Date ranges preferences are stored in the browser. **Chart -colors** are the exception within Appearance: the selected palette is saved -server-wide as `chart_palette` in `~/.agentsview/config.toml`. Agent directory -overrides, terminal settings, the saved GitHub token, and the local server's -remote-access authentication settings also use that file. Worktree mapping rules -moved to the [Data page](/docs/data/#rules) and live in the local archive -database. See [Remote Access](/docs/remote-access/) for details on the remote -access settings. +Language, theme, high contrast, message layout, zoom, block visibility, and +Date ranges preferences use local storage in the current browser or desktop +webview profile. Each profile keeps its own choices. The optional `zoom_level` +setting supplies a default when no local zoom or text-size preference exists. + +Chart colors use the server-wide `chart_palette` setting in +`~/.agentsview/config.toml`. Agent directory overrides, terminal settings, the +saved GitHub token, and the local server's remote-access authentication +settings also use that file. Worktree mapping rules moved to the +[Data page](/docs/data/#rules) and live in the local archive database. See +[Remote Access](/docs/remote-access/) for details on the remote access settings. ______________________________________________________________________ @@ -1200,12 +1202,23 @@ commit, and links to the changelog and GitHub repository. ______________________________________________________________________ -## Desktop Zoom +## Zoom + +Settings > Appearance has one Zoom setting for the whole interface in both the +browser and desktop app. It offers 67%, 75%, 80%, 90%, 100%, 110%, 120%, 125%, +130%, 150%, 175%, and 200%. Existing text-size preferences migrate to Zoom. A +saved non-default zoom takes precedence. Otherwise, a saved non-default text +size takes precedence over 100%. + +To set the default for clients without a local preference, edit +`~/.agentsview/config.toml` and add `zoom_level = 120`. The field accepts the +twelve values listed above. Restart the daemon and reload the client after a +manual edit. Zoom changes in the interface stay local to that browser or +desktop webview and do not write `config.toml`. -In the desktop app, use `Cmd+Plus` and `Cmd+Minus` (or `Ctrl+Plus` / -`Ctrl+Minus` on Windows) to zoom in and out. `Cmd+0` resets to the default zoom -level. The zoom level can also be set in the Settings page under the -**Appearance** tab. +The desktop status bar and shortcuts change the same local setting. Use +`Cmd+Plus` and `Cmd+Minus`, or `Ctrl+Plus` and `Ctrl+Minus` on Windows, to zoom +in and out. `Cmd+0`, or `Ctrl+0` on Windows, resets Zoom to 100%. ______________________________________________________________________ @@ -1220,8 +1233,7 @@ sessions. ![Dark theme](/docs/assets/generated/screenshots/theme-dark.png) Settings > Appearance also offers a **high-contrast** mode for greater -legibility and a **text size** control (90–130%) that scales message and -interface text. Both preferences are saved and persist across sessions. +legibility. The theme and high-contrast preferences persist across sessions. The **Chart colors** control selects the categorical palette used by the dashboard skill trend, Trends, and Usage charts. Choose **Agentsview** for the diff --git a/frontend/e2e/appearance-a11y.spec.ts b/frontend/e2e/appearance-a11y.spec.ts index f3a5beb04..3adac7bc2 100644 --- a/frontend/e2e/appearance-a11y.spec.ts +++ b/frontend/e2e/appearance-a11y.spec.ts @@ -1,10 +1,27 @@ -import { test, expect, type Locator } from "@playwright/test"; +import { test, expect, type Locator, type Page } from "@playwright/test"; import { SessionsPage } from "./pages/sessions-page"; -function readZoom(page: import("@playwright/test").Page): Promise { +function readZoom(page: Page): Promise { return page.evaluate(() => document.documentElement.style.getPropertyValue("zoom")); } +async function isolateServerZoom(page: Page): Promise { + const response = await page.request.get("/api/v1/settings"); + const settingsBody: Record = await response.json(); + delete settingsBody.zoom_level; + await page.route("**/api/v1/settings", async (route) => { + if (["GET", "PUT"].includes(route.request().method())) { + await route.fulfill({ json: settingsBody }); + return; + } + await route.continue(); + }); +} + +test.beforeEach(async ({ page }) => { + await isolateServerZoom(page); +}); + function luminance([r, g, b]: [number, number, number]): number { const [rr, gg, bb] = [r, g, b].map((channel) => { const value = channel / 255; @@ -166,12 +183,17 @@ test.describe("Appearance accessibility", () => { await expect(alert).toHaveCSS("display", "grid"); }); - test("text size scales the UI on web without horizontal overflow", async ({ page }) => { + test("legacy zoom scales the UI on web without horizontal overflow", async ({ page }) => { await page.addInitScript(() => { localStorage.setItem("agentsview-font-scale", "130"); }); const sp = new SessionsPage(page); - await sp.goto(); + await page.goto("/sessions"); + await sp.sessionItems.first().waitFor({ state: "attached" }); + if (await page.evaluate(() => matchMedia("(max-width: 760px)").matches)) { + await page.getByRole("button", { name: "Toggle sidebar", exact: true }).click(); + } + await expect(sp.sessionItems.first()).toBeVisible(); expect(await readZoom(page)).toBe("1.3"); @@ -184,12 +206,17 @@ test.describe("Appearance accessibility", () => { await expect(sp.messageRows.first()).toBeVisible(); }); - test("text size at 90% renders and scrolls the transcript", async ({ page }) => { + test("zoom at 90% renders and scrolls the transcript", async ({ page }) => { await page.addInitScript(() => { - localStorage.setItem("agentsview-font-scale", "90"); + localStorage.setItem("agentsview-zoom-level", "90"); }); const sp = new SessionsPage(page); - await sp.goto(); + await page.goto("/sessions"); + await sp.sessionItems.first().waitFor({ state: "attached" }); + if (await page.evaluate(() => matchMedia("(max-width: 760px)").matches)) { + await page.getByRole("button", { name: "Toggle sidebar", exact: true }).click(); + } + await expect(sp.sessionItems.first()).toBeVisible(); expect(await readZoom(page)).toBe("0.9"); @@ -197,18 +224,6 @@ test.describe("Appearance accessibility", () => { await expect(sp.messageRows.first()).toBeVisible(); }); - test("desktop window zoom stays separate from text size in the browser", async ({ page }) => { - await page.addInitScript(() => { - localStorage.setItem("agentsview-zoom-level", "150"); - localStorage.setItem("agentsview-font-scale", "120"); - }); - await page.goto("/?desktop"); - const sp = new SessionsPage(page); - await expect(sp.sessionItems.first()).toBeVisible({ timeout: 5_000 }); - - expect(await readZoom(page)).toBe("1.8"); - }); - test("high contrast applies the root class and overrides tokens", async ({ page }) => { await page.addInitScript(() => { localStorage.setItem("theme", "light"); @@ -287,3 +302,233 @@ test.describe("Appearance accessibility", () => { expectReadableContrast(await elementColors(assistantRoleIcon)); }); }); + +async function openAppearance(page: Page, url: string) { + await page.goto(url); + await expect(page.getByRole("heading", { name: "Appearance", exact: true })).toBeVisible(); + await expect(page.getByTitle("Zoom", { exact: true })).toBeVisible(); +} + +async function expectZoom(page: Page, label: string, css: string, stored: string | null) { + await expect(page.getByTitle("Zoom", { exact: true })).toHaveText(`Zoom ${label}`); + await expect.poll(() => readZoom(page)).toBe(css); + expect(await page.evaluate(() => localStorage.getItem("agentsview-zoom-level"))).toBe(stored); + expect(await page.evaluate(() => localStorage.getItem("agentsview-font-scale"))).toBeNull(); +} + +for (const desktop of [false, true]) { + test.describe(`Shared zoom in ${desktop ? "desktop-marked browser" : "browser"}`, () => { + const url = desktop ? "/settings?desktop" : "/settings"; + + for (const state of [ + { canonical: null, legacy: null, label: "100%", css: "1", stored: null }, + { canonical: null, legacy: "120", label: "120%", css: "1.2", stored: "120" }, + { canonical: "100", legacy: "130", label: "130%", css: "1.3", stored: "130" }, + { canonical: "150", legacy: "120", label: "150%", css: "1.5", stored: "150" }, + { canonical: "bad", legacy: "90", label: "90%", css: "0.9", stored: "90" }, + ]) { + test(`restores canonical ${state.canonical} and legacy ${state.legacy} through reload`, async ({ + page, + }) => { + await page.addInitScript(({ canonical, legacy }) => { + if (sessionStorage.getItem("zoom-seeded")) return; + if (canonical !== null) localStorage.setItem("agentsview-zoom-level", canonical); + if (legacy !== null) localStorage.setItem("agentsview-font-scale", legacy); + sessionStorage.setItem("zoom-seeded", "true"); + }, state); + await openAppearance(page, url); + await expectZoom(page, state.label, state.css, state.stored); + await page.reload(); + await expectZoom(page, state.label, state.css, state.stored); + }); + } + + for (const width of [1280, 768, 400]) { + test(`selects shared zoom and reloads at width ${width}`, async ({ page }) => { + await page.setViewportSize({ width, height: 900 }); + await openAppearance(page, url); + await expect(page.getByText("Text size", { exact: true })).toHaveCount(0); + await expect(page.getByText("Desktop zoom", { exact: true })).toHaveCount(0); + await page.getByTitle("Zoom", { exact: true }).click(); + await expect(page.getByRole("option")).toHaveText([ + "67%", + "75%", + "80%", + "90%", + "100%", + "110%", + "120%", + "125%", + "130%", + "150%", + "175%", + "200%", + ]); + await page.getByRole("option", { name: "120%", exact: true }).click(); + await expectZoom(page, "120%", "1.2", "120"); + await expect + .poll(() => page.evaluate(() => document.documentElement.scrollWidth - innerWidth)) + .toBeLessThanOrEqual(2); + await page.reload(); + await expectZoom(page, "120%", "1.2", "120"); + await page.getByTitle("Zoom", { exact: true }).click(); + await expect(page.getByRole("option", { name: "120%", exact: true })).toHaveAttribute( + "aria-selected", + "true", + ); + await page.getByRole("combobox", { name: "Zoom", exact: true }).fill("133"); + await expect(page.getByText("No matches", { exact: true })).toBeVisible(); + await page.keyboard.press("Enter"); + expect(await readZoom(page)).toBe("1.2"); + await page.keyboard.press("Escape"); + }); + } + + test("selects the native sink only with both desktop and bridge markers", async ({ page }) => { + await page.addInitScript(() => { + localStorage.setItem("agentsview-zoom-level", "150"); + localStorage.setItem("agentsview-font-scale", "120"); + const calls: number[] = []; + Object.assign(window, { + zoomCalls: calls, + __TAURI__: { + webviewWindow: { + getCurrentWebviewWindow: () => ({ + setZoom: async (factor: number) => { + calls.push(factor); + }, + }), + }, + }, + }); + }); + await openAppearance(page, url); + await expectZoom(page, "150%", desktop ? "1" : "1.5", "150"); + expect( + await page.evaluate(() => (window as Window & { zoomCalls?: number[] }).zoomCalls), + ).toEqual(desktop ? [1.5] : []); + await page.getByTitle("Zoom", { exact: true }).click(); + await page.getByRole("option", { name: "120%", exact: true }).click(); + await expectZoom(page, "120%", desktop ? "1" : "1.2", "120"); + expect( + await page.evaluate(() => (window as Window & { zoomCalls?: number[] }).zoomCalls), + ).toEqual(desktop ? [1.5, 1.2] : []); + }); + }); +} + +test("Shared zoom synchronizes Appearance, desktop status bar, and shortcuts", async ({ page }) => { + await openAppearance(page, "/settings?desktop"); + await page.getByTitle("Zoom", { exact: true }).click(); + await page.getByRole("option", { name: "120%", exact: true }).click(); + await expect(page.locator(".zoom-level")).toHaveText("120%"); + await page + .locator(".zoom-controls") + .getByTitle(/Zoom in/) + .click(); + await expectZoom(page, "125%", "1.25", "125"); + await page.keyboard.press("ControlOrMeta+-"); + await expectZoom(page, "120%", "1.2", "120"); + await page.keyboard.press("ControlOrMeta+="); + await expectZoom(page, "125%", "1.25", "125"); + await page.keyboard.press("ControlOrMeta+0"); + await expectZoom(page, "100%", "1", "100"); + await expect(page.locator(".zoom-level")).toHaveText("100%"); + await page.reload(); + await expectZoom(page, "100%", "1", "100"); +}); + +test("Shared zoom keeps desktop controls and shortcut interception out of the browser", async ({ + page, +}) => { + await openAppearance(page, "/settings"); + await page.getByTitle("Zoom", { exact: true }).click(); + await page.getByRole("option", { name: "120%", exact: true }).click(); + await expect(page.locator(".zoom-controls")).toHaveCount(0); + const prevented = await page.evaluate(() => { + const event = new KeyboardEvent("keydown", { + key: "0", + ctrlKey: true, + metaKey: true, + bubbles: true, + cancelable: true, + }); + document.dispatchEvent(event); + return event.defaultPrevented; + }); + expect(prevented).toBe(false); + await expectZoom(page, "120%", "1.2", "120"); +}); + +test("Zoom menus keep the interface scale and stay aligned with their trigger", async ({ + page, +}) => { + await openAppearance(page, "/settings"); + const trigger = page.getByTitle("Zoom", { exact: true }); + const option = page.getByRole("option", { name: "100%", exact: true }); + await trigger.click(); + const normal = (await option.boundingBox())!; + await page.getByRole("option", { name: "120%", exact: true }).click(); + await expect.poll(() => readZoom(page)).toBe("1.2"); + await trigger.click(); + const enlarged = (await option.boundingBox())!; + expect(enlarged.height / normal.height).toBeCloseTo(1.2, 1); + const panel = (await page.locator(".kit-typeahead__panel").boundingBox())!; + const button = (await page.getByRole("combobox", { name: "Zoom", exact: true }).boundingBox())!; + expect(Math.abs(panel.x - button.x)).toBeLessThanOrEqual(2); + expect(Math.abs(panel.width - button.width)).toBeLessThanOrEqual(2); + expect( + Math.min( + Math.abs(panel.y - button.y - button.height), + Math.abs(panel.y + panel.height - button.y), + ), + ).toBeLessThanOrEqual(4); +}); + +test("configured zoom is a default and browser choices stay independent", async ({ + page, + browser, +}) => { + const response = await page.request.get("/api/v1/settings"); + const defaults = { ...(await response.json()), zoom_level: 120 }; + const other = await browser.newPage(); + const writes: string[] = []; + try { + for (const client of [page, other]) { + await client.route("**/api/v1/settings", (route) => { + if (route.request().method() === "PUT") writes.push(route.request().postData() ?? ""); + return route.fulfill({ json: defaults }); + }); + await openAppearance(client, "/settings"); + await expectZoom(client, "120%", "1.2", null); + } + await page.getByTitle("Zoom", { exact: true }).click(); + await page.getByRole("option", { name: "150%", exact: true }).click(); + await expectZoom(page, "150%", "1.5", "150"); + await expectZoom(other, "120%", "1.2", null); + await other.getByTitle("Zoom", { exact: true }).click(); + await other.getByRole("option", { name: "100%", exact: true }).click(); + await expectZoom(other, "100%", "1", "100"); + for (const client of [page, other]) await client.reload(); + await expectZoom(page, "150%", "1.5", "150"); + await expectZoom(other, "100%", "1", "100"); + expect(writes).toEqual([]); + } finally { + await other.close(); + } +}); + +test("the date picker contains its calendar at 200% zoom", async ({ page }) => { + await page.setViewportSize({ width: 2200, height: 1400 }); + await page.addInitScript(() => localStorage.setItem("agentsview-zoom-level", "200")); + await page.goto("/activity"); + await page.locator(".kit-date-range-picker__trigger").click(); + await page.getByRole("radio", { name: "Custom", exact: true }).click(); + const panel = page.locator(".kit-date-range-picker__panel"); + const calendar = panel.locator(".kit-calendar"); + await expect(calendar).toBeVisible(); + const shell = (await panel.boundingBox())!; + const grid = (await calendar.boundingBox())!; + expect(grid.x).toBeGreaterThanOrEqual(shell.x); + expect(grid.x + grid.width).toBeLessThanOrEqual(shell.x + shell.width); +}); diff --git a/frontend/messages/en.json b/frontend/messages/en.json index 11b9502c7..c215b9da8 100644 --- a/frontend/messages/en.json +++ b/frontend/messages/en.json @@ -780,7 +780,7 @@ "settings_search_empty": "No matching settings", "settings_search_clear": "Clear search", "settings_search_result": "Showing {category}", - "settings_search_keywords_appearance": "theme contrast layout text font blocks", + "settings_search_keywords_appearance": "theme contrast layout text font blocks zoom", "settings_search_keywords_language": "language locale translation", "settings_search_keywords_date_ranges": "date range linked pages", "settings_search_keywords_terminal": "terminal resume launch binary arguments clipboard", @@ -2054,7 +2054,7 @@ "appearance_high_contrast": "High contrast", "appearance_on": "On", "appearance_off": "Off", - "appearance_text_size": "Text size", + "appearance_zoom": "Zoom", "settings_archive_content_title": "Archive content", "settings_archive_content_description": "What ingestion stores from tool results.", "settings_search_keywords_archive_content": "archive content images retention tool results placeholders storage size", diff --git a/frontend/messages/fr.json b/frontend/messages/fr.json index 3abf5a420..957d13ec0 100644 --- a/frontend/messages/fr.json +++ b/frontend/messages/fr.json @@ -780,7 +780,7 @@ "settings_search_empty": "Aucun paramètre correspondant", "settings_search_clear": "Effacer la recherche", "settings_search_result": "Affichage de {category}", - "settings_search_keywords_appearance": "thème contraste disposition texte police blocs theme contrast layout text font blocks", + "settings_search_keywords_appearance": "thème contraste disposition texte police blocs theme contrast layout text font blocks zoom", "settings_search_keywords_language": "langue paramètres régionaux traduction language locale translation", "settings_search_keywords_date_ranges": "date plage pages liées date range linked pages", "settings_search_keywords_terminal": "terminal reprise lancement exécutable arguments presse-papiers terminal resume launch binary clipboard", @@ -2053,7 +2053,7 @@ "appearance_high_contrast": "Contraste élevé", "appearance_on": "Activé", "appearance_off": "Désactivé", - "appearance_text_size": "Taille du texte", + "appearance_zoom": "Zoom", "settings_archive_content_title": "Contenu de l'archive", "settings_archive_content_description": "Ce que l'ingestion conserve des résultats d'outils.", "settings_search_keywords_archive_content": "archive contenu images rétention résultats outils substituts stockage retention tool results placeholders storage", diff --git a/frontend/messages/ja.json b/frontend/messages/ja.json index bc9e58c76..e7f4d59f6 100644 --- a/frontend/messages/ja.json +++ b/frontend/messages/ja.json @@ -780,7 +780,7 @@ "settings_search_empty": "一致する設定がありません", "settings_search_clear": "検索をクリア", "settings_search_result": "{category}を表示中", - "settings_search_keywords_appearance": "テーマのコントラスト レイアウト テキスト フォント ブロック", + "settings_search_keywords_appearance": "テーマのコントラスト レイアウト テキスト フォント ブロック ズーム zoom", "settings_search_keywords_language": "言語ロケールの翻訳", "settings_search_keywords_date_ranges": "リンクされたページの日付範囲", "settings_search_keywords_terminal": "ターミナル再開バイナリ引数クリップボードの起動", @@ -2054,7 +2054,7 @@ "appearance_high_contrast": "ハイコントラスト", "appearance_on": "オン", "appearance_off": "オフ", - "appearance_text_size": "文字サイズ", + "appearance_zoom": "ズーム", "settings_archive_content_title": "アーカイブの内容", "settings_archive_content_description": "取り込み時にツール結果から保存する内容。", "settings_search_keywords_archive_content": "アーカイブ 内容 画像 保持 ツール結果 プレースホルダー ストレージ archive content images retention tool results placeholders storage", diff --git a/frontend/messages/ko.json b/frontend/messages/ko.json index 3bd1b8099..b36ec8eae 100644 --- a/frontend/messages/ko.json +++ b/frontend/messages/ko.json @@ -755,7 +755,7 @@ "settings_search_empty": "일치하는 설정 없음", "settings_search_clear": "검색 지우기", "settings_search_result": "{category} 표시 중", - "settings_search_keywords_appearance": "테마 대비 레이아웃 텍스트 글꼴 블록 theme contrast layout text font blocks", + "settings_search_keywords_appearance": "테마 대비 레이아웃 텍스트 글꼴 블록 theme contrast layout text font blocks 확대 축소 zoom", "settings_search_keywords_language": "언어 로캘 번역 language locale translation", "settings_search_keywords_date_ranges": "날짜 범위 연결 페이지 date range linked pages", "settings_search_keywords_terminal": "터미널 재개 실행 바이너리 인수 클립보드 terminal resume launch binary arguments clipboard", @@ -1989,7 +1989,7 @@ "appearance_high_contrast": "고대비", "appearance_on": "켜짐", "appearance_off": "꺼짐", - "appearance_text_size": "텍스트 크기", + "appearance_zoom": "확대/축소", "settings_archive_content_title": "아카이브 콘텐츠", "settings_archive_content_description": "수집할 때 도구 결과에서 저장하는 내용입니다.", "settings_search_keywords_archive_content": "아카이브 콘텐츠 이미지 보존 도구 결과 플레이스홀더 저장 archive content images retention tool results placeholders storage", diff --git a/frontend/messages/zh-CN.json b/frontend/messages/zh-CN.json index 672c973ab..ab4552569 100644 --- a/frontend/messages/zh-CN.json +++ b/frontend/messages/zh-CN.json @@ -753,7 +753,7 @@ "settings_search_empty": "没有匹配的设置", "settings_search_clear": "清除搜索", "settings_search_result": "显示:{category}", - "settings_search_keywords_appearance": "主题 对比度 布局 文本 字体 区块 theme contrast layout text font blocks", + "settings_search_keywords_appearance": "主题 对比度 布局 文本 字体 区块 theme contrast layout text font blocks 缩放 zoom", "settings_search_keywords_language": "语言 区域设置 翻译 language locale translation", "settings_search_keywords_date_ranges": "日期 范围 关联 页面 date range linked pages", "settings_search_keywords_terminal": "终端 恢复 启动 可执行文件 参数 剪贴板 terminal resume launch binary arguments clipboard", @@ -1989,7 +1989,7 @@ "appearance_high_contrast": "高对比度", "appearance_on": "开", "appearance_off": "关", - "appearance_text_size": "文字大小", + "appearance_zoom": "缩放", "settings_archive_content_title": "归档内容", "settings_archive_content_description": "采集时从工具结果中保存的内容。", "settings_search_keywords_archive_content": "归档 内容 图片 图像 保留 工具结果 占位符 存储 archive content images retention tool results placeholders storage", diff --git a/frontend/messages/zh-TW.json b/frontend/messages/zh-TW.json index ca18e6f3a..67a4fd87e 100644 --- a/frontend/messages/zh-TW.json +++ b/frontend/messages/zh-TW.json @@ -753,7 +753,7 @@ "settings_search_empty": "沒有相符的設定", "settings_search_clear": "清除搜尋", "settings_search_result": "顯示:{category}", - "settings_search_keywords_appearance": "主題 對比度 版面配置 文字 字體 區塊 theme contrast layout text font blocks", + "settings_search_keywords_appearance": "主題 對比度 版面配置 文字 字體 區塊 theme contrast layout text font blocks 縮放 zoom", "settings_search_keywords_language": "語言 地區設定 翻譯 language locale translation", "settings_search_keywords_date_ranges": "日期 範圍 關聯 頁面 date range linked pages", "settings_search_keywords_terminal": "終端機 繼續 啟動 執行檔 參數 剪貼簿 terminal resume launch binary arguments clipboard", @@ -1989,7 +1989,7 @@ "appearance_high_contrast": "高對比", "appearance_on": "開", "appearance_off": "關", - "appearance_text_size": "文字大小", + "appearance_zoom": "縮放", "settings_archive_content_title": "封存內容", "settings_archive_content_description": "擷取時從工具結果保存的內容。", "settings_search_keywords_archive_content": "封存 內容 圖片 影像 保留 工具結果 佔位符 儲存 archive content images retention tool results placeholders storage", diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index e5a7b62eb..77b4c1283 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -38,6 +38,7 @@