Pular para o conteúdo principal
C

dsh-zoom-scale

cieux/dsh-zoom-scale

Whole-interface zoom from 75% to 200% in 5% steps via a Settings row, a session-header stepper or Ctrl/⌘+wheel, with a shim that converts rects, viewport units, media queries and pointer coordinates so fixed overlays stay anchored to their triggers.

Instalar

dsh plugin --profile web add github:cieux/dsh-zoom-scale

README

dsh-zoom-scale

Interface zoom for DeepSeek Harness (DSH). Scales the whole Web / Desktop UI and keeps every overlay (menus, tooltips, hover cards, dialogs) anchored to its trigger at any zoom level.

Features

  • Range: 75% – 200%, 5% steps
  • Entry points
    • Settings → General → Interface zoom (stepper + reset)
    • Session header − 125% + control (click the percentage to reset to 100%)
    • Ctrl (⌘ on macOS) + wheel
    • Ctrl/⌘ + =, -, 0
  • Persistence: same-origin cookie (the desktop shell changes ports between launches) mirrored to localStorage

Two halves

FileRuns inOwns
index.jshost (Node)the head script: the shim source, the seed scale, a reserved desktop-window-restore hook (not implemented, see Desktop window memory)
lib/shim.cjspage, before any client moduleevery geometry conversion; publishes window.__dshzs
client.jspage, as a Loader modulethe controls, the preference, the locale dictionaries, a fallback shim

index.js injects one head script, so the shim is installed before any client module runs and applies the remembered scale before the shell's first mount. client.js drives window.__dshzs when it exists and installs a fallback only when it does not, so no rect is divided twice.

What the shim converts

Chromium reports rects, window size and pointer coordinates in visual pixels after html { zoom }, while CSS lengths stay in layout pixels — every position: fixed overlay would drift. The shim divides that back out, roughly for: element and Range rects, window and VisualViewport metrics, scrolling APIs, pointer and wheel coordinates, hit testing, SVG screen matrices, viewport units (vw/vh/vmin/vmax → calc(… / var(--dshzs-scale))), media-query bounds and devicePixelRatio. Every step is guarded on its own, so a missing API degrades one behaviour instead of the whole zoom.

Stylesheet coverage

Converted declarations follow --dshzs-scale, so a scale change only re-derives media-query bounds and no stylesheet is rescanned. New CSS is caught by a MutationObserver, the adoptedStyleSheets setter, CSSStyleSheet.insertRule / replaceSync, and a capture-phase load listener.

Desktop window memory

Reserved, not implemented. The head script reads localStorage['dshzs:window'] and, under the dsh-app: protocol the desktop shell uses, restores the main window with resizeTo / moveTo. This plugin never writes that key, so the branch does nothing today; it is kept only as the contract a later implementation would fill.

Payload — CSS pixels (DIP), screen coordinates:

{ "x": 234, "y": 90, "w": 1440, "h": 900 }

Only a position inside the current display's work area is restored: saved.x / saved.y are checked against screen.availLeft / availTop, so a position on another monitor is dropped while the size still applies. The shell may also impose a minimum size of its own.

If you need the feature now, install one of these instead:

PluginPlatformNotes
dsh-window-boundsallpage-side resizeTo / moveTo, logical pixels; does not restore the maximized state
dsh-window-stateWindowsnative user32.dll, also remembers the maximized state; the window may flash at the default size first

Install only one. They write different keys but drive the same window, so the last writer wins. Neither needs a change here: a window resize needs no hook, because vw / vh are already rewritten to calc(… / var(--dshzs-scale)).

Install

# from a local directory
dsh plugin add /path/to/dsh-zoom-scale

# or from the packaged artifact
dsh plugin add ./dsh-zoom-scale-0.2.7.tgz

After installation reload DSH (a restart is recommended for the desktop app). The "Interface zoom" row appears under Settings → General.

