dsh-zen-remote 1.1.0 → 1.1.2

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/lib/index.js CHANGED
@@ -18,6 +18,10 @@ import { lstat, mkdir, open, realpath, rm } from 'node:fs/promises';
18
18
  import { basename, extname, isAbsolute, join, relative, resolve, sep } from 'node:path';
19
19
  /** Exact route the phone composer POSTs one file body to. */
20
20
  export const UPLOAD_ROUTE = '/_dsh/mobile-nav/upload';
21
+ /** Exact route the browser GETs the plugin row's client-facing knobs from.
22
+ * The client bundle ships statically and never sees the row config, so the
23
+ * host republishes the client-relevant subset here (issue #2). */
24
+ export const CLIENT_CONFIG_ROUTE = '/_dsh/mobile-nav/client-config';
21
25
  /** Workspace-relative directory uploads land in (also the `@` prefix the composer inserts). */
22
26
  export const UPLOAD_DIR = '.dsh-uploads';
23
27
  /** Body cap when the plugin row sets no `maxUploadBytes`. */
@@ -306,5 +310,21 @@ export function apply(ctx, config = {}) {
306
310
  handler: (req, res) => handleUpload(webCtx, maxBytes, req, res),
307
311
  }), 'dsh-mobile-nav: upload route');
308
312
  });
313
+ // Needs only the webServer: the client-config route must exist even in a
314
+ // composition without live sessions.
315
+ ctx.inject(['webServer'], (webCtx) => {
316
+ webCtx.effect(() => webCtx.webServer.register({
317
+ kind: 'exact',
318
+ path: CLIENT_CONFIG_ROUTE,
319
+ handler: (req, res) => {
320
+ if (req.method !== 'GET') {
321
+ res.setHeader('Allow', 'GET');
322
+ responseJson(res, 405, { ok: false, error: { code: 'method-not-allowed', message: 'Use GET' } });
323
+ return;
324
+ }
325
+ responseJson(res, 200, { turnFoldDesktop: config.turnFoldDesktop === true });
326
+ },
327
+ }), 'dsh-mobile-nav: client config route');
328
+ });
309
329
  }
310
330
  //# sourceMappingURL=index.js.map
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
2
+ /** The safety pad this platform gets: the clearance above, or nothing off
3
+ * Android. UA sniffing is the right tool here — the target is a platform
4
+ * defect, not a feature that could be detected. */
5
+ export declare function safetyPad(userAgent: string): number;
6
+ /**
7
+ * Fallback lift for a keyboard the browser cannot see (issue #1 确诊根因:
8
+ * 微信输入法在该设备上不向系统 insets 上报键盘高度, Chrome 键盘高度恒 0).
9
+ * There is no signal to measure, so this is an estimate: the reporter's
10
+ * measured WeType is ~315 CSS px on a 858px viewport (~37%); 42% capped at
11
+ * 400px covers taller IME toolbars without stranding the composer mid-screen.
12
+ * ponytail: single heuristic constant; per-IME calibration only if reports
13
+ * show it misses.
14
+ */
15
+ export declare function estimatedLift(innerHeight: number): number;
16
+ /** One visualViewport reading, in CSS pixels. */
17
+ export interface ViewportReading {
18
+ /** Layout viewport height (window.innerHeight). */
19
+ innerHeight: number;
20
+ /** visualViewport.height. */
21
+ vvHeight: number;
22
+ /** visualViewport.offsetTop. */
23
+ offsetTop: number;
24
+ /** visualViewport.scale. */
25
+ scale: number;
26
+ }
27
+ /**
28
+ * How far the composer must rise so its bottom edge sits on the visual
29
+ * viewport's bottom edge (issue #1, 方案 2 of
30
+ * docs/research-ime-keyboard-occlusion.md).
31
+ *
32
+ * The composer is sticky at the LAYOUT viewport's bottom; the keyboard
33
+ * shrinks only the VISUAL viewport (Chrome 108+ resizes-visual). When the
34
+ * browser also pans the visual viewport down to reveal the focused field —
35
+ * iOS, and Android when its auto-scroll works — the occluded band is zero
36
+ * and this stays a no-op. When it shrinks without panning (the reported
37
+ * class of bug), the difference is exactly the hidden band.
38
+ *
39
+ * Pinch-zoom shrinks vvHeight too; the scale guard keeps zooming from
40
+ * flinging the composer around.
41
+ */
42
+ export declare function keyboardLift(reading: ViewportReading): number;
43
+ /**
44
+ * The lift the composer actually gets, from the three sources in priority
45
+ * order.
46
+ * @param geometric - {@link keyboardLift} of the current reading.
47
+ * @param estimate - the dumb-keyboard estimate, 0 when not in that mode.
48
+ * @param keyboardShrunk - the viewport lost {@link KEYBOARD_MIN_SHRINK_PX}
49
+ * or more against its no-keyboard baseline, i.e. the browser reacted.
50
+ * @param pad - this platform's safety clearance, from {@link safetyPad}.
51
+ * @returns pixels to translate the composer up by.
52
+ */
53
+ export declare function composerLift(geometric: number, estimate: number, keyboardShrunk: boolean, pad: number): number;
54
+ /**
55
+ * S10 — keep the composer above the software keyboard (< 768px).
56
+ *
57
+ * The shell deliberately relies on the browser's own focus-reveal behaviour
58
+ * (home.css.ts: plain overflow:hidden so iOS pans the visual viewport, no
59
+ * visualViewport JS). Issue #1 (小米 + 微信输入法) showed one environment
60
+ * where that chain can break while the viewport still shrinks. This effect
61
+ * is the increment that covers it: mirror the occluded band into a root CSS
62
+ * variable, and let the stylesheet translate the composer up by it. In every
63
+ * environment where the browser already handles the keyboard the band
64
+ * computes to zero and nothing changes.
65
+ *
66
+ * `scroll` is listened to as well as `resize`: panning the visual viewport
67
+ * changes offsetTop without a resize (CSSOM View §13.2), and both sides of
68
+ * the subtraction must stay fresh.
69
+ *
70
+ * Second layer (2026-08-21, after on-device confirmation): the reporter's
71
+ * 小米 + 微信输入法 keyboard is INVISIBLE to Chrome — vv.height stayed 859/858
72
+ * with the keyboard open, so no event ever fires and the geometry above is
73
+ * honestly zero. For that class only, a touch-granted composer focus starts a
74
+ * short probe; if the viewport has not moved at all by the end, the composer
75
+ * gets an ESTIMATED lift until blur. Guards against false positives:
76
+ * - probe only after a recent touch pointerdown (a hardware-keyboard focus
77
+ * never lifts anything);
78
+ * - any viewport movement ≥ {@link PROBE_EPSILON_PX} cancels the probe — a
79
+ * browser that shows any reaction owns the reveal itself (iOS pans,
80
+ * working Android resizes);
81
+ * - a non-zero geometric lift always wins over the estimate.
82
+ *
83
+ * Third layer: an IME that under-reports its height (toolbar strip left out
84
+ * of what it declares) makes the browser shrink the viewport by less than the
85
+ * keyboard covers — every number the page can read is self-consistent, so no
86
+ * occlusion is computable. Whenever a keyboard is up and the browser DID
87
+ * react, the composer therefore also gets {@link safetyPad} of clearance —
88
+ * Android only, where the under-reporting happens.
89
+ * See {@link composerLift} for how the three sources compose.
90
+ */
91
+ export declare function installKeyboardAvoid(ctx: ClientContext): void;
92
+ //# sourceMappingURL=keyboard-avoid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"keyboard-avoid.d.ts","sourceRoot":"","sources":["../../../../src/client/effects/keyboard-avoid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAgC3E;;mDAEmD;AACnD,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAyBD;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,iDAAiD;AACjD,MAAM,WAAW,eAAe;IAC9B,mDAAmD;IACnD,WAAW,EAAE,MAAM,CAAA;IACnB,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,gCAAgC;IAChC,SAAS,EAAE,MAAM,CAAA;IACjB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAA;CACd;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAI7D;AAED;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAC1B,SAAS,EAAE,MAAM,EACjB,QAAQ,EAAE,MAAM,EAChB,cAAc,EAAE,OAAO,EACvB,GAAG,EAAE,MAAM,GACV,MAAM,CAUR;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAkL7D"}
@@ -1,6 +1,8 @@
1
1
  import type { ClientContext } from '@deepseek-ai/dsh-client-runtime/client';
