Skip to content

About

TauriTavern / SillyTavern 扩展:按住 Ctrl 用鼠标滚轮等比缩放界面(视口单位折算 + JS 视口/坐标兼容,卡片前端与拖拽一起缩放)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

界面缩放(Ctrl+滚轮)

给 TauriTavern 的等比界面缩放:按住 Ctrl 用鼠标滚轮即可像浏览器那样整页缩放, Ctrl+0 复位、Ctrl+= 放大、Ctrl+- 缩小。

  • 扩展目录名:ctrl-wheel-zoom
  • 显示名:界面缩放(Ctrl+滚轮)
  • 版本:1.1.0
  • 依赖:无(不引用酒馆内部模块,纯 DOM + ESM)

1. 用法

操作 效果
Ctrl + 滚轮上/下 按浏览器档位放大/缩小(25% → 500%)
Ctrl+0 回到 100%
Ctrl+= / Ctrl+- 放大 / 缩小一档
扩展面板 总开关、档位滑条、100/125/150/200% 快捷按钮、四个子开关

两个和"卡片前端"有关的开关(都在扩展面板里,默认开):

开关 作用
JS 视口/坐标兼容 让按窗口像素写死尺寸的浮层跟着缩放(见 §4)
卡片前端保持桌面版 让每楼前端 iframe 的逻辑视口越过卡片自己的断点,不再串成手机版(见 §5)

档位表与浏览器一致:25 33 50 67 75 80 90 100 110 125 150 175 200 250 300 400 500。 缩放级别按设备保存在 localStorage(tt-ctrl-wheel-zoom),与浏览器缩放一样属于本机显示偏好。

滚轮只在按住 Ctrl(macOS 上 Cmd)时被接管;普通滚轮行为完全不变。 被接管时事件会被 preventDefault,避免同时触发酒馆自身的滚轮处理(例如滑条数值跟随滚轮增减)。

2. 安装

两种位置任选,同名时 local 优先(与 TauriTavern 文档一致):

类型 目录
全局(global) <数据根目录>/extensions/third-party/ctrl-wheel-zoom/
单用户(local) <数据根目录>/default-user/extensions/ctrl-wheel-zoom/

Windows 下 TauriTavern 的数据根目录通常是:

%APPDATA%\com.tauritavern.client\data

即完整路径形如 …\data\extensions\third-party\ctrl-wheel-zoom\。 放好文件后重新加载页面/重启 TauriTavern,在「扩展」面板里即可看到本扩展(默认启用)。

3. 为什么是 CSS 模拟,而不是真正的 WebView 缩放

真正的"浏览器级"缩放在这里拿不到,原因有两个,都已核对过源码:

  1. 用户缩放被关掉:Windows 上 Tauri 的 zoomHotkeysEnabled 对应 WebView2 的 IsZoomControlEnabled,TauriTavern 的 tauri.conf.json 未开启它,所以 Ctrl+滚轮 在 WebView 层面本来就不会缩放。
  2. 页面脚本也调不到:Tauri 的 core:webview:default 只包含 getter (allow-get-all-webviews / allow-webview-position / allow-webview-size / allow-internal-toggle-devtools),不含 allow-set-webview-zoom; TauriTavern 的 capability 用的是 core:default,因此 plugin:webview|set_webview_zoom 会被 ACL 拒绝。

所以本扩展用 CSS zoom 施加在 <html> 上:它参与布局,百分比宽度、flex、 position: fixed 都会一起缩放,视觉效果与浏览器缩放一致。

3.1 必须补的坑:视口单位

Chromium 的 zoom 不会重算视口单位。实测(真实酒馆页面,1280×860):

处理 200% 时 --sheldWidth: 50vw 的后果
直接 html { zoom: 2 } 聊天列占满整屏、横向滚动条溢出约 993px
本扩展(改写成 calc(50vw / 2)) 聊天列仍为半屏、横向溢出 0

内核会把文档里所有含视口单位(vw/vh/dvw/dvh/svw/svh/lvw/lvh)的长度改写成 calc(<原值> / 缩放倍数),覆盖两类来源:

  • 样式表规则(含 @media / @supports / @import 递归;跨源表跳过)
  • 内联样式——酒馆本体就是把 --sheldWidth: <n>vw 写在 :root 的内联样式上的

每条属性都登记了"作者原值",所以:外部改写会被识别为新基准(不会被本扩展覆盖回去), 反复缩放不会层层叠加 calc,回到 100% 时原值精确还原(实测:--sheldWidth 回到 50vw, style 里无残留)。

4. 让卡片前端也一起缩放:JS 视口 / 坐标兼容

CSS 单位补好了,但卡片前端(HUD、小游戏、状态面板这类由前端脚本注入的界面)往往不写 vw,而是读窗口像素再写内联样式,例如实测到的:

<div id="linjiang-hud-stage" style="position:fixed; width:1280px; height:700px">

CSS zoom 不改 JS 的报告值,所以这类元素会变成 倍数 倍大、顶出横向滚动条。 JS 视口/坐标兼容(默认开)解决这个问题:它把下面这些接口统一改按"缩放后的布局坐标" 报告——与真实浏览器缩放下的语义一致:

接口 处理
window.innerWidth / innerHeight、visualViewport.width / height ÷ 倍数
根元素 clientWidth / clientHeight ÷ 倍数(body.clientWidth、offsetWidth、getComputedStyle 本来就是布局坐标,不动)
Element/Range.getBoundingClientRect / getClientRects 坐标与尺寸 ÷ 倍数
MouseEvent 的 clientX/Y、pageX/Y、offsetX/Y、movementX/Y、x/y ÷ 倍数
document.elementFromPoint / elementsFromPoint 入参 × 倍数(入参是视觉坐标)
缩放变化后 派发一次 resize,让"监听 resize 重排"的前端脚本重新适配

效果(lab 实测,1280 宽窗口 + 200%):

场景 兼容开 兼容关
前端按 innerWidth 写尺寸的浮层 视觉宽 1258px = 正好满屏 2516px,溢出
拖拽(style.left += clientX 位移) 光标与元素偏移恒定(精确跟随) 元素移动速度是光标的 2 倍
100% 时 补丁全部卸掉,接口还原 —

副作用与排查:极少数扩展会假设 rect 就是"屏幕像素",缩放≠100% 时其定位可能偏移; 遇到就把这个开关关掉(CSS 部分照常工作),或用下一节的 --cwz-zoom 精确补偿。

5. 卡片前端"串成手机版":虚拟视口

5.1 现象与原因

酒馆助手(JS-Slash-Runner)把每一楼的前端渲染在一个 <iframe id="TH-message-…"> 里, 这个 iframe 的宽度跟着聊天列走:iframe 宽 = 聊天列宽 − 内边距。真机实测(窗口 1707 宽、 --sheldWidth: 60vw):

窗口 1707 → 聊天列 1024 → 渲染 iframe 967

而卡片前端普遍用 @media (max-width: Npx) 区分桌面版/手机版。命定之诗 v4.3 的断点是 1080px, 于是 967 的 iframe 就落进了手机版:变窄变长、填不满聊天框。窗口一变小/聊天列一变窄就越容易命中, 这就是"有时候会自动串到手机版"。

5.2 做法:只把逻辑视口抬到刚越过断点

扩展会读渲染 iframe 自己的样式表,找出其中最大的宽度断点 N,然后:

<iframe style="width: 1081px; zoom: 967/1081">   <!-- N = 1080 -->
  • zoom 参与布局(不像 transform),所以父容器高度会自动收敛,无需手工记账;
  • 视觉宽度不变(仍是 967),只是内容等比缩小 967/1081 ≈ 0.89,几乎看不出来;
  • iframe 内部的 innerWidth 变成 1081、matchMedia('(max-width: 1000px)') 变 false, 卡片按桌面版排版;
  • 没有大断点的卡片完全不动:例如把 HUD 做成固定 550px "设备壳"的卡片,最大断点只有 520px, 比现有宽度还小,扩展就不会碰它,避免无意义地缩小内容。

真机实测(命定之诗 v4.3,窗口 1707×938):

项 关 开
iframe 逻辑视口(innerWidth) 967 1081
matchMedia('(max-width: 1000px)') true(手机版) false(桌面版)
视觉宽度 967 967
视觉高度 588 527

面板里的「逻辑视口宽度上限」(默认 1200px)是安全阀:断点比它更宽(比如某些框架的 1440)时, 扩展宁可不碰这一楼,也不为了半个断点把内容缩掉 30%;面板状态栏会提示"有楼层断点在 N px, 比宽度上限还宽",把滑条往右拉到 1920 即可。

5.3 和缩放叠加

虚拟视口只改 iframe 的逻辑宽度,页面缩放改的是视觉尺寸,两者互不干扰:

  • 想恢复卡片原本的字号:Ctrl+滚轮 放大一档(125%),0.89 × 1.25 ≈ 1.11, 视觉大小回到略大于原来,而卡片仍按桌面版排版;
  • 缩放变化后扩展会自动重算(zoom 补偿值是"视觉宽 ÷ 逻辑宽",跟着变),不需要重新进入聊天。

5.4 关掉它

个别卡片是"给手机做的"(排版依赖窄视口),抬宽后反而变丑:把「卡片前端保持桌面版」关掉即可, iframe 立刻恢复原状(样式由扩展内联写入,关掉即摘除,不留痕)。

