@linxin666/dsh-remote-web-ui 0.3.6 → 0.3.9

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.
Files changed (238) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +77 -468
  3. package/README.zh.md +83 -113
  4. package/lib/client.js +955 -203
  5. package/lib/client.js.map +1 -1
  6. package/lib/index.js +1136 -1466
  7. package/lib/types/client/FooterRemoteEntry.d.ts +6 -7
  8. package/lib/types/client/FooterRemoteEntry.d.ts.map +1 -1
  9. package/lib/types/client/FooterRemoteEntry.js +1 -1
  10. package/lib/types/client/RemoteEntry.d.ts +7 -4
  11. package/lib/types/client/RemoteEntry.d.ts.map +1 -1
  12. package/lib/types/client/RemoteEntry.js +7 -10
  13. package/lib/types/client/RemotePanel.d.ts +2 -2
  14. package/lib/types/client/RemotePanel.d.ts.map +1 -1
  15. package/lib/types/client/RemotePanel.js +2 -2
  16. package/lib/types/client/RemoteSettingsCard.d.ts +9 -5
  17. package/lib/types/client/RemoteSettingsCard.d.ts.map +1 -1
  18. package/lib/types/client/RemoteSettingsCard.js +56 -3
  19. package/lib/types/client/deep-link.d.ts +10 -13
  20. package/lib/types/client/deep-link.d.ts.map +1 -1
  21. package/lib/types/client/deep-link.js +14 -74
  22. package/lib/types/client/index.d.ts +2 -6
  23. package/lib/types/client/index.d.ts.map +1 -1
  24. package/lib/types/client/index.js +73 -39
  25. package/lib/types/client/locales.d.ts +26 -12
  26. package/lib/types/client/locales.d.ts.map +1 -1
  27. package/lib/types/client/locales.js +36 -22
  28. package/lib/types/client/mobile-adapt.d.ts +40 -0
  29. package/lib/types/client/mobile-adapt.d.ts.map +1 -0
  30. package/lib/types/client/mobile-adapt.js +967 -0
  31. package/lib/types/client/pair-api.d.ts +24 -6
  32. package/lib/types/client/pair-api.d.ts.map +1 -1
  33. package/lib/types/client/pair-api.js +9 -14
  34. package/lib/types/client/remote-channel.d.ts +3 -0
  35. package/lib/types/client/remote-channel.d.ts.map +1 -1
  36. package/lib/types/client/remote-channel.js +35 -3
  37. package/lib/types/client/settings-form.d.ts +35 -43
  38. package/lib/types/client/settings-form.d.ts.map +1 -1
  39. package/lib/types/client/settings-form.js +60 -53
  40. package/lib/types/firewall.d.ts +75 -0
  41. package/lib/types/firewall.d.ts.map +1 -0
  42. package/lib/types/firewall.js +184 -0
  43. package/lib/types/gate.d.ts +11 -5
  44. package/lib/types/gate.d.ts.map +1 -1
  45. package/lib/types/gate.js +11 -5
  46. package/lib/types/index.d.ts +39 -10
  47. package/lib/types/index.d.ts.map +1 -1
  48. package/lib/types/index.js +206 -58
  49. package/lib/types/inner-auth.d.ts +32 -0
  50. package/lib/types/inner-auth.d.ts.map +1 -0
  51. package/lib/types/inner-auth.js +72 -0
  52. package/lib/types/lan-bind-plan.d.ts +55 -0
  53. package/lib/types/lan-bind-plan.d.ts.map +1 -0
  54. package/lib/types/lan-bind-plan.js +60 -0
  55. package/lib/types/lan-bind.d.ts +69 -0
  56. package/lib/types/lan-bind.d.ts.map +1 -0
  57. package/lib/types/lan-bind.js +128 -0
  58. package/lib/types/loopback-proxy.d.ts +9 -2
  59. package/lib/types/loopback-proxy.d.ts.map +1 -1
  60. package/lib/types/loopback-proxy.js +24 -2
  61. package/lib/types/pairing.d.ts +15 -3
  62. package/lib/types/pairing.d.ts.map +1 -1
  63. package/lib/types/pairing.js +20 -6
  64. package/lib/types/remote-api.d.ts +30 -13
  65. package/lib/types/remote-api.d.ts.map +1 -1
  66. package/lib/types/remote-api.js +55 -42
  67. package/lib/types/remote-channel-boot.d.ts +21 -2
  68. package/lib/types/remote-channel-boot.d.ts.map +1 -1
  69. package/lib/types/remote-channel-boot.js +65 -3
  70. package/lib/types/remote-channel-rules.d.ts +8 -0
  71. package/lib/types/remote-channel-rules.d.ts.map +1 -1
  72. package/lib/types/remote-channel-rules.js +9 -2
  73. package/lib/types/remote-methods.d.ts +45 -11
  74. package/lib/types/remote-methods.d.ts.map +1 -1
  75. package/lib/types/remote-methods.js +56 -27
  76. package/lib/types/remote-presence-pet.d.ts +51 -0
  77. package/lib/types/remote-presence-pet.d.ts.map +1 -0
  78. package/lib/types/remote-presence-pet.js +101 -0
  79. package/lib/types/routes.d.ts +76 -4
  80. package/lib/types/routes.d.ts.map +1 -1
  81. package/lib/types/routes.js +336 -19
  82. package/lib/types/tunnel.d.ts.map +1 -1
  83. package/lib/types/tunnel.js +7 -1
  84. package/package.json +20 -18
  85. package/src/client/FooterRemoteEntry.tsx +9 -10
  86. package/src/client/RemoteEntry.tsx +13 -14
  87. package/src/client/RemotePanel.tsx +5 -16
  88. package/src/client/RemoteSettingsCard.tsx +72 -13
  89. package/src/client/deep-link.ts +14 -75
  90. package/src/client/index.ts +77 -39
  91. package/src/client/locales.ts +36 -22
  92. package/src/client/mobile-adapt.ts +913 -0
  93. package/src/client/pair-api.ts +26 -15
  94. package/src/client/remote-channel.ts +31 -3
  95. package/src/client/settings-form.ts +78 -92
  96. package/src/dsh-home.ts +1 -1
  97. package/src/firewall.ts +230 -0
  98. package/src/gate.ts +11 -5
  99. package/src/index.ts +235 -65
  100. package/src/inner-auth.ts +85 -0
  101. package/src/lan-bind-plan.ts +81 -0
  102. package/src/lan-bind.ts +145 -0
  103. package/src/loopback-proxy.ts +34 -0
  104. package/src/pairing.persist.test.ts +1 -1
  105. package/src/pairing.ts +20 -6
  106. package/src/remote-api.ts +67 -45
  107. package/src/remote-channel-boot.ts +70 -3
  108. package/src/remote-channel-rules.ts +17 -2
  109. package/src/remote-methods.ts +58 -27
  110. package/src/remote-presence-pet.ts +129 -0
  111. package/src/routes.ts +358 -19
  112. package/src/tunnel.ts +7 -1
  113. package/assets/apple-touch-icon.png +0 -0
  114. package/assets/mobile-icon-192.png +0 -0
  115. package/assets/mobile-icon-512.png +0 -0
  116. package/assets/mobile-service-worker.js +0 -63
  117. package/lib/mobile.js +0 -17658
  118. package/lib/mobile.js.map +0 -1
  119. package/lib/types/mobile/PairRequiredView.d.ts +0 -7
  120. package/lib/types/mobile/PairRequiredView.d.ts.map +0 -1
  121. package/lib/types/mobile/PairRequiredView.js +0 -28
  122. package/lib/types/mobile/PairRequiredView.test.d.ts +0 -2
  123. package/lib/types/mobile/PairRequiredView.test.d.ts.map +0 -1
  124. package/lib/types/mobile/PairRequiredView.test.js +0 -24
  125. package/lib/types/mobile/api.d.ts +0 -145
  126. package/lib/types/mobile/api.d.ts.map +0 -1
  127. package/lib/types/mobile/api.js +0 -103
  128. package/lib/types/mobile/index.d.ts +0 -10
  129. package/lib/types/mobile/index.d.ts.map +0 -1
  130. package/lib/types/mobile/index.js +0 -38
  131. package/lib/types/mobile/markdown.d.ts +0 -34
  132. package/lib/types/mobile/markdown.d.ts.map +0 -1
  133. package/lib/types/mobile/markdown.js +0 -285
  134. package/lib/types/mobile/markdown.test.d.ts +0 -2
  135. package/lib/types/mobile/markdown.test.d.ts.map +0 -1
  136. package/lib/types/mobile/markdown.test.js +0 -79
  137. package/lib/types/mobile/messages.d.ts +0 -149
  138. package/lib/types/mobile/messages.d.ts.map +0 -1
  139. package/lib/types/mobile/messages.js +0 -561
  140. package/lib/types/mobile/messages.test.d.ts +0 -2
  141. package/lib/types/mobile/messages.test.d.ts.map +0 -1
  142. package/lib/types/mobile/messages.test.js +0 -356
  143. package/lib/types/mobile/mobile-styles.d.ts +0 -6
  144. package/lib/types/mobile/mobile-styles.d.ts.map +0 -1
  145. package/lib/types/mobile/mobile-styles.js +0 -1438
  146. package/lib/types/mobile/mobile-theme.d.ts +0 -22
  147. package/lib/types/mobile/mobile-theme.d.ts.map +0 -1
  148. package/lib/types/mobile/mobile-theme.js +0 -70
  149. package/lib/types/mobile/mobile-theme.test.d.ts +0 -2
  150. package/lib/types/mobile/mobile-theme.test.d.ts.map +0 -1
  151. package/lib/types/mobile/mobile-theme.test.js +0 -71
  152. package/lib/types/mobile/mux.d.ts +0 -128
  153. package/lib/types/mobile/mux.d.ts.map +0 -1
  154. package/lib/types/mobile/mux.js +0 -305
  155. package/lib/types/mobile/mux.test.d.ts +0 -2
  156. package/lib/types/mobile/mux.test.d.ts.map +0 -1
  157. package/lib/types/mobile/mux.test.js +0 -267
  158. package/lib/types/mobile/pairing.d.ts +0 -31
  159. package/lib/types/mobile/pairing.d.ts.map +0 -1
  160. package/lib/types/mobile/pairing.js +0 -62
  161. package/lib/types/mobile/rpc.d.ts +0 -38
  162. package/lib/types/mobile/rpc.d.ts.map +0 -1
  163. package/lib/types/mobile/rpc.js +0 -83
  164. package/lib/types/mobile/theme-toggle.d.ts +0 -12
  165. package/lib/types/mobile/theme-toggle.d.ts.map +0 -1
  166. package/lib/types/mobile/theme-toggle.js +0 -26
  167. package/lib/types/mobile/views/App.d.ts +0 -57
  168. package/lib/types/mobile/views/App.d.ts.map +0 -1
  169. package/lib/types/mobile/views/App.js +0 -150
  170. package/lib/types/mobile/views/App.test.d.ts +0 -2
  171. package/lib/types/mobile/views/App.test.d.ts.map +0 -1
  172. package/lib/types/mobile/views/App.test.js +0 -52
  173. package/lib/types/mobile/views/ChatView.d.ts +0 -63
  174. package/lib/types/mobile/views/ChatView.d.ts.map +0 -1
  175. package/lib/types/mobile/views/ChatView.js +0 -821
  176. package/lib/types/mobile/views/ChatView.stream.test.d.ts +0 -2
  177. package/lib/types/mobile/views/ChatView.stream.test.d.ts.map +0 -1
  178. package/lib/types/mobile/views/ChatView.stream.test.js +0 -236
  179. package/lib/types/mobile/views/ChatView.test.d.ts +0 -2
  180. package/lib/types/mobile/views/ChatView.test.d.ts.map +0 -1
  181. package/lib/types/mobile/views/ChatView.test.js +0 -718
  182. package/lib/types/mobile/views/SessionListView.d.ts +0 -27
  183. package/lib/types/mobile/views/SessionListView.d.ts.map +0 -1
  184. package/lib/types/mobile/views/SessionListView.js +0 -139
  185. package/lib/types/mobile/views/SessionListView.test.d.ts +0 -2
  186. package/lib/types/mobile/views/SessionListView.test.d.ts.map +0 -1
  187. package/lib/types/mobile/views/SessionListView.test.js +0 -167
  188. package/lib/types/mobile/views/WorkspaceView.d.ts +0 -21
  189. package/lib/types/mobile/views/WorkspaceView.d.ts.map +0 -1
  190. package/lib/types/mobile/views/WorkspaceView.js +0 -93
  191. package/lib/types/mobile/views/WorkspaceView.test.d.ts +0 -2
  192. package/lib/types/mobile/views/WorkspaceView.test.d.ts.map +0 -1
  193. package/lib/types/mobile/views/WorkspaceView.test.js +0 -183
  194. package/lib/types/mobile-api.d.ts +0 -65
  195. package/lib/types/mobile-api.d.ts.map +0 -1
  196. package/lib/types/mobile-api.js +0 -404
  197. package/lib/types/mobile-pending.d.ts +0 -38
  198. package/lib/types/mobile-pending.d.ts.map +0 -1
  199. package/lib/types/mobile-pending.js +0 -59
  200. package/lib/types/mobile-pwa.d.ts +0 -6
  201. package/lib/types/mobile-pwa.d.ts.map +0 -1
  202. package/lib/types/mobile-pwa.js +0 -17
  203. package/lib/types/mobile-routes.d.ts +0 -13
  204. package/lib/types/mobile-routes.d.ts.map +0 -1
  205. package/lib/types/mobile-routes.js +0 -217
  206. package/lib/types/paired-model-catalog.d.ts +0 -17
  207. package/lib/types/paired-model-catalog.d.ts.map +0 -1
  208. package/lib/types/paired-model-catalog.js +0 -311
  209. package/src/mobile/PairRequiredView.test.tsx +0 -28
  210. package/src/mobile/PairRequiredView.tsx +0 -58
  211. package/src/mobile/api.ts +0 -229
  212. package/src/mobile/index.tsx +0 -41
  213. package/src/mobile/markdown.test.ts +0 -91
  214. package/src/mobile/markdown.ts +0 -291
  215. package/src/mobile/messages.test.ts +0 -402
  216. package/src/mobile/messages.ts +0 -685
  217. package/src/mobile/mobile-styles.ts +0 -1439
  218. package/src/mobile/mobile-theme.test.ts +0 -84
  219. package/src/mobile/mobile-theme.ts +0 -76
  220. package/src/mobile/mux.test.ts +0 -314
  221. package/src/mobile/mux.ts +0 -336
  222. package/src/mobile/pairing.ts +0 -77
  223. package/src/mobile/rpc.ts +0 -90
  224. package/src/mobile/theme-toggle.tsx +0 -47
  225. package/src/mobile/views/App.test.tsx +0 -62
  226. package/src/mobile/views/App.tsx +0 -213
  227. package/src/mobile/views/ChatView.stream.test.tsx +0 -268
  228. package/src/mobile/views/ChatView.test.tsx +0 -816
  229. package/src/mobile/views/ChatView.tsx +0 -1342
  230. package/src/mobile/views/SessionListView.test.tsx +0 -207
  231. package/src/mobile/views/SessionListView.tsx +0 -244
  232. package/src/mobile/views/WorkspaceView.test.tsx +0 -227
  233. package/src/mobile/views/WorkspaceView.tsx +0 -245
  234. package/src/mobile-api.ts +0 -418
  235. package/src/mobile-pending.ts +0 -88
  236. package/src/mobile-pwa.ts +0 -19
  237. package/src/mobile-routes.ts +0 -231
  238. package/src/paired-model-catalog.ts +0 -272
@@ -1,16 +1,15 @@
1
1
  /**
2
2
  * Sidebar footer-seat wrapper for the remote-control entry.
3
3
  *
4
- * Current dsh web shells declare `sidebar.footer.action` (the seat beside the
5
- * settings trigger) instead of the legacy `sidebar.remote` seat this plugin
6
- * was written against. The footer seat supplies `{ wide }` but not the
7
- * `useWorkspaces` projection hook, so this wrapper substitutes a
8
- * workspace-agnostic selector: pairing without a deep-linked workspace is
9
- * fully supported by the host `/api/pair` routes.
4
+ * Current dsh web shells declare `sidebar.footer.action` (the seat beside
5
+ * the settings trigger) instead of the legacy `sidebar.remote` seat this
6
+ * plugin was written against. The footer seat supplies `{ wide }`; the
7
+ * pairing link is origin-agnostic, so no workspace source is involved.
10
8
  */
11
9
  import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
12
- /** Entry props: the footer seat's column state + the standard locale seat. */
10
+ /** Entry props: the footer seat's column state and the standard locale seat. */
13
11
  export type FooterRemoteEntryProps = PropsLocale<'remote'> & {
12
+ /** Whether the sidebar renders wide content (false = 56px rail). */
14
13
  wide: boolean;
15
14
  };
16
15
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"FooterRemoteEntry.d.ts","sourceRoot":"","sources":["../../../src/client/FooterRemoteEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAGnE,8EAA8E;AAC9E,MAAM,MAAM,sBAAsB,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAE9E;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAS9D"}
1
+ {"version":3,"file":"FooterRemoteEntry.d.ts","sourceRoot":"","sources":["../../../src/client/FooterRemoteEntry.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAGnE,gFAAgF;AAChF,MAAM,MAAM,sBAAsB,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG;IAC3D,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,sBAAsB,+BAO9D"}
@@ -6,5 +6,5 @@ import { RemoteEntry } from "./RemoteEntry.js";
6
6
  * @returns the entry element tree.
7
7
  */
8
8
  export function FooterRemoteEntry(props) {
9
- return (_jsx(RemoteEntry, { wide: props.wide, useWorkspaces: () => undefined, useSessions: () => undefined, t: props.t }));
9
+ return (_jsx(RemoteEntry, { wide: props.wide, t: props.t }));
10
10
  }
@@ -1,10 +1,13 @@
1
- import type { PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
2
- /** Entry props: the sidebar column state + the standard locale seat. */
3
- export type RemoteEntryProps = PropsRuntime<'sidebar.remote'> & PropsLocale<'remote'>;
1
+ import type { PropsLocale } from '@deepseek-ai/dsh-client-ui-slots';
2
+ /** Entry props: the sidebar column state and the standard locale seat. */
3
+ export type RemoteEntryProps = PropsLocale<'remote'> & {
4
+ /** Whether the sidebar renders wide content (false = 56px rail). */
5
+ wide: boolean;
6
+ };
4
7
  /**
5
8
  * Render the remote-control trigger and panel.
6
9
  * @param props - composed slot props (contract in this package).
7
10
  * @returns the entry element tree.
8
11
  */
9
- export declare function RemoteEntry({ wide, useWorkspaces, t }: RemoteEntryProps): import("react").JSX.Element;
12
+ export declare function RemoteEntry({ wide, t }: RemoteEntryProps): import("react").JSX.Element;
10
13
  //# sourceMappingURL=RemoteEntry.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RemoteEntry.d.ts","sourceRoot":"","sources":["../../../src/client/RemoteEntry.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAQjF,wEAAwE;AACxE,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,gBAAgB,CAAC,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAA;AA0BrF;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,aAAa,EAAE,CAAC,EAAE,EAAE,gBAAgB,+BA+LvE"}
1
+ {"version":3,"file":"RemoteEntry.d.ts","sourceRoot":"","sources":["../../../src/client/RemoteEntry.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AAQnE,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG,WAAW,CAAC,QAAQ,CAAC,GAAG;IACrD,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd,CAAA;AA0BD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,EAAE,gBAAgB,+BA2LxD"}
@@ -44,7 +44,7 @@ function mergeFrame(state, frame) {
44
44
  * @param props - composed slot props (contract in this package).
45
45
  * @returns the entry element tree.
46
46
  */
47
- export function RemoteEntry({ wide, useWorkspaces, t }) {
47
+ export function RemoteEntry({ wide, t }) {
48
48
  const [open, setOpen] = useState(false);
49
49
  const [state, setState] = useState({ kind: 'lan-required' });
50
50
  // Latest-state mirror for the EventSource callback: transition detection
@@ -52,15 +52,12 @@ export function RemoteEntry({ wide, useWorkspaces, t }) {
52
52
  // pure), so mint decisions read this ref instead.
53
53
  const stateRef = useRef(state);
54
54
  useEffect(() => { stateRef.current = state; }, [state]);
55
- const [copied, setCopied] = useState(undefined);
55
+ const [copied, setCopied] = useState(false);
56
56
  const eventSource = useRef(undefined);
57
57
  // Generation counter for the open flow: closing (or re-opening) the panel
58
58
  // bumps it, so an in-flight issue() that resolves after a close does not
59
59
  // spawn a stray EventSource.
60
60
  const openSeq = useRef(0);
61
- // The current workspace (the recent-workspace projection the shell's New
62
- // Session flow targets) — the deep-link target for the phone.
63
- const workspaceId = useWorkspaces(s => s.recentWorkspaceId);
64
61
  const closeEventSource = useCallback(() => {
65
62
  eventSource.current?.close();
66
63
  eventSource.current = undefined;
@@ -68,7 +65,7 @@ export function RemoteEntry({ wide, useWorkspaces, t }) {
68
65
  const mint = useCallback(async (address) => {
69
66
  let result;
70
67
  try {
71
- result = await issuePair(workspaceId, address);
68
+ result = await issuePair(address);
72
69
  }
73
70
  catch {
74
71
  // Fetch/network failure: show an explicit state instead of silently
@@ -103,7 +100,7 @@ export function RemoteEntry({ wide, useWorkspaces, t }) {
103
100
  address: address ?? result.lanAddresses[0] ?? '',
104
101
  lanAddresses: result.lanAddresses,
105
102
  };
106
- }, [workspaceId]);
103
+ }, []);
107
104
  const openPanel = useCallback(async () => {
108
105
  const seq = ++openSeq.current;
109
106
  setOpen(true);
@@ -196,12 +193,12 @@ export function RemoteEntry({ wide, useWorkspaces, t }) {
196
193
  const handlePickPublic = useCallback(() => {
197
194
  void mint().then(setState);
198
195
  }, [mint]);
199
- const handleCopy = useCallback((target, url) => {
196
+ const handleCopy = useCallback((url) => {
200
197
  void copyText(url).then((ok) => {
201
198
  if (!ok)
202
199
  return;
203
- setCopied(target);
204
- window.setTimeout(() => { setCopied(undefined); }, 1500);
200
+ setCopied(true);
201
+ window.setTimeout(() => { setCopied(false); }, 1500);
205
202
  });
206
203
  }, []);
207
204
  return (_jsxs(_Fragment, { children: [_jsxs("div", { className: css.entryRow, "data-rail": wide ? undefined : 'rail', children: [_jsx(UpdateEntry, { wide: wide, t: t }), _jsx(TooltipAnchor, { wide: wide, label: t('entry.label'), onClick: openPanel, expanded: open })] }), open && createPortal((_jsxs("div", { className: css.overlay, role: "presentation", children: [_jsx("div", { className: css.mask, "aria-hidden": "true", onClick: closePanel }), _jsx(RemotePanel, { t: t, state: state, copied: copied, onClose: closePanel, onStop: handleStop, onRefresh: handleRefresh, onCopy: handleCopy, onPickAddress: handlePickAddress, onPickPublic: handlePickPublic, onRevoke: handleRevoke })] })), document.body)] }));
@@ -36,11 +36,11 @@ export type PanelState = {
36
36
  export interface RemotePanelProps {
37
37
  t: TranslateNS<'remote'>;
38
38
  state: PanelState;
39
- copied: 'phone' | 'desktop' | undefined;
39
+ copied: boolean;
40
40
  onClose(): void;
41
41
  onStop(): void;
42
42
  onRefresh(): void;
43
- onCopy(target: 'phone' | 'desktop', url: string): void;
43
+ onCopy(url: string): void;
44
44
  /** Re-mint the QR against a different LAN address. */
45
45
  onPickAddress(address: string): void;
46
46
  /** Re-mint the QR against the configured public (tunneled) base. */
@@ -1 +1 @@
1
- {"version":3,"file":"RemotePanel.d.ts","sourceRoot":"","sources":["../../../src/client/RemotePanel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAIL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAA;AAItB,4DAA4D;AAC5D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IACE,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,2DAA2D;IAC3D,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAA;IACf,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAEL,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IACxB,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAA;IACvC,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;IACd,SAAS,IAAI,IAAI,CAAA;IACjB,MAAM,CAAC,MAAM,EAAE,OAAO,GAAG,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACtD,sDAAsD;IACtD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,oEAAoE;IACpE,YAAY,IAAI,IAAI,CAAA;IACpB,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAgBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BAyK5I"}
1
+ {"version":3,"file":"RemotePanel.d.ts","sourceRoot":"","sources":["../../../src/client/RemotePanel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAA;AACnE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AACjD,OAAO,EAGL,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAA;AAItB,4DAA4D;AAC5D,MAAM,MAAM,UAAU,GAClB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CAAE,GACpD;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,aAAa,CAAA;CAAE,GACvB;IACE,IAAI,EAAE,OAAO,CAAA;IACb,GAAG,EAAE,MAAM,CAAA;IACX,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,OAAO,CAAA;IAChB,KAAK,EAAE,YAAY,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,WAAW,EAAE,MAAM,CAAA;IACnB,2DAA2D;IAC3D,OAAO,EAAE,WAAW,EAAE,CAAA;IACtB,qDAAqD;IACrD,OAAO,EAAE,MAAM,CAAA;IACf,yDAAyD;IACzD,YAAY,EAAE,MAAM,EAAE,CAAA;IACtB,yEAAyE;IACzE,MAAM,EAAE,OAAO,CAAA;IACf,+DAA+D;IAC/D,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,mEAAmE;IACnE,MAAM,CAAC,EAAE,iBAAiB,CAAA;IAC1B,6DAA6D;IAC7D,OAAO,CAAC,EAAE,YAAY,CAAA;CACvB,CAAA;AAEL,qDAAqD;AACrD,MAAM,WAAW,gBAAgB;IAC/B,CAAC,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAA;IACxB,KAAK,EAAE,UAAU,CAAA;IACjB,MAAM,EAAE,OAAO,CAAA;IACf,OAAO,IAAI,IAAI,CAAA;IACf,MAAM,IAAI,IAAI,CAAA;IACd,SAAS,IAAI,IAAI,CAAA;IACjB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,sDAAsD;IACtD,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACpC,oEAAoE;IACpE,YAAY,IAAI,IAAI,CAAA;IACpB,gCAAgC;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACjC;AAgBD;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,YAAY,EAAE,QAAQ,EAAE,EAAE,gBAAgB,+BA+J5I"}
@@ -8,7 +8,7 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
8
8
  import clsx from 'clsx';
9
9
  import { QRCodeSVG } from 'qrcode.react';
10
10
  import { IconCloseOutline16, IconCopyOutline16, IconRefreshOutline16, IconStopFill16, } from '@deepseek-ai/dsh-client-ui-primitives';
11
- import { desktopPairUrl, formatClock, formatLastSeen, } from "./pair-api.js";
11
+ import { formatClock, formatLastSeen, } from "./pair-api.js";
12
12
  import { deviceNameFromUserAgent } from "./device-name.js";
13
13
  import css from './remote.module.css';
14
14
  /** Badge text + tone per phase (ready states only). */
@@ -29,7 +29,7 @@ function statusOf(t, state) {
29
29
  export function RemotePanel({ t, state, copied, onClose, onStop, onRefresh, onCopy, onPickAddress, onPickPublic, onRevoke }) {
30
30
  return (_jsxs("div", { className: css.panel, role: "dialog", "aria-modal": "true", "aria-label": t('title'), children: [_jsxs("div", { className: css.header, children: [_jsxs("div", { className: css.heading, children: [_jsx("h2", { className: css.title, children: t('title') }), _jsx("p", { className: css.subtitle, children: t('subtitle') })] }), _jsx("button", { type: "button", className: css.close, "aria-label": t('close.label'), onClick: onClose, children: _jsx(IconCloseOutline16, { size: 14 }) })] }), state.kind === 'lan-required' ? (_jsxs("div", { className: css.banner, role: "alert", children: [_jsx("p", { className: css.bannerTitle, children: t('status.lanRequired') }), _jsx("p", { className: css.bannerHint, children: t('status.lanRequiredHint') })] })) : state.kind === 'loopback-required' ? (_jsxs("div", { className: css.banner, role: "alert", children: [_jsx("p", { className: css.bannerTitle, children: t('status.loopbackRequired') }), _jsx("p", { className: css.bannerHint, children: t('status.loopbackRequiredHint') })] })) : state.kind === 'unreachable' ? (_jsxs("div", { className: css.banner, role: "alert", children: [_jsx("p", { className: css.bannerTitle, children: t('status.unreachable') }), _jsx("p", { className: css.bannerHint, children: t('status.unreachableHint') })] })) : (_jsxs(_Fragment, { children: [state.posture !== undefined && state.posture.hosts.some(host => host.exposed) && (_jsxs("div", { className: css.banner, role: "alert", children: [_jsx("p", { className: css.bannerTitle, children: t('posture.exposed') }), _jsx("p", { className: css.bannerHint, children: t('posture.exposedHint', { hosts: state.posture.hosts.filter(host => host.exposed).map(host => host.host).join(', ') }) })] })), _jsxs("div", { className: css.card, children: [_jsxs("div", { className: css.cardHeader, children: [_jsx("span", { className: css.cardTitle, children: t('card.title') }), _jsxs("span", { className: css.badges, children: [state.public && _jsx("span", { className: clsx(css.badge, css.badgePublic), children: t('public.badge') }), _jsx("span", { className: clsx(css.badge, css[`badge-${statusOf(t, state).tone}`]), children: statusOf(t, state).text })] })] }), _jsx("div", { className: css.qrWrap, "data-testid": "remote-qr", children: _jsx(QRCodeSVG, { value: state.url, size: 184, level: "M", marginSize: 1, className: css.qr }) }), state.expired
31
31
  ? _jsx("p", { className: css.expired, children: t('pair.expired') })
32
- : _jsx("p", { className: css.expiry, children: t('pair.expires', { time: formatClock(state.expiresAt) }) })] }), _jsx("p", { className: css.hint, children: state.public ? t('pair.publicHint') : t('pair.hint') }), _jsxs("div", { className: css.pairLinks, children: [_jsxs("div", { className: css.pairLinkRow, children: [_jsxs("div", { className: css.pairLinkText, children: [_jsx("span", { className: css.pairLinkLabel, children: t('pair.phoneLabel') }), _jsx("code", { className: css.link, title: state.url, children: state.url })] }), _jsxs("button", { type: "button", className: css.copyLink, onClick: () => onCopy('phone', state.url), children: [_jsx(IconCopyOutline16, { size: 14 }), copied === 'phone' ? t('action.copied') : t('action.copyPhone')] })] }), _jsxs("div", { className: css.pairLinkRow, children: [_jsxs("div", { className: css.pairLinkText, children: [_jsx("span", { className: css.pairLinkLabel, children: t('pair.desktopLabel') }), _jsx("code", { className: css.link, title: desktopPairUrl(state.url), children: desktopPairUrl(state.url) })] }), _jsxs("button", { type: "button", className: css.copyLink, onClick: () => onCopy('desktop', desktopPairUrl(state.url)), children: [_jsx(IconCopyOutline16, { size: 14 }), copied === 'desktop' ? t('action.copied') : t('action.copyDesktop')] })] })] }), _jsx("p", { className: css.oneTimeHint, children: t('pair.oneTimeHint') }), state.phase === 'stopped' && _jsx("p", { className: css.stoppedHint, children: t('stopped.hint') }), state.tunnel !== undefined && state.tunnel.state !== 'running' && (_jsx("p", { className: state.tunnel.state === 'failed' ? css.tunnelFailed : css.tunnelNote, role: "status", children: state.tunnel.state === 'failed'
32
+ : _jsx("p", { className: css.expiry, children: t('pair.expires', { time: formatClock(state.expiresAt) }) })] }), _jsx("p", { className: css.hint, children: state.public ? t('pair.publicHint') : t('pair.hint') }), _jsx("div", { className: css.pairLinks, children: _jsxs("div", { className: css.pairLinkRow, children: [_jsxs("div", { className: css.pairLinkText, children: [_jsx("span", { className: css.pairLinkLabel, children: t('pair.linkLabel') }), _jsx("code", { className: css.link, title: state.url, children: state.url })] }), _jsxs("button", { type: "button", className: css.copyLink, onClick: () => onCopy(state.url), children: [_jsx(IconCopyOutline16, { size: 14 }), copied ? t('action.copied') : t('action.copyLink')] })] }) }), _jsx("p", { className: css.oneTimeHint, children: t('pair.oneTimeHint') }), state.phase === 'stopped' && _jsx("p", { className: css.stoppedHint, children: t('stopped.hint') }), state.tunnel !== undefined && state.tunnel.state !== 'running' && (_jsx("p", { className: state.tunnel.state === 'failed' ? css.tunnelFailed : css.tunnelNote, role: "status", children: state.tunnel.state === 'failed'
33
33
  ? t('tunnel.failed', { error: state.tunnel.error ?? t('tunnel.unknownError') })
34
34
  : t('tunnel.starting') })), (state.publicBaseUrl !== undefined || state.lanAddresses.length > 1) && (_jsxs("fieldset", { className: css.addresses, children: [_jsx("legend", { children: t('address.label') }), state.publicBaseUrl !== undefined && (_jsxs("label", { className: css.address, children: [_jsx("input", { type: "radio", name: "lan-address", "aria-label": t('address.public'), checked: state.public, onChange: onPickPublic }), _jsx("span", { children: t('address.public') }), _jsx("code", { className: css.addressValue, children: state.publicBaseUrl })] }, "public")), state.lanAddresses.map(address => (_jsxs("label", { className: css.address, children: [_jsx("input", { type: "radio", name: "lan-address", "aria-label": address, checked: !state.public && address === state.address, onChange: () => onPickAddress(address) }), _jsx("span", { children: t('address.lan') }), _jsx("code", { className: css.addressValue, children: address })] }, address))), _jsx("p", { className: css.addressHint, children: t('address.hint') })] })), _jsxs("div", { className: css.actions, children: [_jsxs("button", { type: "button", className: css.action, onClick: onStop, children: [_jsx(IconStopFill16, { size: 14 }), t('action.stop')] }), _jsxs("button", { type: "button", className: css.action, onClick: onRefresh, children: [_jsx(IconRefreshOutline16, { size: 14 }), t('action.refresh')] })] }), _jsxs("section", { className: css.devices, "data-testid": "remote-devices", "aria-label": t('devices.title'), children: [_jsx("h3", { className: css.devicesTitle, children: t('devices.title') }), state.devices.length === 0 ? (_jsx("p", { className: css.devicesEmpty, children: t('devices.empty') })) : (_jsx("ul", { className: css.deviceList, children: state.devices.map(device => (_jsxs("li", { className: css.deviceRow, children: [_jsxs("div", { className: css.deviceMeta, children: [_jsx("span", { className: css.deviceName, children: deviceNameFromUserAgent(device.userAgent) ?? t('devices.unknown') }), _jsx("span", { className: clsx(css.devicePresence, device.online ? css.deviceOnline : css.deviceOffline), children: device.online ? t('devices.online') : t('devices.offline') }), _jsx("span", { className: css.deviceSeen, children: t('devices.lastSeen', { time: formatLastSeen(device.lastSeenAt) }) })] }), _jsx("button", { type: "button", className: css.deviceRevoke, "aria-label": t('devices.revoke.label'), onClick: () => { onRevoke(device.id); }, children: t('devices.revoke') })] }, device.id))) }))] })] }))] }));
35
35
  }
@@ -4,7 +4,8 @@
4
4
  * renders, bound to the `remote-web-ui` settings namespace.
5
5
  */
6
6
  import type { InjectFace, PropsLocale, PropsRuntime } from '@deepseek-ai/dsh-client-ui-slots';
7
- import type { SettingsScope, SnapshotStore } from '@deepseek-ai/dsh-client-runtime/client';
7
+ import type { SettingsScope } from '@deepseek-ai/dsh-client-ui-settings/client';
8
+ import type { SnapshotStore } from '@deepseek-ai/dsh-client-store';
8
9
  import { type CardActions, type CardShell, type FieldState as CardFieldState } from './settings-form.ts';
9
10
  /** The remote-control fields this card edits (the namespace's full schema). */
10
11
  export interface RemoteSettings {
@@ -26,8 +27,11 @@ export interface RemoteSettings {
26
27
  publicBaseUrl?: string;
27
28
  /** When on, the plugin runs its own Cloudflare quick tunnel automatically. */
28
29
  autoTunnel?: boolean;
29
- /** Mobile composer: plain Enter sends; off means Enter inserts a newline. */
30
- mobileEnterToSend?: boolean;
30
+ /**
31
+ * LAN bind toggle: once flipped, the plugin manages the profile patch's
32
+ * webserver block (0.0.0.0 / 127.0.0.1) and the host firewall rule.
33
+ */
34
+ lanBind?: boolean;
31
35
  }
32
36
  /** What the remote-control card renders. */
33
37
  export interface RemoteSettingsCardState extends CardShell {
@@ -49,8 +53,8 @@ export interface RemoteSettingsCardState extends CardShell {
49
53
  publicBaseUrl: CardFieldState;
50
54
  /** Auto public tunnel switch. */
51
55
  autoTunnel: CardFieldState;
52
- /** Mobile composer Enter-to-send switch. */
53
- mobileEnterToSend: CardFieldState;
56
+ /** LAN bind toggle. */
57
+ lanBind: CardFieldState;
54
58
  }
55
59
  /** The registration-side face the card's slot entry injects. */
56
60
  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,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,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,YAAY,EAAE,cAAc,CAAA;IAC5B,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;IAgBhD,OAAO,CAAC,UAAU;IAgBlB;;;OAGG;IACH,MAAM,IAAI,sBAAsB;IAIhC;;;OAGG;IACH,OAAO,IAAI,IAAI;CAGhB;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,+BAiIhE"}
1
+ {"version":3,"file":"RemoteSettingsCard.d.ts","sourceRoot":"","sources":["../../../src/client/RemoteSettingsCard.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,kCAAkC,CAAA;AAC7F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4CAA4C,CAAA;AAC/E,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAA;AAElE,OAAO,EAAkD,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,UAAU,IAAI,cAAc,EAAE,MAAM,oBAAoB,CAAA;AAIxJ,+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,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,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;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;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,YAAY,EAAE,cAAc,CAAA;IAC5B,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,uBAAuB;IACvB,OAAO,EAAE,cAAc,CAAA;CACxB;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;IAgBhD,OAAO,CAAC,UAAU;IAgBlB;;;OAGG;IACH,MAAM,IAAI,sBAAsB;IAIhC;;;OAGG;IACH,OAAO,IAAI,IAAI;CAGhB;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,+BAkIhE"}
@@ -1,6 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * The remote-control settings card: pairing security and device limits.
4
+ * Registers into the `web-ui.plugin.item` child slot the Web UI plugin group
5
+ * renders, bound to the `remote-web-ui` settings namespace.
6
+ */
7
+ import { useEffect, useState } from 'react';
2
8
  import { PluginSettingsCard, ValueField, BooleanField } from "./PluginSettingsCard.js";
3
9
  import { CardForm, booleanField, numberField, textField } from "./settings-form.js";
10
+ import { readLanBindStatus } from "./pair-api.js";
4
11
  /** Bridges the `remote-web-ui` scope onto the card's staged form. */
5
12
  export class RemoteSettingsCardController {
6
13
  form;
@@ -17,7 +24,7 @@ export class RemoteSettingsCardController {
17
24
  booleanField('requirePairingForLan'),
18
25
  textField('publicBaseUrl'),
19
26
  booleanField('autoTunnel'),
20
- booleanField('mobileEnterToSend'),
27
+ booleanField('lanBind'),
21
28
  ]);
22
29
  this.store = this.form.bind(() => this.projection());
23
30
  }
@@ -33,7 +40,7 @@ export class RemoteSettingsCardController {
33
40
  requirePairingForLan: this.form.field('requirePairingForLan'),
34
41
  publicBaseUrl: this.form.field('publicBaseUrl'),
35
42
  autoTunnel: this.form.field('autoTunnel'),
36
- mobileEnterToSend: this.form.field('mobileEnterToSend'),
43
+ lanBind: this.form.field('lanBind'),
37
44
  };
38
45
  }
39
46
  /**
@@ -66,5 +73,51 @@ export function RemoteSettingsCard(props) {
66
73
  invalidLabel: t('settings.invalidNumber'),
67
74
  disabled,
68
75
  };
69
- return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", defaultOpen: false, 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-idle-expire", label: t('settings.idleExpireMs'), hint: t('settings.idleExpireMsHint'), numeric: true, ...fieldProps, ...state.idleExpireMs, onEdit: (text) => { props.edit('idleExpireMs', text); }, onReset: () => { props.resetField('idleExpireMs'); } }), _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'); } })] }));
76
+ return (_jsxs(PluginSettingsCard, { t: t, titleKey: "settings.title", descriptionKey: "settings.description", defaultOpen: false, 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-idle-expire", label: t('settings.idleExpireMs'), hint: t('settings.idleExpireMsHint'), numeric: true, ...fieldProps, ...state.idleExpireMs, onEdit: (text) => { props.edit('idleExpireMs', text); }, onReset: () => { props.resetField('idleExpireMs'); } }), _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(LanBindStatus, { t: t }), _jsx(BooleanField, { id: "settings-remote-lan-bind", label: t('settings.lanBind'), hint: t('settings.lanBindHint'), inheritLabel: t('settings.inherit'), onLabel: t('settings.on'), offLabel: t('settings.off'), ...fieldProps, ...state.lanBind, onEdit: (text) => { props.edit('lanBind', text); }, onReset: () => { props.resetField('lanBind'); } })] }));
77
+ }
78
+ /**
79
+ * Live LAN-bind facts above the toggle: the managed block's host, the live
80
+ * bind, the firewall summary, and the reachable LAN URLs. Polls the
81
+ * loopback-only status endpoint; a failure (non-loopback origin) renders
82
+ * nothing — the pairing panel carries the loopback banner instead.
83
+ */
84
+ function LanBindStatus({ t }) {
85
+ const [frame, setFrame] = useState(undefined);
86
+ useEffect(() => {
87
+ let alive = true;
88
+ const read = () => {
89
+ void readLanBindStatus().then((value) => {
90
+ if (alive)
91
+ setFrame(value);
92
+ }).catch(() => { });
93
+ };
94
+ read();
95
+ const timer = window.setInterval(read, 10_000);
96
+ return () => {
97
+ alive = false;
98
+ window.clearInterval(timer);
99
+ };
100
+ }, []);
101
+ if (frame === undefined)
102
+ return null;
103
+ const lanOn = frame.blockHost === '0.0.0.0';
104
+ const firewallText = frame.firewall.managed
105
+ ? t(frame.firewall.ok ? 'lan.firewall.ok' : 'lan.firewall.bad')
106
+ : t('lan.firewall.unmanaged');
107
+ const lines = [
108
+ t('lan.bind', { host: frame.bindHost, port: String(frame.port) }) + ' · ' + firewallText,
109
+ ];
110
+ if (lanOn && frame.lanUrls.length > 0) {
111
+ lines.push(t('lan.urls', { urls: frame.lanUrls.join(' ') }));
112
+ }
113
+ if (!lanOn) {
114
+ lines.push(t('lan.off'));
115
+ }
116
+ if (frame.setting === null) {
117
+ lines.push(t('lan.untouched'));
118
+ }
119
+ if (frame.pendingRestart === true) {
120
+ lines.push(t('lan.pendingRestart'));
121
+ }
122
+ return (_jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 2, fontSize: 12, lineHeight: '18px', opacity: 0.85 }, children: lines.map((line, index) => (_jsx("div", { style: { wordBreak: 'break-all' }, children: line }, index))) }));
70
123
  }
@@ -1,13 +1,11 @@
1
1
  /**
2
- * Phone-side boot flow: the QR link's `pair` + `workspace` parameters.
3
- * Runs from the client apply on every page load, on any device:
4
- * - `pair` present → accept the token, then reload so the whole SPA boots
5
- * with the device cookie (the accept endpoint is exempt from the pairing
6
- * gate; every other /api request needs the cookie).
7
- * - `workspace` present (and paired) → deep-link: connect that workspace's
8
- * session and open it, then strip the parameter.
9
- * Failure of accept leaves a sessionStorage marker the entry renders as a
10
- * one-time notice.
2
+ * Pair boot flow: the QR link's `pair` parameter. Runs from the client
3
+ * apply on every page load, on any device: `pair` present → accept the
4
+ * token, then reload so the whole SPA boots with the device cookie (the
5
+ * accept endpoint is exempt from the pairing gate; every other /api request
6
+ * needs the cookie). Every device — phone or PC — lands on the same official
7
+ * Web GUI; phones get the injected portrait adaptation. Failure of accept
8
+ * leaves a sessionStorage marker the entry renders as a one-time notice.
11
9
  */
12
10
  import type { Context } from '@deepseek-ai/cordis';
13
11
  /** sessionStorage key for the failed-pair notice. */
@@ -28,11 +26,10 @@ export interface PageSurface {
28
26
  }
29
27
  /** The browser implementation of {@link PageSurface}. */
30
28
  export declare const browserPage: PageSurface;
31
- /** Whether this browser looks like a phone/tablet (the simplified mobile surface). */
32
- export declare function isMobileSurface(): boolean;
33
29
  /**
34
- * Run the pair/workspace boot flow for this page load.
35
- * @param ctx - client root context (workspaces/sessions read at need time).
30
+ * Run the pair boot flow for this page load.
31
+ * @param ctx - client root context (unused since the deep-link retirement;
32
+ * kept for call-site stability).
36
33
  * @param search - the current location.search.
37
34
  * @param page - the page surface (defaults to the browser).
38
35
  */
@@ -1 +1 @@
1
- {"version":3,"file":"deep-link.d.ts","sourceRoot":"","sources":["../../../src/client/deep-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,2BAA2B,CAAA;AAK1D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,uBAAuB;IACvB,MAAM,IAAI,IAAI,CAAA;CACf;AAED,yDAAyD;AACzD,eAAO,MAAM,WAAW,EAAE,WAazB,CAAA;AAED,sFAAsF;AACtF,wBAAgB,eAAe,IAAI,OAAO,CAGzC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,WAAyB,GAAG,IAAI,CASnG"}
1
+ {"version":3,"file":"deep-link.d.ts","sourceRoot":"","sources":["../../../src/client/deep-link.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,qDAAqD;AACrD,eAAO,MAAM,kBAAkB,2BAA2B,CAAA;AAE1D;;;GAGG;AACH,MAAM,WAAW,WAAW;IAC1B,wDAAwD;IACxD,IAAI,EAAE,MAAM,CAAA;IACZ,yCAAyC;IACzC,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC/B,6CAA6C;IAC7C,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,uBAAuB;IACvB,MAAM,IAAI,IAAI,CAAA;CACf;AAED,yDAAyD;AACzD,eAAO,MAAM,WAAW,EAAE,WAazB,CAAA;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,WAAyB,GAAG,IAAI,CAMnG"}
@@ -1,19 +1,15 @@
1
1
  /**
2
- * Phone-side boot flow: the QR link's `pair` + `workspace` parameters.
3
- * Runs from the client apply on every page load, on any device:
4
- * - `pair` present → accept the token, then reload so the whole SPA boots
5
- * with the device cookie (the accept endpoint is exempt from the pairing
6
- * gate; every other /api request needs the cookie).
7
- * - `workspace` present (and paired) → deep-link: connect that workspace's
8
- * session and open it, then strip the parameter.
9
- * Failure of accept leaves a sessionStorage marker the entry renders as a
10
- * one-time notice.
2
+ * Pair boot flow: the QR link's `pair` parameter. Runs from the client
3
+ * apply on every page load, on any device: `pair` present → accept the
4
+ * token, then reload so the whole SPA boots with the device cookie (the
5
+ * accept endpoint is exempt from the pairing gate; every other /api request
6
+ * needs the cookie). Every device — phone or PC — lands on the same official
7
+ * Web GUI; phones get the injected portrait adaptation. Failure of accept
8
+ * leaves a sessionStorage marker the entry renders as a one-time notice.
11
9
  */
12
10
  import { acceptPair, readPairParams } from "./pair-api.js";
13
11
  /** sessionStorage key for the failed-pair notice. */
14
12
  export const PAIR_FAILED_MARKER = 'dsh-remote-pair-failed';
15
- /** Poll budget for the runtime services (activation order is unconstrained). */
16
- const SERVICE_WAIT_MS = 10_000;
17
13
  /** The browser implementation of {@link PageSurface}. */
18
14
  export const browserPage = {
19
15
  get href() {
@@ -29,29 +25,21 @@ export const browserPage = {
29
25
  window.location.reload();
30
26
  },
31
27
  };
32
- /** Whether this browser looks like a phone/tablet (the simplified mobile surface). */
33
- export function isMobileSurface() {
34
- if (typeof navigator === 'undefined')
35
- return false;
36
- return /Android|iPhone|iPad|iPod|Mobile|mobile/i.test(navigator.userAgent);
37
- }
38
28
  /**
39
- * Run the pair/workspace boot flow for this page load.
40
- * @param ctx - client root context (workspaces/sessions read at need time).
29
+ * Run the pair boot flow for this page load.
30
+ * @param ctx - client root context (unused since the deep-link retirement;
31
+ * kept for call-site stability).
41
32
  * @param search - the current location.search.
42
33
  * @param page - the page surface (defaults to the browser).
43
34
  */
44
35
  export function runPairBootFlow(ctx, search, page = browserPage) {
36
+ void ctx;
45
37
  const params = readPairParams(search);
46
38
  if (params.pair !== undefined) {
47
39
  void runAccept(params.pair, page);
48
- return;
49
- }
50
- if (params.workspace !== undefined) {
51
- void runDeepLink(ctx, params.workspace, page);
52
40
  }
53
41
  }
54
- /** Accept the token, then enter the matching desktop/mobile surface. */
42
+ /** Accept the token, then reload into the paired official UI. */
55
43
  async function runAccept(token, page) {
56
44
  let ok = false;
57
45
  try {
@@ -69,55 +57,7 @@ async function runAccept(token, page) {
69
57
  url.searchParams.delete('pair');
70
58
  page.replaceState(`${url.pathname}${url.search}${url.hash}`);
71
59
  if (ok) {
72
- // Phones land on the standalone simplified surface (the full desktop UI
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 {
81
- page.reload();
82
- }
83
- }
84
- }
85
- /**
86
- * Connect the deep-linked workspace and open its session. Waits for the
87
- * runtime services AND for the target workspace to appear in the workspace
88
- * list (both are asynchronous after boot), then connects and opens; gives
89
- * up silently within the budget — the workspace param is stripped either
90
- * way, so a late failure cannot loop.
91
- * @param ctx - client root context.
92
- * @param workspaceId - the target workspace.
93
- * @param page - the page surface.
94
- */
95
- async function runDeepLink(ctx, workspaceId, page) {
96
- const target = workspaceId;
97
- const deadline = Date.now() + SERVICE_WAIT_MS;
98
- while (Date.now() < deadline) {
99
- const workspaces = ctx.get('workspaces');
100
- const sessions = ctx.get('sessions');
101
- if (workspaces !== undefined && sessions !== undefined) {
102
- const items = workspaces.list.getSnapshot().items;
103
- if (items.some(item => item.workspaceId === target)) {
104
- try {
105
- // Open unconditionally: a host-side "current" session may already
106
- // exist (multi-client mirroring), but the QR's workspace target is
107
- // explicit user intent and must win.
108
- const sessionId = await workspaces.connectWorkspace(target);
109
- sessions.open(sessionId);
110
- }
111
- catch {
112
- // Unknown workspace or a failed connect: fall through to the
113
- // runtime's own default initial selection.
114
- }
115
- break;
116
- }
117
- }
118
- await new Promise(resolve => setTimeout(resolve, 200));
60
+ // Every device reloads into the same official Web GUI at this origin.
61
+ page.reload();
119
62
  }
120
- const url = new URL(page.href);
121
- url.searchParams.delete('workspace');
122
- page.replaceState(`${url.pathname}${url.search}${url.hash}`);
123
63
  }
@@ -1,4 +1,5 @@
1
- import type { ClientContext, SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-runtime/client';
1
+ import type { Context as ClientContext } from '@deepseek-ai/cordis';
2
+ import type { SettingsScope, SettingsScopeSpec } from '@deepseek-ai/dsh-client-ui-settings/client';
2
3
  import { type RemoteKey } from './locales.ts';
3
4
  export type { RemoteEntryProps } from './RemoteEntry.tsx';
4
5
  export type { PanelState, RemotePanelProps } from './RemotePanel.tsx';
@@ -18,11 +19,6 @@ declare module '@deepseek-ai/dsh-client-ui-slots' {
18
19
  * sidebar shell on deployments that carry the feature seat; the shell
19
20
  * passes only its column display state.
20
21
  */
21
- 'sidebar.remote': {
22
- kind: 'single';
23
- scope: 'root';
24
- owner: SidebarRemoteOwnerProps;
25
- };
26
22
  /**
27
23
  * The child slot the Web UI plugin group declares; this card registers
28
24
  * into the group instead of the top-level `settings.plugin.item` list.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,wCAAwC,CAAA;AAa7G,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAcrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH,gBAAgB,EAAE;YAAE,IAAI,EAAE,QAAQ,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,uBAAuB,CAAA;SAAE,CAAA;QACnF;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAwO9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,qBAAqB,CAAA;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,4CAA4C,CAAA;AAelG,OAAO,EAAU,KAAK,SAAS,EAAE,MAAM,cAAc,CAAA;AAqBrD,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACrE,YAAY,EAAE,qBAAqB,EAAE,MAAM,wBAAwB,CAAA;AACnE,YAAY,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAC7C,YAAY,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAA;AAC/F,YAAY,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACzD,YAAY,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAErE,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,0CAA0C;QAC1C,MAAM,EAAE,SAAS,CAAA;KAClB;IAED,UAAU,OAAO;QACf;;;;WAIG;QACH;;;;;WAKG;QACH,oBAAoB,EAAE;YAAE,IAAI,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,MAAM,CAAC;YAAC,KAAK,EAAE,4BAA4B,CAAA;SAAE,CAAA;KAC3F;CACF;AAED,4GAA4G;AAC5G,MAAM,WAAW,uBAAuB;IACtC,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAA;CACd;AAED,mEAAmE;AACnE,MAAM,WAAW,4BAA4B;IAC3C,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,KAAK,CAAA;CACjB;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf;;;;WAIG;QACH,aAAa,CAAC,EAAE;YAAE,IAAI,CAAC,CAAC,EAAE,IAAI,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,CAAC,CAAA;SAAE,CAAA;KAC1E;CACF;AAYD,wCAAwC;AACxC,eAAO,MAAM,MAAM,UAA+D,CAAA;AAElF;;;GAGG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAoQ9C"}