Compatibility

  • DSH 0.1.7 or newer. The head script is delivered through webserver/index-inject, and 0.1.7-rc.1 is the first release whose published packages provide it. 0.1.0–0.1.6 were not tested. schemastery is loaded optionally, so a profile that carries only this plugin still boots (the configurable scale is then absent and the seed stays at 100 %).
  • Do not set zoom on the root. The shim owns html { zoom }; a second html declaration overwrites it (or is overwritten, depending on cascade order) and desynchronises every conversion. zoom on a descendant multiplies with the root zoom, so leave zoom alone outside the shim.
  • Do not use transform: scale() to zoom. It multiplies with the root zoom, creates a containing block that re-anchors position: fixed overlays (the shim cannot correct that), makes getBoundingClientRect disagree with the conversions, and stretches glyphs instead of re-rasterising them.
  • Do not write --dshzs-scale. The shim rewrites it on every change; an inline value on <html> wins over the shim's stylesheet and breaks every converted length. Read the variable, never write it.
  • Viewport units are rewritten in stylesheets and in rules added through insertRule, replaceSync and adoptedStyleSheets. Inline style attributes are converted too, but only after they are observed, so put first-paint-critical measurements in stylesheet rules.
  • Colours: use the --dsw-* tokens. Hard-coded colours do not follow the dark theme.
  • To read the current scale, use window.__dshzs?.get() or html[data-dshzs] (published by the head shim itself, absent at 100 %). Do not read documentElement.style.zoom: in shim mode the zoom lives in a <style> element and only the fallback path writes it inline. At 100 % that style is empty and data-dshzs is absent — that does not mean the plugin is missing.
  • Do not install a second shim. window.__dshzs has one owner; wrapping the same properties again divides every rect twice. Drive the existing object with set().

Changelog

0.2.7 (latest)
  • Refactor: escapeScriptText moved to lib/util.cjs so it can be tested on its own, and it now neutralises a </script that sits at the end of the injected source too.
  • Refactor: persistence keys and the scale range live in lib/zoom-constants.cjs; client.js is an IIFE and keeps its own literals, and a test fails when the two drift apart.
  • Naming: SCALE_KEY / SCALE_COOKIE / LOCALE_NAMESPACE in client.js, SCALE_PROPERTY / MEDIA_QUERY_STATE in the shim.
  • Tests: util (3), dictionary key parity (1), constants drift (2).

License

MIT


dsh-zoom-scale(中文)

DeepSeek Harness(DSH)Web / Desktop 界面的整体缩放插件。把整个 UI 按比例缩放,同时保证所有浮层(菜单、tooltip、hover card、弹窗)在任意缩放下仍然贴住各自的触发元素。

功能

  • 区间:75% – 200%,5% 步进
  • 入口
    • 设置 → 通用设置 → 界面缩放(带 +/- 步进与重置)
    • 会话头部右侧的 − 125% + 控件(点击百分比重置为 100%)
    • Ctrl(macOS 为 ⌘)+ 滚轮直接缩放
    • Ctrl/⌘ + =、-、0
  • 记忆:缩放值写入同源 cookie(桌面壳每次启动端口会变,cookie 按 host 保留)并镜像到 localStorage

两半

文件运行位置负责
index.js宿主(Node)head 脚本:shim 源码、初始缩放值、预留的桌面窗口还原接口(未实装,见桌面窗口记忆)
lib/shim.cjs页面,早于任何客户端模块全部几何换算;发布 window.__dshzs
client.js页面,作为 Loader 模块控件、偏好读写、语言词条、兜底 shim

index.js 注入一段 head 脚本,shim 因此在任何客户端模块之前安装,并在宿主首次挂载前应用记住的缩放值。client.js 在 window.__dshzs 存在时驱动它,不存在时才安装兜底 shim,同一个 rect 不会被除两次。

shim 换算什么

html { zoom } 之后,Chromium 在窗口像素里报告 rect、窗口尺寸和指针坐标,而 CSS 长度仍在布局像素里 —— 每个 position: fixed 浮层都会漂移。shim 把这些量除回去,大致覆盖:元素与 Range 的 rect、窗口与 VisualViewport 尺寸、滚动 API、指针与滚轮坐标、命中测试、SVG 屏幕矩阵、视口单位(vw/vh/vmin/vmax → calc(… / var(--dshzs-scale)))、媒体查询边界与 devicePixelRatio。每一步单独兜底:某个 API 缺失只退化一项行为,不会连带停掉整个缩放。

样式表覆盖

换算后的声明通过 --dshzs-scale 跟随缩放,所以换档时只需重新推导媒体查询边界,不会重扫样式表。新来的 CSS 由 MutationObserver、adoptedStyleSheets 的 setter、CSSStyleSheet.insertRule / replaceSync 以及捕获阶段的 load 监听收拢。

