dsh-mobile 0.3.15 → 0.3.16
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/CHANGELOG.md +12 -0
- package/README.en.md +10 -8
- package/README.md +10 -8
- package/lib/cli.js.map +1 -1
- package/lib/index.mjs +9 -4
- package/lib/index.mjs.map +1 -1
- package/lib/mobile-layout.js +89 -34
- package/lib/mobile-layout.js.map +1 -1
- package/package.json +1 -1
package/lib/mobile-layout.js
CHANGED
|
@@ -34,12 +34,37 @@ window.__ModuleLoader__.load({
|
|
|
34
34
|
* keep the overlay behavior byte-for-byte.
|
|
35
35
|
*/
|
|
36
36
|
const WIDE_LAYOUT_MIN_WIDTH_PX = 900;
|
|
37
|
+
const RIGHTBAR_MIN_WIDTH_PX = 300;
|
|
38
|
+
const RIGHTBAR_MAX_WIDTH_PX = 460;
|
|
39
|
+
const RIGHTBAR_CENTER_MIN_WIDTH_PX = 400;
|
|
40
|
+
const RIGHTBAR_DEFAULT_RATIO = .45;
|
|
41
|
+
const SIDEBAR_EXPANDED_WIDTH_PX = 340;
|
|
42
|
+
const SIDEBAR_COLLAPSED_WIDTH_PX = 56;
|
|
37
43
|
function isWideViewportLayout(viewportWidth) {
|
|
38
44
|
return viewportWidth >= 900;
|
|
39
45
|
}
|
|
40
|
-
/**
|
|
41
|
-
function
|
|
42
|
-
|
|
46
|
+
/** Preserve a readable conversation width before turning a requested right track into a docked column. */
|
|
47
|
+
function resolveMobileRightbarLayout(viewportWidth, sidebarOpen, track, fullscreen) {
|
|
48
|
+
const overlayWidth = Math.min(viewportWidth * .94, RIGHTBAR_MAX_WIDTH_PX);
|
|
49
|
+
if (!track || fullscreen || !isWideViewportLayout(viewportWidth) && sidebarOpen) return Object.freeze({
|
|
50
|
+
docked: false,
|
|
51
|
+
width: overlayWidth
|
|
52
|
+
});
|
|
53
|
+
const sidebarWidth = isWideViewportLayout(viewportWidth) && sidebarOpen ? SIDEBAR_EXPANDED_WIDTH_PX : SIDEBAR_COLLAPSED_WIDTH_PX;
|
|
54
|
+
const available = Math.floor(viewportWidth - sidebarWidth - RIGHTBAR_CENTER_MIN_WIDTH_PX);
|
|
55
|
+
if (available < RIGHTBAR_MIN_WIDTH_PX) return Object.freeze({
|
|
56
|
+
docked: false,
|
|
57
|
+
width: overlayWidth
|
|
58
|
+
});
|
|
59
|
+
const preferred = Math.min(RIGHTBAR_MAX_WIDTH_PX, Math.max(RIGHTBAR_MIN_WIDTH_PX, Math.round(viewportWidth * RIGHTBAR_DEFAULT_RATIO)));
|
|
60
|
+
return Object.freeze({
|
|
61
|
+
docked: true,
|
|
62
|
+
width: Math.min(preferred, available)
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
/** Whether an overlay drawer needs the modal scrim without dimming a docked desktop panel. */
|
|
66
|
+
function isMobileScrimOpen(sidebarOpen, detailsModalOpen, wideViewport) {
|
|
67
|
+
return !wideViewport && sidebarOpen || detailsModalOpen;
|
|
43
68
|
}
|
|
44
69
|
/**
|
|
45
70
|
* Close the details drawer from its scrim, collapsing the DSH 0.1.5 right
|
|
@@ -62,6 +87,8 @@ window.__ModuleLoader__.load({
|
|
|
62
87
|
snapshot = Object.freeze({
|
|
63
88
|
sidebarOpen: viewportIsWide(),
|
|
64
89
|
detailsOpen: false,
|
|
90
|
+
rightbarTrack: false,
|
|
91
|
+
rightbarFullscreen: false,
|
|
65
92
|
panelInfo: Object.freeze({ activePanelId: null })
|
|
66
93
|
});
|
|
67
94
|
listeners = /* @__PURE__ */ new Set();
|
|
@@ -81,7 +108,11 @@ window.__ModuleLoader__.load({
|
|
|
81
108
|
this.update({ detailsOpen: true });
|
|
82
109
|
}
|
|
83
110
|
closeDetails() {
|
|
84
|
-
this.update({
|
|
111
|
+
this.update({
|
|
112
|
+
detailsOpen: false,
|
|
113
|
+
rightbarTrack: false,
|
|
114
|
+
rightbarFullscreen: false
|
|
115
|
+
});
|
|
85
116
|
}
|
|
86
117
|
closeSidebar() {
|
|
87
118
|
this.update({ sidebarOpen: false });
|
|
@@ -110,13 +141,13 @@ window.__ModuleLoader__.load({
|
|
|
110
141
|
if (this.hasMainPanel === hasMainPanel) this.hasMainPanel = void 0;
|
|
111
142
|
};
|
|
112
143
|
}
|
|
113
|
-
/**
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
144
|
+
/** Preserve the native seat's distinction between a docked track and fullscreen. */
|
|
145
|
+
openRightbar(track = true, fullscreen = false) {
|
|
146
|
+
this.update({
|
|
147
|
+
detailsOpen: true,
|
|
148
|
+
rightbarTrack: track,
|
|
149
|
+
rightbarFullscreen: fullscreen
|
|
150
|
+
});
|
|
120
151
|
}
|
|
121
152
|
/** Hide the right panel. */
|
|
122
153
|
closeRightbar() {
|
|
@@ -143,7 +174,7 @@ window.__ModuleLoader__.load({
|
|
|
143
174
|
...this.snapshot,
|
|
144
175
|
...next
|
|
145
176
|
});
|
|
146
|
-
if (snapshot.sidebarOpen === this.snapshot.sidebarOpen && snapshot.detailsOpen === this.snapshot.detailsOpen && snapshot.panelInfo === this.snapshot.panelInfo) return;
|
|
177
|
+
if (snapshot.sidebarOpen === this.snapshot.sidebarOpen && snapshot.detailsOpen === this.snapshot.detailsOpen && snapshot.rightbarTrack === this.snapshot.rightbarTrack && snapshot.rightbarFullscreen === this.snapshot.rightbarFullscreen && snapshot.panelInfo === this.snapshot.panelInfo) return;
|
|
147
178
|
this.snapshot = snapshot;
|
|
148
179
|
for (const listener of this.listeners) listener();
|
|
149
180
|
}
|
|
@@ -177,7 +208,12 @@ window.__ModuleLoader__.load({
|
|
|
177
208
|
const MOBILE_LAYOUT_STYLES = `
|
|
178
209
|
html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
179
210
|
.dshm-shell{position:relative;display:grid;width:100%;height:100dvh;min-width:0;overflow:hidden;background:var(--dsw-alias-bg-base,#fff)}
|
|
180
|
-
.dshm-main{grid-area:1/1;min-width:0;min-height:0;overflow:hidden}
|
|
211
|
+
.dshm-main{grid-area:1/1;position:relative;min-width:0;min-height:0;margin-right:0;overflow:hidden;transition:margin-right var(--ds-transition-duration-slow,190ms) var(--ds-ease-in-out,ease)}
|
|
212
|
+
.dshm-shell[data-rightbar-docked=true] .dshm-main{margin-right:var(--dshm-rightbar-width)}
|
|
213
|
+
.dshm-shell[data-rightbar-docked=true] .dshm-details{position:absolute;width:var(--dshm-rightbar-width);box-shadow:none;padding-top:0}
|
|
214
|
+
/* Draw above the native absolute panel without consuming its width or
|
|
215
|
+
intercepting controls; only docked mode needs this column separator. */
|
|
216
|
+
.dshm-shell[data-rightbar-docked=true] .dshm-details::after{content:"";position:absolute;inset:0 auto 0 0;width:.5px;background:var(--dsw-alias-border-l4);z-index:11;pointer-events:none}
|
|
181
217
|
.dshm-main>*,.dshm-main>*>*{min-width:0}
|
|
182
218
|
.dshm-drawer{position:fixed;z-index:70;inset:0 auto 0 0;box-sizing:border-box;width:56px;max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#f8fafc);box-shadow:none;will-change:width;transition:width 240ms cubic-bezier(.22,1,.36,1),box-shadow 240ms ease}
|
|
183
219
|
.dshm-drawer[data-open=true]{width:min(88vw,340px);box-shadow:18px 0 46px rgb(15 23 42 / 18%)}
|
|
@@ -185,6 +221,8 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
185
221
|
.dshm-drawer>*{width:100%!important;height:100%!important;transform:translateX(-6px);opacity:.94;transition:transform 220ms cubic-bezier(.22,1,.36,1),opacity 160ms ease-out}
|
|
186
222
|
.dshm-drawer[data-open=true]>*{transform:translateX(0);opacity:1}
|
|
187
223
|
.dshm-drawer[data-open=false]>*{width:56px!important}
|
|
224
|
+
/* display:none also suppresses fixed descendants installed by the phone adapter. */
|
|
225
|
+
.dshm-drawer[data-right-modal=true]{display:none!important}
|
|
188
226
|
.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)}
|
|
189
227
|
.dshm-details[data-open=true]{transform:translateX(0)}
|
|
190
228
|
.dshm-details[data-open=false]{pointer-events:none;visibility:hidden;transition:transform 190ms cubic-bezier(.22,1,.36,1),visibility 0s linear 190ms}
|
|
@@ -200,11 +238,11 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
200
238
|
.dshm-shell [data-disclosure-row]>*{min-width:0;overflow-wrap:anywhere}
|
|
201
239
|
.dshm-shell [data-context-fields]>*{min-width:0}
|
|
202
240
|
.dshm-shell [class*="_body"]{max-width:100%;overflow-wrap:anywhere}
|
|
241
|
+
@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}}
|
|
242
|
+
@media(max-width:600px){
|
|
203
243
|
.dshm-shell [data-question-key],.dshm-shell [data-plan-review-key]{box-sizing:border-box;width:100%;height:auto!important;min-width:0;flex:none!important;align-self:flex-end;padding:6px max(10px,env(safe-area-inset-left)) max(10px,env(safe-area-inset-bottom)) max(10px,env(safe-area-inset-right))!important}
|
|
204
244
|
.dshm-shell [data-question-key]>section,.dshm-shell [data-plan-review-key]>section{width:100%;height:auto!important;min-height:0!important;max-width:none!important;max-height:min(68dvh,520px)!important;border-radius:16px!important}
|
|
205
245
|
.dshm-shell [data-question-scroll],.dshm-shell [data-plan-review-scroll]{flex:0 1 auto!important;min-height:0!important;max-height:min(42dvh,360px)!important;overscroll-behavior:contain;scroll-padding-bottom:12px}
|
|
206
|
-
@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}}
|
|
207
|
-
@media(max-width:600px){
|
|
208
246
|
.dshm-shell [data-question-key]>section>header{display:flex!important;visibility:visible!important;flex:none!important;gap:8px!important;padding:12px 8px 4px 14px!important}
|
|
209
247
|
.dshm-shell [data-question-key]>section>header h2{min-width:0;overflow-wrap:anywhere;font-size:16px!important;line-height:22px!important}
|
|
210
248
|
.dshm-shell [data-question-key]>section>header button{min-width:40px;min-height:40px}
|
|
@@ -224,33 +262,39 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
224
262
|
.dshm-drawer[data-open=true]{width:340px;box-shadow:none}
|
|
225
263
|
.dshm-drawer[data-open=false]{width:56px}
|
|
226
264
|
}
|
|
227
|
-
@media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim,.dshm-drawer
|
|
265
|
+
@media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim,.dshm-drawer>*,.dshm-main{transition:none!important}}
|
|
228
266
|
`;
|
|
229
267
|
function MobileAppFrame(props) {
|
|
230
268
|
const state = (0, react.useSyncExternalStore)(props.controller.subscribe, props.controller.getSnapshot);
|
|
231
269
|
const suppressKeyboardUntil = (0, react.useRef)(0);
|
|
232
|
-
const [
|
|
270
|
+
const [viewportWidth, setViewportWidth] = (0, react.useState)(window.innerWidth);
|
|
271
|
+
(0, react.useEffect)(() => {
|
|
272
|
+
let frame;
|
|
273
|
+
const resize = () => {
|
|
274
|
+
if (frame !== void 0) return;
|
|
275
|
+
frame = window.requestAnimationFrame(() => {
|
|
276
|
+
frame = void 0;
|
|
277
|
+
setViewportWidth(window.innerWidth);
|
|
278
|
+
});
|
|
279
|
+
};
|
|
280
|
+
window.addEventListener("resize", resize);
|
|
281
|
+
return () => {
|
|
282
|
+
window.removeEventListener("resize", resize);
|
|
283
|
+
if (frame !== void 0) window.cancelAnimationFrame(frame);
|
|
284
|
+
};
|
|
285
|
+
}, []);
|
|
286
|
+
const wideViewport = isWideViewportLayout(viewportWidth);
|
|
287
|
+
const rightbar = resolveMobileRightbarLayout(viewportWidth, state.sidebarOpen, state.rightbarTrack, state.rightbarFullscreen);
|
|
288
|
+
const rightbarDocked = state.detailsOpen && rightbar.docked;
|
|
233
289
|
const [documentLanguage, setDocumentLanguage] = (0, react.useState)(document.documentElement.lang);
|
|
234
290
|
const language = resolveMobileLayoutLanguage(documentLanguage, navigator.languages.length > 0 ? navigator.languages : [navigator.language]);
|
|
235
291
|
const messages = MOBILE_LAYOUT_MESSAGES[language];
|
|
236
|
-
const scrimOpen = isMobileScrimOpen(state.sidebarOpen, state.detailsOpen, wideViewport);
|
|
292
|
+
const scrimOpen = isMobileScrimOpen(state.sidebarOpen, state.detailsOpen && !rightbarDocked, wideViewport);
|
|
237
293
|
const activeSessionId = props.useSessions((session) => {
|
|
238
294
|
const current = session.current;
|
|
239
295
|
return current !== void 0 && session.byId[current]?.blank === false ? current : void 0;
|
|
240
296
|
});
|
|
241
297
|
const hasSession = activeSessionId !== void 0;
|
|
242
|
-
(0, react.useEffect)(() => {
|
|
243
|
-
if (typeof window.matchMedia !== "function") return;
|
|
244
|
-
const query = window.matchMedia(`(min-width: 900px)`);
|
|
245
|
-
const syncViewport = () => {
|
|
246
|
-
setWideViewport(query.matches);
|
|
247
|
-
};
|
|
248
|
-
syncViewport();
|
|
249
|
-
query.addEventListener("change", syncViewport);
|
|
250
|
-
return () => {
|
|
251
|
-
query.removeEventListener("change", syncViewport);
|
|
252
|
-
};
|
|
253
|
-
}, []);
|
|
254
298
|
(0, react.useEffect)(() => {
|
|
255
299
|
const observer = new MutationObserver(() => {
|
|
256
300
|
setDocumentLanguage(document.documentElement.lang);
|
|
@@ -327,10 +371,13 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
327
371
|
};
|
|
328
372
|
return (0, react.createElement)("div", {
|
|
329
373
|
className: "dshm-shell",
|
|
330
|
-
lang: language
|
|
374
|
+
lang: language,
|
|
375
|
+
"data-rightbar-docked": rightbarDocked,
|
|
376
|
+
style: { "--dshm-rightbar-width": `${rightbar.width}px` }
|
|
331
377
|
}, (0, react.createElement)("main", {
|
|
332
378
|
className: "dshm-main",
|
|
333
|
-
"data-dsh-mobile-session": activeSessionId
|
|
379
|
+
"data-dsh-mobile-session": activeSessionId,
|
|
380
|
+
"data-pane": "conversation"
|
|
334
381
|
}, props.renderSlot("main", {}, {
|
|
335
382
|
entryKey: state.panelInfo.activePanelId ?? "conversation",
|
|
336
383
|
fallback: props.renderSlot("conversation", {})
|
|
@@ -348,7 +395,14 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
348
395
|
}), (0, react.createElement)("aside", {
|
|
349
396
|
"aria-label": messages.workspaceNavigation,
|
|
350
397
|
className: "dshm-drawer",
|
|
398
|
+
"data-pane": "sidebar",
|
|
351
399
|
"data-open": state.sidebarOpen,
|
|
400
|
+
"data-right-modal": state.detailsOpen && !rightbarDocked,
|
|
401
|
+
...state.detailsOpen && !rightbarDocked ? {
|
|
402
|
+
inert: "",
|
|
403
|
+
"aria-hidden": true
|
|
404
|
+
} : {},
|
|
405
|
+
...state.sidebarOpen ? {} : { "data-sidebar-collapsed": "" },
|
|
352
406
|
onClickCapture: closeDrawerAfterSessionAction
|
|
353
407
|
}, props.renderSlot("sidebar", {
|
|
354
408
|
collapsed: !state.sidebarOpen,
|
|
@@ -359,8 +413,8 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
359
413
|
"data-open": state.detailsOpen,
|
|
360
414
|
...state.detailsOpen ? {} : { inert: "" }
|
|
361
415
|
}, [hasSession ? props.renderSlot("details", {}) : void 0, props.renderSlot("rightbar", {
|
|
362
|
-
width:
|
|
363
|
-
viewportWidth
|
|
416
|
+
width: rightbar.width,
|
|
417
|
+
viewportWidth,
|
|
364
418
|
canShow: true
|
|
365
419
|
})]), (0, react.createElement)("div", {
|
|
366
420
|
className: "dshm-overlay",
|
|
@@ -460,6 +514,7 @@ html,body,#root{width:100%;height:100%;overflow:hidden}
|
|
|
460
514
|
exports.isSidebarRightControl = isSidebarRightControl;
|
|
461
515
|
exports.isWideViewportLayout = isWideViewportLayout;
|
|
462
516
|
exports.resolveMobileLayoutLanguage = resolveMobileLayoutLanguage;
|
|
517
|
+
exports.resolveMobileRightbarLayout = resolveMobileRightbarLayout;
|
|
463
518
|
return module.exports;
|
|
464
519
|
}
|
|
465
520
|
});
|
package/lib/mobile-layout.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"mobile-layout.js","names":["useSyncExternalStore","useRef","useState","createElement"],"sources":["../src/mobile-layout-messages.ts","../src/mobile-layout.ts"],"sourcesContent":["export type MobileLayoutLanguage = 'it' | 'en' | 'zh'\n\nexport const MOBILE_LAYOUT_MESSAGES = Object.freeze({\n it: Object.freeze({ closePanels: 'Chiudi pannelli', workspaceNavigation: 'Navigazione area di lavoro e sessioni' }),\n en: Object.freeze({ closePanels: 'Close panels', workspaceNavigation: 'Workspace and session navigation' }),\n zh: Object.freeze({ closePanels: '关闭浮层', workspaceNavigation: '工作区与会话导航' }),\n})\n","import { createElement, useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\nimport { MOBILE_LAYOUT_MESSAGES, type MobileLayoutLanguage } from './mobile-layout-messages.js'\n\nexport { MOBILE_LAYOUT_MESSAGES } from './mobile-layout-messages.js'\nexport type { MobileLayoutLanguage } from './mobile-layout-messages.js'\n\ninterface SessionState {\n readonly current?: string\n readonly byId: Readonly<Record<string, { readonly blank?: boolean; readonly title?: string } | undefined>>\n}\n\ninterface MobileRootProps {\n readonly renderSlot: (\n name: string,\n owner: Record<string, unknown>,\n options?: { readonly entryKey?: string | null; readonly fallback?: ReactNode },\n ) => ReactNode\n readonly useSessions: <T>(selector: (state: SessionState) => T) => T\n}\n\n/** Root contribution carrying one observable hook into the standard kit. */\ninterface RootHookContribution {\n readonly hooks: Readonly<Record<string, {\n readonly getSnapshot: () => unknown\n readonly subscribe: (listener: () => void) => () => void\n }>>\n}\n\ninterface MobileClientContext {\n readonly effect: (effect: () => void | (() => void), label?: string) => void\n readonly get: (name: string) => unknown\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 entries: (name: string) => readonly { readonly options: { readonly key?: string } }[]\n subscribe: (name: string, listener: () => void) => () => void\n /**\n * Publish a root standard hook. The official layout owns `panelInfo`, so\n * replacing that module means replacing the owner too.\n */\n provideRoot?: (contribution: RootHookContribution) => () => 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 readonly panelInfo: PanelInfoSnapshot\n}\n\n/**\n * Panel selection as the standard `usePanelInfo` hook reports it: a null\n * `activePanelId` means the conversation owns the center column.\n */\ninterface PanelInfoSnapshot {\n readonly activePanelId: string | null\n}\n\n/** Optional DSH 0.1.5 right-Sidebar controls used by a Mobile close gesture. */\nexport interface SidebarRightControl {\n isExpanded(): boolean\n toggleExpanded(): void\n}\n\n/** Whether an optional service value supports the public right-Sidebar controls. */\nexport function isSidebarRightControl(value: unknown): value is SidebarRightControl {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Partial<SidebarRightControl>\n return typeof candidate.isExpanded === 'function' && typeof candidate.toggleExpanded === 'function'\n}\n\n/**\n * Viewport width at which the dedicated layout treats the sidebar as a\n * persistent desktop panel instead of an overlay drawer. Narrow screens\n * keep the overlay behavior byte-for-byte.\n */\nexport const WIDE_LAYOUT_MIN_WIDTH_PX = 900\n\nexport function isWideViewportLayout(viewportWidth: number): boolean {\n return viewportWidth >= WIDE_LAYOUT_MIN_WIDTH_PX\n}\n\n/** Whether the narrow layout needs the modal scrim for either side panel. */\nexport function isMobileScrimOpen(\n sidebarOpen: boolean,\n detailsOpen: boolean,\n wideViewport: boolean,\n): boolean {\n return !wideViewport && (sidebarOpen || detailsOpen)\n}\n\n/**\n * Close the details drawer from its scrim, collapsing the DSH 0.1.5 right\n * Sidebar first when that optional service owns the visible content.\n */\nexport function closeDetailsFromScrim(sidebarRight: unknown, closeDetails: () => void): void {\n if (isSidebarRightControl(sidebarRight) && sidebarRight.isExpanded()) {\n sidebarRight.toggleExpanded()\n }\n closeDetails()\n}\n\n/** Reads the live viewport; unknown environments (SSR, tests) stay narrow. */\nfunction viewportIsWide(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false\n return window.matchMedia(`(min-width: ${WIDE_LAYOUT_MIN_WIDTH_PX}px)`).matches\n}\n\n/** Resolve the supported language used by the dedicated mobile layout. */\nexport function resolveMobileLayoutLanguage(\n documentLanguage: string,\n browserLanguages: readonly string[],\n): MobileLayoutLanguage {\n return [documentLanguage, ...browserLanguages]\n .map(value => value.trim().toLowerCase().split(/[-_]/u)[0])\n .find((value): value is MobileLayoutLanguage => value === 'it' || value === 'en' || value === 'zh') ?? 'en'\n}\n\nclass MobileLayoutController {\n // Wide viewports start with the persistent sidebar open; applying the\n // layout again (reconnect, refocus) reuses the module singleton below,\n // so an explicit user collapse is never reset.\n private snapshot: LayoutSnapshot = Object.freeze({\n sidebarOpen: viewportIsWide(),\n detailsOpen: false,\n panelInfo: Object.freeze({ activePanelId: null }),\n })\n private readonly listeners = new Set<() => void>()\n private hasMainPanel: ((id: string) => boolean) | undefined\n // Pending workspace/session navigation, cancelled whenever a newer one\n // starts. Mirrors the official LayoutController's navigation controller.\n private navigation = new AbortController()\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 /**\n * Invalidate the previous navigation and hand back a fresh signal. The\n * workspace controller wraps this around \"open a workspace\" and \"fork a\n * session\" (dsh-client-ui-workspace) — the path behind the sidebar's\n * new-session button. Without it startSession throws inside a\n * `.catch(reason => console.warn('new session failed:', reason))`, so the\n * button silently did nothing.\n */\n beginNavigation(): AbortSignal {\n this.navigation.abort()\n this.navigation = new AbortController()\n return this.navigation.signal\n }\n\n /** Invalidate pending navigations when this layout unloads. */\n dispose(): void {\n this.navigation.abort()\n }\n\n /** Bind panel validation to the live slot registry owned by this application. */\n bindMainPanelRegistry(hasMainPanel: (id: string) => boolean): () => void {\n this.hasMainPanel = hasMainPanel\n return () => {\n if (this.hasMainPanel === hasMainPanel) this.hasMainPanel = undefined\n }\n }\n\n /**\n * Show the right panel. The official controller tracks a persistent column\n * here; this layout always renders the panel as the details overlay, so both\n * the track and fullscreen requests open the same drawer.\n */\n openRightbar(_track?: boolean, _fullscreen?: boolean): void {\n this.openDetails()\n }\n\n /** Hide the right panel. */\n closeRightbar(): void {\n this.closeDetails()\n }\n\n /**\n * Select the main panel rendered in the center column; null means the\n * conversation. Mirrors the official LayoutController so the `panelInfo`\n * root hook stays truthful.\n */\n selectPanel(panelId: string | null): void {\n if (panelId !== null && this.hasMainPanel?.(panelId) !== true) {\n throw new Error(`layout.selectPanel: main panel \"${panelId}\" is not registered`)\n }\n this.navigation.abort()\n if (this.snapshot.panelInfo.activePanelId === panelId) return\n this.update({ panelInfo: Object.freeze({ activePanelId: panelId }) })\n }\n\n /** Drop a selected panel once no main-slot entry declares it. */\n retainMainPanels(panelIds: readonly string[]): void {\n const active = this.snapshot.panelInfo.activePanelId\n if (active !== null && !panelIds.includes(active)) {\n this.update({ panelInfo: Object.freeze({ activePanelId: null }) })\n }\n }\n\n private update(next: Partial<LayoutSnapshot>): void {\n const snapshot = Object.freeze({ ...this.snapshot, ...next })\n if (\n snapshot.sidebarOpen === this.snapshot.sidebarOpen\n && snapshot.detailsOpen === this.snapshot.detailsOpen\n && snapshot.panelInfo === this.snapshot.panelInfo\n ) 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\nexport const 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-drawer{position:fixed;z-index:70;inset:0 auto 0 0;box-sizing:border-box;width:56px;max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#f8fafc);box-shadow:none;will-change:width;transition:width 240ms cubic-bezier(.22,1,.36,1),box-shadow 240ms ease}\n.dshm-drawer[data-open=true]{width:min(88vw,340px);box-shadow:18px 0 46px rgb(15 23 42 / 18%)}\n.dshm-drawer[data-open=false]{pointer-events:auto;visibility:visible}\n.dshm-drawer>*{width:100%!important;height:100%!important;transform:translateX(-6px);opacity:.94;transition:transform 220ms cubic-bezier(.22,1,.36,1),opacity 160ms ease-out}\n.dshm-drawer[data-open=true]>*{transform:translateX(0);opacity:1}\n.dshm-drawer[data-open=false]>*{width:56px!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.dshm-shell [data-question-key],.dshm-shell [data-plan-review-key]{box-sizing:border-box;width:100%;height:auto!important;min-width:0;flex:none!important;align-self:flex-end;padding:6px max(10px,env(safe-area-inset-left)) max(10px,env(safe-area-inset-bottom)) max(10px,env(safe-area-inset-right))!important}\n.dshm-shell [data-question-key]>section,.dshm-shell [data-plan-review-key]>section{width:100%;height:auto!important;min-height:0!important;max-width:none!important;max-height:min(68dvh,520px)!important;border-radius:16px!important}\n.dshm-shell [data-question-scroll],.dshm-shell [data-plan-review-scroll]{flex:0 1 auto!important;min-height:0!important;max-height:min(42dvh,360px)!important;overscroll-behavior:contain;scroll-padding-bottom:12px}\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(max-width:600px){\n.dshm-shell [data-question-key]>section>header{display:flex!important;visibility:visible!important;flex:none!important;gap:8px!important;padding:12px 8px 4px 14px!important}\n.dshm-shell [data-question-key]>section>header h2{min-width:0;overflow-wrap:anywhere;font-size:16px!important;line-height:22px!important}\n.dshm-shell [data-question-key]>section>header button{min-width:40px;min-height:40px}\n.dshm-shell [data-question-key] [role=radio],.dshm-shell [data-question-key] [role=checkbox]{min-height:48px!important;touch-action:manipulation}\n.dshm-shell [data-question-key]>section>footer{display:grid!important;grid-template-columns:auto minmax(0,1fr);align-items:center!important;flex:none!important;gap:6px 8px!important;margin-top:4px!important;padding:6px 10px 10px!important}\n.dshm-shell [data-question-key]>section>footer>:last-child{grid-column:1/-1;display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr));width:100%;gap:8px!important}\n.dshm-shell [data-question-key]>section>footer>:last-child button{width:100%;min-height:44px}\n.dshm-shell [data-plan-review-key]>section>div:last-child{display:grid!important;grid-template-columns:1fr;gap:8px!important;padding:8px 12px 10px!important}\n.dshm-shell [data-plan-review-key]>section>div:last-child>div:last-child{display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr));width:100%;gap:8px!important}\n.dshm-shell [data-plan-review-key]>section>div:last-child>div:last-child>button:first-child{grid-column:1/-1}\n.dshm-shell [data-plan-review-key]>section>div:last-child button{width:100%;min-height:44px}\n}\n@media(min-width:900px){\n.dshm-shell{grid-template-columns:auto minmax(0,1fr)}\n.dshm-main{grid-area:1/2}\n.dshm-drawer{position:static;grid-area:1/1;box-shadow:none}\n.dshm-drawer[data-open=true]{width:340px;box-shadow:none}\n.dshm-drawer[data-open=false]{width:56px}\n}\n@media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim,.dshm-drawer>*{transition:none!important}}\n`\n\nfunction MobileAppFrame(props: MobileRootProps & {\n readonly controller: MobileLayoutController\n readonly requestDetailsClose: () => void\n}): ReactNode {\n const state = useSyncExternalStore(props.controller.subscribe, props.controller.getSnapshot)\n const suppressKeyboardUntil = useRef(0)\n const [wideViewport, setWideViewport] = useState(viewportIsWide)\n const [documentLanguage, setDocumentLanguage] = useState(document.documentElement.lang)\n const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language]\n const language = resolveMobileLayoutLanguage(documentLanguage, browserLanguages)\n const messages = MOBILE_LAYOUT_MESSAGES[language]\n const scrimOpen = isMobileScrimOpen(state.sidebarOpen, state.detailsOpen, wideViewport)\n const activeSessionId = props.useSessions(session => {\n const current = session.current\n return current !== undefined && session.byId[current]?.blank === false ? current : undefined\n })\n const hasSession = activeSessionId !== undefined\n\n useEffect(() => {\n if (typeof window.matchMedia !== 'function') return\n const query = window.matchMedia(`(min-width: ${WIDE_LAYOUT_MIN_WIDTH_PX}px)`)\n const syncViewport = (): void => { setWideViewport(query.matches) }\n syncViewport()\n query.addEventListener('change', syncViewport)\n return () => { query.removeEventListener('change', syncViewport) }\n }, [])\n\n useEffect(() => {\n const observer = new MutationObserver(() => { setDocumentLanguage(document.documentElement.lang) })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] })\n return () => { observer.disconnect() }\n }, [])\n\n useEffect(() => {\n if (!hasSession) props.controller.closeDetails()\n }, [hasSession, props.controller])\n\n // Mirror the stock layout: project the current session title into the\n // browser tab, restoring the product title when no session is selected.\n const sessionTitle = props.useSessions(session =>\n session.current === undefined ? undefined : session.byId[session.current]?.title,\n )\n useEffect(() => {\n const productTitle = document.title\n if (sessionTitle !== undefined) document.title = `${sessionTitle} — ${productTitle}`\n return () => { document.title = productTitle }\n }, [sessionTitle])\n\n useEffect(() => {\n const suppressAutofocus = (event: FocusEvent): void => {\n if (performance.now() >= suppressKeyboardUntil.current) return\n const target = event.target\n if (target instanceof HTMLElement && (target.matches('input,textarea') || target.isContentEditable)) target.blur()\n }\n const suppressBranchAutofocus = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const branch = event.target.closest('button[aria-label*=\"分支\"],button[aria-label*=\"Branch\"],button[aria-label*=\"branch\"],button[aria-label*=\"Ramo\"],button[aria-label*=\"ramo\"]')\n if (branch === null || branch.hasAttribute('disabled') || branch.getAttribute('aria-disabled') === 'true') return\n suppressKeyboardUntil.current = performance.now() + 700\n window.setTimeout(() => {\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }, 0)\n }\n const suppressCommandAutofocus = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const commandButton = event.target.closest('button[aria-haspopup=\"listbox\"]')\n if (commandButton === null) return\n // The native composer deliberately preserves editor focus on mousedown;\n // mobile command menus should open without summoning the soft keyboard.\n suppressKeyboardUntil.current = performance.now() + 700\n event.preventDefault()\n event.stopPropagation()\n window.setTimeout(() => {\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }, 0)\n }\n document.addEventListener('focusin', suppressAutofocus, true)\n document.addEventListener('click', suppressBranchAutofocus, true)\n document.addEventListener('mousedown', suppressCommandAutofocus, true)\n return () => {\n document.removeEventListener('focusin', suppressAutofocus, true)\n document.removeEventListener('click', suppressBranchAutofocus, true)\n document.removeEventListener('mousedown', suppressCommandAutofocus, true)\n }\n }, [])\n\n // A persistent wide sidebar stays put: selecting a session only dismisses\n // the narrow overlay drawer (and its soft-keyboard suppression).\n const closeDrawerAfterSessionAction = (event: { readonly target: EventTarget | null }): void => {\n if (viewportIsWide()) return\n if (!(event.target instanceof Element)) return\n const row = event.target.closest<HTMLElement>('[role=\"treeitem\"][aria-selected]')\n const action = event.target.closest('button,[role=\"button\"]')\n const startsSession = action?.matches('button[class*=\"_newSession\"],button[class*=\"_brand\"]')\n || /新建会话|新会话|new session|new conversation|nuova sessione|nuova conversazione/i.test(action?.getAttribute('aria-label') ?? '')\n if (row === null && !startsSession) return\n if (row !== null && action !== null && action !== row) return\n suppressKeyboardUntil.current = performance.now() + 500\n // Let the session row finish its own click handler before unmounting the drawer.\n window.setTimeout(() => {\n props.controller.closeSidebar()\n const active = document.activeElement\n if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement) active.blur()\n }, 0)\n }\n\n return createElement('div', { className: 'dshm-shell', lang: language },\n createElement('main', { className: 'dshm-main', 'data-dsh-mobile-session': activeSessionId },\n props.renderSlot('main', {}, {\n entryKey: state.panelInfo.activePanelId ?? 'conversation',\n fallback: props.renderSlot('conversation', {}),\n })),\n createElement('button', {\n 'aria-label': messages.closePanels,\n className: 'dshm-scrim',\n // A persistent wide sidebar needs no dimming; the scrim only covers\n // the narrow overlay drawer and the details panel.\n 'data-open': scrimOpen,\n 'aria-hidden': !scrimOpen,\n onClick: () => {\n if (!scrimOpen) return\n state.detailsOpen ? props.requestDetailsClose() : props.controller.closeSidebar()\n },\n tabIndex: scrimOpen ? 0 : -1,\n type: 'button',\n }),\n createElement('aside', {\n 'aria-label': messages.workspaceNavigation,\n className: 'dshm-drawer',\n 'data-open': state.sidebarOpen,\n onClickCapture: closeDrawerAfterSessionAction,\n }, props.renderSlot('sidebar', {\n collapsed: !state.sidebarOpen,\n width: state.sidebarOpen ? 340 : 56,\n })),\n createElement('aside', {\n 'aria-hidden': !state.detailsOpen,\n className: 'dshm-details',\n 'data-open': state.detailsOpen,\n ...(state.detailsOpen ? {} : { inert: '' }),\n }, [\n // DSH 0.1.3 and earlier bind details to a Session. Newer DSH owns the\n // Session decision inside its root-scoped rightbar occupant, so that\n // occupant must stay mounted even while no Session is selected.\n hasSession ? props.renderSlot('details', {}) : undefined,\n props.renderSlot('rightbar', {\n width: Math.min(window.innerWidth * 0.94, 460),\n viewportWidth: window.innerWidth,\n // The official frame reports whether a persistent column still fits\n // (normal.rightbar > 0); this layout renders the right panel in the\n // details overlay, which needs no column space. Reporting false would\n // make RightbarSeat force the surface closed immediately after opening\n // it (ui-sidebar-right: `shown && !fullscreen && !canShow` ->\n // actions.setExpanded(false)), so the panel could never stay open on a\n // phone-width viewport.\n canShow: true,\n }),\n ]),\n createElement('div', { className: 'dshm-overlay', 'data-shell-overlay': true }, props.renderSlot('shell.overlay', {})),\n )\n}\n\n// One controller per document: re-applying the layout (reconnect, tab\n// refocus) must not reset the user's explicit sidebar toggle.\nlet sharedController: MobileLayoutController | undefined\n\n/** Replace the desktop layout module on the authenticated mobile surface. */\nexport function apply(ctx: MobileClientContext): void {\n sharedController ??= new MobileLayoutController()\n const controller = sharedController\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 disposeMainPanelRegistry = controller.bindMainPanelRegistry(id =>\n ctx.slots.entries('main').some(entry => entry.options.key === id))\n const disposeService = ctx.reflect.provide('layout', controller)\n // The official layout owns the root `panelInfo` hook; replacing the layout\n // module means replacing that owner. Without it every registration that\n // declares the standard `usePanelInfo` prop fails assembly\n // (\"strict standard hook 'panelInfo' has no source\") and renders as a dead\n // cell — the workspace sidebar's session list lives exactly there.\n const disposePanelInfo = typeof ctx.slots.provideRoot === 'function'\n ? ctx.slots.provideRoot({ hooks: { panelInfo: {\n getSnapshot: () => controller.getSnapshot().panelInfo,\n subscribe: (listener: () => void) => controller.subscribe(listener),\n } } })\n : () => {}\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 // The conversation panel moved into this keyed slot in DSH 0.1.5;\n // without the declaration the center column stays blank.\n main: { kind: 'keyed', scope: 'root' },\n rightbar: { kind: 'single', scope: 'root' },\n details: { kind: 'single', scope: 'session' },\n 'shell.overlay': { kind: 'list', scope: 'root' },\n },\n }, props => createElement(MobileAppFrame, {\n ...props,\n controller,\n // sidebarRight is registered after the layout it depends on, so resolve\n // the optional service at gesture time rather than caching it at boot.\n requestDetailsClose: () => {\n closeDetailsFromScrim(ctx.get('sidebarRight'), () => { controller.closeDetails() })\n },\n }))\n const retainMainPanels = (): void => {\n controller.retainMainPanels(ctx.slots.entries('main').flatMap(entry =>\n entry.options.key === undefined ? [] : [entry.options.key]))\n }\n const disposePanels = ctx.slots.subscribe('main', retainMainPanels)\n retainMainPanels()\n return () => {\n controller.dispose()\n disposePanels()\n disposeRoot()\n disposePanelInfo()\n void disposeService()\n disposeMainPanelRegistry()\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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"}
|
|
1
|
+
{"version":3,"file":"mobile-layout.js","names":["useSyncExternalStore","useRef","useState","createElement"],"sources":["../src/mobile-layout-messages.ts","../src/mobile-layout.ts"],"sourcesContent":["export type MobileLayoutLanguage = 'it' | 'en' | 'zh'\n\nexport const MOBILE_LAYOUT_MESSAGES = Object.freeze({\n it: Object.freeze({ closePanels: 'Chiudi pannelli', workspaceNavigation: 'Navigazione area di lavoro e sessioni' }),\n en: Object.freeze({ closePanels: 'Close panels', workspaceNavigation: 'Workspace and session navigation' }),\n zh: Object.freeze({ closePanels: '关闭浮层', workspaceNavigation: '工作区与会话导航' }),\n})\n","import { createElement, useEffect, useRef, useState, useSyncExternalStore } from 'react'\nimport type { ReactNode } from 'react'\nimport { MOBILE_LAYOUT_MESSAGES, type MobileLayoutLanguage } from './mobile-layout-messages.js'\n\nexport { MOBILE_LAYOUT_MESSAGES } from './mobile-layout-messages.js'\nexport type { MobileLayoutLanguage } from './mobile-layout-messages.js'\n\ninterface SessionState {\n readonly current?: string\n readonly byId: Readonly<Record<string, { readonly blank?: boolean; readonly title?: string } | undefined>>\n}\n\ninterface MobileRootProps {\n readonly renderSlot: (\n name: string,\n owner: Record<string, unknown>,\n options?: { readonly entryKey?: string | null; readonly fallback?: ReactNode },\n ) => ReactNode\n readonly useSessions: <T>(selector: (state: SessionState) => T) => T\n}\n\n/** Root contribution carrying one observable hook into the standard kit. */\ninterface RootHookContribution {\n readonly hooks: Readonly<Record<string, {\n readonly getSnapshot: () => unknown\n readonly subscribe: (listener: () => void) => () => void\n }>>\n}\n\ninterface MobileClientContext {\n readonly effect: (effect: () => void | (() => void), label?: string) => void\n readonly get: (name: string) => unknown\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 entries: (name: string) => readonly { readonly options: { readonly key?: string } }[]\n subscribe: (name: string, listener: () => void) => () => void\n /**\n * Publish a root standard hook. The official layout owns `panelInfo`, so\n * replacing that module means replacing the owner too.\n */\n provideRoot?: (contribution: RootHookContribution) => () => 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 readonly rightbarTrack: boolean\n readonly rightbarFullscreen: boolean\n readonly panelInfo: PanelInfoSnapshot\n}\n\n/**\n * Panel selection as the standard `usePanelInfo` hook reports it: a null\n * `activePanelId` means the conversation owns the center column.\n */\ninterface PanelInfoSnapshot {\n readonly activePanelId: string | null\n}\n\n/** Optional DSH 0.1.5 right-Sidebar controls used by a Mobile close gesture. */\nexport interface SidebarRightControl {\n isExpanded(): boolean\n toggleExpanded(): void\n}\n\n/** Whether an optional service value supports the public right-Sidebar controls. */\nexport function isSidebarRightControl(value: unknown): value is SidebarRightControl {\n if (typeof value !== 'object' || value === null) return false\n const candidate = value as Partial<SidebarRightControl>\n return typeof candidate.isExpanded === 'function' && typeof candidate.toggleExpanded === 'function'\n}\n\n/**\n * Viewport width at which the dedicated layout treats the sidebar as a\n * persistent desktop panel instead of an overlay drawer. Narrow screens\n * keep the overlay behavior byte-for-byte.\n */\nexport const WIDE_LAYOUT_MIN_WIDTH_PX = 900\nconst RIGHTBAR_MIN_WIDTH_PX = 300\nconst RIGHTBAR_MAX_WIDTH_PX = 460\nconst RIGHTBAR_CENTER_MIN_WIDTH_PX = 400\nconst RIGHTBAR_DEFAULT_RATIO = 0.45\nconst SIDEBAR_EXPANDED_WIDTH_PX = 340\nconst SIDEBAR_COLLAPSED_WIDTH_PX = 56\n\nexport function isWideViewportLayout(viewportWidth: number): boolean {\n return viewportWidth >= WIDE_LAYOUT_MIN_WIDTH_PX\n}\n\n/** Resolved right-panel presentation for the dedicated responsive layout. */\nexport interface MobileRightbarLayout {\n readonly docked: boolean\n readonly width: number\n}\n\n/** Preserve a readable conversation width before turning a requested right track into a docked column. */\nexport function resolveMobileRightbarLayout(\n viewportWidth: number,\n sidebarOpen: boolean,\n track: boolean,\n fullscreen: boolean,\n): MobileRightbarLayout {\n const overlayWidth = Math.min(viewportWidth * 0.94, RIGHTBAR_MAX_WIDTH_PX)\n if (!track || fullscreen || (!isWideViewportLayout(viewportWidth) && sidebarOpen)) {\n return Object.freeze({ docked: false, width: overlayWidth })\n }\n const sidebarWidth = isWideViewportLayout(viewportWidth) && sidebarOpen\n ? SIDEBAR_EXPANDED_WIDTH_PX\n : SIDEBAR_COLLAPSED_WIDTH_PX\n const available = Math.floor(viewportWidth - sidebarWidth - RIGHTBAR_CENTER_MIN_WIDTH_PX)\n if (available < RIGHTBAR_MIN_WIDTH_PX) return Object.freeze({ docked: false, width: overlayWidth })\n const preferred = Math.min(\n RIGHTBAR_MAX_WIDTH_PX,\n Math.max(RIGHTBAR_MIN_WIDTH_PX, Math.round(viewportWidth * RIGHTBAR_DEFAULT_RATIO)),\n )\n return Object.freeze({ docked: true, width: Math.min(preferred, available) })\n}\n\n/** Whether an overlay drawer needs the modal scrim without dimming a docked desktop panel. */\nexport function isMobileScrimOpen(\n sidebarOpen: boolean,\n detailsModalOpen: boolean,\n wideViewport: boolean,\n): boolean {\n return (!wideViewport && sidebarOpen) || detailsModalOpen\n}\n\n/**\n * Close the details drawer from its scrim, collapsing the DSH 0.1.5 right\n * Sidebar first when that optional service owns the visible content.\n */\nexport function closeDetailsFromScrim(sidebarRight: unknown, closeDetails: () => void): void {\n if (isSidebarRightControl(sidebarRight) && sidebarRight.isExpanded()) {\n sidebarRight.toggleExpanded()\n }\n closeDetails()\n}\n\n/** Reads the live viewport; unknown environments (SSR, tests) stay narrow. */\nfunction viewportIsWide(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false\n return window.matchMedia(`(min-width: ${WIDE_LAYOUT_MIN_WIDTH_PX}px)`).matches\n}\n\n/** Resolve the supported language used by the dedicated mobile layout. */\nexport function resolveMobileLayoutLanguage(\n documentLanguage: string,\n browserLanguages: readonly string[],\n): MobileLayoutLanguage {\n return [documentLanguage, ...browserLanguages]\n .map(value => value.trim().toLowerCase().split(/[-_]/u)[0])\n .find((value): value is MobileLayoutLanguage => value === 'it' || value === 'en' || value === 'zh') ?? 'en'\n}\n\nclass MobileLayoutController {\n // Wide viewports start with the persistent sidebar open; applying the\n // layout again (reconnect, refocus) reuses the module singleton below,\n // so an explicit user collapse is never reset.\n private snapshot: LayoutSnapshot = Object.freeze({\n sidebarOpen: viewportIsWide(),\n detailsOpen: false,\n rightbarTrack: false,\n rightbarFullscreen: false,\n panelInfo: Object.freeze({ activePanelId: null }),\n })\n private readonly listeners = new Set<() => void>()\n private hasMainPanel: ((id: string) => boolean) | undefined\n // Pending workspace/session navigation, cancelled whenever a newer one\n // starts. Mirrors the official LayoutController's navigation controller.\n private navigation = new AbortController()\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, rightbarTrack: false, rightbarFullscreen: false })\n }\n\n closeSidebar(): void {\n this.update({ sidebarOpen: false })\n }\n\n /**\n * Invalidate the previous navigation and hand back a fresh signal. The\n * workspace controller wraps this around \"open a workspace\" and \"fork a\n * session\" (dsh-client-ui-workspace) — the path behind the sidebar's\n * new-session button. Without it startSession throws inside a\n * `.catch(reason => console.warn('new session failed:', reason))`, so the\n * button silently did nothing.\n */\n beginNavigation(): AbortSignal {\n this.navigation.abort()\n this.navigation = new AbortController()\n return this.navigation.signal\n }\n\n /** Invalidate pending navigations when this layout unloads. */\n dispose(): void {\n this.navigation.abort()\n }\n\n /** Bind panel validation to the live slot registry owned by this application. */\n bindMainPanelRegistry(hasMainPanel: (id: string) => boolean): () => void {\n this.hasMainPanel = hasMainPanel\n return () => {\n if (this.hasMainPanel === hasMainPanel) this.hasMainPanel = undefined\n }\n }\n\n /** Preserve the native seat's distinction between a docked track and fullscreen. */\n openRightbar(track = true, fullscreen = false): void {\n this.update({ detailsOpen: true, rightbarTrack: track, rightbarFullscreen: fullscreen })\n }\n\n /** Hide the right panel. */\n closeRightbar(): void {\n this.closeDetails()\n }\n\n /**\n * Select the main panel rendered in the center column; null means the\n * conversation. Mirrors the official LayoutController so the `panelInfo`\n * root hook stays truthful.\n */\n selectPanel(panelId: string | null): void {\n if (panelId !== null && this.hasMainPanel?.(panelId) !== true) {\n throw new Error(`layout.selectPanel: main panel \"${panelId}\" is not registered`)\n }\n this.navigation.abort()\n if (this.snapshot.panelInfo.activePanelId === panelId) return\n this.update({ panelInfo: Object.freeze({ activePanelId: panelId }) })\n }\n\n /** Drop a selected panel once no main-slot entry declares it. */\n retainMainPanels(panelIds: readonly string[]): void {\n const active = this.snapshot.panelInfo.activePanelId\n if (active !== null && !panelIds.includes(active)) {\n this.update({ panelInfo: Object.freeze({ activePanelId: null }) })\n }\n }\n\n private update(next: Partial<LayoutSnapshot>): void {\n const snapshot = Object.freeze({ ...this.snapshot, ...next })\n if (\n snapshot.sidebarOpen === this.snapshot.sidebarOpen\n && snapshot.detailsOpen === this.snapshot.detailsOpen\n && snapshot.rightbarTrack === this.snapshot.rightbarTrack\n && snapshot.rightbarFullscreen === this.snapshot.rightbarFullscreen\n && snapshot.panelInfo === this.snapshot.panelInfo\n ) 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\nexport const 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;position:relative;min-width:0;min-height:0;margin-right:0;overflow:hidden;transition:margin-right var(--ds-transition-duration-slow,190ms) var(--ds-ease-in-out,ease)}\n.dshm-shell[data-rightbar-docked=true] .dshm-main{margin-right:var(--dshm-rightbar-width)}\n.dshm-shell[data-rightbar-docked=true] .dshm-details{position:absolute;width:var(--dshm-rightbar-width);box-shadow:none;padding-top:0}\n/* Draw above the native absolute panel without consuming its width or\n intercepting controls; only docked mode needs this column separator. */\n.dshm-shell[data-rightbar-docked=true] .dshm-details::after{content:\"\";position:absolute;inset:0 auto 0 0;width:.5px;background:var(--dsw-alias-border-l4);z-index:11;pointer-events:none}\n.dshm-main>*,.dshm-main>*>*{min-width:0}\n.dshm-drawer{position:fixed;z-index:70;inset:0 auto 0 0;box-sizing:border-box;width:56px;max-width:100%;padding-top:env(safe-area-inset-top);overflow:hidden;background:var(--dsw-alias-bg-layer-1,#f8fafc);box-shadow:none;will-change:width;transition:width 240ms cubic-bezier(.22,1,.36,1),box-shadow 240ms ease}\n.dshm-drawer[data-open=true]{width:min(88vw,340px);box-shadow:18px 0 46px rgb(15 23 42 / 18%)}\n.dshm-drawer[data-open=false]{pointer-events:auto;visibility:visible}\n.dshm-drawer>*{width:100%!important;height:100%!important;transform:translateX(-6px);opacity:.94;transition:transform 220ms cubic-bezier(.22,1,.36,1),opacity 160ms ease-out}\n.dshm-drawer[data-open=true]>*{transform:translateX(0);opacity:1}\n.dshm-drawer[data-open=false]>*{width:56px!important}\n/* display:none also suppresses fixed descendants installed by the phone adapter. */\n.dshm-drawer[data-right-modal=true]{display:none!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(max-width:600px){\n.dshm-shell [data-question-key],.dshm-shell [data-plan-review-key]{box-sizing:border-box;width:100%;height:auto!important;min-width:0;flex:none!important;align-self:flex-end;padding:6px max(10px,env(safe-area-inset-left)) max(10px,env(safe-area-inset-bottom)) max(10px,env(safe-area-inset-right))!important}\n.dshm-shell [data-question-key]>section,.dshm-shell [data-plan-review-key]>section{width:100%;height:auto!important;min-height:0!important;max-width:none!important;max-height:min(68dvh,520px)!important;border-radius:16px!important}\n.dshm-shell [data-question-scroll],.dshm-shell [data-plan-review-scroll]{flex:0 1 auto!important;min-height:0!important;max-height:min(42dvh,360px)!important;overscroll-behavior:contain;scroll-padding-bottom:12px}\n.dshm-shell [data-question-key]>section>header{display:flex!important;visibility:visible!important;flex:none!important;gap:8px!important;padding:12px 8px 4px 14px!important}\n.dshm-shell [data-question-key]>section>header h2{min-width:0;overflow-wrap:anywhere;font-size:16px!important;line-height:22px!important}\n.dshm-shell [data-question-key]>section>header button{min-width:40px;min-height:40px}\n.dshm-shell [data-question-key] [role=radio],.dshm-shell [data-question-key] [role=checkbox]{min-height:48px!important;touch-action:manipulation}\n.dshm-shell [data-question-key]>section>footer{display:grid!important;grid-template-columns:auto minmax(0,1fr);align-items:center!important;flex:none!important;gap:6px 8px!important;margin-top:4px!important;padding:6px 10px 10px!important}\n.dshm-shell [data-question-key]>section>footer>:last-child{grid-column:1/-1;display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr));width:100%;gap:8px!important}\n.dshm-shell [data-question-key]>section>footer>:last-child button{width:100%;min-height:44px}\n.dshm-shell [data-plan-review-key]>section>div:last-child{display:grid!important;grid-template-columns:1fr;gap:8px!important;padding:8px 12px 10px!important}\n.dshm-shell [data-plan-review-key]>section>div:last-child>div:last-child{display:grid!important;grid-template-columns:repeat(2,minmax(0,1fr));width:100%;gap:8px!important}\n.dshm-shell [data-plan-review-key]>section>div:last-child>div:last-child>button:first-child{grid-column:1/-1}\n.dshm-shell [data-plan-review-key]>section>div:last-child button{width:100%;min-height:44px}\n}\n@media(min-width:900px){\n.dshm-shell{grid-template-columns:auto minmax(0,1fr)}\n.dshm-main{grid-area:1/2}\n.dshm-drawer{position:static;grid-area:1/1;box-shadow:none}\n.dshm-drawer[data-open=true]{width:340px;box-shadow:none}\n.dshm-drawer[data-open=false]{width:56px}\n}\n@media(prefers-reduced-motion:reduce){.dshm-drawer,.dshm-details,.dshm-scrim,.dshm-drawer>*,.dshm-main{transition:none!important}}\n`\n\nfunction MobileAppFrame(props: MobileRootProps & {\n readonly controller: MobileLayoutController\n readonly requestDetailsClose: () => void\n}): ReactNode {\n const state = useSyncExternalStore(props.controller.subscribe, props.controller.getSnapshot)\n const suppressKeyboardUntil = useRef(0)\n const [viewportWidth, setViewportWidth] = useState(window.innerWidth)\n useEffect(() => {\n let frame: number | undefined\n const resize = (): void => {\n if (frame !== undefined) return\n frame = window.requestAnimationFrame(() => {\n frame = undefined\n setViewportWidth(window.innerWidth)\n })\n }\n window.addEventListener('resize', resize)\n return () => {\n window.removeEventListener('resize', resize)\n if (frame !== undefined) window.cancelAnimationFrame(frame)\n }\n }, [])\n const wideViewport = isWideViewportLayout(viewportWidth)\n const rightbar = resolveMobileRightbarLayout(\n viewportWidth,\n state.sidebarOpen,\n state.rightbarTrack,\n state.rightbarFullscreen,\n )\n const rightbarDocked = state.detailsOpen && rightbar.docked\n const [documentLanguage, setDocumentLanguage] = useState(document.documentElement.lang)\n const browserLanguages = navigator.languages.length > 0 ? navigator.languages : [navigator.language]\n const language = resolveMobileLayoutLanguage(documentLanguage, browserLanguages)\n const messages = MOBILE_LAYOUT_MESSAGES[language]\n const scrimOpen = isMobileScrimOpen(state.sidebarOpen, state.detailsOpen && !rightbarDocked, wideViewport)\n const activeSessionId = props.useSessions(session => {\n const current = session.current\n return current !== undefined && session.byId[current]?.blank === false ? current : undefined\n })\n const hasSession = activeSessionId !== undefined\n\n useEffect(() => {\n const observer = new MutationObserver(() => { setDocumentLanguage(document.documentElement.lang) })\n observer.observe(document.documentElement, { attributes: true, attributeFilter: ['lang'] })\n return () => { observer.disconnect() }\n }, [])\n\n useEffect(() => {\n if (!hasSession) props.controller.closeDetails()\n }, [hasSession, props.controller])\n\n // Mirror the stock layout: project the current session title into the\n // browser tab, restoring the product title when no session is selected.\n const sessionTitle = props.useSessions(session =>\n session.current === undefined ? undefined : session.byId[session.current]?.title,\n )\n useEffect(() => {\n const productTitle = document.title\n if (sessionTitle !== undefined) document.title = `${sessionTitle} — ${productTitle}`\n return () => { document.title = productTitle }\n }, [sessionTitle])\n\n useEffect(() => {\n const suppressAutofocus = (event: FocusEvent): void => {\n if (performance.now() >= suppressKeyboardUntil.current) return\n const target = event.target\n if (target instanceof HTMLElement && (target.matches('input,textarea') || target.isContentEditable)) target.blur()\n }\n const suppressBranchAutofocus = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const branch = event.target.closest('button[aria-label*=\"分支\"],button[aria-label*=\"Branch\"],button[aria-label*=\"branch\"],button[aria-label*=\"Ramo\"],button[aria-label*=\"ramo\"]')\n if (branch === null || branch.hasAttribute('disabled') || branch.getAttribute('aria-disabled') === 'true') return\n suppressKeyboardUntil.current = performance.now() + 700\n window.setTimeout(() => {\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }, 0)\n }\n const suppressCommandAutofocus = (event: MouseEvent): void => {\n if (!(event.target instanceof Element)) return\n const commandButton = event.target.closest('button[aria-haspopup=\"listbox\"]')\n if (commandButton === null) return\n // The native composer deliberately preserves editor focus on mousedown;\n // mobile command menus should open without summoning the soft keyboard.\n suppressKeyboardUntil.current = performance.now() + 700\n event.preventDefault()\n event.stopPropagation()\n window.setTimeout(() => {\n const active = document.activeElement\n if (active instanceof HTMLElement && (active.matches('input,textarea') || active.isContentEditable)) active.blur()\n }, 0)\n }\n document.addEventListener('focusin', suppressAutofocus, true)\n document.addEventListener('click', suppressBranchAutofocus, true)\n document.addEventListener('mousedown', suppressCommandAutofocus, true)\n return () => {\n document.removeEventListener('focusin', suppressAutofocus, true)\n document.removeEventListener('click', suppressBranchAutofocus, true)\n document.removeEventListener('mousedown', suppressCommandAutofocus, true)\n }\n }, [])\n\n // A persistent wide sidebar stays put: selecting a session only dismisses\n // the narrow overlay drawer (and its soft-keyboard suppression).\n const closeDrawerAfterSessionAction = (event: { readonly target: EventTarget | null }): void => {\n if (viewportIsWide()) return\n if (!(event.target instanceof Element)) return\n const row = event.target.closest<HTMLElement>('[role=\"treeitem\"][aria-selected]')\n const action = event.target.closest('button,[role=\"button\"]')\n const startsSession = action?.matches('button[class*=\"_newSession\"],button[class*=\"_brand\"]')\n || /新建会话|新会话|new session|new conversation|nuova sessione|nuova conversazione/i.test(action?.getAttribute('aria-label') ?? '')\n if (row === null && !startsSession) return\n if (row !== null && action !== null && action !== row) return\n suppressKeyboardUntil.current = performance.now() + 500\n // Let the session row finish its own click handler before unmounting the drawer.\n window.setTimeout(() => {\n props.controller.closeSidebar()\n const active = document.activeElement\n if (active instanceof HTMLInputElement || active instanceof HTMLTextAreaElement) active.blur()\n }, 0)\n }\n\n return createElement('div', {\n className: 'dshm-shell', lang: language,\n 'data-rightbar-docked': rightbarDocked,\n style: { '--dshm-rightbar-width': `${rightbar.width}px` },\n },\n createElement('main', {\n className: 'dshm-main',\n 'data-dsh-mobile-session': activeSessionId,\n // Legacy dsh-web community plugins use semantic pane attributes as their\n // compatibility path when the stock CSS-module column names are absent.\n 'data-pane': 'conversation',\n },\n props.renderSlot('main', {}, {\n entryKey: state.panelInfo.activePanelId ?? 'conversation',\n fallback: props.renderSlot('conversation', {}),\n })),\n createElement('button', {\n 'aria-label': messages.closePanels,\n className: 'dshm-scrim',\n // A persistent wide sidebar needs no dimming; the scrim only covers\n // the narrow overlay drawer and the details panel.\n 'data-open': scrimOpen,\n 'aria-hidden': !scrimOpen,\n onClick: () => {\n if (!scrimOpen) return\n state.detailsOpen ? props.requestDetailsClose() : props.controller.closeSidebar()\n },\n tabIndex: scrimOpen ? 0 : -1,\n type: 'button',\n }),\n createElement('aside', {\n 'aria-label': messages.workspaceNavigation,\n className: 'dshm-drawer',\n 'data-pane': 'sidebar',\n 'data-open': state.sidebarOpen,\n // The phone adapter promotes the whale toggle to a fixed high layer.\n // Remove that entire navigation surface while the right modal owns focus.\n 'data-right-modal': state.detailsOpen && !rightbarDocked,\n ...(state.detailsOpen && !rightbarDocked ? { inert: '', 'aria-hidden': true } : {}),\n // Community rows collapse their label when an ancestor carries this marker.\n ...(state.sidebarOpen ? {} : { 'data-sidebar-collapsed': '' }),\n onClickCapture: closeDrawerAfterSessionAction,\n }, props.renderSlot('sidebar', {\n collapsed: !state.sidebarOpen,\n width: state.sidebarOpen ? 340 : 56,\n })),\n createElement('aside', {\n 'aria-hidden': !state.detailsOpen,\n className: 'dshm-details',\n 'data-open': state.detailsOpen,\n ...(state.detailsOpen ? {} : { inert: '' }),\n }, [\n // DSH 0.1.3 and earlier bind details to a Session. Newer DSH owns the\n // Session decision inside its root-scoped rightbar occupant, so that\n // occupant must stay mounted even while no Session is selected.\n hasSession ? props.renderSlot('details', {}) : undefined,\n props.renderSlot('rightbar', {\n width: rightbar.width,\n viewportWidth,\n // The official frame reports whether a persistent column still fits\n // (normal.rightbar > 0); this layout renders the right panel in the\n // details overlay, which needs no column space. Reporting false would\n // make RightbarSeat force the surface closed immediately after opening\n // it (ui-sidebar-right: `shown && !fullscreen && !canShow` ->\n // actions.setExpanded(false)), so the panel could never stay open on a\n // phone-width viewport.\n canShow: true,\n }),\n ]),\n createElement('div', { className: 'dshm-overlay', 'data-shell-overlay': true }, props.renderSlot('shell.overlay', {})),\n )\n}\n\n// One controller per document: re-applying the layout (reconnect, tab\n// refocus) must not reset the user's explicit sidebar toggle.\nlet sharedController: MobileLayoutController | undefined\n\n/** Replace the desktop layout module on the authenticated mobile surface. */\nexport function apply(ctx: MobileClientContext): void {\n sharedController ??= new MobileLayoutController()\n const controller = sharedController\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 disposeMainPanelRegistry = controller.bindMainPanelRegistry(id =>\n ctx.slots.entries('main').some(entry => entry.options.key === id))\n const disposeService = ctx.reflect.provide('layout', controller)\n // The official layout owns the root `panelInfo` hook; replacing the layout\n // module means replacing that owner. Without it every registration that\n // declares the standard `usePanelInfo` prop fails assembly\n // (\"strict standard hook 'panelInfo' has no source\") and renders as a dead\n // cell — the workspace sidebar's session list lives exactly there.\n const disposePanelInfo = typeof ctx.slots.provideRoot === 'function'\n ? ctx.slots.provideRoot({ hooks: { panelInfo: {\n getSnapshot: () => controller.getSnapshot().panelInfo,\n subscribe: (listener: () => void) => controller.subscribe(listener),\n } } })\n : () => {}\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 // The conversation panel moved into this keyed slot in DSH 0.1.5;\n // without the declaration the center column stays blank.\n main: { kind: 'keyed', scope: 'root' },\n rightbar: { kind: 'single', scope: 'root' },\n details: { kind: 'single', scope: 'session' },\n 'shell.overlay': { kind: 'list', scope: 'root' },\n },\n }, props => createElement(MobileAppFrame, {\n ...props,\n controller,\n // sidebarRight is registered after the layout it depends on, so resolve\n // the optional service at gesture time rather than caching it at boot.\n requestDetailsClose: () => {\n closeDetailsFromScrim(ctx.get('sidebarRight'), () => { controller.closeDetails() })\n },\n }))\n const retainMainPanels = (): void => {\n controller.retainMainPanels(ctx.slots.entries('main').flatMap(entry =>\n entry.options.key === undefined ? [] : [entry.options.key]))\n }\n const disposePanels = ctx.slots.subscribe('main', retainMainPanels)\n retainMainPanels()\n return () => {\n controller.dispose()\n disposePanels()\n disposeRoot()\n disposePanelInfo()\n void disposeService()\n disposeMainPanelRegistry()\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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"}
|