From 86d32190cc820c2dbaf9c73ce8fa929b927e08cc Mon Sep 17 00:00:00 2001 From: trickypr <23250792+trickypr@users.noreply.github.com> Date: Fri, 5 Jan 2024 16:58:13 +1100 Subject: [PATCH 1/4] :sparkles: Implement zoom support --- .../components/keybindings/Keybindings.svelte | 12 ++++++++++ .../browser/components/omnibox/Omnibox.svelte | 4 ++++ .../components/omnibox/ZoomDisplay.svelte | 24 +++++++++++++++++++ apps/content/src/browser/lib/window/tab.ts | 10 ++++++++ apps/content/src/settings/Settings.svelte | 3 +++ apps/misc/static/defaults/pref/prefs.js | 2 ++ libs/link/types/globals/Elements.d.ts | 2 ++ 7 files changed, 57 insertions(+) create mode 100644 apps/content/src/browser/components/omnibox/ZoomDisplay.svelte diff --git a/apps/content/src/browser/components/keybindings/Keybindings.svelte b/apps/content/src/browser/components/keybindings/Keybindings.svelte index 6145b5a..a1e9535 100644 --- a/apps/content/src/browser/components/keybindings/Keybindings.svelte +++ b/apps/content/src/browser/components/keybindings/Keybindings.svelte @@ -43,6 +43,18 @@ pref="browser.keybinds.previousTab" on:command={() => setCurrentTabIndex(getCurrentTabIndex() - 1)} /> + + + runOnCurrentTab((tab) => tab.zoom.update((zoom) => zoom + 0.1))} + /> + + runOnCurrentTab((tab) => tab.zoom.update((zoom) => zoom - 0.1))} + /> + {#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum} + zoom.set(1)} /> + {#each $pageActions as [_extId, pageAction]} {#if pageAction.shouldShow($uri.asciiSpec, tab.getTabId())} diff --git a/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte b/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte new file mode 100644 index 0000000..e1fb3da --- /dev/null +++ b/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte @@ -0,0 +1,24 @@ + + +{#if zoom != 1} + +{/if} + + diff --git a/apps/content/src/browser/lib/window/tab.ts b/apps/content/src/browser/lib/window/tab.ts index bdfe823..ca6da5e 100644 --- a/apps/content/src/browser/lib/window/tab.ts +++ b/apps/content/src/browser/lib/window/tab.ts @@ -43,6 +43,8 @@ export class Tab { public loading = writable(false) public loadingProgress = writable(0) + public zoom = writable(1) + /** * This is used by the omnibox to determine if text input should be focused. */ @@ -59,6 +61,11 @@ export class Tab { this.goToUri(uri) this.title.set(uri.asciiHost) + this.zoom.subscribe( + (newZoom) => + this.browserElement.browsingContext && + (this.browserElement.fullZoom = newZoom), + ) this.uri.subscribe(async (uri) => this.bookmarkInfo.set( await search({ url: uri.spec }).then((r) => @@ -156,6 +163,9 @@ export class Tab { this.uri.set(event.aLocation) this.canGoBack.set(this.browserElement.canGoBack) this.canGoForward.set(this.browserElement.canGoForward) + this.zoom.set( + this.browserElement.browsingContext ? this.browserElement.fullZoom : 1, + ) }) this.progressListener.events.on('progressPercent', this.loadingProgress.set) diff --git a/apps/content/src/settings/Settings.svelte b/apps/content/src/settings/Settings.svelte index 67de213..80c0ff5 100644 --- a/apps/content/src/settings/Settings.svelte +++ b/apps/content/src/settings/Settings.svelte @@ -75,6 +75,9 @@ Next tab Previous Tab + Zoom in + Zoom out + {#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum} Jump to tab {tabNum} Date: Fri, 5 Jan 2024 19:50:41 +1100 Subject: [PATCH 2/4] :sparkles: Store zoom state across browser starts --- apps/content/src/browser/lib/resources.ts | 1 + apps/content/src/browser/lib/window/tab.ts | 36 ++++++-- apps/modules/lib/ZoomStore.sys.mjs | 101 +++++++++++++++++++++ libs/link/package.json | 1 + libs/link/types/_link.d.ts | 1 + libs/link/types/modules/ZoomStore.d.ts | 24 +++++ pnpm-lock.yaml | 12 ++- 7 files changed, 167 insertions(+), 9 deletions(-) create mode 100644 apps/modules/lib/ZoomStore.sys.mjs create mode 100644 libs/link/types/modules/ZoomStore.d.ts diff --git a/apps/content/src/browser/lib/resources.ts b/apps/content/src/browser/lib/resources.ts index 5f427c0..af87056 100644 --- a/apps/content/src/browser/lib/resources.ts +++ b/apps/content/src/browser/lib/resources.ts @@ -11,4 +11,5 @@ export const resource = lazyESModuleGetters({ NetUtil: 'resource://gre/modules/NetUtil.sys.mjs', PageThumbs: 'resource://gre/modules/PageThumbs.sys.mjs', WindowTracker: 'resource://app/modules/BrowserWindowTracker.sys.mjs', + ZoomStore: 'resource://app/modules/ZoomStore.sys.mjs', }) diff --git a/apps/content/src/browser/lib/window/tab.ts b/apps/content/src/browser/lib/window/tab.ts index ca6da5e..756d09a 100644 --- a/apps/content/src/browser/lib/window/tab.ts +++ b/apps/content/src/browser/lib/window/tab.ts @@ -5,6 +5,8 @@ import { type ViewableWritable, viewableWritable } from '@experiment/shared' import mitt from 'mitt' import { type Writable, get, writable } from 'svelte/store' +import type { ZoomStoreEvents } from 'resource://app/modules/ZoomStore.sys.mjs' + import { type BookmarkTreeNode, search } from '@shared/ExtBookmarkAPI' import { resource } from '../resources' @@ -61,11 +63,17 @@ export class Tab { this.goToUri(uri) this.title.set(uri.asciiHost) - this.zoom.subscribe( - (newZoom) => - this.browserElement.browsingContext && - (this.browserElement.fullZoom = newZoom), - ) + this.zoom.subscribe((newZoom) => { + if ( + !this.browserElement.browsingContext || + this.browserElement.fullZoom === newZoom + ) { + return + } + + this.browserElement.fullZoom = newZoom + resource.ZoomStore.setZoomForUri(this.uri.readOnce(), newZoom) + }) this.uri.subscribe(async (uri) => this.bookmarkInfo.set( await search({ url: uri.spec }).then((r) => @@ -73,6 +81,9 @@ export class Tab { ), ), ) + + // Remember to unsubscribe from any listeners you register here! + resource.ZoomStore.events.on('setZoom', this.zoomChange) } public getId(): number { @@ -154,6 +165,13 @@ export class Tab { this.useProgressListener() } + zoomChange = (event: ZoomStoreEvents['setZoom']) => { + try { + if (this.uri.readOnce().host != event.host) return + this.zoom.set(event.zoom) + } catch {} + } + protected useProgressListener() { this.progressListener.events.on('locationChange', (event) => { if (!event.aWebProgress.isTopLevel) return @@ -163,9 +181,8 @@ export class Tab { this.uri.set(event.aLocation) this.canGoBack.set(this.browserElement.canGoBack) this.canGoForward.set(this.browserElement.canGoForward) - this.zoom.set( - this.browserElement.browsingContext ? this.browserElement.fullZoom : 1, - ) + + this.zoom.set(resource.ZoomStore.getZoomForUri(event.aLocation)) }) this.progressListener.events.on('progressPercent', this.loadingProgress.set) @@ -188,6 +205,9 @@ export class Tab { } public destroy() { + this.removeEventListeners() + resource.ZoomStore.events.off('setZoom', this.zoomChange) + this.browserElement.remove() } diff --git a/apps/modules/lib/ZoomStore.sys.mjs b/apps/modules/lib/ZoomStore.sys.mjs new file mode 100644 index 0000000..76bb137 --- /dev/null +++ b/apps/modules/lib/ZoomStore.sys.mjs @@ -0,0 +1,101 @@ +// @ts-check +/// +import mitt from 'resource://app/modules/mitt.sys.mjs' + +const ZOOM_STORE_FILE = PathUtils.join(PathUtils.profileDir, 'zoomstore.json') + +/** @typedef {import("resource://app/modules/ZoomStore.sys.mjs").ZoomStoreInterface} ZoomStoreInterface */ + +/** @implements {ZoomStoreInterface} */ +class ZoomStoreImpl { + /** + * @private + * @type {Map | null} + */ + zoomPages = null + + /** @type {import('resource://app/modules/mitt.sys.mjs').Emitter} */ + events = mitt() + + constructor() { + this.init() + } + + /** @protected */ + async init() { + if (this.zoomPages) { + return + } + + if (!(await IOUtils.exists(ZOOM_STORE_FILE))) { + this.zoomPages = new Map() + return + } + + try { + const pages = await IOUtils.readJSON(ZOOM_STORE_FILE) + if (this.zoomPages) { + return + } + + this.zoomPages = new Map(pages) + } catch (e) { + console.error('Failed to load zoomStore from file: ', e) + return + } + } + + /** @private */ + async save() { + if (!this.zoomPages) { + return + } + + const toWrite = Array.from(this.zoomPages.entries()) + try { + await IOUtils.writeJSON(ZOOM_STORE_FILE, toWrite) + } catch (e) { + console.error('Failed to write zoomStore to file:', e) + return + } + } + + /** + * @param {nsIURIType} uri The uri to check zoom for + * @returns {number} + */ + getZoomForUri(uri) { + try { + return this.zoomPages?.get(uri.host) || 1 + } catch { + return 1 + } + } + + /** + * @param {nsIURIType} uri + * @param {number} zoom The zoom to store. If set to 1, will delete any stored values + */ + setZoomForUri(uri, zoom) { + try { + uri.host + } catch { + return + } + + if (zoom === 1) { + this.zoomPages?.delete(uri.host) + return + } + + this.zoomPages?.set(uri.host, Math.round(zoom * 100) / 100) + + // @ts-ignore + Services.tm.dispatchToMainThread(() => { + this.events.emit('setZoom', { host: uri.host, zoom }) + this.save() + }) + } +} + +export const ZoomStore = new ZoomStoreImpl() diff --git a/libs/link/package.json b/libs/link/package.json index 8cc389f..15a7347 100644 --- a/libs/link/package.json +++ b/libs/link/package.json @@ -8,6 +8,7 @@ "license": "ISC", "dependencies": { "gecko-types": "github:quark-platform/gecko-types", + "gecko-types@latest": "link:quark-platform/gecko-types@latest", "mitt": "^3.0.1" } } diff --git a/libs/link/types/_link.d.ts b/libs/link/types/_link.d.ts index 7896154..4367b18 100644 --- a/libs/link/types/_link.d.ts +++ b/libs/link/types/_link.d.ts @@ -24,3 +24,4 @@ /// /// /// +/// diff --git a/libs/link/types/modules/ZoomStore.d.ts b/libs/link/types/modules/ZoomStore.d.ts new file mode 100644 index 0000000..8a3f6fd --- /dev/null +++ b/libs/link/types/modules/ZoomStore.d.ts @@ -0,0 +1,24 @@ +declare module 'resource://app/modules/ZoomStore.sys.mjs' { + import { Emitter } from 'mitt' + + export type ZoomStoreEvents = { + setZoom: { host: string; zoom: number } + } + + export const ZoomStore: { + events: Emitter + + getZoomForUri(uri: nsIURIType): number + setZoomForUri(uri: nsIURIType, zoom: number): void + } + + export type ZoomStoreInterface = typeof ZoomStore +} + +declare interface MozESMExportFile { + ZoomStore: 'resource://app/modules/ZoomStore.sys.mjs' +} + +declare interface MozESMExportType { + ZoomStore: typeof import('resource://app/modules/ZoomStore.sys.mjs').ZoomStore +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index aabdd8b..46b750d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -189,7 +189,10 @@ importers: dependencies: gecko-types: specifier: github:quark-platform/gecko-types - version: github.com/quark-platform/gecko-types/e4efe38ce26c3617d7a3ecb13491bca2004e0377 + version: github.com/quark-platform/gecko-types/015f4eddb9a02d5f60ef82bf04e7b78bbb15ffd6 + gecko-types@latest: + specifier: link:quark-platform/gecko-types@latest + version: link:quark-platform/gecko-types@latest mitt: specifier: ^3.0.1 version: 3.0.1 @@ -7595,7 +7598,14 @@ packages: resolution: {integrity: sha512-FSZOvfJVfMWhk/poictNsDBCXq/Z+2Zu2peWs6d8OhWWb9nY++czw95D47hdw06L/kfjasLevwrbUtnXyWLAJw==} dev: false + github.com/quark-platform/gecko-types/015f4eddb9a02d5f60ef82bf04e7b78bbb15ffd6: + resolution: {tarball: https://codeload.github.com/quark-platform/gecko-types/tar.gz/015f4eddb9a02d5f60ef82bf04e7b78bbb15ffd6} + name: gecko-types + version: 1.0.0 + dev: false + github.com/quark-platform/gecko-types/e4efe38ce26c3617d7a3ecb13491bca2004e0377: resolution: {tarball: https://codeload.github.com/quark-platform/gecko-types/tar.gz/e4efe38ce26c3617d7a3ecb13491bca2004e0377} name: gecko-types version: 1.0.0 + dev: true From 58f67b6627c9d2bd2595c7fceccabccba7c6245e Mon Sep 17 00:00:00 2001 From: trickypr <23250792+trickypr@users.noreply.github.com> Date: Fri, 5 Jan 2024 19:54:51 +1100 Subject: [PATCH 3/4] :sparkles: Keybind for reseting zoom --- .../src/browser/components/keybindings/Keybindings.svelte | 4 ++++ apps/content/src/settings/Settings.svelte | 1 + apps/misc/static/defaults/pref/prefs.js | 1 + apps/modules/lib/ZoomStore.sys.mjs | 1 + 4 files changed, 7 insertions(+) diff --git a/apps/content/src/browser/components/keybindings/Keybindings.svelte b/apps/content/src/browser/components/keybindings/Keybindings.svelte index a1e9535..b5fbb52 100644 --- a/apps/content/src/browser/components/keybindings/Keybindings.svelte +++ b/apps/content/src/browser/components/keybindings/Keybindings.svelte @@ -54,6 +54,10 @@ on:command={() => runOnCurrentTab((tab) => tab.zoom.update((zoom) => zoom - 0.1))} /> + runOnCurrentTab((tab) => tab.zoom.set(1))} + /> {#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum} Zoom in Zoom out + Reset Zoom {#each [1, 2, 3, 4, 5, 6, 7, 8] as tabNum} Date: Fri, 5 Jan 2024 19:56:17 +1100 Subject: [PATCH 4/4] :page_facing_up: Add license headers --- .../src/browser/components/omnibox/ZoomDisplay.svelte | 4 ++++ apps/content/src/browser/lib/window/tab.ts | 6 ++---- apps/modules/lib/ZoomStore.sys.mjs | 10 +++++----- libs/link/types/modules/ZoomStore.d.ts | 4 ++++ 4 files changed, 15 insertions(+), 9 deletions(-) diff --git a/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte b/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte index e1fb3da..07c8deb 100644 --- a/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte +++ b/apps/content/src/browser/components/omnibox/ZoomDisplay.svelte @@ -1,3 +1,7 @@ + + diff --git a/apps/content/src/browser/lib/window/tab.ts b/apps/content/src/browser/lib/window/tab.ts index 756d09a..ea3ddbc 100644 --- a/apps/content/src/browser/lib/window/tab.ts +++ b/apps/content/src/browser/lib/window/tab.ts @@ -166,10 +166,8 @@ export class Tab { } zoomChange = (event: ZoomStoreEvents['setZoom']) => { - try { - if (this.uri.readOnce().host != event.host) return - this.zoom.set(event.zoom) - } catch {} + if (this.uri.readOnce().asciiHost != event.host) return + this.zoom.set(event.zoom) } protected useProgressListener() { diff --git a/apps/modules/lib/ZoomStore.sys.mjs b/apps/modules/lib/ZoomStore.sys.mjs index 3ce2cac..e64ecc6 100644 --- a/apps/modules/lib/ZoomStore.sys.mjs +++ b/apps/modules/lib/ZoomStore.sys.mjs @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + // @ts-check /// import mitt from 'resource://app/modules/mitt.sys.mjs' @@ -65,11 +69,7 @@ class ZoomStoreImpl { * @returns {number} */ getZoomForUri(uri) { - try { - return this.zoomPages?.get(uri.host) || 1 - } catch { - return 1 - } + return this.zoomPages?.get(uri.asciiHost) || 1 } /** diff --git a/libs/link/types/modules/ZoomStore.d.ts b/libs/link/types/modules/ZoomStore.d.ts index 8a3f6fd..ff6ba0d 100644 --- a/libs/link/types/modules/ZoomStore.d.ts +++ b/libs/link/types/modules/ZoomStore.d.ts @@ -1,3 +1,7 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + declare module 'resource://app/modules/ZoomStore.sys.mjs' { import { Emitter } from 'mitt'