@linxin666/dsh-remote-web-ui 0.1.11 → 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 +170 -99
- 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/index.d.ts +13 -1
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +2 -1
- 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 +2 -12
- 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/index.ts +15 -2
- 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
package/lib/types/mobile-api.js
CHANGED
|
@@ -32,6 +32,12 @@ const MOBILE_ALLOWLIST = new Set([
|
|
|
32
32
|
'session.selectModel',
|
|
33
33
|
'session.rename',
|
|
34
34
|
]);
|
|
35
|
+
/**
|
|
36
|
+
* Locally answered display-preference method (the phone's read-only
|
|
37
|
+
* surface preferences; never proxied to the host ApiProxy and never a
|
|
38
|
+
* settings-domain write).
|
|
39
|
+
*/
|
|
40
|
+
const MOBILE_PREFERENCES_METHOD = 'mobile.preferences';
|
|
35
41
|
/** One session.list page (thin phones load incrementally). */
|
|
36
42
|
const SESSION_PAGE_SIZE = 20;
|
|
37
43
|
/** Encode one list position as an opaque continuation cursor. */
|
|
@@ -67,7 +73,7 @@ const MOBILE_API_METHOD_PREFIX = `${MOBILE_API_PREFIX}/`;
|
|
|
67
73
|
* @returns the routes to register on webServer.
|
|
68
74
|
*/
|
|
69
75
|
export function makeMobileApiRoutes(deps) {
|
|
70
|
-
const { service, apiProxy } = deps;
|
|
76
|
+
const { service, apiProxy, mobileEnterToSend } = deps;
|
|
71
77
|
/** The phone gate: a live paired-device cookie, or nothing else proceeds. */
|
|
72
78
|
const gateOk = (req) => {
|
|
73
79
|
const deviceId = readCookie(req.headers.cookie, service.config.cookieName);
|
|
@@ -93,7 +99,8 @@ export function makeMobileApiRoutes(deps) {
|
|
|
93
99
|
return;
|
|
94
100
|
}
|
|
95
101
|
const method = pathname.slice(MOBILE_API_METHOD_PREFIX.length);
|
|
96
|
-
|
|
102
|
+
const local = method === MOBILE_PREFERENCES_METHOD;
|
|
103
|
+
if (!MOBILE_ALLOWLIST.has(method) && !local) {
|
|
97
104
|
writeJson(res, 403, { ok: false, error: { code: 'forbidden', message: `method ${method} is not exposed to the mobile surface` } });
|
|
98
105
|
return;
|
|
99
106
|
}
|
|
@@ -111,6 +118,14 @@ export function makeMobileApiRoutes(deps) {
|
|
|
111
118
|
writeJson(res, 400, { ok: false, error: { code: 'bad-request', message: 'missing rpcId' } });
|
|
112
119
|
return;
|
|
113
120
|
}
|
|
121
|
+
if (local) {
|
|
122
|
+
writeJson(res, 200, {
|
|
123
|
+
type: 'server-response',
|
|
124
|
+
rpcId,
|
|
125
|
+
result: { ok: true, value: { mobileEnterToSend: mobileEnterToSend() } },
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
114
129
|
try {
|
|
115
130
|
const response = await dispatch(apiProxy, method, parsed?.payload, rpcId);
|
|
116
131
|
writeJson(res, 200, response);
|
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.13",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
7
7
|
"node": "^22.19.0 || >=24.0.0"
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"src",
|
|
33
33
|
"cordis.patch.yml"
|
|
34
34
|
],
|
|
35
|
-
"license": "
|
|
35
|
+
"license": "Apache-2.0",
|
|
36
36
|
"dsh": {
|
|
37
37
|
"bundle": {
|
|
38
38
|
"patch": "./cordis.patch.yml"
|
|
@@ -55,16 +55,6 @@
|
|
|
55
55
|
"zod": "^4.4.3"
|
|
56
56
|
},
|
|
57
57
|
"peerDependencies": {
|
|
58
|
-
"@deepseek-ai/dsh-client-connection": "^0.1.0-rc.6",
|
|
59
|
-
"@deepseek-ai/dsh-client-locale": "^0.1.0-rc.6",
|
|
60
|
-
"@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
|
|
61
|
-
"@deepseek-ai/dsh-client-ui-primitives": "^0.1.0-rc.6",
|
|
62
|
-
"@deepseek-ai/dsh-client-ui-settings": "^0.1.0-rc.6",
|
|
63
|
-
"@deepseek-ai/dsh-client-ui-sidebar": "^0.1.0-rc.6",
|
|
64
|
-
"@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
|
|
65
|
-
"@deepseek-ai/dsh-host-webserver": "^0.1.0-rc.6",
|
|
66
|
-
"@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
|
|
67
|
-
"@deepseek-ai/dsh-settings": "^0.1.0-rc.6",
|
|
68
58
|
"react": "^18.2.0",
|
|
69
59
|
"react-dom": "^18.2.0"
|
|
70
60
|
},
|
|
@@ -41,13 +41,14 @@ 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
|
+
const cardClass = open ? `${css.cardOpen} ${css.card}` : css.card
|
|
44
45
|
// The namespace exists but the Host does not serve it to this client (the
|
|
45
46
|
// official settings allowlist omits third-party namespaces): show a card
|
|
46
47
|
// that explains the gap instead of vanishing, so a missing card never
|
|
47
48
|
// reads as a missing plugin.
|
|
48
49
|
if (!state.exposed) {
|
|
49
50
|
return (
|
|
50
|
-
<li className={
|
|
51
|
+
<li className={cardClass}>
|
|
51
52
|
<button
|
|
52
53
|
type="button"
|
|
53
54
|
className={css.header}
|
|
@@ -59,7 +60,19 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
59
60
|
<span className={css.name}>{title}</span>
|
|
60
61
|
<span className={css.description}>{props.t(props.descriptionKey)}</span>
|
|
61
62
|
</span>
|
|
62
|
-
<
|
|
63
|
+
<svg
|
|
64
|
+
width="14"
|
|
65
|
+
height="14"
|
|
66
|
+
viewBox="0 0 14 14"
|
|
67
|
+
fill="none"
|
|
68
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
69
|
+
className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}
|
|
70
|
+
>
|
|
71
|
+
<path
|
|
72
|
+
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"
|
|
73
|
+
fill="currentColor"
|
|
74
|
+
/>
|
|
75
|
+
</svg>
|
|
63
76
|
</button>
|
|
64
77
|
{open
|
|
65
78
|
? (
|
|
@@ -72,7 +85,7 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
72
85
|
)
|
|
73
86
|
}
|
|
74
87
|
return (
|
|
75
|
-
<li className={
|
|
88
|
+
<li className={cardClass}>
|
|
76
89
|
<button
|
|
77
90
|
type="button"
|
|
78
91
|
className={css.header}
|
|
@@ -85,7 +98,19 @@ export function PluginSettingsCard(props: PluginSettingsCardProps) {
|
|
|
85
98
|
<span className={css.description}>{props.t(props.descriptionKey)}</span>
|
|
86
99
|
</span>
|
|
87
100
|
{state.dirty ? <span className={css.pending}>{props.t('settings.unsaved')}</span> : null}
|
|
88
|
-
<
|
|
101
|
+
<svg
|
|
102
|
+
width="14"
|
|
103
|
+
height="14"
|
|
104
|
+
viewBox="0 0 14 14"
|
|
105
|
+
fill="none"
|
|
106
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
107
|
+
className={open ? `${css.chevron} ${css.chevronOpen}` : css.chevron}
|
|
108
|
+
>
|
|
109
|
+
<path
|
|
110
|
+
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"
|
|
111
|
+
fill="currentColor"
|
|
112
|
+
/>
|
|
113
|
+
</svg>
|
|
89
114
|
</button>
|
|
90
115
|
{open
|
|
91
116
|
? (
|
|
@@ -234,4 +259,4 @@ export function BooleanField(props: FieldProps & {
|
|
|
234
259
|
<p className={css.hint}>{props.hint}</p>
|
|
235
260
|
</div>
|
|
236
261
|
)
|
|
237
|
-
}
|
|
262
|
+
}
|
|
@@ -27,6 +27,8 @@ export interface RemoteSettings {
|
|
|
27
27
|
publicBaseUrl?: string
|
|
28
28
|
/** When on, the plugin runs its own Cloudflare quick tunnel automatically. */
|
|
29
29
|
autoTunnel?: boolean
|
|
30
|
+
/** Mobile composer: plain Enter sends; off means Enter inserts a newline. */
|
|
31
|
+
mobileEnterToSend?: boolean
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/** What the remote-control card renders. */
|
|
@@ -47,6 +49,8 @@ export interface RemoteSettingsCardState extends CardShell {
|
|
|
47
49
|
publicBaseUrl: CardFieldState
|
|
48
50
|
/** Auto public tunnel switch. */
|
|
49
51
|
autoTunnel: CardFieldState
|
|
52
|
+
/** Mobile composer Enter-to-send switch. */
|
|
53
|
+
mobileEnterToSend: CardFieldState
|
|
50
54
|
}
|
|
51
55
|
|
|
52
56
|
/** The registration-side face the card's slot entry injects. */
|
|
@@ -73,6 +77,7 @@ export class RemoteSettingsCardController {
|
|
|
73
77
|
booleanField('requirePairingForLan'),
|
|
74
78
|
textField('publicBaseUrl'),
|
|
75
79
|
booleanField('autoTunnel'),
|
|
80
|
+
booleanField('mobileEnterToSend'),
|
|
76
81
|
])
|
|
77
82
|
this.store = this.form.bind(() => this.projection())
|
|
78
83
|
}
|
|
@@ -88,6 +93,7 @@ export class RemoteSettingsCardController {
|
|
|
88
93
|
requirePairingForLan: this.form.field('requirePairingForLan'),
|
|
89
94
|
publicBaseUrl: this.form.field('publicBaseUrl'),
|
|
90
95
|
autoTunnel: this.form.field('autoTunnel'),
|
|
96
|
+
mobileEnterToSend: this.form.field('mobileEnterToSend'),
|
|
91
97
|
}
|
|
92
98
|
}
|
|
93
99
|
|
|
@@ -215,6 +221,18 @@ export function RemoteSettingsCard(props: RemoteSettingsCardProps) {
|
|
|
215
221
|
onEdit={(text) => { props.edit('autoTunnel', text) }}
|
|
216
222
|
onReset={() => { props.resetField('autoTunnel') }}
|
|
217
223
|
/>
|
|
224
|
+
<BooleanField
|
|
225
|
+
id="settings-remote-mobile-enter"
|
|
226
|
+
label={t('settings.mobileEnterToSend')}
|
|
227
|
+
hint={t('settings.mobileEnterToSendHint')}
|
|
228
|
+
inheritLabel={t('settings.inherit')}
|
|
229
|
+
onLabel={t('settings.on')}
|
|
230
|
+
offLabel={t('settings.off')}
|
|
231
|
+
{...fieldProps}
|
|
232
|
+
{...state.mobileEnterToSend}
|
|
233
|
+
onEdit={(text) => { props.edit('mobileEnterToSend', text) }}
|
|
234
|
+
onReset={() => { props.resetField('mobileEnterToSend') }}
|
|
235
|
+
/>
|
|
218
236
|
</PluginSettingsCard>
|
|
219
237
|
)
|
|
220
238
|
}
|
|
@@ -10,7 +10,7 @@ import { createPortal } from "react-dom"
|
|
|
10
10
|
import type { TranslateNS } from '@deepseek-ai/dsh-client-ui-slots'
|
|
11
11
|
import { IconDownloadOutline16 } from '@deepseek-ai/dsh-client-ui-primitives'
|
|
12
12
|
import type { UpdateStatus } from '../update.ts'
|
|
13
|
-
import { fetchUpdateStatus, runUpdate } from "./update-api.ts"
|
|
13
|
+
import { fetchUpdateStatus, runUpdate, UpdateStatusError } from "./update-api.ts"
|
|
14
14
|
import { UpdatePanel, type UpdateView } from "./UpdatePanel.tsx"
|
|
15
15
|
import css from "./remote.module.css"
|
|
16
16
|
|
|
@@ -36,7 +36,15 @@ export function UpdateEntry({ wide, t }: UpdateEntryProps) {
|
|
|
36
36
|
let status: UpdateStatus
|
|
37
37
|
try {
|
|
38
38
|
status = await fetchUpdateStatus()
|
|
39
|
-
} catch {
|
|
39
|
+
} catch (error) {
|
|
40
|
+
// HTTP 404: the update route is not mounted — the host process runs an
|
|
41
|
+
// older plugin build (client refreshed, host did not). Restarting dsh
|
|
42
|
+
// web loads the new plugin; a plain network failure gets the generic
|
|
43
|
+
// offline copy instead of a misleading "cannot reach update source".
|
|
44
|
+
if (error instanceof UpdateStatusError && error.status === 404) {
|
|
45
|
+
setView({ kind: "error", message: t("update.unmounted"), detail: t("update.unmountedDetail") })
|
|
46
|
+
return
|
|
47
|
+
}
|
|
40
48
|
setView({ kind: "error", message: t("update.offline"), detail: t("update.offlineDetail") })
|
|
41
49
|
return
|
|
42
50
|
}
|
package/src/client/deep-link.ts
CHANGED
|
@@ -74,7 +74,7 @@ export function runPairBootFlow(ctx: Context, search: string, page: PageSurface
|
|
|
74
74
|
}
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
-
/** Accept the token, then
|
|
77
|
+
/** Accept the token, then enter the matching desktop/mobile surface. */
|
|
78
78
|
async function runAccept(token: string, page: PageSurface): Promise<void> {
|
|
79
79
|
let ok = false
|
|
80
80
|
try {
|
|
@@ -91,9 +91,15 @@ async function runAccept(token: string, page: PageSurface): Promise<void> {
|
|
|
91
91
|
page.replaceState(`${url.pathname}${url.search}${url.hash}`)
|
|
92
92
|
if (ok) {
|
|
93
93
|
// Phones land on the standalone simplified surface (the full desktop UI
|
|
94
|
-
// is not built for small screens)
|
|
95
|
-
|
|
96
|
-
|
|
94
|
+
// is not built for small screens). Keep the workspace target so the
|
|
95
|
+
// mobile surface can open the intended workspace instead of losing the
|
|
96
|
+
// QR context at this navigation boundary.
|
|
97
|
+
if (isMobileSurface()) {
|
|
98
|
+
url.pathname = '/m'
|
|
99
|
+
page.navigate(`${url.pathname}${url.search}${url.hash}`)
|
|
100
|
+
} else {
|
|
101
|
+
page.reload()
|
|
102
|
+
}
|
|
97
103
|
}
|
|
98
104
|
}
|
|
99
105
|
|
package/src/client/index.ts
CHANGED
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
import { createElement } from 'react'
|
|
10
10
|
import { createRoot } from 'react-dom/client'
|
|
11
|
-
import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client'
|
|
11
|
+
import type { ClientContext, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client'
|
|
12
12
|
// Type-only: pulls the locale plugin's Context merge (ctx.locale) and the
|
|
13
13
|
// ui-sidebar SlotMap merge (the 'sidebar.remote' hole).
|
|
14
14
|
import type {} from '@deepseek-ai/dsh-client-locale/client'
|
|
@@ -68,6 +68,18 @@ export interface SettingsPluginItemOwnerProps {
|
|
|
68
68
|
children?: never
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
+
declare module '@deepseek-ai/cordis' {
|
|
72
|
+
interface Context {
|
|
73
|
+
/**
|
|
74
|
+
* Optional rc.6 compatibility binder provided by dsh-web-ui-settings;
|
|
75
|
+
* absent when that group plugin is not installed, so callers fall back to
|
|
76
|
+
* the official settings scope.
|
|
77
|
+
*/
|
|
78
|
+
webUiSettings?: { bind<S>(spec: SettingsScopeSpec<S>): SettingsScope<S> }
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
71
83
|
/** Dictionary namespace owned by this plugin. */
|
|
72
84
|
const NS = 'remote'
|
|
73
85
|
|
|
@@ -88,7 +100,8 @@ export function apply(ctx: ClientContext): void {
|
|
|
88
100
|
ctx.effect(() => ctx.locale.register(NS, { zh, en }), 'remote-web-ui: dictionaries')
|
|
89
101
|
|
|
90
102
|
const t = ctx.locale.bind(NS)
|
|
91
|
-
const
|
|
103
|
+
const binder = ctx.get('webUiSettings') ?? ctx.settingsScope
|
|
104
|
+
const settingsScope = binder.bind<RemoteSettings>({ namespace: REMOTE_WEB_UI_NS })
|
|
92
105
|
const enabled = (): boolean => {
|
|
93
106
|
const snapshot = settingsScope.getSnapshot()
|
|
94
107
|
return snapshot.status === 'ready'
|
package/src/client/locales.ts
CHANGED
|
@@ -55,6 +55,8 @@ export const zh = {
|
|
|
55
55
|
'settings.publicBaseUrlHint': '内网穿透的公网地址,如 Cloudflare Tunnel:https://xxx.trycloudflare.com。填写后二维码将生成公网链接,手机不在同一网络也能配对;留空则仅局域网可用。',
|
|
56
56
|
'settings.autoTunnel': '自动公网隧道',
|
|
57
57
|
'settings.autoTunnelHint': '开启后插件自动启动 Cloudflare quick tunnel(无需安装任何工具),并自动更新公网地址与信任配置,手机随时可用公网配对;关闭时使用上方手动填写的公网地址。',
|
|
58
|
+
'settings.mobileEnterToSend': '移动端回车发送',
|
|
59
|
+
'settings.mobileEnterToSendHint': '开启:移动端输入框按 Enter 直接发送,Shift+Enter 换行;关闭:Enter 换行,仅点发送按钮发送。',
|
|
58
60
|
'settings.inherit': '继承',
|
|
59
61
|
'settings.on': '开',
|
|
60
62
|
'settings.off': '关',
|
|
@@ -89,6 +91,8 @@ export const zh = {
|
|
|
89
91
|
'update.missingDetail': '无法定位已安装的 dsh-web-ui 聚合包。请先运行 dsh plugin --profile <profile> add @linxin666/dsh-web-ui-all。',
|
|
90
92
|
'update.offline': '无法连接更新源',
|
|
91
93
|
'update.offlineDetail': '网络不可用或更新服务异常,请稍后重试。',
|
|
94
|
+
'update.unmounted': '更新服务未加载',
|
|
95
|
+
'update.unmountedDetail': '当前 dsh web 进程运行的插件版本不含更新服务,请重启 dsh web(Ctrl+C 后重新运行)后再试。',
|
|
92
96
|
'update.error': '更新失败',
|
|
93
97
|
'update.packages': '组件版本',
|
|
94
98
|
'update.retry': '重新检查',
|
|
@@ -162,6 +166,8 @@ export const en = {
|
|
|
162
166
|
'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.',
|
|
163
167
|
'settings.autoTunnel': 'Auto public tunnel',
|
|
164
168
|
'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.',
|
|
169
|
+
'settings.mobileEnterToSend': 'Enter to send on mobile',
|
|
170
|
+
'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.',
|
|
165
171
|
'settings.inherit': 'Inherit',
|
|
166
172
|
'settings.on': 'On',
|
|
167
173
|
'settings.off': 'Off',
|
|
@@ -196,6 +202,8 @@ export const en = {
|
|
|
196
202
|
'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',
|
|
197
203
|
'update.offline': 'Cannot reach the update source',
|
|
198
204
|
'update.offlineDetail': 'The network is unavailable or the update service is failing; try again later.',
|
|
205
|
+
'update.unmounted': 'The update service is not loaded',
|
|
206
|
+
'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.',
|
|
199
207
|
'update.error': 'Update failed',
|
|
200
208
|
'update.packages': 'Component versions',
|
|
201
209
|
'update.retry': 'Check again',
|