dsh-width-tiers 1.0.0 → 1.0.1
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 +16 -0
- package/README.zh.md +14 -0
- package/lib/client.js +66 -16
- package/package.json +4 -3
package/README.md
CHANGED
|
@@ -47,6 +47,11 @@ Restart `dsh web` (the plugin loads at boot) and hard-refresh the page
|
|
|
47
47
|
(Ctrl+Shift+R). A round button with five bars appears at the bottom-right.
|
|
48
48
|
|
|
49
49
|
> Installing from a local checkout: `dsh plugin --profile web add file:/path/to/dsh-width-tiers`.
|
|
50
|
+
> Local-development gotcha: `file:` dependencies are **copied** into the
|
|
51
|
+
> profile's `node_modules` (pnpm does not symlink them). After editing the
|
|
52
|
+
> plugin source, re-sync the copy (e.g. `rsync -a --delete --exclude .git
|
|
53
|
+
> /path/to/dsh-width-tiers/ ~/.dsh/profiles/web/node_modules/dsh-width-tiers/`)
|
|
54
|
+
> or re-install after removing it, then restart `dsh web` and hard-refresh.
|
|
50
55
|
|
|
51
56
|
## Usage
|
|
52
57
|
|
|
@@ -57,6 +62,14 @@ Restart `dsh web` (the plugin loads at boot) and hard-refresh the page
|
|
|
57
62
|
- Non-standard tiers also close the details panel (re-closed automatically if
|
|
58
63
|
reopened); `standard` leaves everything to the app.
|
|
59
64
|
|
|
65
|
+
## Language
|
|
66
|
+
|
|
67
|
+
All picker copy (tier labels, button `aria-label` and title) follows the
|
|
68
|
+
app's **Settings → Language** (中文 / English), through the app's own locale
|
|
69
|
+
service. Switching the language re-renders the button and menu immediately;
|
|
70
|
+
when no explicit language is chosen the app falls back to the browser's
|
|
71
|
+
language, and Chinese is the last resort. No plugin-side setting needed.
|
|
72
|
+
|
|
60
73
|
## How it works
|
|
61
74
|
|
|
62
75
|
- The element that defines `--dsh-chat-content-width` is **located at
|
|
@@ -67,6 +80,9 @@ Restart `dsh web` (the plugin loads at boot) and hard-refresh the page
|
|
|
67
80
|
simulating clicks.
|
|
68
81
|
- The sidebar is only ever collapsed by the `full` tier; returning to
|
|
69
82
|
`standard` restores it only if this plugin collapsed it.
|
|
83
|
+
- Dictionaries are registered with the app's **locale service**
|
|
84
|
+
(`ctx.locale.register` / `bind`) and re-rendered through its change
|
|
85
|
+
subscription, so the copy always matches the app's active language.
|
|
70
86
|
|
|
71
87
|
## Limitations
|
|
72
88
|
|
package/README.zh.md
CHANGED
|
@@ -43,6 +43,11 @@ dsh plugin --profile web add dsh-width-tiers
|
|
|
43
43
|
右下角会出现五条横条的圆形按钮。
|
|
44
44
|
|
|
45
45
|
> 本地目录安装:`dsh plugin --profile web add file:/path/to/dsh-width-tiers`。
|
|
46
|
+
> 本地开发注意:`file:` 依赖会被 **拷贝** 进 profile 的 node_modules(pnpm
|
|
47
|
+
> 不会建软链)。改完插件源码后,需要重新同步拷贝(例如 `rsync -a --delete
|
|
48
|
+
> --exclude .git /path/to/dsh-width-tiers/
|
|
49
|
+
> ~/.dsh/profiles/web/node_modules/dsh-width-tiers/`)或先 remove 再 add,
|
|
50
|
+
> 然后重启 `dsh web` 并硬刷新。
|
|
46
51
|
|
|
47
52
|
## 使用
|
|
48
53
|
|
|
@@ -51,6 +56,13 @@ dsh plugin --profile web add dsh-width-tiers
|
|
|
51
56
|
随时点轨道图标展开,展开后保持展开。
|
|
52
57
|
- 非标准档会关闭详情面板(被重新打开会自动再关);标准档完全交给应用。
|
|
53
58
|
|
|
59
|
+
## 多语言
|
|
60
|
+
|
|
61
|
+
选择器里的所有文案(档位名、按钮的 `aria-label` 和标题)跟随应用的
|
|
62
|
+
**设置 → 语言**(中文 / English),通过应用自身的 locale 服务实现。切换
|
|
63
|
+
语言后按钮和菜单会立即重渲染;未显式选择语言时,应用会回退到浏览器语言,
|
|
64
|
+
最后兜底中文。无需插件侧单独设置。
|
|
65
|
+
|
|
54
66
|
## 原理
|
|
55
67
|
|
|
56
68
|
- 运行时**自动定位**定义 `--dsh-chat-content-width` 的元素(第一个计算值
|
|
@@ -58,6 +70,8 @@ dsh plugin --profile web add dsh-width-tiers
|
|
|
58
70
|
- 面板操作走 **layout 服务**(`ctx.layout`),从不模拟点击。
|
|
59
71
|
- 侧边栏只被「全宽」档收起;回到「标准」时,只有本插件自己收起的侧边栏
|
|
60
72
|
才会被恢复展开(你手动收的不动)。
|
|
73
|
+
- 字典注册到应用的 **locale 服务**(`ctx.locale.register` / `bind`),并
|
|
74
|
+
订阅其变更事件重渲染,文案始终与应用的当前语言一致。
|
|
61
75
|
|
|
62
76
|
## 已知限制
|
|
63
77
|
|
package/lib/client.js
CHANGED
|
@@ -35,18 +35,50 @@ window.__ModuleLoader__.load({
|
|
|
35
35
|
* active); `standard` leaves everything to the app.
|
|
36
36
|
* - The chosen tier persists in localStorage and is restored on the
|
|
37
37
|
* next visit.
|
|
38
|
+
* - All copy is localized through the app's locale service: tier
|
|
39
|
+
* labels and the button's aria/title follow Settings → Language
|
|
40
|
+
* (zh / en, browser-derived fallback) and re-render on switch.
|
|
38
41
|
*/
|
|
39
42
|
const KEY = "dsh.widthTier";
|
|
40
43
|
const OLD_KEY = "dsh.wide";
|
|
41
44
|
const SELF_HEAL_MS = 1000;
|
|
42
45
|
const READY_TIMEOUT_MS = 30000;
|
|
43
46
|
|
|
47
|
+
/** Dictionary namespace owned by this plugin. */
|
|
48
|
+
const NS = "width-tiers";
|
|
49
|
+
|
|
50
|
+
//#region lib/types/client/locales.js
|
|
51
|
+
/** `width-tiers` namespace dictionaries (tier labels and picker copy). */
|
|
52
|
+
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
53
|
+
const zh = {
|
|
54
|
+
"tier.standard": "标准",
|
|
55
|
+
"tier.medium": "中等",
|
|
56
|
+
"tier.wide": "宽",
|
|
57
|
+
"tier.ultra": "超宽",
|
|
58
|
+
"tier.full": "全宽",
|
|
59
|
+
"button.aria": "对话区宽度档位:{label}({value}),点击选择档位",
|
|
60
|
+
"button.title.summary": "对话区宽度档位:{label}({value})",
|
|
61
|
+
"button.title.hint": "点击选择:{list}"
|
|
62
|
+
};
|
|
63
|
+
/** English dictionary, checked complete against the zh key set. */
|
|
64
|
+
const en = {
|
|
65
|
+
"tier.standard": "Standard",
|
|
66
|
+
"tier.medium": "Medium",
|
|
67
|
+
"tier.wide": "Wide",
|
|
68
|
+
"tier.ultra": "Ultra",
|
|
69
|
+
"tier.full": "Full",
|
|
70
|
+
"button.aria": "Chat width tier: {label} ({value}). Click to choose a tier.",
|
|
71
|
+
"button.title.summary": "Chat width tier: {label} ({value})",
|
|
72
|
+
"button.title.hint": "Click to choose: {list}"
|
|
73
|
+
};
|
|
74
|
+
//#endregion
|
|
75
|
+
|
|
44
76
|
const TIERS = [
|
|
45
|
-
{ id: "standard",
|
|
46
|
-
{ id: "medium",
|
|
47
|
-
{ id: "wide",
|
|
48
|
-
{ id: "ultra",
|
|
49
|
-
{ id: "full",
|
|
77
|
+
{ id: "standard", key: "tier.standard", value: "748px" },
|
|
78
|
+
{ id: "medium", key: "tier.medium", value: "960px" },
|
|
79
|
+
{ id: "wide", key: "tier.wide", value: "1280px" },
|
|
80
|
+
{ id: "ultra", key: "tier.ultra", value: "1400px" },
|
|
81
|
+
{ id: "full", key: "tier.full", value: "100%" }
|
|
50
82
|
];
|
|
51
83
|
|
|
52
84
|
function tierOf(id) {
|
|
@@ -76,14 +108,18 @@ window.__ModuleLoader__.load({
|
|
|
76
108
|
//#endregion
|
|
77
109
|
|
|
78
110
|
/** Services required by the browser bundle. */
|
|
79
|
-
const inject = ["layout"];
|
|
111
|
+
const inject = ["layout", "locale"];
|
|
80
112
|
|
|
81
113
|
/**
|
|
82
114
|
* Client plugin body: inject the styles, build the picker button and
|
|
83
115
|
* menu, and drive the width tiers through the layout service.
|
|
84
|
-
* @param ctx - client root context (layout injected).
|
|
116
|
+
* @param ctx - client root context (layout + locale injected).
|
|
85
117
|
*/
|
|
86
118
|
function apply(ctx) {
|
|
119
|
+
ctx.effect(() => ctx.locale.register(NS, {
|
|
120
|
+
zh,
|
|
121
|
+
en
|
|
122
|
+
}), "dsh-width-tiers: dictionaries");
|
|
87
123
|
ctx.effect(() => {
|
|
88
124
|
if (typeof document === "undefined") return;
|
|
89
125
|
|
|
@@ -93,6 +129,7 @@ window.__ModuleLoader__.load({
|
|
|
93
129
|
document.head.appendChild(styleTag);
|
|
94
130
|
|
|
95
131
|
const layout = ctx.layout;
|
|
132
|
+
const t = ctx.locale.bind(NS);
|
|
96
133
|
|
|
97
134
|
/* ---- helpers ---- */
|
|
98
135
|
const body = () => document.body;
|
|
@@ -227,13 +264,16 @@ window.__ModuleLoader__.load({
|
|
|
227
264
|
|
|
228
265
|
function syncButton() {
|
|
229
266
|
const tier = currentTier();
|
|
230
|
-
const
|
|
267
|
+
const def = tierOf(tier);
|
|
268
|
+
const label = t(def.key);
|
|
269
|
+
const summary = t("button.title.summary", { label, value: def.value });
|
|
270
|
+
const hint = t("button.title.hint", {
|
|
271
|
+
list: TIERS.map((tierDef) => `${t(tierDef.key)} ${tierDef.value}`).join(" / ")
|
|
272
|
+
});
|
|
231
273
|
btn.innerHTML = barIcon(tier);
|
|
232
274
|
btn.classList.toggle("active", tier !== "standard");
|
|
233
|
-
btn.setAttribute("aria-label",
|
|
234
|
-
btn.title =
|
|
235
|
-
`对话区宽度档位:${t.label}(${t.value})\n` +
|
|
236
|
-
"点击选择:标准 748px / 中等 960px / 宽 1280px / 超宽 1400px / 全宽 100%";
|
|
275
|
+
btn.setAttribute("aria-label", t("button.aria", { label, value: def.value }));
|
|
276
|
+
btn.title = `${summary}\n${hint}`;
|
|
237
277
|
}
|
|
238
278
|
|
|
239
279
|
/* ---- tier menu ---- */
|
|
@@ -242,15 +282,15 @@ window.__ModuleLoader__.load({
|
|
|
242
282
|
menu.setAttribute("role", "menu");
|
|
243
283
|
menu.hidden = true;
|
|
244
284
|
|
|
245
|
-
for (const
|
|
285
|
+
for (const def of TIERS) {
|
|
246
286
|
const item = document.createElement("button");
|
|
247
287
|
item.type = "button";
|
|
248
288
|
item.setAttribute("role", "menuitemradio");
|
|
249
|
-
item.setAttribute("data-tier",
|
|
250
|
-
item.innerHTML = `<span class="dshwm-label">${t.
|
|
289
|
+
item.setAttribute("data-tier", def.id);
|
|
290
|
+
item.innerHTML = `<span class="dshwm-label">${t(def.key)}</span><span class="dshwm-value">${def.value}</span>`;
|
|
251
291
|
item.addEventListener("click", (e) => {
|
|
252
292
|
if (e && e.stopPropagation) e.stopPropagation();
|
|
253
|
-
applyTier(
|
|
293
|
+
applyTier(def.id);
|
|
254
294
|
hideMenu();
|
|
255
295
|
});
|
|
256
296
|
menu.appendChild(item);
|
|
@@ -263,6 +303,8 @@ window.__ModuleLoader__.load({
|
|
|
263
303
|
const on = items[i].getAttribute("data-tier") === tier;
|
|
264
304
|
items[i].classList.toggle("selected", on);
|
|
265
305
|
items[i].setAttribute("aria-checked", String(on));
|
|
306
|
+
const labelEl = items[i].querySelector(".dshwm-label");
|
|
307
|
+
if (labelEl) labelEl.textContent = t(tierOf(items[i].getAttribute("data-tier")).key);
|
|
266
308
|
}
|
|
267
309
|
}
|
|
268
310
|
|
|
@@ -301,6 +343,13 @@ window.__ModuleLoader__.load({
|
|
|
301
343
|
window.setTimeout(() => btn.classList.remove("pulse"), 700);
|
|
302
344
|
}
|
|
303
345
|
|
|
346
|
+
/* ---- re-render localized copy when the app language changes ---- */
|
|
347
|
+
const renderTexts = () => {
|
|
348
|
+
syncButton();
|
|
349
|
+
syncMenu();
|
|
350
|
+
};
|
|
351
|
+
const unsubscribeLocale = ctx.locale.subscribe(renderTexts);
|
|
352
|
+
|
|
304
353
|
/* ---- attach once the app shell has mounted ---- */
|
|
305
354
|
let attached = false;
|
|
306
355
|
|
|
@@ -339,6 +388,7 @@ window.__ModuleLoader__.load({
|
|
|
339
388
|
|
|
340
389
|
/* ---- disposer ---- */
|
|
341
390
|
return () => {
|
|
391
|
+
unsubscribeLocale();
|
|
342
392
|
clearInterval(readyTimer);
|
|
343
393
|
clearInterval(healTimer);
|
|
344
394
|
clearTimeout(timeout);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-width-tiers",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "DSH Web client plugin: chat content width tiers (standard / medium / wide / ultra / full) with a floating picker button. Frees wide tables from the app's hard-coded 748px content cap.",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "DSH Web client plugin: chat content width tiers (standard / medium / wide / ultra / full) with a floating picker button. Frees wide tables from the app's hard-coded 748px content cap. UI copy follows the app language (zh / en).",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "aaronlei",
|
|
7
7
|
"publishConfig": {
|
|
@@ -24,7 +24,8 @@
|
|
|
24
24
|
"client": {
|
|
25
25
|
"platform": "web",
|
|
26
26
|
"inject": [
|
|
27
|
-
"@deepseek-ai/dsh-client-ui-layout"
|
|
27
|
+
"@deepseek-ai/dsh-client-ui-layout",
|
|
28
|
+
"@deepseek-ai/dsh-client-locale"
|
|
28
29
|
]
|
|
29
30
|
}
|
|
30
31
|
},
|