2
2
  /**
3
- * S8 — collapse a turn's process into one summary row (< 768px only).
3
+ * S8 — collapse a turn's process into one summary row (< 768px by default;
4
+ * every width when the plugin row sets `config.turnFoldDesktop` or a browser
5
+ * opts itself in via `?mobile-nav-turn-fold=1`).
4
6
  *
5
7
  * Route taken: DOM marking, not the `conversation.chat.node` keyed slot.
6
8
  * That slot dispatches on node kind and a second registration at a key
@@ -1 +1 @@
1
- {"version":3,"file":"turn-fold.d.ts","sourceRoot":"","sources":["../../../../src/client/effects/turn-fold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAqH3E;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAoIxD"}
1
+ {"version":3,"file":"turn-fold.d.ts","sourceRoot":"","sources":["../../../../src/client/effects/turn-fold.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,wCAAwC,CAAA;AAwJ3E;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,eAAe,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAwJxD"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAA0B,MAAM,wCAAwC,CAAA;AAqBnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,sCAAsC;QACtC,WAAW,EAAE,YAAY,CAAA;KAC1B;CACF;AAED,0GAA0G;AAC1G,eAAO,MAAM,MAAM,UAAgF,CAAA;AAEnG;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAgM9C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/client/index.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAA0B,MAAM,wCAAwC,CAAA;AAsBnG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,cAAc,CAAA;AAEhD,OAAO,QAAQ,kCAAkC,CAAC;IAChD,UAAU,kBAAkB;QAC1B,sCAAsC;QACtC,WAAW,EAAE,YAAY,CAAA;KAC1B;CACF;AAED,0GAA0G;AAC1G,eAAO,MAAM,MAAM,UAAgF,CAAA;AAEnG;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,aAAa,GAAG,IAAI,CAqM9C"}
@@ -1,2 +1,2 @@
1
- export declare const COMPOSER_CSS = "/* ---------- phone composer (< 768px) ---------- */\n\n/* The file picker the attachment button drives (S7) \u2014 hidden at EVERY width,\n deliberately OUTSIDE the phone media block below. A bare <input type=\"file\">\n renders as a native \"Choose Files\" control, and the slot wrapper around it is\n `display: contents`, so an un-hidden one becomes a flex item of the official\n tool row. Scoping this to < 768px once put that control in the DESKTOP\n composer (caught on a live deploy, 2026-08-17): the button that drives it is\n phone-only, but the input it drives is in the DOM at all widths. */\n[data-mobile-nav=\"attach-picker\"] {\n display: none !important;\n}\n\n@media (max-width: 767px) {\n /* --- 1. flatten the two official groups ---\n `_tools` and `_trailing` only exist to cluster controls left/right.\n display:contents dissolves them into `_row`, so the six leaf controls\n become siblings in one flex line and `order` alone drives the layout:\n [attach \u00B7 + \u00B7 permission \u00B7 model] \u2026elastic gap\u2026 [context ring \u00B7 send]. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] {\n justify-content: flex-start !important;\n gap: 5px !important;\n padding: 2px 8px 8px !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] {\n display: contents !important;\n }\n\n /* --- 2. the running order ---\n The attachment button (S3 placeholder, S7 wires it up) sits leftmost;\n the official \"+\" command menu keeps its seat right next to it, where\n the two \"insert something\" affordances read as one cluster. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] [data-mobile-nav=\"attach\"] {\n order: 1 !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_add\"] {\n order: 2 !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] {\n order: 3 !important;\n flex: 0 1 auto !important;\n min-width: 0 !important;\n gap: 6px !important;\n }\n /* The model seat carries the elastic gap: everything after it is pushed to\n the right edge, so no spacer element is needed. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] {\n order: 4 !important;\n flex: 0 1 auto !important;\n min-width: 0 !important;\n margin-right: auto !important;\n }\n /* Third-party input.right entries park next to the ring rather than\n landing at order 0 (= far left) once the groups are flattened. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.right\"] > * {\n order: 5 !important;\n }\n /* ContextMeter \u2014 the only `span` ending in _root inside the row. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > span[class$=\"_root\"] {\n order: 6 !important;\n flex: none !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [class$=\"_primary\"] {\n order: 7 !important;\n }\n\n /* --- 3. permission + model as icon-only pills (real-device round 2, 2026-08-17) ---\n S3's icon-and-label capsules read as noise on an actual phone \u2014 there\n is no room to usefully show a preset name or a model id, so the label\n text is now hidden outright and both triggers collapse to a plain\n ~44x30 icon button. This AGREES with (rather than fights) the official\n container query (`@container (width <= 460px) { .trigger:has(.triggerIcon)\n .triggerLabel { display: none } }`) that S3 had to override \u2014 no need\n to override it back. Accessible name is unaffected: both official\n triggers already ship a descriptive aria-label independent of the\n visible text (PermissionSelect: t('input.accessMode', {name}); the\n model trigger: t('trigger.aria'/'trigger.ariaEffort')) \u2014 verified live\n in dsh-client-ui-conversation / dsh-client-ui-model-selection lib/\n client.js, 2026-08-17 \u2014 so hiding the label costs nothing for screen\n readers. The permission trigger only renders an icon for the\n \"read-only\" / \"workspace-write\" presets (permissionGlyphs in\n dsh-client-ui-conversation) \u2014 \"Full access\" and any host-configured\n preset name fall back to chevron-only, a known gap in the official\n markup this plugin cannot fill without inventing new icon meaning.\n The model trigger never renders an icon at all (only label + optional\n effort text + chevron), so its ::before below draws one from a\n primitives icon path (Sparkle \u2014 the closest existing \"model\" glyph) as\n a CSS-only pseudo-element: it survives React re-renders for free\n (unlike a DOM-injected node, which would need a MutationObserver, see\n the preview-full-toggle pitfall in AGENTS.md) and does not touch\n accessible-name computation (empty generated content). S3's\n rtl-ellipsis trick on the model label is simply inert under\n display:none now; left alone rather than unpicked. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [class$=\"_triggerLabel\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] > [class$=\"_triggerLabel\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] > [class$=\"_triggerEffort\"] {\n display: none !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] button[class$=\"_trigger\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] {\n background: var(--dsw-specific-selector, rgba(127, 127, 127, .12)) !important;\n width: 44px !important;\n height: 30px !important;\n max-width: none !important;\n min-width: 0 !important;\n padding: 0 !important;\n gap: 2px !important;\n justify-content: center !important;\n border-radius: 999px !important;\n touch-action: manipulation !important;\n }\n /* PermissionSelect wraps its trigger in the Menu primitive's root span,\n which must shrink to the icon button's fixed width. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] > span:has(> button[class$=\"_trigger\"]) {\n flex: 0 0 auto !important;\n min-width: 0 !important;\n }\n /* ic_ds_sparkle_16 (@deepseek-ai/dsh-client-ui-primitives IconSparkle16\n path, copied verbatim) as a mask so it inherits currentColor like every\n other icon in the row \u2014 the model trigger has no official icon slot to\n hook into. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"]::before {\n content: '';\n width: 16px;\n height: 16px;\n flex: none;\n background: currentColor;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E\");\n -webkit-mask-size: contain;\n mask-size: contain;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n }\n\n /* --- 4. both menus become bottom sheets ---\n The permission menu is the Menu primitive (role=menu, absolute, side=top)\n and the model menu is ModelSelect's own `_menu` (absolute, bottom+right).\n Neither has a transformed ancestor between it and the viewport, so\n position:fixed re-anchors both to the screen edge. Only the shell moves \u2014\n the items, the two-level model panes and every selection handler stay\n official. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] {\n position: fixed !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n top: auto !important;\n transform: none !important;\n width: 100% !important;\n min-width: 0 !important;\n max-width: none !important;\n max-height: min(70dvh, 520px) !important;\n box-sizing: border-box !important;\n border-radius: 16px 16px 0 0 !important;\n border-bottom: none !important;\n padding: 8px 8px calc(8px + var(--mnav-sab)) !important;\n z-index: 60 !important;\n box-shadow: 0 -8px 32px rgba(0, 0, 0, .18) !important;\n }\n /* 44pt+ rows in both sheets (Menu items, model options, and the model\n sheet's two root cells that drill into the model / effort panes). */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"] [role=\"menuitem\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] [class$=\"_option\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] [class$=\"_cell\"] {\n min-height: 48px !important;\n border-radius: 12px !important;\n font-size: 15px !important;\n }\n /* --- 4b. the official scroll-to-bottom button must not poke through the\n sheet (real-device follow-up, 2026-08-17) ---\n ChatView's own \"jump to latest\" button (aria-label t(\"chat.toBottom\"))\n is `position: sticky; z-index: 8` inside the message column, not\n `position: fixed` \u2014 it never escapes to the same top-level stacking\n context our sheets get promoted to, so raising the sheet's z-index\n further does nothing (measured live: it still rendered on top at\n z-index 60 vs 8). Rather than chase engine-specific stacking-context\n semantics (Chromium and WebKit do not always agree here \u2014 see AGENTS.md\n CDP/document.hidden lesson for another instance of that), this hides\n the button outright while either sheet is open and lets it reappear on\n close: a plain `display: none` behind a live `:has()` read is correct\n regardless of which stacking rules the engine happens to apply.\n Selector: no hashed classes (dsh-client-ui-conversation lib/client.js,\n verified 2026-08-17) \u2014 `data-chat-flow` is the one stable attribute on\n the message column, and the button's wrapper is its only sibling\n (ChatView only ever renders the column and, conditionally, this one\n slot), so the adjacent-sibling combinator pins it precisely. */\n body:has([data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"]) [data-chat-flow] + div,\n body:has([data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"]) [data-chat-flow] + div {\n display: none !important;\n }\n /* --- 5. input box: two lines minimum, five lines maximum ---\n The official autosizer drives the box off the hidden mirror's height, so\n a min-height on the mirror IS the min-height of the field; the scroll cap\n rides the official custom property. 52px = 2 x 24px line box + 4px pad,\n 124px = 5 lines. The hero card keeps its own one-line collapse (misc.css\n pins _scroll/_grow while the placeholder shows). */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] [class$=\"_mirror\"] {\n min-height: 52px !important;\n }\n [class$=\"_composerSeat\"] {\n --dsh-composer-text-max-height: 124px;\n }\n\n /* --- 6. no divider above OR below the message list ---\n Instead of a rule the messages butt against, the message scroller\n fades out over its last 26px (S3, no divider above the composer) AND\n fades in over its first 20px (S3.1 real-device round 2: the header's\n own bottom line is removed too, styles/header.css.ts's\n `header::after` rule). The mask lives on the scroll body (NOT on the\n header or the composer): a mask clips everything it paints, and both\n surfaces host position:fixed children (composer's permission/model\n sheets, any future header overlay) that must not be clipped.\n `mask-image` can only be declared once per element, so both fades are\n ONE linear-gradient rather than two separate declarations (the second\n would silently replace the first) \u2014 this is the merge of what used to\n be S3's bottom-only mask. */\n [class$=\"_scrollBody\"] {\n -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 26px), transparent 100%);\n mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 26px), transparent 100%);\n }\n [class$=\"_composerSeat\"],\n [class$=\"_composerStack\"] {\n border-top: none !important;\n }\n\n /* --- 7. dock entries become mini chips above the input card ---\n conversation.input.dock is display:contents (inline style), so its\n entries are stacked rows of the composer column. Forcing the slot itself\n to flex turns them into ONE scrollable chip line outside and above the\n card. Written against the slot, not against any particular plugin's chip\n (the git branch chip and the todo panel are third-party and may not be\n installed at all). */\n [data-slot=\"conversation.input.dock\"] {\n display: flex !important;\n flex-flow: row nowrap !important;\n align-items: center !important;\n gap: 6px !important;\n min-width: 0 !important;\n margin: 0 16px !important;\n overflow-x: auto !important;\n overflow-y: hidden !important;\n scrollbar-width: none !important;\n }\n [data-slot=\"conversation.input.dock\"]::-webkit-scrollbar {\n display: none !important;\n }\n [data-slot=\"conversation.input.dock\"] > * {\n flex: 0 0 auto !important;\n max-width: 70% !important;\n min-height: 26px !important;\n max-height: 26px !important;\n border-radius: 999px !important;\n font-size: 11.5px !important;\n line-height: 18px !important;\n overflow: hidden !important;\n }\n /* The git-graph chip carries a 34px tablet target from misc.css; that\n selector is more specific, so restate it at the phone breakpoint. */\n [data-slot=\"conversation.input.dock\"] [data-gitgraph-chip-anchor] [data-gitgraph-chip] {\n min-height: 26px !important;\n padding: 0 10px !important;\n font-size: 11.5px !important;\n }\n /* --- 7a. the native to-do card opts OUT of the 26px pill cage ---\n conversation.input.dock is not only a chip rail: DSH's own TodoPanel\n (dsh-client-ui-conversation) registers here, and it is an expandable\n card, not a pill \u2014 tapping its header renders the to-do list INSIDE\n itself. The cage's max-height:26px + overflow:hidden left that list\n rendered but clipped to nothing, so on a phone the bar looked dead:\n aria-expanded flipped, 8 rows mounted at 216px, and the card still\n painted 26px tall (measured live, 2026-08-20). Give it the official\n geometry back \u2014 full row, natural height, 12px radius. Its list caps\n itself at 180px with its own scroller, so this cannot run into the\n composer. Anchored on the official data-testid, a stable contract\n marker rather than a hashed class name. */\n [data-slot=\"conversation.input.dock\"] > [data-testid=\"todo-panel\"] {\n flex: 1 1 auto !important;\n min-width: 0 !important;\n max-width: none !important;\n min-height: 0 !important;\n max-height: none !important;\n overflow: visible !important;\n border-radius: 12px !important;\n /* The cage shrinks dock text to 11.5px/18px; the card's rows inherit it\n and turn cramped. Restore the official 13px body scale. */\n font-size: 13px !important;\n line-height: normal !important;\n }\n\n /* --- 7b. attachment chips (S7.1) ---\n Our own dock entry opts OUT of the 26px pill cage above: an image chip is\n a 48px tile. The row sits tight under the card's top edge, so the chips\n read as part of the composer rather than as a floating strip. */\n [data-slot=\"conversation.input.dock\"] > [data-mobile-nav=\"attach-chips\"] {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n max-width: none !important;\n min-height: 0 !important;\n max-height: none !important;\n border-radius: 0 !important;\n overflow: visible !important;\n margin-bottom: -2px;\n }\n [data-mobile-nav=\"attach-chip\"] {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex: 0 0 auto;\n max-width: 62vw;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12));\n color: var(--dsw-alias-label-primary, inherit);\n }\n [data-mobile-nav=\"attach-chip\"][data-kind=\"file\"] {\n height: 26px;\n padding: 0 4px 0 8px;\n border-radius: 999px;\n font-size: 11.5px;\n line-height: 18px;\n }\n /* An image chip is the thumbnail: no name line, the filename lives in the\n title attribute (and in the draft text right below). */\n [data-mobile-nav=\"attach-chip\"][data-kind=\"image\"] {\n width: 48px;\n height: 48px;\n padding: 0;\n border-radius: 10px;\n overflow: hidden;\n }\n [data-mobile-nav=\"attach-chip-art\"] {\n position: relative;\n display: grid;\n place-items: center;\n flex: none;\n overflow: hidden;\n }\n [data-kind=\"file\"] > [data-mobile-nav=\"attach-chip-art\"] {\n width: 16px;\n height: 16px;\n opacity: .7;\n }\n [data-kind=\"image\"] > [data-mobile-nav=\"attach-chip-art\"] {\n width: 100%;\n height: 100%;\n }\n /* The <img> is stacked over the paperclip, not swapped for it: a format the\n engine cannot decode (HEIC outside WebKit) simply paints nothing and the\n icon shows through \u2014 no onError state to carry. */\n [data-mobile-nav=\"attach-chip-art\"] img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: var(--dsw-alias-bg-layer-2, rgba(127, 127, 127, .18));\n }\n [data-mobile-nav=\"attach-chip-name\"] {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n }\n [data-mobile-nav=\"attach-chip-remove\"] {\n display: grid;\n place-items: center;\n flex: none;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n touch-action: manipulation;\n }\n /* On a tile the \u00D7 floats in the corner over the picture, so it needs its own\n ground to stay legible against an arbitrary photo. */\n [data-kind=\"image\"] > [data-mobile-nav=\"attach-chip-remove\"] {\n position: absolute;\n top: 2px;\n right: 2px;\n background: rgba(0, 0, 0, .55);\n color: #fff;\n }\n [data-mobile-nav=\"attach-chip-remove\"]:active {\n transform: scale(.9);\n }\n\n /* --- 8. the official stats strip leaves the composer ---\n Its data moves into the session info card (S4). The row is the composer\n dock's own `_root` entry; the slot itself stays live for later entries. */\n [data-slot=\"conversation.composer.dock\"] > [class$=\"_root\"] {\n display: none !important;\n }\n\n /* --- 9. the attachment button (S7; its file input is hidden at the top of\n this file, at every width) --- */\n [data-mobile-nav=\"attach\"] {\n position: relative;\n width: 28px !important;\n height: 28px !important;\n flex: none !important;\n display: grid !important;\n place-items: center !important;\n padding: 0 !important;\n border: none !important;\n border-radius: 999px !important;\n background: var(--dsw-specific-selector, rgba(127, 127, 127, .12));\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n touch-action: manipulation;\n }\n [data-mobile-nav=\"attach\"]:active {\n transform: scale(.94);\n transition: transform .12s;\n }\n /* Busy: a ring sweeps around the paperclip. Drawn with a conic gradient in\n a pseudo-element rather than a spinner node, so the official React tree\n around us has nothing extra to re-render (same reasoning as the model\n pill's mask icon, S3.1). */\n [data-mobile-nav=\"attach\"][data-busy]::after {\n content: \"\";\n position: absolute;\n inset: -2px;\n border-radius: 999px;\n background: conic-gradient(from 0deg, transparent 0 65%, currentColor 100%);\n mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));\n -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));\n animation: mnav-attach-spin .9s linear infinite;\n pointer-events: none;\n }\n @keyframes mnav-attach-spin {\n to { transform: rotate(1turn); }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-mobile-nav=\"attach\"][data-busy]::after {\n animation-duration: 3s;\n }\n }\n /* Failure note: one line above the button, tap-to-dismiss. Sits on the\n composer card (z 2) rather than in an overlay layer \u2014 nothing here needs\n to clear the permission/model sheets. */\n [data-mobile-nav=\"attach-error\"] {\n position: absolute;\n bottom: calc(100% + 6px);\n left: 0;\n z-index: 2;\n max-width: 62vw;\n padding: 5px 8px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, .82));\n color: var(--dsw-alias-label-primary, #fff);\n box-shadow: 0 2px 8px rgba(0, 0, 0, .24);\n }\n\n /* --- 9. home-indicator clearance (S4.1, 2026-08-17) ---\n Owned HERE, not by the gateway half. That plugin used to carry\n [data-slot=\"conversation.composer\"] { padding-bottom: max(env(safe-area-inset-bottom), 8px) }\n in both pwa/app.css and the gateway's inline DEVICE_CSS. Both were\n INERT and had always been: that slot element is `display: contents`\n (measured 2026-08-17 \u2014 the slot wrapper generates no box, so padding on\n it is discarded), which is why raising it never moved anything. The rule\n is deleted on the PWA side and restated here on an element that actually\n lays out, reading --mnav-sab so ?mobile-nav-inset=54,34 can regress it\n off-device (env() is hard 0 on desktop \u2014 the whole reason S2.1 exists).\n\n Target: the card should look EQUALLY inset on all four sides. This same\n element carries the card's side inset (`padding: 0 16px` officially,\n measured 16px left and 16px right at 390px), so the bottom gap is simply\n capped at that same 16px. Clearing the FULL 34px inset \u2014 the naive\n max(sab, 8px) \u2014 is what read as \"too thick\": it is more than double the\n side margin, so the card looked shoved up off the bottom edge.\n\n clamp() says all three requirements at once:\n - floor 8px -> a device with no home indicator (sab: 0) keeps the\n official 8px exactly, so this is a strict no-op there;\n - track sab -> a shallower inset than 16px is honoured as-is;\n - cap 16px -> a full-size indicator (sab: 34) lands on 16px, equal\n to the side inset, which is the look being asked for.\n Keep the 16px in step with the side padding above if that ever changes;\n that equality IS the spec here, not a coincidence. */\n [data-slot=\"conversation.composer.bar\"] > [class$=\"_root\"] {\n padding-bottom: clamp(8px, var(--mnav-sab), 16px) !important;\n }\n}\n\n/* The attachment button and its preview row only exist for the phone shell.\n Both render at every width (the slots are not breakpoint-aware), so both\n need hiding here \u2014 the dock-row rules that shape the chips live in the\n < 768px block and would otherwise leave a bare, unstyled chip line in the\n desktop composer. */\n@media (min-width: 768px) {\n [data-mobile-nav=\"attach\"],\n [data-mobile-nav=\"attach-chips\"] {\n display: none !important;\n }\n}\n";
1
+ export declare const COMPOSER_CSS = "/* ---------- phone composer (< 768px) ---------- */\n\n/* The file picker the attachment button drives (S7) \u2014 hidden at EVERY width,\n deliberately OUTSIDE the phone media block below. A bare <input type=\"file\">\n renders as a native \"Choose Files\" control, and the slot wrapper around it is\n `display: contents`, so an un-hidden one becomes a flex item of the official\n tool row. Scoping this to < 768px once put that control in the DESKTOP\n composer (caught on a live deploy, 2026-08-17): the button that drives it is\n phone-only, but the input it drives is in the DOM at all widths. */\n[data-mobile-nav=\"attach-picker\"] {\n display: none !important;\n}\n\n@media (max-width: 767px) {\n /* --- 1. flatten the two official groups ---\n `_tools` and `_trailing` only exist to cluster controls left/right.\n display:contents dissolves them into `_row`, so the six leaf controls\n become siblings in one flex line and `order` alone drives the layout:\n [attach \u00B7 + \u00B7 permission \u00B7 model] \u2026elastic gap\u2026 [context ring \u00B7 send]. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] {\n justify-content: flex-start !important;\n gap: 5px !important;\n padding: 2px 8px 8px !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] {\n display: contents !important;\n }\n\n /* --- 2. the running order ---\n The attachment button (S3 placeholder, S7 wires it up) sits leftmost;\n the official \"+\" command menu keeps its seat right next to it, where\n the two \"insert something\" affordances read as one cluster. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] [data-mobile-nav=\"attach\"] {\n order: 1 !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_add\"] {\n order: 2 !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] {\n order: 3 !important;\n flex: 0 1 auto !important;\n min-width: 0 !important;\n gap: 6px !important;\n }\n /* The model seat carries the elastic gap: everything after it is pushed to\n the right edge, so no spacer element is needed. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] {\n order: 4 !important;\n flex: 0 1 auto !important;\n min-width: 0 !important;\n margin-right: auto !important;\n }\n /* Third-party input.right entries park next to the ring rather than\n landing at order 0 (= far left) once the groups are flattened. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.right\"] > * {\n order: 5 !important;\n }\n /* ContextMeter \u2014 the only `span` ending in _root inside the row. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > span[class$=\"_root\"] {\n order: 6 !important;\n flex: none !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [class$=\"_primary\"] {\n order: 7 !important;\n }\n\n /* --- 3. permission + model as icon-only pills (real-device round 2, 2026-08-17) ---\n S3's icon-and-label capsules read as noise on an actual phone \u2014 there\n is no room to usefully show a preset name or a model id, so the label\n text is now hidden outright and both triggers collapse to a plain\n ~44x30 icon button. This AGREES with (rather than fights) the official\n container query (`@container (width <= 460px) { .trigger:has(.triggerIcon)\n .triggerLabel { display: none } }`) that S3 had to override \u2014 no need\n to override it back. Accessible name is unaffected: both official\n triggers already ship a descriptive aria-label independent of the\n visible text (PermissionSelect: t('input.accessMode', {name}); the\n model trigger: t('trigger.aria'/'trigger.ariaEffort')) \u2014 verified live\n in dsh-client-ui-conversation / dsh-client-ui-model-selection lib/\n client.js, 2026-08-17 \u2014 so hiding the label costs nothing for screen\n readers. The permission trigger only renders an icon for the\n \"read-only\" / \"workspace-write\" presets (permissionGlyphs in\n dsh-client-ui-conversation) \u2014 \"Full access\" and any host-configured\n preset name fall back to chevron-only, a known gap in the official\n markup this plugin cannot fill without inventing new icon meaning.\n The model trigger never renders an icon at all (only label + optional\n effort text + chevron), so its ::before below draws one from a\n primitives icon path (Sparkle \u2014 the closest existing \"model\" glyph) as\n a CSS-only pseudo-element: it survives React re-renders for free\n (unlike a DOM-injected node, which would need a MutationObserver, see\n the preview-full-toggle pitfall in AGENTS.md) and does not touch\n accessible-name computation (empty generated content). S3's\n rtl-ellipsis trick on the model label is simply inert under\n display:none now; left alone rather than unpicked. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [class$=\"_triggerLabel\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] > [class$=\"_triggerLabel\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] > [class$=\"_triggerEffort\"] {\n display: none !important;\n }\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] button[class$=\"_trigger\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"] {\n background: var(--dsw-specific-selector, rgba(127, 127, 127, .12)) !important;\n width: 44px !important;\n height: 30px !important;\n max-width: none !important;\n min-width: 0 !important;\n padding: 0 !important;\n gap: 2px !important;\n justify-content: center !important;\n border-radius: 999px !important;\n touch-action: manipulation !important;\n }\n /* PermissionSelect wraps its trigger in the Menu primitive's root span,\n which must shrink to the icon button's fixed width. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] > span:has(> button[class$=\"_trigger\"]) {\n flex: 0 0 auto !important;\n min-width: 0 !important;\n }\n /* ic_ds_sparkle_16 (@deepseek-ai/dsh-client-ui-primitives IconSparkle16\n path, copied verbatim) as a mask so it inherits currentColor like every\n other icon in the row \u2014 the model trigger has no official icon slot to\n hook into. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_trigger\"]::before {\n content: '';\n width: 16px;\n height: 16px;\n flex: none;\n background: currentColor;\n -webkit-mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E\");\n mask-image: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6.1 3.1Q6.6 7.8 11.3 8.3Q6.6 8.8 6.1 13.5Q5.6 8.8 0.9 8.3Q5.6 7.8 6.1 3.1Z'/%3E%3Cpath d='M11.9 1Q12.2 3.7 14.9 4Q12.2 4.3 11.9 7Q11.6 4.3 8.9 4Q11.6 3.7 11.9 1Z'/%3E%3C/svg%3E\");\n -webkit-mask-size: contain;\n mask-size: contain;\n -webkit-mask-repeat: no-repeat;\n mask-repeat: no-repeat;\n }\n\n /* --- 4. both menus become bottom sheets ---\n The permission menu is the Menu primitive (role=menu, absolute, side=top)\n and the model menu is ModelSelect's own `_menu` (absolute, bottom+right).\n Neither has a transformed ancestor between it and the viewport, so\n position:fixed re-anchors both to the screen edge. Only the shell moves \u2014\n the items, the two-level model panes and every selection handler stay\n official. */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] {\n position: fixed !important;\n left: 0 !important;\n right: 0 !important;\n bottom: 0 !important;\n top: auto !important;\n transform: none !important;\n width: 100% !important;\n min-width: 0 !important;\n max-width: none !important;\n max-height: min(70dvh, 520px) !important;\n box-sizing: border-box !important;\n border-radius: 16px 16px 0 0 !important;\n border-bottom: none !important;\n padding: 8px 8px calc(8px + var(--mnav-sab)) !important;\n z-index: 60 !important;\n box-shadow: 0 -8px 32px rgba(0, 0, 0, .18) !important;\n }\n /* 44pt+ rows in both sheets (Menu items, model options, and the model\n sheet's two root cells that drill into the model / effort panes). */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"] [role=\"menuitem\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] [class$=\"_option\"],\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"] [class$=\"_cell\"] {\n min-height: 48px !important;\n border-radius: 12px !important;\n font-size: 15px !important;\n }\n /* --- 4b. the official scroll-to-bottom button must not poke through the\n sheet (real-device follow-up, 2026-08-17) ---\n ChatView's own \"jump to latest\" button (aria-label t(\"chat.toBottom\"))\n is `position: sticky; z-index: 8` inside the message column, not\n `position: fixed` \u2014 it never escapes to the same top-level stacking\n context our sheets get promoted to, so raising the sheet's z-index\n further does nothing (measured live: it still rendered on top at\n z-index 60 vs 8). Rather than chase engine-specific stacking-context\n semantics (Chromium and WebKit do not always agree here \u2014 see AGENTS.md\n CDP/document.hidden lesson for another instance of that), this hides\n the button outright while either sheet is open and lets it reappear on\n close: a plain `display: none` behind a live `:has()` read is correct\n regardless of which stacking rules the engine happens to apply.\n Selector: no hashed classes (dsh-client-ui-conversation lib/client.js,\n verified 2026-08-17) \u2014 `data-chat-flow` is the one stable attribute on\n the message column, and the button's wrapper is its only sibling\n (ChatView only ever renders the column and, conditionally, this one\n slot), so the adjacent-sibling combinator pins it precisely. */\n body:has([data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_tools\"] > [class$=\"_modes\"] [role=\"menu\"]) [data-chat-flow] + div,\n body:has([data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] > [class$=\"_row\"] > [class$=\"_trailing\"] > [data-slot=\"conversation.input.model\"] > [class$=\"_root\"] > [class$=\"_menu\"]) [data-chat-flow] + div {\n display: none !important;\n }\n /* --- 5. input box: two lines minimum, five lines maximum ---\n The official autosizer drives the box off the hidden mirror's height, so\n a min-height on the mirror IS the min-height of the field; the scroll cap\n rides the official custom property. 52px = 2 x 24px line box + 4px pad,\n 124px = 5 lines. The hero card keeps its own one-line collapse (misc.css\n pins _scroll/_grow while the placeholder shows). */\n [data-slot=\"conversation.composer.bar\"] [class$=\"_card\"] [class$=\"_mirror\"] {\n min-height: 52px !important;\n }\n [class$=\"_composerSeat\"] {\n --dsh-composer-text-max-height: 124px;\n }\n\n /* --- 6. no divider above OR below the message list ---\n Instead of a rule the messages butt against, the message scroller\n fades out over its last 26px (S3, no divider above the composer) AND\n fades in over its first 20px (S3.1 real-device round 2: the header's\n own bottom line is removed too, styles/header.css.ts's\n `header::after` rule). The mask lives on the scroll body (NOT on the\n header or the composer): a mask clips everything it paints, and both\n surfaces host position:fixed children (composer's permission/model\n sheets, any future header overlay) that must not be clipped.\n `mask-image` can only be declared once per element, so both fades are\n ONE linear-gradient rather than two separate declarations (the second\n would silently replace the first) \u2014 this is the merge of what used to\n be S3's bottom-only mask. */\n [class$=\"_scrollBody\"] {\n -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 26px), transparent 100%);\n mask-image: linear-gradient(to bottom, transparent 0, #000 20px, #000 calc(100% - 26px), transparent 100%);\n }\n [class$=\"_composerSeat\"],\n [class$=\"_composerStack\"] {\n border-top: none !important;\n }\n\n /* --- 7. dock entries become mini chips above the input card ---\n conversation.input.dock is display:contents (inline style), so its\n entries are stacked rows of the composer column. Forcing the slot itself\n to flex turns them into ONE scrollable chip line outside and above the\n card. Written against the slot, not against any particular plugin's chip\n (the git branch chip and the todo panel are third-party and may not be\n installed at all). */\n [data-slot=\"conversation.input.dock\"] {\n display: flex !important;\n flex-flow: row nowrap !important;\n align-items: center !important;\n gap: 6px !important;\n min-width: 0 !important;\n margin: 0 16px !important;\n overflow-x: auto !important;\n overflow-y: hidden !important;\n scrollbar-width: none !important;\n }\n [data-slot=\"conversation.input.dock\"]::-webkit-scrollbar {\n display: none !important;\n }\n [data-slot=\"conversation.input.dock\"] > * {\n flex: 0 0 auto !important;\n max-width: 70% !important;\n min-height: 26px !important;\n max-height: 26px !important;\n border-radius: 999px !important;\n font-size: 11.5px !important;\n line-height: 18px !important;\n overflow: hidden !important;\n }\n /* The git-graph chip carries a 34px tablet target from misc.css; that\n selector is more specific, so restate it at the phone breakpoint. */\n [data-slot=\"conversation.input.dock\"] [data-gitgraph-chip-anchor] [data-gitgraph-chip] {\n min-height: 26px !important;\n padding: 0 10px !important;\n font-size: 11.5px !important;\n }\n /* --- 7a. the native to-do card opts OUT of the 26px pill cage ---\n conversation.input.dock is not only a chip rail: DSH's own TodoPanel\n (dsh-client-ui-conversation) registers here, and it is an expandable\n card, not a pill \u2014 tapping its header renders the to-do list INSIDE\n itself. The cage's max-height:26px + overflow:hidden left that list\n rendered but clipped to nothing, so on a phone the bar looked dead:\n aria-expanded flipped, 8 rows mounted at 216px, and the card still\n painted 26px tall (measured live, 2026-08-20). Give it the official\n geometry back \u2014 full row, natural height, 12px radius. Its list caps\n itself at 180px with its own scroller, so this cannot run into the\n composer. Anchored on the official data-testid, a stable contract\n marker rather than a hashed class name. */\n [data-slot=\"conversation.input.dock\"] > [data-testid=\"todo-panel\"] {\n flex: 1 1 auto !important;\n min-width: 0 !important;\n max-width: none !important;\n min-height: 0 !important;\n max-height: none !important;\n overflow: visible !important;\n border-radius: 12px !important;\n /* The cage shrinks dock text to 11.5px/18px; the card's rows inherit it\n and turn cramped. Restore the official 13px body scale. */\n font-size: 13px !important;\n line-height: normal !important;\n }\n\n /* --- 7b. attachment chips (S7.1) ---\n Our own dock entry opts OUT of the 26px pill cage above: an image chip is\n a 48px tile. The row sits tight under the card's top edge, so the chips\n read as part of the composer rather than as a floating strip. */\n [data-slot=\"conversation.input.dock\"] > [data-mobile-nav=\"attach-chips\"] {\n display: flex !important;\n align-items: center !important;\n gap: 6px !important;\n max-width: none !important;\n min-height: 0 !important;\n max-height: none !important;\n border-radius: 0 !important;\n overflow: visible !important;\n margin-bottom: -2px;\n }\n [data-mobile-nav=\"attach-chip\"] {\n position: relative;\n display: inline-flex;\n align-items: center;\n gap: 6px;\n flex: 0 0 auto;\n max-width: 62vw;\n background: var(--dsw-alias-interactive-bg-hover, rgba(127, 127, 127, .12));\n color: var(--dsw-alias-label-primary, inherit);\n }\n [data-mobile-nav=\"attach-chip\"][data-kind=\"file\"] {\n height: 26px;\n padding: 0 4px 0 8px;\n border-radius: 999px;\n font-size: 11.5px;\n line-height: 18px;\n }\n /* An image chip is the thumbnail: no name line, the filename lives in the\n title attribute (and in the draft text right below). */\n [data-mobile-nav=\"attach-chip\"][data-kind=\"image\"] {\n width: 48px;\n height: 48px;\n padding: 0;\n border-radius: 10px;\n overflow: hidden;\n }\n [data-mobile-nav=\"attach-chip-art\"] {\n position: relative;\n display: grid;\n place-items: center;\n flex: none;\n overflow: hidden;\n }\n [data-kind=\"file\"] > [data-mobile-nav=\"attach-chip-art\"] {\n width: 16px;\n height: 16px;\n opacity: .7;\n }\n [data-kind=\"image\"] > [data-mobile-nav=\"attach-chip-art\"] {\n width: 100%;\n height: 100%;\n }\n /* The <img> is stacked over the paperclip, not swapped for it: a format the\n engine cannot decode (HEIC outside WebKit) simply paints nothing and the\n icon shows through \u2014 no onError state to carry. */\n [data-mobile-nav=\"attach-chip-art\"] img {\n position: absolute;\n inset: 0;\n width: 100%;\n height: 100%;\n object-fit: cover;\n background: var(--dsw-alias-bg-layer-2, rgba(127, 127, 127, .18));\n }\n [data-mobile-nav=\"attach-chip-name\"] {\n min-width: 0;\n overflow: hidden;\n white-space: nowrap;\n }\n [data-mobile-nav=\"attach-chip-remove\"] {\n display: grid;\n place-items: center;\n flex: none;\n width: 18px;\n height: 18px;\n padding: 0;\n border: none;\n border-radius: 999px;\n background: transparent;\n color: inherit;\n cursor: pointer;\n touch-action: manipulation;\n }\n /* On a tile the \u00D7 floats in the corner over the picture, so it needs its own\n ground to stay legible against an arbitrary photo. */\n [data-kind=\"image\"] > [data-mobile-nav=\"attach-chip-remove\"] {\n position: absolute;\n top: 2px;\n right: 2px;\n background: rgba(0, 0, 0, .55);\n color: #fff;\n }\n [data-mobile-nav=\"attach-chip-remove\"]:active {\n transform: scale(.9);\n }\n\n /* --- 8. the official stats strip leaves the composer ---\n Its data moves into the session info card (S4). The row is the composer\n dock's own `_root` entry; the slot itself stays live for later entries. */\n [data-slot=\"conversation.composer.dock\"] > [class$=\"_root\"] {\n display: none !important;\n }\n\n /* --- 9. the attachment button (S7; its file input is hidden at the top of\n this file, at every width) --- */\n [data-mobile-nav=\"attach\"] {\n position: relative;\n width: 28px !important;\n height: 28px !important;\n flex: none !important;\n display: grid !important;\n place-items: center !important;\n padding: 0 !important;\n border: none !important;\n border-radius: 999px !important;\n background: var(--dsw-specific-selector, rgba(127, 127, 127, .12));\n color: var(--dsw-alias-label-primary, inherit);\n cursor: pointer;\n touch-action: manipulation;\n }\n [data-mobile-nav=\"attach\"]:active {\n transform: scale(.94);\n transition: transform .12s;\n }\n /* Busy: a ring sweeps around the paperclip. Drawn with a conic gradient in\n a pseudo-element rather than a spinner node, so the official React tree\n around us has nothing extra to re-render (same reasoning as the model\n pill's mask icon, S3.1). */\n [data-mobile-nav=\"attach\"][data-busy]::after {\n content: \"\";\n position: absolute;\n inset: -2px;\n border-radius: 999px;\n background: conic-gradient(from 0deg, transparent 0 65%, currentColor 100%);\n mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));\n -webkit-mask: radial-gradient(closest-side, transparent calc(100% - 2px), #000 calc(100% - 2px));\n animation: mnav-attach-spin .9s linear infinite;\n pointer-events: none;\n }\n @keyframes mnav-attach-spin {\n to { transform: rotate(1turn); }\n }\n @media (prefers-reduced-motion: reduce) {\n [data-mobile-nav=\"attach\"][data-busy]::after {\n animation-duration: 3s;\n }\n }\n /* Failure note: one line above the button, tap-to-dismiss. Sits on the\n composer card (z 2) rather than in an overlay layer \u2014 nothing here needs\n to clear the permission/model sheets. */\n [data-mobile-nav=\"attach-error\"] {\n position: absolute;\n bottom: calc(100% + 6px);\n left: 0;\n z-index: 2;\n max-width: 62vw;\n padding: 5px 8px;\n border-radius: 8px;\n font-size: 12px;\n line-height: 1.3;\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n background: var(--dsw-alias-bg-layer-2, rgba(0, 0, 0, .82));\n color: var(--dsw-alias-label-primary, #fff);\n box-shadow: 0 2px 8px rgba(0, 0, 0, .24);\n }\n\n /* --- 9. home-indicator clearance (S4.1, 2026-08-17) ---\n Owned HERE, not by the gateway half. That plugin used to carry\n [data-slot=\"conversation.composer\"] { padding-bottom: max(env(safe-area-inset-bottom), 8px) }\n in both pwa/app.css and the gateway's inline DEVICE_CSS. Both were\n INERT and had always been: that slot element is `display: contents`\n (measured 2026-08-17 \u2014 the slot wrapper generates no box, so padding on\n it is discarded), which is why raising it never moved anything. The rule\n is deleted on the PWA side and restated here on an element that actually\n lays out, reading --mnav-sab so ?mobile-nav-inset=54,34 can regress it\n off-device (env() is hard 0 on desktop \u2014 the whole reason S2.1 exists).\n\n Target: the card should look EQUALLY inset on all four sides. This same\n element carries the card's side inset (`padding: 0 16px` officially,\n measured 16px left and 16px right at 390px), so the bottom gap is simply\n capped at that same 16px. Clearing the FULL 34px inset \u2014 the naive\n max(sab, 8px) \u2014 is what read as \"too thick\": it is more than double the\n side margin, so the card looked shoved up off the bottom edge.\n\n clamp() says all three requirements at once:\n - floor 8px -> a device with no home indicator (sab: 0) keeps the\n official 8px exactly, so this is a strict no-op there;\n - track sab -> a shallower inset than 16px is honoured as-is;\n - cap 16px -> a full-size indicator (sab: 34) lands on 16px, equal\n to the side inset, which is the look being asked for.\n Keep the 16px in step with the side padding above if that ever changes;\n that equality IS the spec here, not a coincidence. */\n [data-slot=\"conversation.composer.bar\"] > [class$=\"_root\"] {\n padding-bottom: clamp(8px, var(--mnav-sab), 16px) !important;\n }\n\n /* S10 keyboard avoid: effects/keyboard-avoid.ts mirrors the band of the\n layout viewport hidden behind the software keyboard into --mnav-kb-lift\n and stamps the html attribute while (and only while) it is non-zero \u2014\n a resting transform, even translateY(0), would silently become the\n containing block for any fixed-position descendant of the bar. In every\n environment where the browser's own focus-reveal works the attribute is\n simply never set.\n The target is the slot's _root CHILD, not the slot wrapper: the wrapper\n is display:contents \u2014 it generates no box, so a transform on it is a\n silent no-op (found live on-device via CDP, 2026-08-21; the variable and\n attribute were set and nothing moved). */\n html[data-mnav-kb] [data-slot=\"conversation.composer.bar\"] > [class$=\"_root\"] {\n transform: translateY(calc(-1 * var(--mnav-kb-lift, 0px)));\n }\n}\n\n/* The attachment button and its preview row only exist for the phone shell.\n Both render at every width (the slots are not breakpoint-aware), so both\n need hiding here \u2014 the dock-row rules that shape the chips live in the\n < 768px block and would otherwise leave a bare, unstyled chip line in the\n desktop composer. */\n@media (min-width: 768px) {\n [data-mobile-nav=\"attach\"],\n [data-mobile-nav=\"attach-chips\"] {\n display: none !important;\n }\n}\n";
2
2
  //# sourceMappingURL=composer.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"composer.css.d.ts","sourceRoot":"","sources":["../../../../src/client/styles/composer.css.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,YAAY,m3yBA0fxB,CAAA"}
1
+ {"version":3,"file":"composer.css.d.ts","sourceRoot":"","sources":["../../../../src/client/styles/composer.css.ts"],"names":[],"mappings":"AAsBA,eAAO,MAAM,YAAY,ux0BAygBxB,CAAA"}
@@ -1,2 +1,2 @@
1
- export declare const TURN_FOLD_CSS = "/* ---------- turn process fold (< 768px) ---------- */\n\n/* The summary row never paints outside the phone breakpoint, even if a\n media-query change raced the effect's own cleanup. */\n[data-mobile-nav=\"turn-fold\"] {\n display: none;\n}\n\n@media (max-width: 767px) {\n /* Folded process: tool-call / context / command rows and the Think\n disclosures inside an assistant step. !important because the official\n flow item and ReasoningRow both set their own display. */\n [data-mnav-fold]:not([data-mnav-fold-open]) {\n display: none !important;\n }\n\n /* Compact chip, sized like the composer's own pills rather than a button:\n it is a reading affordance in the middle of the message flow, so it has\n to stay quieter than the content around it. The flow column is a flex\n column with gap: 16px (ChatView.module.css) \u2014 align-self keeps the chip\n from stretching, and the negative block margin trims that generous gap\n back to something a 26px row can live in. */\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"] {\n display: inline-flex;\n align-self: flex-start;\n align-items: center;\n gap: 6px;\n margin: -4px 0;\n padding: 0 10px;\n height: 26px;\n border: none;\n border-radius: 13px;\n background: var(--dsw-alias-interactive-bg-hover, rgba(0, 0, 0, .06));\n color: var(--dsw-alias-label-secondary, rgba(0, 0, 0, .55));\n font: inherit;\n font-size: 12px;\n line-height: 26px;\n white-space: nowrap;\n cursor: pointer;\n -webkit-tap-highlight-color: transparent;\n }\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"]:active {\n background: var(--dsw-alias-interactive-bg-pressed, rgba(0, 0, 0, .1));\n }\n\n /* Leading dot: idle turns get a quiet mark, a running turn gets the same\n business-primary colour the session header's own status dot uses\n (styles/header.css.ts) plus a breathing pulse. */\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"]::before {\n content: '';\n flex: none;\n width: 6px;\n height: 6px;\n border-radius: 50%;\n background: currentColor;\n opacity: .45;\n }\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"][data-running]::before {\n background: var(--dsw-alias-state-business-primary, #4f6ef7);\n opacity: 1;\n animation: dsh-mobile-nav-breathe 1.4s var(--ds-ease-in-out, ease-in-out) infinite;\n }\n\n /* Chevron drawn from two borders \u2014 no icon injection, no mask image, and\n it rotates to point up once the turn is open. */\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"]::after {\n content: '';\n flex: none;\n width: 5px;\n height: 5px;\n margin-top: -3px;\n border-right: 1.5px solid currentColor;\n border-bottom: 1.5px solid currentColor;\n transform: rotate(45deg);\n transition: transform .16s var(--ds-ease-out, ease-in-out);\n }\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"][data-open]::after {\n margin-top: 3px;\n transform: rotate(-135deg);\n }\n\n @media (prefers-reduced-motion: reduce) {\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"][data-running]::before {\n animation: none;\n }\n [data-chat-flow] > [data-mobile-nav=\"turn-fold\"]::after {\n transition: none;\n }\n }\n}\n\n@keyframes dsh-mobile-nav-breathe {\n 0%, 100% { opacity: 1; transform: scale(1); }\n 50% { opacity: .4; transform: scale(.72); }\n}\n";
1
+ export declare const TURN_FOLD_CSS: string;
2
2
  //# sourceMappingURL=turn-fold.css.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"turn-fold.css.d.ts","sourceRoot":"","sources":["../../../../src/client/styles/turn-fold.css.ts"],"names":[],"mappings":"AAUA,eAAO,MAAM,aAAa,02GA+FzB,CAAA"}
1
+ {"version":3,"file":"turn-fold.css.d.ts","sourceRoot":"","sources":["../../../../src/client/styles/turn-fold.css.ts"],"names":[],"mappings":"AAiGA,eAAO,MAAM,aAAa,QAmBzB,CAAA"}
@@ -18,14 +18,22 @@ import type { IncomingMessage, ServerResponse } from 'node:http';
18
18
  import type { Context } from '@deepseek-ai/cordis';
19
19
  /** Exact route the phone composer POSTs one file body to. */
20
20
  export declare const UPLOAD_ROUTE = "/_dsh/mobile-nav/upload";
21
+ /** Exact route the browser GETs the plugin row's client-facing knobs from.
22
+ * The client bundle ships statically and never sees the row config, so the
23
+ * host republishes the client-relevant subset here (issue #2). */
24
+ export declare const CLIENT_CONFIG_ROUTE = "/_dsh/mobile-nav/client-config";
21
25
  /** Workspace-relative directory uploads land in (also the `@` prefix the composer inserts). */
22
26
  export declare const UPLOAD_DIR = ".dsh-uploads";
23
27
  /** Body cap when the plugin row sets no `maxUploadBytes`. */
24
28
  export declare const DEFAULT_MAX_UPLOAD_BYTES: number;
25
- /** Host half config; the only knob is the body cap. */
29
+ /** Host half config. */
26
30
  export interface MobileNavConfig {
27
31
  /** Max upload body in bytes; larger bodies get 413. Default {@link DEFAULT_MAX_UPLOAD_BYTES}. */
28
32
  maxUploadBytes?: number;
33
+ /** Fold each turn's process at every viewport width, not just below the
34
+ * phone breakpoint. Default false (phone-only). A browser can still opt
35
+ * itself in via `?mobile-nav-turn-fold=1` when this is off. */
36
+ turnFoldDesktop?: boolean;
29
37
  }
30
38
  /**
31
39
  * Accept a state-changing request only from this DSH Web application's origin.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,6DAA6D;AAC7D,eAAO,MAAM,YAAY,4BAA4B,CAAA;AAErD,+FAA+F;AAC/F,eAAO,MAAM,UAAU,iBAAiB,CAAA;AAExC,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,QAAmB,CAAA;AAQxD,uDAAuD;AACvD,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAa5D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKnE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBlD;AA6HD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,eAAoB,GAAG,IAAI,CAStE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AAEhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAIlD,6DAA6D;AAC7D,eAAO,MAAM,YAAY,4BAA4B,CAAA;AAErD;;kEAEkE;AAClE,eAAO,MAAM,mBAAmB,mCAAmC,CAAA;AAEnE,+FAA+F;AAC/F,eAAO,MAAM,UAAU,iBAAiB,CAAA;AAExC,6DAA6D;AAC7D,eAAO,MAAM,wBAAwB,QAAmB,CAAA;AAQxD,wBAAwB;AACxB,MAAM,WAAW,eAAe;IAC9B,iGAAiG;IACjG,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;mEAE+D;IAC/D,eAAe,CAAC,EAAE,OAAO,CAAA;CAC1B;AAwCD;;;;;;;;;GASG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,eAAe,GAAG,OAAO,CAa5D;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAKnE;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAgBlD;AA6HD;;;;;;;;;GASG;AACH,wBAAsB,YAAY,CAChC,GAAG,EAAE,OAAO,EACZ,QAAQ,EAAE,MAAM,EAChB,GAAG,EAAE,eAAe,EACpB,GAAG,EAAE,cAAc,GAClB,OAAO,CAAC,IAAI,CAAC,CA+Bf;AAED;;;;;;;GAOG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,eAAoB,GAAG,IAAI,CAyBtE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-zen-remote",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
4
4
  "description": "把 DeepSeek Harness 变成手机 App:移动端界面重排(两级页面栈、会话列表主屏、composer 重排、手势、附件上传)+ 配对码认证网关 + PWA 安装 + 真 Web Push。Complete mobile plugin for DeepSeek Harness (DSH): app-shell mobile UI, pairing-code gateway for public-internet access behind your own reverse proxy, installable PWA, offline service worker, and real Web Push agent-done notifications.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -122,7 +122,7 @@
122
122
  "scripts": {
123
123
  "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json && node scripts/build-client.mjs",
124
124
  "verify": "tsc -p tsconfig.json --noEmit && tsc -p tsconfig.client.json --noEmit",
125
- "test": "node --test \"test/*.test.cjs\" && node scripts/check-sunk-viewport.mjs && node scripts/check-attach-upload.mjs && node scripts/check-upload-endpoint.mjs && node scripts/sync-doc-version.mjs --check",
125
+ "test": "node --test \"test/*.test.cjs\" && node scripts/check-sunk-viewport.mjs && node scripts/check-keyboard-avoid.mjs && node scripts/check-attach-upload.mjs && node scripts/check-upload-endpoint.mjs && node scripts/sync-doc-version.mjs --check",
126
126
  "version": "node scripts/sync-doc-version.mjs && git add README.md"
127
127
  }
128
128
  }
@@ -0,0 +1,91 @@
1
+ // Self-check for the composer keyboard-avoid lift (S10, issue #1 方案 2).
2
+ // The failing environment (小米 + 微信输入法) cannot be reproduced off-device,
3
+ // so the boundary cases of the pure geometry are asserted numerically.
4
+ // Run: node scripts/check-keyboard-avoid.mjs (needs Node >= 23.6 type stripping)
5
+ import assert from 'node:assert/strict'
6
+ import { readFileSync } from 'node:fs'
7
+ import { composerLift, estimatedLift, keyboardLift as lift, safetyPad } from '../src/client/effects/keyboard-avoid.ts'
8
+
9
+ // The reported class of bug: keyboard shrinks the visual viewport, browser
10
+ // does not pan it — the whole shrink is an occluded band at the bottom.
11
+ assert.equal(lift({ innerHeight: 800, vvHeight: 500, offsetTop: 0, scale: 1 }), 300)
12
+
13
+ // Browser panned the visual viewport fully down to the focused field
14
+ // (working Android auto-scroll, and iOS): nothing is occluded.
15
+ assert.equal(lift({ innerHeight: 800, vvHeight: 500, offsetTop: 300, scale: 1 }), 0)
16
+
17
+ // Partial pan leaves a partial band.
18
+ assert.equal(lift({ innerHeight: 800, vvHeight: 500, offsetTop: 120, scale: 1 }), 180)
19
+
20
+ // No keyboard at all.
21
+ assert.equal(lift({ innerHeight: 800, vvHeight: 800, offsetTop: 0, scale: 1 }), 0)
22
+
23
+ // iOS-style: innerHeight itself shrinks with the keyboard, so the
24
+ // subtraction is already balanced — must stay a strict no-op.
25
+ assert.equal(lift({ innerHeight: 500, vvHeight: 500, offsetTop: 0, scale: 1 }), 0)
26
+
27
+ // Pinch-zoom shrinks vvHeight without any keyboard: the scale guard wins.
28
+ assert.equal(lift({ innerHeight: 800, vvHeight: 400, offsetTop: 100, scale: 2 }), 0)
29
+
30
+ // Sub-threshold rounding noise (URL-bar animation) never jitters the bar.
31
+ assert.equal(lift({ innerHeight: 800, vvHeight: 799, offsetTop: 0, scale: 1 }), 0)
32
+
33
+ // Fractional CSS pixels round to an integer lift.
34
+ assert.equal(lift({ innerHeight: 800, vvHeight: 507.5, offsetTop: 0, scale: 1 }), 293)
35
+
36
+ // Over-pan (elastic edge) must not produce a negative lift.
37
+ assert.equal(lift({ innerHeight: 800, vvHeight: 500, offsetTop: 320, scale: 1 }), 0)
38
+
39
+ // Dumb-keyboard estimate (issue #1 确诊设备): the reporter's WeType measured
40
+ // ~315 CSS px on an 858px viewport — the estimate must clear it, and never
41
+ // exceed its 400px cap on tall viewports.
42
+ assert.equal(estimatedLift(858), 360)
43
+ assert.ok(estimatedLift(858) > 315, 'estimate clears the one measured WeType height')
44
+ assert.equal(estimatedLift(2000), 400)
45
+ assert.equal(estimatedLift(600), 252)
46
+
47
+ // Cached verdict (first-focus-only delay): the effect must persist the dumb
48
+ // verdict, lift immediately for a convicted browser, and revoke the cache
49
+ // the moment a probe sees the viewport move.
50
+ const effectSrc = readFileSync(new URL('../src/client/effects/keyboard-avoid.ts', import.meta.url), 'utf8')
51
+ assert.match(effectSrc, /'dsh-mobile-nav\.kb-dumb'/, 'verdict cache key exists')
52
+ assert.match(effectSrc, /localStorage\.setItem\(DUMB_KEY, '1'\)/, 'verdict is persisted on conviction')
53
+ assert.match(effectSrc, /if \(localStorage\.getItem\(DUMB_KEY\) === '1'\) \{\s*estimate = estimatedLift/, 'convicted browser lifts immediately on focus')
54
+ assert.match(effectSrc, /if \(moved\(\)\) \{\s*[\s\S]{0,200}localStorage\.removeItem\(DUMB_KEY\)/, 'a moving viewport revokes the verdict')
55
+
56
+ // The safety pad is ANDROID-ONLY: the under-reporting is an Android IME
57
+ // behaviour, and iOS reports its one system keyboard exactly — padding there
58
+ // would be a visible gap bought for nothing.
59
+ const ANDROID = 'Mozilla/5.0 (Linux; Android 17) AppleWebKit/537.36 Chrome/135 Mobile'
60
+ const IPHONE = 'Mozilla/5.0 (iPhone; CPU iPhone OS 26_0 like Mac OS X) AppleWebKit/605.1.15 Mobile'
61
+ assert.equal(safetyPad(ANDROID), 15)
62
+ assert.equal(safetyPad(IPHONE), 0)
63
+ assert.equal(safetyPad('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)'), 0)
64
+
65
+ // Three-source composition. The pad rides on a measured occlusion and stands
66
+ // alone when a keyboard is up with nothing occluded on paper, but never
67
+ // inflates the (already generous) dumb-keyboard estimate.
68
+ const A = safetyPad(ANDROID)
69
+ assert.equal(composerLift(300, 0, true, A), 315, 'measured occlusion gets the pad')
70
+ assert.equal(composerLift(0, 0, true, A), 15, 'under-reporting IME: pad alone')
71
+ assert.equal(composerLift(0, 360, false, A), 360, 'the estimate is never padded')
72
+ assert.equal(composerLift(0, 0, false, A), 0, 'no keyboard, no lift')
73
+ // A shrunk viewport must not add the pad twice via both paths.
74
+ assert.equal(composerLift(300, 360, true, A), 315, 'geometry outranks the estimate')
75
+
76
+ // Off Android the pad vanishes and every path is exactly the measurement.
77
+ const I = safetyPad(IPHONE)
78
+ assert.equal(composerLift(300, 0, true, I), 300, 'iOS: measured occlusion, unpadded')
79
+ assert.equal(composerLift(0, 0, true, I), 0, 'iOS: a handled keyboard lifts nothing')
80
+
81
+ // The lift rule must target the slot's _root CHILD: the slot wrapper itself
82
+ // is display:contents (no box), a transform there is a silent no-op — the
83
+ // exact on-device failure of 2026-08-21.
84
+ const composerCss = readFileSync(new URL('../src/client/styles/composer.css.ts', import.meta.url), 'utf8')
85
+ assert.match(
86
+ composerCss,
87
+ /html\[data-mnav-kb\] \[data-slot="conversation\.composer\.bar"\] > \[class\$="_root"\]\s*\{\s*transform/,
88
+ 'keyboard-avoid transform must hit the box-generating _root child',
89
+ )
90
+
91
+ console.log('KEYBOARD AVOID CHECK OK')