dsh-happy-reader 0.3.3 → 0.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +55 -0
- package/README.md +12 -5
- package/lib/client.js +59 -15
- package/package.json +5 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# 版本记录
|
|
2
|
+
|
|
3
|
+
版本策略:插件版本与 DSH 客户端版本绑定。`package.json.engines.dsh` 是安装/更新时的
|
|
4
|
+
硬门槛;客户端 `apply()` 里还有一次运行时闸门,手动 `file:///` 安装也会被拦住。
|
|
5
|
+
旧的 DSH 用户继续装旧的插件版本,不追求“一个版本通吃”。
|
|
6
|
+
|
|
7
|
+
| 插件版本 | DSH 范围 | 状态 |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| 0.3.4 | `>=0.2.2-rc.2 <0.2.3` | 2026-11-12 已发布 |
|
|
10
|
+
| 0.3.3 / 0.3.2 | DSH 0.2.2-rc.1 / rc.2 | 已发布 |
|
|
11
|
+
| 0.3.0 / 0.3.1 | DSH 0.2.2-rc.1 | 已发布 |
|
|
12
|
+
| 0.2.0 – 0.2.7 | DSH 0.2.2 早期 | 已发布,保留 tag |
|
|
13
|
+
|
|
14
|
+
## 0.3.4(2026-11-12 发布)
|
|
15
|
+
|
|
16
|
+
- 适配 DSH 0.2.2-rc.2;`engines.dsh` 收紧到 `>=0.2.2-rc.2 <0.2.3`,并加运行时版本闸门。
|
|
17
|
+
- 默认快捷键改为 `Ctrl/⌘+Alt+I`(隐藏输入框)/ `Ctrl/⌘+Alt+T`(隐藏上边栏)。
|
|
18
|
+
- 复制监听先看 `e.defaultPrevented`:官方或其它插件先处理时主动让行。
|
|
19
|
+
- macOS:面板与 Aa 加 `data-window-drag-exclude`(单个按钮不需要
|
|
20
|
+
`data-window-drag-controls`;仅基础声明,未实机验证)。
|
|
21
|
+
- 面板稳定性:最大化/还原/全屏切换时只按新视口夹取当前落点,不再重跑自动候选;
|
|
22
|
+
拖动 Aa 不再带着已打开的面板一起移动;未拖动过的 Aa 在窗口变化后仍按右下 16px 锚定。
|
|
23
|
+
|
|
24
|
+
## 0.3.3
|
|
25
|
+
|
|
26
|
+
- 修复混合选区被整段退回原生:代码块↔列表/正文、表头↔列表、正文↔表格边界、表格↔表格。
|
|
27
|
+
- 首尾代码块补语言;边界表补完整表头/整行;有序列表中段续接真实序号;
|
|
28
|
+
嵌套列表子项不再输出 `- -`;任务项 checkbox 状态补回。
|
|
29
|
+
- 代码横幅/复制按钮的 UI 选区交还原生复制。
|
|
30
|
+
|
|
31
|
+
## 0.3.2
|
|
32
|
+
|
|
33
|
+
- 通过官方 `ctx.shortcuts` 注册“隐藏输入框 / 隐藏上边栏”,面板显示官方键帽提示。
|
|
34
|
+
- 隐藏输入区改用 `visibility:hidden` 预防聚焦,官方“引用文本”不再带动会话滚动。
|
|
35
|
+
- 代码围栏从 DSH 横幅读取语言;表格/列表结构补全;浅色主题面板白底深字。
|
|
36
|
+
- 面板高度锁定、滚动区顶部跳过 `scrollHeight` 读取、宽度观察器延后一帧。
|
|
37
|
+
|
|
38
|
+
## 0.3.1
|
|
39
|
+
|
|
40
|
+
- 修复行内代码选区复制外壳(`Elf64_Rela` 不再被转义成 `Elf64\_Rela`)。
|
|
41
|
+
- 记录官方原生右键“复制”与全局 copy 拦截的冲突(见 TODO)。
|
|
42
|
+
|
|
43
|
+
## 0.3.0
|
|
44
|
+
|
|
45
|
+
- 界面迁移到官方 `shell.overlay` / `settings.general.item` 槽位;文案走官方 locale;
|
|
46
|
+
开关符合 `role="switch"`;取色只用官方主题变量。
|
|
47
|
+
- 正文字体三组可折叠卡片(正文/代码/终端),中英分设字体,官方代码/终端字体与字号同步。
|
|
48
|
+
- 官方「通用设置」增加中文/西文两行正文字体;浮动面板与设置页双向同步。
|
|
49
|
+
- 宽度扩展、全屏补偿、引用滚动修复、热重载清理与禁用即恢复。
|
|
50
|
+
|
|
51
|
+
## 0.2.0 – 0.2.7
|
|
52
|
+
|
|
53
|
+
公开版本重命名后的迭代:面板开关与底部设置栏、滚动条贴右缘、隐藏上边栏收敛为
|
|
54
|
+
会话头正向判据、行距/字距、面板拖动/拉高、失败回退与安装/卸载清理。逐版本提交见
|
|
55
|
+
GitHub tag `v0.2.0` – `v0.2.7`。
|
package/README.md
CHANGED
|
@@ -128,8 +128,8 @@
|
|
|
128
128
|
### 10. 快捷键(官方通用设置)
|
|
129
129
|
- 通过官方 `ctx.shortcuts` 注册两条命令,自动出现在**设置 → 通用 → 快捷键**
|
|
130
130
|
(官方“编辑快捷键”入口)里:**隐藏输入框**、**隐藏上边栏**
|
|
131
|
-
- 桌面端默认键位:Windows/Linux `Ctrl+
|
|
132
|
-
macOS `⌘+
|
|
131
|
+
- 桌面端默认键位:Windows/Linux `Ctrl+Alt+I` / `Ctrl+Alt+T`,
|
|
132
|
+
macOS `⌘+Option+I` / `⌘+Option+T`;Web 端默认不绑定
|
|
133
133
|
- 在速查里可以直接录制、清除或恢复默认;如果默认键位被注册服务拒绝(与已注册
|
|
134
134
|
命令冲突),插件会自动改为“未绑定”命令;若被固定操作占用,速查会标出冲突,
|
|
135
135
|
仍可自行改键
|
|
@@ -167,9 +167,16 @@ dsh plugin --profile <profile> add dsh-happy-reader
|
|
|
167
167
|
|
|
168
168
|
## 兼容性
|
|
169
169
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
-
|
|
170
|
+
> 逐版本改动与 DSH 范围见 [CHANGELOG.md](CHANGELOG.md);旧版本继续保留在 npm 与 GitHub tag 上。
|
|
171
|
+
|
|
172
|
+
- **0.3.4**:DSH `0.2.2-rc.2` ≤ 版本 < `0.2.3`(`package.json.engines.dsh`);插件管理器
|
|
173
|
+
会在安装/更新时拒绝旧 DSH,客户端挂载时也会再做一次版本闸门。
|
|
174
|
+
- **0.3.3 / 0.3.2**:DSH 0.2.2-rc.1 / rc.2;**0.3.0 / 0.3.1**:DSH 0.2.2-rc.1。
|
|
175
|
+
- 仍在旧 DSH 上的用户请显式安装对应旧版,例如
|
|
176
|
+
`dsh plugin --profile <profile> add dsh-happy-reader@0.3.3`;不要依赖 `latest` 自动升级。
|
|
177
|
+
- Windows + DSH 桌面端实机验证;macOS 已给面板与 Aa 加 `data-window-drag-exclude`
|
|
178
|
+
基础兼容声明,但未实机验证;Linux 字体候选仍按 Windows 常用字体。
|
|
179
|
+
- 依赖官方客户端界面结构,DSH 版本升级后需要跟随发布对应插件版本。
|
|
173
180
|
- 运行时按事件工作:无常驻定时轮询;宽度观察器只在面板打开或扩展宽度生效时挂载,
|
|
174
181
|
纯高度变化不触发宽度逻辑;字体 token 晚就绪用有限次退避重试(成功后停止)。
|
|
175
182
|
|
package/lib/client.js
CHANGED
|
@@ -21,7 +21,7 @@ window.__ModuleLoader__.load({
|
|
|
21
21
|
var exports = module.exports
|
|
22
22
|
|
|
23
23
|
// ── 常量 ────────────────────────────────────────────────────────────
|
|
24
|
-
const VERSION = '0.3.
|
|
24
|
+
const VERSION = '0.3.4'
|
|
25
25
|
const INSTANCE_TOKEN = {}
|
|
26
26
|
const GROUPS_VERSION = 3
|
|
27
27
|
const STATE_KEY = 'dsh.reader-boost.v1'
|
|
@@ -868,8 +868,32 @@ window.__ModuleLoader__.load({
|
|
|
868
868
|
}
|
|
869
869
|
}
|
|
870
870
|
|
|
871
|
+
/** 0.3.4 起明确声明 DSH 兼容范围;手动 file:/// 安装没有插件管理器兜底,
|
|
872
|
+
* 这里再做一次运行时闸门。未知版本不拦截,避免破坏开发/预览环境。 */
|
|
873
|
+
function dshVersionSupported(version) {
|
|
874
|
+
if (typeof version !== 'string' || !version) return true
|
|
875
|
+
const m = /^(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?/.exec(version)
|
|
876
|
+
if (!m) return true
|
|
877
|
+
const major = Number(m[1]), minor = Number(m[2]), patch = Number(m[3])
|
|
878
|
+
if (major !== 0 || minor !== 2 || patch !== 2) return false
|
|
879
|
+
if (!m[4]) return true
|
|
880
|
+
const pre = /^rc\.(\d+)$/.exec(m[4])
|
|
881
|
+
return !!(pre && Number(pre[1]) >= 2)
|
|
882
|
+
}
|
|
883
|
+
|
|
871
884
|
function apply(ctx) {
|
|
872
885
|
try {
|
|
886
|
+
const bootVersion = globalThis.__DSH_BOOT__ && globalThis.__DSH_BOOT__.release
|
|
887
|
+
? globalThis.__DSH_BOOT__.release.version : undefined
|
|
888
|
+
if (!dshVersionSupported(bootVersion)) {
|
|
889
|
+
console.error('[dsh-happy-reader] 当前 DSH ' + bootVersion + ' 不在本插件支持范围(需 0.2.2-rc.2 至 0.2.2.x),已停止挂载。')
|
|
890
|
+
// 支持范围被收紧后热重载到本版本:旧实例不应继续留在页面上。
|
|
891
|
+
const staleDispose = window.__dsrbDispose
|
|
892
|
+
if (typeof staleDispose === 'function') {
|
|
893
|
+
try { staleDispose() } catch { /* 旧实例拆除失败不阻塞退出 */ }
|
|
894
|
+
}
|
|
895
|
+
return
|
|
896
|
+
}
|
|
873
897
|
// 热重载:DSH 监视插件文件变化,会再次执行本客户端。先拆旧实例、再按 id
|
|
874
898
|
// 清扫遗留节点、最后重建 —— 页面上运行的必然是最新代码。
|
|
875
899
|
const prevDispose = window.__dsrbDispose
|
|
@@ -996,9 +1020,9 @@ window.__ModuleLoader__.load({
|
|
|
996
1020
|
const shortcuts = sctx.shortcuts
|
|
997
1021
|
if (!shortcuts || typeof shortcuts.register !== 'function') return
|
|
998
1022
|
const desktopDefaults = (code) => ({
|
|
999
|
-
'desktop:macos': { code, modifiers: ['primary', '
|
|
1000
|
-
'desktop:windows': { code, modifiers: ['primary', '
|
|
1001
|
-
'desktop:linux': { code, modifiers: ['primary', '
|
|
1023
|
+
'desktop:macos': { code, modifiers: ['primary', 'alt'] },
|
|
1024
|
+
'desktop:windows': { code, modifiers: ['primary', 'alt'] },
|
|
1025
|
+
'desktop:linux': { code, modifiers: ['primary', 'alt'] },
|
|
1002
1026
|
})
|
|
1003
1027
|
const command = (id, labelKey, aliases, code, action) => ({
|
|
1004
1028
|
id,
|
|
@@ -1150,6 +1174,10 @@ window.__ModuleLoader__.load({
|
|
|
1150
1174
|
fab.type = 'button'
|
|
1151
1175
|
fab.title = t('fabTitle')
|
|
1152
1176
|
fab.setAttribute('aria-label', t('fabTitle'))
|
|
1177
|
+
// 0.2.2-rc.1+ macOS:自定义浮层控件需显式声明窗口拖拽排除。
|
|
1178
|
+
// 单个按钮用 data-window-drag-exclude 即可;data-window-drag-controls 是
|
|
1179
|
+
// “容器内控件分别排除”的作用域,不应标在按钮自身。
|
|
1180
|
+
fab.setAttribute('data-window-drag-exclude', '')
|
|
1153
1181
|
const [fx, fy] = clampPos(
|
|
1154
1182
|
state.x ?? window.innerWidth - FAB_SIZE - 16,
|
|
1155
1183
|
state.y ?? window.innerHeight - FAB_SIZE - 16,
|
|
@@ -1165,6 +1193,7 @@ window.__ModuleLoader__.load({
|
|
|
1165
1193
|
panel.id = PANEL_ID
|
|
1166
1194
|
panel.setAttribute('role', 'region')
|
|
1167
1195
|
panel.setAttribute('aria-label', t('panelTitle'))
|
|
1196
|
+
panel.setAttribute('data-window-drag-exclude', '') // macOS:浮层可见矩形不参与窗口拖拽
|
|
1168
1197
|
|
|
1169
1198
|
const headRow = el('div', 'dsrb-head')
|
|
1170
1199
|
const title = el('h4', null, t('panelTitle'))
|
|
@@ -1476,9 +1505,11 @@ window.__ModuleLoader__.load({
|
|
|
1476
1505
|
const titlebarH =
|
|
1477
1506
|
Number.parseFloat(getComputedStyle(document.documentElement).getPropertyValue('--dsh-windows-titlebar-height')) || 40
|
|
1478
1507
|
const CAP_W = 138
|
|
1479
|
-
function placePanel(keepScroll) {
|
|
1508
|
+
function placePanel(keepScroll, keepPosition) {
|
|
1480
1509
|
const scrollTop = typeof keepScroll === 'number' ? keepScroll : panelBody.scrollTop
|
|
1481
1510
|
const r = fab.getBoundingClientRect()
|
|
1511
|
+
const currentLeft = Number.parseFloat(panel.style.left)
|
|
1512
|
+
const currentTop = Number.parseFloat(panel.style.top)
|
|
1482
1513
|
const fixedH = typeof state.panelH === 'number'
|
|
1483
1514
|
if (!fixedH) {
|
|
1484
1515
|
panel.style.height = ''
|
|
@@ -1498,14 +1529,21 @@ window.__ModuleLoader__.load({
|
|
|
1498
1529
|
const ph = Math.max(PANEL_MIN_H, Math.min(desired, vh - 2 * M - BORDER_Y))
|
|
1499
1530
|
const clampX = (x) => Math.min(Math.max(M, x), Math.max(M, vw - pw - M))
|
|
1500
1531
|
const clampY = (y, yMin) => Math.min(Math.max(yMin, y), Math.max(yMin, vh - ph - BORDER_Y - M))
|
|
1532
|
+
const yMinFor = (x) => CAP_H > 0 && x + pw > vw - CAP_W ? CAP_H : M
|
|
1501
1533
|
let best = null
|
|
1502
|
-
|
|
1534
|
+
// 窗口最大化/还原/全屏切换时,面板若已经开着,保持当前视觉位置并只做视口夹取;
|
|
1535
|
+
// 不重新跑自动候选,避免面板在窗口状态变化时跳位。
|
|
1536
|
+
if (keepPosition && Number.isFinite(currentLeft) && Number.isFinite(currentTop)) {
|
|
1537
|
+
const sx = clampX(currentLeft)
|
|
1538
|
+
best = { x: sx, y: clampY(currentTop, yMinFor(sx)) }
|
|
1539
|
+
}
|
|
1540
|
+
if (!best && typeof state.panelX === 'number' && typeof state.panelY === 'number') {
|
|
1503
1541
|
// 用户拖过位置 → 用记住的位置;仍做两件保护:避开右上角系统按钮条;
|
|
1504
1542
|
// 若与按钮重叠(按钮可能后来被拖到附近)则放弃记忆、回自动布局
|
|
1505
1543
|
const sx = clampX(state.panelX)
|
|
1506
|
-
const sy = clampY(state.panelY,
|
|
1544
|
+
const sy = clampY(state.panelY, yMinFor(sx))
|
|
1507
1545
|
const overlapsFab = sx < r.right + GAP && sx + pw > r.left - GAP && sy < r.bottom + GAP && sy + ph > r.top - GAP
|
|
1508
|
-
if (overlapsFab) {
|
|
1546
|
+
if (overlapsFab && !keepPosition) {
|
|
1509
1547
|
state.panelX = null
|
|
1510
1548
|
state.panelY = null
|
|
1511
1549
|
writeState({ panelX: null, panelY: null })
|
|
@@ -1525,8 +1563,7 @@ window.__ModuleLoader__.load({
|
|
|
1525
1563
|
x < r.right + GAP && x + pw > r.left - GAP && y < r.bottom + GAP && y + ph > r.top - GAP
|
|
1526
1564
|
for (const c of candidates) {
|
|
1527
1565
|
const x = clampX(c.x)
|
|
1528
|
-
const
|
|
1529
|
-
const y = clampY(c.y, yMin)
|
|
1566
|
+
const y = clampY(c.y, yMinFor(x))
|
|
1530
1567
|
if (!hits(x, y)) {
|
|
1531
1568
|
best = { x, y }
|
|
1532
1569
|
break
|
|
@@ -1703,8 +1740,9 @@ window.__ModuleLoader__.load({
|
|
|
1703
1740
|
const root = document.documentElement
|
|
1704
1741
|
if (on) root.setAttribute(FS_ATTR, '')
|
|
1705
1742
|
else root.removeAttribute(FS_ATTR)
|
|
1706
|
-
// 全屏切换会改变系统按钮条的有无 →
|
|
1707
|
-
|
|
1743
|
+
// 全屏切换会改变系统按钮条的有无 → 面板开着时夹取到新视口;
|
|
1744
|
+
// 保留用户当前看到的落点,不重新跑自动候选。
|
|
1745
|
+
if (state.open) placePanel(undefined, true)
|
|
1708
1746
|
}
|
|
1709
1747
|
function applyFullscreen() {
|
|
1710
1748
|
// 进入全屏必须发生在用户手势内:本函数由开关 change / 整条点击同步触发,满足
|
|
@@ -2133,7 +2171,8 @@ window.__ModuleLoader__.load({
|
|
|
2133
2171
|
drag = null
|
|
2134
2172
|
if (wasDrag) {
|
|
2135
2173
|
writeState({ x: fab.offsetLeft, y: fab.offsetTop })
|
|
2136
|
-
|
|
2174
|
+
// 面板是独立浮层:拖动 Aa 时保持面板原位,不跟着按钮跑;
|
|
2175
|
+
// 下次打开面板再按新按钮位置自动布局。
|
|
2137
2176
|
} else {
|
|
2138
2177
|
togglePanel(!state.open)
|
|
2139
2178
|
}
|
|
@@ -2622,6 +2661,7 @@ window.__ModuleLoader__.load({
|
|
|
2622
2661
|
}
|
|
2623
2662
|
function onCopy(e) {
|
|
2624
2663
|
try {
|
|
2664
|
+
if (e.defaultPrevented) return // 尊重先注册/先执行的复制处理(含官方公式复制)
|
|
2625
2665
|
const sel = document.getSelection()
|
|
2626
2666
|
if (!sel || sel.isCollapsed || !sel.rangeCount) return
|
|
2627
2667
|
const elOf = (n) => (n && n.nodeType === 3 ? n.parentElement : n)
|
|
@@ -2753,11 +2793,15 @@ window.__ModuleLoader__.load({
|
|
|
2753
2793
|
document.addEventListener('keydown', onDocKeyDown)
|
|
2754
2794
|
|
|
2755
2795
|
function onResize() {
|
|
2756
|
-
|
|
2796
|
+
// 未拖动过:Aa 始终按“右下 16px”重新锚定;拖动过:按记住的绝对位置夹取。
|
|
2797
|
+
const baseX = state.x == null ? window.innerWidth - FAB_SIZE - 16 : state.x
|
|
2798
|
+
const baseY = state.y == null ? window.innerHeight - FAB_SIZE - 16 : state.y
|
|
2799
|
+
const [x, y] = clampPos(baseX, baseY, FAB_SIZE, FAB_SIZE)
|
|
2757
2800
|
fab.style.left = x + 'px'
|
|
2758
2801
|
fab.style.top = y + 'px'
|
|
2759
2802
|
refreshWidthTargets()
|
|
2760
|
-
|
|
2803
|
+
// 面板已开着:保持当前视觉位置并夹取视口,避免最大化/还原时跳位。
|
|
2804
|
+
if (state.open) placePanel(undefined, true)
|
|
2761
2805
|
}
|
|
2762
2806
|
window.addEventListener('resize', onResize)
|
|
2763
2807
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-happy-reader",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Happy Reader(阅读增强):隐藏输入框 / 上边栏、全屏切换、字号 10–40px、行距/字距微调、面板可拖动可拉高、复制为 Markdown(公式→LaTeX)、内容宽度扩展、中英文分别选正文字体 + 官方代码/终端字体与字号(仅列本机已装)—— 与官方设置协同,不冲突",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -34,6 +34,7 @@
|
|
|
34
34
|
"lib",
|
|
35
35
|
"cordis.patch.yml",
|
|
36
36
|
"README.md",
|
|
37
|
+
"CHANGELOG.md",
|
|
37
38
|
"LICENSE",
|
|
38
39
|
"locale/*.json",
|
|
39
40
|
"icon.svg"
|
|
@@ -46,5 +47,8 @@
|
|
|
46
47
|
"platform": "web",
|
|
47
48
|
"immediately": true
|
|
48
49
|
}
|
|
50
|
+
},
|
|
51
|
+
"engines": {
|
|
52
|
+
"dsh": ">=0.2.2-rc.2 <0.2.3"
|
|
49
53
|
}
|
|
50
54
|
}
|