dsh-width-tiers 1.0.6 → 1.0.7

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/README.md CHANGED
@@ -10,11 +10,11 @@ ultra / full / custom).
10
10
  ## Why
11
11
 
12
12
  The DSH Web app caps the message column — and every table inside it — at
13
- `--dsh-chat-content-width` (0.1.7 default `clamp(680px, column × 0.64,
13
+ `--dsh-chat-content-width` (0.1.7–0.2.0 default `clamp(680px, column × 0.64,
14
14
  920px)`), no matter how wide your window is, so wide tables always need
15
15
  horizontal scrolling.
16
16
 
17
- This plugin sets the width per tier through 0.1.7's **native width axis**
17
+ This plugin sets the width per tier through the app's **native width axis**
18
18
  (`--dsh-chat-user-width` + the app's `dsh.conversation.contentWidth`
19
19
  preference), letting the app clamp every value to "column − 176px handle
20
20
  budget" — so the built-in drag handles keep working:
@@ -22,18 +22,22 @@ budget" — so the built-in drag handles keep working:
22
22
  | Tier | Content width | Sidebar |
23
23
  |---|---|---|
24
24
  | standard 标准 | app default | untouched |
25
- | medium 中等 | 960px | untouched |
25
+ | medium 中等 | 1120px | untouched |
26
26
  | wide 宽 | 1280px | untouched |
27
27
  | ultra 超宽 | 1400px | untouched |
28
28
  | full 全宽 | column minus the handle budget (fills the available width) | collapsed to the icon rail |
29
29
  | custom 自定义 | your dragged width, re-selectable from the menu | untouched |
30
30
 
31
+ The menu and the button title always show the width a tier resolves to **at
32
+ the current column**, so on narrow windows a clamped tier reads its real
33
+ value (e.g. `宽 668px`) instead of its nominal one.
34
+
31
35
  On narrow windows wider tiers are clamped to the available column, so nothing
32
36
  overflows under the sidebar.
33
37
 
34
38
  The choice persists in `localStorage` and is restored on the next visit; the
35
39
  menu highlights the current tier, and the button's five level-bars show it at
36
- a glance (all lit for custom). Dragging one of 0.1.7's native edge handles
40
+ a glance (all lit for custom). Dragging one of the app's native edge handles
37
41
  switches the picker to the custom tier — the plugin stands aside for native
38
42
  drags.
39
43
 
@@ -62,8 +66,9 @@ Restart `dsh web` (the plugin loads at boot) and hard-refresh the page
62
66
  - `full` collapses the sidebar to the icon rail through the app's own toggle
63
67
  — it is never force-hidden or locked: click a rail icon any time to expand
64
68
  it, and it stays expanded.
65
- - Non-standard tiers also close the details panel (re-closed automatically if
66
- reopened); `standard` leaves everything to the app.
69
+ - The right panel (rightbar) is never touched: its track and panel are fully
70
+ owned by the app, and the conversation yields space to it on its own.
71
+ `standard` leaves the layout entirely to the app.
67
72
 
68
73
  ## Language
69
74
 
@@ -90,10 +95,10 @@ language, and Chinese is the last resort. No plugin-side setting needed.
90
95
  ## Limitations
91
96
 
92
97
  - Content width cannot exceed the window: on a narrow window, wider tiers are
93
- capped by the available column. Use `full` (which also collapses the
94
- sidebar) for the maximum.
98
+ capped by the available column (the menu shows the capped value). Use
99
+ `full` (which also collapses the sidebar) for the maximum.
95
100
  - Requires the DSH Web client plugin system (dsh ≥ 0.1.0-rc.x with a `web`
96
- profile).
101
+ profile). Verified on dsh 0.1.7-alpha.1 and 0.2.0-rc.2.
97
102
 
98
103
  ## Uninstall
99
104
 
package/README.zh.md CHANGED
@@ -9,26 +9,29 @@ DeepSeek Harness Web GUI 的客户端插件:给对话区加**内容宽度档
9
9
  ## 为什么需要它
10
10
 
11
11
  DSH Web 把消息列——包括里面的所有表格——的宽度限制在
12
- `--dsh-chat-content-width`(0.1.7 默认 `clamp(680px, 列宽 × 0.64, 920px)`),
13
- 不管窗口多宽都不变,宽表格只能横向滚动。
12
+ `--dsh-chat-content-width`(0.1.7–0.2.0 默认
13
+ `clamp(680px, 列宽 × 0.64, 920px)`),不管窗口多宽都不变,宽表格只能横向滚动。
14
14
 
15
- 本插件通过 0.1.7 的**原生宽度通道**(`--dsh-chat-user-width` +
15
+ 本插件通过应用自身的**原生宽度通道**(`--dsh-chat-user-width` +
16
16
  `dsh.conversation.contentWidth` 偏好)按档位设定宽度,由应用自身钳制到
17
17
  「列宽 − 176px 手柄预留」,因此自带的原生拖拽手柄始终可用:
18
18
 
19
19
  | 档位 | 内容宽度 | 侧边栏 |
20
20
  |---|---|---|
21
21
  | 标准 | 应用默认 | 不动 |
22
- | 中等 | 960px | 不动 |
22
+ | 中等 | 1120px | 不动 |
23
23
  | 宽 | 1280px | 不动 |
24
24
  | 超宽 | 1400px | 不动 |
25
25
  | 全宽 | 列宽减手柄预留(占满可用宽度) | 收起为图标轨道 |
26
26
  | 自定义 | 拖拽原生手柄得出,可在菜单回切 | 不动 |
27
27
 
28
+ 菜单与按钮标题显示的是**当前列宽下实际生效的 px**:窄窗口下被钳制的档位会
29
+ 显示真实值(如「宽 668px」),而不是标称值。
30
+
28
31
  窄窗口下超宽档位会自动被钳到可用宽度,不会溢出被侧栏覆盖。
29
32
 
30
33
  选择会记住(localStorage),下次打开自动恢复;菜单里当前档位高亮,按钮上
31
- 的五条横条图标也直观显示当前档位(自定义档全亮)。拖动 0.1.7 自带的
34
+ 的五条横条图标也直观显示当前档位(自定义档全亮)。拖动应用自带的
32
35
  对话区边缘手柄会自动切到「自定义」档,插件让位于原生拖拽。
33
36
 
34
37
  ## 安装
@@ -55,7 +58,8 @@ bundle patch 会自动插入 `dsh-width-tiers` 加载项——无需手动改
55
58
  - 点圆形按钮 → 选择档位,当前档位高亮。
56
59
  - 「全宽」通过应用自己的收起按钮把侧边栏收成图标轨道——**不会被锁死**:
57
60
  随时点轨道图标展开,展开后保持展开。
58
- - 非标准档会关闭详情面板(被重新打开会自动再关);标准档完全交给应用。
61
+ - 插件**不碰右侧面板**:右栏的轨道与面板完全由应用管理,展开时对话内容
62
+ 自适应让位;「标准」档完全交给应用。
59
63
 
60
64
  ## 多语言
61
65
 
@@ -76,9 +80,10 @@ bundle patch 会自动插入 `dsh-width-tiers` 加载项——无需手动改
76
80
 
77
81
  ## 已知限制
78
82
 
79
- - 内容宽度不可能超过窗口:窗口窄时,高档位会被可用列宽截断;想要最大
80
- 空间请用「全宽」(同时收起侧边栏)。
83
+ - 内容宽度不可能超过窗口:窗口窄时,高档位会被可用列宽截断(菜单会显示
84
+ 截断后的实际值);想要最大空间请用「全宽」(同时收起侧边栏)。
81
85
  - 依赖 DSH Web 客户端插件体系(带 web profile 的 dsh ≥ 0.1.0-rc.x)。
86
+ 已在 dsh 0.1.7-alpha.1 与 0.2.0-rc.2 上验证。
82
87
 
83
88
  ## 卸载
84
89
 
package/lib/client.js CHANGED
@@ -10,7 +10,7 @@ window.__ModuleLoader__.load({
10
10
  * dsh-width-tiers — chat content width tiers for the DSH Web GUI.
11
11
  *
12
12
  * The app derives the transcript width from
13
- * `--dsh-chat-content-width`, which 0.1.7-alpha.1 resolves as
13
+ * `--dsh-chat-content-width`, which 0.1.7 and 0.2.0 resolve as
14
14
  * `var(--dsh-chat-user-width, clamp(680px, column * .64, 920px))` on
15
15
  * the conversation body. 0.1.7 also added native drag handles
16
16
  * (ConversationWidthControls) that publish
@@ -23,7 +23,7 @@ window.__ModuleLoader__.load({
23
23
  * `applyContentWidth`. Other invariants:
24
24
  *
25
25
  * 标准 standard app default (no override)
26
- * 中等 medium 960px (sidebar untouched)
26
+ * 中等 medium 1120px (sidebar untouched)
27
27
  * 宽 wide 1280px (sidebar untouched)
28
28
  * 超宽 ultra 1400px (sidebar untouched)
29
29
  * 全宽 full column max (sidebar collapsed to the icon rail)
@@ -31,13 +31,18 @@ window.__ModuleLoader__.load({
31
31
  * the app's native handles; the dragged value stays
32
32
  * re-selectable from the menu)
33
33
  *
34
+ * The menu and title always show the px a tier resolves to at the
35
+ * CURRENT column (see `tierValueText`), so a clamped tier never
36
+ * claims its nominal width on a narrow window.
37
+ *
34
38
  * - Panels are driven through the layout service (ctx.layout), never
35
39
  * by simulating clicks.
36
40
  * - The sidebar is only ever collapsed by the `full` tier, through the
37
41
  * app's own toggle — never force-hidden, never locked. Going back to
38
42
  * `standard` restores it only if this plugin collapsed it.
39
- * - Non-standard tiers close the right panel (rightbar, self-heal
40
- * while active); `standard` leaves everything to the app.
43
+ * - The rightbar is never touched: on 0.1.7+ its panel lives in its
44
+ * own seat, so closing the track would leave it overlaying the
45
+ * conversation; the app's width axis yields space on its own.
41
46
  * - The chosen tier persists in localStorage and is restored on the
42
47
  * next visit.
43
48
  * - All copy is localized through the app's locale service: tier
@@ -54,6 +59,12 @@ window.__ModuleLoader__.load({
54
59
  * the horizontal budget both width handles + safe edges reserve. */
55
60
  const CONTENT_MIN_PX = 640;
56
61
  const CONTENT_EDGE_BUDGET_PX = 176;
62
+ /* The app's default transcript width (resolveContentWidth's
63
+ * no-preference branch): clamp(680px, column × 0.64, 920px). Used to
64
+ * show what `standard` actually resolves to at the current column. */
65
+ const DEFAULT_MIN_PX = 680;
66
+ const DEFAULT_MAX_PX = 920;
67
+ const DEFAULT_COLUMN_RATIO = 0.64;
57
68
  const READY_TIMEOUT_MS = 30000;
58
69
  /* Self-heal cadence: DOM-driven (MutationObserver scheduling on the
59
70
  * next animation frame — before paint, so the width never flashes
@@ -94,15 +105,17 @@ window.__ModuleLoader__.load({
94
105
  };
95
106
  //#endregion
96
107
 
108
+ /* Tier ids and label keys only — every displayed width is resolved at
109
+ * render time from the live column (see tierValueText), so the menu
110
+ * never claims a width the app would not actually apply. */
97
111
  const TIERS = [
98
- { id: "standard", key: "tier.standard", value: "748px" },
99
- { id: "medium", key: "tier.medium", value: "960px" },
100
- { id: "wide", key: "tier.wide", value: "1280px" },
101
- { id: "ultra", key: "tier.ultra", value: "1400px" },
102
- { id: "full", key: "tier.full", value: "100%" },
103
- /* Reached only by dragging a native width handle; the displayed
104
- * value is the dragged width, resolved at render time. */
105
- { id: "custom", key: "tier.custom", value: null }
112
+ { id: "standard", key: "tier.standard" },
113
+ { id: "medium", key: "tier.medium" },
114
+ { id: "wide", key: "tier.wide" },
115
+ { id: "ultra", key: "tier.ultra" },
116
+ { id: "full", key: "tier.full" },
117
+ /* Reached only by dragging a native width handle. */
118
+ { id: "custom", key: "tier.custom" }
106
119
  ];
107
120
 
108
121
  function tierOf(id) {
@@ -286,7 +299,7 @@ window.__ModuleLoader__.load({
286
299
  function tierPx(tier, root) {
287
300
  const cap = Math.max(CONTENT_MIN_PX, root.offsetWidth - CONTENT_EDGE_BUDGET_PX);
288
301
  switch (tier) {
289
- case "medium": return Math.min(960, cap);
302
+ case "medium": return Math.min(1120, cap);
290
303
  case "wide": return Math.min(1280, cap);
291
304
  case "ultra": return Math.min(1400, cap);
292
305
  case "full": return cap;
@@ -298,6 +311,17 @@ window.__ModuleLoader__.load({
298
311
  }
299
312
  }
300
313
 
314
+ /* The px a tier resolves to right now, including the app's own
315
+ * default formula for `standard`; null when the tier has no
316
+ * width of its own yet (custom before the first drag). */
317
+ function resolvedWidthPx(tier, root) {
318
+ if (tier === "standard") {
319
+ const column = root.offsetWidth;
320
+ return Math.round(Math.max(DEFAULT_MIN_PX, Math.min(column * DEFAULT_COLUMN_RATIO, DEFAULT_MAX_PX)));
321
+ }
322
+ return tierPx(tier, root);
323
+ }
324
+
301
325
  function applyContentWidth(tier) {
302
326
  const root = findWidthRoot();
303
327
  if (root === null) return;
@@ -383,12 +407,18 @@ window.__ModuleLoader__.load({
383
407
  return `<svg viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" focusable="false">${bars}</svg>`;
384
408
  }
