dsh-zen-remote 1.0.2 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/AGENTS.md +9 -1
  2. package/README.md +56 -16
  3. package/docs/interface.md +69 -6
  4. package/docs/remote-access.en.md +7 -5
  5. package/docs/remote-access.md +8 -6
  6. package/dsh-push.mjs +272 -64
  7. package/lib/client.js +550 -28
  8. package/lib/lan-gate-server.cjs +7 -1
  9. package/lib/types/client/MobileHome.d.ts.map +1 -1
  10. package/lib/types/client/MobileSessionHeader.d.ts +1 -1
  11. package/lib/types/client/MobileSessionHeader.d.ts.map +1 -1
  12. package/lib/types/client/MobileSessionInfo.d.ts.map +1 -1
  13. package/lib/types/client/effects/modal-back.d.ts +22 -0
  14. package/lib/types/client/effects/modal-back.d.ts.map +1 -0
  15. package/lib/types/client/history-nav.d.ts +49 -0
  16. package/lib/types/client/history-nav.d.ts.map +1 -0
  17. package/lib/types/client/index.d.ts.map +1 -1
  18. package/lib/types/client/locales.d.ts +4 -2
  19. package/lib/types/client/locales.d.ts.map +1 -1
  20. package/lib/types/client/styles/base.css.d.ts +1 -1
  21. package/lib/types/client/styles/base.css.d.ts.map +1 -1
  22. package/lib/types/client/styles/chips.css.d.ts +1 -1
  23. package/lib/types/client/styles/chips.css.d.ts.map +1 -1
  24. package/lib/types/client/styles/compat.css.d.ts +1 -1
  25. package/lib/types/client/styles/compat.css.d.ts.map +1 -1
  26. package/lib/types/client/styles/composer.css.d.ts +1 -1
  27. package/lib/types/client/styles/composer.css.d.ts.map +1 -1
  28. package/lib/types/client/styles/content.css.d.ts +1 -1
  29. package/lib/types/client/styles/content.css.d.ts.map +1 -1
  30. package/lib/types/client/styles/header.css.d.ts +1 -1
  31. package/lib/types/client/styles/header.css.d.ts.map +1 -1
  32. package/lib/types/client/styles/home.css.d.ts +1 -1
  33. package/lib/types/client/styles/home.css.d.ts.map +1 -1
  34. package/llms.txt +1 -1
  35. package/package.json +23 -21
  36. package/pwa/inject.js +1 -0
  37. package/scripts/sync-doc-version.mjs +83 -0
  38. package/src/client/MobileHome.tsx +17 -1
  39. package/src/client/MobileSessionHeader.tsx +134 -1
  40. package/src/client/MobileSessionInfo.tsx +38 -8
  41. package/src/client/effects/modal-back.ts +98 -0
  42. package/src/client/history-nav.ts +121 -0
  43. package/src/client/index.tsx +2 -0
  44. package/src/client/locales.ts +8 -4
  45. package/src/client/styles/base.css.ts +6 -0
  46. package/src/client/styles/chips.css.ts +4 -1
  47. package/src/client/styles/compat.css.ts +8 -1
  48. package/src/client/styles/composer.css.ts +25 -0
  49. package/src/client/styles/content.css.ts +24 -0
  50. package/src/client/styles/header.css.ts +123 -0
  51. package/src/client/styles/home.css.ts +19 -10
