@linxin666/dsh-remote-web-ui 0.1.12 → 0.1.13
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.i18n.yaml +6 -0
- package/README.md +12 -1
- package/README.zh.md +171 -0
- package/lib/client.js +169 -98
- package/lib/client.js.map +1 -1
- package/lib/index.js +224 -4
- package/lib/mobile.js +109 -33
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +3 -2
- package/lib/types/client/RemoteSettingsCard.d.ts +4 -0
- package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
- package/lib/types/client/RemoteSettingsCard.js +3 -1
- package/lib/types/client/UpdateEntry.d.ts.map +1 -1
- package/lib/types/client/UpdateEntry.js +10 -2
- package/lib/types/client/deep-link.js +10 -5
- 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/update-api.d.ts +10 -0
- package/lib/types/client/update-api.d.ts.map +1 -1
- package/lib/types/client/update-api.js +22 -2
- package/lib/types/index.d.ts +7 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +7 -1
- package/lib/types/mobile/api.d.ts +9 -2
- package/lib/types/mobile/api.d.ts.map +1 -1
- package/lib/types/mobile/api.js +7 -3
- package/lib/types/mobile/mobile-styles.d.ts +1 -1
- package/lib/types/mobile/mobile-styles.d.ts.map +1 -1
- package/lib/types/mobile/mobile-styles.js +3 -0
- package/lib/types/mobile/views/App.d.ts +3 -1
- package/lib/types/mobile/views/App.d.ts.map +1 -1
- package/lib/types/mobile/views/App.js +18 -3
- package/lib/types/mobile/views/ChatView.d.ts +6 -0
- package/lib/types/mobile/views/ChatView.d.ts.map +1 -1
- package/lib/types/mobile/views/ChatView.js +95 -7
- package/lib/types/mobile/views/ChatView.test.js +161 -3
- package/lib/types/mobile/views/WorkspaceView.d.ts +3 -1
- package/lib/types/mobile/views/WorkspaceView.d.ts.map +1 -1
- package/lib/types/mobile/views/WorkspaceView.js +9 -2
- package/lib/types/mobile/views/WorkspaceView.test.d.ts +2 -0
- package/lib/types/mobile/views/WorkspaceView.test.d.ts.map +1 -0
- package/lib/types/mobile/views/WorkspaceView.test.js +56 -0
- package/lib/types/mobile-api.d.ts +2 -0
- package/lib/types/mobile-api.d.ts.map +1 -1
- package/lib/types/mobile-api.js +17 -2
- package/package.json +1 -11
- package/src/client/PluginSettingsCard.tsx +30 -5
- package/src/client/RemoteSettingsCard.tsx +18 -0
- package/src/client/UpdateEntry.tsx +10 -2
- package/src/client/deep-link.ts +10 -4
- package/src/client/locales.ts +8 -0
- package/src/client/settings-card.module.css +139 -144
- package/src/client/update-api.ts +22 -2
- package/src/index.ts +14 -1
- package/src/mobile/api.ts +14 -2
- package/src/mobile/mobile-styles.ts +3 -0
- package/src/mobile/views/App.tsx +22 -3
- package/src/mobile/views/ChatView.test.tsx +185 -3
- package/src/mobile/views/ChatView.tsx +102 -8
- package/src/mobile/views/WorkspaceView.test.tsx +66 -0
- package/src/mobile/views/WorkspaceView.tsx +11 -2
- package/src/mobile-api.ts +20 -2
|
@@ -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,sCA0GhE;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"}
|
|
@@ -21,16 +21,17 @@ 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
|
+
const cardClass = open ? `${css.cardOpen} ${css.card}` : css.card;
|
|
24
25
|
// The namespace exists but the Host does not serve it to this client (the
|
|
25
26
|
// official settings allowlist omits third-party namespaces): show a card
|
|
26
27
|
// that explains the gap instead of vanishing, so a missing card never
|
|
27
28
|
// reads as a missing plugin.
|
|
28
29
|
if (!state.exposed) {
|
|
29
|
-
return (_jsxs("li", { className:
|
|
30
|
+
return (_jsxs("li", { className: cardClass, 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("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }), open
|
|
30
31
|
? (_jsx("div", { className: css.body, children: _jsx("p", { className: css.notExposed, role: "status", children: props.t('settings.notExposed') }) }))
|
|
31
32
|
: null] }));
|
|
32
33
|
}
|
|
33
|
-
return (_jsxs("li", { className:
|
|
34
|
+
return (_jsxs("li", { className: cardClass, 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("svg", { width: "14", height: "14", viewBox: "0 0 14 14", fill: "none", xmlns: "http://www.w3.org/2000/svg", className: open ? `${css.chevron} ${css.chevronOpen}` : css.chevron, children: _jsx("path", { d: "M11.8486 5.5L11.4238 5.92383L8.69727 8.65137C8.44157 8.90706 8.21562 9.13382 8.01172 9.29785C7.79912 9.46883 7.55595 9.61756 7.25 9.66602C7.08435 9.69222 6.91565 9.69222 6.75 9.66602C6.44405 9.61756 6.20088 9.46883 5.98828 9.29785C5.78438 9.13382 5.55843 8.90706 5.30273 8.65137L2.57617 5.92383L2.15137 5.5L3 4.65137L3.42383 5.07617L6.15137 7.80273C6.42595 8.07732 6.59876 8.24849 6.74023 8.3623C6.87291 8.46904 6.92272 8.47813 6.9375 8.48047C6.97895 8.48703 7.02105 8.48703 7.0625 8.48047C7.07728 8.47813 7.12709 8.46904 7.25977 8.3623C7.40124 8.24849 7.57405 8.07732 7.84863 7.80273L10.5762 5.07617L11 4.65137L11.8486 5.5Z", fill: "currentColor" }) })] }), open
|
|
34
35
|
? (_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') })] })] }))
|
|
35
36
|
: null] }));
|
|
36
37
|
}
|
|
@@ -24,6 +24,8 @@ export interface RemoteSettings {
|
|
|
24
24
|
publicBaseUrl?: string;
|
|
25
25
|
/** When on, the plugin runs its own Cloudflare quick tunnel automatically. */
|
|
26
26
|
autoTunnel?: boolean;
|
|
27
|
+
/** Mobile composer: plain Enter sends; off means Enter inserts a newline. */
|
|
28
|
+
mobileEnterToSend?: boolean;
|
|
27
29
|
}
|
|
28
30
|
/** What the remote-control card renders. */
|
|
29
31
|
export interface RemoteSettingsCardState extends CardShell {
|
|
@@ -43,6 +45,8 @@ export interface RemoteSettingsCardState extends CardShell {
|
|
|
43
45
|
publicBaseUrl: CardFieldState;
|
|
44
46
|
/** Auto public tunnel switch. */
|
|
45
47
|
autoTunnel: CardFieldState;
|
|
48
|
+
/** Mobile composer Enter-to-send switch. */
|
|
49
|
+
mobileEnterToSend: CardFieldState;
|
|
46
50
|
}
|
|
47
51
|
/** The registration-side face the card's slot entry injects. */
|
|
48
52
|
export interface RemoteSettingsCardFace extends CardActions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RemoteSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/RemoteSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAE1F,OAAO,EAAkD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExJ,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6FAA6F;IAC7F,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"RemoteSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/RemoteSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAE1F,OAAO,EAAkD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAExJ,+EAA+E;AAC/E,MAAM,WAAW,cAAc;IAC7B,oCAAoC;IACpC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,6FAA6F;IAC7F,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B,qEAAqE;IACrE,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,6EAA6E;IAC7E,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B;AAED,4CAA4C;AAC5C,MAAM,WAAW,uBAAwB,SAAQ,SAAS;IACxD,qBAAqB;IACrB,OAAO,EAAE,cAAc,CAAA;IACvB,sBAAsB;IACtB,UAAU,EAAE,cAAc,CAAA;IAC1B,gCAAgC;IAChC,cAAc,EAAE,cAAc,CAAA;IAC9B,yBAAyB;IACzB,UAAU,EAAE,cAAc,CAAA;IAC1B,0BAA0B;IAC1B,UAAU,EAAE,cAAc,CAAA;IAC1B,sBAAsB;IACtB,oBAAoB,EAAE,cAAc,CAAA;IACpC,kCAAkC;IAClC,aAAa,EAAE,cAAc,CAAA;IAC7B,iCAAiC;IACjC,UAAU,EAAE,cAAc,CAAA;IAC1B,4CAA4C;IAC5C,iBAAiB,EAAE,cAAc,CAAA;CAClC;AAED,gEAAgE;AAChE,MAAM,WAAW,sBAAuB,SAAQ,WAAW;IACzD,KAAK,EAAE;QACL,oEAAoE;QACpE,kBAAkB,EAAE,aAAa,CAAC,uBAAuB,CAAC,CAAA;KAC3D,CAAA;CACF;AAED,qEAAqE;AACrE,qBAAa,4BAA4B;IACvC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAA0B;IAC/C,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAwC;IAE9D,iFAAiF;gBACrE,KAAK,EAAE,aAAa,CAAC,cAAc,CAAC;IAehD,OAAO,CAAC,UAAU;IAelB;;;OAGG;IACH,MAAM,IAAI,sBAAsB;CAGjC;AAED,4DAA4D;AAC5D,MAAM,MAAM,uBAAuB,GACjC,YAAY,CAAC,oBAAoB,CAAC,GAChC,WAAW,CAAC,QAAQ,CAAC,GACrB,UAAU,CAAC,sBAAsB,CAAC,CAAA;AAEtC;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,+BAsHhE"}
|
|
@@ -16,6 +16,7 @@ export class RemoteSettingsCardController {
|
|
|
16
16
|
booleanField('requirePairingForLan'),
|
|
17
17
|
textField('publicBaseUrl'),
|
|
18
18
|
booleanField('autoTunnel'),
|
|
19
|
+
booleanField('mobileEnterToSend'),
|
|
19
20
|
]);
|
|
20
21
|
this.store = this.form.bind(() => this.projection());
|
|
21
22
|
}
|
|
@@ -30,6 +31,7 @@ export class RemoteSettingsCardController {
|
|
|
30
31
|
requirePairingForLan: this.form.field('requirePairingForLan'),
|
|
31
32
|
publicBaseUrl: this.form.field('publicBaseUrl'),
|
|
32
33
|
autoTunnel: this.form.field('autoTunnel'),
|
|
34
|
+
mobileEnterToSend: this.form.field('mobileEnterToSend'),
|
|
33
35
|
};
|
|
34
36
|
}
|
|
35
37
|
/**
|
|
@@ -55,5 +57,5 @@ export function RemoteSettingsCard(props) {
|
|
|
55
57
|
invalidLabel: t('settings.invalidNumber'),
|
|
56
58
|
disabled,
|
|
57
59
|
};
|
|
58
|
-
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, children: [_jsx(BooleanField, { id: "settings-remote-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ValueField, { id: "settings-remote-token-ttl", label: t('settings.tokenTtlMs'), hint: t('settings.tokenTtlMsHint'), numeric: true, ...fieldProps, ...state.tokenTtlMs, onEdit: (text) => { props.edit('tokenTtlMs', text); }, onReset: () => { props.resetField('tokenTtlMs'); } }), _jsx(ValueField, { id: "settings-remote-offline", label: t('settings.offlineAfterMs'), hint: t('settings.offlineAfterMsHint'), numeric: true, ...fieldProps, ...state.offlineAfterMs, onEdit: (text) => { props.edit('offlineAfterMs', text); }, onReset: () => { props.resetField('offlineAfterMs'); } }), _jsx(ValueField, { id: "settings-remote-max-devices", label: t('settings.maxDevices'), hint: t('settings.maxDevicesHint'), numeric: true, ...fieldProps, ...state.maxDevices, onEdit: (text) => { props.edit('maxDevices', text); }, onReset: () => { props.resetField('maxDevices'); } }), _jsx(ValueField, { id: "settings-remote-cookie", label: t('settings.cookieName'), hint: t('settings.cookieNameHint'), ...fieldProps, ...state.cookieName, onEdit: (text) => { props.edit('cookieName', text); }, onReset: () => { props.resetField('cookieName'); } }), _jsx(BooleanField, { id: "settings-remote-fence", label: t('settings.requirePairingForLan'), hint: t('settings.requirePairingForLanHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.requirePairingForLan, onEdit: (text) => { props.edit('requirePairingForLan', text); }, onReset: () => { props.resetField('requirePairingForLan'); } }), _jsx(ValueField, { id: "settings-remote-public-base", label: t('settings.publicBaseUrl'), hint: t('settings.publicBaseUrlHint'), placeholder: "https://example.trycloudflare.com", ...fieldProps, ...state.publicBaseUrl, onEdit: (text) => { props.edit('publicBaseUrl', text); }, onReset: () => { props.resetField('publicBaseUrl'); } }), _jsx(BooleanField, { id: "settings-remote-auto-tunnel", label: t('settings.autoTunnel'), hint: t('settings.autoTunnelHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.autoTunnel, onEdit: (text) => { props.edit('autoTunnel', text); }, onReset: () => { props.resetField('autoTunnel'); } })] }));
|
|
60
|
+
return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", state: state, onSave: props.save, onDiscard: props.discard, children: [_jsx(BooleanField, { id: "settings-remote-enabled", label: t('settings.enabled'), hint: t('settings.enabledHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.enabled, onEdit: (text) => { props.edit('enabled', text); }, onReset: () => { props.resetField('enabled'); } }), _jsx(ValueField, { id: "settings-remote-token-ttl", label: t('settings.tokenTtlMs'), hint: t('settings.tokenTtlMsHint'), numeric: true, ...fieldProps, ...state.tokenTtlMs, onEdit: (text) => { props.edit('tokenTtlMs', text); }, onReset: () => { props.resetField('tokenTtlMs'); } }), _jsx(ValueField, { id: "settings-remote-offline", label: t('settings.offlineAfterMs'), hint: t('settings.offlineAfterMsHint'), numeric: true, ...fieldProps, ...state.offlineAfterMs, onEdit: (text) => { props.edit('offlineAfterMs', text); }, onReset: () => { props.resetField('offlineAfterMs'); } }), _jsx(ValueField, { id: "settings-remote-max-devices", label: t('settings.maxDevices'), hint: t('settings.maxDevicesHint'), numeric: true, ...fieldProps, ...state.maxDevices, onEdit: (text) => { props.edit('maxDevices', text); }, onReset: () => { props.resetField('maxDevices'); } }), _jsx(ValueField, { id: "settings-remote-cookie", label: t('settings.cookieName'), hint: t('settings.cookieNameHint'), ...fieldProps, ...state.cookieName, onEdit: (text) => { props.edit('cookieName', text); }, onReset: () => { props.resetField('cookieName'); } }), _jsx(BooleanField, { id: "settings-remote-fence", label: t('settings.requirePairingForLan'), hint: t('settings.requirePairingForLanHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.requirePairingForLan, onEdit: (text) => { props.edit('requirePairingForLan', text); }, onReset: () => { props.resetField('requirePairingForLan'); } }), _jsx(ValueField, { id: "settings-remote-public-base", label: t('settings.publicBaseUrl'), hint: t('settings.publicBaseUrlHint'), placeholder: "https://example.trycloudflare.com", ...fieldProps, ...state.publicBaseUrl, onEdit: (text) => { props.edit('publicBaseUrl', text); }, onReset: () => { props.resetField('publicBaseUrl'); } }), _jsx(BooleanField, { id: "settings-remote-auto-tunnel", label: t('settings.autoTunnel'), hint: t('settings.autoTunnelHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.autoTunnel, onEdit: (text) => { props.edit('autoTunnel', text); }, onReset: () => { props.resetField('autoTunnel'); } }), _jsx(BooleanField, { id: "settings-remote-mobile-enter", label: t('settings.mobileEnterToSend'), hint: t('settings.mobileEnterToSendHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.mobileEnterToSend, onEdit: (text) => { props.edit('mobileEnterToSend', text); }, onReset: () => { props.resetField('mobileEnterToSend'); } })] }));
|
|
59
61
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"UpdateEntry.d.ts","sourceRoot":"","sources":["../../../src/client/UpdateEntry.tsx"],"names":[],"mappings":"AASA,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":"AASA,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,+BA2ExD"}
|
|
@@ -9,7 +9,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
9
9
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
10
10
|
import { createPortal } from "react-dom";
|
|
11
11
|
import { IconDownloadOutline16 } from '@deepseek-ai/dsh-client-ui-primitives';
|
|
12
|
-
import { fetchUpdateStatus, runUpdate } from "./update-api.js";
|
|
12
|
+
import { fetchUpdateStatus, runUpdate, UpdateStatusError } from "./update-api.js";
|
|
13
13
|
import { UpdatePanel } from "./UpdatePanel.js";
|
|
14
14
|
import css from "./remote.module.css";
|
|
15
15
|
/**
|
|
@@ -27,7 +27,15 @@ export function UpdateEntry({ wide, t }) {
|
|
|
27
27
|
try {
|
|
28
28
|
status = await fetchUpdateStatus();
|
|
29
29
|
}
|
|
30
|
-
catch {
|
|
30
|
+
catch (error) {
|
|
31
|
+
// HTTP 404: the update route is not mounted — the host process runs an
|
|
32
|
+
// older plugin build (client refreshed, host did not). Restarting dsh
|
|
33
|
+
// web loads the new plugin; a plain network failure gets the generic
|
|
34
|
+
// offline copy instead of a misleading "cannot reach update source".
|
|
35
|
+
if (error instanceof UpdateStatusError && error.status === 404) {
|
|
36
|
+
setView({ kind: "error", message: t("update.unmounted"), detail: t("update.unmountedDetail") });
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
31
39
|
setView({ kind: "error", message: t("update.offline"), detail: t("update.offlineDetail") });
|
|
32
40
|
return;
|
|
33
41
|
}
|
|
@@ -51,7 +51,7 @@ export function runPairBootFlow(ctx, search, page = browserPage) {
|
|
|
51
51
|
void runDeepLink(ctx, params.workspace, page);
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
|
-
/** Accept the token, then
|
|
54
|
+
/** Accept the token, then enter the matching desktop/mobile surface. */
|
|
55
55
|
async function runAccept(token, page) {
|
|
56
56
|
let ok = false;
|
|
57
57
|
try {
|
|
@@ -70,11 +70,16 @@ async function runAccept(token, page) {
|
|
|
70
70
|
page.replaceState(`${url.pathname}${url.search}${url.hash}`);
|
|
71
71
|
if (ok) {
|
|
72
72
|
// Phones land on the standalone simplified surface (the full desktop UI
|
|
73
|
-
// is not built for small screens)
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
73
|
+
// is not built for small screens). Keep the workspace target so the
|
|
74
|
+
// mobile surface can open the intended workspace instead of losing the
|
|
75
|
+
// QR context at this navigation boundary.
|
|
76
|
+
if (isMobileSurface()) {
|
|
77
|
+
url.pathname = '/m';
|
|
78
|
+
page.navigate(`${url.pathname}${url.search}${url.hash}`);
|
|
79
|
+
}
|
|
80
|
+
else {
|
|
77
81
|
page.reload();
|
|
82
|
+
}
|
|
78
83
|
}
|
|
79
84
|
}
|
|
80
85
|
/**
|
|
@@ -53,6 +53,8 @@ export declare const zh: {
|
|
|
53
53
|
'settings.publicBaseUrlHint': string;
|
|
54
54
|
'settings.autoTunnel': string;
|
|
55
55
|
'settings.autoTunnelHint': string;
|
|
56
|
+
'settings.mobileEnterToSend': string;
|
|
57
|
+
'settings.mobileEnterToSendHint': string;
|
|
56
58
|
'settings.inherit': string;
|
|
57
59
|
'settings.on': string;
|
|
58
60
|
'settings.off': string;
|
|
@@ -86,6 +88,8 @@ export declare const zh: {
|
|
|
86
88
|
'update.missingDetail': string;
|
|
87
89
|
'update.offline': string;
|
|
88
90
|
'update.offlineDetail': string;
|
|
91
|
+
'update.unmounted': string;
|
|
92
|
+
'update.unmountedDetail': string;
|
|
89
93
|
'update.error': string;
|
|
90
94
|
'update.packages': string;
|
|
91
95
|
'update.retry': string;
|
|
@@ -155,6 +159,8 @@ export declare const en: {
|
|
|
155
159
|
'settings.publicBaseUrlHint': string;
|
|
156
160
|
'settings.autoTunnel': string;
|
|
157
161
|
'settings.autoTunnelHint': string;
|
|
162
|
+
'settings.mobileEnterToSend': string;
|
|
163
|
+
'settings.mobileEnterToSendHint': string;
|
|
158
164
|
'settings.inherit': string;
|
|
159
165
|
'settings.on': string;
|
|
160
166
|
'settings.off': string;
|
|
@@ -188,6 +194,8 @@ export declare const en: {
|
|
|
188
194
|
'update.missingDetail': string;
|
|
189
195
|
'update.offline': string;
|
|
190
196
|
'update.offlineDetail': string;
|
|
197
|
+
'update.unmounted': string;
|
|
198
|
+
'update.unmountedDetail': string;
|
|
191
199
|
'update.error': string;
|
|
192
200
|
'update.packages': string;
|
|
193
201
|
'update.retry': 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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGmB,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAsGsB,CAAA"}
|
|
@@ -54,6 +54,8 @@ export const zh = {
|
|
|
54
54
|
'settings.publicBaseUrlHint': '内网穿透的公网地址,如 Cloudflare Tunnel:https://xxx.trycloudflare.com。填写后二维码将生成公网链接,手机不在同一网络也能配对;留空则仅局域网可用。',
|
|
55
55
|
'settings.autoTunnel': '自动公网隧道',
|
|
56
56
|
'settings.autoTunnelHint': '开启后插件自动启动 Cloudflare quick tunnel(无需安装任何工具),并自动更新公网地址与信任配置,手机随时可用公网配对;关闭时使用上方手动填写的公网地址。',
|
|
57
|
+
'settings.mobileEnterToSend': '移动端回车发送',
|
|
58
|
+
'settings.mobileEnterToSendHint': '开启:移动端输入框按 Enter 直接发送,Shift+Enter 换行;关闭:Enter 换行,仅点发送按钮发送。',
|
|
57
59
|
'settings.inherit': '继承',
|
|
58
60
|
'settings.on': '开',
|
|
59
61
|
'settings.off': '关',
|
|
@@ -88,6 +90,8 @@ export const zh = {
|
|
|
88
90
|
'update.missingDetail': '无法定位已安装的 dsh-web-ui 聚合包。请先运行 dsh plugin --profile <profile> add @linxin666/dsh-web-ui-all。',
|
|
89
91
|
'update.offline': '无法连接更新源',
|
|
90
92
|
'update.offlineDetail': '网络不可用或更新服务异常,请稍后重试。',
|
|
93
|
+
'update.unmounted': '更新服务未加载',
|
|
94
|
+
'update.unmountedDetail': '当前 dsh web 进程运行的插件版本不含更新服务,请重启 dsh web(Ctrl+C 后重新运行)后再试。',
|
|
91
95
|
'update.error': '更新失败',
|
|
92
96
|
'update.packages': '组件版本',
|
|
93
97
|
'update.retry': '重新检查',
|
|
@@ -154,6 +158,8 @@ export const en = {
|
|
|
154
158
|
'settings.publicBaseUrlHint': 'The public URL of a tunnel in front of this server, e.g. a Cloudflare Tunnel: https://xxx.trycloudflare.com. When set, the QR link is built from it so a phone anywhere can pair; leave blank for LAN-only usage.',
|
|
155
159
|
'settings.autoTunnel': 'Auto public tunnel',
|
|
156
160
|
'settings.autoTunnelHint': 'When on, the plugin runs its own Cloudflare quick tunnel (no tool installation needed) and keeps the public address and trust config in sync automatically, so a phone anywhere can pair at any time; when off, the manually entered public address above applies.',
|
|
161
|
+
'settings.mobileEnterToSend': 'Enter to send on mobile',
|
|
162
|
+
'settings.mobileEnterToSendHint': 'On: Enter in the mobile composer sends immediately and Shift+Enter inserts a newline. Off: Enter inserts a newline and only the send button sends.',
|
|
157
163
|
'settings.inherit': 'Inherit',
|
|
158
164
|
'settings.on': 'On',
|
|
159
165
|
'settings.off': 'Off',
|
|
@@ -188,6 +194,8 @@ export const en = {
|
|
|
188
194
|
'update.missingDetail': 'Could not locate the installed dsh-web-ui aggregate package. Install it with: dsh plugin --profile <profile> add @linxin666/dsh-web-ui-all',
|
|
189
195
|
'update.offline': 'Cannot reach the update source',
|
|
190
196
|
'update.offlineDetail': 'The network is unavailable or the update service is failing; try again later.',
|
|
197
|
+
'update.unmounted': 'The update service is not loaded',
|
|
198
|
+
'update.unmountedDetail': 'The running dsh web process loads a plugin build without the update service; restart dsh web (Ctrl+C, then run dsh web again) and retry.',
|
|
191
199
|
'update.error': 'Update failed',
|
|
192
200
|
'update.packages': 'Component versions',
|
|
193
201
|
'update.retry': 'Check again',
|
|
@@ -4,6 +4,16 @@
|
|
|
4
4
|
* loopback-only fence and owns the pnpm run.
|
|
5
5
|
*/
|
|
6
6
|
import type { UpdateRunResult, UpdateStatus } from '../update.ts';
|
|
7
|
+
/**
|
|
8
|
+
* Error thrown when the update status probe fails: carries the HTTP status
|
|
9
|
+
* so the panel can tell "the update route is not mounted" (404 — the host
|
|
10
|
+
* process runs an older plugin build) apart from real network failures.
|
|
11
|
+
*/
|
|
12
|
+
export declare class UpdateStatusError extends Error {
|
|
13
|
+
/** HTTP status of the failed response (0 when the fetch never returned). */
|
|
14
|
+
readonly status: number;
|
|
15
|
+
constructor(status: number);
|
|
16
|
+
}
|
|
7
17
|
/**
|
|
8
18
|
* Probe the update status: install mode, owning profile, and the
|
|
9
19
|
* current-vs-latest comparison for every family package.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"update-api.d.ts","sourceRoot":"","sources":["../../../src/client/update-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEjE;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,YAAY,CAAC,
|
|
1
|
+
{"version":3,"file":"update-api.d.ts","sourceRoot":"","sources":["../../../src/client/update-api.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEjE;;;;GAIG;AACH,qBAAa,iBAAkB,SAAQ,KAAK;IAC1C,4EAA4E;IAC5E,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;gBACX,MAAM,EAAE,MAAM;CAI3B;AAED;;;;GAIG;AACH,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,YAAY,CAAC,CAU/D;AAED;;;;GAIG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,eAAe,CAAC,CAI1D"}
|
|
@@ -3,15 +3,35 @@
|
|
|
3
3
|
* same-origin /api like the pairing client; the host half enforces the
|
|
4
4
|
* loopback-only fence and owns the pnpm run.
|
|
5
5
|
*/
|
|
6
|
+
/**
|
|
7
|
+
* Error thrown when the update status probe fails: carries the HTTP status
|
|
8
|
+
* so the panel can tell "the update route is not mounted" (404 — the host
|
|
9
|
+
* process runs an older plugin build) apart from real network failures.
|
|
10
|
+
*/
|
|
11
|
+
export class UpdateStatusError extends Error {
|
|
12
|
+
/** HTTP status of the failed response (0 when the fetch never returned). */
|
|
13
|
+
status;
|
|
14
|
+
constructor(status) {
|
|
15
|
+
super('update status unavailable (HTTP ' + String(status) + ')');
|
|
16
|
+
this.status = status;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
6
19
|
/**
|
|
7
20
|
* Probe the update status: install mode, owning profile, and the
|
|
8
21
|
* current-vs-latest comparison for every family package.
|
|
9
22
|
* @returns the status snapshot.
|
|
10
23
|
*/
|
|
11
24
|
export async function fetchUpdateStatus() {
|
|
12
|
-
|
|
25
|
+
let response;
|
|
26
|
+
try {
|
|
27
|
+
response = await fetch('/api/update/status');
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Network-level failure: nothing came back at all.
|
|
31
|
+
throw new UpdateStatusError(0);
|
|
32
|
+
}
|
|
13
33
|
if (!response.ok)
|
|
14
|
-
throw new
|
|
34
|
+
throw new UpdateStatusError(response.status);
|
|
15
35
|
return await response.json();
|
|
16
36
|
}
|
|
17
37
|
/**
|
package/lib/types/index.d.ts
CHANGED
|
@@ -65,6 +65,13 @@ export interface Config {
|
|
|
65
65
|
* tunnel setup. The manual `publicBaseUrl` is ignored while this is on.
|
|
66
66
|
*/
|
|
67
67
|
autoTunnel?: boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Mobile composer behavior: when true (default), a plain Enter in the
|
|
70
|
+
* phone chat textarea sends the prompt and Shift+Enter inserts a newline.
|
|
71
|
+
* When false, plain Enter inserts a newline and only the send button
|
|
72
|
+
* sends (Shift+Enter keeps inserting a newline).
|
|
73
|
+
*/
|
|
74
|
+
mobileEnterToSend?: boolean;
|
|
68
75
|
/** Master switch for the plugin (browser half + host pairing surfaces). */
|
|
69
76
|
enabled?: boolean;
|
|
70
77
|
}
|
package/lib/types/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,aAAa,CAAA;AAmB3B,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd;;;;;WAKG;QACH,UAAU,CACR,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACrC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAC9B;CACF;AAED,iCAAiC;AACjC,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAA4B,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,uDAAqC,CAAA;AAElF,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAChD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAElD,OAAO,CAAC,MAAM,aAAa,CAAA;AAmB3B,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,MAAM;QACd;;;;;WAKG;QACH,UAAU,CACR,IAAI,EAAE,OAAO,EACb,OAAO,EAAE,eAAe,EACxB,MAAM,EAAE,MAAM,GAAG,SAAS,EAC1B,IAAI,EAAE,MAAM,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,GACrC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAC9B;CACF;AAED,iCAAiC;AACjC,eAAO,MAAM,IAAI,kBAAkB,CAAA;AAEnC,+DAA+D;AAC/D,eAAO,MAAM,MAAM,UAA4B,CAAA;AAE/C;;;;GAIG;AACH,eAAO,MAAM,gCAAgC,uDAAqC,CAAA;AAElF,qEAAqE;AACrE,MAAM,WAAW,MAAM;IACrB,yDAAyD;IACzD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,yEAAyE;IACzE,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,qEAAqE;IACrE,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iDAAiD;IACjD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,OAAO,CAAA;IAC9B;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;;OAKG;IACH,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,2EAA2E;IAC3E,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,eAAO,MAAM,MAAM,EAAE,CAAC,CAAC,MAAM,CAU3B,CAAA;AAyBF;;;;GAIG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CA0MzD"}
|
package/lib/types/index.js
CHANGED
|
@@ -38,6 +38,7 @@ export const Config = z.object({
|
|
|
38
38
|
requirePairingForLan: z.boolean().default(true),
|
|
39
39
|
publicBaseUrl: z.string(),
|
|
40
40
|
autoTunnel: z.boolean().default(false),
|
|
41
|
+
mobileEnterToSend: z.boolean().default(true),
|
|
41
42
|
enabled: z.boolean().default(true),
|
|
42
43
|
});
|
|
43
44
|
/** Presence sweep cadence (a stale device flips to disconnected within two sweeps). */
|
|
@@ -51,6 +52,7 @@ const DEFAULTS = {
|
|
|
51
52
|
requirePairingForLan: true,
|
|
52
53
|
publicBaseUrl: undefined,
|
|
53
54
|
autoTunnel: false,
|
|
55
|
+
mobileEnterToSend: true,
|
|
54
56
|
enabled: true,
|
|
55
57
|
};
|
|
56
58
|
/**
|
|
@@ -67,6 +69,7 @@ export function apply(ctx, config) {
|
|
|
67
69
|
requirePairingForLan: config?.requirePairingForLan ?? DEFAULTS.requirePairingForLan,
|
|
68
70
|
publicBaseUrl: config?.publicBaseUrl,
|
|
69
71
|
autoTunnel: config?.autoTunnel ?? DEFAULTS.autoTunnel,
|
|
72
|
+
mobileEnterToSend: config?.mobileEnterToSend ?? DEFAULTS.mobileEnterToSend,
|
|
70
73
|
enabled: config?.enabled ?? DEFAULTS.enabled,
|
|
71
74
|
};
|
|
72
75
|
// The live source the pairing service and the gate read: the settings
|
|
@@ -83,6 +86,7 @@ export function apply(ctx, config) {
|
|
|
83
86
|
requirePairingForLan: value.requirePairingForLan ?? DEFAULTS.requirePairingForLan,
|
|
84
87
|
publicBaseUrl: value.publicBaseUrl,
|
|
85
88
|
autoTunnel: value.autoTunnel ?? DEFAULTS.autoTunnel,
|
|
89
|
+
mobileEnterToSend: value.mobileEnterToSend ?? DEFAULTS.mobileEnterToSend,
|
|
86
90
|
enabled: value.enabled ?? DEFAULTS.enabled,
|
|
87
91
|
};
|
|
88
92
|
};
|
|
@@ -189,7 +193,9 @@ export function apply(ctx, config) {
|
|
|
189
193
|
const routes = [
|
|
190
194
|
...makeRoutes({ service, lanAddresses }),
|
|
191
195
|
...makeMobileRoutes(),
|
|
192
|
-
...(apiProxy !== undefined
|
|
196
|
+
...(apiProxy !== undefined
|
|
197
|
+
? makeMobileApiRoutes({ service, apiProxy, mobileEnterToSend: () => resolve().mobileEnterToSend })
|
|
198
|
+
: []),
|
|
193
199
|
...updateRoutes,
|
|
194
200
|
];
|
|
195
201
|
const gate = makeGateListener(service, () => resolve().requirePairingForLan, () => resolve().enabled);
|
|
@@ -29,8 +29,15 @@ export interface HistoryPage {
|
|
|
29
29
|
*/
|
|
30
30
|
projections?: SessionProjectionsBlock;
|
|
31
31
|
}
|
|
32
|
+
/** Read-only display preferences the plugin answers locally on `/m/api`. */
|
|
33
|
+
export interface MobilePreferences {
|
|
34
|
+
/** Plain Enter sends the drafted prompt (false: Enter inserts a newline). */
|
|
35
|
+
mobileEnterToSend: boolean;
|
|
36
|
+
}
|
|
32
37
|
/** The workspace roster (session ids come back per workspace). */
|
|
33
38
|
export declare function listWorkspaces(): Promise<WorkspaceView[]>;
|
|
39
|
+
/** Read-only mobile display preferences (answered by the plugin, not the host proxy). */
|
|
40
|
+
export declare function fetchMobilePreferences(): Promise<MobilePreferences>;
|
|
34
41
|
/** One session.list page; omit the cursor for the first page. */
|
|
35
42
|
export declare function listSessions(cursor?: string): Promise<SessionPage>;
|
|
36
43
|
/**
|
|
@@ -41,8 +48,8 @@ export declare function createSession(options?: {
|
|
|
41
48
|
workspaceId?: string;
|
|
42
49
|
cwd?: string;
|
|
43
50
|
}): Promise<CreatedSession>;
|
|
44
|
-
/** One history window; omit beforeSeq for the tail page. */
|
|
45
|
-
export declare function history(sessionId: string, beforeSeq?: number, maxMessages?: number): Promise<HistoryPage>;
|
|
51
|
+
/** One history window; omit beforeSeq for the tail page, pass a signal to abort. */
|
|
52
|
+
export declare function history(sessionId: string, beforeSeq?: number, maxMessages?: number, signal?: AbortSignal): Promise<HistoryPage>;
|
|
46
53
|
/** Send one text prompt (queued: the agent picks it up in order). */
|
|
47
54
|
export declare function prompt(sessionId: string, text: string): Promise<void>;
|
|
48
55
|
/** Send one slash command line (e.g. `/permission workspace-write`). */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/mobile/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAA;AAGzH,6BAA6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,6CAA6C,EAAE,YAAY,EAAE,CAAA;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CACtC;AAED,kEAAkE;AAClE,wBAAsB,cAAc,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAG/D;AAED,iEAAiE;AACjE,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAExE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,OAAO,CAAC,cAAc,CAAC,CAEzB;AAED,
|
|
1
|
+
{"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../src/mobile/api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAA;AACjF,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,uBAAuB,EAAE,MAAM,6CAA6C,CAAA;AAGzH,6BAA6B;AAC7B,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,cAAc,EAAE,CAAA;IACvB,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,OAAO,EAAE,OAAO,CAAA;CACjB;AAED,gFAAgF;AAChF,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAA;IACjB,gFAAgF;IAChF,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,wEAAwE;AACxE,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,OAAO,6CAA6C,EAAE,YAAY,EAAE,CAAA;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;;OAGG;IACH,WAAW,CAAC,EAAE,uBAAuB,CAAA;CACtC;AAED,4EAA4E;AAC5E,MAAM,WAAW,iBAAiB;IAChC,6EAA6E;IAC7E,iBAAiB,EAAE,OAAO,CAAA;CAC3B;AAED,kEAAkE;AAClE,wBAAsB,cAAc,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAG/D;AAED,yFAAyF;AACzF,wBAAsB,sBAAsB,IAAI,OAAO,CAAC,iBAAiB,CAAC,CAEzE;AAED,iEAAiE;AACjE,wBAAsB,YAAY,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAExE;AAED;;;GAGG;AACH,wBAAsB,aAAa,CACjC,OAAO,GAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAO,GACnD,OAAO,CAAC,cAAc,CAAC,CAEzB;AAED,oFAAoF;AACpF,wBAAsB,OAAO,CAC3B,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,WAAW,SAAK,EAChB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,WAAW,CAAC,CAMtB;AAED,qEAAqE;AACrE,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAM3E;AAED,wEAAwE;AACxE,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAMnF;AAED,oFAAoF;AACpF,wBAAsB,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAEtE;AAED,2FAA2F;AAC3F,wBAAsB,WAAW,CAC/B,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,MAAM,CAAA;CAAE,GACvE,OAAO,CAAC;IAAE,QAAQ,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,eAAe,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAAE,CAAC,CAOtF"}
|
package/lib/types/mobile/api.js
CHANGED
|
@@ -10,6 +10,10 @@ export async function listWorkspaces() {
|
|
|
10
10
|
const { items } = await callUnary('workspace.list', {});
|
|
11
11
|
return items;
|
|
12
12
|
}
|
|
13
|
+
/** Read-only mobile display preferences (answered by the plugin, not the host proxy). */
|
|
14
|
+
export async function fetchMobilePreferences() {
|
|
15
|
+
return await callUnary('mobile.preferences', {});
|
|
16
|
+
}
|
|
13
17
|
/** One session.list page; omit the cursor for the first page. */
|
|
14
18
|
export async function listSessions(cursor) {
|
|
15
19
|
return await callUnary('session.list', cursor === undefined ? {} : { cursor });
|
|
@@ -21,13 +25,13 @@ export async function listSessions(cursor) {
|
|
|
21
25
|
export async function createSession(options = {}) {
|
|
22
26
|
return await callUnary('session.create', options);
|
|
23
27
|
}
|
|
24
|
-
/** One history window; omit beforeSeq for the tail page. */
|
|
25
|
-
export async function history(sessionId, beforeSeq, maxMessages = 30) {
|
|
28
|
+
/** One history window; omit beforeSeq for the tail page, pass a signal to abort. */
|
|
29
|
+
export async function history(sessionId, beforeSeq, maxMessages = 30, signal) {
|
|
26
30
|
return await callUnary('session.history', {
|
|
27
31
|
sessionId,
|
|
28
32
|
maxMessages,
|
|
29
33
|
...(beforeSeq !== undefined ? { beforeSeq } : {}),
|
|
30
|
-
});
|
|
34
|
+
}, signal);
|
|
31
35
|
}
|
|
32
36
|
/** Send one text prompt (queued: the agent picks it up in order). */
|
|
33
37
|
export async function prompt(sessionId, text) {
|
|
@@ -2,5 +2,5 @@
|
|
|
2
2
|
* Mobile surface stylesheet, shipped as a string and injected at boot (the
|
|
3
3
|
* standalone page has no CSS-module pipeline).
|
|
4
4
|
*/
|
|
5
|
-
export declare const mobileCss = "/* Mobile surface chrome: standalone stylesheet (no main-UI design tokens \u2014\n this page boots without the shell). Light-first palette; the dark palette\n applies only when the header toggle sets [data-theme='dark']. */\n\n:root {\n --m-bg: #f3f5f9;\n --m-bg-raised: #ffffff;\n --m-bg-input: #ffffff;\n --m-border: #dfe4ec;\n --m-text: #1c2333;\n --m-text-secondary: #4d5768;\n --m-text-tertiary: #8b95a7;\n --m-accent: #2f6fed;\n --m-accent-soft: rgba(47, 111, 237, 0.12);\n --m-danger: #d64545;\n --m-success: #1e9e6a;\n --m-radius: 12px;\n --m-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);\n --m-shadow-raise: 0 6px 20px rgba(16, 24, 40, 0.1);\n --m-backdrop: rgba(15, 23, 42, 0.4);\n}\n\n:root[data-theme='dark'] {\n --m-bg: #111418;\n --m-bg-raised: #1a1f26;\n --m-bg-input: #20262e;\n --m-border: #2b333d;\n --m-text: #e8eaed;\n --m-text-secondary: #9aa3ad;\n --m-text-tertiary: #6b747f;\n --m-accent: #4f8cff;\n --m-accent-soft: rgba(79, 140, 255, 0.14);\n --m-danger: #e5534b;\n --m-success: #3fb68b;\n --m-radius: 12px;\n --m-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n --m-shadow-raise: 0 6px 20px rgba(0, 0, 0, 0.4);\n --m-backdrop: rgba(0, 0, 0, 0.55);\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody {\n margin: 0;\n padding: 0;\n height: 100%;\n background: var(--m-bg);\n color: var(--m-text);\n font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Noto Sans SC', 'Segoe UI', sans-serif;\n font-size: 15px;\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n}\n\n/* The app fills exactly one viewport: the page itself never scrolls, each\n view owns its scroll region (chat history, session lists) and the chat\n composer stays pinned to the bottom of the screen. */\n#root {\n height: 100dvh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.mobile {\n min-height: 0;\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* \u2500\u2500 header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-header {\n position: sticky;\n top: 0;\n z-index: 10;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 10px;\n background: color-mix(in srgb, var(--m-bg) 88%, transparent);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid var(--m-border);\n}\n\n.mobile-title {\n margin: 0;\n font-size: 17px;\n font-weight: 600;\n}\n\n.mobile-titleInline {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mobile-back {\n flex: none;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--m-text);\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n transition: background-color 0.12s ease, box-shadow 0.12s ease;\n}\n\n.mobile-back:active {\n background: var(--m-bg-raised);\n}\n\n.mobile-back:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n.mobile-theme-toggle {\n flex: none;\n margin-left: auto;\n width: 32px;\n height: 32px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--m-text-secondary);\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;\n}\n\n.mobile-theme-toggle:active {\n background: var(--m-bg-raised);\n}\n\n.mobile-theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n.mobile-theme-toggle svg {\n width: 18px;\n height: 18px;\n}\n\n/* \u2500\u2500 lists \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n list-style: none;\n margin: 0;\n padding: 8px 0 calc(env(safe-area-inset-bottom, 0px) + 16px);\n}\n\n.mobile-row {\n display: flex;\n align-items: center;\n gap: 10px;\n width: calc(100% - 24px);\n margin: 0 12px 8px;\n padding: 13px 14px;\n border: 1px solid var(--m-border);\n border-radius: 14px;\n background: var(--m-bg-raised);\n box-shadow: var(--m-shadow);\n color: var(--m-text);\n text-align: left;\n cursor: pointer;\n}\n\n.mobile-row:active {\n background: color-mix(in srgb, var(--m-text) 5%, var(--m-bg-raised));\n}\n\n.mobile-rowMain {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.mobile-rowTitle {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 15px;\n}\n\n.mobile-rowMeta {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n.mobile-chevron {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 18px;\n}\n\n.mobile-live {\n margin-left: 6px;\n color: var(--m-success);\n font-size: 10px;\n vertical-align: middle;\n}\n\n/* \u2500\u2500 empty / error states \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 32px;\n}\n\n.mobile-muted {\n margin: 0;\n color: var(--m-text-tertiary);\n}\n\n.mobile-error {\n margin: 0;\n color: var(--m-danger);\n}\n\n.mobile-pad {\n padding: 8px 16px;\n}\n\n/* \u2500\u2500 buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-button {\n height: 38px;\n padding: 0 18px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: var(--m-bg-raised);\n color: var(--m-text);\n font-size: 14px;\n cursor: pointer;\n}\n\n.mobile-button:disabled {\n opacity: 0.5;\n}\n\n.mobile-block {\n display: block;\n width: calc(100% - 32px);\n margin: 4px 16px;\n}\n\n/* Primary action (create session): accent fill, readable on both themes. */\n.mobile-new {\n display: block;\n width: 100%;\n height: 44px;\n border: none;\n border-radius: 12px;\n background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);\n color: #fff;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(47, 111, 237, 0.3);\n}\n\n.mobile-new:active {\n opacity: 0.88;\n}\n\n.mobile-new:disabled {\n opacity: 0.6;\n}\n\n.mobile-hint {\n display: block;\n margin-top: 2px;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n/* \u2500\u2500 chat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.chat-scroll {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px calc(env(safe-area-inset-bottom, 0px) + 12px);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.chat-msg {\n max-width: 88%;\n padding: 10px 12px;\n border-radius: var(--m-radius);\n font-size: 14.5px;\n line-height: 1.55;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n}\n\n.chat-msg-user {\n align-self: flex-end;\n background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n box-shadow: 0 1px 3px rgba(47, 111, 237, 0.28);\n}\n\n.chat-msg-assistant {\n align-self: flex-start;\n background: var(--m-bg-raised);\n border: 1px solid var(--m-border);\n border-bottom-left-radius: 4px;\n box-shadow: var(--m-shadow);\n}\n\n.chat-msg-pending .chat-msg-text::after {\n content: '\u258D';\n animation: chat-blink 1s steps(2) infinite;\n}\n\n@keyframes chat-blink {\n 50% {\n opacity: 0;\n }\n}\n\n.chat-msg-failed {\n border-color: var(--m-danger);\n}\n\n.chat-msg-failtag {\n display: inline-block;\n margin-top: 6px;\n padding: 1px 8px;\n border: 1px solid var(--m-danger);\n border-radius: 999px;\n color: var(--m-danger);\n font-size: 11px;\n}\n\n.chat-msg-time {\n display: block;\n margin-top: 4px;\n color: var(--m-text-tertiary);\n font-size: 10.5px;\n line-height: 1.2;\n}\n\n.chat-msg-toggle {\n display: block;\n margin-top: 8px;\n padding: 4px 0;\n border: none;\n background: transparent;\n color: var(--m-accent);\n font-size: 13px;\n cursor: pointer;\n}\n\n/* \u2500\u2500 message disclosures (reasoning / tools) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat-disclosure {\n margin-bottom: 8px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: color-mix(in srgb, var(--m-bg) 55%, transparent);\n overflow: hidden;\n}\n\n.chat-disclosure-head {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 7px 10px;\n border: none;\n background: transparent;\n color: var(--m-text);\n font-size: 12.5px;\n text-align: left;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.chat-disclosure-caret {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 14px;\n line-height: 1;\n transition: transform 0.15s ease;\n}\n\n.chat-disclosure-open .chat-disclosure-caret {\n transform: rotate(90deg);\n}\n\n.chat-disclosure-label {\n flex: none;\n color: var(--m-text-secondary);\n}\n\n.chat-reasoning[data-pending] .chat-disclosure-label {\n color: var(--m-accent);\n}\n\n.chat-disclosure-summary {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n.chat-disclosure-count {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 11px;\n}\n\n.chat-disclosure-body {\n padding: 8px 10px 10px;\n border-top: 1px dashed var(--m-border);\n color: var(--m-text-secondary);\n font-size: 12.5px;\n line-height: 1.6;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n max-height: 40vh;\n overflow-y: auto;\n}\n\n.chat-tools-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.chat-tool-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.chat-tool-name {\n color: var(--m-text);\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.chat-tool-args {\n margin: 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--m-bg-input);\n color: var(--m-text-secondary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n}\n\n/* \u2500\u2500 composer toolbar (model / permission chips) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat-tools {\n display: flex;\n gap: 8px;\n padding: 6px 12px 0;\n}\n\n.chat-chip {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--m-border);\n border-radius: 999px;\n background: var(--m-bg-raised);\n box-shadow: var(--m-shadow);\n color: var(--m-text);\n font-size: 12px;\n cursor: pointer;\n}\n\n.chat-chip:active {\n border-color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.chat-chip-label {\n flex: none;\n color: var(--m-text-tertiary);\n}\n\n.chat-chip-value {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.chat-chip-chevron {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 14px;\n}\n\n/* \u2500\u2500 bottom sheets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.sheet-backdrop {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n background: var(--m-backdrop);\n animation: sheet-fade 0.18s ease;\n}\n\n@keyframes sheet-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.sheet {\n max-height: 74dvh;\n display: flex;\n flex-direction: column;\n background: var(--m-bg-raised);\n border: 1px solid var(--m-border);\n border-bottom: none;\n border-radius: 16px 16px 0 0;\n box-shadow: var(--m-shadow-raise);\n padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);\n animation: sheet-up 0.22s ease;\n}\n\n@keyframes sheet-up {\n from {\n transform: translateY(40px);\n opacity: 0.6;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n.sheet-handle {\n flex: none;\n align-self: center;\n width: 36px;\n height: 4px;\n margin: 8px 0 4px;\n border-radius: 999px;\n background: var(--m-border);\n}\n\n.sheet-title {\n flex: none;\n padding: 6px 16px 10px;\n font-size: 15px;\n font-weight: 600;\n}\n\n.sheet-body {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 12px 8px;\n}\n\n.sheet-status {\n padding: 18px 8px;\n color: var(--m-text-tertiary);\n font-size: 13px;\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n}\n\n.sheet-status-error {\n color: var(--m-danger);\n}\n\n.sheet-hint {\n margin: 0;\n color: var(--m-text-secondary);\n font-size: 12px;\n line-height: 1.5;\n max-width: 32em;\n}\n\n.sheet-error {\n margin: 0 0 8px;\n color: var(--m-danger);\n font-size: 12.5px;\n}\n\n.sheet-section {\n margin-bottom: 14px;\n}\n\n.sheet-section-title {\n padding: 4px 8px 6px;\n color: var(--m-text-tertiary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.sheet-option {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 12px;\n border: 1px solid transparent;\n border-radius: 10px;\n background: transparent;\n color: var(--m-text);\n text-align: left;\n cursor: pointer;\n}\n\n.sheet-option:active {\n background: var(--m-bg-input);\n}\n\n.sheet-option-selected {\n border-color: var(--m-accent);\n background: color-mix(in srgb, var(--m-accent) 12%, transparent);\n}\n\n.sheet-option:disabled {\n opacity: 0.5;\n}\n\n.sheet-option-copy {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.sheet-option-title {\n font-size: 14px;\n}\n\n.sheet-option-desc {\n color: var(--m-text-tertiary);\n font-size: 12px;\n line-height: 1.4;\n}\n\n.sheet-option-check {\n flex: none;\n color: var(--m-accent);\n font-size: 15px;\n font-weight: 700;\n}\n\n.sheet-confirm-desc {\n margin: 0 4px 14px;\n color: var(--m-text-secondary);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.sheet-confirm-actions {\n display: flex;\n gap: 10px;\n padding: 4px;\n}\n\n.sheet-confirm-actions .mobile-button {\n flex: 1;\n}\n\n.sheet-confirm-danger {\n flex: 1;\n height: 38px;\n border: none;\n border-radius: 10px;\n background: var(--m-danger);\n color: #fff;\n font-size: 14px;\n cursor: pointer;\n}\n\n.sheet-confirm-danger:disabled {\n opacity: 0.5;\n}\n\n.chat-meta {\n margin-top: 4px;\n color: var(--m-text-tertiary);\n font-size: 11px;\n}\n\n.chat-typing {\n color: var(--m-text-tertiary);\n font-size: 13px;\n padding: 4px 2px;\n}\n\n.chat-inputbar {\n display: flex;\n gap: 8px;\n padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 10px);\n border-top: 1px solid var(--m-border);\n background: var(--m-bg);\n}\n\n.chat-input {\n flex: 1;\n min-width: 0;\n min-height: 40px;\n max-height: 120px;\n padding: 9px 12px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: var(--m-bg-input);\n color: var(--m-text);\n font: inherit;\n font-size: 14.5px;\n resize: none;\n outline: none;\n}\n\n.chat-input:focus {\n border-color: var(--m-accent);\n}\n\n.chat-send {\n flex: none;\n align-self: flex-end;\n height: 40px;\n padding: 0 16px;\n border: none;\n border-radius: 10px;\n background: var(--m-accent);\n color: #fff;\n font-size: 14px;\n cursor: pointer;\n}\n\n.chat-send:disabled {\n opacity: 0.5;\n}\n\n.chat-load-older {\n align-self: center;\n margin: 4px 0;\n border: none;\n background: transparent;\n color: var(--m-text-secondary);\n font-size: 13px;\n cursor: pointer;\n padding: 6px 10px;\n}\n\n/* Touch surfaces have no hover, so keyboard focus must be fully visible.\n One shared ring keeps every interactive control consistent and readable\n on both palettes. */\n.mobile-row:focus-visible,\n.mobile-button:focus-visible,\n.mobile-new:focus-visible,\n.chat-send:focus-visible,\n.chat-chip:focus-visible,\n.chat-msg-toggle:focus-visible,\n.chat-load-older:focus-visible,\n.chat-disclosure-head:focus-visible,\n.sheet-option:focus-visible,\n.sheet-confirm-danger:focus-visible,\n.chat-input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n/* Accessibility: collapse every interaction/enter animation for\n reduced-motion users; surface layout itself stays static. */\n@media (prefers-reduced-motion: reduce) {\n .mobile-theme-toggle,\n .mobile-back,\n .mobile-row,\n .mobile-button,\n .mobile-new,\n .chat-send,\n .chat-chip,\n .chat-msg-toggle,\n .chat-load-older,\n .chat-disclosure-head,\n .chat-disclosure-caret,\n .chat-msg-pending .chat-msg-text::after,\n .sheet-backdrop,\n .sheet,\n .sheet-option,\n .sheet-confirm-danger,\n .chat-input {\n animation: none;\n transition: none;\n }\n}\n";
|
|
5
|
+
export declare const mobileCss = "/* Mobile surface chrome: standalone stylesheet (no main-UI design tokens \u2014\n this page boots without the shell). Light-first palette; the dark palette\n applies only when the header toggle sets [data-theme='dark']. */\n\n:root {\n --m-bg: #f3f5f9;\n --m-bg-raised: #ffffff;\n --m-bg-input: #ffffff;\n --m-border: #dfe4ec;\n --m-text: #1c2333;\n --m-text-secondary: #4d5768;\n --m-text-tertiary: #8b95a7;\n --m-accent: #2f6fed;\n --m-accent-soft: rgba(47, 111, 237, 0.12);\n --m-danger: #d64545;\n --m-success: #1e9e6a;\n --m-radius: 12px;\n --m-shadow: 0 1px 2px rgba(16, 24, 40, 0.06);\n --m-shadow-raise: 0 6px 20px rgba(16, 24, 40, 0.1);\n --m-backdrop: rgba(15, 23, 42, 0.4);\n}\n\n:root[data-theme='dark'] {\n --m-bg: #111418;\n --m-bg-raised: #1a1f26;\n --m-bg-input: #20262e;\n --m-border: #2b333d;\n --m-text: #e8eaed;\n --m-text-secondary: #9aa3ad;\n --m-text-tertiary: #6b747f;\n --m-accent: #4f8cff;\n --m-accent-soft: rgba(79, 140, 255, 0.14);\n --m-danger: #e5534b;\n --m-success: #3fb68b;\n --m-radius: 12px;\n --m-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);\n --m-shadow-raise: 0 6px 20px rgba(0, 0, 0, 0.4);\n --m-backdrop: rgba(0, 0, 0, 0.55);\n}\n\n* {\n box-sizing: border-box;\n}\n\nhtml,\nbody {\n margin: 0;\n padding: 0;\n height: 100%;\n background: var(--m-bg);\n color: var(--m-text);\n font-family: -apple-system, BlinkMacSystemFont, 'PingFang SC', 'Noto Sans SC', 'Segoe UI', sans-serif;\n font-size: 15px;\n line-height: 1.5;\n -webkit-text-size-adjust: 100%;\n}\n\n/* The app fills exactly one viewport: the page itself never scrolls, each\n view owns its scroll region (chat history, session lists) and the chat\n composer stays pinned to the bottom of the screen. */\n#root {\n height: 100dvh;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n.mobile {\n min-height: 0;\n flex: 1;\n display: flex;\n flex-direction: column;\n overflow: hidden;\n}\n\n/* \u2500\u2500 header \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-header {\n position: sticky;\n top: 0;\n z-index: 10;\n flex-shrink: 0;\n display: flex;\n align-items: center;\n gap: 8px;\n padding: calc(env(safe-area-inset-top, 0px) + 12px) 16px 10px;\n background: color-mix(in srgb, var(--m-bg) 88%, transparent);\n backdrop-filter: blur(10px);\n border-bottom: 1px solid var(--m-border);\n}\n\n.mobile-title {\n margin: 0;\n font-size: 17px;\n font-weight: 600;\n}\n\n.mobile-titleInline {\n flex: 1;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.mobile-back {\n flex: none;\n width: 32px;\n height: 32px;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--m-text);\n font-size: 22px;\n line-height: 1;\n cursor: pointer;\n transition: background-color 0.12s ease, box-shadow 0.12s ease;\n}\n\n.mobile-back:active {\n background: var(--m-bg-raised);\n}\n\n.mobile-back:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n.mobile-theme-toggle {\n flex: none;\n margin-left: auto;\n width: 32px;\n height: 32px;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n border: none;\n border-radius: 50%;\n background: transparent;\n color: var(--m-text-secondary);\n cursor: pointer;\n transition: background-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease;\n}\n\n.mobile-theme-toggle:active {\n background: var(--m-bg-raised);\n}\n\n.mobile-theme-toggle:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n.mobile-theme-toggle svg {\n width: 18px;\n height: 18px;\n}\n\n/* \u2500\u2500 lists \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-list {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n list-style: none;\n margin: 0;\n padding: 8px 0 calc(env(safe-area-inset-bottom, 0px) + 16px);\n}\n\n.mobile-row {\n display: flex;\n align-items: center;\n gap: 10px;\n width: calc(100% - 24px);\n margin: 0 12px 8px;\n padding: 13px 14px;\n border: 1px solid var(--m-border);\n border-radius: 14px;\n background: var(--m-bg-raised);\n box-shadow: var(--m-shadow);\n color: var(--m-text);\n text-align: left;\n cursor: pointer;\n}\n\n.mobile-row:active {\n background: color-mix(in srgb, var(--m-text) 5%, var(--m-bg-raised));\n}\n\n.mobile-rowMain {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: baseline;\n gap: 8px;\n}\n\n.mobile-rowTitle {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n font-size: 15px;\n}\n\n.mobile-rowMeta {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n.mobile-chevron {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 18px;\n}\n\n.mobile-live {\n margin-left: 6px;\n color: var(--m-success);\n font-size: 10px;\n vertical-align: middle;\n}\n\n/* \u2500\u2500 empty / error states \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-empty {\n flex: 1;\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n gap: 12px;\n padding: 32px;\n}\n\n.mobile-muted {\n margin: 0;\n color: var(--m-text-tertiary);\n}\n\n.mobile-error {\n margin: 0;\n color: var(--m-danger);\n}\n\n.mobile-pad {\n padding: 8px 16px;\n}\n\n/* \u2500\u2500 buttons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.mobile-button {\n height: 38px;\n padding: 0 18px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: var(--m-bg-raised);\n color: var(--m-text);\n font-size: 14px;\n cursor: pointer;\n}\n\n.mobile-button:disabled {\n opacity: 0.5;\n}\n\n.mobile-block {\n display: block;\n width: calc(100% - 32px);\n margin: 4px 16px;\n}\n\n/* Primary action (create session): accent fill, readable on both themes. */\n.mobile-new {\n display: block;\n width: 100%;\n height: 44px;\n border: none;\n border-radius: 12px;\n background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);\n color: #fff;\n font-size: 15px;\n font-weight: 600;\n cursor: pointer;\n box-shadow: 0 2px 8px rgba(47, 111, 237, 0.3);\n}\n\n.mobile-new:active {\n opacity: 0.88;\n}\n\n.mobile-new:disabled {\n opacity: 0.6;\n}\n\n.mobile-hint {\n display: block;\n margin-top: 2px;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n/* \u2500\u2500 chat \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat {\n flex: 1;\n display: flex;\n flex-direction: column;\n min-height: 0;\n}\n\n.chat-scroll {\n flex: 1;\n overflow-y: auto;\n padding: 12px 14px calc(env(safe-area-inset-bottom, 0px) + 12px);\n display: flex;\n flex-direction: column;\n gap: 10px;\n}\n\n.chat-msg {\n max-width: 88%;\n padding: 10px 12px;\n border-radius: var(--m-radius);\n font-size: 14.5px;\n line-height: 1.55;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n}\n\n.chat-msg-user {\n align-self: flex-end;\n background: linear-gradient(135deg, var(--m-accent) 0%, #4c8dff 100%);\n color: #fff;\n border-bottom-right-radius: 4px;\n box-shadow: 0 1px 3px rgba(47, 111, 237, 0.28);\n}\n\n.chat-msg-assistant {\n align-self: flex-start;\n background: var(--m-bg-raised);\n border: 1px solid var(--m-border);\n border-bottom-left-radius: 4px;\n box-shadow: var(--m-shadow);\n}\n\n.chat-msg-pending .chat-msg-text::after {\n content: '\u258D';\n animation: chat-blink 1s steps(2) infinite;\n}\n\n@keyframes chat-blink {\n 50% {\n opacity: 0;\n }\n}\n\n.chat-msg-failed {\n border-color: var(--m-danger);\n}\n\n.chat-msg-failtag {\n display: inline-block;\n margin-top: 6px;\n padding: 1px 8px;\n border: 1px solid var(--m-danger);\n border-radius: 999px;\n color: var(--m-danger);\n font-size: 11px;\n}\n\n.chat-msg-time {\n display: block;\n margin-top: 4px;\n color: var(--m-text-tertiary);\n font-size: 10.5px;\n line-height: 1.2;\n}\n\n.chat-msg-toggle {\n display: block;\n margin-top: 8px;\n padding: 4px 0;\n border: none;\n background: transparent;\n color: var(--m-accent);\n font-size: 13px;\n cursor: pointer;\n}\n\n/* \u2500\u2500 message disclosures (reasoning / tools) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat-disclosure {\n margin-bottom: 8px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: color-mix(in srgb, var(--m-bg) 55%, transparent);\n overflow: hidden;\n}\n\n.chat-disclosure-head {\n display: flex;\n align-items: center;\n gap: 6px;\n width: 100%;\n padding: 7px 10px;\n border: none;\n background: transparent;\n color: var(--m-text);\n font-size: 12.5px;\n text-align: left;\n cursor: pointer;\n white-space: nowrap;\n}\n\n.chat-disclosure-caret {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 14px;\n line-height: 1;\n transition: transform 0.15s ease;\n}\n\n.chat-disclosure-open .chat-disclosure-caret {\n transform: rotate(90deg);\n}\n\n.chat-disclosure-label {\n flex: none;\n color: var(--m-text-secondary);\n}\n\n.chat-reasoning[data-pending] .chat-disclosure-label {\n color: var(--m-accent);\n}\n\n.chat-disclosure-summary {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n color: var(--m-text-tertiary);\n font-size: 12px;\n}\n\n.chat-disclosure-count {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 11px;\n}\n\n.chat-disclosure-body {\n padding: 8px 10px 10px;\n border-top: 1px dashed var(--m-border);\n color: var(--m-text-secondary);\n font-size: 12.5px;\n line-height: 1.6;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n max-height: 40vh;\n overflow-y: auto;\n}\n\n.chat-tools-body {\n display: flex;\n flex-direction: column;\n gap: 8px;\n}\n\n.chat-tool-item {\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.chat-tool-name {\n color: var(--m-text);\n font-size: 12.5px;\n font-weight: 600;\n}\n\n.chat-tool-args {\n margin: 0;\n padding: 6px 8px;\n border-radius: 6px;\n background: var(--m-bg-input);\n color: var(--m-text-secondary);\n font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n font-size: 11px;\n line-height: 1.5;\n overflow-wrap: break-word;\n white-space: pre-wrap;\n}\n\n/* \u2500\u2500 composer toolbar (model / permission chips) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.chat-tools {\n display: flex;\n gap: 8px;\n padding: 6px 12px 0;\n}\n\n.chat-chip {\n flex: 1;\n min-width: 0;\n display: flex;\n align-items: center;\n gap: 6px;\n height: 32px;\n padding: 0 10px;\n border: 1px solid var(--m-border);\n border-radius: 999px;\n background: var(--m-bg-raised);\n box-shadow: var(--m-shadow);\n color: var(--m-text);\n font-size: 12px;\n cursor: pointer;\n}\n\n.chat-chip:active {\n border-color: var(--m-accent);\n background: var(--m-accent-soft);\n}\n\n.chat-chip-label {\n flex: none;\n color: var(--m-text-tertiary);\n}\n\n.chat-chip-value {\n flex: 1;\n min-width: 0;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.chat-chip-chevron {\n flex: none;\n color: var(--m-text-tertiary);\n font-size: 14px;\n}\n\n/* \u2500\u2500 bottom sheets \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n\n.sheet-backdrop {\n position: fixed;\n inset: 0;\n z-index: 50;\n display: flex;\n flex-direction: column;\n justify-content: flex-end;\n background: var(--m-backdrop);\n animation: sheet-fade 0.18s ease;\n}\n\n@keyframes sheet-fade {\n from {\n opacity: 0;\n }\n to {\n opacity: 1;\n }\n}\n\n.sheet {\n max-height: 74dvh;\n display: flex;\n flex-direction: column;\n background: var(--m-bg-raised);\n border: 1px solid var(--m-border);\n border-bottom: none;\n border-radius: 16px 16px 0 0;\n box-shadow: var(--m-shadow-raise);\n padding-bottom: calc(env(safe-area-inset-bottom, 0px) + 10px);\n animation: sheet-up 0.22s ease;\n}\n\n@keyframes sheet-up {\n from {\n transform: translateY(40px);\n opacity: 0.6;\n }\n to {\n transform: translateY(0);\n opacity: 1;\n }\n}\n\n.sheet-handle {\n flex: none;\n align-self: center;\n width: 36px;\n height: 4px;\n margin: 8px 0 4px;\n border-radius: 999px;\n background: var(--m-border);\n}\n\n.sheet-title {\n flex: none;\n padding: 6px 16px 10px;\n font-size: 15px;\n font-weight: 600;\n}\n\n.sheet-body {\n flex: 1;\n min-height: 0;\n overflow-y: auto;\n padding: 0 12px 8px;\n}\n\n.sheet-status {\n padding: 18px 8px;\n color: var(--m-text-tertiary);\n font-size: 13px;\n text-align: center;\n display: flex;\n flex-direction: column;\n align-items: center;\n gap: 8px;\n}\n\n.sheet-status-error {\n color: var(--m-danger);\n}\n\n.sheet-hint {\n margin: 0;\n color: var(--m-text-secondary);\n font-size: 12px;\n line-height: 1.5;\n max-width: 32em;\n}\n\n.sheet-error {\n margin: 0 0 8px;\n color: var(--m-danger);\n font-size: 12.5px;\n}\n\n.sheet-section {\n margin-bottom: 14px;\n}\n\n.sheet-section-title {\n padding: 4px 8px 6px;\n color: var(--m-text-tertiary);\n font-size: 12px;\n font-weight: 600;\n}\n\n.sheet-option {\n display: flex;\n align-items: center;\n gap: 10px;\n width: 100%;\n padding: 10px 12px;\n border: 1px solid transparent;\n border-radius: 10px;\n background: transparent;\n color: var(--m-text);\n text-align: left;\n cursor: pointer;\n}\n\n.sheet-option:active {\n background: var(--m-bg-input);\n}\n\n.sheet-option-selected {\n border-color: var(--m-accent);\n background: color-mix(in srgb, var(--m-accent) 12%, transparent);\n}\n\n.sheet-option:disabled {\n opacity: 0.5;\n}\n\n.sheet-option-copy {\n flex: 1;\n min-width: 0;\n display: flex;\n flex-direction: column;\n gap: 2px;\n}\n\n.sheet-option-title {\n font-size: 14px;\n}\n\n.sheet-option-desc {\n color: var(--m-text-tertiary);\n font-size: 12px;\n line-height: 1.4;\n}\n\n.sheet-option-check {\n flex: none;\n color: var(--m-accent);\n font-size: 15px;\n font-weight: 700;\n}\n\n.sheet-confirm-desc {\n margin: 0 4px 14px;\n color: var(--m-text-secondary);\n font-size: 13px;\n line-height: 1.6;\n}\n\n.sheet-confirm-actions {\n display: flex;\n gap: 10px;\n padding: 4px;\n}\n\n.sheet-confirm-actions .mobile-button {\n flex: 1;\n}\n\n.sheet-confirm-danger {\n flex: 1;\n height: 38px;\n border: none;\n border-radius: 10px;\n background: var(--m-danger);\n color: #fff;\n font-size: 14px;\n cursor: pointer;\n}\n\n.sheet-confirm-danger:disabled {\n opacity: 0.5;\n}\n\n.chat-meta {\n margin-top: 4px;\n color: var(--m-text-tertiary);\n font-size: 11px;\n}\n\n.chat-typing {\n color: var(--m-text-tertiary);\n font-size: 13px;\n padding: 4px 2px;\n}\n\n.chat-inputbar {\n display: flex;\n gap: 8px;\n padding: 10px 12px calc(env(safe-area-inset-bottom, 0px) + 10px);\n border-top: 1px solid var(--m-border);\n background: var(--m-bg);\n}\n\n.chat-input {\n flex: 1;\n min-width: 0;\n min-height: 40px;\n max-height: 120px;\n /* Grow with the content (still bounded by min/max-height); browsers\n without field-sizing support ignore this line and keep the old size. */\n field-sizing: content;\n padding: 9px 12px;\n border: 1px solid var(--m-border);\n border-radius: 10px;\n background: var(--m-bg-input);\n color: var(--m-text);\n font: inherit;\n font-size: 14.5px;\n resize: none;\n outline: none;\n}\n\n.chat-input:focus {\n border-color: var(--m-accent);\n}\n\n.chat-send {\n flex: none;\n align-self: flex-end;\n height: 40px;\n padding: 0 16px;\n border: none;\n border-radius: 10px;\n background: var(--m-accent);\n color: #fff;\n font-size: 14px;\n cursor: pointer;\n}\n\n.chat-send:disabled {\n opacity: 0.5;\n}\n\n.chat-load-older {\n align-self: center;\n margin: 4px 0;\n border: none;\n background: transparent;\n color: var(--m-text-secondary);\n font-size: 13px;\n cursor: pointer;\n padding: 6px 10px;\n}\n\n/* Touch surfaces have no hover, so keyboard focus must be fully visible.\n One shared ring keeps every interactive control consistent and readable\n on both palettes. */\n.mobile-row:focus-visible,\n.mobile-button:focus-visible,\n.mobile-new:focus-visible,\n.chat-send:focus-visible,\n.chat-chip:focus-visible,\n.chat-msg-toggle:focus-visible,\n.chat-load-older:focus-visible,\n.chat-disclosure-head:focus-visible,\n.sheet-option:focus-visible,\n.sheet-confirm-danger:focus-visible,\n.chat-input:focus-visible {\n outline: none;\n box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);\n}\n\n/* Accessibility: collapse every interaction/enter animation for\n reduced-motion users; surface layout itself stays static. */\n@media (prefers-reduced-motion: reduce) {\n .mobile-theme-toggle,\n .mobile-back,\n .mobile-row,\n .mobile-button,\n .mobile-new,\n .chat-send,\n .chat-chip,\n .chat-msg-toggle,\n .chat-load-older,\n .chat-disclosure-head,\n .chat-disclosure-caret,\n .chat-msg-pending .chat-msg-text::after,\n .sheet-backdrop,\n .sheet,\n .sheet-option,\n .sheet-confirm-danger,\n .chat-input {\n animation: none;\n transition: none;\n }\n}\n";
|
|
6
6
|
//# sourceMappingURL=mobile-styles.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-styles.d.ts","sourceRoot":"","sources":["../../../src/mobile/mobile-styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,SAAS,
|
|
1
|
+
{"version":3,"file":"mobile-styles.d.ts","sourceRoot":"","sources":["../../../src/mobile/mobile-styles.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,eAAO,MAAM,SAAS,4nlBAm1BrB,CAAA"}
|
|
@@ -765,6 +765,9 @@ body {
|
|
|
765
765
|
min-width: 0;
|
|
766
766
|
min-height: 40px;
|
|
767
767
|
max-height: 120px;
|
|
768
|
+
/* Grow with the content (still bounded by min/max-height); browsers
|
|
769
|
+
without field-sizing support ignore this line and keep the old size. */
|
|
770
|
+
field-sizing: content;
|
|
768
771
|
padding: 9px 12px;
|
|
769
772
|
border: 1px solid var(--m-border);
|
|
770
773
|
border-radius: 10px;
|
|
@@ -25,6 +25,8 @@ export interface RenderMessage {
|
|
|
25
25
|
failed?: boolean;
|
|
26
26
|
toolSummary?: string;
|
|
27
27
|
}
|
|
28
|
+
/** Read the optional workspace target carried from the pairing QR flow. */
|
|
29
|
+
export declare function mobileWorkspaceTarget(search: string): string | undefined;
|
|
28
30
|
/** Map a list row to the surface model; the title comes from projections when present. */
|
|
29
31
|
export declare function toSessionView(item: {
|
|
30
32
|
sessionId: string;
|
|
@@ -54,6 +56,6 @@ export declare function errorText(error: unknown): string;
|
|
|
54
56
|
*/
|
|
55
57
|
export declare function staleHostHint(message: string): string | undefined;
|
|
56
58
|
/** Fetch one history page (tail by default) — thin wrapper so views share the call shape. */
|
|
57
|
-
export declare function loadHistory(sessionId: string, beforeSeq?: number): Promise<import("../api.ts").HistoryPage>;
|
|
59
|
+
export declare function loadHistory(sessionId: string, beforeSeq?: number, signal?: AbortSignal): Promise<import("../api.ts").HistoryPage>;
|
|
58
60
|
export { listSessions, listWorkspaces, prompt };
|
|
59
61
|
//# sourceMappingURL=App.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/mobile/views/App.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAA2B,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAazF,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAA;CACnD,GAAG,WAAW,CAad;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;;GAGG;AACH,wBAAgB,GAAG,
|
|
1
|
+
{"version":3,"file":"App.d.ts","sourceRoot":"","sources":["../../../../src/mobile/views/App.tsx"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,EAA2B,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,WAAW,CAAA;AAazF,yDAAyD;AACzD,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,KAAK,EAAE,MAAM,CAAA;IACb,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;CACf;AAED,wDAAwD;AACxD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,GAAG,WAAW,CAAA;IAC1B,IAAI,EAAE,MAAM,CAAA;IACZ,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,2EAA2E;AAC3E,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAGxE;AAED,0FAA0F;AAC1F,wBAAgB,aAAa,CAAC,IAAI,EAAE;IAClC,SAAS,EAAE,MAAM,CAAA;IACjB,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,OAAO,CAAA;IACd,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,WAAW,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAE,CAAA;CACnD,GAAG,WAAW,CAad;AAED,+CAA+C;AAC/C,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;;GAGG;AACH,wBAAgB,GAAG,gCA4DlB;AAED,qEAAqE;AACrE,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAKhD;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAIjE;AAED,6FAA6F;AAC7F,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,4CAEtF;AAED,OAAO,EAAE,YAAY,EAAE,cAAc,EAAE,MAAM,EAAE,CAAA"}
|