385
409
 
386
- /* Display value for a tier; `custom` shows the dragged width. */
410
+ /* Display value for a tier: the px it resolves to at the live
411
+ * column (what the app would actually apply), so narrow windows
412
+ * show the clamped value instead of the nominal one. Falls back
413
+ * to the recorded drag when the width root is not mounted. */
387
414
  function tierValueText(tierId) {
388
- const def = tierOf(tierId);
389
- if (def.value !== null) return def.value;
390
- const px = readStoredNumber(CUSTOM_KEY);
391
- return px === null ? "—" : `${Math.round(px)}px`;
415
+ const root = findWidthRoot();
416
+ if (root !== null) {
417
+ const px = resolvedWidthPx(tierId, root);
418
+ if (px !== null) return `${px}px`;
419
+ }
420
+ const dragged = readStoredNumber(CUSTOM_KEY);
421
+ return dragged === null ? "—" : `${Math.round(dragged)}px`;
392
422
  }
393
423
 
394
424
  function syncButton() {
@@ -427,6 +457,13 @@ window.__ModuleLoader__.load({
427
457
  menu.appendChild(item);
428
458
  }
429
459
 
460
+ /* Write only on change: syncMenu also runs on window resizes,
461
+ * and untouched text should not churn the DOM (the plugin's own
462
+ * MutationObserver watches childList). */
463
+ function setText(el, text) {
464
+ if (el.textContent !== text) el.textContent = text;
465
+ }
466
+
430
467
  function syncMenu() {
431
468
  const tier = currentTier();
432
469
  const items = menu.querySelectorAll("button[data-tier]");
@@ -436,9 +473,9 @@ window.__ModuleLoader__.load({
436
473
  items[i].classList.toggle("selected", on);
437
474
  items[i].setAttribute("aria-checked", String(on));
438
475
  const labelEl = items[i].querySelector(".dshwm-label");
439
- if (labelEl) labelEl.textContent = t(tierOf(id).key);
476
+ if (labelEl) setText(labelEl, t(tierOf(id).key));
440
477
  const valueEl = items[i].querySelector(".dshwm-value");
441
- if (valueEl) valueEl.textContent = tierValueText(id);
478
+ if (valueEl) setText(valueEl, tierValueText(id));
442
479
  /* custom is inert until the user has dragged once */
443
480
  const disabled = id === "custom" && readStoredNumber(CUSTOM_KEY) === null;
444
481
  items[i].classList.toggle("disabled", disabled);
@@ -665,6 +702,22 @@ window.__ModuleLoader__.load({
665
702
  fallbackTimer = setInterval(() => heal(false), FALLBACK_HEAL_MS);
666
703
  }
667
704
 
705
+ /* ---- keep the displayed px honest when the column changes ----
706
+ * Window resizes (and layout handle drags) change what every
707
+ * tier resolves to, so re-render the copy on the next frame.
708
+ * The menu is also re-synced whenever it opens. */
709
+ let resizeFrame = null;
710
+
711
+ const onWindowResize = () => {
712
+ if (resizeFrame !== null) return;
713
+ scheduleFrame(() => {
714
+ resizeFrame = null;
715
+ syncButton();
716
+ syncMenu();
717
+ });
718
+ };
719
+ window.addEventListener("resize", onWindowResize);
720
+
668
721
  /* ---- disposer ---- */
669
722
  return () => {
670
723
  unsubscribeLocale();
@@ -673,6 +726,8 @@ window.__ModuleLoader__.load({
673
726
  clearTimeout(timeout);
674
727
  clearTimeout(rootRetryTimer);
675
728
  if (healFrame !== null) cancelFrame(healFrame);
729
+ if (resizeFrame !== null) cancelFrame(resizeFrame);
730
+ window.removeEventListener("resize", onWindowResize);
676
731
  if (observer !== null) observer.disconnect();
677
732
  document.removeEventListener("click", onDocClick);
678
733
  document.removeEventListener("keydown", onDocKey);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "dsh-width-tiers",
3
- "version": "1.0.6",
4
- "description": "DSH Web client plugin: chat content width tiers (standard / medium / wide / ultra / full / custom) with a floating picker button. Drives the app's native width axis on DSH 0.1.7+, so the built-in drag handles keep working. UI copy follows the app language (zh / en).",
3
+ "version": "1.0.7",
4
+ "description": "DSH Web client plugin: chat content width tiers (standard / medium / wide / ultra / full / custom) with a floating picker button. Drives the app's native width axis on DSH 0.1.7+ (verified on 0.2.0-rc.2), so the built-in drag handles keep working. UI copy follows the app language (zh / en).",
5
5
  "license": "MIT",
6
6
  "author": "aaronlei",
7
7
  "publishConfig": {