桌面窗口记忆

预留接口,未实装。 head 脚本会读 localStorage['dshzs:window'],在桌面壳使用的 dsh-app: 协议下用 resizeTo / moveTo 还原主窗口。本插件从不写这个键,所以这条分支目前不会生效 —— 它只是留给将来实装时用的契约。

负载格式为 CSS 像素(DIP)、屏幕坐标:

{ "x": 234, "y": 90, "w": 1440, "h": 900 }

只还原当前显示器工作区内的位置:saved.x / saved.y 相对 screen.availLeft / availTop 校验,位于其它显示器的位置会被丢弃,尺寸仍然生效。壳也可能有自己的最小尺寸约束。

现在就想要这个功能,请改装下面的插件:

插件平台说明
dsh-window-bounds全平台页面侧 resizeTo / moveTo,逻辑像素;不还原最大化
dsh-window-state仅 Windows原生 user32.dll,能记住最大化;窗口会先以默认尺寸闪现一次

两者只装一个:键不同,但操作的是同一个窗口,谁后跑谁赢。装哪个都不需要改本插件 —— 窗口尺寸变化不需要钩子,vw / vh 已被改写成 calc(… / var(--dshzs-scale))。

安装

# 本地目录
dsh plugin add /path/to/dsh-zoom-scale

# 或安装打包产物
dsh plugin add ./dsh-zoom-scale-0.2.7.tgz

安装后重载 DSH(桌面应用建议重启),在 设置 → 通用设置 中即可看到「界面缩放」一行。

与其它 CSS / 插件共处

  • 需要 DSH 0.1.7 及以上:head 脚本经 webserver/index-inject 注入,0.1.7-rc.1 是查证过第一个提供该事件的发布;0.1.0–0.1.6 未验证。schemastery 改为可选加载,所以只装本插件的干净 profile 也能启动(此时没有可配置的缩放项,种子保持 100 %)。
  • 不要给根元素写 zoom:html { zoom } 归 shim 所有,再写一条会覆盖它(或被它覆盖,取决于层叠顺序),换算随之错位;而后代元素上的 zoom 会与根缩放相乘,所以 shim 之外不要碰 zoom。
  • 不要用 transform: scale() 做缩放:它会与根缩放相乘,创建新的包含块使 position: fixed 浮层改相对它定位(shim 修正不了),让 getBoundingClientRect 与换算结果不一致,而且文字是被拉伸而非重新栅格化。
  • 不要写 --dshzs-scale:shim 每次换档都会重写它;写在 <html> 内联样式上反而会赢过 shim 的样式表,让所有换算长度失效。请读它,不要写它。
  • 视口单位会被改写:样式表里的 100vh 变成 calc(100vh / var(--dshzs-scale, 1)),运行时经 insertRule、replaceSync、adoptedStyleSheets 插入的规则同样接管。内联 style 也会转换,但要等它被观察到,所以首帧就要准确的尺寸优先写成样式表规则。
  • 颜色用 --dsw-* 令牌:硬编码颜色不会跟随暗色主题。
  • 判断当前缩放用 window.__dshzs?.get()(百分比)或 html[data-dshzs](由 head shim 自己发布,100 % 时不存在)。不要读 documentElement.style.zoom:shim 模式下 zoom 写在 <style> 里,只有降级路径才写内联;100 % 时那条样式表是空的、也没有 data-dshzs,但这不代表插件没装。
  • 不要再装一套 shim:window.__dshzs 只有一个持有者,重复包装同一批属性会让每个 rect 被除两次。要接管请调用它的 set()。

变更记录

0.2.7(最新)
  • 重构:escapeScriptText 移到 lib/util.cjs 以便单独测试;顺带补上「</script 位于注入源码末尾」这一漏转义。
  • 重构:持久化键与缩放范围收进 lib/zoom-constants.cjs;client.js 是 IIFE、保留自己的字面量,由测试保证两边不漂移。
  • 命名:client.js 的 SCALE_KEY / SCALE_COOKIE / LOCALE_NAMESPACE,shim 的 SCALE_PROPERTY / MEDIA_QUERY_STATE。
  • 测试:util(3)、字典 key 一致(1)、常量漂移(2)。

许可

MIT

Plugins relacionados