@linxin666/dsh-remote-web-ui 0.3.9 → 0.3.11
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/lib/client.js +53 -9
- package/lib/client.js.map +1 -1
- package/lib/types/client/UpdateEntry.d.ts.map +1 -1
- package/lib/types/client/UpdateEntry.js +1 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +6 -0
- package/lib/types/client/locales.d.ts +8 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +8 -0
- package/lib/types/client/mobile-adapt.d.ts +9 -0
- package/lib/types/client/mobile-adapt.d.ts.map +1 -1
- package/lib/types/client/mobile-adapt.js +41 -6
- package/package.json +18 -18
- package/src/client/UpdateEntry.tsx +4 -1
- package/src/client/index.ts +5 -0
- package/src/client/locales.ts +8 -0
- package/src/client/mobile-adapt.ts +47 -6
- package/src/client/remote.module.css +20 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateEntry.d.ts","sourceRoot":"","sources":["../../../src/client/UpdateEntry.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAOnE,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;IACb,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,gBAAgB,+
|
|
1
|
+
{"version":3,"file":"UpdateEntry.d.ts","sourceRoot":"","sources":["../../../src/client/UpdateEntry.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAOnE,wEAAwE;AACxE,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;IACb,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;CACzB;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,gBAAgB,+BAkHxD"}
|
|
@@ -108,5 +108,5 @@ export function UpdateEntry({ wide, t }) {
|
|
|
108
108
|
};
|
|
109
109
|
}, [probeAvailability]);
|
|
110
110
|
const updateLabel = updateAvailable ? t("update.availableLabel") : t("update.label");
|
|
111
|
-
return (_jsxs(_Fragment, { children: [
|
|
111
|
+
return (_jsxs(_Fragment, { children: [_jsxs("button", { type: "button", className: css.trigger, "data-wide": wide ? (updateAvailable ? "wide" : undefined) : "rail", "data-update-available": updateAvailable ? "true" : undefined, "aria-label": updateLabel, "aria-expanded": open, title: updateLabel, onClick: openPanel, children: [_jsx(IconDownloadOutline16, { size: wide ? 16 : 18 }), updateAvailable && wide && (_jsx("span", { className: css.updateBadgeText, children: t("update.badge") }))] }), open && createPortal((_jsxs("div", { className: css.overlay, role: "presentation", children: [_jsx("div", { className: css.mask, "aria-hidden": "true", onClick: closePanel }), _jsx(UpdatePanel, { t: t, view: view, onClose: closePanel, onRecheck: () => { void check(); }, onStartUpdate: (status) => { void startUpdate(status); } })] })), document.body)] }));
|
|
112
112
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAA;AAelG,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAqBrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAA;AAelG,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAqBrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAyQ9C"}
|
|
@@ -85,6 +85,12 @@ export function apply(ctx) {
|
|
|
85
85
|
};
|
|
86
86
|
}
|
|
87
87
|
const t = ctx.locale.bind(NS);
|
|
88
|
+
// Hand the `remote` translate seat to the module-scope adaptation layer:
|
|
89
|
+
// the whale/compact-picker labels were rendered with the English fallback
|
|
90
|
+
// before any dictionary existed; the wiring plus the layer's own sync tick
|
|
91
|
+
// re-render them in the active locale.
|
|
92
|
+
if (adapt !== undefined)
|
|
93
|
+
adapt.translate = t;
|
|
88
94
|
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope;
|
|
89
95
|
const settingsScope = binder.bind({ namespace: REMOTE_WEB_UI_NS });
|
|
90
96
|
const enabled = () => {
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
3
|
export declare const zh: {
|
|
4
4
|
'entry.label': string;
|
|
5
|
+
'mobile.whale.open': string;
|
|
6
|
+
'mobile.composer.pickModel': string;
|
|
7
|
+
'mobile.composer.pickEffort': string;
|
|
5
8
|
title: string;
|
|
6
9
|
subtitle: string;
|
|
7
10
|
'card.title': string;
|
|
@@ -109,6 +112,7 @@ export declare const zh: {
|
|
|
109
112
|
'settings.saveFailed': string;
|
|
110
113
|
'settings.invalidNumber': string;
|
|
111
114
|
'update.label': string;
|
|
115
|
+
'update.badge': string;
|
|
112
116
|
'update.availableLabel': string;
|
|
113
117
|
'update.title': string;
|
|
114
118
|
'update.checking': string;
|
|
@@ -157,6 +161,9 @@ export type SettingsCardKey = RemoteKey;
|
|
|
157
161
|
/** English dictionary, checked complete against the zh key set. */
|
|
158
162
|
export declare const en: {
|
|
159
163
|
'entry.label': string;
|
|
164
|
+
'mobile.whale.open': string;
|
|
165
|
+
'mobile.composer.pickModel': string;
|
|
166
|
+
'mobile.composer.pickEffort': string;
|
|
160
167
|
title: string;
|
|
161
168
|
subtitle: string;
|
|
162
169
|
'card.title': string;
|
|
@@ -264,6 +271,7 @@ export declare const en: {
|
|
|
264
271
|
'settings.saveFailed': string;
|
|
265
272
|
'settings.invalidNumber': string;
|
|
266
273
|
'update.label': string;
|
|
274
|
+
'update.badge': string;
|
|
267
275
|
'update.availableLabel': string;
|
|
268
276
|
'update.title': string;
|
|
269
277
|
'update.checking': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,mEAAmE;AACnE,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,uEAAuE;AAEvE,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2JmB,CAAA;AAElC,sCAAsC;AACtC,MAAM,MAAM,SAAS,GAAG,MAAM,OAAO,EAAE,CAAA;AAEvC,wDAAwD;AACxD,MAAM,MAAM,eAAe,GAAG,SAAS,CAAA;AAEvC,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2JsB,CAAA"}
|
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
3
3
|
export const zh = {
|
|
4
4
|
'entry.label': '远程访问',
|
|
5
|
+
'mobile.whale.open': '打开侧边栏',
|
|
6
|
+
'mobile.composer.pickModel': '选择模型',
|
|
7
|
+
'mobile.composer.pickEffort': '选择推理等级',
|
|
5
8
|
'title': '远程访问',
|
|
6
9
|
'subtitle': '通过手机或另一台电脑配对,远程使用同一份 Web 界面(官方界面 + 移动端适配)',
|
|
7
10
|
'card.title': '设备配对',
|
|
@@ -111,6 +114,7 @@ export const zh = {
|
|
|
111
114
|
'settings.invalidNumber': '请输入数字,留空则使用默认值。',
|
|
112
115
|
// 更新面板(侧边栏更新按钮)。
|
|
113
116
|
'update.label': '检查更新',
|
|
117
|
+
'update.badge': '有更新',
|
|
114
118
|
'update.availableLabel': '发现新版本,检查更新',
|
|
115
119
|
'update.title': '检查更新',
|
|
116
120
|
'update.checking': '正在检查远程仓库中的新版本…',
|
|
@@ -155,6 +159,9 @@ export const zh = {
|
|
|
155
159
|
/** English dictionary, checked complete against the zh key set. */
|
|
156
160
|
export const en = {
|
|
157
161
|
'entry.label': 'Remote access',
|
|
162
|
+
'mobile.whale.open': 'Open sidebar',
|
|
163
|
+
'mobile.composer.pickModel': 'Pick model',
|
|
164
|
+
'mobile.composer.pickEffort': 'Pick reasoning effort',
|
|
158
165
|
'title': 'Remote access',
|
|
159
166
|
'subtitle': 'Pair a phone or another computer to share the same Web GUI (official UI + mobile adaptation)',
|
|
160
167
|
'card.title': 'Pair a device',
|
|
@@ -264,6 +271,7 @@ export const en = {
|
|
|
264
271
|
'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',
|
|
265
272
|
// Update panel (the sidebar update trigger).
|
|
266
273
|
'update.label': 'Check for updates',
|
|
274
|
+
'update.badge': 'Update available',
|
|
267
275
|
'update.availableLabel': 'New version available. Check for updates',
|
|
268
276
|
'update.title': 'Check for updates',
|
|
269
277
|
'update.checking': 'Checking the remote repository for newer releases…',
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
* reference (MIT, v47-v78) keep their version comments.
|
|
21
21
|
* @module @linxin666/dsh-remote-web-ui/client/mobile-adapt
|
|
22
22
|
*/
|
|
23
|
+
import type { RemoteKey } from './locales.ts';
|
|
23
24
|
/** The window global the plugin apply() wires the layout service into. */
|
|
24
25
|
export interface RemoteAdaptGlobal {
|
|
25
26
|
evaluate: () => void;
|
|
@@ -27,6 +28,14 @@ export interface RemoteAdaptGlobal {
|
|
|
27
28
|
toggleSidebar: (() => void) | null;
|
|
28
29
|
/** Wired by the plugin apply once ctx.layout is live. */
|
|
29
30
|
closeDetails: (() => void) | null;
|
|
31
|
+
/**
|
|
32
|
+
* Wired by the plugin apply once the `remote` locale namespace is bound:
|
|
33
|
+
* the translate seat the injected surfaces (whale, compact picker) read
|
|
34
|
+
* their labels from. Null until then; label reads fall back to English
|
|
35
|
+
* (the SDK's universal fallback) and the 600ms sync tick re-renders the
|
|
36
|
+
* labels once the seat is wired.
|
|
37
|
+
*/
|
|
38
|
+
translate: ((key: RemoteKey) => string) | null;
|
|
30
39
|
/** Plugin master switch: false reverts the layer, true re-evaluates. */
|
|
31
40
|
setEnabled(on: boolean): void;
|
|
32
41
|
/** Replays a pending closeDetails once the layout face is wired (the first apply ran before the wiring). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-adapt.d.ts","sourceRoot":"","sources":["../../../src/client/mobile-adapt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,yDAAyD;IACzD,aAAa,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAClC,yDAAyD;IACzD,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IACjC,wEAAwE;IACxE,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;IAC7B,4GAA4G;IAC5G,iBAAiB,IAAI,IAAI,CAAA;CAC1B;AAgOD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,
|
|
1
|
+
{"version":3,"file":"mobile-adapt.d.ts","sourceRoot":"","sources":["../../../src/client/mobile-adapt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,0EAA0E;AAC1E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,IAAI,CAAA;IACpB,yDAAyD;IACzD,aAAa,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAClC,yDAAyD;IACzD,YAAY,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IACjC;;;;;;OAMG;IACH,SAAS,EAAE,CAAC,CAAC,GAAG,EAAE,SAAS,KAAK,MAAM,CAAC,GAAG,IAAI,CAAA;IAC9C,wEAAwE;IACxE,UAAU,CAAC,EAAE,EAAE,OAAO,GAAG,IAAI,CAAA;IAC7B,4GAA4G;IAC5G,iBAAiB,IAAI,IAAI,CAAA;CAC1B;AAgOD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAyqBvC"}
|
|
@@ -252,6 +252,20 @@ export function startMobileAdapt() {
|
|
|
252
252
|
return;
|
|
253
253
|
window.__dshRemoteAdaptInstalled = true;
|
|
254
254
|
const w = window;
|
|
255
|
+
/**
|
|
256
|
+
* Label lookup for the injected surfaces: the `remote` namespace seat wired
|
|
257
|
+
* by the plugin apply, else the English fallback (the SDK's universal
|
|
258
|
+
* fallback; the sync tick re-renders the labels once the seat is live).
|
|
259
|
+
*/
|
|
260
|
+
const surfaceText = (key, fallback) => {
|
|
261
|
+
try {
|
|
262
|
+
const out = w.__dshRemoteAdapt?.translate?.(key);
|
|
263
|
+
return typeof out === 'string' && out.length > 0 ? out : fallback;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return fallback;
|
|
267
|
+
}
|
|
268
|
+
};
|
|
255
269
|
let active = false;
|
|
256
270
|
let savedViewportContent = null;
|
|
257
271
|
let whaleEl = null;
|
|
@@ -397,12 +411,26 @@ export function startMobileAdapt() {
|
|
|
397
411
|
removeCompactPicker();
|
|
398
412
|
return;
|
|
399
413
|
}
|
|
400
|
-
const zh = (navigator.language ?? '').toLowerCase().startsWith('zh');
|
|
401
414
|
if (document.getElementById(MODEL_BTN_ID) === null) {
|
|
402
|
-
|
|
415
|
+
// The cell pattern matches the OFFICIAL picker cell text (the official
|
|
416
|
+
// client's own zh/en copy), not plugin-owned text.
|
|
417
|
+
tools.appendChild(makeCompactButton(MODEL_BTN_ID, surfaceText('mobile.composer.pickModel', 'Pick model'), CUBE_ICON, /模型|Model/)); // i18n-allow: matches official picker cell text, not plugin copy
|
|
403
418
|
}
|
|
404
419
|
if (document.getElementById(EFFORT_BTN_ID) === null) {
|
|
405
|
-
tools.appendChild(makeCompactButton(EFFORT_BTN_ID,
|
|
420
|
+
tools.appendChild(makeCompactButton(EFFORT_BTN_ID, surfaceText('mobile.composer.pickEffort', 'Pick reasoning effort'), LEVELS_ICON, /推理等级|Reasoning|Effort/i)); // i18n-allow: matches official picker cell text, not plugin copy
|
|
421
|
+
}
|
|
422
|
+
// Titles re-read every tick so a locale switch (or the translate seat
|
|
423
|
+
// arriving after this layer installed) is picked up without a reload.
|
|
424
|
+
for (const [id, key, fallback] of [
|
|
425
|
+
[MODEL_BTN_ID, 'mobile.composer.pickModel', 'Pick model'],
|
|
426
|
+
[EFFORT_BTN_ID, 'mobile.composer.pickEffort', 'Pick reasoning effort'],
|
|
427
|
+
]) {
|
|
428
|
+
const btn = document.getElementById(id);
|
|
429
|
+
if (btn === null)
|
|
430
|
+
continue;
|
|
431
|
+
const label = surfaceText(key, fallback);
|
|
432
|
+
btn.title = label;
|
|
433
|
+
btn.setAttribute('aria-label', label);
|
|
406
434
|
}
|
|
407
435
|
document.body.classList.add(COMPACT_CLASS);
|
|
408
436
|
}
|
|
@@ -439,9 +467,9 @@ export function startMobileAdapt() {
|
|
|
439
467
|
whale.id = WHALE_ID;
|
|
440
468
|
whale.type = 'button';
|
|
441
469
|
whale.dataset.dshPlugin = 'remote-web-ui';
|
|
442
|
-
//
|
|
443
|
-
//
|
|
444
|
-
const whaleLabel = (
|
|
470
|
+
// The label reads the `remote` namespace (wired by the plugin apply);
|
|
471
|
+
// before wiring it falls back to English and the sync tick re-renders it.
|
|
472
|
+
const whaleLabel = surfaceText('mobile.whale.open', 'Open sidebar');
|
|
445
473
|
whale.title = whaleLabel;
|
|
446
474
|
whale.setAttribute('aria-label', whaleLabel);
|
|
447
475
|
whale.innerHTML = `<svg viewBox="0 0 23.16 17.04" fill="none" aria-hidden="true"><path d="${FISH_PATH}" fill="currentColor"/></svg>`;
|
|
@@ -558,6 +586,12 @@ export function startMobileAdapt() {
|
|
|
558
586
|
applyWhalePos();
|
|
559
587
|
whaleEl.style.display = show ? '' : 'none';
|
|
560
588
|
whaleShown = show;
|
|
589
|
+
// Re-read the label every tick so a locale switch is picked up live.
|
|
590
|
+
const whaleLabel = surfaceText('mobile.whale.open', 'Open sidebar');
|
|
591
|
+
if (whaleEl.title !== whaleLabel)
|
|
592
|
+
whaleEl.title = whaleLabel;
|
|
593
|
+
if (whaleEl.getAttribute('aria-label') !== whaleLabel)
|
|
594
|
+
whaleEl.setAttribute('aria-label', whaleLabel);
|
|
561
595
|
document.body.classList.toggle(RAIL_HIDDEN_CLASS, collapsed);
|
|
562
596
|
disableRowDrag();
|
|
563
597
|
seatHeaderActions();
|
|
@@ -949,6 +983,7 @@ export function startMobileAdapt() {
|
|
|
949
983
|
evaluate,
|
|
950
984
|
toggleSidebar: null,
|
|
951
985
|
closeDetails: null,
|
|
986
|
+
translate: null,
|
|
952
987
|
setEnabled(on) {
|
|
953
988
|
adaptEnabled = on;
|
|
954
989
|
if (on)
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-remote-web-ui",
|
|
3
3
|
"description": "Scan-to-pair remote access for the dsh web GUI that shares one official interface: a QR beside the settings button pairs phones and PCs into the same Web GUI (a portrait-touch adaptation layer for phones, full desktop on PCs) through one-time tokens and revocable device sessions, with a LAN bind toggle, optional Cloudflare tunnel, and one-click family self-update",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.11",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"license": "Apache-2.0",
|
|
37
37
|
"dsh": {
|
|
38
38
|
"engines": {
|
|
39
|
-
"dsh": ">=0.1.2-alpha.
|
|
39
|
+
"dsh": ">=0.1.2-alpha.3"
|
|
40
40
|
},
|
|
41
41
|
"bundle": {
|
|
42
42
|
"patch": "./cordis.patch.yml"
|
|
@@ -63,22 +63,22 @@
|
|
|
63
63
|
},
|
|
64
64
|
"devDependencies": {
|
|
65
65
|
"@deepseek-ai/cordis": "^4.0.2",
|
|
66
|
-
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.
|
|
67
|
-
"@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.
|
|
68
|
-
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.
|
|
69
|
-
"@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.
|
|
70
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.
|
|
71
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.
|
|
72
|
-
"@deepseek-ai/dsh-client-store": "^0.1.2-alpha.
|
|
73
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.
|
|
74
|
-
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.
|
|
75
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.
|
|
76
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.
|
|
77
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.
|
|
78
|
-
"@deepseek-ai/dsh-commands": "^0.1.2-alpha.
|
|
79
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.
|
|
80
|
-
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.
|
|
81
|
-
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.
|
|
66
|
+
"@deepseek-ai/dsh-agent": "^0.1.2-alpha.3",
|
|
67
|
+
"@deepseek-ai/dsh-api-remotes": "^0.1.2-alpha.3",
|
|
68
|
+
"@deepseek-ai/dsh-api-session-controller": "^0.1.2-alpha.3",
|
|
69
|
+
"@deepseek-ai/dsh-api-workspace-controller": "^0.1.2-alpha.3",
|
|
70
|
+
"@deepseek-ai/dsh-client-connection": "^0.1.2-alpha.3",
|
|
71
|
+
"@deepseek-ai/dsh-client-locale": "^0.1.2-alpha.3",
|
|
72
|
+
"@deepseek-ai/dsh-client-store": "^0.1.2-alpha.3",
|
|
73
|
+
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.2-alpha.3",
|
|
74
|
+
"@deepseek-ai/dsh-client-ui-renderer": "^0.1.2-alpha.3",
|
|
75
|
+
"@deepseek-ai/dsh-client-ui-settings": "^0.1.2-alpha.3",
|
|
76
|
+
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.2-alpha.3",
|
|
77
|
+
"@deepseek-ai/dsh-client-ui-slots": "^0.1.2-alpha.3",
|
|
78
|
+
"@deepseek-ai/dsh-commands": "^0.1.2-alpha.3",
|
|
79
|
+
"@deepseek-ai/dsh-host-webserver": "^0.1.2-alpha.3",
|
|
80
|
+
"@deepseek-ai/dsh-invariants": "^0.1.2-alpha.3",
|
|
81
|
+
"@deepseek-ai/dsh-settings": "^0.1.2-alpha.3",
|
|
82
82
|
"@testing-library/dom": "^10.4.1",
|
|
83
83
|
"@testing-library/react": "^16.3.2",
|
|
84
84
|
"@types/node": "^22.20.0",
|
|
@@ -121,7 +121,7 @@ export function UpdateEntry({ wide, t }: UpdateEntryProps) {
|
|
|
121
121
|
<button
|
|
122
122
|
type="button"
|
|
123
123
|
className={css.trigger}
|
|
124
|
-
data-wide={wide ? undefined : "rail"}
|
|
124
|
+
data-wide={wide ? (updateAvailable ? "wide" : undefined) : "rail"}
|
|
125
125
|
data-update-available={updateAvailable ? "true" : undefined}
|
|
126
126
|
aria-label={updateLabel}
|
|
127
127
|
aria-expanded={open}
|
|
@@ -129,6 +129,9 @@ export function UpdateEntry({ wide, t }: UpdateEntryProps) {
|
|
|
129
129
|
onClick={openPanel}
|
|
130
130
|
>
|
|
131
131
|
<IconDownloadOutline16 size={wide ? 16 : 18} />
|
|
132
|
+
{updateAvailable && wide && (
|
|
133
|
+
<span className={css.updateBadgeText}>{t("update.badge")}</span>
|
|
134
|
+
)}
|
|
132
135
|
</button>
|
|
133
136
|
{open && createPortal((
|
|
134
137
|
<div className={css.overlay} role="presentation">
|
package/src/client/index.ts
CHANGED
|
@@ -166,6 +166,11 @@ export function apply(ctx: ClientContext): void {
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
const t = ctx.locale.bind(NS)
|
|
169
|
+
// Hand the `remote` translate seat to the module-scope adaptation layer:
|
|
170
|
+
// the whale/compact-picker labels were rendered with the English fallback
|
|
171
|
+
// before any dictionary existed; the wiring plus the layer's own sync tick
|
|
172
|
+
// re-render them in the active locale.
|
|
173
|
+
if (adapt !== undefined) adapt.translate = t
|
|
169
174
|
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope
|
|
170
175
|
const settingsScope = binder.bind<RemoteSettings>({ namespace: REMOTE_WEB_UI_NS })
|
|
171
176
|
const enabled = (): boolean => {
|
package/src/client/locales.ts
CHANGED
|
@@ -3,6 +3,9 @@
|
|
|
3
3
|
/** Simplified Chinese dictionary (the key-set source of truth). */
|
|
4
4
|
export const zh = {
|
|
5
5
|
'entry.label': '远程访问',
|
|
6
|
+
'mobile.whale.open': '打开侧边栏',
|
|
7
|
+
'mobile.composer.pickModel': '选择模型',
|
|
8
|
+
'mobile.composer.pickEffort': '选择推理等级',
|
|
6
9
|
'title': '远程访问',
|
|
7
10
|
'subtitle': '通过手机或另一台电脑配对,远程使用同一份 Web 界面(官方界面 + 移动端适配)',
|
|
8
11
|
'card.title': '设备配对',
|
|
@@ -112,6 +115,7 @@ export const zh = {
|
|
|
112
115
|
'settings.invalidNumber': '请输入数字,留空则使用默认值。',
|
|
113
116
|
// 更新面板(侧边栏更新按钮)。
|
|
114
117
|
'update.label': '检查更新',
|
|
118
|
+
'update.badge': '有更新',
|
|
115
119
|
'update.availableLabel': '发现新版本,检查更新',
|
|
116
120
|
'update.title': '检查更新',
|
|
117
121
|
'update.checking': '正在检查远程仓库中的新版本…',
|
|
@@ -163,6 +167,9 @@ export type SettingsCardKey = RemoteKey
|
|
|
163
167
|
/** English dictionary, checked complete against the zh key set. */
|
|
164
168
|
export const en = {
|
|
165
169
|
'entry.label': 'Remote access',
|
|
170
|
+
'mobile.whale.open': 'Open sidebar',
|
|
171
|
+
'mobile.composer.pickModel': 'Pick model',
|
|
172
|
+
'mobile.composer.pickEffort': 'Pick reasoning effort',
|
|
166
173
|
'title': 'Remote access',
|
|
167
174
|
'subtitle': 'Pair a phone or another computer to share the same Web GUI (official UI + mobile adaptation)',
|
|
168
175
|
'card.title': 'Pair a device',
|
|
@@ -272,6 +279,7 @@ export const en = {
|
|
|
272
279
|
'settings.invalidNumber': 'Enter a number, or leave blank to use the default.',
|
|
273
280
|
// Update panel (the sidebar update trigger).
|
|
274
281
|
'update.label': 'Check for updates',
|
|
282
|
+
'update.badge': 'Update available',
|
|
275
283
|
'update.availableLabel': 'New version available. Check for updates',
|
|
276
284
|
'update.title': 'Check for updates',
|
|
277
285
|
'update.checking': 'Checking the remote repository for newer releases…',
|
|
@@ -21,6 +21,8 @@
|
|
|
21
21
|
* @module @linxin666/dsh-remote-web-ui/client/mobile-adapt
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
+
import type { RemoteKey } from './locales.ts'
|
|
25
|
+
|
|
24
26
|
/** The window global the plugin apply() wires the layout service into. */
|
|
25
27
|
export interface RemoteAdaptGlobal {
|
|
26
28
|
evaluate: () => void
|
|
@@ -28,6 +30,14 @@ export interface RemoteAdaptGlobal {
|
|
|
28
30
|
toggleSidebar: (() => void) | null
|
|
29
31
|
/** Wired by the plugin apply once ctx.layout is live. */
|
|
30
32
|
closeDetails: (() => void) | null
|
|
33
|
+
/**
|
|
34
|
+
* Wired by the plugin apply once the `remote` locale namespace is bound:
|
|
35
|
+
* the translate seat the injected surfaces (whale, compact picker) read
|
|
36
|
+
* their labels from. Null until then; label reads fall back to English
|
|
37
|
+
* (the SDK's universal fallback) and the 600ms sync tick re-renders the
|
|
38
|
+
* labels once the seat is wired.
|
|
39
|
+
*/
|
|
40
|
+
translate: ((key: RemoteKey) => string) | null
|
|
31
41
|
/** Plugin master switch: false reverts the layer, true re-evaluates. */
|
|
32
42
|
setEnabled(on: boolean): void
|
|
33
43
|
/** Replays a pending closeDetails once the layout face is wired (the first apply ran before the wiring). */
|
|
@@ -266,6 +276,19 @@ export function startMobileAdapt(): void {
|
|
|
266
276
|
;(window as unknown as { __dshRemoteAdaptInstalled?: boolean }).__dshRemoteAdaptInstalled = true
|
|
267
277
|
|
|
268
278
|
const w = window as unknown as { __dshRemoteAdapt?: RemoteAdaptGlobal } & Record<string, unknown>
|
|
279
|
+
/**
|
|
280
|
+
* Label lookup for the injected surfaces: the `remote` namespace seat wired
|
|
281
|
+
* by the plugin apply, else the English fallback (the SDK's universal
|
|
282
|
+
* fallback; the sync tick re-renders the labels once the seat is live).
|
|
283
|
+
*/
|
|
284
|
+
const surfaceText = (key: RemoteKey, fallback: string): string => {
|
|
285
|
+
try {
|
|
286
|
+
const out = w.__dshRemoteAdapt?.translate?.(key)
|
|
287
|
+
return typeof out === 'string' && out.length > 0 ? out : fallback
|
|
288
|
+
} catch {
|
|
289
|
+
return fallback
|
|
290
|
+
}
|
|
291
|
+
}
|
|
269
292
|
let active = false
|
|
270
293
|
let savedViewportContent: string | null = null
|
|
271
294
|
let whaleEl: HTMLButtonElement | null = null
|
|
@@ -409,12 +432,25 @@ export function startMobileAdapt(): void {
|
|
|
409
432
|
removeCompactPicker()
|
|
410
433
|
return
|
|
411
434
|
}
|
|
412
|
-
const zh = (navigator.language ?? '').toLowerCase().startsWith('zh')
|
|
413
435
|
if (document.getElementById(MODEL_BTN_ID) === null) {
|
|
414
|
-
|
|
436
|
+
// The cell pattern matches the OFFICIAL picker cell text (the official
|
|
437
|
+
// client's own zh/en copy), not plugin-owned text.
|
|
438
|
+
tools.appendChild(makeCompactButton(MODEL_BTN_ID, surfaceText('mobile.composer.pickModel', 'Pick model'), CUBE_ICON, /模型|Model/)) // i18n-allow: matches official picker cell text, not plugin copy
|
|
415
439
|
}
|
|
416
440
|
if (document.getElementById(EFFORT_BTN_ID) === null) {
|
|
417
|
-
tools.appendChild(makeCompactButton(EFFORT_BTN_ID,
|
|
441
|
+
tools.appendChild(makeCompactButton(EFFORT_BTN_ID, surfaceText('mobile.composer.pickEffort', 'Pick reasoning effort'), LEVELS_ICON, /推理等级|Reasoning|Effort/i)) // i18n-allow: matches official picker cell text, not plugin copy
|
|
442
|
+
}
|
|
443
|
+
// Titles re-read every tick so a locale switch (or the translate seat
|
|
444
|
+
// arriving after this layer installed) is picked up without a reload.
|
|
445
|
+
for (const [id, key, fallback] of [
|
|
446
|
+
[MODEL_BTN_ID, 'mobile.composer.pickModel', 'Pick model'],
|
|
447
|
+
[EFFORT_BTN_ID, 'mobile.composer.pickEffort', 'Pick reasoning effort'],
|
|
448
|
+
] as const) {
|
|
449
|
+
const btn = document.getElementById(id)
|
|
450
|
+
if (btn === null) continue
|
|
451
|
+
const label = surfaceText(key, fallback)
|
|
452
|
+
btn.title = label
|
|
453
|
+
btn.setAttribute('aria-label', label)
|
|
418
454
|
}
|
|
419
455
|
document.body.classList.add(COMPACT_CLASS)
|
|
420
456
|
}
|
|
@@ -446,9 +482,9 @@ export function startMobileAdapt(): void {
|
|
|
446
482
|
whale.id = WHALE_ID
|
|
447
483
|
whale.type = 'button'
|
|
448
484
|
whale.dataset.dshPlugin = 'remote-web-ui'
|
|
449
|
-
//
|
|
450
|
-
//
|
|
451
|
-
const whaleLabel = (
|
|
485
|
+
// The label reads the `remote` namespace (wired by the plugin apply);
|
|
486
|
+
// before wiring it falls back to English and the sync tick re-renders it.
|
|
487
|
+
const whaleLabel = surfaceText('mobile.whale.open', 'Open sidebar')
|
|
452
488
|
whale.title = whaleLabel
|
|
453
489
|
whale.setAttribute('aria-label', whaleLabel)
|
|
454
490
|
whale.innerHTML = `<svg viewBox="0 0 23.16 17.04" fill="none" aria-hidden="true"><path d="${FISH_PATH}" fill="currentColor"/></svg>`
|
|
@@ -553,6 +589,10 @@ export function startMobileAdapt(): void {
|
|
|
553
589
|
if (show && !whaleShown) applyWhalePos()
|
|
554
590
|
whaleEl.style.display = show ? '' : 'none'
|
|
555
591
|
whaleShown = show
|
|
592
|
+
// Re-read the label every tick so a locale switch is picked up live.
|
|
593
|
+
const whaleLabel = surfaceText('mobile.whale.open', 'Open sidebar')
|
|
594
|
+
if (whaleEl.title !== whaleLabel) whaleEl.title = whaleLabel
|
|
595
|
+
if (whaleEl.getAttribute('aria-label') !== whaleLabel) whaleEl.setAttribute('aria-label', whaleLabel)
|
|
556
596
|
document.body.classList.toggle(RAIL_HIDDEN_CLASS, collapsed)
|
|
557
597
|
disableRowDrag()
|
|
558
598
|
seatHeaderActions()
|
|
@@ -898,6 +938,7 @@ export function startMobileAdapt(): void {
|
|
|
898
938
|
evaluate,
|
|
899
939
|
toggleSidebar: null,
|
|
900
940
|
closeDetails: null,
|
|
941
|
+
translate: null,
|
|
901
942
|
setEnabled(on: boolean): void {
|
|
902
943
|
adaptEnabled = on
|
|
903
944
|
if (on) evaluate()
|
|
@@ -40,6 +40,10 @@
|
|
|
40
40
|
transition: background-color 120ms ease, color 120ms ease, box-shadow 120ms ease;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
.trigger[data-update-available] {
|
|
44
|
+
color: var(--dsw-alias-brand-primary);
|
|
45
|
+
}
|
|
46
|
+
|
|
43
47
|
.trigger[data-update-available]::after {
|
|
44
48
|
position: absolute;
|
|
45
49
|
top: 4px;
|
|
@@ -66,6 +70,22 @@
|
|
|
66
70
|
padding: 0 10px;
|
|
67
71
|
}
|
|
68
72
|
|
|
73
|
+
.trigger[data-update-available][data-wide='wide'] {
|
|
74
|
+
flex: none;
|
|
75
|
+
gap: 6px;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.trigger[data-update-available][data-wide='wide']::after {
|
|
79
|
+
display: none;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
.updateBadgeText {
|
|
83
|
+
font-size: 13px;
|
|
84
|
+
font-weight: 500;
|
|
85
|
+
line-height: 1;
|
|
86
|
+
white-space: nowrap;
|
|
87
|
+
}
|
|
88
|
+
|
|
69
89
|
.trigger:hover:not(:disabled) {
|
|
70
90
|
background: var(--dsw-alias-interactive-bg-hover);
|
|
71
91
|
color: var(--dsw-alias-label-primary);
|