@linxin666/dsh-remote-web-ui 0.1.20 → 0.2.0
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 +2 -2
- package/README.md +41 -19
- package/README.zh.md +17 -12
- package/assets/mobile-icon-192.png +0 -0
- package/assets/mobile-icon-512.png +0 -0
- package/assets/mobile-service-worker.js +64 -0
- package/lib/client.js +37 -10
- package/lib/client.js.map +1 -1
- package/lib/index.js +290 -53
- package/lib/mobile.js +278 -17
- package/lib/mobile.js.map +1 -1
- package/lib/types/client/PluginSettingsCard.d.ts +6 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +5 -3
- package/lib/types/client/RemoteEntry.js +2 -2
- package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
- package/lib/types/client/RemoteSettingsCard.js +1 -1
- package/lib/types/client/UpdateEntry.d.ts.map +1 -1
- package/lib/types/client/UpdateEntry.js +36 -3
- package/lib/types/client/deep-link.js +1 -1
- package/lib/types/client/locales.d.ts +2 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +2 -0
- package/lib/types/gate.d.ts +9 -0
- package/lib/types/gate.d.ts.map +1 -1
- package/lib/types/gate.js +15 -4
- package/lib/types/index.d.ts +25 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/index.js +30 -13
- package/lib/types/mobile/PairRequiredView.d.ts +7 -0
- package/lib/types/mobile/PairRequiredView.d.ts.map +1 -0
- package/lib/types/mobile/PairRequiredView.js +28 -0
- package/lib/types/mobile/PairRequiredView.test.d.ts +2 -0
- package/lib/types/mobile/PairRequiredView.test.d.ts.map +1 -0
- package/lib/types/mobile/PairRequiredView.test.js +24 -0
- package/lib/types/mobile/index.js +14 -1
- 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 +48 -0
- package/lib/types/mobile/pairing.d.ts +31 -0
- package/lib/types/mobile/pairing.d.ts.map +1 -0
- package/lib/types/mobile/pairing.js +62 -0
- package/lib/types/mobile/views/App.d.ts +12 -5
- package/lib/types/mobile/views/App.d.ts.map +1 -1
- package/lib/types/mobile/views/App.js +34 -7
- package/lib/types/mobile/views/App.test.d.ts +2 -0
- package/lib/types/mobile/views/App.test.d.ts.map +1 -0
- package/lib/types/mobile/views/App.test.js +52 -0
- package/lib/types/mobile-pwa.d.ts +6 -0
- package/lib/types/mobile-pwa.d.ts.map +1 -0
- package/lib/types/mobile-pwa.js +17 -0
- package/lib/types/mobile-routes.d.ts +5 -8
- package/lib/types/mobile-routes.d.ts.map +1 -1
- package/lib/types/mobile-routes.js +115 -30
- package/lib/types/pairing-access.d.ts +28 -0
- package/lib/types/pairing-access.d.ts.map +1 -0
- package/lib/types/pairing-access.js +26 -0
- package/lib/types/pairing.d.ts +27 -0
- package/lib/types/pairing.d.ts.map +1 -1
- package/lib/types/pairing.js +68 -0
- package/lib/types/pairing.persist.test.d.ts +2 -0
- package/lib/types/pairing.persist.test.d.ts.map +1 -0
- package/lib/types/pairing.persist.test.js +128 -0
- package/lib/types/routes.js +1 -1
- package/lib/types/update.d.ts +3 -3
- package/lib/types/update.d.ts.map +1 -1
- package/lib/types/update.js +34 -11
- package/package.json +12 -12
- package/src/client/PluginSettingsCard.tsx +10 -0
- package/src/client/RemoteEntry.tsx +2 -2
- package/src/client/RemoteSettingsCard.tsx +1 -0
- package/src/client/UpdateEntry.tsx +36 -4
- package/src/client/deep-link.ts +1 -1
- package/src/client/locales.ts +2 -0
- package/src/client/remote.module.css +26 -0
- package/src/gate.ts +15 -3
- package/src/index.ts +46 -15
- package/src/mobile/PairRequiredView.test.tsx +28 -0
- package/src/mobile/PairRequiredView.tsx +58 -0
- package/src/mobile/index.tsx +15 -1
- package/src/mobile/mobile-styles.ts +48 -0
- package/src/mobile/pairing.ts +77 -0
- package/src/mobile/views/App.test.tsx +62 -0
- package/src/mobile/views/App.tsx +53 -6
- package/src/mobile-pwa.ts +19 -0
- package/src/mobile-routes.ts +123 -30
- package/src/pairing-access.ts +36 -0
- package/src/pairing.persist.test.ts +145 -0
- package/src/pairing.ts +70 -1
- package/src/routes.ts +1 -1
- package/src/update.ts +40 -12
package/lib/mobile.js
CHANGED
|
@@ -11958,7 +11958,7 @@ function superRefine(fn, params) {
|
|
|
11958
11958
|
return /* @__PURE__ */ _superRefine(fn, params);
|
|
11959
11959
|
}
|
|
11960
11960
|
//#endregion
|
|
11961
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
11961
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/rpc.schema.js
|
|
11962
11962
|
/**
|
|
11963
11963
|
* Message-layer zod schemas: the four wire full forms + error body +
|
|
11964
11964
|
* carrier receipt. The payload slot is unknown in the full-form schemas — business payloads
|
|
@@ -12139,11 +12139,6 @@ const rpcErrorSchema = discriminatedUnion("code", [
|
|
|
12139
12139
|
message: string(),
|
|
12140
12140
|
details: object({ ns: string() })
|
|
12141
12141
|
}),
|
|
12142
|
-
object({
|
|
12143
|
-
code: literal("settings-not-exposed"),
|
|
12144
|
-
message: string(),
|
|
12145
|
-
details: object({ ns: string() })
|
|
12146
|
-
}),
|
|
12147
12142
|
object({
|
|
12148
12143
|
code: literal("settings-conflict"),
|
|
12149
12144
|
message: string(),
|
|
@@ -12272,7 +12267,7 @@ union([object({ accepted: literal(true) }), object({
|
|
|
12272
12267
|
reason: union([literal("not-pending"), literal("bad-response")])
|
|
12273
12268
|
})]);
|
|
12274
12269
|
//#endregion
|
|
12275
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
12270
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/session-search.js
|
|
12276
12271
|
/**
|
|
12277
12272
|
* Return the longest prefix containing at most `maximum` Unicode code points.
|
|
12278
12273
|
* @param value - text to bound.
|
|
@@ -12290,7 +12285,7 @@ function truncateUnicodeCodePoints(value, maximum) {
|
|
|
12290
12285
|
return value;
|
|
12291
12286
|
}
|
|
12292
12287
|
//#endregion
|
|
12293
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
12288
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/sessions.schema.js
|
|
12294
12289
|
/**
|
|
12295
12290
|
* sessions domain zod schemas (names derived from map keys: sessionListRequestSchema /
|
|
12296
12291
|
* sessionListValueSchema). SessionEvent passthrough = strict envelope (type/seq/time) + wide
|
|
@@ -12526,7 +12521,7 @@ object({ accepted: literal(true) });
|
|
|
12526
12521
|
object({ sessionId: sessionIdSchema });
|
|
12527
12522
|
object({ accepted: literal(true) });
|
|
12528
12523
|
//#endregion
|
|
12529
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
12524
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/approvals.schema.js
|
|
12530
12525
|
/**
|
|
12531
12526
|
* approvals domain zod schemas (respond is a client-response; the payload schema serves
|
|
12532
12527
|
* the /api/respond endpoint's second parse after routing via the pending table).
|
|
@@ -12560,7 +12555,7 @@ const taskViewSchema = object({
|
|
|
12560
12555
|
finishedAt: number().int().nonnegative().optional()
|
|
12561
12556
|
});
|
|
12562
12557
|
//#endregion
|
|
12563
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
12558
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/workspace.schema.js
|
|
12564
12559
|
/**
|
|
12565
12560
|
* workspace domain zod schemas (names derived from map keys). The
|
|
12566
12561
|
* WorkspaceId brand cast lives in sessions.schema (see the note there) and
|
|
@@ -12606,7 +12601,7 @@ object({ workspace: workspaceViewSchema });
|
|
|
12606
12601
|
object({ sessionId: sessionIdSchema });
|
|
12607
12602
|
object({ archivedSessionIds: array(sessionIdSchema) });
|
|
12608
12603
|
//#endregion
|
|
12609
|
-
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.
|
|
12604
|
+
//#region ../../node_modules/.pnpm/@deepseek-ai+dsh-host-apiproxy@0.1.0-rc.7_36988429fa7c61ceeac966dddad3597f/node_modules/@deepseek-ai/dsh-host-apiproxy/lib/types/api/events.schema.js
|
|
12610
12605
|
/**
|
|
12611
12606
|
* events domain zod schemas: MuxFrame / HostFrame unions (discriminatedUnion('type')).
|
|
12612
12607
|
* A frame is the payload slot of the ServerRequest full form; the SessionEvent inside
|
|
@@ -15136,6 +15131,154 @@ function WorkspaceView({ initialWorkspaceId, onPick }) {
|
|
|
15136
15131
|
});
|
|
15137
15132
|
}
|
|
15138
15133
|
//#endregion
|
|
15134
|
+
//#region src/mobile/pairing.ts
|
|
15135
|
+
const MOBILE_ROOT = "/m/";
|
|
15136
|
+
/** Parse a pairing token or a copied pairing link without following its origin. */
|
|
15137
|
+
function parseMobilePairInput(value) {
|
|
15138
|
+
const trimmed = value.trim();
|
|
15139
|
+
if (trimmed === "") return void 0;
|
|
15140
|
+
try {
|
|
15141
|
+
const url = new URL(trimmed);
|
|
15142
|
+
const token = url.searchParams.get("pair");
|
|
15143
|
+
if (token === null || token === "") return void 0;
|
|
15144
|
+
const workspaceId = url.searchParams.get("workspace");
|
|
15145
|
+
return {
|
|
15146
|
+
token,
|
|
15147
|
+
...workspaceId !== null && workspaceId !== "" ? { workspaceId } : {}
|
|
15148
|
+
};
|
|
15149
|
+
} catch {
|
|
15150
|
+
return { token: trimmed };
|
|
15151
|
+
}
|
|
15152
|
+
}
|
|
15153
|
+
/** Build the safe, token-free mobile destination after successful pairing. */
|
|
15154
|
+
function mobilePairPath(workspaceId) {
|
|
15155
|
+
return workspaceId === void 0 ? MOBILE_ROOT : "/m/?workspace=" + encodeURIComponent(workspaceId);
|
|
15156
|
+
}
|
|
15157
|
+
/** Accept one pairing token on this exact browser or installed-web-app context. */
|
|
15158
|
+
async function acceptMobilePair(token, fetcher = fetch) {
|
|
15159
|
+
try {
|
|
15160
|
+
const response = await fetcher("/api/pair/accept", {
|
|
15161
|
+
method: "POST",
|
|
15162
|
+
headers: { "content-type": "application/json" },
|
|
15163
|
+
body: JSON.stringify({ token })
|
|
15164
|
+
});
|
|
15165
|
+
if (response.ok) return { ok: true };
|
|
15166
|
+
if (response.status === 404) return {
|
|
15167
|
+
ok: false,
|
|
15168
|
+
message: "配对链接无效或已过期。"
|
|
15169
|
+
};
|
|
15170
|
+
if (response.status === 409) return {
|
|
15171
|
+
ok: false,
|
|
15172
|
+
message: "配对链接已被使用。"
|
|
15173
|
+
};
|
|
15174
|
+
return {
|
|
15175
|
+
ok: false,
|
|
15176
|
+
message: "此设备无法使用该配对链接。"
|
|
15177
|
+
};
|
|
15178
|
+
} catch {
|
|
15179
|
+
return {
|
|
15180
|
+
ok: false,
|
|
15181
|
+
message: "无法连接到配对服务。"
|
|
15182
|
+
};
|
|
15183
|
+
}
|
|
15184
|
+
}
|
|
15185
|
+
/** Consume a QR pairing token before the mobile application starts making RPC calls. */
|
|
15186
|
+
async function consumeMobilePairUrl(href, fetcher = fetch) {
|
|
15187
|
+
let url;
|
|
15188
|
+
try {
|
|
15189
|
+
url = new URL(href);
|
|
15190
|
+
} catch {
|
|
15191
|
+
return { kind: "none" };
|
|
15192
|
+
}
|
|
15193
|
+
const token = url.searchParams.get("pair");
|
|
15194
|
+
if (token === null || token === "") return { kind: "none" };
|
|
15195
|
+
const workspaceId = url.searchParams.get("workspace");
|
|
15196
|
+
const path = mobilePairPath(workspaceId === null || workspaceId === "" ? void 0 : workspaceId);
|
|
15197
|
+
const result = await acceptMobilePair(token, fetcher);
|
|
15198
|
+
return result.ok ? {
|
|
15199
|
+
kind: "accepted",
|
|
15200
|
+
path
|
|
15201
|
+
} : {
|
|
15202
|
+
kind: "failed",
|
|
15203
|
+
path,
|
|
15204
|
+
message: result.message
|
|
15205
|
+
};
|
|
15206
|
+
}
|
|
15207
|
+
//#endregion
|
|
15208
|
+
//#region src/mobile/PairRequiredView.tsx
|
|
15209
|
+
/** Device-pairing gate for installed mobile web-app contexts with isolated storage. */
|
|
15210
|
+
/** Collect a fresh desktop-issued pairing link when this client has no paired cookie. */
|
|
15211
|
+
function PairRequiredView({ initialError, onPaired }) {
|
|
15212
|
+
const [value, setValue] = (0, import_react.useState)("");
|
|
15213
|
+
const [error, setError] = (0, import_react.useState)(initialError);
|
|
15214
|
+
const [submitting, setSubmitting] = (0, import_react.useState)(false);
|
|
15215
|
+
const submit = async (event) => {
|
|
15216
|
+
event.preventDefault();
|
|
15217
|
+
const input = parseMobilePairInput(value);
|
|
15218
|
+
if (input === void 0) {
|
|
15219
|
+
setError("请输入有效的配对链接。");
|
|
15220
|
+
return;
|
|
15221
|
+
}
|
|
15222
|
+
setSubmitting(true);
|
|
15223
|
+
setError(void 0);
|
|
15224
|
+
const result = await acceptMobilePair(input.token);
|
|
15225
|
+
setSubmitting(false);
|
|
15226
|
+
if (!result.ok) {
|
|
15227
|
+
setError(result.message);
|
|
15228
|
+
return;
|
|
15229
|
+
}
|
|
15230
|
+
onPaired(mobilePairPath(input.workspaceId));
|
|
15231
|
+
};
|
|
15232
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
|
|
15233
|
+
className: "mobile mobile-pair",
|
|
15234
|
+
"aria-labelledby": "mobile-pair-title",
|
|
15235
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("form", {
|
|
15236
|
+
className: "mobile-pairCard",
|
|
15237
|
+
onSubmit: (event) => {
|
|
15238
|
+
submit(event);
|
|
15239
|
+
},
|
|
15240
|
+
children: [
|
|
15241
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("h1", {
|
|
15242
|
+
id: "mobile-pair-title",
|
|
15243
|
+
className: "mobile-title",
|
|
15244
|
+
children: "设备配对"
|
|
15245
|
+
}),
|
|
15246
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15247
|
+
className: "mobile-muted",
|
|
15248
|
+
children: "粘贴桌面端复制的配对链接以连接此设备。"
|
|
15249
|
+
}),
|
|
15250
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("label", {
|
|
15251
|
+
className: "mobile-pairLabel",
|
|
15252
|
+
htmlFor: "mobile-pair-link",
|
|
15253
|
+
children: "配对链接"
|
|
15254
|
+
}),
|
|
15255
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", {
|
|
15256
|
+
id: "mobile-pair-link",
|
|
15257
|
+
className: "mobile-pairInput",
|
|
15258
|
+
value,
|
|
15259
|
+
onChange: (event) => {
|
|
15260
|
+
setValue(event.target.value);
|
|
15261
|
+
},
|
|
15262
|
+
autoComplete: "off",
|
|
15263
|
+
autoCapitalize: "off",
|
|
15264
|
+
spellCheck: false
|
|
15265
|
+
}),
|
|
15266
|
+
error === void 0 ? null : /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15267
|
+
className: "mobile-error",
|
|
15268
|
+
role: "alert",
|
|
15269
|
+
children: error
|
|
15270
|
+
}),
|
|
15271
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15272
|
+
className: "mobile-new mobile-pairSubmit",
|
|
15273
|
+
type: "submit",
|
|
15274
|
+
disabled: submitting,
|
|
15275
|
+
children: submitting ? "正在配对" : "配对"
|
|
15276
|
+
})
|
|
15277
|
+
]
|
|
15278
|
+
})
|
|
15279
|
+
});
|
|
15280
|
+
}
|
|
15281
|
+
//#endregion
|
|
15139
15282
|
//#region src/mobile/views/App.tsx
|
|
15140
15283
|
/**
|
|
15141
15284
|
* Mobile surface root: the view state machine (workspaces → sessions →
|
|
@@ -15172,11 +15315,60 @@ function formatTime(epochMs) {
|
|
|
15172
15315
|
if (date.toDateString() === yesterday.toDateString()) return `昨天 ${clock}`;
|
|
15173
15316
|
return `${String(date.getMonth() + 1)}月${String(date.getDate())}日 ${clock}`;
|
|
15174
15317
|
}
|
|
15175
|
-
/**
|
|
15176
|
-
|
|
15177
|
-
|
|
15178
|
-
|
|
15179
|
-
|
|
15318
|
+
/** Whether the mobile gateway rejected this browser for lack of a paired cookie. */
|
|
15319
|
+
function isUnpairedMobileError(error) {
|
|
15320
|
+
return error instanceof RpcTransportError && error.message === "HTTP 403";
|
|
15321
|
+
}
|
|
15322
|
+
/** Classify an initial mobile preference failure without treating outage as authorization. */
|
|
15323
|
+
function mobilePairStateForError(error) {
|
|
15324
|
+
return isUnpairedMobileError(error) ? "unpaired" : "unavailable";
|
|
15325
|
+
}
|
|
15326
|
+
/** Gate the independent mobile bundle until its own browser context is paired. */
|
|
15327
|
+
function App({ initialPairError }) {
|
|
15328
|
+
const [pairState, setPairState] = (0, import_react.useState)("checking");
|
|
15329
|
+
(0, import_react.useEffect)(() => {
|
|
15330
|
+
let current = true;
|
|
15331
|
+
fetchMobilePreferences().then(() => {
|
|
15332
|
+
if (current) setPairState("paired");
|
|
15333
|
+
}, (error) => {
|
|
15334
|
+
if (current) setPairState(mobilePairStateForError(error));
|
|
15335
|
+
});
|
|
15336
|
+
return () => {
|
|
15337
|
+
current = false;
|
|
15338
|
+
};
|
|
15339
|
+
}, []);
|
|
15340
|
+
if (pairState === "checking") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("main", {
|
|
15341
|
+
className: "mobile mobile-empty",
|
|
15342
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15343
|
+
className: "mobile-muted",
|
|
15344
|
+
children: "正在连接..."
|
|
15345
|
+
})
|
|
15346
|
+
});
|
|
15347
|
+
if (pairState === "unpaired") return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PairRequiredView, {
|
|
15348
|
+
initialError: initialPairError,
|
|
15349
|
+
onPaired: (path) => {
|
|
15350
|
+
window.location.replace(path);
|
|
15351
|
+
}
|
|
15352
|
+
});
|
|
15353
|
+
if (pairState === "unavailable") return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("main", {
|
|
15354
|
+
className: "mobile mobile-empty",
|
|
15355
|
+
children: [/* @__PURE__ */ (0, import_jsx_runtime.jsx)("p", {
|
|
15356
|
+
className: "mobile-error",
|
|
15357
|
+
role: "alert",
|
|
15358
|
+
children: "无法连接到运行中的 DSH host。"
|
|
15359
|
+
}), /* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", {
|
|
15360
|
+
className: "mobile-new",
|
|
15361
|
+
type: "button",
|
|
15362
|
+
onClick: () => {
|
|
15363
|
+
window.location.reload();
|
|
15364
|
+
},
|
|
15365
|
+
children: "重试"
|
|
15366
|
+
})]
|
|
15367
|
+
});
|
|
15368
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(PairedApp, {});
|
|
15369
|
+
}
|
|
15370
|
+
/** The existing remote mobile surface, mounted only after device pairing succeeds. */
|
|
15371
|
+
function PairedApp() {
|
|
15180
15372
|
const [route, setRoute] = (0, import_react.useState)({ kind: "workspaces" });
|
|
15181
15373
|
const [initialWorkspaceId, setInitialWorkspaceId] = (0, import_react.useState)(() => mobileWorkspaceTarget(window.location.search));
|
|
15182
15374
|
const muxRef = (0, import_react.useRef)(void 0);
|
|
@@ -16283,7 +16475,66 @@ body {
|
|
|
16283
16475
|
transition: none;
|
|
16284
16476
|
}
|
|
16285
16477
|
}
|
|
16478
|
+
|
|
16479
|
+
/* ── installed-app pairing ───────────────────────────────────────────── */
|
|
16480
|
+
|
|
16481
|
+
.mobile-pair {
|
|
16482
|
+
min-height: 100dvh;
|
|
16483
|
+
display: grid;
|
|
16484
|
+
place-items: center;
|
|
16485
|
+
padding: 24px;
|
|
16486
|
+
}
|
|
16487
|
+
|
|
16488
|
+
.mobile-pairCard {
|
|
16489
|
+
display: grid;
|
|
16490
|
+
width: min(100%, 420px);
|
|
16491
|
+
gap: 12px;
|
|
16492
|
+
padding: 24px;
|
|
16493
|
+
border: 1px solid var(--m-border);
|
|
16494
|
+
border-radius: 12px;
|
|
16495
|
+
background: var(--m-bg-raised);
|
|
16496
|
+
box-shadow: var(--m-shadow-raise);
|
|
16497
|
+
}
|
|
16498
|
+
|
|
16499
|
+
.mobile-pairLabel {
|
|
16500
|
+
color: var(--m-text-secondary);
|
|
16501
|
+
font-size: 13px;
|
|
16502
|
+
font-weight: 600;
|
|
16503
|
+
}
|
|
16504
|
+
|
|
16505
|
+
.mobile-pairInput {
|
|
16506
|
+
width: 100%;
|
|
16507
|
+
min-height: 42px;
|
|
16508
|
+
box-sizing: border-box;
|
|
16509
|
+
padding: 9px 12px;
|
|
16510
|
+
border: 1px solid var(--m-border);
|
|
16511
|
+
border-radius: 8px;
|
|
16512
|
+
background: var(--m-bg-input);
|
|
16513
|
+
color: var(--m-text);
|
|
16514
|
+
font: inherit;
|
|
16515
|
+
outline: none;
|
|
16516
|
+
}
|
|
16517
|
+
|
|
16518
|
+
.mobile-pairInput:focus-visible {
|
|
16519
|
+
border-color: var(--m-accent);
|
|
16520
|
+
box-shadow: 0 0 0 2px var(--m-bg), 0 0 0 4px var(--m-accent);
|
|
16521
|
+
}
|
|
16522
|
+
|
|
16523
|
+
.mobile-pairSubmit {
|
|
16524
|
+
width: 100%;
|
|
16525
|
+
}
|
|
16286
16526
|
`;
|
|
16527
|
+
const MOBILE_SERVICE_WORKER_URL = "/m/service-worker.js";
|
|
16528
|
+
/** Register the mobile shell worker when the current browser supports it. */
|
|
16529
|
+
async function registerMobilePwa(serviceWorker = typeof navigator === "undefined" ? void 0 : navigator.serviceWorker) {
|
|
16530
|
+
if (serviceWorker === void 0) return;
|
|
16531
|
+
try {
|
|
16532
|
+
await serviceWorker.register(MOBILE_SERVICE_WORKER_URL, {
|
|
16533
|
+
scope: "/m/",
|
|
16534
|
+
updateViaCache: "none"
|
|
16535
|
+
});
|
|
16536
|
+
} catch {}
|
|
16537
|
+
}
|
|
16287
16538
|
//#endregion
|
|
16288
16539
|
//#region src/mobile/index.tsx
|
|
16289
16540
|
/**
|
|
@@ -16301,7 +16552,17 @@ style.textContent = mobileCss;
|
|
|
16301
16552
|
document.head.appendChild(style);
|
|
16302
16553
|
const root = document.getElementById("root");
|
|
16303
16554
|
if (root === null) throw new Error("mobile: #root missing");
|
|
16304
|
-
(
|
|
16555
|
+
bootMobile(root);
|
|
16556
|
+
async function bootMobile(mount) {
|
|
16557
|
+
const pair = await consumeMobilePairUrl(window.location.href);
|
|
16558
|
+
if (pair.kind === "accepted") {
|
|
16559
|
+
window.location.replace(pair.path);
|
|
16560
|
+
return;
|
|
16561
|
+
}
|
|
16562
|
+
if (pair.kind === "failed") window.history.replaceState(null, "", pair.path);
|
|
16563
|
+
registerMobilePwa();
|
|
16564
|
+
(0, import_client.createRoot)(mount).render(/* @__PURE__ */ (0, import_jsx_runtime.jsx)(App, { initialPairError: pair.kind === "failed" ? pair.message : void 0 }));
|
|
16565
|
+
}
|
|
16305
16566
|
//#endregion
|
|
16306
16567
|
|
|
16307
16568
|
//# sourceMappingURL=mobile.js.map
|