6. 已知边界

  • 只测一次窗口尺寸的前端脚本:兼容模式让 innerWidth 变小,但一个"只在加载时量一次、 之后不监听 resize"的脚本仍会保留旧值。此时在目标缩放下重新进入聊天/刷新一次即可让它 按当前倍数适配;若不想刷新,可用 --cwz-zoom 精确补偿:

    #linjiang-hud-stage {
        width: calc(100vw / var(--cwz-zoom, 1)) !important;
        height: calc(100dvh / var(--cwz-zoom, 1)) !important;
    }

    内核会把当前倍数写到 :root 的 --cwz-zoom(100% 时为 1);凡是用到它的声明内核 不再插手(避免二次折算),扩展未加载时 var(--cwz-zoom, 1) 的兜底值保证样式仍然有效。

  • 跨源样式表无法折算(cssRules 读不到,只能跳过)。实测 TauriTavern 页面里确实存在 这种表(酒馆自己的 dynamic-styles.js 也会因此打印 SecurityError 警告), 但其中不含视口单位规则,所以不影响布局。

  • CSS @media 条件不折算:vw 这类长度是内核改写的,媒体查询却不是(改不了)。 酒馆的移动端判定走的是 UA(isMobile() 读 navigator.platform),所以不会因此误判; 但如果你有脚本按 matchMedia('(max-width: …)') 做布局分支,它会沿用未缩放的视口答案。

  • 跨源 iframe 内的滚轮事件不会触发缩放(顶层文档收不到)。

  • 缩放级别按设备保存,不进入酒馆的 settings.json,因此不随 TT-Sync 同步。

  • 虚拟视口只处理用 CSS 断点/媒体查询做响应式的卡片;纯靠 JS 读 innerWidth 分支的卡片 需要它自己在 resize 时重排(iframe 内部 innerWidth 已变成 1081,重排后即为桌面版)。

  • 虚拟视口读不到样式表时(跨源 iframe,例如 https://…/ 的 HUD 页面)不处理,只影响那一楼。

7. 如果要真正的原生缩放

给 TauriTavern 本体加两处即可(扩展无法代劳):

  1. tauri.conf.json 的窗口配置(或 Rust 侧 WebviewWindowBuilder)加上 "zoomHotkeysEnabled": true;
  2. 若还要让脚本控制缩放,capability 里补 core:webview:allow-set-webview-zoom。

在那之后本扩展可以直接停用(Ctrl+滚轮 由 WebView 原生处理,效果与浏览器完全一致, 连兼容开关都不需要:原生缩放本来就缩小 CSS 像素并把 innerWidth 一起改小)。

8. 文件与开发

zoom-core.js         缩放内核:视口单位补偿、JS 视口/坐标兼容、滚轮/快捷键、提示浮层(无酒馆依赖)
frontend-viewport.js 卡片前端虚拟视口:读 iframe 断点、抬逻辑宽度并用 zoom 缩回视觉宽度
index.js             扩展外壳:localStorage 持久化、设置面板、诊断入口
style.css            设置面板样式(纯 px)
manifest.json        扩展清单

诊断:控制台执行 __ctrlWheelZoom.getState(),或 __ctrlWheelZoom.controller.setZoom(150); __ctrlWheelZoom.frontendKeeper.getState() 能看到每一楼被判定的断点与套用的虚拟宽度。

内核可在普通浏览器里独立驱动,本次开发就是用 headless Chromium + CDP 加载真实酒馆页面 (public/index.html 的真实 CSS 与 DOM)跑的回归:

  • 档位 150/200/300/75/50/100:横向溢出恒为 0,#sheld 视觉宽度恒为 629px(= 50vw), 缩放比例与档位一致
  • Ctrl+滚轮 经真实输入管线:3 格向上 100% → 150%,6 格向下 150% → 75%; 不带 Ctrl 的滚轮不影响缩放
  • 快捷键:Ctrl+= → 110%,Ctrl+-×2 → 90%,Ctrl+0 → 100%
  • 连续 10 次改档共 10–20ms(每档约 1–2ms;首次扫描样式表约 30–50ms)
  • 兼容模式:按 innerWidth 写尺寸的前端浮层在 200% 下视觉宽 1258px(正好满屏)、 拖拽偏移恒定;关掉兼容后同场景分别是溢出与 2 倍过冲;100% 时补丁全部卸净
  • 动态场景:缩放中新增/替换样式表、酒馆改写内联 --sheldWidth、往返缩放,均不叠加 calc、100% 时精确还原(--sheldWidth 回到作者原值,无残留)
  • 真机(TauriTavern 2.3.0 + 你的扩展/聊天):#sheld 视觉宽恒为 60vw、Ctrl+0 精确还原、 扩展自身零异常

9. 许可

MIT,见 LICENSE。

About

TauriTavern / SillyTavern 扩展:按住 Ctrl 用鼠标滚轮等比缩放界面(视口单位折算 + JS 视口/坐标兼容,卡片前端与拖拽一起缩放)

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages