dsh-mobile 0.1.0-alpha.17 → 0.1.0-alpha.18

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.en.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="assets/brand/repository-hero.png" alt="Use DeepSeek Harness from a phone" width="100%">
2
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/brand/repository-hero.png" alt="Use DeepSeek Harness from a phone" width="100%">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">DSH Mobile</h1>
@@ -19,7 +19,7 @@
19
19
 
20
20
  <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases"><strong>Download the Android app</strong></a></p>
21
21
 
22
- DSH Mobile adapts the stock DeepSeek Harness React/Cordis surface for phones and exposes it through an authenticated HTTPS LAN gateway. Desktop, Android, and mobile browsers use the same Workspaces, sessions, messages, tools, settings, and event streams.
22
+ DSH Mobile provides a dedicated phone layout that composes the native DeepSeek Harness conversation, Workspace, settings, and plugin components. An authenticated HTTPS LAN gateway keeps desktop, Android, and mobile browsers on the same Workspaces, sessions, messages, tools, settings, and event streams.
23
23
 
24
24
  ## Quick start
25
25
 
@@ -55,10 +55,12 @@ Discovery uses mDNS/NSD, UDP announcements and queries, plus HTTPS probing. It p
55
55
 
56
56
  ## Mobile UI
57
57
 
58
- Phones still render the stock DeepSeek Harness page. The plugin provides a lightweight default mobile adaptation, and you can freely redesign the page structure and extend its interactions through **Customize from DeepSeek Harness**. By default, the session sidebar becomes a drawer, details open as an overlay, settings use a single-column layout, and the native composer is adapted for touch. Adding a Workspace browses computer folders inside the phone page. Images can come from the phone or the computer.
58
+ Phones use a dedicated layout shell instead of depending on the desktop three-column DOM. Native DeepSeek Harness feature plugins still render the content. You can freely redesign the page structure and extend its interactions through **Customize from DeepSeek Harness**. By default, the session sidebar becomes a drawer, details open as an overlay, settings use a single-column layout, and the native composer is adapted for touch. Adding a Workspace browses computer folders inside the phone page. Images can come from the phone or the computer.
59
59
 
60
60
  The Android app is a thin Kotlin WebView shell and contains no frontend copy.
61
61
 
62
+ For compatibility diagnosis, append `?frontend=stock` to the browser URL to temporarily use the previous desktop-page adaptation.
63
+
62
64
  ## Customize from DeepSeek Harness
63
65
 
64
66
  Customization is intentionally open-ended: use your own ideas to arrange the mobile layout, interactions, and features instead of staying within the default style.
@@ -80,7 +82,7 @@ flowchart LR
80
82
  Gateway -->|"loopback proxy"| DSH["Stock DSH Web and Host"]
81
83
  ```
82
84
 
83
- The Host face owns discovery, pairing, HTTPS, and the proxy. The Client face installs the responsive adapter on the stock DeepSeek Harness page. No DeepSeek Harness source files are modified.
85
+ The Host face owns discovery, pairing, HTTPS, and the proxy. The Client face replaces only the phone layout entry while reusing native feature plugins. Neither the DeepSeek Harness source nor its desktop page on port 3080 is modified.
84
86
 
85
87
  ## Security
86
88
 
package/README.md CHANGED
@@ -1,5 +1,5 @@
1
1
  <p align="center">
2
- <img src="assets/brand/repository-hero.png" alt="使用手机连接 DeepSeek Harness" width="100%">
2
+ <img src="https://raw.githubusercontent.com/saya-ch/dsh-mobile/main/assets/brand/repository-hero.png" alt="使用手机连接 DeepSeek Harness" width="100%">
3
3
  </p>
4
4
 
5
5
  <h1 align="center">DSH Mobile</h1>
@@ -24,7 +24,7 @@
24
24
 
25
25
  <p align="center"><a href="https://github.com/saya-ch/dsh-mobile/releases"><strong>下载 Android App</strong></a></p>
26
26
 
27
- DSH Mobile 不复制 DeepSeek Harness 前端。它为原生 React/Cordis 页面增加移动布局,并通过受保护的局域网 HTTPS 网关让 Android App 或手机浏览器访问同一份工作区、会话、消息和运行状态。
27
+ DSH Mobile 提供专属移动端布局入口,并直接组合 DeepSeek Harness 原生的对话、工作区、设置和插件组件。它通过受保护的局域网 HTTPS 网关,让 Android App 或手机浏览器访问电脑端同一份工作区、会话、消息和运行状态。
28
28
 
29
29
  ## 能做什么
30
30
 
@@ -78,7 +78,7 @@ App 配对后保存可撤销的设备凭据和私有证书信任;Wi-Fi、热
78
78
 
79
79
  ## 移动端界面
80
80
 
81
- 手机端仍然渲染原生 DeepSeek Harness 页面。插件提供轻量的默认移动适配;你也可以通过“自定义移动布局与功能”自由调整页面结构并扩展交互:
81
+ 手机端使用插件自带的独立布局外壳,不再依赖桌面三栏页面的 DOM 结构;内部功能仍由原生 DeepSeek Harness 插件渲染。你也可以通过“自定义移动布局与功能”继续调整页面结构并扩展交互:
82
82
 
83
83
  - 左上角打开工作区与会话抽屉。
84
84
  - 对话、轨迹、工具详情和 Session log 保持原有能力。
@@ -88,6 +88,8 @@ App 配对后保存可撤销的设备凭据和私有证书信任;Wi-Fi、热
88
88
 
89
89
  Android App 只是 Kotlin WebView 薄壳,不内置另一份网页。手机浏览器访问的也是同一页面。
90
90
 
91
+ 需要排查兼容性时,可在浏览器地址后追加 `?frontend=stock`,临时回到旧的桌面页面适配模式。
92
+
91
93
  ## 自定义移动布局与功能
92
94
 
93
95
  自定义程度很高:你可以发挥创意,自由布置移动端的布局、交互和功能,而不必局限于默认样式。
@@ -118,7 +120,7 @@ flowchart LR
118
120
  DSH -->|"同一工作区、会话和事件流"| Phone
119
121
  ```
120
122
 
121
- 插件包含两部分:Host face 提供发现、配对、HTTPS 和回环代理;Client face 为原生 DeepSeek Harness 页面安装移动响应式适配。安装和卸载都通过 DeepSeek Harness 插件机制完成。
123
+ 插件包含两部分:Host face 提供发现、配对、HTTPS 和回环代理;Client face 替换移动端的布局入口,并复用原生功能插件。DeepSeek Harness 的源码和 3080 桌面页面都不会被修改,安装和卸载仍完全通过插件机制完成。
122
124
 
123
125
  ## 安全
124
126
 
Binary file
package/lib/client.js CHANGED
@@ -6,10 +6,13 @@ window.__ModuleLoader__.load({
6
6
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
7
  let react = require("react");
8
8
  //#region src/native-mobile.ts
9
- /** Mobile layout rules applied to the stock DSH React surface. */
9
+ /** Mobile feature and compatibility rules applied to DSH React surfaces. */
10
10
  const NATIVE_MOBILE_STYLES = `
11
11
  @media (max-width:720px) {
12
12
  html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }
13
+ html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }
14
+ html.dsh-native-mobile-active :is(a,button,[role="button"],[role="tab"],[tabindex]) { -webkit-tap-highlight-color:transparent; }
15
+ html.dsh-native-mobile-active[data-dsh-mobile-input="touch"] :is(a,button,[role="button"],[role="tab"],[tabindex]):focus { outline:none !important; }
13
16
  [data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }
14
17
  [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }
15
18
  [data-dsh-mobile-center] > * { min-width:0 !important; }
@@ -27,7 +30,7 @@ window.__ModuleLoader__.load({
27
30
  [data-dsh-mobile-header] [class*="_tabs"] { min-height:42px; overflow-x:auto; scrollbar-width:none; }
28
31
  [data-dsh-mobile-header] [class*="_tabs"]::-webkit-scrollbar { display:none; }
29
32
  [data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }
30
- [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:0 auto 0 0 !important; height:100dvh !important; transition:transform 160ms ease !important; }
33
+ [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:0 auto 0 0 !important; height:100dvh !important; transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease) !important; }
31
34
  [data-dsh-mobile-sidebar][data-open="true"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }
32
35
  [data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }
33
36
  [data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }
@@ -37,27 +40,47 @@ window.__ModuleLoader__.load({
37
40
  [data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-toggle] svg { display:none !important; }
38
41
  [data-dsh-mobile-sidebar][data-open="false"] [data-dsh-mobile-toggle]::after { width:20px; height:2px; border-radius:2px; background:currentColor; box-shadow:0 -6px 0 currentColor,0 6px 0 currentColor; content:""; }
39
42
  .dsh-native-mobile-backdrop { position:fixed; z-index:235; inset:0; border:0; background:rgb(15 23 42 / 32%); }
43
+ .dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }
40
44
  .dsh-native-mobile-backdrop[hidden] { display:none; }
41
- [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform 160ms ease; background:var(--dsw-bg, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }
45
+ [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); background:var(--dsw-bg, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }
42
46
  [data-dsh-mobile-details][data-open="true"] { transform:translateX(0); }
43
47
  [data-dsh-mobile-handle] { display:none !important; }
44
- [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; }
48
+ [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; animation:dsh-mobile-panel-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }
45
49
  [data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-subtle,#e8ebef); }
46
50
  [data-dsh-mobile-settings-nav] [class*="_navTitle"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }
47
51
  [data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }
48
52
  [data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }
49
53
  [data-dsh-mobile-settings-list] [class*="_navCell"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }
54
+ [data-dsh-mobile-settings-list] [aria-current="true"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }
50
55
  [data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }
51
56
  [data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }
52
57
  [data-dsh-mobile-settings-header] [class*="_close"] { width:36px !important; height:36px !important; }
53
58
  [data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }
54
59
  [data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }
60
+ [data-dsh-mobile-settings-options] [data-slot="settings.general.item"] > [class*="_row"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }
61
+ [data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_rowText"] { width:100% !important; padding-right:0 !important; }
62
+ [data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:44px !important; max-width:100% !important; }
63
+ [data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }
64
+ [data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }
65
+ [data-dsh-mobile-settings-options] [class*="_head"] { min-width:0; flex-wrap:wrap; }
66
+ [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition="true"],
67
+ [data-dsh-mobile-view][data-dsh-mobile-view-transition="true"] { animation:dsh-mobile-view-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }
55
68
  [data-dsh-mobile-center] textarea { font-size:16px !important; }
56
69
  [data-dsh-mobile-center] table { display:block; max-width:100%; overflow-x:auto; }
57
70
  [data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }
71
+ [data-dsh-mobile-center] :is(img,video,canvas,svg) { max-width:100%; }
58
72
  [data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:12px 16px 20px !important; }
59
73
  [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:14px !important; }
60
74
  [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }
75
+ [data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:44px !important; padding:5px 0 !important; }
76
+ [data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_leading"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }
77
+ [data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_title"] { grid-column:2 !important; grid-row:1 !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
78
+ [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*="_sep"],[class*="_separator"]) { display:none !important; }
79
+ [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*="_summary"],[class*="_fileLink"]) { grid-column:2 !important; grid-row:2 !important; width:100% !important; min-width:0 !important; max-width:100% !important; overflow:hidden !important; line-height:20px !important; text-overflow:ellipsis !important; white-space:nowrap !important; }
80
+ [data-dsh-mobile-message-column] [data-disclosure-row] > [class*="_summarySuffix"] { grid-column:2 !important; grid-row:3 !important; margin-left:0 !important; }
81
+ [data-dsh-mobile-message-column] [data-context-fields] > * { display:grid !important; grid-template-columns:minmax(72px,30%) minmax(0,1fr) !important; gap:4px 10px !important; }
82
+ [data-dsh-mobile-message-column] [class*="_ioSection"] { grid-template-columns:1fr !important; row-gap:4px !important; }
83
+ [data-dsh-mobile-message-column] [class*="_body"] { max-width:100% !important; overflow-wrap:anywhere; }
61
84
  [data-dsh-mobile-center] [class*="_composer"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }
62
85
  [data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_row"] { display:flex !important; align-items:center !important; gap:4px !important; }
63
86
  [data-dsh-mobile-center] [class*="_card"]:has(textarea) [class*="_tools"] { flex:0 0 auto !important; width:auto !important; min-width:0 !important; gap:6px !important; }
@@ -74,7 +97,7 @@ window.__ModuleLoader__.load({
74
97
  .dsh-native-mobile-attach svg { width:22px; height:22px; }
75
98
  .dsh-native-mobile-sheet-mask { position:fixed; z-index:1080; inset:0; border:0; background:rgb(15 23 42 / 32%); }
76
99
  .dsh-native-mobile-sheet-mask[hidden] { display:none; }
77
- .dsh-native-mobile-sheet { position:fixed; z-index:1081; right:0; bottom:0; left:0; display:flex; flex-direction:column; gap:8px; max-height:min(78dvh,680px); padding:8px 16px max(18px,env(safe-area-inset-bottom)); overflow-y:auto; border-radius:22px 22px 0 0; background:var(--dsw-alias-bg-layer-2,#fff); box-shadow:0 -16px 44px rgb(15 23 42 / 16%); }
100
+ .dsh-native-mobile-sheet { position:fixed; z-index:1081; right:0; bottom:0; left:0; display:flex; flex-direction:column; gap:8px; max-height:min(78dvh,680px); padding:8px 16px max(18px,env(safe-area-inset-bottom)); overflow-y:auto; border-radius:22px 22px 0 0; background:var(--dsw-alias-bg-layer-2,#fff); box-shadow:0 -16px 44px rgb(15 23 42 / 16%); animation:dsh-mobile-sheet-in 220ms var(--dsh-mobile-motion-ease); }
78
101
  .dsh-native-mobile-sheet[hidden] { display:none; }
79
102
  .dsh-native-mobile-sheet__header { position:sticky; z-index:1; top:-8px; display:flex; align-items:center; justify-content:space-between; min-height:48px; padding:8px 0; background:var(--dsw-alias-bg-layer-2,#fff); }
80
103
  .dsh-native-mobile-sheet__header strong { font-size:17px; font-weight:600; }
@@ -85,12 +108,22 @@ window.__ModuleLoader__.load({
85
108
  .dsh-native-mobile-sheet__choice small { color:var(--dsw-alias-label-secondary,#6b7280); }
86
109
  .dsh-native-mobile-sheet__error { margin:4px 0; color:var(--dsw-alias-status-error,#b42318); font-size:13px; }
87
110
  }
111
+ @keyframes dsh-mobile-fade-in { from { opacity:0; } }
112
+ @keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }
113
+ @keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }
114
+ @keyframes dsh-mobile-sheet-in { from { opacity:.7; transform:translateY(18px); } }
88
115
  @media (max-width:420px) {
89
116
  [data-dsh-mobile-header] [class*="_headerActions"] { max-width:42vw; }
90
117
  [data-dsh-mobile-header] [class*="_titleRow"] { min-height:50px; }
118
+ [data-dsh-mobile-settings-options] [data-slot="settings.general.item"] [class*="_selector"] { align-self:stretch !important; width:100% !important; }
119
+ [data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }
91
120
  }
92
121
  @media (prefers-reduced-motion:reduce) {
93
122
  [data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }
123
+ .dsh-native-mobile-backdrop:not([hidden]),[data-dsh-mobile-settings],
124
+ [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition="true"],
125
+ [data-dsh-mobile-view][data-dsh-mobile-view-transition="true"],
126
+ .dsh-native-mobile-sheet { animation:none !important; }
94
127
  }
95
128
  `;
96
129
  function classToken(element, suffix) {
@@ -119,9 +152,20 @@ window.__ModuleLoader__.load({
119
152
  svg.append(path);
120
153
  return svg;
121
154
  }
122
- /** Add mobile semantics to the stock DSH layout without replacing its React tree. */
155
+ /** Add mobile semantics and phone-local file actions without replacing feature trees. */
123
156
  function installNativeMobileSurface() {
124
157
  document.documentElement.classList.add("dsh-native-mobile-active");
158
+ const setInputMode = (mode) => {
159
+ document.documentElement.dataset.dshMobileInput = mode;
160
+ };
161
+ const onPointerDown = (event) => {
162
+ if (event.pointerType === "touch" || event.pointerType === "pen") setInputMode("touch");
163
+ };
164
+ const onKeyDown = (event) => {
165
+ if (event.key === "Tab" || event.key.startsWith("Arrow")) setInputMode("keyboard");
166
+ };
167
+ document.addEventListener("pointerdown", onPointerDown, true);
168
+ document.addEventListener("keydown", onKeyDown, true);
125
169
  const backdrop = document.createElement("button");
126
170
  backdrop.type = "button";
127
171
  backdrop.className = "dsh-native-mobile-backdrop";
@@ -132,7 +176,12 @@ window.__ModuleLoader__.load({
132
176
  let sidebar;
133
177
  let sidebarRoot;
134
178
  let toggle;
179
+ let viewArea;
135
180
  let scheduled = 0;
181
+ let transitionFrame = 0;
182
+ let transitionRestartFrame = 0;
183
+ let transitionTimer = 0;
184
+ let transitionTarget;
136
185
  const fileInput = document.createElement("input");
137
186
  fileInput.type = "file";
138
187
  fileInput.accept = "image/*";
@@ -276,18 +325,53 @@ window.__ModuleLoader__.load({
276
325
  handFilesToComposer([...fileInput.files ?? []]);
277
326
  fileInput.value = "";
278
327
  });
328
+ const animateNavigation = (event) => {
329
+ if (!(event.target instanceof Element)) return;
330
+ const trigger = event.target.closest("button,a,[role=\"tab\"],[aria-selected]");
331
+ if (trigger === null || trigger.hasAttribute("disabled") || trigger.getAttribute("aria-disabled") === "true") return;
332
+ if (trigger.getAttribute("aria-selected") === "true" || trigger.getAttribute("aria-current") === "true") return;
333
+ const settingsNavigation = trigger.closest("[data-dsh-mobile-settings-list]") !== null;
334
+ const conversationNavigation = trigger.matches("[role=\"tab\"]");
335
+ const sidebarNavigation = trigger.closest("[data-dsh-mobile-sidebar-root]") !== null && trigger.closest("[data-dsh-mobile-toggle]") === null;
336
+ if (!settingsNavigation && !conversationNavigation && !sidebarNavigation) return;
337
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
338
+ if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame);
339
+ if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame);
340
+ transitionFrame = requestAnimationFrame(() => {
341
+ transitionFrame = 0;
342
+ const target = settingsNavigation ? document.querySelector("[data-dsh-mobile-settings-content]") : viewArea;
343
+ if (target === null || target === void 0) return;
344
+ transitionTarget?.removeAttribute("data-dsh-mobile-view-transition");
345
+ target.removeAttribute("data-dsh-mobile-view-transition");
346
+ transitionRestartFrame = requestAnimationFrame(() => {
347
+ transitionRestartFrame = 0;
348
+ transitionTarget = target;
349
+ target.dataset.dshMobileViewTransition = "true";
350
+ if (transitionTimer !== 0) clearTimeout(transitionTimer);
351
+ transitionTimer = window.setTimeout(() => {
352
+ target.removeAttribute("data-dsh-mobile-view-transition");
353
+ if (transitionTarget === target) transitionTarget = void 0;
354
+ transitionTimer = 0;
355
+ }, 240);
356
+ });
357
+ });
358
+ };
359
+ document.addEventListener("click", animateNavigation);
279
360
  const sync = () => {
280
361
  scheduled = 0;
281
362
  frame = firstByClassSuffix(document, "_frame");
282
- if (frame === void 0) return;
283
- frame.dataset.dshMobileFrame = "true";
284
- sidebar = firstByClassSuffix(frame, "_sidebarCol");
285
- const center = firstByClassSuffix(frame, "_centerCol");
286
- const details = firstByClassSuffix(frame, "_detailsCol");
287
- const handle = firstByClassSuffix(frame, "_handle");
363
+ const dedicatedCenter = document.querySelector(".dshm-main") ?? void 0;
364
+ if (frame !== void 0) frame.dataset.dshMobileFrame = "true";
365
+ sidebar = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_sidebarCol");
366
+ const center = frame === void 0 ? dedicatedCenter : firstByClassSuffix(frame, "_centerCol");
367
+ const details = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_detailsCol");
368
+ const handle = frame === void 0 ? void 0 : firstByClassSuffix(frame, "_handle");
369
+ if (center === void 0) return;
288
370
  if (center !== void 0) {
289
371
  center.dataset.dshMobileCenter = "true";
290
372
  center.querySelector("header")?.setAttribute("data-dsh-mobile-header", "true");
373
+ viewArea = firstByClassSuffix(center, "_viewArea");
374
+ if (viewArea !== void 0) viewArea.dataset.dshMobileView = "true";
291
375
  const conversation = center.querySelector("[data-conversation-scroll]");
292
376
  const messageColumn = conversation === null ? void 0 : firstByClassSuffix(conversation, "_column");
293
377
  const messageScroll = messageColumn?.parentElement;
@@ -299,7 +383,7 @@ window.__ModuleLoader__.load({
299
383
  if (handle !== void 0) handle.dataset.dshMobileHandle = "true";
300
384
  if (details !== void 0) {
301
385
  details.dataset.dshMobileDetails = "true";
302
- const lastColumn = frame.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
386
+ const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\s+/).at(-1);
303
387
  details.dataset.open = String(lastColumn !== void 0 && lastColumn !== "0px" && lastColumn !== "0");
304
388
  }
305
389
  const settings = Array.from(document.querySelectorAll("[role=\"dialog\"]")).find((dialog) => {
@@ -354,12 +438,20 @@ window.__ModuleLoader__.load({
354
438
  return () => {
355
439
  observer.disconnect();
356
440
  if (scheduled !== 0) cancelAnimationFrame(scheduled);
441
+ if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame);
442
+ if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame);
443
+ if (transitionTimer !== 0) clearTimeout(transitionTimer);
444
+ transitionTarget?.removeAttribute("data-dsh-mobile-view-transition");
445
+ document.removeEventListener("pointerdown", onPointerDown, true);
446
+ document.removeEventListener("keydown", onKeyDown, true);
447
+ document.removeEventListener("click", animateNavigation);
357
448
  backdrop.remove();
358
449
  attach.remove();
359
450
  fileInput.remove();
360
451
  sheet.remove();
361
452
  sheetMask.remove();
362
453
  document.documentElement.classList.remove("dsh-native-mobile-active");
454
+ delete document.documentElement.dataset.dshMobileInput;
363
455
  };
364
456
  }
365
457
  //#endregion
@@ -594,16 +686,17 @@ window.__ModuleLoader__.load({
594
686
  .dsh-mobile-control__actions{display:flex;flex-wrap:wrap;gap:8px}.dsh-mobile-control__actions button{min-height:40px;padding:8px 12px;border-radius:11px;font:inherit;cursor:pointer}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}
595
687
  .dsh-mobile-control__trigger{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px;padding:6px 2px 6px 10px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font:14px/22px system-ui;cursor:pointer}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger.is-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;border-radius:50%}.dsh-mobile-control__trigger-icon{position:relative;box-sizing:border-box;flex:none;width:14px;height:19px;border:1.7px solid currentColor;border-radius:3px}.dsh-mobile-control__trigger-icon::after{position:absolute;right:4px;bottom:2px;width:4px;height:1.5px;border-radius:2px;background:currentColor;content:""}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}
596
688
  `;
597
- /** Mount the desktop control or the stock DSH mobile adaptation. */
689
+ /** Mount the desktop control or mobile feature enhancements. */
598
690
  function apply(ctx) {
599
691
  ctx.effect(() => {
692
+ const loopback = isLoopbackHost(location.hostname) && !new URLSearchParams(location.search).has("dsh-mobile-preview");
600
693
  const style = element("style");
601
694
  style.dataset.plugin = "dsh-mobile";
602
- style.textContent = `${NATIVE_MOBILE_STYLES}\n${CONTROL_STYLES}`;
695
+ style.textContent = loopback ? CONTROL_STYLES : NATIVE_MOBILE_STYLES;
603
696
  document.head.append(style);
604
- if (!(isLoopbackHost(location.hostname) && !new URLSearchParams(location.search).has("dsh-mobile-preview"))) {
605
- const removeSurface = installNativeMobileSurface();
697
+ if (!loopback) {
606
698
  const removeCustom = installCustomAssets();
699
+ const removeSurface = installNativeMobileSurface();
607
700
  return () => {
608
701
  removeCustom();
609
702
  removeSurface();
package/lib/client.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"client.js","names":["createElement"],"sources":["../src/native-mobile.ts","../src/client.ts"],"sourcesContent":["/** Mobile layout rules applied to the stock DSH React surface. */\nexport const NATIVE_MOBILE_STYLES = `\n@media (max-width:720px) {\n html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }\n [data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }\n [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }\n [data-dsh-mobile-center] > * { min-width:0 !important; }\n [data-dsh-mobile-header] { min-width:0; padding-right:12px !important; padding-left:50px !important; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { box-sizing:border-box !important; min-width:0; min-height:52px !important; height:52px !important; gap:6px !important; padding:4px 8px !important; }\n [data-dsh-mobile-header] [class*=\"_titleCluster\"] { min-width:0; }\n [data-dsh-mobile-header] [class*=\"_crumbs\"] { min-width:0; overflow:hidden; }\n [data-dsh-mobile-header] [class*=\"_crumb\"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { min-width:0; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-header] [class*=\"_headerUtilities\"] { gap:2px !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] > * { display:none !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { color:var(--dsw-text, #171a21); content:\"日志\"; font-size:11px; font-weight:600; }\n [data-dsh-mobile-header] [class*=\"_tabs\"] { min-height:42px; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_tabs\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:0 auto 0 0 !important; height:100dvh !important; transition:transform 160ms ease !important; }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }\n [data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle] svg { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle]::after { width:20px; height:2px; border-radius:2px; background:currentColor; box-shadow:0 -6px 0 currentColor,0 6px 0 currentColor; content:\"\"; }\n .dsh-native-mobile-backdrop { position:fixed; z-index:235; inset:0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-backdrop[hidden] { display:none; }\n [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform 160ms ease; background:var(--dsw-bg, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-details][data-open=\"true\"] { transform:translateX(0); }\n [data-dsh-mobile-handle] { display:none !important; }\n [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; }\n [data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-subtle,#e8ebef); }\n [data-dsh-mobile-settings-nav] [class*=\"_navTitle\"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }\n [data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }\n [data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-settings-list] [class*=\"_navCell\"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }\n [data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }\n [data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }\n [data-dsh-mobile-settings-header] [class*=\"_close\"] { width:36px !important; height:36px !important; }\n [data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }\n [data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }\n [data-dsh-mobile-center] textarea { font-size:16px !important; }\n [data-dsh-mobile-center] table { display:block; max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:12px 16px 20px !important; }\n [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:14px !important; }\n [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }\n [data-dsh-mobile-center] [class*=\"_composer\"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_row\"] { display:flex !important; align-items:center !important; gap:4px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_tools\"] { flex:0 0 auto !important; width:auto !important; min-width:0 !important; gap:6px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_trailing\"] { flex:1 1 auto !important; min-width:0 !important; gap:6px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_trailing\"] [class*=\"_root\"]:has(button[aria-label^=\"选择模型\"]) { flex:1 1 auto !important; min-width:0 !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) button[aria-label^=\"选择模型\"] { width:100% !important; min-width:0 !important; padding-left:6px !important; padding-right:4px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) button[aria-label^=\"选择模型\"] [class*=\"_triggerLabel\"] { max-width:78px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) { box-sizing:border-box !important; width:100% !important; padding:0 0 8px !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) > [class=\"\"]:last-child { display:none !important; }\n .dsh-native-mobile-attach { display:inline-flex; align-items:center; justify-content:center; flex:0 0 44px; width:44px; height:44px; padding:0; border:0; border-radius:12px; background:transparent; color:var(--dsw-text-secondary,#5e6673); cursor:pointer; }\n .dsh-native-mobile-attach:active { background:var(--dsw-bg-subtle,#f1f3f6); }\n .dsh-native-mobile-attach:focus-visible { outline:2px solid var(--dsw-accent,#4c7eea); outline-offset:2px; }\n .dsh-native-mobile-attach:disabled { cursor:not-allowed; opacity:.38; }\n .dsh-native-mobile-attach svg { width:22px; height:22px; }\n .dsh-native-mobile-sheet-mask { position:fixed; z-index:1080; inset:0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-sheet-mask[hidden] { display:none; }\n .dsh-native-mobile-sheet { position:fixed; z-index:1081; right:0; bottom:0; left:0; display:flex; flex-direction:column; gap:8px; max-height:min(78dvh,680px); padding:8px 16px max(18px,env(safe-area-inset-bottom)); overflow-y:auto; border-radius:22px 22px 0 0; background:var(--dsw-alias-bg-layer-2,#fff); box-shadow:0 -16px 44px rgb(15 23 42 / 16%); }\n .dsh-native-mobile-sheet[hidden] { display:none; }\n .dsh-native-mobile-sheet__header { position:sticky; z-index:1; top:-8px; display:flex; align-items:center; justify-content:space-between; min-height:48px; padding:8px 0; background:var(--dsw-alias-bg-layer-2,#fff); }\n .dsh-native-mobile-sheet__header strong { font-size:17px; font-weight:600; }\n .dsh-native-mobile-sheet__close { width:44px; height:44px; border:0; border-radius:12px; background:transparent; color:inherit; font-size:24px; }\n .dsh-native-mobile-sheet__path { margin:0 0 4px; overflow-wrap:anywhere; color:var(--dsw-alias-label-secondary,#6b7280); font-size:12px; }\n .dsh-native-mobile-sheet__choice { display:flex; flex-direction:column; align-items:flex-start; gap:2px; width:100%; min-height:56px; padding:10px 14px; border:1px solid var(--dsw-alias-border-subtle,#e2e6eb); border-radius:14px; background:var(--dsw-alias-bg-layer-2,#fff); color:inherit; text-align:left; }\n .dsh-native-mobile-sheet__choice:active { background:var(--dsw-alias-interactive-bg-hover,#f1f3f6); }\n .dsh-native-mobile-sheet__choice small { color:var(--dsw-alias-label-secondary,#6b7280); }\n .dsh-native-mobile-sheet__error { margin:4px 0; color:var(--dsw-alias-status-error,#b42318); font-size:13px; }\n}\n@media (max-width:420px) {\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { max-width:42vw; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { min-height:50px; }\n}\n@media (prefers-reduced-motion:reduce) {\n [data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }\n}\n`\n\nfunction classToken(element: Element, suffix: string): boolean {\n return Array.from(element.classList).some(value => value.endsWith(suffix))\n}\n\nfunction firstByClassSuffix(root: ParentNode, suffix: string): HTMLElement | undefined {\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(element => classToken(element, suffix))\n}\n\nfunction composerCard(textarea: HTMLTextAreaElement): HTMLElement | undefined {\n let candidate: HTMLElement | null = textarea.parentElement\n while (candidate !== null && !classToken(candidate, '_card')) candidate = candidate.parentElement\n return candidate ?? undefined\n}\n\nfunction paperclipIcon(): SVGSVGElement {\n const namespace = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(namespace, 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.setAttribute('aria-hidden', 'true')\n const path = document.createElementNS(namespace, 'path')\n path.setAttribute('d', 'm21.4 11.6-8.9 8.9a6 6 0 0 1-8.5-8.5l9.6-9.6a4 4 0 0 1 5.7 5.7l-9.7 9.6a2 2 0 0 1-2.8-2.8l8.9-8.9')\n svg.append(path)\n return svg\n}\n\n/** Add mobile semantics to the stock DSH layout without replacing its React tree. */\nexport function installNativeMobileSurface(): () => void {\n document.documentElement.classList.add('dsh-native-mobile-active')\n const backdrop = document.createElement('button')\n backdrop.type = 'button'\n backdrop.className = 'dsh-native-mobile-backdrop'\n backdrop.hidden = true\n backdrop.setAttribute('aria-label', '关闭工作区导航')\n document.body.append(backdrop)\n let frame: HTMLElement | undefined\n let sidebar: HTMLElement | undefined\n let sidebarRoot: HTMLElement | undefined\n let toggle: HTMLButtonElement | undefined\n let scheduled = 0\n const fileInput = document.createElement('input')\n fileInput.type = 'file'\n fileInput.accept = 'image/*'\n fileInput.multiple = true\n fileInput.hidden = true\n fileInput.setAttribute('aria-hidden', 'true')\n const attach = document.createElement('button')\n attach.type = 'button'\n attach.className = 'dsh-native-mobile-attach'\n attach.setAttribute('aria-label', '添加图片')\n attach.setAttribute('title', '添加图片')\n attach.append(paperclipIcon())\n document.body.append(fileInput)\n const sheetMask = document.createElement('button')\n sheetMask.type = 'button'\n sheetMask.className = 'dsh-native-mobile-sheet-mask'\n sheetMask.hidden = true\n sheetMask.setAttribute('aria-label', '关闭图片选择')\n const sheet = document.createElement('section')\n sheet.className = 'dsh-native-mobile-sheet'\n sheet.hidden = true\n sheet.setAttribute('aria-label', '添加图片')\n document.body.append(sheetMask, sheet)\n\n const handFilesToComposer = (files: readonly File[]): void => {\n const textarea = document.querySelector<HTMLTextAreaElement>('[data-dsh-mobile-center] textarea')\n if (textarea === null || files.length === 0) return\n const paste = new Event('paste', { bubbles: true, cancelable: true })\n Object.defineProperty(paste, 'clipboardData', {\n value: Object.freeze({\n files,\n items: Object.freeze(files.map(file => Object.freeze({\n kind: 'file',\n type: file.type,\n getAsFile: () => file,\n }))),\n getData: () => '',\n }),\n })\n textarea.dispatchEvent(paste)\n }\n\n const closeSheet = (): void => {\n sheet.hidden = true\n sheetMask.hidden = true\n sheet.replaceChildren()\n }\n const sheetHeader = (title: string): void => {\n const header = document.createElement('header')\n header.className = 'dsh-native-mobile-sheet__header'\n const heading = document.createElement('strong')\n heading.textContent = title\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'dsh-native-mobile-sheet__close'\n close.setAttribute('aria-label', '关闭')\n close.textContent = '×'\n close.addEventListener('click', closeSheet)\n header.append(heading, close)\n sheet.append(header)\n }\n const choice = (title: string, detail: string, action: () => void): HTMLButtonElement => {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = 'dsh-native-mobile-sheet__choice'\n const label = document.createElement('span')\n label.textContent = title\n const hint = document.createElement('small')\n hint.textContent = detail\n button.append(label, hint)\n button.addEventListener('click', action)\n return button\n }\n const showSheet = (): void => {\n sheet.hidden = false\n sheetMask.hidden = false\n }\n const openComputerImages = async (path?: string): Promise<void> => {\n sheet.replaceChildren()\n sheetHeader('电脑图片')\n const progress = document.createElement('p')\n progress.className = 'dsh-native-mobile-sheet__path'\n progress.textContent = '正在读取电脑目录…'\n sheet.append(progress)\n showSheet()\n try {\n const suffix = path === undefined ? '' : `?path=${encodeURIComponent(path)}`\n const response = await fetch(`/mobile-access/computer-images${suffix}`, { credentials: 'same-origin', cache: 'no-store' })\n if (!response.ok) throw new Error(`读取失败(${String(response.status)})`)\n const listing = await response.json() as {\n readonly path: string\n readonly parent?: string\n readonly entries: readonly { readonly kind: 'directory' | 'image'; readonly name: string; readonly path: string }[]\n }\n progress.textContent = listing.path\n if (listing.parent !== undefined) sheet.append(choice('返回上一级', listing.parent, () => { void openComputerImages(listing.parent) }))\n for (const entry of listing.entries) {\n if (entry.kind === 'directory') {\n sheet.append(choice(entry.name, '文件夹', () => { void openComputerImages(entry.path) }))\n continue\n }\n const row = choice(entry.name, '添加这张电脑图片', () => {\n row.disabled = true\n void fetch(`/mobile-access/computer-image?path=${encodeURIComponent(entry.path)}`, { credentials: 'same-origin', cache: 'no-store' })\n .then(async response => {\n if (!response.ok) throw new Error(`读取失败(${String(response.status)})`)\n const blob = await response.blob()\n handFilesToComposer([new File([blob], entry.name, { type: blob.type })])\n closeSheet()\n })\n .catch(error => {\n row.disabled = false\n progress.className = 'dsh-native-mobile-sheet__error'\n progress.textContent = error instanceof Error ? error.message : String(error)\n })\n })\n sheet.append(row)\n }\n } catch (error) {\n progress.className = 'dsh-native-mobile-sheet__error'\n progress.textContent = error instanceof Error ? error.message : String(error)\n }\n }\n const openAttachmentSources = (): void => {\n sheet.replaceChildren()\n sheetHeader('添加图片')\n sheet.append(\n choice('从手机选择', '相册、相机或手机存储', () => { closeSheet(); fileInput.click() }),\n choice('从电脑选择', '在手机页面浏览电脑中的图片', () => { void openComputerImages() }),\n )\n showSheet()\n }\n attach.addEventListener('click', openAttachmentSources)\n sheetMask.addEventListener('click', closeSheet)\n fileInput.addEventListener('change', () => {\n handFilesToComposer([...(fileInput.files ?? [])])\n fileInput.value = ''\n })\n\n const sync = (): void => {\n scheduled = 0\n frame = firstByClassSuffix(document, '_frame')\n if (frame === undefined) return\n frame.dataset.dshMobileFrame = 'true'\n sidebar = firstByClassSuffix(frame, '_sidebarCol')\n const center = firstByClassSuffix(frame, '_centerCol')\n const details = firstByClassSuffix(frame, '_detailsCol')\n const handle = firstByClassSuffix(frame, '_handle')\n if (center !== undefined) {\n center.dataset.dshMobileCenter = 'true'\n center.querySelector<HTMLElement>('header')?.setAttribute('data-dsh-mobile-header', 'true')\n const conversation = center.querySelector<HTMLElement>('[data-conversation-scroll]')\n const messageColumn = conversation === null ? undefined : firstByClassSuffix(conversation, '_column')\n const messageScroll = messageColumn?.parentElement\n if (messageColumn !== undefined && messageScroll !== null && messageScroll !== undefined && classToken(messageScroll, '_scroll')) {\n messageColumn.dataset.dshMobileMessageColumn = 'true'\n messageScroll.dataset.dshMobileMessageScroll = 'true'\n }\n }\n if (handle !== undefined) handle.dataset.dshMobileHandle = 'true'\n if (details !== undefined) {\n details.dataset.dshMobileDetails = 'true'\n const lastColumn = frame.style.gridTemplateColumns.trim().split(/\\s+/).at(-1)\n details.dataset.open = String(lastColumn !== undefined && lastColumn !== '0px' && lastColumn !== '0')\n }\n const settings = Array.from(document.querySelectorAll<HTMLElement>('[role=\"dialog\"]')).find(dialog => {\n const directNav = Array.from(dialog.children).find(child => child instanceof HTMLElement && classToken(child, '_nav'))\n return directNav !== undefined\n })\n if (settings !== undefined) {\n settings.dataset.dshMobileSettings = 'true'\n const nav = Array.from(settings.children).find(child => child instanceof HTMLElement && classToken(child, '_nav')) as HTMLElement | undefined\n const content = Array.from(settings.children).find(child => child instanceof HTMLElement && classToken(child, '_content')) as HTMLElement | undefined\n if (nav !== undefined) {\n nav.dataset.dshMobileSettingsNav = 'true'\n firstByClassSuffix(nav, '_navList')?.setAttribute('data-dsh-mobile-settings-list', 'true')\n }\n if (content !== undefined) {\n content.dataset.dshMobileSettingsContent = 'true'\n firstByClassSuffix(content, '_header')?.setAttribute('data-dsh-mobile-settings-header', 'true')\n firstByClassSuffix(content, '_options')?.setAttribute('data-dsh-mobile-settings-options', 'true')\n }\n }\n const textarea = center?.querySelector<HTMLTextAreaElement>('textarea')\n const card = textarea === null || textarea === undefined ? undefined : composerCard(textarea)\n const tools = card === undefined ? undefined : firstByClassSuffix(card, '_tools')\n attach.disabled = textarea === undefined || textarea === null || textarea.disabled || textarea.readOnly\n if (tools !== undefined && attach.parentElement !== tools) tools.append(attach)\n if (sidebar === undefined) return\n sidebar.dataset.dshMobileSidebar = 'true'\n toggle = firstByClassSuffix(sidebar, '_toggle') as HTMLButtonElement | undefined\n let candidate = toggle?.parentElement\n while (candidate !== undefined && candidate !== null && candidate !== sidebar && !classToken(candidate, '_root')) candidate = candidate.parentElement\n sidebarRoot = candidate !== sidebar && candidate !== null ? candidate : undefined\n if (sidebarRoot === undefined) return\n sidebarRoot.dataset.dshMobileSidebarRoot = 'true'\n if (toggle !== undefined) toggle.dataset.dshMobileToggle = 'true'\n const collapsed = classToken(sidebarRoot, '_collapsed')\n sidebar.dataset.open = String(!collapsed)\n backdrop.hidden = collapsed\n }\n const schedule = (): void => { if (scheduled === 0) scheduled = requestAnimationFrame(sync) }\n const observer = new MutationObserver(schedule)\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] })\n backdrop.addEventListener('click', () => { if (sidebar?.dataset.open === 'true') toggle?.click() })\n sync()\n return () => {\n observer.disconnect()\n if (scheduled !== 0) cancelAnimationFrame(scheduled)\n backdrop.remove()\n attach.remove()\n fileInput.remove()\n sheet.remove()\n sheetMask.remove()\n document.documentElement.classList.remove('dsh-native-mobile-active')\n }\n}\n","import { createElement } from 'react'\nimport { installNativeMobileSurface, NATIVE_MOBILE_STYLES } from './native-mobile.js'\n\ninterface ClientContext {\n effect(effect: () => void | (() => void), label?: string): void\n slots: {\n inject(key: string, callback: () => (() => void)): () => void\n register(options: { name: string; id: string }, component: (props: { wide: boolean }) => unknown): () => void\n }\n}\n\ninterface MobileExtensionContext {\n readonly document: Document\n readonly request: (path: string, init?: RequestInit) => Promise<Response>\n readonly root: HTMLElement\n readonly window: Window\n}\n\ntype MobileExtensionMount = (context: MobileExtensionContext) => void | (() => void)\n\ndeclare global {\n interface Window {\n dshMobile?: { register(mount: MobileExtensionMount): void }\n }\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]'\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className !== undefined) node.className = className\n return node\n}\n\nasync function requestJson(path: string, init?: RequestInit): Promise<Record<string, unknown>> {\n const response = await fetch(path, { ...init, headers: { 'content-type': 'application/json', ...init?.headers } })\n const body = await response.json() as Record<string, unknown>\n if (!response.ok) throw new Error(typeof body.error === 'string' ? body.error : `HTTP ${String(response.status)}`)\n return body\n}\n\nfunction installControl(): { remove: () => void; toggle: () => void } {\n const root = element('div', 'dsh-mobile-control')\n const panel = element('section', 'dsh-mobile-control__panel'); panel.hidden = true\n panel.setAttribute('aria-label', '移动访问')\n const header = element('header', 'dsh-mobile-control__header')\n const title = element('h2'); title.textContent = '移动访问'\n const close = element('button', 'dsh-mobile-control__close'); close.type = 'button'; close.textContent = '×'; close.setAttribute('aria-label', '收起移动访问')\n const access = element('div', 'dsh-mobile-control__access'); access.hidden = true\n const accessLabel = element('span', 'dsh-mobile-control__access-label'); accessLabel.textContent = '浏览器访问'\n const accessLink = element('a', 'dsh-mobile-control__access-link'); accessLink.target = '_blank'; accessLink.rel = 'noreferrer'\n access.append(accessLabel, accessLink)\n const status = element('p', 'dsh-mobile-control__status'); status.textContent = '正在读取状态…'\n const actions = element('div', 'dsh-mobile-control__actions')\n const toggle = element('button', 'dsh-mobile-control__secondary'); toggle.type = 'button'\n const pair = element('button', 'dsh-mobile-control__primary'); pair.type = 'button'; pair.textContent = '生成并复制密钥'\n header.append(title, close); actions.append(toggle, pair); panel.append(header, access, status, actions); root.append(panel); document.body.append(root)\n let running = false\n let origin = ''\n const setOpen = (open: boolean): void => {\n panel.hidden = !open\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger')) trigger.setAttribute('aria-expanded', String(open))\n }\n const render = (data: Record<string, unknown>): void => {\n running = data.running === true\n origin = running && typeof data.origin === 'string' ? data.origin : ''\n access.hidden = origin === ''\n accessLink.href = origin\n accessLink.textContent = origin\n accessLink.title = origin\n status.classList.toggle('is-running', running)\n status.textContent = running ? '移动访问已开启。' : '已关闭。DSH 仍只在本机可用。'\n toggle.textContent = running ? '关闭移动访问' : '开启移动访问'; pair.disabled = !running\n }\n toggle.addEventListener('click', () => { toggle.disabled = true; void requestJson('/api/mobile-access/control', { method: 'POST', body: JSON.stringify({ running: !running }) }).then(render, error => { status.textContent = String(error) }).finally(() => { toggle.disabled = false }) })\n pair.addEventListener('click', () => { pair.disabled = true; void requestJson('/api/mobile-access/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const key = typeof data.appKey === 'string' ? data.appKey : ''\n if (key === '') { status.textContent = '无法生成配对密钥。'; return }\n try {\n await navigator.clipboard.writeText(key)\n status.textContent = '配对密钥已复制,请粘贴到 Android App。'\n } catch {\n status.textContent = `请复制配对密钥:${key}`\n status.classList.add('is-key')\n }\n }, error => { status.textContent = String(error) }).finally(() => { pair.disabled = !running }) })\n close.addEventListener('click', () => { setOpen(false) })\n const dismiss = (event: PointerEvent): void => {\n if (panel.hidden || !(event.target instanceof Node)) return\n if (!panel.contains(event.target) && !document.querySelector('.dsh-mobile-control__trigger')?.contains(event.target)) setOpen(false)\n }\n document.addEventListener('pointerdown', dismiss)\n void requestJson('/api/mobile-access/control').then(render, error => { status.textContent = String(error) })\n return { remove: () => { document.removeEventListener('pointerdown', dismiss); root.remove() }, toggle: () => { setOpen(panel.hidden !== false) } }\n}\n\nfunction mobileRequest(path: string, init: RequestInit = {}): Promise<Response> {\n const target = new URL(path, location.href)\n if (target.origin !== location.origin) throw new TypeError('mobile extension requests must stay on the DSH origin')\n const headers = new Headers(init.headers)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const csrf = document.cookie.split(';').map(value => value.trim()).find(value => value.startsWith('dsh_ma_csrf='))?.slice(12)\n if (csrf !== undefined) headers.set('x-dsh-mobile-csrf', csrf)\n }\n return fetch(target, { ...init, headers, credentials: 'same-origin', cache: 'no-store', redirect: 'error' })\n}\n\nfunction installCustomAssets(): () => void {\n const style = element('style'); style.dataset.plugin = 'dsh-mobile-custom'; document.head.append(style)\n let cssLoading = false\n const refreshCss = async (): Promise<void> => { if (cssLoading || document.visibilityState === 'hidden') return; cssLoading = true; try { const response = await fetch('/mobile-access/custom.css', { cache: 'no-store', credentials: 'same-origin' }); if (response.ok) { const css = await response.text(); if (style.textContent !== css) style.textContent = css } } finally { cssLoading = false } }\n let activeRoot: HTMLElement | undefined; let disposeActive: (() => void) | undefined; let source = ''; let pending: MobileExtensionMount | undefined\n const previous = window.dshMobile\n window.dshMobile = Object.freeze({ register: (mount: MobileExtensionMount) => { pending = mount } })\n const registeredMount = (): MobileExtensionMount | undefined => pending\n const refreshJs = async (): Promise<void> => { try { const response = await fetch('/mobile-access/custom.js', { cache: 'no-store', credentials: 'same-origin' }); if (!response.ok) return; const next = await response.text(); if (next === source) return; source = next; pending = undefined; const script = element('script'); script.textContent = `${next}\\n//# sourceURL=dsh-mobile-custom.js`; document.head.append(script); script.remove(); const mount = registeredMount(); if (mount === undefined) return; const nextRoot = element('div'); nextRoot.dataset.dshMobileExtension = 'true'; document.body.append(nextRoot); const nextDispose = mount({ document, request: mobileRequest, root: nextRoot, window }); disposeActive?.(); activeRoot?.remove(); activeRoot = nextRoot; disposeActive = typeof nextDispose === 'function' ? nextDispose : undefined } catch { /* Preserve the last good customization during reconnects. */ } }\n void refreshCss(); void refreshJs()\n const timer = window.setInterval(() => { void refreshCss(); void refreshJs() }, 1_000)\n return () => { clearInterval(timer); disposeActive?.(); activeRoot?.remove(); style.remove(); if (previous === undefined) delete window.dshMobile; else window.dshMobile = previous }\n}\n\nconst CONTROL_STYLES = `\n.dsh-mobile-control{position:fixed;z-index:1000;left:16px;bottom:64px;font:14px/1.45 system-ui;color:var(--dsw-alias-label-primary,#16181d)}\n.dsh-mobile-control__panel{box-sizing:border-box;width:min(328px,calc(100vw - 32px));padding:16px;border:1px solid var(--dsw-alias-border-subtle,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}\n.dsh-mobile-control__header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.dsh-mobile-control__panel h2{margin:0;font-size:17px;line-height:24px}.dsh-mobile-control__close{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:transparent;color:inherit;font-size:24px;line-height:1;cursor:pointer}.dsh-mobile-control__close:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}\n.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:\":\"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:#2563eb;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}\n.dsh-mobile-control__status{margin:0 0 14px;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,#606873)}.dsh-mobile-control__status::before{display:inline-block;width:8px;height:8px;margin-right:7px;border-radius:50%;background:#98a1ad;content:\"\"}.dsh-mobile-control__status.is-running::before{background:#16a36a}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}\n.dsh-mobile-control__actions{display:flex;flex-wrap:wrap;gap:8px}.dsh-mobile-control__actions button{min-height:40px;padding:8px 12px;border-radius:11px;font:inherit;cursor:pointer}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}\n.dsh-mobile-control__trigger{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px;padding:6px 2px 6px 10px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font:14px/22px system-ui;cursor:pointer}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger.is-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;border-radius:50%}.dsh-mobile-control__trigger-icon{position:relative;box-sizing:border-box;flex:none;width:14px;height:19px;border:1.7px solid currentColor;border-radius:3px}.dsh-mobile-control__trigger-icon::after{position:absolute;right:4px;bottom:2px;width:4px;height:1.5px;border-radius:2px;background:currentColor;content:\"\"}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n`\n\n/** Mount the desktop control or the stock DSH mobile adaptation. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n const style = element('style'); style.dataset.plugin = 'dsh-mobile'; style.textContent = `${NATIVE_MOBILE_STYLES}\\n${CONTROL_STYLES}`; document.head.append(style)\n const loopback = isLoopbackHost(location.hostname) && !new URLSearchParams(location.search).has('dsh-mobile-preview')\n if (!loopback) {\n const removeSurface = installNativeMobileSurface()\n const removeCustom = installCustomAssets()\n return () => { removeCustom(); removeSurface(); style.remove() }\n }\n const control = installControl()\n const disposeSlot = ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ name: 'sidebar.footer.action', id: 'dsh-mobile' }, ({ wide }) => createElement('button', {\n 'aria-expanded': false,\n 'aria-label': '移动访问',\n className: `dsh-mobile-control__trigger${wide ? '' : ' is-rail'}`,\n type: 'button',\n title: '移动访问',\n onClick: control.toggle,\n }, createElement('span', { 'aria-hidden': true, className: 'dsh-mobile-control__trigger-icon' }), wide ? createElement('span', { className: 'dsh-mobile-control__trigger-label' }, '移动访问') : undefined)))\n return () => { disposeSlot(); control.remove(); style.remove() }\n }, 'dsh-mobile: stock mobile adaptation and local control')\n}\n\n/** Client services required by the mobile adaptation. */\nexport const inject: readonly string[] = ['slots']\n"],"mappings":"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"}
1
+ {"version":3,"file":"client.js","names":["createElement"],"sources":["../src/native-mobile.ts","../src/client.ts"],"sourcesContent":["/** Mobile feature and compatibility rules applied to DSH React surfaces. */\nexport const NATIVE_MOBILE_STYLES = `\n@media (max-width:720px) {\n html.dsh-native-mobile-active,html.dsh-native-mobile-active body { width:100%; height:100%; overflow:hidden; }\n html.dsh-native-mobile-active { --dsh-mobile-motion-duration:200ms; --dsh-mobile-motion-ease:cubic-bezier(.22,1,.36,1); }\n html.dsh-native-mobile-active :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]) { -webkit-tap-highlight-color:transparent; }\n html.dsh-native-mobile-active[data-dsh-mobile-input=\"touch\"] :is(a,button,[role=\"button\"],[role=\"tab\"],[tabindex]):focus { outline:none !important; }\n [data-dsh-mobile-frame] { grid-template-columns:0 minmax(0,1fr) 0 !important; width:100% !important; height:100dvh !important; overflow:hidden !important; }\n [data-dsh-mobile-center] { grid-column:2 !important; width:100vw !important; min-width:0 !important; }\n [data-dsh-mobile-center] > * { min-width:0 !important; }\n [data-dsh-mobile-header] { min-width:0; padding-right:12px !important; padding-left:50px !important; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { box-sizing:border-box !important; min-width:0; min-height:52px !important; height:52px !important; gap:6px !important; padding:4px 8px !important; }\n [data-dsh-mobile-header] [class*=\"_titleCluster\"] { min-width:0; }\n [data-dsh-mobile-header] [class*=\"_crumbs\"] { min-width:0; overflow:hidden; }\n [data-dsh-mobile-header] [class*=\"_crumb\"] { max-width:46vw; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { min-width:0; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_headerActions\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-header] [class*=\"_headerUtilities\"] { gap:2px !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] { width:40px; min-width:40px; padding:0 !important; overflow:hidden; color:transparent; font-size:0 !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"] > * { display:none !important; }\n [data-dsh-mobile-header] [class*=\"_sessionLogButton\"]::after { color:var(--dsw-text, #171a21); content:\"日志\"; font-size:11px; font-weight:600; }\n [data-dsh-mobile-header] [class*=\"_tabs\"] { min-height:42px; overflow-x:auto; scrollbar-width:none; }\n [data-dsh-mobile-header] [class*=\"_tabs\"]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-sidebar] { position:fixed !important; z-index:240 !important; inset:0 auto 0 0 !important; width:0 !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar-root] { position:fixed !important; z-index:241 !important; inset:0 auto 0 0 !important; height:100dvh !important; transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease) !important; }\n [data-dsh-mobile-sidebar][data-open=\"true\"] [data-dsh-mobile-sidebar-root] { width:min(88vw,340px) !important; box-shadow:18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] { width:0 !important; border:0 !important; background:transparent !important; box-shadow:none !important; overflow:visible !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :not(:has([data-dsh-mobile-toggle])) { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) { position:fixed !important; z-index:244 !important; top:env(safe-area-inset-top) !important; left:0 !important; width:50px !important; height:52px !important; padding:4px !important; border:0 !important; background:transparent !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-sidebar-root] > :has([data-dsh-mobile-toggle]) > :not([data-dsh-mobile-toggle]) { display:none !important; }\n [data-dsh-mobile-toggle] { width:44px !important; height:44px !important; min-width:44px !important; min-height:44px !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle] svg { display:none !important; }\n [data-dsh-mobile-sidebar][data-open=\"false\"] [data-dsh-mobile-toggle]::after { width:20px; height:2px; border-radius:2px; background:currentColor; box-shadow:0 -6px 0 currentColor,0 6px 0 currentColor; content:\"\"; }\n .dsh-native-mobile-backdrop { position:fixed; z-index:235; inset:0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-backdrop:not([hidden]) { animation:dsh-mobile-fade-in var(--dsh-mobile-motion-duration) ease-out; }\n .dsh-native-mobile-backdrop[hidden] { display:none; }\n [data-dsh-mobile-details] { position:fixed !important; z-index:250 !important; inset:0 0 0 auto !important; width:min(94vw,460px) !important; max-width:none !important; transform:translateX(100%); transition:transform var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); background:var(--dsw-bg, #fff); box-shadow:-18px 0 46px rgb(15 23 42 / 18%); }\n [data-dsh-mobile-details][data-open=\"true\"] { transform:translateX(0); }\n [data-dsh-mobile-handle] { display:none !important; }\n [data-dsh-mobile-settings] { flex-direction:column !important; width:100vw !important; height:100dvh !important; max-width:none !important; border-radius:0 !important; animation:dsh-mobile-panel-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-settings-nav] { flex:none !important; width:100% !important; padding:max(14px,env(safe-area-inset-top)) 12px 8px !important; gap:10px !important; border-bottom:1px solid var(--dsw-alias-border-subtle,#e8ebef); }\n [data-dsh-mobile-settings-nav] [class*=\"_navTitle\"] { padding:0 8px !important; font-size:18px !important; line-height:28px !important; }\n [data-dsh-mobile-settings-list] { flex-direction:row !important; gap:4px !important; overflow-x:auto !important; scrollbar-width:none; }\n [data-dsh-mobile-settings-list]::-webkit-scrollbar { display:none; }\n [data-dsh-mobile-settings-list] [class*=\"_navCell\"] { flex:0 0 auto !important; min-width:max-content !important; height:44px !important; padding:10px 12px !important; }\n [data-dsh-mobile-settings-list] [aria-current=\"true\"] { border-color:transparent !important; outline:0 !important; box-shadow:none !important; }\n [data-dsh-mobile-settings-content] { flex:1 1 auto !important; width:100% !important; min-height:0 !important; }\n [data-dsh-mobile-settings-header] { height:48px !important; min-height:48px !important; padding:10px 12px 6px !important; }\n [data-dsh-mobile-settings-header] [class*=\"_close\"] { width:36px !important; height:36px !important; }\n [data-dsh-mobile-settings-options] { box-sizing:border-box !important; width:100% !important; padding:4px 16px max(24px,env(safe-area-inset-bottom)) !important; overflow-x:hidden !important; }\n [data-dsh-mobile-settings-options] > * { width:100% !important; min-width:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] > [class*=\"_row\"] { flex-direction:column !important; align-items:stretch !important; gap:12px !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_rowText\"] { width:100% !important; padding-right:0 !important; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { box-sizing:border-box !important; align-self:flex-start !important; justify-content:space-between !important; min-width:0 !important; min-height:44px !important; max-width:100% !important; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea,button) { max-width:100%; }\n [data-dsh-mobile-settings-options] :is(input,select,textarea) { box-sizing:border-box; width:100%; min-width:0; }\n [data-dsh-mobile-settings-options] [class*=\"_head\"] { min-width:0; flex-wrap:wrap; }\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"] { animation:dsh-mobile-view-in var(--dsh-mobile-motion-duration) var(--dsh-mobile-motion-ease); }\n [data-dsh-mobile-center] textarea { font-size:16px !important; }\n [data-dsh-mobile-center] table { display:block; max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-center] pre { max-width:100%; overflow-x:auto; }\n [data-dsh-mobile-center] :is(img,video,canvas,svg) { max-width:100%; }\n [data-dsh-mobile-message-scroll] { box-sizing:border-box !important; width:100% !important; padding:12px 16px 20px !important; }\n [data-dsh-mobile-message-column] { box-sizing:border-box !important; width:100% !important; max-width:none !important; margin:0 !important; padding:0 !important; gap:14px !important; }\n [data-dsh-mobile-message-column] > * { width:100% !important; max-width:100% !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] { box-sizing:border-box !important; display:grid !important; grid-template-columns:16px minmax(0,1fr) !important; grid-auto-rows:auto !important; align-items:center !important; column-gap:6px !important; width:100% !important; height:auto !important; min-height:44px !important; padding:5px 0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_leading\"] { grid-column:1 !important; grid-row:1 !important; margin-right:0 !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_title\"] { grid-column:2 !important; grid-row:1 !important; min-width:0 !important; overflow:hidden !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_sep\"],[class*=\"_separator\"]) { display:none !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > :is([class*=\"_summary\"],[class*=\"_fileLink\"]) { grid-column:2 !important; grid-row:2 !important; width:100% !important; min-width:0 !important; max-width:100% !important; overflow:hidden !important; line-height:20px !important; text-overflow:ellipsis !important; white-space:nowrap !important; }\n [data-dsh-mobile-message-column] [data-disclosure-row] > [class*=\"_summarySuffix\"] { grid-column:2 !important; grid-row:3 !important; margin-left:0 !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { display:grid !important; grid-template-columns:minmax(72px,30%) minmax(0,1fr) !important; gap:4px 10px !important; }\n [data-dsh-mobile-message-column] [class*=\"_ioSection\"] { grid-template-columns:1fr !important; row-gap:4px !important; }\n [data-dsh-mobile-message-column] [class*=\"_body\"] { max-width:100% !important; overflow-wrap:anywhere; }\n [data-dsh-mobile-center] [class*=\"_composer\"] { padding-left:8px !important; padding-right:8px !important; padding-bottom:max(8px,env(safe-area-inset-bottom)) !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_row\"] { display:flex !important; align-items:center !important; gap:4px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_tools\"] { flex:0 0 auto !important; width:auto !important; min-width:0 !important; gap:6px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_trailing\"] { flex:1 1 auto !important; min-width:0 !important; gap:6px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) [class*=\"_trailing\"] [class*=\"_root\"]:has(button[aria-label^=\"选择模型\"]) { flex:1 1 auto !important; min-width:0 !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) button[aria-label^=\"选择模型\"] { width:100% !important; min-width:0 !important; padding-left:6px !important; padding-right:4px !important; }\n [data-dsh-mobile-center] [class*=\"_card\"]:has(textarea) button[aria-label^=\"选择模型\"] [class*=\"_triggerLabel\"] { max-width:78px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) { box-sizing:border-box !important; width:100% !important; padding:0 0 8px !important; }\n [data-dsh-mobile-center] [class*=\"_root\"]:has(> [class*=\"_card\"] textarea) > [class=\"\"]:last-child { display:none !important; }\n .dsh-native-mobile-attach { display:inline-flex; align-items:center; justify-content:center; flex:0 0 44px; width:44px; height:44px; padding:0; border:0; border-radius:12px; background:transparent; color:var(--dsw-text-secondary,#5e6673); cursor:pointer; }\n .dsh-native-mobile-attach:active { background:var(--dsw-bg-subtle,#f1f3f6); }\n .dsh-native-mobile-attach:focus-visible { outline:2px solid var(--dsw-accent,#4c7eea); outline-offset:2px; }\n .dsh-native-mobile-attach:disabled { cursor:not-allowed; opacity:.38; }\n .dsh-native-mobile-attach svg { width:22px; height:22px; }\n .dsh-native-mobile-sheet-mask { position:fixed; z-index:1080; inset:0; border:0; background:rgb(15 23 42 / 32%); }\n .dsh-native-mobile-sheet-mask[hidden] { display:none; }\n .dsh-native-mobile-sheet { position:fixed; z-index:1081; right:0; bottom:0; left:0; display:flex; flex-direction:column; gap:8px; max-height:min(78dvh,680px); padding:8px 16px max(18px,env(safe-area-inset-bottom)); overflow-y:auto; border-radius:22px 22px 0 0; background:var(--dsw-alias-bg-layer-2,#fff); box-shadow:0 -16px 44px rgb(15 23 42 / 16%); animation:dsh-mobile-sheet-in 220ms var(--dsh-mobile-motion-ease); }\n .dsh-native-mobile-sheet[hidden] { display:none; }\n .dsh-native-mobile-sheet__header { position:sticky; z-index:1; top:-8px; display:flex; align-items:center; justify-content:space-between; min-height:48px; padding:8px 0; background:var(--dsw-alias-bg-layer-2,#fff); }\n .dsh-native-mobile-sheet__header strong { font-size:17px; font-weight:600; }\n .dsh-native-mobile-sheet__close { width:44px; height:44px; border:0; border-radius:12px; background:transparent; color:inherit; font-size:24px; }\n .dsh-native-mobile-sheet__path { margin:0 0 4px; overflow-wrap:anywhere; color:var(--dsw-alias-label-secondary,#6b7280); font-size:12px; }\n .dsh-native-mobile-sheet__choice { display:flex; flex-direction:column; align-items:flex-start; gap:2px; width:100%; min-height:56px; padding:10px 14px; border:1px solid var(--dsw-alias-border-subtle,#e2e6eb); border-radius:14px; background:var(--dsw-alias-bg-layer-2,#fff); color:inherit; text-align:left; }\n .dsh-native-mobile-sheet__choice:active { background:var(--dsw-alias-interactive-bg-hover,#f1f3f6); }\n .dsh-native-mobile-sheet__choice small { color:var(--dsw-alias-label-secondary,#6b7280); }\n .dsh-native-mobile-sheet__error { margin:4px 0; color:var(--dsw-alias-status-error,#b42318); font-size:13px; }\n}\n@keyframes dsh-mobile-fade-in { from { opacity:0; } }\n@keyframes dsh-mobile-panel-in { from { opacity:.72; transform:translateY(6px); } }\n@keyframes dsh-mobile-view-in { from { opacity:.58; transform:translateY(5px); } }\n@keyframes dsh-mobile-sheet-in { from { opacity:.7; transform:translateY(18px); } }\n@media (max-width:420px) {\n [data-dsh-mobile-header] [class*=\"_headerActions\"] { max-width:42vw; }\n [data-dsh-mobile-header] [class*=\"_titleRow\"] { min-height:50px; }\n [data-dsh-mobile-settings-options] [data-slot=\"settings.general.item\"] [class*=\"_selector\"] { align-self:stretch !important; width:100% !important; }\n [data-dsh-mobile-message-column] [data-context-fields] > * { grid-template-columns:1fr !important; }\n}\n@media (prefers-reduced-motion:reduce) {\n [data-dsh-mobile-sidebar-root],[data-dsh-mobile-details] { transition:none !important; }\n .dsh-native-mobile-backdrop:not([hidden]),[data-dsh-mobile-settings],\n [data-dsh-mobile-settings-content][data-dsh-mobile-view-transition=\"true\"],\n [data-dsh-mobile-view][data-dsh-mobile-view-transition=\"true\"],\n .dsh-native-mobile-sheet { animation:none !important; }\n}\n`\n\nfunction classToken(element: Element, suffix: string): boolean {\n return Array.from(element.classList).some(value => value.endsWith(suffix))\n}\n\nfunction firstByClassSuffix(root: ParentNode, suffix: string): HTMLElement | undefined {\n return Array.from(root.querySelectorAll<HTMLElement>('[class]')).find(element => classToken(element, suffix))\n}\n\nfunction composerCard(textarea: HTMLTextAreaElement): HTMLElement | undefined {\n let candidate: HTMLElement | null = textarea.parentElement\n while (candidate !== null && !classToken(candidate, '_card')) candidate = candidate.parentElement\n return candidate ?? undefined\n}\n\nfunction paperclipIcon(): SVGSVGElement {\n const namespace = 'http://www.w3.org/2000/svg'\n const svg = document.createElementNS(namespace, 'svg')\n svg.setAttribute('viewBox', '0 0 24 24')\n svg.setAttribute('fill', 'none')\n svg.setAttribute('stroke', 'currentColor')\n svg.setAttribute('stroke-width', '2')\n svg.setAttribute('stroke-linecap', 'round')\n svg.setAttribute('stroke-linejoin', 'round')\n svg.setAttribute('aria-hidden', 'true')\n const path = document.createElementNS(namespace, 'path')\n path.setAttribute('d', 'm21.4 11.6-8.9 8.9a6 6 0 0 1-8.5-8.5l9.6-9.6a4 4 0 0 1 5.7 5.7l-9.7 9.6a2 2 0 0 1-2.8-2.8l8.9-8.9')\n svg.append(path)\n return svg\n}\n\n/** Add mobile semantics and phone-local file actions without replacing feature trees. */\nexport function installNativeMobileSurface(): () => void {\n document.documentElement.classList.add('dsh-native-mobile-active')\n const setInputMode = (mode: 'keyboard' | 'touch'): void => {\n document.documentElement.dataset.dshMobileInput = mode\n }\n const onPointerDown = (event: PointerEvent): void => {\n if (event.pointerType === 'touch' || event.pointerType === 'pen') setInputMode('touch')\n }\n const onKeyDown = (event: KeyboardEvent): void => {\n if (event.key === 'Tab' || event.key.startsWith('Arrow')) setInputMode('keyboard')\n }\n document.addEventListener('pointerdown', onPointerDown, true)\n document.addEventListener('keydown', onKeyDown, true)\n const backdrop = document.createElement('button')\n backdrop.type = 'button'\n backdrop.className = 'dsh-native-mobile-backdrop'\n backdrop.hidden = true\n backdrop.setAttribute('aria-label', '关闭工作区导航')\n document.body.append(backdrop)\n let frame: HTMLElement | undefined\n let sidebar: HTMLElement | undefined\n let sidebarRoot: HTMLElement | undefined\n let toggle: HTMLButtonElement | undefined\n let viewArea: HTMLElement | undefined\n let scheduled = 0\n let transitionFrame = 0\n let transitionRestartFrame = 0\n let transitionTimer = 0\n let transitionTarget: HTMLElement | undefined\n const fileInput = document.createElement('input')\n fileInput.type = 'file'\n fileInput.accept = 'image/*'\n fileInput.multiple = true\n fileInput.hidden = true\n fileInput.setAttribute('aria-hidden', 'true')\n const attach = document.createElement('button')\n attach.type = 'button'\n attach.className = 'dsh-native-mobile-attach'\n attach.setAttribute('aria-label', '添加图片')\n attach.setAttribute('title', '添加图片')\n attach.append(paperclipIcon())\n document.body.append(fileInput)\n const sheetMask = document.createElement('button')\n sheetMask.type = 'button'\n sheetMask.className = 'dsh-native-mobile-sheet-mask'\n sheetMask.hidden = true\n sheetMask.setAttribute('aria-label', '关闭图片选择')\n const sheet = document.createElement('section')\n sheet.className = 'dsh-native-mobile-sheet'\n sheet.hidden = true\n sheet.setAttribute('aria-label', '添加图片')\n document.body.append(sheetMask, sheet)\n\n const handFilesToComposer = (files: readonly File[]): void => {\n const textarea = document.querySelector<HTMLTextAreaElement>('[data-dsh-mobile-center] textarea')\n if (textarea === null || files.length === 0) return\n const paste = new Event('paste', { bubbles: true, cancelable: true })\n Object.defineProperty(paste, 'clipboardData', {\n value: Object.freeze({\n files,\n items: Object.freeze(files.map(file => Object.freeze({\n kind: 'file',\n type: file.type,\n getAsFile: () => file,\n }))),\n getData: () => '',\n }),\n })\n textarea.dispatchEvent(paste)\n }\n\n const closeSheet = (): void => {\n sheet.hidden = true\n sheetMask.hidden = true\n sheet.replaceChildren()\n }\n const sheetHeader = (title: string): void => {\n const header = document.createElement('header')\n header.className = 'dsh-native-mobile-sheet__header'\n const heading = document.createElement('strong')\n heading.textContent = title\n const close = document.createElement('button')\n close.type = 'button'\n close.className = 'dsh-native-mobile-sheet__close'\n close.setAttribute('aria-label', '关闭')\n close.textContent = '×'\n close.addEventListener('click', closeSheet)\n header.append(heading, close)\n sheet.append(header)\n }\n const choice = (title: string, detail: string, action: () => void): HTMLButtonElement => {\n const button = document.createElement('button')\n button.type = 'button'\n button.className = 'dsh-native-mobile-sheet__choice'\n const label = document.createElement('span')\n label.textContent = title\n const hint = document.createElement('small')\n hint.textContent = detail\n button.append(label, hint)\n button.addEventListener('click', action)\n return button\n }\n const showSheet = (): void => {\n sheet.hidden = false\n sheetMask.hidden = false\n }\n const openComputerImages = async (path?: string): Promise<void> => {\n sheet.replaceChildren()\n sheetHeader('电脑图片')\n const progress = document.createElement('p')\n progress.className = 'dsh-native-mobile-sheet__path'\n progress.textContent = '正在读取电脑目录…'\n sheet.append(progress)\n showSheet()\n try {\n const suffix = path === undefined ? '' : `?path=${encodeURIComponent(path)}`\n const response = await fetch(`/mobile-access/computer-images${suffix}`, { credentials: 'same-origin', cache: 'no-store' })\n if (!response.ok) throw new Error(`读取失败(${String(response.status)})`)\n const listing = await response.json() as {\n readonly path: string\n readonly parent?: string\n readonly entries: readonly { readonly kind: 'directory' | 'image'; readonly name: string; readonly path: string }[]\n }\n progress.textContent = listing.path\n if (listing.parent !== undefined) sheet.append(choice('返回上一级', listing.parent, () => { void openComputerImages(listing.parent) }))\n for (const entry of listing.entries) {\n if (entry.kind === 'directory') {\n sheet.append(choice(entry.name, '文件夹', () => { void openComputerImages(entry.path) }))\n continue\n }\n const row = choice(entry.name, '添加这张电脑图片', () => {\n row.disabled = true\n void fetch(`/mobile-access/computer-image?path=${encodeURIComponent(entry.path)}`, { credentials: 'same-origin', cache: 'no-store' })\n .then(async response => {\n if (!response.ok) throw new Error(`读取失败(${String(response.status)})`)\n const blob = await response.blob()\n handFilesToComposer([new File([blob], entry.name, { type: blob.type })])\n closeSheet()\n })\n .catch(error => {\n row.disabled = false\n progress.className = 'dsh-native-mobile-sheet__error'\n progress.textContent = error instanceof Error ? error.message : String(error)\n })\n })\n sheet.append(row)\n }\n } catch (error) {\n progress.className = 'dsh-native-mobile-sheet__error'\n progress.textContent = error instanceof Error ? error.message : String(error)\n }\n }\n const openAttachmentSources = (): void => {\n sheet.replaceChildren()\n sheetHeader('添加图片')\n sheet.append(\n choice('从手机选择', '相册、相机或手机存储', () => { closeSheet(); fileInput.click() }),\n choice('从电脑选择', '在手机页面浏览电脑中的图片', () => { void openComputerImages() }),\n )\n showSheet()\n }\n attach.addEventListener('click', openAttachmentSources)\n sheetMask.addEventListener('click', closeSheet)\n fileInput.addEventListener('change', () => {\n handFilesToComposer([...(fileInput.files ?? [])])\n fileInput.value = ''\n })\n\n const animateNavigation = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const trigger = event.target.closest<HTMLElement>('button,a,[role=\"tab\"],[aria-selected]')\n if (trigger === null || trigger.hasAttribute('disabled') || trigger.getAttribute('aria-disabled') === 'true') return\n if (trigger.getAttribute('aria-selected') === 'true' || trigger.getAttribute('aria-current') === 'true') return\n const settingsNavigation = trigger.closest('[data-dsh-mobile-settings-list]') !== null\n const conversationNavigation = trigger.matches('[role=\"tab\"]')\n const sidebarNavigation = trigger.closest('[data-dsh-mobile-sidebar-root]') !== null\n && trigger.closest('[data-dsh-mobile-toggle]') === null\n if (!settingsNavigation && !conversationNavigation && !sidebarNavigation) return\n if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n transitionFrame = requestAnimationFrame(() => {\n transitionFrame = 0\n const target = settingsNavigation\n ? document.querySelector<HTMLElement>('[data-dsh-mobile-settings-content]')\n : viewArea\n if (target === null || target === undefined) return\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n target.removeAttribute('data-dsh-mobile-view-transition')\n transitionRestartFrame = requestAnimationFrame(() => {\n transitionRestartFrame = 0\n transitionTarget = target\n target.dataset.dshMobileViewTransition = 'true'\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTimer = window.setTimeout(() => {\n target.removeAttribute('data-dsh-mobile-view-transition')\n if (transitionTarget === target) transitionTarget = undefined\n transitionTimer = 0\n }, 240)\n })\n })\n }\n document.addEventListener('click', animateNavigation)\n\n const sync = (): void => {\n scheduled = 0\n frame = firstByClassSuffix(document, '_frame')\n const dedicatedCenter = document.querySelector<HTMLElement>('.dshm-main') ?? undefined\n if (frame !== undefined) frame.dataset.dshMobileFrame = 'true'\n sidebar = frame === undefined ? undefined : firstByClassSuffix(frame, '_sidebarCol')\n const center = frame === undefined ? dedicatedCenter : firstByClassSuffix(frame, '_centerCol')\n const details = frame === undefined ? undefined : firstByClassSuffix(frame, '_detailsCol')\n const handle = frame === undefined ? undefined : firstByClassSuffix(frame, '_handle')\n if (center === undefined) return\n if (center !== undefined) {\n center.dataset.dshMobileCenter = 'true'\n center.querySelector<HTMLElement>('header')?.setAttribute('data-dsh-mobile-header', 'true')\n viewArea = firstByClassSuffix(center, '_viewArea')\n if (viewArea !== undefined) viewArea.dataset.dshMobileView = 'true'\n const conversation = center.querySelector<HTMLElement>('[data-conversation-scroll]')\n const messageColumn = conversation === null ? undefined : firstByClassSuffix(conversation, '_column')\n const messageScroll = messageColumn?.parentElement\n if (messageColumn !== undefined && messageScroll !== null && messageScroll !== undefined && classToken(messageScroll, '_scroll')) {\n messageColumn.dataset.dshMobileMessageColumn = 'true'\n messageScroll.dataset.dshMobileMessageScroll = 'true'\n }\n }\n if (handle !== undefined) handle.dataset.dshMobileHandle = 'true'\n if (details !== undefined) {\n details.dataset.dshMobileDetails = 'true'\n const lastColumn = frame?.style.gridTemplateColumns.trim().split(/\\s+/).at(-1)\n details.dataset.open = String(lastColumn !== undefined && lastColumn !== '0px' && lastColumn !== '0')\n }\n const settings = Array.from(document.querySelectorAll<HTMLElement>('[role=\"dialog\"]')).find(dialog => {\n const directNav = Array.from(dialog.children).find(child => child instanceof HTMLElement && classToken(child, '_nav'))\n return directNav !== undefined\n })\n if (settings !== undefined) {\n settings.dataset.dshMobileSettings = 'true'\n const nav = Array.from(settings.children).find(child => child instanceof HTMLElement && classToken(child, '_nav')) as HTMLElement | undefined\n const content = Array.from(settings.children).find(child => child instanceof HTMLElement && classToken(child, '_content')) as HTMLElement | undefined\n if (nav !== undefined) {\n nav.dataset.dshMobileSettingsNav = 'true'\n firstByClassSuffix(nav, '_navList')?.setAttribute('data-dsh-mobile-settings-list', 'true')\n }\n if (content !== undefined) {\n content.dataset.dshMobileSettingsContent = 'true'\n firstByClassSuffix(content, '_header')?.setAttribute('data-dsh-mobile-settings-header', 'true')\n firstByClassSuffix(content, '_options')?.setAttribute('data-dsh-mobile-settings-options', 'true')\n }\n }\n const textarea = center?.querySelector<HTMLTextAreaElement>('textarea')\n const card = textarea === null || textarea === undefined ? undefined : composerCard(textarea)\n const tools = card === undefined ? undefined : firstByClassSuffix(card, '_tools')\n attach.disabled = textarea === undefined || textarea === null || textarea.disabled || textarea.readOnly\n if (tools !== undefined && attach.parentElement !== tools) tools.append(attach)\n if (sidebar === undefined) return\n sidebar.dataset.dshMobileSidebar = 'true'\n toggle = firstByClassSuffix(sidebar, '_toggle') as HTMLButtonElement | undefined\n let candidate = toggle?.parentElement\n while (candidate !== undefined && candidate !== null && candidate !== sidebar && !classToken(candidate, '_root')) candidate = candidate.parentElement\n sidebarRoot = candidate !== sidebar && candidate !== null ? candidate : undefined\n if (sidebarRoot === undefined) return\n sidebarRoot.dataset.dshMobileSidebarRoot = 'true'\n if (toggle !== undefined) toggle.dataset.dshMobileToggle = 'true'\n const collapsed = classToken(sidebarRoot, '_collapsed')\n sidebar.dataset.open = String(!collapsed)\n backdrop.hidden = collapsed\n }\n const schedule = (): void => { if (scheduled === 0) scheduled = requestAnimationFrame(sync) }\n const observer = new MutationObserver(schedule)\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['class', 'style'] })\n backdrop.addEventListener('click', () => { if (sidebar?.dataset.open === 'true') toggle?.click() })\n sync()\n return () => {\n observer.disconnect()\n if (scheduled !== 0) cancelAnimationFrame(scheduled)\n if (transitionFrame !== 0) cancelAnimationFrame(transitionFrame)\n if (transitionRestartFrame !== 0) cancelAnimationFrame(transitionRestartFrame)\n if (transitionTimer !== 0) clearTimeout(transitionTimer)\n transitionTarget?.removeAttribute('data-dsh-mobile-view-transition')\n document.removeEventListener('pointerdown', onPointerDown, true)\n document.removeEventListener('keydown', onKeyDown, true)\n document.removeEventListener('click', animateNavigation)\n backdrop.remove()\n attach.remove()\n fileInput.remove()\n sheet.remove()\n sheetMask.remove()\n document.documentElement.classList.remove('dsh-native-mobile-active')\n delete document.documentElement.dataset.dshMobileInput\n }\n}\n","import { createElement } from 'react'\nimport { installNativeMobileSurface, NATIVE_MOBILE_STYLES } from './native-mobile.js'\n\ninterface ClientContext {\n effect(effect: () => void | (() => void), label?: string): void\n slots: {\n inject(key: string, callback: () => (() => void)): () => void\n register(options: { name: string; id: string }, component: (props: { wide: boolean }) => unknown): () => void\n }\n}\n\ninterface MobileExtensionContext {\n readonly document: Document\n readonly request: (path: string, init?: RequestInit) => Promise<Response>\n readonly root: HTMLElement\n readonly window: Window\n}\n\ntype MobileExtensionMount = (context: MobileExtensionContext) => void | (() => void)\n\ndeclare global {\n interface Window {\n dshMobile?: { register(mount: MobileExtensionMount): void }\n __DSH_MOBILE_FRONTEND__?: 'dedicated'\n }\n}\n\nfunction isLoopbackHost(hostname: string): boolean {\n return hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]'\n}\n\nfunction element<K extends keyof HTMLElementTagNameMap>(tag: K, className?: string): HTMLElementTagNameMap[K] {\n const node = document.createElement(tag)\n if (className !== undefined) node.className = className\n return node\n}\n\nasync function requestJson(path: string, init?: RequestInit): Promise<Record<string, unknown>> {\n const response = await fetch(path, { ...init, headers: { 'content-type': 'application/json', ...init?.headers } })\n const body = await response.json() as Record<string, unknown>\n if (!response.ok) throw new Error(typeof body.error === 'string' ? body.error : `HTTP ${String(response.status)}`)\n return body\n}\n\nfunction installControl(): { remove: () => void; toggle: () => void } {\n const root = element('div', 'dsh-mobile-control')\n const panel = element('section', 'dsh-mobile-control__panel'); panel.hidden = true\n panel.setAttribute('aria-label', '移动访问')\n const header = element('header', 'dsh-mobile-control__header')\n const title = element('h2'); title.textContent = '移动访问'\n const close = element('button', 'dsh-mobile-control__close'); close.type = 'button'; close.textContent = '×'; close.setAttribute('aria-label', '收起移动访问')\n const access = element('div', 'dsh-mobile-control__access'); access.hidden = true\n const accessLabel = element('span', 'dsh-mobile-control__access-label'); accessLabel.textContent = '浏览器访问'\n const accessLink = element('a', 'dsh-mobile-control__access-link'); accessLink.target = '_blank'; accessLink.rel = 'noreferrer'\n access.append(accessLabel, accessLink)\n const status = element('p', 'dsh-mobile-control__status'); status.textContent = '正在读取状态…'\n const actions = element('div', 'dsh-mobile-control__actions')\n const toggle = element('button', 'dsh-mobile-control__secondary'); toggle.type = 'button'\n const pair = element('button', 'dsh-mobile-control__primary'); pair.type = 'button'; pair.textContent = '生成并复制密钥'\n header.append(title, close); actions.append(toggle, pair); panel.append(header, access, status, actions); root.append(panel); document.body.append(root)\n let running = false\n let origin = ''\n const setOpen = (open: boolean): void => {\n panel.hidden = !open\n for (const trigger of document.querySelectorAll('.dsh-mobile-control__trigger')) trigger.setAttribute('aria-expanded', String(open))\n }\n const render = (data: Record<string, unknown>): void => {\n running = data.running === true\n origin = running && typeof data.origin === 'string' ? data.origin : ''\n access.hidden = origin === ''\n accessLink.href = origin\n accessLink.textContent = origin\n accessLink.title = origin\n status.classList.toggle('is-running', running)\n status.textContent = running ? '移动访问已开启。' : '已关闭。DSH 仍只在本机可用。'\n toggle.textContent = running ? '关闭移动访问' : '开启移动访问'; pair.disabled = !running\n }\n toggle.addEventListener('click', () => { toggle.disabled = true; void requestJson('/api/mobile-access/control', { method: 'POST', body: JSON.stringify({ running: !running }) }).then(render, error => { status.textContent = String(error) }).finally(() => { toggle.disabled = false }) })\n pair.addEventListener('click', () => { pair.disabled = true; void requestJson('/api/mobile-access/pairing/open', { method: 'POST', body: '{}' }).then(async data => {\n const key = typeof data.appKey === 'string' ? data.appKey : ''\n if (key === '') { status.textContent = '无法生成配对密钥。'; return }\n try {\n await navigator.clipboard.writeText(key)\n status.textContent = '配对密钥已复制,请粘贴到 Android App。'\n } catch {\n status.textContent = `请复制配对密钥:${key}`\n status.classList.add('is-key')\n }\n }, error => { status.textContent = String(error) }).finally(() => { pair.disabled = !running }) })\n close.addEventListener('click', () => { setOpen(false) })\n const dismiss = (event: PointerEvent): void => {\n if (panel.hidden || !(event.target instanceof Node)) return\n if (!panel.contains(event.target) && !document.querySelector('.dsh-mobile-control__trigger')?.contains(event.target)) setOpen(false)\n }\n document.addEventListener('pointerdown', dismiss)\n void requestJson('/api/mobile-access/control').then(render, error => { status.textContent = String(error) })\n return { remove: () => { document.removeEventListener('pointerdown', dismiss); root.remove() }, toggle: () => { setOpen(panel.hidden !== false) } }\n}\n\nfunction mobileRequest(path: string, init: RequestInit = {}): Promise<Response> {\n const target = new URL(path, location.href)\n if (target.origin !== location.origin) throw new TypeError('mobile extension requests must stay on the DSH origin')\n const headers = new Headers(init.headers)\n const method = (init.method ?? 'GET').toUpperCase()\n if (method !== 'GET' && method !== 'HEAD') {\n const csrf = document.cookie.split(';').map(value => value.trim()).find(value => value.startsWith('dsh_ma_csrf='))?.slice(12)\n if (csrf !== undefined) headers.set('x-dsh-mobile-csrf', csrf)\n }\n return fetch(target, { ...init, headers, credentials: 'same-origin', cache: 'no-store', redirect: 'error' })\n}\n\nfunction installCustomAssets(): () => void {\n const style = element('style'); style.dataset.plugin = 'dsh-mobile-custom'; document.head.append(style)\n let cssLoading = false\n const refreshCss = async (): Promise<void> => { if (cssLoading || document.visibilityState === 'hidden') return; cssLoading = true; try { const response = await fetch('/mobile-access/custom.css', { cache: 'no-store', credentials: 'same-origin' }); if (response.ok) { const css = await response.text(); if (style.textContent !== css) style.textContent = css } } finally { cssLoading = false } }\n let activeRoot: HTMLElement | undefined; let disposeActive: (() => void) | undefined; let source = ''; let pending: MobileExtensionMount | undefined\n const previous = window.dshMobile\n window.dshMobile = Object.freeze({ register: (mount: MobileExtensionMount) => { pending = mount } })\n const registeredMount = (): MobileExtensionMount | undefined => pending\n const refreshJs = async (): Promise<void> => { try { const response = await fetch('/mobile-access/custom.js', { cache: 'no-store', credentials: 'same-origin' }); if (!response.ok) return; const next = await response.text(); if (next === source) return; source = next; pending = undefined; const script = element('script'); script.textContent = `${next}\\n//# sourceURL=dsh-mobile-custom.js`; document.head.append(script); script.remove(); const mount = registeredMount(); if (mount === undefined) return; const nextRoot = element('div'); nextRoot.dataset.dshMobileExtension = 'true'; document.body.append(nextRoot); const nextDispose = mount({ document, request: mobileRequest, root: nextRoot, window }); disposeActive?.(); activeRoot?.remove(); activeRoot = nextRoot; disposeActive = typeof nextDispose === 'function' ? nextDispose : undefined } catch { /* Preserve the last good customization during reconnects. */ } }\n void refreshCss(); void refreshJs()\n const timer = window.setInterval(() => { void refreshCss(); void refreshJs() }, 1_000)\n return () => { clearInterval(timer); disposeActive?.(); activeRoot?.remove(); style.remove(); if (previous === undefined) delete window.dshMobile; else window.dshMobile = previous }\n}\n\nconst CONTROL_STYLES = `\n.dsh-mobile-control{position:fixed;z-index:1000;left:16px;bottom:64px;font:14px/1.45 system-ui;color:var(--dsw-alias-label-primary,#16181d)}\n.dsh-mobile-control__panel{box-sizing:border-box;width:min(328px,calc(100vw - 32px));padding:16px;border:1px solid var(--dsw-alias-border-subtle,#e1e5eb);border-radius:18px;background:var(--dsw-alias-bg-layer-2,#fff);box-shadow:0 18px 50px rgb(15 23 42 / 18%)}\n.dsh-mobile-control__header{display:flex;align-items:center;justify-content:space-between;gap:12px;margin-bottom:10px}.dsh-mobile-control__panel h2{margin:0;font-size:17px;line-height:24px}.dsh-mobile-control__close{display:inline-flex;align-items:center;justify-content:center;width:36px;height:36px;padding:0;border:0;border-radius:10px;background:transparent;color:inherit;font-size:24px;line-height:1;cursor:pointer}.dsh-mobile-control__close:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}\n.dsh-mobile-control__access{display:flex;align-items:baseline;gap:6px;min-width:0;margin:0 0 12px}.dsh-mobile-control__access[hidden]{display:none}.dsh-mobile-control__access-label{flex:none;color:var(--dsw-alias-label-secondary,#606873);white-space:nowrap}.dsh-mobile-control__access-label::after{content:\":\"}.dsh-mobile-control__access-link{min-width:0;overflow:hidden;color:#2563eb;text-decoration:none;text-overflow:ellipsis;white-space:nowrap}.dsh-mobile-control__access-link:hover{text-decoration:underline}\n.dsh-mobile-control__status{margin:0 0 14px;overflow-wrap:anywhere;color:var(--dsw-alias-label-secondary,#606873)}.dsh-mobile-control__status::before{display:inline-block;width:8px;height:8px;margin-right:7px;border-radius:50%;background:#98a1ad;content:\"\"}.dsh-mobile-control__status.is-running::before{background:#16a36a}.dsh-mobile-control__status.is-key{font-family:ui-monospace,SFMono-Regular,Consolas,monospace;font-size:12px;word-break:break-all}\n.dsh-mobile-control__actions{display:flex;flex-wrap:wrap;gap:8px}.dsh-mobile-control__actions button{min-height:40px;padding:8px 12px;border-radius:11px;font:inherit;cursor:pointer}.dsh-mobile-control__secondary{border:1px solid var(--dsw-alias-border-normal,#cfd5dd);background:transparent;color:inherit}.dsh-mobile-control__primary{border:1px solid #2563eb;background:#2563eb;color:#fff}.dsh-mobile-control__actions button:disabled{cursor:not-allowed;opacity:.45}\n.dsh-mobile-control__trigger{box-sizing:border-box;display:flex;align-items:center;gap:8px;width:calc(100% + 8px);height:34px;margin:4px -4px;padding:6px 2px 6px 10px;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#16181d);font:14px/22px system-ui;cursor:pointer}.dsh-mobile-control__trigger:hover{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}.dsh-mobile-control__trigger.is-rail{width:36px;height:36px;margin:8px 0 10px;padding:0;justify-content:center;border-radius:50%}.dsh-mobile-control__trigger-icon{position:relative;box-sizing:border-box;flex:none;width:14px;height:19px;border:1.7px solid currentColor;border-radius:3px}.dsh-mobile-control__trigger-icon::after{position:absolute;right:4px;bottom:2px;width:4px;height:1.5px;border-radius:2px;background:currentColor;content:\"\"}.dsh-mobile-control__trigger-label{min-width:0;overflow:hidden;white-space:nowrap;text-overflow:ellipsis}\n`\n\n/** Mount the desktop control or mobile feature enhancements. */\nexport function apply(ctx: ClientContext): void {\n ctx.effect(() => {\n const loopback = isLoopbackHost(location.hostname) && !new URLSearchParams(location.search).has('dsh-mobile-preview')\n const style = element('style'); style.dataset.plugin = 'dsh-mobile'; style.textContent = loopback\n ? CONTROL_STYLES\n : NATIVE_MOBILE_STYLES\n document.head.append(style)\n if (!loopback) {\n const removeCustom = installCustomAssets()\n const removeSurface = installNativeMobileSurface()\n return () => { removeCustom(); removeSurface(); style.remove() }\n }\n const control = installControl()\n const disposeSlot = ctx.slots.inject('sidebar.footer.action', () => ctx.slots.register({ name: 'sidebar.footer.action', id: 'dsh-mobile' }, ({ wide }) => createElement('button', {\n 'aria-expanded': false,\n 'aria-label': '移动访问',\n className: `dsh-mobile-control__trigger${wide ? '' : ' is-rail'}`,\n type: 'button',\n title: '移动访问',\n onClick: control.toggle,\n }, createElement('span', { 'aria-hidden': true, className: 'dsh-mobile-control__trigger-icon' }), wide ? createElement('span', { className: 'dsh-mobile-control__trigger-label' }, '移动访问') : undefined)))\n return () => { disposeSlot(); control.remove(); style.remove() }\n }, 'dsh-mobile: stock mobile adaptation and local control')\n}\n\n/** Client services required by the mobile adaptation. */\nexport const inject: readonly string[] = ['slots']\n"],"mappings":"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"}
package/lib/index.d.mts CHANGED
@@ -239,6 +239,8 @@ interface PluginConfig {
239
239
  customCssFile?: string;
240
240
  /** Optional user script that mounts authenticated mobile-only Web features. */
241
241
  customScriptFile?: string;
242
+ /** Internal dedicated mobile layout browser bundle. */
243
+ mobileLayoutFile?: string;
242
244
  /** Stable public discovery identifier; it is not an authentication secret. */
243
245
  instanceId?: string;
244
246
  /** Managed CA certificate offered to the Android installer after fingerprint binding. */
@@ -275,6 +277,7 @@ interface ResolvedGatewayConfig {
275
277
  readonly stateFile: string;
276
278
  readonly customCssFile: string;
277
279
  readonly customScriptFile: string;
280
+ readonly mobileLayoutFile: string;
278
281
  readonly instanceId: string;
279
282
  readonly pairingCaFile?: string;
280
283
  readonly tls: TlsConfig;
@@ -348,6 +351,8 @@ declare class MobileAccessGatewayController {
348
351
  }
349
352
  //#endregion
350
353
  //#region src/gateway.d.ts
354
+ /** Replace only DSH's layout client module while retaining its complete plugin graph. */
355
+ declare function rewriteMobileIndex(html: string): string;
351
356
  /** Authenticated TLS edge in front of the ordinary loopback-only DSH Web server. */
352
357
  declare class MobileAccessGateway {
353
358
  readonly config: ResolvedGatewayConfig;
@@ -392,6 +397,7 @@ declare class MobileAccessGateway {
392
397
  private handleNativeRenew;
393
398
  private handleLogout;
394
399
  private handleExternalRequest;
400
+ private proxyMobileIndex;
395
401
  private allocateRequest;
396
402
  private proxyHttp;
397
403
  private abortSessionResources;
@@ -423,5 +429,5 @@ declare const inject: string[];
423
429
  /** Mount the resident control route and its optional authenticated LAN gateway. */
424
430
  declare function apply(ctx: Context, config: PluginConfig): Promise<void>;
425
431
  //#endregion
426
- export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, DEVICE_COOKIE, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, JsonDeviceStore, JsonMobileAccessControlStore, LOCAL_ADMIN_PREFIX, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessRuntime, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, type SessionAuthorization, type StoredDevice, type TlsConfig, WS_PATHS, addressAllowed, apply, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseGatewayConfig, parseMobileAccessControlState, resolveAuthority };
432
+ export { AUTH_PREFIX, AccessController, type AccessControllerOptions, AccessError, type AuthoritySpec, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, DEVICE_COOKIE, type DeviceSnapshot, type DeviceStore, type DeviceSummary, type DisabledTlsConfig, JsonDeviceStore, JsonMobileAccessControlStore, LOCAL_ADMIN_PREFIX, MemoryDeviceStore, type MobileAccessControlState, type MobileAccessControlStore, MobileAccessGateway, MobileAccessGatewayController, type MobileAccessRuntime, type PairingResult, type ParsedCidr, type PluginConfig, type ProvidedTlsConfig, type RenewalResult, RequestTrustPolicy, type ResolvedGatewayConfig, SESSION_COOKIE, type SessionAuthorization, type StoredDevice, type TlsConfig, WS_PATHS, addressAllowed, apply, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseGatewayConfig, parseMobileAccessControlState, resolveAuthority, rewriteMobileIndex };
427
433
  //# sourceMappingURL=index.d.mts.map
package/lib/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { X509Certificate, createHash, createPrivateKey, createPublicKey, randomBytes, timingSafeEqual } from "node:crypto";
2
2
  import { basename, dirname, extname, isAbsolute, join, resolve } from "node:path";
3
+ import { fileURLToPath } from "node:url";
3
4
  import z from "@deepseek-ai/schemastery";
4
5
  import { connect, isIP } from "node:net";
5
6
  import { chmod, lstat, mkdir, opendir, readFile, rename, rm, writeFile } from "node:fs/promises";
@@ -547,6 +548,7 @@ const Config = z.object({
547
548
  controlFile: z.string().hidden().required(),
548
549
  customCssFile: z.string().hidden(),
549
550
  customScriptFile: z.string().hidden(),
551
+ mobileLayoutFile: z.string().hidden(),
550
552
  instanceId: z.string().hidden(),
551
553
  pairingCaFile: z.string().hidden(),
552
554
  initiallyEnabled: z.boolean().hidden().required(),
@@ -670,6 +672,7 @@ function parseGatewayConfig(raw) {
670
672
  stateFile: absoluteFile(value.stateFile, "stateFile"),
671
673
  customCssFile: value.customCssFile === void 0 ? join(dirname(absoluteFile(value.stateFile, "stateFile")), "mobile.css") : absoluteFile(value.customCssFile, "customCssFile"),
672
674
  customScriptFile: value.customScriptFile === void 0 ? join(dirname(absoluteFile(value.stateFile, "stateFile")), "mobile.js") : absoluteFile(value.customScriptFile, "customScriptFile"),
675
+ mobileLayoutFile: value.mobileLayoutFile === void 0 ? fileURLToPath(new URL("./mobile-layout.js", import.meta.url)) : absoluteFile(value.mobileLayoutFile, "mobileLayoutFile"),
673
676
  instanceId: value.instanceId === void 0 ? createHash("sha256").update(absoluteFile(value.stateFile, "stateFile")).digest("hex") : /^[a-f\d]{64}$/u.test(value.instanceId) ? value.instanceId : (() => {
674
677
  throw new Error("instanceId must be a lowercase SHA-256 value");
675
678
  })(),
@@ -1159,6 +1162,8 @@ const DISCOVERY_QUERY = Buffer.from("DSH_MOBILE_DISCOVER_V1", "ascii");
1159
1162
  const DISCOVERY_PROTOCOL = 1;
1160
1163
  const DISCOVERY_INTERVAL_MS = 3e3;
1161
1164
  const MDNS_SERVICE_TYPE = "dsh-mobile";
1165
+ const MOBILE_LAYOUT_MODULE = "@deepseek-ai/dsh-client-ui-layout";
1166
+ const MOBILE_LAYOUT_PATH = `${AUTH_PREFIX}/mobile-layout.js`;
1162
1167
  const PAIR_PAGE = `<!doctype html>
1163
1168
  <html lang="en">
1164
1169
  <meta charset="utf-8">
@@ -1176,6 +1181,24 @@ const PAIR_PAGE = `<!doctype html>
1176
1181
  <script src="/mobile-access/pair.js" defer><\/script>
1177
1182
  </html>
1178
1183
  `;
1184
+ /** Replace only DSH's layout client module while retaining its complete plugin graph. */
1185
+ function rewriteMobileIndex(html) {
1186
+ const assignment = "window.__DSH_BOOT__ = ";
1187
+ const start = html.indexOf(assignment);
1188
+ if (start < 0) throw new Error("upstream DSH index has no boot manifest");
1189
+ const valueStart = start + 22;
1190
+ const scriptEnd = html.indexOf("<\/script>", valueStart);
1191
+ if (scriptEnd < 0) throw new Error("upstream DSH boot manifest script is incomplete");
1192
+ const source = html.slice(valueStart, scriptEnd).trim().replace(/;$/u, "");
1193
+ const parsed = JSON.parse(source);
1194
+ if (typeof parsed.rev !== "string" || !Array.isArray(parsed.entries)) throw new Error("upstream DSH boot manifest is malformed");
1195
+ const layout = parsed.entries.filter((entry) => entry !== null && typeof entry === "object" && entry.id === MOBILE_LAYOUT_MODULE);
1196
+ if (layout.length !== 1 || typeof layout[0]?.url !== "string" || typeof layout[0].rev !== "string") throw new Error("upstream DSH boot manifest has no unique layout module");
1197
+ layout[0].url = MOBILE_LAYOUT_PATH;
1198
+ layout[0].rev = "dsh-mobile-layout-v1";
1199
+ const replacement = `window.__DSH_MOBILE_FRONTEND__="dedicated";${assignment}${JSON.stringify(parsed)};`;
1200
+ return `${html.slice(0, start)}${replacement}${html.slice(scriptEnd)}`;
1201
+ }
1179
1202
  const PAIR_SCRIPT = `(() => {
1180
1203
  const form = document.getElementById('pair-form')
1181
1204
  const token = document.getElementById('pair-token')
@@ -1842,6 +1865,10 @@ var MobileAccessGateway = class {
1842
1865
  file: this.config.customScriptFile,
1843
1866
  contentType: "text/javascript; charset=utf-8",
1844
1867
  fallback: "window.dshMobile?.register(() => undefined)\n"
1868
+ } : target.decodedPathname === MOBILE_LAYOUT_PATH ? {
1869
+ file: this.config.mobileLayoutFile,
1870
+ contentType: "text/javascript; charset=utf-8",
1871
+ fallback: void 0
1845
1872
  } : void 0 : void 0;
1846
1873
  if (customAsset === void 0 && !computerImages && !computerImage && (target.decodedPathname === "/mobile-access" || target.decodedPathname.startsWith(`/mobile-access/`))) throw new HttpError(404, "not_found");
1847
1874
  if (request.method !== "GET" && request.method !== "HEAD" && request.method !== "POST") throw new HttpError(405, "method_not_allowed");
@@ -1871,6 +1898,7 @@ var MobileAccessGateway = class {
1871
1898
  body = await readFile(customAsset.file);
1872
1899
  } catch (error) {
1873
1900
  if (error.code !== "ENOENT") throw error;
1901
+ if (customAsset.fallback === void 0) throw new HttpError(503, "mobile_frontend_unavailable");
1874
1902
  body = Buffer.from(customAsset.fallback);
1875
1903
  }
1876
1904
  if (body.byteLength > 262144) throw new HttpError(413, "payload_too_large");
@@ -1900,8 +1928,74 @@ var MobileAccessGateway = class {
1900
1928
  response.end(image.body);
1901
1929
  return;
1902
1930
  }
1931
+ const stockFrontend = new URL(target.raw, this.address().origin).searchParams.get("frontend") === "stock";
1932
+ const acceptsHtml = request.headers.accept?.split(",").some((value) => value.trim().split(";", 1)[0] === "text/html") ?? false;
1933
+ if (request.method === "GET" && target.decodedPathname === "/" && acceptsHtml && !stockFrontend) {
1934
+ await this.proxyMobileIndex(request, response, authorization);
1935
+ return;
1936
+ }
1937
+ if (stockFrontend && target.decodedPathname === "/") request.url = "/";
1903
1938
  await this.proxyHttp(request, response, authorization);
1904
1939
  }
1940
+ async proxyMobileIndex(request$1, response, authorization) {
1941
+ const holder = {};
1942
+ const operation = this.allocateRequest(authorization, response, holder);
1943
+ try {
1944
+ const upstreamHeaders = sanitizeRequestHeaders(request$1, this.config.upstreamOrigin);
1945
+ upstreamHeaders["accept-encoding"] = "identity";
1946
+ const proxied = await new Promise((resolve, reject) => {
1947
+ const upstreamRequest = request({
1948
+ protocol: "http:",
1949
+ hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),
1950
+ port: Number(this.config.upstreamOrigin.port),
1951
+ method: "GET",
1952
+ path: "/",
1953
+ headers: upstreamHeaders,
1954
+ agent: false
1955
+ });
1956
+ holder.request = upstreamRequest;
1957
+ upstreamRequest.setTimeout(this.config.upstreamTimeoutMs, () => {
1958
+ upstreamRequest.destroy(/* @__PURE__ */ new Error("upstream timeout"));
1959
+ });
1960
+ upstreamRequest.once("response", resolve);
1961
+ upstreamRequest.once("error", reject);
1962
+ upstreamRequest.end();
1963
+ });
1964
+ if ((proxied.statusCode ?? 502) !== 200) throw new HttpError(502, "upstream_unavailable");
1965
+ const chunks = [];
1966
+ let bytes = 0;
1967
+ for await (const chunk of proxied) {
1968
+ const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
1969
+ bytes += buffer.byteLength;
1970
+ if (bytes > 4194304) throw new HttpError(502, "upstream_unavailable");
1971
+ chunks.push(buffer);
1972
+ }
1973
+ let body;
1974
+ try {
1975
+ body = Buffer.from(rewriteMobileIndex(Buffer.concat(chunks).toString("utf8")));
1976
+ } catch {
1977
+ throw new HttpError(502, "upstream_unavailable");
1978
+ }
1979
+ const headers = sanitizeResponseHeaders(proxied.headers, this.config.upstreamOrigin);
1980
+ delete headers["content-length"];
1981
+ delete headers["content-encoding"];
1982
+ delete headers.etag;
1983
+ setSecurityHeaders(response, this.tlsEnabled);
1984
+ response.writeHead(200, {
1985
+ ...headers,
1986
+ "Content-Type": "text/html; charset=utf-8",
1987
+ "Content-Length": body.byteLength
1988
+ });
1989
+ response.end(body);
1990
+ } catch (error) {
1991
+ holder.request?.destroy();
1992
+ if (error instanceof HttpError) throw error;
1993
+ if (response.headersSent) response.destroy();
1994
+ else throw new HttpError(502, "upstream_unavailable");
1995
+ } finally {
1996
+ operation.release();
1997
+ }
1998
+ }
1905
1999
  allocateRequest(authorization, response, upstream) {
1906
2000
  if (this.activeRequests.size >= this.config.maxActiveRequests) throw new HttpError(429, "busy");
1907
2001
  const id = this.nextOperationId++;
@@ -1925,8 +2019,8 @@ var MobileAccessGateway = class {
1925
2019
  }
1926
2020
  };
1927
2021
  }
1928
- async proxyHttp(request$1, response, authorization) {
1929
- const declared = request$1.headers["content-length"];
2022
+ async proxyHttp(request$2, response, authorization) {
2023
+ const declared = request$2.headers["content-length"];
1930
2024
  if (declared !== void 0 && (!/^\d+$/u.test(declared) || Number(declared) > this.config.maxBodyBytes)) throw new HttpError(413, "payload_too_large");
1931
2025
  const holder = {};
1932
2026
  const operation = this.allocateRequest(authorization, response, holder);
@@ -1937,9 +2031,9 @@ var MobileAccessGateway = class {
1937
2031
  protocol: "http:",
1938
2032
  hostname: stripIpv6Brackets(this.config.upstreamOrigin.hostname),
1939
2033
  port: Number(this.config.upstreamOrigin.port),
1940
- method: request$1.method,
1941
- path: request$1.url,
1942
- headers: sanitizeRequestHeaders(request$1, this.config.upstreamOrigin),
2034
+ method: request$2.method,
2035
+ path: request$2.url,
2036
+ headers: sanitizeRequestHeaders(request$2, this.config.upstreamOrigin),
1943
2037
  agent: false
1944
2038
  });
1945
2039
  holder.request = upstreamRequest;
@@ -1948,7 +2042,7 @@ var MobileAccessGateway = class {
1948
2042
  });
1949
2043
  upstreamRequest.once("response", resolve);
1950
2044
  upstreamRequest.once("error", reject);
1951
- bodyDone = pipeline(request$1, new ByteLimitTransform(this.config.maxBodyBytes), upstreamRequest);
2045
+ bodyDone = pipeline(request$2, new ByteLimitTransform(this.config.maxBodyBytes), upstreamRequest);
1952
2046
  bodyDone.catch(reject);
1953
2047
  });
1954
2048
  setSecurityHeaders(response, this.tlsEnabled);
@@ -2702,6 +2796,6 @@ async function apply(ctx, config) {
2702
2796
  }, "dsh-mobile: local control and authenticated LAN gateway");
2703
2797
  }
2704
2798
  //#endregion
2705
- export { AUTH_PREFIX, AccessController, AccessError, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, DEVICE_COOKIE, JsonDeviceStore, JsonMobileAccessControlStore, LOCAL_ADMIN_PREFIX, MemoryDeviceStore, MobileAccessGateway, MobileAccessGatewayController, RequestTrustPolicy, SESSION_COOKIE, WS_PATHS, addressAllowed, apply, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseGatewayConfig, parseMobileAccessControlState, resolveAuthority };
2799
+ export { AUTH_PREFIX, AccessController, AccessError, BoundedRateLimiter, CSRF_COOKIE, CSRF_HEADER, Config, DEVICE_COOKIE, JsonDeviceStore, JsonMobileAccessControlStore, LOCAL_ADMIN_PREFIX, MemoryDeviceStore, MobileAccessGateway, MobileAccessGatewayController, RequestTrustPolicy, SESSION_COOKIE, WS_PATHS, addressAllowed, apply, inject, isLoopbackAddress, name, parseAuthority, parseCidr, parseControlFile, parseDeviceSnapshot, parseGatewayConfig, parseMobileAccessControlState, resolveAuthority, rewriteMobileIndex };
2706
2800
 
2707
2801
  //# sourceMappingURL=index.mjs.map
@@ -0,0 +1,227 @@
1
+ window.__ModuleLoader__.load({
2
+ id: "@deepseek-ai/dsh-client-ui-layout",
3
+ factory: (require) => {
4
+ var module = { exports: {} };
5
+ var exports = module.exports;
6
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
7
+ let react = require("react");
8
+ //#region src/mobile-layout.ts
9
+ var MobileLayoutController = class {
10
+ snapshot = Object.freeze({
11
+ sidebarOpen: false,
12
+ detailsOpen: false
13
+ });
14
+ listeners = /* @__PURE__ */ new Set();
15
+ subscribe = (listener) => {
16
+ this.listeners.add(listener);
17
+ return () => {
18
+ this.listeners.delete(listener);
19
+ };
20
+ };
21
+ getSnapshot = () => this.snapshot;
22
+ toggleSidebar() {
23
+ this.update({ sidebarOpen: !this.snapshot.sidebarOpen });
24
+ }
25
+ openDetails() {
26
+ this.update({ detailsOpen: true });
27
+ }
28
+ closeDetails() {
29
+ this.update({ detailsOpen: false });
30
+ }
31
+ closeSidebar() {
32
+ this.update({ sidebarOpen: false });
33
+ }
34
+ update(next) {
35
+ const snapshot = Object.freeze({
36
+ ...this.snapshot,
37
+ ...next
38
+ });
39
+ if (snapshot.sidebarOpen === this.snapshot.sidebarOpen && snapshot.detailsOpen === this.snapshot.detailsOpen) return;
40
+ this.snapshot = snapshot;
41
+ for (const listener of this.listeners) listener();
42
+ }
43
+ };
44
+ var ThemePresenter = class {
45
+ appliedTokens = [];
46
+ meta = document.createElement("meta");
47
+ constructor() {
48
+ this.meta.name = "theme-color";
49
+ }
50
+ apply(snapshot) {
51
+ const scheme = snapshot.active.colorScheme;
52
+ document.documentElement.style.colorScheme = scheme;
53
+ document.body.toggleAttribute("data-ds-dark-theme", scheme === "dark");
54
+ for (const name of this.appliedTokens) document.body.style.removeProperty(name);
55
+ this.appliedTokens = [];
56
+ for (const [name, value] of Object.entries(snapshot.active.tokens)) {
57
+ document.body.style.setProperty(name, value);
58
+ this.appliedTokens.push(name);
59
+ }
60
+ this.meta.content = getComputedStyle(document.body).backgroundColor;
61
+ if (!this.meta.isConnected) document.head.append(this.meta);
62
+ }
63
+ dispose() {
64
+ document.documentElement.style.removeProperty("color-scheme");
65
+ document.body.removeAttribute("data-ds-dark-theme");
66
+ for (const name of this.appliedTokens) document.body.style.removeProperty(name);
67
+ this.meta.remove();
68
+ }
69
+ };
70
+ const MOBILE_LAYOUT_STYLES = `
71
+ html,body,#root{width:100%;height:100%;overflow:hidden}
72
+ .dshm-shell{position:relative;display:grid;width:100%;height:100dvh;min-width:0;overflow:hidden;background:var(--dsw-alias-bg-base,#fff)}
73
+ .dshm-main{grid-area:1/1;min-width:0;min-height:0;overflow:hidden}
74
+ .dshm-main>*,.dshm-main>*>*{min-width:0}
75
+ .dshm-menu{position:fixed;z-index:62;top:max(4px,env(safe-area-inset-top));left:4px;display:grid;place-items:center;width:44px;height:44px;padding:0;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#171a21);cursor:pointer;-webkit-tap-highlight-color:transparent}
76
+ .dshm-menu:hover,.dshm-menu:active{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}
77
+ .dshm-menu:focus-visible,.dshm-close:focus-visible{outline:2px solid var(--dsw-alias-border-focus,#2563eb);outline-offset:2px}
78
+ .dshm-menu svg,.dshm-close svg{width:22px;height:22px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}
79
+ .dshm-drawer{position:fixed;z-index:70;inset:0 auto 0 0;box-sizing:border-box;width:min(88vw,340px);max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#f8fafc);box-shadow:18px 0 46px rgb(15 23 42 / 18%);left:-104%;transition:left 190ms cubic-bezier(.22,1,.36,1)}
80
+ .dshm-drawer[data-open=true]{left:0}
81
+ .dshm-drawer[data-open=false]{pointer-events:none;visibility:hidden;transition:left 190ms cubic-bezier(.22,1,.36,1),visibility 0s linear 190ms}
82
+ .dshm-drawer>*{width:100%!important;height:100%!important}
83
+ .dshm-details{position:fixed;z-index:80;inset:0 0 0 auto;box-sizing:border-box;width:min(94vw,460px);max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#fff);box-shadow:-18px 0 46px rgb(15 23 42 / 18%);transform:translateX(104%);transition:transform 190ms cubic-bezier(.22,1,.36,1)}
84
+ .dshm-details[data-open=true]{transform:translateX(0)}
85
+ .dshm-details[data-open=false]{pointer-events:none;visibility:hidden;transition:transform 190ms cubic-bezier(.22,1,.36,1),visibility 0s linear 190ms}
86
+ .dshm-scrim{position:fixed;z-index:65;inset:0;border:0;background:rgb(15 23 42 / 40%);opacity:0;pointer-events:none;transition:opacity 180ms ease-out}
87
+ .dshm-scrim[data-open=true]{opacity:1;pointer-events:auto}
88
+ .dshm-overlay{position:fixed;z-index:90;inset:0;pointer-events:none}.dshm-overlay>*{pointer-events:auto}
89
+ .dshm-shell header{min-width:0;padding-left:52px}
90
+ .dshm-shell textarea{font-size:16px}
91
+ .dshm-shell table{display:block;max-width:100%;overflow-x:auto}
92
+ .dshm-shell pre{max-width:100%;overflow-x:auto}
93
+ .dshm-shell img,.dshm-shell video,.dshm-shell canvas,.dshm-shell svg{max-width:100%}
94
+ .dshm-shell [data-disclosure-row]{min-width:0;max-width:100%}
95
+ .dshm-shell [data-disclosure-row]>*{min-width:0;overflow-wrap:anywhere}
96
+ .dshm-shell [data-context-fields]>*{min-width:0}
97
+ .dshm-shell [class*="_body"]{max-width:100%;overflow-wrap:anywhere}
98
+ @media(max-width:420px){.dshm-shell [data-context-fields]>*{display:grid;grid-template-columns:1fr!important;gap:4px}.dshm-shell [class*="_ioSection"]{grid-template-columns:1fr!important}}
99
+ @media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim{transition:none!important}}
100
+ `;
101
+ function icon(paths) {
102
+ return (0, react.createElement)("svg", {
103
+ "aria-hidden": true,
104
+ viewBox: "0 0 24 24"
105
+ }, ...paths.map((path, index) => (0, react.createElement)("path", {
106
+ d: path,
107
+ key: index
108
+ })));
109
+ }
110
+ function MobileAppFrame(props) {
111
+ const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.getSnapshot);
112
+ const hasSession = props.useSessions((session) => {
113
+ const current = session.current;
114
+ return current !== void 0 && session.byId[current]?.blank === false;
115
+ });
116
+ (0, react.useEffect)(() => {
117
+ if (!hasSession) props.controller.closeDetails();
118
+ }, [hasSession, props.controller]);
119
+ const closeDrawerAfterAction = (event) => {
120
+ if (!(event.target instanceof Element)) return;
121
+ const action = event.target.closest("button,a,[role=\"button\"],[role=\"option\"],[role=\"treeitem\"]");
122
+ if (action === null || action.closest("[data-dshm-keep-open],[aria-haspopup=\"dialog\"]") !== null) return;
123
+ window.setTimeout(() => {
124
+ props.controller.closeSidebar();
125
+ }, 80);
126
+ };
127
+ return (0, react.createElement)("div", { className: "dshm-shell" }, (0, react.createElement)("main", { className: "dshm-main" }, props.renderSlot("conversation", {})), (0, react.createElement)("button", {
128
+ "aria-expanded": state.sidebarOpen,
129
+ "aria-label": state.sidebarOpen ? "关闭导航" : "打开导航",
130
+ className: "dshm-menu",
131
+ onClick: () => {
132
+ props.controller.toggleSidebar();
133
+ },
134
+ type: "button"
135
+ }, icon([
136
+ "M4 6h16",
137
+ "M4 12h16",
138
+ "M4 18h16"
139
+ ])), (0, react.createElement)("button", {
140
+ "aria-label": "关闭浮层",
141
+ className: "dshm-scrim",
142
+ "data-open": state.sidebarOpen || state.detailsOpen,
143
+ onClick: () => {
144
+ state.detailsOpen ? props.controller.closeDetails() : props.controller.closeSidebar();
145
+ },
146
+ tabIndex: state.sidebarOpen || state.detailsOpen ? 0 : -1,
147
+ type: "button"
148
+ }), (0, react.createElement)("aside", {
149
+ "aria-hidden": !state.sidebarOpen,
150
+ className: "dshm-drawer",
151
+ "data-open": state.sidebarOpen,
152
+ onClickCapture: closeDrawerAfterAction,
153
+ ...state.sidebarOpen ? {} : { inert: "" }
154
+ }, props.renderSlot("sidebar", {
155
+ collapsed: false,
156
+ width: 340
157
+ })), (0, react.createElement)("aside", {
158
+ "aria-hidden": !state.detailsOpen,
159
+ className: "dshm-details",
160
+ "data-open": state.detailsOpen,
161
+ ...state.detailsOpen ? {} : { inert: "" }
162
+ }, hasSession ? props.renderSlot("details", {}) : void 0), (0, react.createElement)("div", {
163
+ className: "dshm-overlay",
164
+ "data-shell-overlay": true
165
+ }, props.renderSlot("shell.overlay", {})));
166
+ }
167
+ /** Replace the desktop layout module on the authenticated mobile surface. */
168
+ function apply(ctx) {
169
+ const controller = new MobileLayoutController();
170
+ ctx.effect(() => {
171
+ const style = document.createElement("style");
172
+ style.dataset.plugin = "dsh-mobile-layout";
173
+ style.textContent = MOBILE_LAYOUT_STYLES;
174
+ document.head.append(style);
175
+ const disposeService = ctx.reflect.provide("layout", controller);
176
+ const disposeRoot = ctx.slots.register({
177
+ name: "root",
178
+ children: {
179
+ sidebar: {
180
+ kind: "single",
181
+ scope: "root"
182
+ },
183
+ conversation: {
184
+ kind: "single",
185
+ scope: "session-maybe"
186
+ },
187
+ details: {
188
+ kind: "single",
189
+ scope: "session"
190
+ },
191
+ "shell.overlay": {
192
+ kind: "list",
193
+ scope: "root"
194
+ }
195
+ }
196
+ }, (props) => (0, react.createElement)(MobileAppFrame, {
197
+ ...props,
198
+ controller
199
+ }));
200
+ return () => {
201
+ disposeRoot();
202
+ disposeService();
203
+ style.remove();
204
+ };
205
+ }, "dsh-mobile: dedicated root layout");
206
+ ctx.effect(() => {
207
+ const presenter = new ThemePresenter();
208
+ presenter.apply(ctx.theme.getTheme());
209
+ const off = ctx.on("theme/change", (snapshot) => {
210
+ presenter.apply(snapshot);
211
+ });
212
+ return () => {
213
+ off();
214
+ presenter.dispose();
215
+ };
216
+ }, "dsh-mobile: theme presenter");
217
+ }
218
+ /** Preserve the official layout module's dependency ordering. */
219
+ const inject = ["slots", "theme"];
220
+ //#endregion
221
+ exports.apply = apply;
222
+ exports.inject = inject;
223
+ return module.exports;
224
+ }
225
+ });
226
+
227
+ //# sourceMappingURL=mobile-layout.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mobile-layout.js","names":["createElement","useSyncExternalStore"],"sources":["../src/mobile-layout.ts"],"sourcesContent":["import { createElement, useEffect, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\n\ninterface SessionState {\n readonly current?: string\n readonly byId: Readonly<Record<string, { readonly blank?: boolean } | undefined>>\n}\n\ninterface MobileRootProps {\n readonly renderSlot: (name: string, owner: Record<string, unknown>) => ReactNode\n readonly useSessions: <T>(selector: (state: SessionState) => T) => T\n}\n\ninterface MobileClientContext {\n readonly effect: (effect: () => void | (() => void), label?: string) => void\n readonly on: (event: string, listener: (value: ThemeSnapshot) => void) => () => void\n readonly reflect: { provide: (name: string, value: unknown) => () => void | Promise<void> }\n readonly slots: {\n register: (options: Record<string, unknown>, component: (props: MobileRootProps) => ReactNode) => () => void\n }\n readonly theme: { getTheme: () => ThemeSnapshot }\n}\n\ninterface ThemeSnapshot {\n readonly active: {\n readonly colorScheme: 'dark' | 'light'\n readonly tokens: Readonly<Record<string, string>>\n }\n}\n\ninterface LayoutSnapshot {\n readonly sidebarOpen: boolean\n readonly detailsOpen: boolean\n}\n\nclass MobileLayoutController {\n private snapshot: LayoutSnapshot = Object.freeze({ sidebarOpen: false, detailsOpen: false })\n private readonly listeners = new Set<() => void>()\n\n readonly subscribe = (listener: () => void): (() => void) => {\n this.listeners.add(listener)\n return () => { this.listeners.delete(listener) }\n }\n\n readonly getSnapshot = (): LayoutSnapshot => this.snapshot\n\n toggleSidebar(): void {\n this.update({ sidebarOpen: !this.snapshot.sidebarOpen })\n }\n\n openDetails(): void {\n this.update({ detailsOpen: true })\n }\n\n closeDetails(): void {\n this.update({ detailsOpen: false })\n }\n\n closeSidebar(): void {\n this.update({ sidebarOpen: false })\n }\n\n private update(next: Partial<LayoutSnapshot>): void {\n const snapshot = Object.freeze({ ...this.snapshot, ...next })\n if (snapshot.sidebarOpen === this.snapshot.sidebarOpen && snapshot.detailsOpen === this.snapshot.detailsOpen) return\n this.snapshot = snapshot\n for (const listener of this.listeners) listener()\n }\n}\n\nclass ThemePresenter {\n private appliedTokens: string[] = []\n private readonly meta = document.createElement('meta')\n\n constructor() {\n this.meta.name = 'theme-color'\n }\n\n apply(snapshot: ThemeSnapshot): void {\n const scheme = snapshot.active.colorScheme\n document.documentElement.style.colorScheme = scheme\n document.body.toggleAttribute('data-ds-dark-theme', scheme === 'dark')\n for (const name of this.appliedTokens) document.body.style.removeProperty(name)\n this.appliedTokens = []\n for (const [name, value] of Object.entries(snapshot.active.tokens)) {\n document.body.style.setProperty(name, value)\n this.appliedTokens.push(name)\n }\n this.meta.content = getComputedStyle(document.body).backgroundColor\n if (!this.meta.isConnected) document.head.append(this.meta)\n }\n\n dispose(): void {\n document.documentElement.style.removeProperty('color-scheme')\n document.body.removeAttribute('data-ds-dark-theme')\n for (const name of this.appliedTokens) document.body.style.removeProperty(name)\n this.meta.remove()\n }\n}\n\nconst MOBILE_LAYOUT_STYLES = `\nhtml,body,#root{width:100%;height:100%;overflow:hidden}\n.dshm-shell{position:relative;display:grid;width:100%;height:100dvh;min-width:0;overflow:hidden;background:var(--dsw-alias-bg-base,#fff)}\n.dshm-main{grid-area:1/1;min-width:0;min-height:0;overflow:hidden}\n.dshm-main>*,.dshm-main>*>*{min-width:0}\n.dshm-menu{position:fixed;z-index:62;top:max(4px,env(safe-area-inset-top));left:4px;display:grid;place-items:center;width:44px;height:44px;padding:0;border:0;border-radius:12px;background:transparent;color:var(--dsw-alias-label-primary,#171a21);cursor:pointer;-webkit-tap-highlight-color:transparent}\n.dshm-menu:hover,.dshm-menu:active{background:var(--dsw-alias-interactive-bg-hover,#f1f3f6)}\n.dshm-menu:focus-visible,.dshm-close:focus-visible{outline:2px solid var(--dsw-alias-border-focus,#2563eb);outline-offset:2px}\n.dshm-menu svg,.dshm-close svg{width:22px;height:22px;stroke:currentColor;stroke-width:2;fill:none;stroke-linecap:round;stroke-linejoin:round}\n.dshm-drawer{position:fixed;z-index:70;inset:0 auto 0 0;box-sizing:border-box;width:min(88vw,340px);max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#f8fafc);box-shadow:18px 0 46px rgb(15 23 42 / 18%);left:-104%;transition:left 190ms cubic-bezier(.22,1,.36,1)}\n.dshm-drawer[data-open=true]{left:0}\n.dshm-drawer[data-open=false]{pointer-events:none;visibility:hidden;transition:left 190ms cubic-bezier(.22,1,.36,1),visibility 0s linear 190ms}\n.dshm-drawer>*{width:100%!important;height:100%!important}\n.dshm-details{position:fixed;z-index:80;inset:0 0 0 auto;box-sizing:border-box;width:min(94vw,460px);max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#fff);box-shadow:-18px 0 46px rgb(15 23 42 / 18%);transform:translateX(104%);transition:transform 190ms cubic-bezier(.22,1,.36,1)}\n.dshm-details[data-open=true]{transform:translateX(0)}\n.dshm-details[data-open=false]{pointer-events:none;visibility:hidden;transition:transform 190ms cubic-bezier(.22,1,.36,1),visibility 0s linear 190ms}\n.dshm-scrim{position:fixed;z-index:65;inset:0;border:0;background:rgb(15 23 42 / 40%);opacity:0;pointer-events:none;transition:opacity 180ms ease-out}\n.dshm-scrim[data-open=true]{opacity:1;pointer-events:auto}\n.dshm-overlay{position:fixed;z-index:90;inset:0;pointer-events:none}.dshm-overlay>*{pointer-events:auto}\n.dshm-shell header{min-width:0;padding-left:52px}\n.dshm-shell textarea{font-size:16px}\n.dshm-shell table{display:block;max-width:100%;overflow-x:auto}\n.dshm-shell pre{max-width:100%;overflow-x:auto}\n.dshm-shell img,.dshm-shell video,.dshm-shell canvas,.dshm-shell svg{max-width:100%}\n.dshm-shell [data-disclosure-row]{min-width:0;max-width:100%}\n.dshm-shell [data-disclosure-row]>*{min-width:0;overflow-wrap:anywhere}\n.dshm-shell [data-context-fields]>*{min-width:0}\n.dshm-shell [class*=\"_body\"]{max-width:100%;overflow-wrap:anywhere}\n@media(max-width:420px){.dshm-shell [data-context-fields]>*{display:grid;grid-template-columns:1fr!important;gap:4px}.dshm-shell [class*=\"_ioSection\"]{grid-template-columns:1fr!important}}\n@media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim{transition:none!important}}\n`\n\nfunction icon(paths: readonly string[]): ReactNode {\n return createElement('svg', { 'aria-hidden': true, viewBox: '0 0 24 24' },\n ...paths.map((path, index) => createElement('path', { d: path, key: index })))\n}\n\nfunction MobileAppFrame(props: MobileRootProps & { readonly controller: MobileLayoutController }): ReactNode {\n const state = useSyncExternalStore(props.controller.subscribe, props.controller.getSnapshot)\n const hasSession = props.useSessions(session => {\n const current = session.current\n return current !== undefined && session.byId[current]?.blank === false\n })\n\n useEffect(() => {\n if (!hasSession) props.controller.closeDetails()\n }, [hasSession, props.controller])\n\n const closeDrawerAfterAction = (event: { readonly target: EventTarget | null; readonly currentTarget: EventTarget & HTMLElement }): void => {\n if (!(event.target instanceof Element)) return\n const action = event.target.closest('button,a,[role=\"button\"],[role=\"option\"],[role=\"treeitem\"]')\n if (action === null || action.closest('[data-dshm-keep-open],[aria-haspopup=\"dialog\"]') !== null) return\n window.setTimeout(() => { props.controller.closeSidebar() }, 80)\n }\n\n return createElement('div', { className: 'dshm-shell' },\n createElement('main', { className: 'dshm-main' }, props.renderSlot('conversation', {})),\n createElement('button', {\n 'aria-expanded': state.sidebarOpen,\n 'aria-label': state.sidebarOpen ? '关闭导航' : '打开导航',\n className: 'dshm-menu',\n onClick: () => { props.controller.toggleSidebar() },\n type: 'button',\n }, icon(['M4 6h16', 'M4 12h16', 'M4 18h16'])),\n createElement('button', {\n 'aria-label': '关闭浮层',\n className: 'dshm-scrim',\n 'data-open': state.sidebarOpen || state.detailsOpen,\n onClick: () => { state.detailsOpen ? props.controller.closeDetails() : props.controller.closeSidebar() },\n tabIndex: state.sidebarOpen || state.detailsOpen ? 0 : -1,\n type: 'button',\n }),\n createElement('aside', {\n 'aria-hidden': !state.sidebarOpen,\n className: 'dshm-drawer',\n 'data-open': state.sidebarOpen,\n onClickCapture: closeDrawerAfterAction,\n ...(state.sidebarOpen ? {} : { inert: '' }),\n }, props.renderSlot('sidebar', { collapsed: false, width: 340 })),\n createElement('aside', {\n 'aria-hidden': !state.detailsOpen,\n className: 'dshm-details',\n 'data-open': state.detailsOpen,\n ...(state.detailsOpen ? {} : { inert: '' }),\n }, hasSession ? props.renderSlot('details', {}) : undefined),\n createElement('div', { className: 'dshm-overlay', 'data-shell-overlay': true }, props.renderSlot('shell.overlay', {})),\n )\n}\n\n/** Replace the desktop layout module on the authenticated mobile surface. */\nexport function apply(ctx: MobileClientContext): void {\n const controller = new MobileLayoutController()\n ctx.effect(() => {\n const style = document.createElement('style')\n style.dataset.plugin = 'dsh-mobile-layout'\n style.textContent = MOBILE_LAYOUT_STYLES\n document.head.append(style)\n const disposeService = ctx.reflect.provide('layout', controller)\n const disposeRoot = ctx.slots.register({\n name: 'root',\n children: {\n sidebar: { kind: 'single', scope: 'root' },\n conversation: { kind: 'single', scope: 'session-maybe' },\n details: { kind: 'single', scope: 'session' },\n 'shell.overlay': { kind: 'list', scope: 'root' },\n },\n }, props => createElement(MobileAppFrame, { ...props, controller }))\n return () => {\n disposeRoot()\n void disposeService()\n style.remove()\n }\n }, 'dsh-mobile: dedicated root layout')\n\n ctx.effect(() => {\n const presenter = new ThemePresenter()\n presenter.apply(ctx.theme.getTheme())\n const off = ctx.on('theme/change', snapshot => { presenter.apply(snapshot) })\n return () => { off(); presenter.dispose() }\n }, 'dsh-mobile: theme presenter')\n}\n\n/** Preserve the official layout module's dependency ordering. */\nexport const inject: readonly string[] = ['slots', 'theme']\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mobile",
3
- "version": "0.1.0-alpha.17",
3
+ "version": "0.1.0-alpha.18",
4
4
  "private": false,
5
5
  "description": "DeepSeek Harness 移动端适配与安全局域网访问插件,支持 Android App 和手机浏览器。",
6
6
  "type": "module",
@@ -23,8 +23,10 @@
23
23
  "lib/**/*.mjs",
24
24
  "lib/cli.js",
25
25
  "lib/**/*.d.mts",
26
- "lib/client.js",
27
- "lib/client.js.map",
26
+ "lib/client.js",
27
+ "lib/client.js.map",
28
+ "lib/mobile-layout.js",
29
+ "lib/mobile-layout.js.map",
28
30
  "cordis.patch.yml",
29
31
  "README.md",
30
32
  "README.en.md",