@@ -24,6 +24,7 @@ export const HEADER_CSS = `/* ---------- session header five-piece reflow (< 768
24
24
  list in misc.css.ts. */
25
25
  [data-mobile-nav="header-back"],
26
26
  [data-mobile-nav="header-viewrow"],
27
+ [data-mobile-nav="header-activity"],
27
28
  [data-mobile-nav="header-info"],
28
29
  [data-mobile-nav="header-workbench"],
29
30
  /* The workbench close pill's default-hidden used to live in compat.css.ts —
@@ -266,6 +267,48 @@ export const HEADER_CSS = `/* ---------- session header five-piece reflow (< 768
266
267
  [data-phase] header [data-slot="conversation.session.header.utilities"] > * {
267
268
  display: none !important;
268
269
  }
270
+ /* Native background-task / subagent entries: kept mounted, but parked as
271
+ ZERO-WIDTH INVISIBLE ANCHORS at the right end of the view-switch band.
272
+ Our activity pill is the visible control and forwards the tap to the
273
+ official trigger, so the official popover — a child of this root — still
274
+ mounts and positions from here.
275
+
276
+ Why not just leave them visible in the header: each is ~103px wide while
277
+ headerActions' grid column is 92px, and flex children that cannot shrink
278
+ simply overflow — measured at 375px the subagent entry ran x=60..163
279
+ straight across the centred title at x=100..275.
280
+
281
+ Why not display:none them either: the menu lives inside the root, so
282
+ hiding the root takes the popover with it and the pill would have
283
+ nothing to open. Hide the TRIGGER (below) and keep the root as a box. */
284
+ [data-phase] header [data-slot="conversation.session.header.actions"] > [class$="_root"]:has(> button[class$="_trigger"]) {
285
+ display: block !important;
286
+ position: absolute !important;
287
+ right: 0 !important;
288
+ top: 100% !important;
289
+ width: 0 !important;
290
+ height: 28px !important;
291
+ overflow: visible !important;
292
+ order: 0 !important;
293
+ }
294
+ /* The official trigger never shows — the pill replaced it. Programmatic
295
+ .click() still reaches React's handler on a display:none button. */
296
+ [data-phase] header [data-slot="conversation.session.header.actions"] > [class$="_root"] > button[class$="_trigger"] {
297
+ display: none !important;
298
+ }
299
+ /* Anchor the popover to the RIGHT edge and clamp it to the viewport. The
300
+ root now sits at the right edge with zero width, so the official
301
+ \`left: 0\` would start the 336px panel off-screen; layout.css.ts's
302
+ \`left: 8px\` re-anchor was written for a viewport-width containing block
303
+ that this element is not. Right-anchoring is the one that holds for both
304
+ (2026-08-20: the old pairing put the panel 29px past the right edge). */
305
+ [data-phase] header [data-slot="conversation.session.header.actions"] [class$="_menu"] {
306
+ left: auto !important;
307
+ right: 0 !important;
308
+ width: min(336px, calc(100vw - 32px)) !important;
309
+ max-width: none !important;
310
+ max-height: min(420px, calc(100dvh - 140px)) !important;
311
+ }
269
312
  /* The agent-preset mode badge specifically survives the blanket hide
270
313
  above: layout.css.ts's shared <=1023px block targets it directly
271
314
  (header [class$="_label"]:has(> svg) { display: block !important }),
@@ -283,6 +326,12 @@ export const HEADER_CSS = `/* ---------- session header five-piece reflow (< 768
283
326
  [data-phase] header [data-slot="conversation.session.header.actions"] > [data-mobile-nav="header-viewrow"] {
284
327
  display: flex !important;
285
328
  }
329
+ /* Our own chip lives in the same slot, so the blanket hide above swallows
330
+ it unless it is named here too (it was, on the first build: the chip
331
+ rendered with the right counts and painted nothing). */
332
+ [data-phase] header [data-slot="conversation.session.header.actions"] > [data-mobile-nav="header-activity"] {
333
+ display: inline-flex !important;
334
+ }
286
335
  [data-phase] header [data-slot="conversation.session.header.utilities"] > [data-mobile-nav="header-info"],
