@linxin666/dsh-remote-web-ui 0.1.9 → 0.1.10
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 +39 -3
- package/lib/client.js.map +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +10 -1
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +2 -0
- package/lib/types/client/settings-form.d.ts +9 -1
- package/lib/types/client/settings-form.d.ts.map +1 -1
- package/lib/types/client/settings-form.js +2 -1
- package/package.json +1 -1
- package/src/client/PluginSettingsCard.tsx +31 -1
- package/src/client/locales.ts +2 -0
- package/src/client/settings-card.module.css +7 -0
- package/src/client/settings-form.ts +11 -2
|
@@ -29,7 +29,7 @@ export interface PluginSettingsCardProps {
|
|
|
29
29
|
/**
|
|
30
30
|
* Render one plugin settings card.
|
|
31
31
|
* @param props - the plugin's copy keys, its form state, and its controls.
|
|
32
|
-
* @returns the card, or nothing
|
|
32
|
+
* @returns the card, or nothing while the namespace is still loading.
|
|
33
33
|
*/
|
|
34
34
|
export declare function PluginSettingsCard(props: PluginSettingsCardProps): import("react").JSX.Element | null;
|
|
35
35
|
/** Props every field control needs regardless of its value type. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGnD,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAA;IACnC,uCAAuC;IACvC,QAAQ,EAAE,eAAe,CAAA;IACzB,4EAA4E;IAC5E,cAAc,EAAE,eAAe,CAAA;IAC/B,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,
|
|
1
|
+
{"version":3,"file":"PluginSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/PluginSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AACnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,cAAc,CAAA;AAGnD,wDAAwD;AACxD,MAAM,WAAW,uBAAuB;IACtC,0CAA0C;IAC1C,CAAC,EAAE,CAAC,GAAG,EAAE,eAAe,KAAK,MAAM,CAAA;IACnC,uCAAuC;IACvC,QAAQ,EAAE,eAAe,CAAA;IACzB,4EAA4E;IAC5E,cAAc,EAAE,eAAe,CAAA;IAC/B,kFAAkF;IAClF,KAAK,EAAE,SAAS,CAAA;IAChB,+BAA+B;IAC/B,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,8BAA8B;IAC9B,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,6BAA6B;IAC7B,QAAQ,EAAE,SAAS,CAAA;CACpB;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,sCAiFhE;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,wDAAwD;IACxD,EAAE,EAAE,MAAM,CAAA;IACV,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAA;IACb,uDAAuD;IACvD,IAAI,EAAE,MAAM,CAAA;IACZ,uCAAuC;IACvC,IAAI,EAAE,MAAM,CAAA;IACZ,sEAAsE;IACtE,UAAU,EAAE,OAAO,CAAA;IACnB,6DAA6D;IAC7D,OAAO,EAAE,OAAO,CAAA;IAChB,qCAAqC;IACrC,eAAe,EAAE,MAAM,CAAA;IACvB,kCAAkC;IAClC,UAAU,EAAE,MAAM,CAAA;IAClB,kEAAkE;IAClE,YAAY,EAAE,MAAM,CAAA;IACpB,gFAAgF;IAChF,QAAQ,EAAE,OAAO,CAAA;IACjB,wBAAwB;IACxB,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IAC9B,oEAAoE;IACpE,OAAO,EAAE,MAAM,IAAI,CAAA;CACpB;AAED,kHAAkH;AAClH,wBAAgB,UAAU,CAAC,KAAK,EAAE,UAAU,GAAG;IAC7C,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,kDAAkD;IAClD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB,+BAqCA;AAED,0CAA0C;AAC1C,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG;IAC/C,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAA;IACpB,8BAA8B;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,+BAA+B;IAC/B,QAAQ,EAAE,MAAM,CAAA;CACjB,+BAmCA"}
|
|
@@ -12,7 +12,7 @@ import css from './settings-card.module.css';
|
|
|
12
12
|
/**
|
|
13
13
|
* Render one plugin settings card.
|
|
14
14
|
* @param props - the plugin's copy keys, its form state, and its controls.
|
|
15
|
-
* @returns the card, or nothing
|
|
15
|
+
* @returns the card, or nothing while the namespace is still loading.
|
|
16
16
|
*/
|
|
17
17
|
export function PluginSettingsCard(props) {
|
|
18
18
|
const [open, setOpen] = useState(false);
|
|
@@ -21,6 +21,15 @@ export function PluginSettingsCard(props) {
|
|
|
21
21
|
return null;
|
|
22
22
|
const title = props.t(props.titleKey);
|
|
23
23
|
const blocked = !state.dirty || state.invalid || state.saving;
|
|
24
|
+
// The namespace exists but the Host does not serve it to this client (the
|
|
25
|
+
// official settings allowlist omits third-party namespaces): show a card
|
|
26
|
+
// that explains the gap instead of vanishing, so a missing card never
|
|
27
|
+
// reads as a missing plugin.
|
|
28
|
+
if (!state.exposed) {
|
|
29
|
+
return (_jsxs("li", { className: css.card, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: props.t(props.descriptionKey) })] }), _jsx("span", { className: open ? css.chevronOpen : css.chevron, children: "\u25BE" })] }), open
|
|
30
|
+
? (_jsx("div", { className: css.body, children: _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) }))
|
|
31
|
+
: null] }));
|
|
32
|
+
}
|
|
24
33
|
return (_jsxs("li", { className: css.card, children: [_jsxs("button", { type: "button", className: css.header, "aria-expanded": open, "aria-label": `${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`, onClick: () => { setOpen(!open); }, children: [_jsxs("span", { className: css.headText, children: [_jsx("span", { className: css.name, children: title }), _jsx("span", { className: css.description, children: props.t(props.descriptionKey) })] }), state.dirty ? _jsx("span", { className: css.pending, children: props.t('settings.unsaved') }) : null, _jsx("span", { className: open ? css.chevronOpen : css.chevron, children: "\u25BE" })] }), open
|
|
25
34
|
? (_jsxs("div", { className: css.body, children: [!state.writable ? _jsx("p", { className: css.readOnly, role: "status", children: props.t('settings.readOnly') }) : null, props.children, _jsxs("div", { className: css.footer, children: [state.failed ? _jsx("p", { className: css.failed, role: "status", children: props.t('settings.saveFailed') }) : null, _jsx("button", { type: "button", className: css.discard, disabled: !state.dirty || state.saving, onClick: props.onDiscard, children: props.t('settings.discard') }), _jsx("button", { type: "button", className: css.save, disabled: blocked, onClick: props.onSave, children: props.t(!state.saving ? 'settings.save' : 'settings.saving') })] })] }))
|
|
26
35
|
: null] }));
|
|
@@ -58,6 +58,7 @@ export declare const zh: {
|
|
|
58
58
|
'settings.off': string;
|
|
59
59
|
'settings.overridden': string;
|
|
60
60
|
'settings.reset': string;
|
|
61
|
+
'settings.notExposed': string;
|
|
61
62
|
'settings.readOnly': string;
|
|
62
63
|
'settings.expand': string;
|
|
63
64
|
'settings.collapse': string;
|
|
@@ -131,6 +132,7 @@ export declare const en: {
|
|
|
131
132
|
'settings.off': string;
|
|
132
133
|
'settings.overridden': string;
|
|
133
134
|
'settings.reset': string;
|
|
135
|
+
'settings.notExposed': string;
|
|
134
136
|
'settings.readOnly': string;
|
|
135
137
|
'settings.expand': string;
|
|
136
138
|
'settings.collapse': string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,mEAAmE;AACnE,eAAO,MAAM,EAAE
|
|
1
|
+
{"version":3,"file":"locales.d.ts","sourceRoot":"","sources":["../../../src/client/locales.ts"],"names":[],"mappings":"AAAA,+EAA+E;AAE/E,mEAAmE;AACnE,eAAO,MAAM,EAAE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqEmB,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqEsB,CAAA"}
|
|
@@ -59,6 +59,7 @@ export const zh = {
|
|
|
59
59
|
'settings.off': '关',
|
|
60
60
|
'settings.overridden': '已覆盖',
|
|
61
61
|
'settings.reset': '恢复默认',
|
|
62
|
+
'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。',
|
|
62
63
|
'settings.readOnly': '当前部署的设置只读。',
|
|
63
64
|
'settings.expand': '展开设置',
|
|
64
65
|
'settings.collapse': '收起设置',
|
|
@@ -129,6 +130,7 @@ export const en = {
|
|
|
129
130
|
'settings.off': 'Off',
|
|
130
131
|
'settings.overridden': 'Overridden',
|
|
131
132
|
'settings.reset': 'Reset to default',
|
|
133
|
+
'settings.notExposed': 'This DSH version does not expose this plugin\'s settings namespace to the configuration page, so the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to dsh-host-apiproxy\'s WEB_SETTINGS_NAMESPACES allowlist and restart.',
|
|
132
134
|
'settings.readOnly': 'This deployment stores settings read-only.',
|
|
133
135
|
'settings.expand': 'Show settings',
|
|
134
136
|
'settings.collapse': 'Hide settings',
|
|
@@ -37,8 +37,16 @@ export interface FieldState {
|
|
|
37
37
|
}
|
|
38
38
|
/** Form state every plugin settings card shares. */
|
|
39
39
|
export interface CardShell {
|
|
40
|
-
/** False while the namespace is
|
|
40
|
+
/** False while the namespace is still loading; the card renders nothing. */
|
|
41
41
|
available: boolean;
|
|
42
|
+
/**
|
|
43
|
+
* Whether the namespace is actually served to this client. False when the
|
|
44
|
+
* Host deployment does not expose it (e.g. the official apiproxy settings
|
|
45
|
+
* allowlist omits third-party namespaces): the card renders an explanation
|
|
46
|
+
* instead of its form, so a missing namespace never looks like a missing
|
|
47
|
+
* plugin.
|
|
48
|
+
*/
|
|
49
|
+
exposed: boolean;
|
|
42
50
|
/** Whether the Host document accepts writes. */
|
|
43
51
|
writable: boolean;
|
|
44
52
|
/** Whether the form holds edits that a save would write. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAyB,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAGjH,yEAAyE;AACzE,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAErB,0EAA0E;AAC1E,MAAM,WAAW,SAAS;IACxB,+CAA+C;IAC/C,KAAK,EAAE,MAAM,CAAA;IACb,2FAA2F;IAC3F,MAAM,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,MAAM,CAAA;IAClC;;;OAGG;IACH,KAAK,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,UAAU,GAAG,SAAS,CAAA;CAChD;AAED,wCAAwC;AACxC,MAAM,WAAW,UAAU;IACzB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,oEAAoE;IACpE,UAAU,EAAE,OAAO,CAAA;IACnB,gFAAgF;IAChF,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,oDAAoD;AACpD,MAAM,WAAW,SAAS;IACxB,
|
|
1
|
+
{"version":3,"file":"settings-form.d.ts","sourceRoot":"","sources":["../../../src/client/settings-form.ts"],"names":[],"mappings":"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"}
|
|
@@ -78,7 +78,8 @@ export class CardForm {
|
|
|
78
78
|
const snapshot = this.scope.getSnapshot();
|
|
79
79
|
const plan = this.plan();
|
|
80
80
|
return {
|
|
81
|
-
available: snapshot.status
|
|
81
|
+
available: snapshot.status !== 'loading',
|
|
82
|
+
exposed: snapshot.status === 'ready',
|
|
82
83
|
writable: snapshot.writable,
|
|
83
84
|
dirty: plan.length > 0,
|
|
84
85
|
invalid: plan.some(item => item.run === undefined),
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@linxin666/dsh-remote-web-ui",
|
|
3
3
|
"description": "Mobile remote control for the dsh web GUI: scan-to-pair QR entry beside the settings button, one-time pairing tokens, live device status, and revocable mobile sessions",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.10",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -33,7 +33,7 @@ export interface PluginSettingsCardProps {
|
|
|
33
33
|
/**
|
|
34
34
|
* Render one plugin settings card.
|
|
35
35
|
* @param props - the plugin's copy keys, its form state, and its controls.
|
|
36
|
-
* @returns the card, or nothing
|
|
36
|
+
* @returns the card, or nothing while the namespace is still loading.
|
|
37
37
|
*/
|
|
38
38
|
export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
39
39
|
const [open, setOpen] = useState(false)
|
|
@@ -41,6 +41,36 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
41
41
|
if (!state.available) return null
|
|
42
42
|
const title = props.t(props.titleKey)
|
|
43
43
|
const blocked = !state.dirty || state.invalid || state.saving
|
|
44
|
+
// The namespace exists but the Host does not serve it to this client (the
|
|
45
|
+
// official settings allowlist omits third-party namespaces): show a card
|
|
46
|
+
// that explains the gap instead of vanishing, so a missing card never
|
|
47
|
+
// reads as a missing plugin.
|
|
48
|
+
if (!state.exposed) {
|
|
49
|
+
return (
|
|
50
|
+
<li className={css.card}>
|
|
51
|
+
<button
|
|
52
|
+
type="button"
|
|
53
|
+
className={css.header}
|
|
54
|
+
aria-expanded={open}
|
|
55
|
+
aria-label={`${props.t(open ? 'settings.collapse' : 'settings.expand')}: ${title}`}
|
|
56
|
+
onClick={() => { setOpen(!open) }}
|
|
57
|
+
>
|
|
58
|
+
<span className={css.headText}>
|
|
59
|
+
<span className={css.name}>{title}</span>
|
|
60
|
+
<span className={css.description}>{props.t(props.descriptionKey)}</span>
|
|
61
|
+
</span>
|
|
62
|
+
<span className={open ? css.chevronOpen : css.chevron}>▾</span>
|
|
63
|
+
</button>
|
|
64
|
+
{open
|
|
65
|
+
? (
|
|
66
|
+
<div className={css.body}>
|
|
67
|
+
<p className={css.notExposed} role="status">{props.t('settings.notExposed')}</p>
|
|
68
|
+
</div>
|
|
69
|
+
)
|
|
70
|
+
: null}
|
|
71
|
+
</li>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
44
74
|
return (
|
|
45
75
|
<li className={css.card}>
|
|
46
76
|
<button
|
package/src/client/locales.ts
CHANGED
|
@@ -60,6 +60,7 @@ export const zh = {
|
|
|
60
60
|
'settings.off': '关',
|
|
61
61
|
'settings.overridden': '已覆盖',
|
|
62
62
|
'settings.reset': '恢复默认',
|
|
63
|
+
'settings.notExposed': '当前 DSH 版本未向设置页暴露本插件的配置命名空间,表单不可用。可编辑 ~/.dsh/settings.yaml 直接配置,或为 dsh-host-apiproxy 的 WEB_SETTINGS_NAMESPACES 白名单补充本命名空间后重启。',
|
|
63
64
|
'settings.readOnly': '当前部署的设置只读。',
|
|
64
65
|
'settings.expand': '展开设置',
|
|
65
66
|
'settings.collapse': '收起设置',
|
|
@@ -137,6 +138,7 @@ export const en = {
|
|
|
137
138
|
'settings.off': 'Off',
|
|
138
139
|
'settings.overridden': 'Overridden',
|
|
139
140
|
'settings.reset': 'Reset to default',
|
|
141
|
+
'settings.notExposed': 'This DSH version does not expose this plugin\'s settings namespace to the configuration page, so the form is unavailable. Edit ~/.dsh/settings.yaml directly, or add the namespace to dsh-host-apiproxy\'s WEB_SETTINGS_NAMESPACES allowlist and restart.',
|
|
140
142
|
'settings.readOnly': 'This deployment stores settings read-only.',
|
|
141
143
|
'settings.expand': 'Show settings',
|
|
142
144
|
'settings.collapse': 'Hide settings',
|
|
@@ -40,8 +40,16 @@ export interface FieldState {
|
|
|
40
40
|
|
|
41
41
|
/** Form state every plugin settings card shares. */
|
|
42
42
|
export interface CardShell {
|
|
43
|
-
/** False while the namespace is
|
|
43
|
+
/** False while the namespace is still loading; the card renders nothing. */
|
|
44
44
|
available: boolean
|
|
45
|
+
/**
|
|
46
|
+
* Whether the namespace is actually served to this client. False when the
|
|
47
|
+
* Host deployment does not expose it (e.g. the official apiproxy settings
|
|
48
|
+
* allowlist omits third-party namespaces): the card renders an explanation
|
|
49
|
+
* instead of its form, so a missing namespace never looks like a missing
|
|
50
|
+
* plugin.
|
|
51
|
+
*/
|
|
52
|
+
exposed: boolean
|
|
45
53
|
/** Whether the Host document accepts writes. */
|
|
46
54
|
writable: boolean
|
|
47
55
|
/** Whether the form holds edits that a save would write. */
|
|
@@ -157,7 +165,8 @@ export class CardForm<T> {
|
|
|
157
165
|
const snapshot = this.scope.getSnapshot()
|
|
158
166
|
const plan = this.plan()
|
|
159
167
|
return {
|
|
160
|
-
available: snapshot.status
|
|
168
|
+
available: snapshot.status !== 'loading',
|
|
169
|
+
exposed: snapshot.status === 'ready',
|
|
161
170
|
writable: snapshot.writable,
|
|
162
171
|
dirty: plan.length > 0,
|
|
163
172
|
invalid: plan.some(item => item.run === undefined),
|