mirror of
https://github.com/block/buzz.git
synced 2026-08-18 06:50:31 +02:00
Signed-off-by: Wes <wesbillman@users.noreply.github.com> Co-authored-by: Pinky <44b8e82baa6e0e254e0208d68f335c283c94e7b78dd1fa10d5a49d3f13dd0435@sprout-oss.stage.blox.sqprod.co>
129 lines
3.2 KiB
TypeScript
129 lines
3.2 KiB
TypeScript
import * as React from "react";
|
|
import { getCurrentWebview } from "@tauri-apps/api/webview";
|
|
|
|
import { hasPrimaryShortcutModifier } from "@/shared/lib/platform";
|
|
|
|
const DEFAULT_ZOOM_FACTOR = 1;
|
|
const MIN_ZOOM_FACTOR = 0.75;
|
|
const MAX_ZOOM_FACTOR = 1.5;
|
|
const ZOOM_STEP = 0.1;
|
|
const BASE_FONT_SIZE_PX = 16;
|
|
const TEXT_SCALE_STORAGE_KEY = "buzz:text-scale";
|
|
|
|
type ZoomAction = "increase" | "decrease" | "reset";
|
|
|
|
function roundZoomFactor(zoomFactor: number) {
|
|
return Math.round(zoomFactor * 10) / 10;
|
|
}
|
|
|
|
function getZoomAction(event: KeyboardEvent): ZoomAction | null {
|
|
if (!hasPrimaryShortcutModifier(event) || event.altKey) {
|
|
return null;
|
|
}
|
|
|
|
if (
|
|
event.key === "+" ||
|
|
event.key === "=" ||
|
|
event.code === "Equal" ||
|
|
event.code === "NumpadAdd"
|
|
) {
|
|
return "increase";
|
|
}
|
|
|
|
if (
|
|
!event.shiftKey &&
|
|
(event.key === "-" ||
|
|
event.code === "Minus" ||
|
|
event.code === "NumpadSubtract")
|
|
) {
|
|
return "decrease";
|
|
}
|
|
|
|
if (
|
|
!event.shiftKey &&
|
|
(event.key === "0" || event.code === "Digit0" || event.code === "Numpad0")
|
|
) {
|
|
return "reset";
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getNextZoomFactor(action: ZoomAction, zoomFactor: number) {
|
|
if (action === "reset") {
|
|
return DEFAULT_ZOOM_FACTOR;
|
|
}
|
|
|
|
if (action === "increase") {
|
|
return Math.min(roundZoomFactor(zoomFactor + ZOOM_STEP), MAX_ZOOM_FACTOR);
|
|
}
|
|
|
|
return Math.max(roundZoomFactor(zoomFactor - ZOOM_STEP), MIN_ZOOM_FACTOR);
|
|
}
|
|
|
|
function readStoredZoomFactor() {
|
|
const raw = window.localStorage.getItem(TEXT_SCALE_STORAGE_KEY);
|
|
if (!raw) {
|
|
return DEFAULT_ZOOM_FACTOR;
|
|
}
|
|
|
|
const parsed = Number.parseFloat(raw);
|
|
if (!Number.isFinite(parsed)) {
|
|
return DEFAULT_ZOOM_FACTOR;
|
|
}
|
|
|
|
return Math.min(Math.max(parsed, MIN_ZOOM_FACTOR), MAX_ZOOM_FACTOR);
|
|
}
|
|
|
|
function applyTextScale(zoomFactor: number) {
|
|
if (zoomFactor === DEFAULT_ZOOM_FACTOR) {
|
|
document.documentElement.style.fontSize = "";
|
|
window.localStorage.removeItem(TEXT_SCALE_STORAGE_KEY);
|
|
return;
|
|
}
|
|
|
|
document.documentElement.style.fontSize = `${BASE_FONT_SIZE_PX * zoomFactor}px`;
|
|
window.localStorage.setItem(TEXT_SCALE_STORAGE_KEY, String(zoomFactor));
|
|
}
|
|
|
|
export function useWebviewZoomShortcuts() {
|
|
const zoomFactorRef = React.useRef(DEFAULT_ZOOM_FACTOR);
|
|
|
|
React.useLayoutEffect(() => {
|
|
const webview = getCurrentWebview();
|
|
const storedZoomFactor = readStoredZoomFactor();
|
|
|
|
zoomFactorRef.current = storedZoomFactor;
|
|
applyTextScale(storedZoomFactor);
|
|
|
|
// Keep the webview coordinate system stable; only text should scale.
|
|
void webview.setZoom(DEFAULT_ZOOM_FACTOR).catch((error) => {
|
|
console.error("Failed to reset webview zoom", error);
|
|
});
|
|
|
|
function handleKeyDown(event: KeyboardEvent) {
|
|
const action = getZoomAction(event);
|
|
if (!action) {
|
|
return;
|
|
}
|
|
|
|
event.preventDefault();
|
|
|
|
const previousZoomFactor = zoomFactorRef.current;
|
|
const nextZoomFactor = getNextZoomFactor(action, previousZoomFactor);
|
|
|
|
if (nextZoomFactor === previousZoomFactor) {
|
|
return;
|
|
}
|
|
|
|
zoomFactorRef.current = nextZoomFactor;
|
|
applyTextScale(nextZoomFactor);
|
|
}
|
|
|
|
window.addEventListener("keydown", handleKeyDown);
|
|
return () => {
|
|
window.removeEventListener("keydown", handleKeyDown);
|
|
};
|
|
}, []);
|
|
}
|