287
336
  [data-phase] header [data-slot="conversation.session.header.utilities"] > [data-mobile-nav="header-workbench"] {
288
337
  display: inline-flex !important;
@@ -360,6 +409,80 @@ export const HEADER_CSS = `/* ---------- session header five-piece reflow (< 768
360
409
  display:contents on titleCluster does not break that search — so
361
410
  top:100% sits directly under the title regardless of the title row's
362
411
  actual height, with no hardcoded offset to keep in sync. */
412
+ /* Activity chip (subagents / background tasks) rides the right end of the
413
+ same band. It is a SIBLING of the switch button, not a child — that
414
+ button spans the full row, so nesting would make every tap on the chip
415
+ switch views too — and it shares titleRow as its containing block, so
416
+ top:100% lands both on the same 28px line. z-index puts it above the
417
+ row it overlaps. */
418
+ [data-mobile-nav="header-activity"] {
419
+ position: absolute;
420
+ right: 0;
421
+ top: 100%;
422
+ z-index: 1;
423
+ display: inline-flex;
424
+ align-items: center;
425
+ gap: 9px;
426
+ height: 28px;
427
+ padding: 0 2px 0 10px;
428
+ border: none;
429
+ background: transparent;
430
+ color: var(--dsw-alias-label-secondary, rgba(0, 0, 0, .5));
431
+ font-family: inherit;
432
+ cursor: pointer;
433
+ touch-action: manipulation;
434
+ -webkit-tap-highlight-color: transparent;
435
+ }
436
+ [data-mobile-nav="activity-pill"] {
437
+ display: inline-flex;
438
+ align-items: center;
439
+ gap: 3px;
440
+ height: 28px;
441
+ padding: 0;
442
+ border: none;
443
+ background: transparent;
444
+ color: inherit;
445
+ font-family: inherit;
446
+ font-size: 12px;
447
+ line-height: 18px;
448
+ cursor: pointer;
449
+ touch-action: manipulation;
450
+ -webkit-tap-highlight-color: transparent;
451
+ }
452
+ [data-mobile-nav="activity-pill"]:active {
453
+ opacity: .55;
454
+ }
455
+ [data-mobile-nav="activity-count"] {
456
+ font-variant-numeric: tabular-nums;
457
+ }
458
+ /* Steady green = everything settled. */
459
+ [data-mobile-nav="activity-dot"] {
460
+ display: block;
461
+ width: 6px;
462
+ height: 6px;
463
+ margin-left: 1px;
464
+ border-radius: 50%;
465
+ background: var(--dsw-alias-state-success-primary, #16a34a);
466
+ }
467
+ /* A job that failed or was killed is not a success — never paint it green. */
468
+ [data-activity-state="warning"] [data-mobile-nav="activity-dot"] {
469
+ background: var(--dsw-alias-state-warn-primary, #d97706);
470
+ }
471
+ /* Pulsing blue = still running. Same state palette as the session dot. */
472
+ [data-activity-state="running"] [data-mobile-nav="activity-dot"] {
473
+ background: var(--dsw-alias-state-business-primary, #4f6ef7);
474
+ animation: dsh-zen-activity-pulse 1.4s ease-in-out infinite;
475
+ }
476
+ @keyframes dsh-zen-activity-pulse {
477
+ 0%, 100% { opacity: 1; transform: scale(1); }
478
+ 50% { opacity: .45; transform: scale(.72); }
479
+ }
480
+ @media (prefers-reduced-motion: reduce) {
481
+ [data-activity-state="running"] [data-mobile-nav="activity-dot"] {
482
+ animation: none;
483
+ }
484
+ }
485
+
363
486
  [data-mobile-nav="header-viewrow"] {
364
487
  position: absolute;
365
488
  left: 0;
@@ -101,14 +101,19 @@ export const HOME_CSS = `/* ---------- phone app shell (< 768px) ---------- */
101
101
  values, not source-read guesses, are what actually matter here —
102
102
  --dsw-alias-bg-base/-layer-1/-layer-2 all resolve to the exact same
103
103
  color in the LIGHT theme (neutral-bluish-00, i.e. plain white); they
104
- only diverge in dark mode. A layer-* token would have made the page
105
- and its cards indistinguishable in light mode specifically — the
106
- opposite of what was asked. --dsw-specific-sidebar-fill differs from
107
- bg-base in BOTH themes (bluish-50 vs -00 light, bluish-900 vs -950
108
- dark) and is the exact token dsh-better-sidebar's own panel already
109
- uses for this same "secondary surface next to bg-base content"
110
- role, so it is the correct reuse rather than a new hardcoded gray. */
111
- background: var(--dsw-specific-sidebar-fill, #f5f5f5);
104
+ only diverge in dark mode.
105
+
106
+ 2026-08-20: the page and the CARDS swapped tokens. This surface used
107
+ to be --dsw-specific-sidebar-fill, which made the list page a
108
+ different shade from the session page — measured on-device in dark
109
+ theme, rgb(27,27,28) here against rgb(21,21,23) there, a visible step
110
+ every time you opened a session. The page now takes bg-base, the same
111
+ token the session page and <body> resolve to, so the two screens are
112
+ literally the same colour; the cards took sidebar-fill in exchange, so
113
+ the contrast that separates a row from the page is preserved — it
114
+ just runs the other way (raised card on a base page, the more
115
+ conventional reading of the two anyway). */
116
+ background: var(--dsw-alias-bg-base, #ffffff);
112
117
  color: var(--dsw-alias-label-primary, inherit);
113
118
  padding-top: var(--mnav-sat);
114
119
  transform: translateX(0);
@@ -226,7 +231,10 @@ export const HOME_CSS = `/* ---------- phone app shell (< 768px) ---------- */
226
231
  padding: 14px 16px;
227
232
  border: none;
228
233
  border-radius: 22px;
229
- background: var(--dsw-alias-bg-base, #ffffff);
234
+ /* The page's old colour (see the swap note on [data-mobile-nav="home"]):
235
+ differs from bg-base in BOTH themes, and is the token
236
+ dsh-better-sidebar's panel already uses for a secondary surface. */
237
+ background: var(--dsw-specific-sidebar-fill, #f5f5f5);
230
238
  color: inherit;
231
239
  font-family: inherit;
232
240
  text-align: left;
@@ -329,7 +337,8 @@ export const HOME_CSS = `/* ---------- phone app shell (< 768px) ---------- */
329
337
  margin: 12px 16px;
330
338
  padding: 48px 24px;
331
339
  border-radius: 22px;
332
- background: var(--dsw-alias-bg-base, #ffffff);
340
+ /* Stands in for the rows, so it carries the rows' surface. */
341
+ background: var(--dsw-specific-sidebar-fill, #f5f5f5);
333
342
  color: var(--dsw-alias-label-secondary, rgba(0, 0, 0, .5));
334
343
  font-size: 15px;
335
344
  text-align: center;