@deepseek-insight/dsi 0.8.0 → 0.8.1

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 (116) hide show
  1. package/build/client/_app/immutable/assets/0.DBVVEjy9.css +2 -0
  2. package/build/client/_app/immutable/assets/2.BhBfHqja.css +1 -0
  3. package/build/client/_app/immutable/chunks/{Dk9aRp2m.js → 4B6J_kM2.js} +1 -1
  4. package/build/client/_app/immutable/chunks/{ANnPTMPF.js → 4cwyag_I.js} +1 -1
  5. package/build/client/_app/immutable/chunks/{D7-uGicP.js → 7FnrqGK9.js} +1 -1
  6. package/build/client/_app/immutable/chunks/{CIH5Juz4.js → AcmOeh2U.js} +1 -1
  7. package/build/client/_app/immutable/chunks/{BqX9BB0G.js → B2jrDnt8.js} +1 -1
  8. package/build/client/_app/immutable/chunks/{A5Iiq81h.js → B4F5liKq.js} +1 -1
  9. package/build/client/_app/immutable/chunks/{D3rUyYDm.js → B6bTO8_7.js} +1 -1
  10. package/build/client/_app/immutable/chunks/{2XtVrxH4.js → BBd0nTHG.js} +1 -1
  11. package/build/client/_app/immutable/chunks/BHDqH9nE.js +1 -0
  12. package/build/client/_app/immutable/chunks/{-izoh3h0.js → BO99LSSA.js} +1 -1
  13. package/build/client/_app/immutable/chunks/{BjafRUyA.js → BQC6O_RV.js} +1 -1
  14. package/build/client/_app/immutable/chunks/{BJsp4NMH.js → BWRXzjmQ.js} +1 -1
  15. package/build/client/_app/immutable/chunks/{crRp5OG4.js → BXKc4uuX.js} +1 -1
  16. package/build/client/_app/immutable/chunks/{j2lJ25s0.js → BenkQ_4r.js} +1 -1
  17. package/build/client/_app/immutable/chunks/{Dso_gKeU.js → BkYT72a1.js} +1 -1
  18. package/build/client/_app/immutable/chunks/{CCWXLdgd.js → BlwWx85l.js} +1 -1
  19. package/build/client/_app/immutable/chunks/{DCmONIPx.js → BmDADyfH.js} +1 -1
  20. package/build/client/_app/immutable/chunks/{DYfx-RRD.js → Bpd0I3U7.js} +1 -1
  21. package/build/client/_app/immutable/chunks/{B-V7Vlsz.js → Bqwvv7Vf.js} +4 -4
  22. package/build/client/_app/immutable/chunks/{CQTGR8fW.js → BsBBq-Qk.js} +1 -1
  23. package/build/client/_app/immutable/chunks/{IBXFCASw.js → Byu_teLA.js} +1 -1
  24. package/build/client/_app/immutable/chunks/{DpSjzsDs.js → C0tWPpc3.js} +2 -2
  25. package/build/client/_app/immutable/chunks/{Cx8YXmgi.js → C4gs6zoI.js} +1 -1
  26. package/build/client/_app/immutable/chunks/CAneZX4t.js +8 -0
  27. package/build/client/_app/immutable/chunks/{olUfRNXS.js → CB55Rp7-.js} +1 -1
  28. package/build/client/_app/immutable/chunks/{C1dC47iq.js → CH_bR_0j.js} +1 -1
  29. package/build/client/_app/immutable/chunks/{CvvYpRG9.js → CJ1fzlPC.js} +1 -1
  30. package/build/client/_app/immutable/chunks/{DfIqw6WM.js → CK5vhXvt.js} +1 -1
  31. package/build/client/_app/immutable/chunks/{0HT0ILkA.js → CRFuOqJI.js} +1 -1
  32. package/build/client/_app/immutable/chunks/{gXyYVhfn.js → CZoElI5B.js} +1 -1
  33. package/build/client/_app/immutable/chunks/{dcghWWmq.js → CajOZJDl.js} +1 -1
  34. package/build/client/_app/immutable/chunks/{BImoJlJo.js → CdaJvl_A.js} +1 -1
  35. package/build/client/_app/immutable/chunks/{CNJYmM8n.js → CeLBjOd9.js} +1 -1
  36. package/build/client/_app/immutable/chunks/{IoX9lmZK.js → CevKuuic.js} +1 -1
  37. package/build/client/_app/immutable/chunks/{B_Gknb5_.js → Cf0aMC4n.js} +1 -1
  38. package/build/client/_app/immutable/chunks/{C_MwBDNt.js → CnxoqrkQ.js} +1 -1
  39. package/build/client/_app/immutable/chunks/{ChC4viXw.js → Cr2ck_ss.js} +1 -1
  40. package/build/client/_app/immutable/chunks/{fJBuRi-A.js → Czc4_W7v.js} +1 -1
  41. package/build/client/_app/immutable/chunks/{CHYEvS-6.js → Czey8tDK.js} +1 -1
  42. package/build/client/_app/immutable/chunks/{bpkmZ_Dv.js → D0ChUnpi.js} +1 -1
  43. package/build/client/_app/immutable/chunks/{B5S0Wke9.js → D0uxHECr.js} +1 -1
  44. package/build/client/_app/immutable/chunks/{2M1KS72j.js → D6KenyiF.js} +1 -1
  45. package/build/client/_app/immutable/chunks/{DT1b6JaK.js → DBKMIEwD.js} +1 -1
  46. package/build/client/_app/immutable/chunks/{Bq4mG8qS.js → DEVxi5CK.js} +1 -1
  47. package/build/client/_app/immutable/chunks/{Bh_Z0-Fh.js → DFBKiA2Z.js} +1 -1
  48. package/build/client/_app/immutable/chunks/{DSqRqQNp.js → DIxy2USu.js} +1 -1
  49. package/build/client/_app/immutable/chunks/{D4I0Dqwh.js → DJQr4efH.js} +1 -1
  50. package/build/client/_app/immutable/chunks/DQXo7qdJ.js +1 -0
  51. package/build/client/_app/immutable/chunks/{3E8jbhSl.js → DU8C5thd.js} +1 -1
  52. package/build/client/_app/immutable/chunks/{D7eKc9ur.js → DUCaFYlx.js} +1 -1
  53. package/build/client/_app/immutable/chunks/{CbtU_GW3.js → DdI16loP.js} +1 -1
  54. package/build/client/_app/immutable/chunks/{DykTZ90H.js → DdJNsJBH.js} +1 -1
  55. package/build/client/_app/immutable/chunks/{C6zqVyWx.js → DfUgh5E_.js} +1 -1
  56. package/build/client/_app/immutable/chunks/{Ch7rc-L7.js → DmRY6FWm.js} +4 -4
  57. package/build/client/_app/immutable/chunks/{D7XrPGMK.js → DuRZnRVw.js} +1 -1
  58. package/build/client/_app/immutable/chunks/{Do0VAo0_.js → DuiFyTVb.js} +1 -1
  59. package/build/client/_app/immutable/chunks/{Ck2086Sr.js → DwcY-69G.js} +1 -1
  60. package/build/client/_app/immutable/chunks/K3xyLTzZ.js +1 -0
  61. package/build/client/_app/immutable/chunks/{GMyoOYdi.js → NcOjFWr3.js} +1 -1
  62. package/build/client/_app/immutable/chunks/{BZ6uvzU8.js → O6xxeEm8.js} +1 -1
  63. package/build/client/_app/immutable/chunks/{DrVddB75.js → OW5Q7tGb.js} +1 -1
  64. package/build/client/_app/immutable/chunks/{CW2uYEvb.js → SN3Efo-Q.js} +1 -1
  65. package/build/client/_app/immutable/chunks/{Bf1I5ROF.js → ap4GAVq-.js} +1 -1
  66. package/build/client/_app/immutable/chunks/{DWYSL4om.js → at6PQqQ4.js} +1 -1
  67. package/build/client/_app/immutable/chunks/cHj8EuyS.js +3 -0
  68. package/build/client/_app/immutable/chunks/{l3etNtOh.js → eFcFJ9Ld.js} +1 -1
  69. package/build/client/_app/immutable/chunks/gZFoJ-NI.js +1 -0
  70. package/build/client/_app/immutable/chunks/{Bx68-tOf.js → kzciT-3i.js} +1 -1
  71. package/build/client/_app/immutable/chunks/{DNf96C-O.js → lF8CNVOA.js} +1 -1
  72. package/build/client/_app/immutable/chunks/o7DVZODA.js +1 -0
  73. package/build/client/_app/immutable/chunks/pdYhlwhf.js +1 -0
  74. package/build/client/_app/immutable/chunks/{q1Yj7q99.js → q5PRaRZk.js} +1 -1
  75. package/build/client/_app/immutable/chunks/xkjLeSzJ.js +1 -0
  76. package/build/client/_app/immutable/chunks/{BKU5-8Td.js → zypfUZYM.js} +1 -1
  77. package/build/client/_app/immutable/entry/app.DP0oSUf-.js +2 -0
  78. package/build/client/_app/immutable/entry/start.mBp3LB6J.js +1 -0
  79. package/build/client/_app/immutable/nodes/0.Dm_ry-Pr.js +1 -0
  80. package/build/client/_app/immutable/nodes/1.UuScj-IS.js +1 -0
  81. package/build/client/_app/immutable/nodes/2.CJRPZhbe.js +340 -0
  82. package/build/client/_app/version.json +1 -1
  83. package/build/handler.js +6 -6
  84. package/build/index.js +6 -6
  85. package/build/server/chunks/chunks/{index-server.js-CV2lq-ha.js → index-server.js-DmIEbDVz.js} +2 -2
  86. package/build/server/chunks/chunks/{internal2.js-BtL7AFjQ.js → internal2.js-Pv5iygVN.js} +3 -3
  87. package/build/server/chunks/chunks/{server.js-DqHGsQU2.js → server.js-B1bDXhHu.js} +47 -2
  88. package/build/server/chunks/chunks/{state.js-D3ob-Gqa.js → state.js-BCaKzPy6.js} +4 -4
  89. package/build/server/chunks/entries/pages/{_error.svelte.js-9cKDDMkE.js → _error.svelte.js-BRyCyR0c.js} +5 -5
  90. package/build/server/chunks/entries/pages/{_page.svelte.js-hjZYZCe1.js → _page.svelte.js-CKY8hz9T.js} +962 -539
  91. package/build/server/chunks/{handler-B_azsHTz.js → handler-D1oqmwyy.js} +3 -3
  92. package/build/server/chunks/{index.js-3VY3QWe-.js → index.js-BZ8zP9wS.js} +4 -4
  93. package/build/server/chunks/{manifest.js-hHcXduh_.js → manifest.js-CA6PuZTD.js} +5 -5
  94. package/build/server/chunks/nodes/{0.js-B_LVBEe8.js → 0.js-DtkNHIFx.js} +3 -3
  95. package/build/server/chunks/nodes/1.js-Q6zZupRG.js +9 -0
  96. package/build/server/chunks/nodes/{2.js-bVRsbAhW.js → 2.js-P1boc94A.js} +4 -4
  97. package/package.json +1 -1
  98. package/template/.dsi/prompts.sqlite-shm +0 -0
  99. package/template/.dsi/prompts.sqlite-wal +0 -0
  100. package/build/client/_app/immutable/assets/0.c2dRYi3B.css +0 -2
  101. package/build/client/_app/immutable/assets/2.DBblYO_a.css +0 -1
  102. package/build/client/_app/immutable/chunks/BBDOLPkB.js +0 -1
  103. package/build/client/_app/immutable/chunks/BMgyB-Sy.js +0 -1
  104. package/build/client/_app/immutable/chunks/BYHxHjHc.js +0 -1
  105. package/build/client/_app/immutable/chunks/BwNsQhd8.js +0 -1
  106. package/build/client/_app/immutable/chunks/C0CuJuob.js +0 -1
  107. package/build/client/_app/immutable/chunks/CMByLjfJ.js +0 -1
  108. package/build/client/_app/immutable/chunks/DmLHHlla.js +0 -3
  109. package/build/client/_app/immutable/chunks/J_8-FdbR.js +0 -1
  110. package/build/client/_app/immutable/chunks/da2fibLv.js +0 -8
  111. package/build/client/_app/immutable/entry/app.GsYnI6xk.js +0 -2
  112. package/build/client/_app/immutable/entry/start.keNjq0dA.js +0 -1
  113. package/build/client/_app/immutable/nodes/0.BJVfjaPJ.js +0 -1
  114. package/build/client/_app/immutable/nodes/1.rhLd5ssJ.js +0 -1
  115. package/build/client/_app/immutable/nodes/2.DdzHBwFT.js +0 -340
  116. package/build/server/chunks/nodes/1.js-BRybp38b.js +0 -9
@@ -1,5 +1,6 @@
1
- import { a as __exportAll, o as onDestroy } from '../../chunks/index-server.js-CV2lq-ha.js';
2
- import { M as derived, V as head, X as bind_props, W as escape_html, Y as attr, Z as attr_style, _ as attr_class, a as setContext, $ as spread_props, a0 as attributes, a1 as clsx, a2 as ensure_array_like, a3 as element, g as getContext, a4 as stringify, a5 as html, r as run, a6 as props_id } from '../../chunks/server.js-DqHGsQU2.js';
1
+ import { a as __exportAll, o as onDestroy } from '../../chunks/index-server.js-DmIEbDVz.js';
2
+ import '../../chunks/exports.js-DH_tjtdv.js';
3
+ import { M as derived, V as head, T as writable, W as escape_html, X as attr, Y as bind_props, a as setContext, Z as attr_class, _ as attr_style, $ as store_get, a0 as spread_props, a1 as stringify, a2 as attributes, a3 as clsx, a4 as ensure_array_like, a5 as element, g as getContext, a6 as unsubscribe_stores, a7 as html, r as run, a8 as props_id } from '../../chunks/server.js-B1bDXhHu.js';
3
4
  import { U as UI_LOCALES } from '../../chunks/config.js-vThvQeMn.js';
4
5
  import { m as mergeEntries, c as collapseSystemPromptEntries, t as truncate } from '../../chunks/merge.js-yMwW440S.js';
5
6
  import { p as parseA2aSignature, m as mintA2aId, b as buildDeliveredText } from '../../chunks/a2a-protocol.js-C77mVg2Q.js';
@@ -8,7 +9,7 @@ import { D as DEFAULT_IMAGE_LIMITS } from '../../chunks/image-limits.js-CUdJ0f7r
8
9
  import { s as sanitizeWorkspaceProfile, d as dsiKey, t as themePrefs } from '../../chunks/theme-prefs.svelte.js-8CaWz46g.js';
9
10
  import { a as appConfig, p as previewSegments, b as parseTagWords, i as isValidTagWord, s as shouldRecordPrompt, g as getTrigger, t as triggerMode, c as searchKey, d as textPreviewOf } from '../../chunks/app-config.svelte.js-BM4WbMFB.js';
10
11
  import { c as getLocale } from '../../chunks/runtime.js-CCJi3XaY.js';
11
- import { p as page, a as afterNavigate, t, c as currentLocale, e as replaceState, u as unexpectederror1, s as sessionnotfound2, r as reapednote1, d as dshunreachable1, b as backtoworkspace2 } from '../../chunks/state.js-D3ob-Gqa.js';
12
+ import { p as page, a as afterNavigate, t, c as currentLocale, e as replaceState, u as unexpectederror1, s as sessionnotfound2, r as reapednote1, d as dshunreachable1, b as backtoworkspace2 } from '../../chunks/state.js-BCaKzPy6.js';
12
13
  import 'yaml';
13
14
  import 'html-to-image';
14
15
  import hljs from 'highlight.js/lib/core';
@@ -20,8 +21,7 @@ import xml from 'highlight.js/lib/languages/xml';
20
21
  import css from 'highlight.js/lib/languages/css';
21
22
  import '../../chunks/uneval.js-BJ9Qq2SF.js';
22
23
  import '../../chunks/shared.js-HQ0rfhm4.js';
23
- import '../../chunks/internal2.js-BtL7AFjQ.js';
24
- import '../../chunks/exports.js-DH_tjtdv.js';
24
+ import '../../chunks/internal2.js-Pv5iygVN.js';
25
25
  import '../../chunks/utils.js-CAdNO8Tu.js';
26
26
 
27
27
  //#region src/lib/paraglide/messages/abort.js
@@ -17155,19 +17155,23 @@ function SessionStatus($$renderer, $$props) {
17155
17155
  * plus the ×N badge), the replacement must be re-measured, never
17156
17156
  * assumed.
17157
17157
  */
17158
- let { running, delegated = false, count = 0 } = $$props;
17158
+ let { delegated = false, count = 0, browser = false } = $$props;
17159
17159
  /** Accessible name — four distinct states; the count rides along
17160
17160
  * (WCAG 1.4.1: never color alone). */
17161
- const label = derived(() => running ? delegated ? count > 0 ? `running and delegating, ${count} running` : "running and delegating" : "running" : delegated ? count > 0 ? `delegating, ${count} running` : "delegating" : "idle");
17161
+ const label = derived(() => browser ? "browser session, folded — click to open" : delegated ? count > 0 ? `running and delegating, ${count} running` : "running and delegating" : "running" );
17162
17162
  $$renderer.push(`<span data-svelte="SessionStatus"${attr_class("status svelte-x2ntn8", void 0, {
17163
- "run": running,
17164
- "idle": !running,
17163
+ "run": !browser,
17164
+ "idle": false,
17165
+ "browser": browser,
17165
17166
  "delegated": delegated
17166
17167
  })} role="img"${attr("aria-label", label())} data-testid="session-status">`);
17167
- if (running) {
17168
+ if (browser) {
17168
17169
  $$renderer.push("<!--[0-->");
17170
+ $$renderer.push(`<span class="frame svelte-x2ntn8" aria-hidden="true"></span>`);
17171
+ } else {
17172
+ $$renderer.push("<!--[1-->");
17169
17173
  $$renderer.push(`<span class="bar svelte-x2ntn8"></span><span class="bar svelte-x2ntn8"></span><span class="bar svelte-x2ntn8"></span>`);
17170
- } else $$renderer.push("<!--[-1-->");
17174
+ }
17171
17175
  $$renderer.push(`<!--]-->`);
17172
17176
  if (count > 0) {
17173
17177
  $$renderer.push("<!--[0-->");
@@ -17581,7 +17585,7 @@ function ThemeToggle($$renderer, $$props) {
17581
17585
  }
17582
17586
  //#endregion
17583
17587
  //#region package.json
17584
- var version = "0.8.0";
17588
+ var version = "0.8.1";
17585
17589
  var dsh = { "webVersion": "0.2.1-rc.2" };
17586
17590
  //#endregion
17587
17591
  //#region src/lib/version.ts
@@ -19029,12 +19033,34 @@ function SidebarSessions($$renderer, $$props) {
19029
19033
  * picker dialog). Blank never-prompted sessions hide by default
19030
19034
  * (toggle in the filter row).
19031
19035
  */
19032
- let { currentSessionId, filter, paneledSessionIds = [], onfilterchange } = $$props;
19036
+ let { currentSessionId, filter, paneledSessionIds = [], glassRows = [], onfilterchange } = $$props;
19033
19037
  /** The host workspace registry — the AUTHORITY for chips/pills here. */
19034
19038
  let workspaces = [];
19035
19039
  /** The full host preset catalog — the agent pills list it entirely. */
19036
19040
  let availablePresets = [];
19037
19041
  const ready = derived(() => []);
19042
+ /** Folded-glass synthetic rows (2026-10-22): shaped as session
19043
+ * summaries so the WHOLE spine pipeline (lineage, family nesting,
19044
+ * filters) treats them as the opener's children. origin 'browser'
19045
+ * marks the row for RowSessionItem's summon click; workspace and
19046
+ * title ride the opener so filters keep the family whole. */
19047
+ const glassSummaries = derived(() => {
19048
+ return glassRows.map((g) => {
19049
+ const opener = ready().find((s) => s.sessionId === g.openedBy);
19050
+ return {
19051
+ sessionId: g.sessionId,
19052
+ title: g.title || "Browser",
19053
+ agentPreset: null,
19054
+ running: true,
19055
+ blank: false,
19056
+ updatedAt: opener?.updatedAt ?? 0,
19057
+ workspace: opener?.workspace ?? null,
19058
+ turns: null,
19059
+ parentSessionId: g.openedBy,
19060
+ origin: "browser"
19061
+ };
19062
+ });
19063
+ });
19038
19064
  /** Current row — looked up from the RAW list (never filtered away). */
19039
19065
  const current = derived(() => ready().find((s) => s.sessionId === currentSessionId) ?? null);
19040
19066
  let spine = loadSpineGroupPrefs(null);
@@ -19044,8 +19070,8 @@ function SidebarSessions($$renderer, $$props) {
19044
19070
  * generalized from 1 to N). */
19045
19071
  const paneled = derived(() => new Set(paneledSessionIds));
19046
19072
  const others = derived(() => ready().filter((s) => s.sessionId !== currentSessionId && !paneled().has(s.sessionId)));
19047
- const lineage = derived(() => deriveLineage(ready(), []));
19048
- const unsuppressed = derived(() => suppressFromSpine(others(), lineage(), paneled()));
19073
+ const lineage = derived(() => deriveLineage([...ready(), ...glassSummaries()], []));
19074
+ const unsuppressed = derived(() => suppressFromSpine([...others(), ...glassSummaries()], lineage(), paneled()));
19049
19075
  const wsPills = derived(() => workspaceOptions(others(), workspaces));
19050
19076
  const presets = derived(() => presetOptions(availablePresets, others()));
19051
19077
  /** Self-healed filter — a persisted key no pill carries reads as All. */
@@ -19220,26 +19246,424 @@ function CanvasCopyButton($$renderer, $$props) {
19220
19246
  });
19221
19247
  }
19222
19248
  //#endregion
19249
+ //#region src/lib/utils/panel-prefs.ts
19250
+ /** localStorage base key — one JSON blob `{ panels, selectedPanelId, panelWidth, zoom }`.
19251
+ * A workspace profile (`?profile=…` on the URL) suffixes EVERY dsi-* key
19252
+ * (see storage-profile.ts): the default desk lives under `dsi-panels`,
19253
+ * profile `widi` under `dsi-panels_widi`. */
19254
+ var PANEL_PREFS_KEY = "dsi-panels";
19255
+ /**
19256
+ * The storage key for a workspace profile.
19257
+ * @param profile sanitized profile segment, or null/undefined for the
19258
+ * default desk (`dsi-panels`).
19259
+ */
19260
+ function panelPrefsKey(profile) {
19261
+ return dsiKey(PANEL_PREFS_KEY, profile);
19262
+ }
19263
+ var PANEL_WORKSPACE_FILE_MAX_WIDTH = 1436;
19264
+ /** The single treePct bound site — sanitize a stored or dragged value
19265
+ * into [TREE_PCT_MIN, TREE_PCT_MAX]; junk (non-finite) falls back to
19266
+ * TREE_PCT_DEFAULT. Mirrors clampPanelZoom's discipline. */
19267
+ function clampTreePct(pct) {
19268
+ if (typeof pct !== "number" || !Number.isFinite(pct)) return 30;
19269
+ return Math.max(15, Math.min(70, Math.round(pct)));
19270
+ }
19271
+ /**
19272
+ * The single panel-width bound site — load-side sanitizing, gutter drags,
19273
+ * and the width slider all route through here. Bounds read the config
19274
+ * store (layout load has landed them before any mount; defaults before).
19275
+ */
19276
+ function clampPanelWidth(width, kind) {
19277
+ const { minWidth, maxWidth } = appConfig().panel;
19278
+ if (kind === "workspace-explorer") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
19279
+ if (kind === "workspace-file") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
19280
+ return Math.max(minWidth, Math.min(maxWidth, width));
19281
+ }
19282
+ /** The single zoom bound site — load-side sanitizing and the zoom slider. */
19283
+ function clampPanelZoom(zoom) {
19284
+ const { minZoom, maxZoom } = appConfig().panel;
19285
+ return Math.max(minZoom, Math.min(maxZoom, zoom));
19286
+ }
19287
+ /** Config-driven default width (clamped through the single bound site). */
19288
+ function defaultPanelWidth() {
19289
+ return clampPanelWidth(appConfig().panel.defaultWidth);
19290
+ }
19291
+ /**
19292
+ * The skill shelf's born width (The Shelf Chrome ADR, D5): a name list,
19293
+ * not a five-column table — the shelf pins 480 (DEFAULT_PANEL_MIN_WIDTH)
19294
+ * instead of the 730 conversation default, clamped through the single
19295
+ * bound site.
19296
+ */
19297
+ function shelfPanelWidth() {
19298
+ return clampPanelWidth(480);
19299
+ }
19300
+ /**
19301
+ * Clamp a stored explorer `expanded` list to sane paths (Workspace
19302
+ * Explorer restore): non-string members drop and a repeated path keeps
19303
+ * its FIRST occurrence — the tree treats `expanded` as a set of
19304
+ * root-relative directories, and a duplicate would double-render a row.
19305
+ */
19306
+ function clampExpanded(value) {
19307
+ if (!Array.isArray(value)) return [];
19308
+ const seen = /* @__PURE__ */ new Set();
19309
+ const out = [];
19310
+ for (const member of value) {
19311
+ if (typeof member !== "string" || seen.has(member)) continue;
19312
+ seen.add(member);
19313
+ out.push(member);
19314
+ }
19315
+ return out;
19316
+ }
19317
+ /** The terminal desk's structure clamp (Terminal Desk ADR D3, Wave 3):
19318
+ * sanitize a stored desk mirror into { tabs, selectedTab } or undefined.
19319
+ * Junk drops FIELD-WISE — non-string sessionIds drop, empty/junk tabs
19320
+ * drop, selectedTab clamps into range — so a stale blob degrades to
19321
+ * "fewer rows remembered", never a phantom row. All tabs dropped (or
19322
+ * junk at the top) ⇒ undefined: the desk falls back to one fresh row. */
19323
+ function clampDeskMirror(value) {
19324
+ if (value === null || typeof value !== "object") return void 0;
19325
+ const raw = value;
19326
+ if (!Array.isArray(raw.tabs)) return void 0;
19327
+ const tabs = [];
19328
+ for (const tab of raw.tabs) {
19329
+ if (tab === null || typeof tab !== "object") continue;
19330
+ const ids = tab.sessionIds;
19331
+ if (!Array.isArray(ids)) continue;
19332
+ const sessionIds = ids.filter((id) => typeof id === "string" && id.length > 0);
19333
+ if (sessionIds.length === 0) continue;
19334
+ tabs.push({ sessionIds });
19335
+ }
19336
+ const jobs = [];
19337
+ if (Array.isArray(raw.jobs)) for (const j of raw.jobs) {
19338
+ if (j === null || typeof j !== "object") continue;
19339
+ const owner = j.owner;
19340
+ const jobId = j.jobId;
19341
+ if (typeof owner !== "string" || typeof jobId !== "string") continue;
19342
+ if (owner.length === 0 || jobId.length === 0) continue;
19343
+ jobs.push({
19344
+ owner,
19345
+ jobId
19346
+ });
19347
+ }
19348
+ if (tabs.length === 0 && jobs.length === 0) return void 0;
19349
+ const maxIndex = tabs.length + jobs.length;
19350
+ return {
19351
+ tabs,
19352
+ selectedTab: typeof raw.selectedTab === "number" && Number.isFinite(raw.selectedTab) ? Math.max(0, Math.min(maxIndex, Math.round(raw.selectedTab))) : 0,
19353
+ ...jobs.length > 0 ? { jobs } : {}
19354
+ };
19355
+ }
19356
+ /** Persisted defaults when nothing (usable) is stored. */
19357
+ function defaultPanelPrefs() {
19358
+ return {
19359
+ panels: [],
19360
+ selectedPanelId: null,
19361
+ panelWidth: defaultPanelWidth(),
19362
+ zoom: 1,
19363
+ treePct: 30
19364
+ };
19365
+ }
19366
+ /**
19367
+ * Hard shape check for a stored panel entry (the parse boundary).
19368
+ * KIND (2026-09-06, ADR D6): absent or 'conversation' is the legacy shape —
19369
+ * `sessionId` required; 'prompt-manager' carries no session fields;
19370
+ * 'injected-doc' (Loadinjected ADR D2) requires non-empty `sourceSessionId`
19371
+ * + `displayPath` — the dedupe key. Any other kind value is hard junk
19372
+ * (entry drops). `agentPreset` and `width`
19373
+ * may be absent (defaulted later); `agentPreset` when present must be
19374
+ * string|null, `width` when present must be a finite number. Unknown
19375
+ * extra keys are fine — they survive sanitizing.
19376
+ */
19377
+ function isPanelEntry(value) {
19378
+ if (value === null || typeof value !== "object") return false;
19379
+ const entry = value;
19380
+ if (typeof entry.id !== "string" || entry.id.length === 0) return false;
19381
+ if (entry.kind !== void 0 && entry.kind !== "conversation" && entry.kind !== "prompt-manager" && entry.kind !== "settings-editor" && entry.kind !== "injected-doc" && entry.kind !== "skill-shelf" && entry.kind !== "plugin-rack" && entry.kind !== "workspace-explorer" && entry.kind !== "workspace-file" && entry.kind !== "terminal") return false;
19382
+ if (entry.kind === "settings-editor" && entry.target !== "dsi" && entry.target !== "dsh") return false;
19383
+ if (entry.kind === "workspace-explorer") {
19384
+ if (entry.sessionId !== null && (typeof entry.sessionId !== "string" || entry.sessionId.length === 0)) return false;
19385
+ if (entry.sessionId === null && entry.home !== "dsi" && entry.home !== "dsh") return false;
19386
+ }
19387
+ if (entry.kind === "workspace-file") {
19388
+ if (typeof entry.sessionId !== "string" || entry.sessionId.length === 0) return false;
19389
+ }
19390
+ if (entry.kind === "workspace-explorer" && typeof entry.root !== "string") return false;
19391
+ if (entry.kind === "workspace-file") {
19392
+ if (typeof entry.path !== "string" || entry.path.length === 0) return false;
19393
+ }
19394
+ if (entry.kind === "injected-doc") {
19395
+ if (typeof entry.sourceSessionId !== "string" || entry.sourceSessionId.length === 0) return false;
19396
+ if (typeof entry.displayPath !== "string" || entry.displayPath.length === 0) return false;
19397
+ }
19398
+ if (entry.kind !== "prompt-manager" && entry.kind !== "settings-editor" && entry.kind !== "skill-shelf" && entry.kind !== "plugin-rack" && entry.kind !== "injected-doc" && entry.kind !== "workspace-explorer" && entry.kind !== "workspace-file" && entry.kind !== "terminal") {
19399
+ if (typeof entry.sessionId !== "string" || entry.sessionId.length === 0) return false;
19400
+ if (entry.agentPreset !== void 0 && typeof entry.agentPreset !== "string" && entry.agentPreset !== null) return false;
19401
+ }
19402
+ if (entry.width !== void 0 && (typeof entry.width !== "number" || !Number.isFinite(entry.width))) return false;
19403
+ return true;
19404
+ }
19405
+ /** Drop hard-junk entries; default missing keys; clamp widths. Unknown
19406
+ * keys on surviving entries pass through untouched.
19407
+ *
19408
+ * Duplicate ids (2026-08-24 crash fix): a corrupted blob carrying the
19409
+ * same panel-N twice would reach the keyed each in SidebarOpenPanels
19410
+ * and crash Svelte (each_key_duplicate) at render — the each's key is
19411
+ * the panel id. Duplicates drop to the FIRST occurrence (order is the
19412
+ * floor's left-to-right truth); a selectedPanelId left dangling by the
19413
+ * dedupe re-sanitizes to the first panel in loadPanelPrefs. */
19414
+ function sanitizePanels(raw) {
19415
+ if (!Array.isArray(raw)) return [];
19416
+ const seen = /* @__PURE__ */ new Set();
19417
+ return raw.filter((entry) => isPanelEntry(entry)).map((entry) => {
19418
+ const kindDefault = entry.kind === "skill-shelf" ? shelfPanelWidth() : entry.kind === "plugin-rack" ? shelfPanelWidth() : entry.kind === "workspace-explorer" ? 850 : entry.kind === "workspace-file" ? 850 : defaultPanelWidth();
19419
+ const width = clampPanelWidth(typeof entry.width === "number" && Number.isFinite(entry.width) ? entry.width : kindDefault, entry.kind);
19420
+ if (entry.kind === "prompt-manager") return {
19421
+ id: entry.id,
19422
+ kind: "prompt-manager",
19423
+ width
19424
+ };
19425
+ if (entry.kind === "terminal") {
19426
+ const desk = clampDeskMirror(entry.desk);
19427
+ return {
19428
+ id: entry.id,
19429
+ kind: "terminal",
19430
+ ...desk ? { desk } : {},
19431
+ width
19432
+ };
19433
+ }
19434
+ if (entry.kind === "settings-editor") return {
19435
+ id: entry.id,
19436
+ kind: "settings-editor",
19437
+ target: entry.target,
19438
+ width
19439
+ };
19440
+ if (entry.kind === "skill-shelf") {
19441
+ let reload = null;
19442
+ const r = entry.reload;
19443
+ if (r && r.phase === "loading" && typeof r.startedAt === "number") reload = {
19444
+ phase: "loading",
19445
+ startedAt: r.startedAt
19446
+ };
19447
+ else if (r && r.phase === "done" && typeof r.startedAt === "number" && typeof r.doneAt === "number" && r.doneAt > Date.now()) reload = {
19448
+ phase: "done",
19449
+ startedAt: r.startedAt,
19450
+ doneAt: r.doneAt
19451
+ };
19452
+ return {
19453
+ id: entry.id,
19454
+ kind: "skill-shelf",
19455
+ tab: entry.tab === "uninstall" ? "uninstall" : "install",
19456
+ collapsed: clampExpanded(entry.collapsed),
19457
+ searchQ: typeof entry.searchQ === "string" ? entry.searchQ : "",
19458
+ reload,
19459
+ width
19460
+ };
19461
+ }
19462
+ if (entry.kind === "plugin-rack") return {
19463
+ id: entry.id,
19464
+ kind: "plugin-rack",
19465
+ tab: entry.tab === "uninstall" ? "uninstall" : "install",
19466
+ width
19467
+ };
19468
+ if (entry.kind === "injected-doc") return {
19469
+ id: entry.id,
19470
+ kind: "injected-doc",
19471
+ sourceSessionId: entry.sourceSessionId,
19472
+ displayPath: entry.displayPath,
19473
+ width
19474
+ };
19475
+ if (entry.kind === "workspace-explorer") {
19476
+ const home = entry.home === "dsi" || entry.home === "dsh" ? entry.home : void 0;
19477
+ return {
19478
+ id: entry.id,
19479
+ kind: "workspace-explorer",
19480
+ sessionId: entry.sessionId ?? null,
19481
+ root: entry.root,
19482
+ ...typeof entry.title === "string" && entry.title.length > 0 ? { title: entry.title } : {},
19483
+ ...home !== void 0 ? { home } : {},
19484
+ expanded: clampExpanded(entry.expanded),
19485
+ tab: entry.tab === "changes" ? "changes" : "explorer",
19486
+ collapsedRepos: clampExpanded(entry.collapsedRepos),
19487
+ openTabs: clampExpanded(entry.openTabs),
19488
+ activeFile: typeof entry.activeFile === "string" ? entry.activeFile : null,
19489
+ width
19490
+ };
19491
+ }
19492
+ if (entry.kind === "workspace-file") return {
19493
+ id: entry.id,
19494
+ kind: "workspace-file",
19495
+ sessionId: entry.sessionId,
19496
+ path: entry.path,
19497
+ explorerPanelId: typeof entry.explorerPanelId === "string" && entry.explorerPanelId.length > 0 ? entry.explorerPanelId : null,
19498
+ view: entry.view === "diff" ? "diff" : "edit",
19499
+ width
19500
+ };
19501
+ return {
19502
+ ...entry,
19503
+ id: entry.id,
19504
+ kind: "conversation",
19505
+ sessionId: entry.sessionId,
19506
+ agentPreset: entry.agentPreset ?? null,
19507
+ width
19508
+ };
19509
+ }).filter((entry) => {
19510
+ if (seen.has(entry.id)) return false;
19511
+ seen.add(entry.id);
19512
+ return true;
19513
+ });
19514
+ }
19515
+ /**
19516
+ * Read the workspace state from localStorage.
19517
+ * @param profile sanitized workspace profile (null/omitted = default desk).
19518
+ * @returns stored state when parseable (sanitized), defaults when absent,
19519
+ * junk, or SSR. A `selectedPanelId` pointing at a missing panel
19520
+ * sanitizes to the first panel's id (null when no panels remain).
19521
+ */
19522
+ function loadPanelPrefs(profile) {
19523
+ if (typeof localStorage === "undefined") return defaultPanelPrefs();
19524
+ try {
19525
+ const raw = localStorage.getItem(panelPrefsKey(profile));
19526
+ if (!raw) return defaultPanelPrefs();
19527
+ const parsed = JSON.parse(raw);
19528
+ if (parsed === null || typeof parsed !== "object") return defaultPanelPrefs();
19529
+ if (!Array.isArray(parsed.panels)) return defaultPanelPrefs();
19530
+ const panels = sanitizePanels(parsed.panels);
19531
+ const panelWidth = typeof parsed.panelWidth === "number" && Number.isFinite(parsed.panelWidth) ? clampPanelWidth(parsed.panelWidth) : defaultPanelWidth();
19532
+ const zoom = typeof parsed.zoom === "number" && Number.isFinite(parsed.zoom) ? clampPanelZoom(parsed.zoom) : 1;
19533
+ const treePct = typeof parsed.treePct === "number" && Number.isFinite(parsed.treePct) ? clampTreePct(parsed.treePct) : 30;
19534
+ return {
19535
+ panels,
19536
+ selectedPanelId: typeof parsed.selectedPanelId === "string" && panels.some((panel) => panel.id === parsed.selectedPanelId) ? parsed.selectedPanelId : panels[0]?.id ?? null,
19537
+ panelWidth,
19538
+ zoom,
19539
+ treePct
19540
+ };
19541
+ } catch {
19542
+ return defaultPanelPrefs();
19543
+ }
19544
+ }
19545
+ //#endregion
19546
+ //#region src/lib/components/panels/control-bar/ControlBar.svelte
19547
+ function ControlBar($$renderer, $$props) {
19548
+ $$renderer.component(($$renderer) => {
19549
+ /**
19550
+ * ControlBar — the floor's hover tray (Panel Floor W5 task 5.1,
19551
+ * ADR-0006 R7; OCI ControlBar precedent, minus its orchestration
19552
+ * organs). Pinned top-right over the panels viewport; hovering the
19553
+ * gear trigger reveals the tray, leaving hides it after 250ms (the
19554
+ * timer clears on re-enter, so grazing the tray keeps it open).
19555
+ *
19556
+ * This file owns the trigger + the reveal/hide lifecycle; the tray
19557
+ * body — the floor controls + the action-button row — is
19558
+ * ControlBarTray:
19559
+ * - AddPanel — paste a sessionId → two verbs: Add appends a new
19560
+ * panel beside the focus, Rplc replaces the ACTIVE
19561
+ * panel in place (both registry actions)
19562
+ * - SliderWidth — uniform-width preset 480..860 (`onresizeall` sets
19563
+ * every panel + the preset LIVE per thumb move)
19564
+ * - SliderZoom — floor zoom 25%..125% step 5 ($bindable zoom)
19565
+ * - TrayButtons — the tray's button row (the floor canvas-capture:
19566
+ * `captureContainer` flows the page-owned floor
19567
+ * element down to its CanvasCopyButton)
19568
+ *
19569
+ * Deliberately ABSENT (ADR-0006 R7): RegisterFeature and a home link —
19570
+ * DSI has no orchestration backend for the former, and the sidebar
19571
+ * owns Home for the latter. The OCI tray ships both; this floor does not.
19572
+ *
19573
+ * Communication: AddPanel fires `addPanelFromSidebar` /
19574
+ * `replaceSelectedFromRegistry` through the panel registry — the
19575
+ * tray is a LEAF like a spine row, and the
19576
+ * registry is the only leaf→root action channel (commitment 2). Zoom
19577
+ * is a `$bindable` prop the route owns; every bound routes through
19578
+ * panel-prefs clamps (one clamp site, commitment 3). `onresizeall` is
19579
+ * a ControlBar-specific prop: the tray is a DIRECT child of the
19580
+ * workspace route (not a sidebar intermediate), so prop wiring here
19581
+ * breaks no commitment.
19582
+ */
19583
+ let { panelWidth = 730, zoom = 1, onresizeall, captureContainer = null } = $$props;
19584
+ let open = false;
19585
+ let $$settled = true;
19586
+ let $$inner_renderer;
19587
+ function $$render_inner($$renderer) {
19588
+ $$renderer.push(`<div data-svelte="ControlBar"${attr_class("controlbar svelte-vs008k", void 0, { "open": open })} role="toolbar"${attr("aria-label", t(floorcontrols1))} aria-orientation="vertical" tabindex="-1" data-testid="controlbar"${attr("data-open", open)}><button type="button" class="trigger svelte-vs008k" data-testid="controlbar-trigger"${attr("aria-label", t(floorcontrols1))}${attr("aria-expanded", open)}>`);
19589
+ Settings($$renderer, {
19590
+ size: 14,
19591
+ "aria-hidden": "true"
19592
+ });
19593
+ $$renderer.push(`<!----></button> `);
19594
+ $$renderer.push("<!--[-1-->");
19595
+ $$renderer.push(`<!--]--></div>`);
19596
+ }
19597
+ do {
19598
+ $$settled = true;
19599
+ $$inner_renderer = $$renderer.copy();
19600
+ $$render_inner($$inner_renderer);
19601
+ } while (!$$settled);
19602
+ $$renderer.subsume($$inner_renderer);
19603
+ bind_props($$props, {
19604
+ panelWidth,
19605
+ zoom
19606
+ });
19607
+ });
19608
+ }
19609
+ //#endregion
19223
19610
  //#region src/lib/components/common/layout/AppSidebarHeaderAction.svelte
19224
19611
  function AppSidebarHeaderAction($$renderer, $$props) {
19225
19612
  $$renderer.component(($$renderer) => {
19226
19613
  /**
19227
19614
  * AppSidebarHeaderAction — the expanded rail header's right-hand action
19228
19615
  * cluster (extracted from AppSidebar, 2026-09): add-workspace, locale
19229
- * switcher, canvas copy, and the collapse button.
19616
+ * switcher, canvas copy, and the collapse button. The floor ControlBar
19617
+ * (2026-10-22) rides the cluster's head — it self-positions fixed
19618
+ * top-right over the floor, so hosting it here is purely a mount-site
19619
+ * move; the page still owns panelWidth/zoom state and the resize-all
19620
+ * handler, threaded through the sidebar as $bindable props.
19230
19621
  */
19231
- let { captureContainer = null} = $$props;
19232
- $$renderer.push(`<div data-svelte="AppSidebarHeaderAction" class="rail-header-actions svelte-ijmtbh">`);
19233
- CanvasCopyButton($$renderer, {
19234
- container: captureContainer,
19235
- mode: "visible",
19236
- title: t(copypageimage2),
19237
- size: 14,
19238
- class: "inline-flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded transition-colors hover:bg-surface-hover"
19622
+ let { captureContainer = null, trayContainer = null, panelWidth = 730, zoom = 1, onresizeall, onToggleCollapse } = $$props;
19623
+ let $$settled = true;
19624
+ let $$inner_renderer;
19625
+ function $$render_inner($$renderer) {
19626
+ $$renderer.push(`<div data-svelte="AppSidebarHeaderAction" class="rail-header-actions svelte-ijmtbh">`);
19627
+ CanvasCopyButton($$renderer, {
19628
+ container: captureContainer,
19629
+ mode: "visible",
19630
+ title: t(copypageimage2),
19631
+ size: 14,
19632
+ class: "inline-flex h-[22px] w-[22px] shrink-0 items-center justify-center rounded transition-colors hover:bg-surface-hover"
19633
+ });
19634
+ $$renderer.push(`<!----> <button type="button" class="icon-btn svelte-ijmtbh"${attr("aria-expanded", true)}${attr("aria-label", t(collapsesidebar1))}${attr("title", t(collapsesidebar1))}>`);
19635
+ Panel_left_close($$renderer, { size: 14 });
19636
+ $$renderer.push(`<!----></button> `);
19637
+ ControlBar($$renderer, {
19638
+ onresizeall: onresizeall ?? (() => {}),
19639
+ captureContainer: trayContainer,
19640
+ get panelWidth() {
19641
+ return panelWidth;
19642
+ },
19643
+ set panelWidth($$value) {
19644
+ panelWidth = $$value;
19645
+ $$settled = false;
19646
+ },
19647
+ get zoom() {
19648
+ return zoom;
19649
+ },
19650
+ set zoom($$value) {
19651
+ zoom = $$value;
19652
+ $$settled = false;
19653
+ }
19654
+ });
19655
+ $$renderer.push(`<!----></div>`);
19656
+ }
19657
+ do {
19658
+ $$settled = true;
19659
+ $$inner_renderer = $$renderer.copy();
19660
+ $$render_inner($$inner_renderer);
19661
+ } while (!$$settled);
19662
+ $$renderer.subsume($$inner_renderer);
19663
+ bind_props($$props, {
19664
+ panelWidth,
19665
+ zoom
19239
19666
  });
19240
- $$renderer.push(`<!----> <button type="button" class="icon-btn svelte-ijmtbh"${attr("aria-expanded", true)}${attr("aria-label", t(collapsesidebar1))}${attr("title", t(collapsesidebar1))}>`);
19241
- Panel_left_close($$renderer, { size: 14 });
19242
- $$renderer.push(`<!----></button></div>`);
19243
19667
  });
19244
19668
  }
19245
19669
  //#endregion
@@ -19283,29 +19707,63 @@ function RestartButton($$renderer, $$props) {
19283
19707
  //#endregion
19284
19708
  //#region src/lib/components/common/layout/AppSidebarHeader.svelte
19285
19709
  function AppSidebarHeader($$renderer, $$props) {
19286
- /**
19287
- * AppSidebarHeader — the expanded rail header (extracted from
19288
- * AppSidebar, 2026-09): the About prefix, the brand title, and the
19289
- * right-hand action cluster, composed from AboutButton,
19290
- * AppSidebarHeaderTitle, and AppSidebarHeaderAction. Owns the
19291
- * animated gradient surface for the expanded state (the collapsed
19292
- * stub keeps its copy in AppSidebar — Svelte styles are
19293
- * component-scoped, so the shared rule had to be split; keep the two
19294
- * gradients in sync).
19295
- *
19296
- * Presentational only — collapse flipping stays delegated upward.
19297
- */
19298
- let { captureContainer = null, onToggleCollapse } = $$props;
19299
- $$renderer.push(`<header data-svelte="AppSidebarHeader" class="rail-header svelte-184i93r">`);
19300
- RestartButton($$renderer, { class: "rail-collapsed-label" });
19301
- $$renderer.push(`<!----> `);
19302
- AboutButton($$renderer, {});
19303
- $$renderer.push(`<!----> `);
19304
- AppSidebarHeaderTitle($$renderer);
19305
- $$renderer.push(`<!----> `);
19306
- AppSidebarHeaderAction($$renderer, {
19307
- captureContainer});
19308
- $$renderer.push(`<!----></header>`);
19710
+ $$renderer.component(($$renderer) => {
19711
+ /**
19712
+ * AppSidebarHeader — the expanded rail header (extracted from
19713
+ * AppSidebar, 2026-09): the About prefix, the brand title, and the
19714
+ * right-hand action cluster, composed from AboutButton,
19715
+ * AppSidebarHeaderTitle, and AppSidebarHeaderAction. Owns the
19716
+ * animated gradient surface for the expanded state (the collapsed
19717
+ * stub keeps its copy in AppSidebar — Svelte styles are
19718
+ * component-scoped, so the shared rule had to be split; keep the two
19719
+ * gradients in sync).
19720
+ *
19721
+ * Presentational only — collapse flipping stays delegated upward.
19722
+ */
19723
+ let { captureContainer = null, trayContainer = null, panelWidth = 730, zoom = 1, onresizeall, onToggleCollapse } = $$props;
19724
+ let $$settled = true;
19725
+ let $$inner_renderer;
19726
+ function $$render_inner($$renderer) {
19727
+ $$renderer.push(`<header data-svelte="AppSidebarHeader" class="rail-header svelte-184i93r">`);
19728
+ RestartButton($$renderer, { class: "rail-collapsed-label" });
19729
+ $$renderer.push(`<!----> `);
19730
+ AboutButton($$renderer, {});
19731
+ $$renderer.push(`<!----> `);
19732
+ AppSidebarHeaderTitle($$renderer);
19733
+ $$renderer.push(`<!----> `);
19734
+ AppSidebarHeaderAction($$renderer, {
19735
+ captureContainer,
19736
+ trayContainer,
19737
+ onresizeall,
19738
+ onToggleCollapse,
19739
+ get panelWidth() {
19740
+ return panelWidth;
19741
+ },
19742
+ set panelWidth($$value) {
19743
+ panelWidth = $$value;
19744
+ $$settled = false;
19745
+ },
19746
+ get zoom() {
19747
+ return zoom;
19748
+ },
19749
+ set zoom($$value) {
19750
+ zoom = $$value;
19751
+ $$settled = false;
19752
+ }
19753
+ });
19754
+ $$renderer.push(`<!----></header>`);
19755
+ }
19756
+ do {
19757
+ $$settled = true;
19758
+ $$inner_renderer = $$renderer.copy();
19759
+ $$render_inner($$inner_renderer);
19760
+ } while (!$$settled);
19761
+ $$renderer.subsume($$inner_renderer);
19762
+ bind_props($$props, {
19763
+ panelWidth,
19764
+ zoom
19765
+ });
19766
+ });
19309
19767
  }
19310
19768
  //#endregion
19311
19769
  //#region src/lib/components/common/layout/AppSidebar.svelte
@@ -19341,52 +19799,118 @@ function AppSidebar($$renderer, $$props) {
19341
19799
  * page (OCI ownership split); the gutter just reports mousedown, and the
19342
19800
  * single width clamp lives in sidebar-prefs (clampSidebarWidth).
19343
19801
  */
19344
- let { profile = null, collapsed = false, width = 400, embedded = false, currentSessionId, paneledSessionIds = [], captureContainer = null, onToggleCollapse} = $$props;
19802
+ let { profile = null, collapsed = false, width = 400, embedded = false, currentSessionId, paneledSessionIds = [], captureContainer = null, trayContainer = null, glassRows = [], panelWidth = 730, zoom = 1, onresizeall, onToggleCollapse, onResizeStart } = $$props;
19345
19803
  let filter = loadSessionFilter(profile);
19346
- if (collapsed) {
19347
- $$renderer.push("<!--[0-->");
19348
- $$renderer.push(`<div data-svelte="AppSidebar" class="rail-collapsed svelte-113gpvg" data-testid="app-sidebar-collapsed">`);
19349
- RestartButton($$renderer, { class: "rail-collapsed-label" });
19350
- $$renderer.push(`<!----> `);
19351
- AboutButton($$renderer, { class: "rail-collapsed-label" });
19352
- $$renderer.push(`<!----> <button type="button" class="stub-expand svelte-113gpvg"${attr("aria-expanded", false)}${attr("aria-label", t(expandsidebar1))}${attr("title", t(expandsidebar1))}>`);
19353
- Panel_left_close($$renderer, {
19354
- size: 14,
19355
- class: "rotate-180"
19356
- });
19357
- $$renderer.push(`<!----> <span class="rail-collapsed-label svelte-113gpvg">Deepseek Insight</span></button></div>`);
19358
- } else if (embedded) {
19359
- $$renderer.push("<!--[1-->");
19360
- $$renderer.push(`<aside data-svelte="AppSidebar" class="rail rail-embedded svelte-113gpvg" data-testid="app-sidebar">`);
19361
- AppSidebarHeader($$renderer, {
19362
- captureContainer,
19363
- onToggleCollapse
19364
- });
19365
- $$renderer.push(`<!----> `);
19366
- SidebarSessions($$renderer, {
19367
- currentSessionId,
19368
- paneledSessionIds,
19369
- filter,
19370
- onfilterchange: (f) => filter = f
19371
- });
19372
- $$renderer.push(`<!----></aside>`);
19373
- } else {
19374
- $$renderer.push("<!--[-1-->");
19375
- $$renderer.push(`<div data-svelte="AppSidebar" class="rail-wrap svelte-113gpvg" data-testid="app-sidebar"${attr_style(`width: ${stringify(width)}px`)}><aside class="rail svelte-113gpvg">`);
19376
- AppSidebarHeader($$renderer, {
19377
- captureContainer,
19378
- onToggleCollapse
19379
- });
19380
- $$renderer.push(`<!----> `);
19381
- SidebarSessions($$renderer, {
19382
- currentSessionId,
19383
- paneledSessionIds,
19384
- filter,
19385
- onfilterchange: (f) => filter = f
19386
- });
19387
- $$renderer.push(`<!----></aside> <div class="rail-gutter svelte-113gpvg" data-testid="sidebar-gutter" role="separator" aria-orientation="vertical"${attr("aria-label", t(resizesidebar1))}></div></div>`);
19804
+ let $$settled = true;
19805
+ let $$inner_renderer;
19806
+ function $$render_inner($$renderer) {
19807
+ if (collapsed) {
19808
+ $$renderer.push("<!--[0-->");
19809
+ $$renderer.push(`<div data-svelte="AppSidebar" class="rail-collapsed svelte-113gpvg" data-testid="app-sidebar-collapsed">`);
19810
+ ControlBar($$renderer, {
19811
+ onresizeall: onresizeall ?? (() => {}),
19812
+ captureContainer: trayContainer,
19813
+ get panelWidth() {
19814
+ return panelWidth;
19815
+ },
19816
+ set panelWidth($$value) {
19817
+ panelWidth = $$value;
19818
+ $$settled = false;
19819
+ },
19820
+ get zoom() {
19821
+ return zoom;
19822
+ },
19823
+ set zoom($$value) {
19824
+ zoom = $$value;
19825
+ $$settled = false;
19826
+ }
19827
+ });
19828
+ $$renderer.push(`<!----> `);
19829
+ RestartButton($$renderer, { class: "rail-collapsed-label" });
19830
+ $$renderer.push(`<!----> `);
19831
+ AboutButton($$renderer, { class: "rail-collapsed-label" });
19832
+ $$renderer.push(`<!----> <button type="button" class="stub-expand svelte-113gpvg"${attr("aria-expanded", false)}${attr("aria-label", t(expandsidebar1))}${attr("title", t(expandsidebar1))}>`);
19833
+ Panel_left_close($$renderer, {
19834
+ size: 14,
19835
+ class: "rotate-180"
19836
+ });
19837
+ $$renderer.push(`<!----> <span class="rail-collapsed-label svelte-113gpvg">Deepseek Insight</span></button></div>`);
19838
+ } else if (embedded) {
19839
+ $$renderer.push("<!--[1-->");
19840
+ $$renderer.push(`<aside data-svelte="AppSidebar" class="rail rail-embedded svelte-113gpvg" data-testid="app-sidebar">`);
19841
+ AppSidebarHeader($$renderer, {
19842
+ captureContainer,
19843
+ trayContainer,
19844
+ onresizeall,
19845
+ onToggleCollapse,
19846
+ get panelWidth() {
19847
+ return panelWidth;
19848
+ },
19849
+ set panelWidth($$value) {
19850
+ panelWidth = $$value;
19851
+ $$settled = false;
19852
+ },
19853
+ get zoom() {
19854
+ return zoom;
19855
+ },
19856
+ set zoom($$value) {
19857
+ zoom = $$value;
19858
+ $$settled = false;
19859
+ }
19860
+ });
19861
+ $$renderer.push(`<!----> `);
19862
+ SidebarSessions($$renderer, {
19863
+ currentSessionId,
19864
+ paneledSessionIds,
19865
+ filter,
19866
+ onfilterchange: (f) => filter = f
19867
+ });
19868
+ $$renderer.push(`<!----></aside>`);
19869
+ } else {
19870
+ $$renderer.push("<!--[-1-->");
19871
+ $$renderer.push(`<div data-svelte="AppSidebar" class="rail-wrap svelte-113gpvg" data-testid="app-sidebar"${attr_style(`width: ${stringify(width)}px`)}><aside class="rail svelte-113gpvg">`);
19872
+ AppSidebarHeader($$renderer, {
19873
+ captureContainer,
19874
+ trayContainer,
19875
+ onresizeall,
19876
+ onToggleCollapse,
19877
+ get panelWidth() {
19878
+ return panelWidth;
19879
+ },
19880
+ set panelWidth($$value) {
19881
+ panelWidth = $$value;
19882
+ $$settled = false;
19883
+ },
19884
+ get zoom() {
19885
+ return zoom;
19886
+ },
19887
+ set zoom($$value) {
19888
+ zoom = $$value;
19889
+ $$settled = false;
19890
+ }
19891
+ });
19892
+ $$renderer.push(`<!----> `);
19893
+ SidebarSessions($$renderer, {
19894
+ currentSessionId,
19895
+ paneledSessionIds,
19896
+ filter,
19897
+ glassRows,
19898
+ onfilterchange: (f) => filter = f
19899
+ });
19900
+ $$renderer.push(`<!----></aside> <div class="rail-gutter svelte-113gpvg" data-testid="sidebar-gutter" role="separator" aria-orientation="vertical"${attr("aria-label", t(resizesidebar1))}></div></div>`);
19901
+ }
19902
+ $$renderer.push(`<!--]-->`);
19388
19903
  }
19389
- $$renderer.push(`<!--]-->`);
19904
+ do {
19905
+ $$settled = true;
19906
+ $$inner_renderer = $$renderer.copy();
19907
+ $$render_inner($$inner_renderer);
19908
+ } while (!$$settled);
19909
+ $$renderer.subsume($$inner_renderer);
19910
+ bind_props($$props, {
19911
+ panelWidth,
19912
+ zoom
19913
+ });
19390
19914
  });
19391
19915
  }
19392
19916
  //#endregion
@@ -22213,6 +22737,25 @@ function PromptManagerPanel($$renderer, $$props) {
22213
22737
  function sessionOf(p) {
22214
22738
  return p.kind === "conversation" ? p.sessionId : null;
22215
22739
  }
22740
+ /**
22741
+ * The end index of the contiguous injected-doc tail following the index
22742
+ * "end" whose documents belong to "sourceSessionId" (Loadinjected ADR
22743
+ * D3, consolidated 2026-09-07). DOCUMENTS ONLY — deliberately NOT the
22744
+ * workspace tail (2026-09-10 order fix): a session child pinned under
22745
+ * this parent lands directly below it (the Lineage Pin) and pushes the
22746
+ * explorer/file pair right, keeping the floor order identical to the
22747
+ * sidebar's familyRenderOrder. The workspace pair stays contiguous via
22748
+ * unitOf/explorerUnit — it does not displace pinned children.
22749
+ */
22750
+ function docTailEnd(panels, end, sourceSessionId) {
22751
+ let i = end;
22752
+ while (i + 1 < panels.length) {
22753
+ const p = panels[i + 1];
22754
+ if (p.kind !== "injected-doc" || p.sourceSessionId !== sourceSessionId) break;
22755
+ i++;
22756
+ }
22757
+ return i;
22758
+ }
22216
22759
  /** Does the file panel's edge name this explorer — directly, or through
22217
22760
  * the legacy null edge restored before the edge existed (then adjacency
22218
22761
  * plus the shared session IS the edge; the dedupe keeps one explorer per
@@ -22343,13 +22886,31 @@ function blockBounds(panels, idx, facts) {
22343
22886
  if (head !== void 0 && head.kind === "workspace-explorer" && fileBelongsTo(at, head)) return explorerUnit(panels, i - 1);
22344
22887
  return [idx, idx];
22345
22888
  }
22346
- if (at.kind !== "injected-doc") return [idx, idx];
22347
- let i = idx;
22348
- while (i > 0 && panels[i - 1].kind === "injected-doc") i--;
22349
- const head = panels[i - 1];
22350
- if (head === void 0 || head.kind !== "conversation") return [idx, idx];
22351
- if (head.sessionId !== at.sourceSessionId) return [idx, idx];
22352
- sessionId = head.sessionId;
22889
+ if (at.kind === "browser") {
22890
+ let i = idx;
22891
+ while (i > 0 && panels[i - 1].kind === "browser") i--;
22892
+ const head = panels[i - 1];
22893
+ const inFamily = (id, root) => {
22894
+ if (id === void 0) return false;
22895
+ let cur = id;
22896
+ for (let guard = 0; guard < panels.length + 1 && cur !== null; guard++) {
22897
+ if (cur === root) return true;
22898
+ cur = facts.parentOf.get(cur) ?? null;
22899
+ }
22900
+ return false;
22901
+ };
22902
+ if (head === void 0 || head.kind !== "conversation" || !inFamily(head.sessionId, at.openedBy)) return [idx, idx];
22903
+ sessionId = head.sessionId;
22904
+ }
22905
+ if (at.kind !== "injected-doc" && sessionId === null) return [idx, idx];
22906
+ if (at.kind === "injected-doc") {
22907
+ let i = idx;
22908
+ while (i > 0 && panels[i - 1].kind === "injected-doc") i--;
22909
+ const head = panels[i - 1];
22910
+ if (head === void 0 || head.kind !== "conversation") return [idx, idx];
22911
+ if (head.sessionId !== at.sourceSessionId) return [idx, idx];
22912
+ sessionId = head.sessionId;
22913
+ }
22353
22914
  }
22354
22915
  for (let guard = 0; guard < panels.length + 1; guard++) {
22355
22916
  const parent = facts.parentOf.get(sessionId) ?? null;
@@ -22392,6 +22953,10 @@ function unitOf(panels, sessionId, facts) {
22392
22953
  end = i;
22393
22954
  continue;
22394
22955
  }
22956
+ if (p.kind === "browser" && p.openedBy !== void 0 && familySessions.has(p.openedBy)) {
22957
+ end = i;
22958
+ continue;
22959
+ }
22395
22960
  break;
22396
22961
  }
22397
22962
  return [start, end];
@@ -22660,7 +23225,27 @@ function panelRowsFor(input) {
22660
23225
  const { panels, spineRows, coldCache, deadSessions, edgeCache, lineageFacts, moveFacts } = input;
22661
23226
  const openSessionIds = openSessionIdsOf(panels);
22662
23227
  return panels.map((p, index) => {
22663
- if (p.kind === "terminal" || p.kind === "browser" || p.kind === "prompt-manager" || p.kind === "settings-editor" || p.kind === "skill-shelf" || p.kind === "plugin-rack") return {
23228
+ if (p.kind === "browser") {
23229
+ const parentOpen = p.openedBy !== void 0 && openSessionIds.has(p.openedBy);
23230
+ const openerDepth = p.openedBy !== void 0 ? lineageFacts.depthById.get(p.openedBy) ?? 0 : 0;
23231
+ return {
23232
+ panel: p,
23233
+ title: "Browser",
23234
+ workspace: null,
23235
+ dead: false,
23236
+ running: false,
23237
+ depth: parentOpen ? openerDepth + 1 : 0,
23238
+ runningDescendants: 0,
23239
+ kind: "panel",
23240
+ parentSessionId: p.openedBy ?? null,
23241
+ fork: false,
23242
+ spawnerTitle: null,
23243
+ siblingIndex: -1,
23244
+ canMoveUp: false,
23245
+ canMoveDown: false
23246
+ };
23247
+ }
23248
+ if (p.kind === "terminal" || p.kind === "prompt-manager" || p.kind === "settings-editor" || p.kind === "skill-shelf" || p.kind === "plugin-rack") return {
22664
23249
  panel: p,
22665
23250
  title: p.kind === "terminal" ? "Terminal" : p.kind === "browser" ? "Browser" : p.kind === "prompt-manager" ? MANAGER_ROW_TITLE : p.kind === "skill-shelf" ? "Skill Shelf" : p.kind === "plugin-rack" ? "Plugin Rack" : p.kind === "settings-editor" ? SETTINGS_ROW_TITLE[p.target] : "",
22666
23251
  workspace: null,
@@ -26047,9 +26632,10 @@ function hasDotDotSegment(path) {
26047
26632
  function parseFileLinkHref(href) {
26048
26633
  const value = stripAngleWrapper(href.trim());
26049
26634
  const hash = value.indexOf("#");
26050
- const raw = hash === -1 ? value : value.slice(0, hash);
26635
+ const raw = hash === -1 ? splitColonAnchor(value).path : value.slice(0, hash);
26051
26636
  const anchor = hash === -1 ? "" : value.slice(hash + 1);
26052
26637
  let lines = null;
26638
+ if (hash === -1) lines = splitColonAnchor(value).lines;
26053
26639
  const single = anchor.match(/^L?(\d+)$/);
26054
26640
  const range = anchor.match(/^L?(\d+)-L?(\d+)$/);
26055
26641
  if (single) {
@@ -26074,6 +26660,48 @@ function parseFileLinkHref(href) {
26074
26660
  /** CommonMark angle-bracket destinations (<path> and their %3C/%3E
26075
26661
  * encodings) are destination syntax, never path bytes - renderers that
26076
26662
  * hand the wrapper through must not have it reach the host read. */
26663
+ /**
26664
+ * The colon anchor arm (ADR-0028 The Colon Anchor): IDE-style :line
26665
+ * suffixes — path/file.ts:334, :10-20 — are the SAME fact as #L anchors
26666
+ * in IDE clothes, so they split into the display hint the same way.
26667
+ * Digits-only tail after the LAST colon; a malformed tail (:0, :20-10)
26668
+ * degrades to no hint and KEEPS the colon in the path (never guesses,
26669
+ * hash-grammar posture); any other colon stays path bytes (a:b/c.ts).
26670
+ */
26671
+ function splitColonAnchor(value) {
26672
+ const colon = value.lastIndexOf(":");
26673
+ if (colon === -1) return {
26674
+ path: value,
26675
+ lines: null
26676
+ };
26677
+ const tail = value.slice(colon + 1);
26678
+ const single = tail.match(/^(\d+)$/);
26679
+ const range = tail.match(/^(\d+)-(\d+)$/);
26680
+ if (single) {
26681
+ const n = Number(single[1]);
26682
+ if (n >= 1) return {
26683
+ path: value.slice(0, colon),
26684
+ lines: {
26685
+ start: n,
26686
+ end: n
26687
+ }
26688
+ };
26689
+ } else if (range) {
26690
+ const s = Number(range[1]);
26691
+ const e = Number(range[2]);
26692
+ if (s >= 1 && e >= s) return {
26693
+ path: value.slice(0, colon),
26694
+ lines: {
26695
+ start: s,
26696
+ end: e
26697
+ }
26698
+ };
26699
+ }
26700
+ return {
26701
+ path: value,
26702
+ lines: null
26703
+ };
26704
+ }
26077
26705
  function stripAngleWrapper(value) {
26078
26706
  return value.replace(/^(?:%3C|<)/i, "").replace(/(?:%3E|>)$/i, "");
26079
26707
  }
@@ -26081,6 +26709,7 @@ function normalizeFileLinkPath(href) {
26081
26709
  let value = stripAngleWrapper(href.trim()).replace(/^\.\//, "").replace(/\/{2,}/g, "/");
26082
26710
  const hash = value.indexOf("#");
26083
26711
  if (hash !== -1) value = value.slice(0, hash);
26712
+ value = splitColonAnchor(value).path;
26084
26713
  if (value.startsWith("/")) value = value.slice(1);
26085
26714
  if (value === "" || hasDotDotSegment(value)) return null;
26086
26715
  return value;
@@ -27484,7 +28113,7 @@ function ReasoningSection($$renderer, $$props) {
27484
28113
  $$renderer.push(`<!--]--> <span class="text-[10px]">${escape_html(t(thinklabel1))}</span> `);
27485
28114
  if (streaming) {
27486
28115
  $$renderer.push("<!--[0-->");
27487
- SessionStatus($$renderer, { running: true });
28116
+ SessionStatus($$renderer, { });
27488
28117
  } else $$renderer.push("<!--[-1-->");
27489
28118
  $$renderer.push(`<!--]--></button></div>`);
27490
28119
  } else $$renderer.push("<!--[-1-->");
@@ -32155,18 +32784,6 @@ function TerminalDesk($$renderer, $$props) {
32155
32784
  bind_props($$props, { applyAction });
32156
32785
  });
32157
32786
  }
32158
- //#endregion
32159
- //#region src/lib/services/panels/browser-session-resolver.ts
32160
- /**
32161
- * browser-session-resolver — the ONE re-bind-or-open seam (The Sticky Glass
32162
- * spec Wave 2 task 2.1; ADR-0030 D1/D4). The chip, the floor placement, and
32163
- * the panel's remount all ask here; none of them POSTs /api/browser open
32164
- * directly. Policy: the persisted localStorage id wins if the registry still
32165
- * lists it live (RE-BIND — reload survival); a dead id falls through to a
32166
- * fresh open, never an error surface. The resolved id is persisted so the
32167
- * NEXT reload re-binds too.
32168
- */
32169
- var LS_KEY = "dsi.browser.sessionId";
32170
32787
  /** localStorage guarded — SSR and privacy modes have no window.localStorage. */
32171
32788
  function defaultStorage() {
32172
32789
  try {
@@ -32175,23 +32792,62 @@ function defaultStorage() {
32175
32792
  return null;
32176
32793
  }
32177
32794
  }
32178
- function readPersistedId(storage = defaultStorage()) {
32179
- try {
32180
- return storage?.getItem(LS_KEY) ?? null;
32181
- } catch {
32182
- return null;
32183
- }
32184
- }
32185
32795
  var IDX_KEY = "dsi.browser.panelIndex";
32186
- function readPersistedIndex(storage = defaultStorage()) {
32796
+ /** Read the persisted glass list; every legacy shape upgrades, junk
32797
+ * drops. Sorted by floor position (restore order = floor order). */
32798
+ function readBrowserSlots(storage = defaultStorage()) {
32187
32799
  try {
32188
32800
  const raw = storage?.getItem(IDX_KEY);
32189
- const n = raw === null || raw === void 0 ? NaN : Number(raw);
32190
- return Number.isInteger(n) && n >= 0 ? n : null;
32801
+ if (raw === null || raw === void 0) return [];
32802
+ const parsed = JSON.parse(raw);
32803
+ const list = Array.isArray(parsed) ? parsed : parsed !== null && typeof parsed === "object" && Array.isArray(parsed.slots) ? parsed.slots : [parsed];
32804
+ const out = [];
32805
+ for (const item of list) {
32806
+ if (typeof item === "number") {
32807
+ if (Number.isInteger(item) && item >= 0) out.push({ index: item });
32808
+ continue;
32809
+ }
32810
+ if (item === null || typeof item !== "object") continue;
32811
+ const o = item;
32812
+ if (!Number.isInteger(o.index) || o.index < 0) continue;
32813
+ const slot = { index: o.index };
32814
+ if (typeof o.width === "number" && Number.isFinite(o.width) && o.width > 0) slot.width = o.width;
32815
+ if (typeof o.openedBy === "string" && o.openedBy.length > 0) slot.openedBy = o.openedBy;
32816
+ if (typeof o.sessionId === "string" && o.sessionId.length > 0) slot.sessionId = o.sessionId;
32817
+ if (o.folded === true) slot.folded = true;
32818
+ out.push(slot);
32819
+ }
32820
+ return out.sort((a, b) => a.index - b.index);
32191
32821
  } catch {
32192
- return null;
32822
+ return [];
32193
32823
  }
32194
32824
  }
32825
+ /** Write the whole glass list; an empty list clears the key. Also
32826
+ * mirrors the written list into the reactive store — the sidebar's
32827
+ * folded-glass rows derive from it (ADR-0037 D4). */
32828
+ function persistBrowserSlots(slots, storage = defaultStorage()) {
32829
+ try {
32830
+ if (slots.length > 0) storage?.setItem(IDX_KEY, JSON.stringify(slots));
32831
+ else storage?.removeItem(IDX_KEY);
32832
+ } catch {}
32833
+ browserSlotStore.set([...slots]);
32834
+ }
32835
+ /** Reactive mirror of the persisted glass list (Svelte store — the
32836
+ * plain .ts module stays rune-free). Rows and the route derive the
32837
+ * folded set from it; persistBrowserSlots is the only writer. */
32838
+ var browserSlotStore = writable([]);
32839
+ /** Seed the mirror from storage (boot). */
32840
+ function primeBrowserSlotStore(storage = defaultStorage()) {
32841
+ browserSlotStore.set(readBrowserSlots(storage));
32842
+ }
32843
+ //#endregion
32844
+ //#region src/lib/components/browser/BrowserClosed.svelte
32845
+ function BrowserClosed($$renderer, $$props) {
32846
+ $$renderer.component(($$renderer) => {
32847
+ let { closing = false} = $$props;
32848
+ $$renderer.push(`<button data-svelte="BrowserClosed" class="rounded px-1 hover:bg-surface-active" data-testid="browse-close"${attr("disabled", closing, true)}>✕</button>`);
32849
+ });
32850
+ }
32195
32851
  //#endregion
32196
32852
  //#region src/lib/components/browser/BrowserHeader.svelte
32197
32853
  function BrowserHeader($$renderer, $$props) {
@@ -32209,7 +32865,10 @@ function BrowserHeader($$renderer, $$props) {
32209
32865
  $$renderer.push("<!--[0-->");
32210
32866
  $$renderer.push(`<span class="rounded-t-md border border-b-0 border-surface-border bg-surface-elevated px-2 py-0.5 font-mono text-[10px] opacity-80" data-testid="browser-session-tab">${escape_html(sessionId.slice(0, 8))}</span>`);
32211
32867
  } else $$renderer.push("<!--[-1-->");
32212
- $$renderer.push(`<!--]--> <button class="rounded px-1 hover:bg-surface-active" data-testid="panel-close"${attr("disabled", closing, true)}>✕</button></span></div>`);
32868
+ $$renderer.push(`<!--]--> `);
32869
+ BrowserClosed($$renderer, {
32870
+ closing});
32871
+ $$renderer.push(`<!----></span></div>`);
32213
32872
  bind_props($$props, { clearInput });
32214
32873
  });
32215
32874
  }
@@ -32284,6 +32943,7 @@ function createMoveCoalescer(raf, fire) {
32284
32943
  //#region src/lib/components/browser/BrowserPanel.svelte
32285
32944
  function BrowserPanel($$renderer, $$props) {
32286
32945
  $$renderer.component(($$renderer) => {
32946
+ let { onclose, openedBy = void 0, parentTitle = null, depth = 0} = $$props;
32287
32947
  let sessionId = null;
32288
32948
  /** True when mount re-bound to a surviving session (ADR-0030 D1) —
32289
32949
  * surfaced in the status strip for the operator. */
@@ -32354,11 +33014,16 @@ function BrowserPanel($$renderer, $$props) {
32354
33014
  function goHistory(dir) {
32355
33015
  proxy();
32356
33016
  }
32357
- /** D8 owner-release: close the SERVER session first (kills the browser),
32358
- * then report upward so the floor removes the panel. Exactly once —
32359
- * the closing flag guards double-invocations (the single-close-post
32360
- * unit contract). */
32361
- async function closePanel() {}
33017
+ /** The FOLD (ADR-0037 D1): the header close button only reports
33018
+ * upward - the route's onclose is removePanel, the panel leaves the
33019
+ * floor, and the SERVER SESSION STAYS ALIVE (the reaper is the
33020
+ * bound; the slot entry keeps the re-bind facts). The old
33021
+ * owner-release kill (POST /close on this click, ADR-0029 D8) is
33022
+ * retired from this surface: kills belong to the reaper and
33023
+ * cascade close now. */
33024
+ function fold() {
33025
+ onclose?.();
33026
+ }
32362
33027
  onDestroy(() => {});
32363
33028
  let $$settled = true;
32364
33029
  let $$inner_renderer;
@@ -32372,12 +33037,22 @@ function BrowserPanel($$renderer, $$props) {
32372
33037
  busy: proxyBusy,
32373
33038
  canBack,
32374
33039
  canForward,
32375
- onclose: () => void closePanel(),
33040
+ onclose: () => fold(),
32376
33041
  onnavigate: submitUrl,
32377
33042
  onreload: () => void proxy(),
32378
33043
  onhistory: goHistory
32379
33044
  });
32380
33045
  $$renderer.push(`<!----> `);
33046
+ if (openedBy) {
33047
+ $$renderer.push("<!--[0-->");
33048
+ $$renderer.push(`<div${attr_class("browser-provenance flex items-center gap-1 border-b border-surface-border px-2 py-0.5 text-[10px] text-surface-muted svelte-283365", void 0, { "depth-tinted": depth > 0 })} data-testid="browser-provenance"${attr("title", openedBy)}><span class="opacity-70">⌥</span> <span class="truncate">${escape_html(parentTitle ?? openedBy)}</span> `);
33049
+ if (depth > 0) {
33050
+ $$renderer.push("<!--[0-->");
33051
+ $$renderer.push(`<span class="opacity-60">·d${escape_html(depth)}</span>`);
33052
+ } else $$renderer.push("<!--[-1-->");
33053
+ $$renderer.push(`<!--]--></div>`);
33054
+ } else $$renderer.push("<!--[-1-->");
33055
+ $$renderer.push(`<!--]--> `);
32381
33056
  BrowserContent($$renderer, {
32382
33057
  status,
32383
33058
  failReason,
@@ -32412,6 +33087,18 @@ function BrowserPanel($$renderer, $$props) {
32412
33087
  });
32413
33088
  }
32414
33089
  //#endregion
33090
+ //#region src/lib/components/browser/BrowserManager.svelte
33091
+ function BrowserManager($$renderer, $$props) {
33092
+ let { onclose, openedBy = void 0, parentTitle = null, depth = 0} = $$props;
33093
+ $$renderer.push(`<div data-svelte="BrowserManager" class="panel-manager-body svelte-1qpx9ce" data-testid="panel-browser-slot">`);
33094
+ BrowserPanel($$renderer, {
33095
+ onclose,
33096
+ openedBy,
33097
+ parentTitle,
33098
+ depth});
33099
+ $$renderer.push(`<!----></div>`);
33100
+ }
33101
+ //#endregion
32415
33102
  //#region src/lib/components/settings-skills/SettingsSkillsToolbar.svelte
32416
33103
  function SettingsSkillsToolbar($$renderer, $$props) {
32417
33104
  $$renderer.component(($$renderer) => {
@@ -32932,303 +33619,6 @@ function InjectedDocPanel($$renderer, $$props) {
32932
33619
  });
32933
33620
  }
32934
33621
  //#endregion
32935
- //#region src/lib/utils/panel-prefs.ts
32936
- /** localStorage base key — one JSON blob `{ panels, selectedPanelId, panelWidth, zoom }`.
32937
- * A workspace profile (`?profile=…` on the URL) suffixes EVERY dsi-* key
32938
- * (see storage-profile.ts): the default desk lives under `dsi-panels`,
32939
- * profile `widi` under `dsi-panels_widi`. */
32940
- var PANEL_PREFS_KEY = "dsi-panels";
32941
- /**
32942
- * The storage key for a workspace profile.
32943
- * @param profile sanitized profile segment, or null/undefined for the
32944
- * default desk (`dsi-panels`).
32945
- */
32946
- function panelPrefsKey(profile) {
32947
- return dsiKey(PANEL_PREFS_KEY, profile);
32948
- }
32949
- var PANEL_WORKSPACE_FILE_MAX_WIDTH = 1436;
32950
- /** The single treePct bound site — sanitize a stored or dragged value
32951
- * into [TREE_PCT_MIN, TREE_PCT_MAX]; junk (non-finite) falls back to
32952
- * TREE_PCT_DEFAULT. Mirrors clampPanelZoom's discipline. */
32953
- function clampTreePct(pct) {
32954
- if (typeof pct !== "number" || !Number.isFinite(pct)) return 30;
32955
- return Math.max(15, Math.min(70, Math.round(pct)));
32956
- }
32957
- /**
32958
- * The single panel-width bound site — load-side sanitizing, gutter drags,
32959
- * and the width slider all route through here. Bounds read the config
32960
- * store (layout load has landed them before any mount; defaults before).
32961
- */
32962
- function clampPanelWidth(width, kind) {
32963
- const { minWidth, maxWidth } = appConfig().panel;
32964
- if (kind === "workspace-explorer") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
32965
- if (kind === "workspace-file") return Math.max(520, Math.min(PANEL_WORKSPACE_FILE_MAX_WIDTH, width));
32966
- return Math.max(minWidth, Math.min(maxWidth, width));
32967
- }
32968
- /** The single zoom bound site — load-side sanitizing and the zoom slider. */
32969
- function clampPanelZoom(zoom) {
32970
- const { minZoom, maxZoom } = appConfig().panel;
32971
- return Math.max(minZoom, Math.min(maxZoom, zoom));
32972
- }
32973
- /** Config-driven default width (clamped through the single bound site). */
32974
- function defaultPanelWidth() {
32975
- return clampPanelWidth(appConfig().panel.defaultWidth);
32976
- }
32977
- /**
32978
- * The skill shelf's born width (The Shelf Chrome ADR, D5): a name list,
32979
- * not a five-column table — the shelf pins 480 (DEFAULT_PANEL_MIN_WIDTH)
32980
- * instead of the 730 conversation default, clamped through the single
32981
- * bound site.
32982
- */
32983
- function shelfPanelWidth() {
32984
- return clampPanelWidth(480);
32985
- }
32986
- /**
32987
- * Clamp a stored explorer `expanded` list to sane paths (Workspace
32988
- * Explorer restore): non-string members drop and a repeated path keeps
32989
- * its FIRST occurrence — the tree treats `expanded` as a set of
32990
- * root-relative directories, and a duplicate would double-render a row.
32991
- */
32992
- function clampExpanded(value) {
32993
- if (!Array.isArray(value)) return [];
32994
- const seen = /* @__PURE__ */ new Set();
32995
- const out = [];
32996
- for (const member of value) {
32997
- if (typeof member !== "string" || seen.has(member)) continue;
32998
- seen.add(member);
32999
- out.push(member);
33000
- }
33001
- return out;
33002
- }
33003
- /** The terminal desk's structure clamp (Terminal Desk ADR D3, Wave 3):
33004
- * sanitize a stored desk mirror into { tabs, selectedTab } or undefined.
33005
- * Junk drops FIELD-WISE — non-string sessionIds drop, empty/junk tabs
33006
- * drop, selectedTab clamps into range — so a stale blob degrades to
33007
- * "fewer rows remembered", never a phantom row. All tabs dropped (or
33008
- * junk at the top) ⇒ undefined: the desk falls back to one fresh row. */
33009
- function clampDeskMirror(value) {
33010
- if (value === null || typeof value !== "object") return void 0;
33011
- const raw = value;
33012
- if (!Array.isArray(raw.tabs)) return void 0;
33013
- const tabs = [];
33014
- for (const tab of raw.tabs) {
33015
- if (tab === null || typeof tab !== "object") continue;
33016
- const ids = tab.sessionIds;
33017
- if (!Array.isArray(ids)) continue;
33018
- const sessionIds = ids.filter((id) => typeof id === "string" && id.length > 0);
33019
- if (sessionIds.length === 0) continue;
33020
- tabs.push({ sessionIds });
33021
- }
33022
- const jobs = [];
33023
- if (Array.isArray(raw.jobs)) for (const j of raw.jobs) {
33024
- if (j === null || typeof j !== "object") continue;
33025
- const owner = j.owner;
33026
- const jobId = j.jobId;
33027
- if (typeof owner !== "string" || typeof jobId !== "string") continue;
33028
- if (owner.length === 0 || jobId.length === 0) continue;
33029
- jobs.push({
33030
- owner,
33031
- jobId
33032
- });
33033
- }
33034
- if (tabs.length === 0 && jobs.length === 0) return void 0;
33035
- const maxIndex = tabs.length + jobs.length;
33036
- return {
33037
- tabs,
33038
- selectedTab: typeof raw.selectedTab === "number" && Number.isFinite(raw.selectedTab) ? Math.max(0, Math.min(maxIndex, Math.round(raw.selectedTab))) : 0,
33039
- ...jobs.length > 0 ? { jobs } : {}
33040
- };
33041
- }
33042
- /** Persisted defaults when nothing (usable) is stored. */
33043
- function defaultPanelPrefs() {
33044
- return {
33045
- panels: [],
33046
- selectedPanelId: null,
33047
- panelWidth: defaultPanelWidth(),
33048
- zoom: 1,
33049
- treePct: 30
33050
- };
33051
- }
33052
- /**
33053
- * Hard shape check for a stored panel entry (the parse boundary).
33054
- * KIND (2026-09-06, ADR D6): absent or 'conversation' is the legacy shape —
33055
- * `sessionId` required; 'prompt-manager' carries no session fields;
33056
- * 'injected-doc' (Loadinjected ADR D2) requires non-empty `sourceSessionId`
33057
- * + `displayPath` — the dedupe key. Any other kind value is hard junk
33058
- * (entry drops). `agentPreset` and `width`
33059
- * may be absent (defaulted later); `agentPreset` when present must be
33060
- * string|null, `width` when present must be a finite number. Unknown
33061
- * extra keys are fine — they survive sanitizing.
33062
- */
33063
- function isPanelEntry(value) {
33064
- if (value === null || typeof value !== "object") return false;
33065
- const entry = value;
33066
- if (typeof entry.id !== "string" || entry.id.length === 0) return false;
33067
- if (entry.kind !== void 0 && entry.kind !== "conversation" && entry.kind !== "prompt-manager" && entry.kind !== "settings-editor" && entry.kind !== "injected-doc" && entry.kind !== "skill-shelf" && entry.kind !== "plugin-rack" && entry.kind !== "workspace-explorer" && entry.kind !== "workspace-file" && entry.kind !== "terminal") return false;
33068
- if (entry.kind === "settings-editor" && entry.target !== "dsi" && entry.target !== "dsh") return false;
33069
- if (entry.kind === "workspace-explorer") {
33070
- if (entry.sessionId !== null && (typeof entry.sessionId !== "string" || entry.sessionId.length === 0)) return false;
33071
- if (entry.sessionId === null && entry.home !== "dsi" && entry.home !== "dsh") return false;
33072
- }
33073
- if (entry.kind === "workspace-file") {
33074
- if (typeof entry.sessionId !== "string" || entry.sessionId.length === 0) return false;
33075
- }
33076
- if (entry.kind === "workspace-explorer" && typeof entry.root !== "string") return false;
33077
- if (entry.kind === "workspace-file") {
33078
- if (typeof entry.path !== "string" || entry.path.length === 0) return false;
33079
- }
33080
- if (entry.kind === "injected-doc") {
33081
- if (typeof entry.sourceSessionId !== "string" || entry.sourceSessionId.length === 0) return false;
33082
- if (typeof entry.displayPath !== "string" || entry.displayPath.length === 0) return false;
33083
- }
33084
- if (entry.kind !== "prompt-manager" && entry.kind !== "settings-editor" && entry.kind !== "skill-shelf" && entry.kind !== "plugin-rack" && entry.kind !== "injected-doc" && entry.kind !== "workspace-explorer" && entry.kind !== "workspace-file" && entry.kind !== "terminal") {
33085
- if (typeof entry.sessionId !== "string" || entry.sessionId.length === 0) return false;
33086
- if (entry.agentPreset !== void 0 && typeof entry.agentPreset !== "string" && entry.agentPreset !== null) return false;
33087
- }
33088
- if (entry.width !== void 0 && (typeof entry.width !== "number" || !Number.isFinite(entry.width))) return false;
33089
- return true;
33090
- }
33091
- /** Drop hard-junk entries; default missing keys; clamp widths. Unknown
33092
- * keys on surviving entries pass through untouched.
33093
- *
33094
- * Duplicate ids (2026-08-24 crash fix): a corrupted blob carrying the
33095
- * same panel-N twice would reach the keyed each in SidebarOpenPanels
33096
- * and crash Svelte (each_key_duplicate) at render — the each's key is
33097
- * the panel id. Duplicates drop to the FIRST occurrence (order is the
33098
- * floor's left-to-right truth); a selectedPanelId left dangling by the
33099
- * dedupe re-sanitizes to the first panel in loadPanelPrefs. */
33100
- function sanitizePanels(raw) {
33101
- if (!Array.isArray(raw)) return [];
33102
- const seen = /* @__PURE__ */ new Set();
33103
- return raw.filter((entry) => isPanelEntry(entry)).map((entry) => {
33104
- const kindDefault = entry.kind === "skill-shelf" ? shelfPanelWidth() : entry.kind === "plugin-rack" ? shelfPanelWidth() : entry.kind === "workspace-explorer" ? 850 : entry.kind === "workspace-file" ? 850 : defaultPanelWidth();
33105
- const width = clampPanelWidth(typeof entry.width === "number" && Number.isFinite(entry.width) ? entry.width : kindDefault, entry.kind);
33106
- if (entry.kind === "prompt-manager") return {
33107
- id: entry.id,
33108
- kind: "prompt-manager",
33109
- width
33110
- };
33111
- if (entry.kind === "terminal") {
33112
- const desk = clampDeskMirror(entry.desk);
33113
- return {
33114
- id: entry.id,
33115
- kind: "terminal",
33116
- ...desk ? { desk } : {},
33117
- width
33118
- };
33119
- }
33120
- if (entry.kind === "settings-editor") return {
33121
- id: entry.id,
33122
- kind: "settings-editor",
33123
- target: entry.target,
33124
- width
33125
- };
33126
- if (entry.kind === "skill-shelf") {
33127
- let reload = null;
33128
- const r = entry.reload;
33129
- if (r && r.phase === "loading" && typeof r.startedAt === "number") reload = {
33130
- phase: "loading",
33131
- startedAt: r.startedAt
33132
- };
33133
- else if (r && r.phase === "done" && typeof r.startedAt === "number" && typeof r.doneAt === "number" && r.doneAt > Date.now()) reload = {
33134
- phase: "done",
33135
- startedAt: r.startedAt,
33136
- doneAt: r.doneAt
33137
- };
33138
- return {
33139
- id: entry.id,
33140
- kind: "skill-shelf",
33141
- tab: entry.tab === "uninstall" ? "uninstall" : "install",
33142
- collapsed: clampExpanded(entry.collapsed),
33143
- searchQ: typeof entry.searchQ === "string" ? entry.searchQ : "",
33144
- reload,
33145
- width
33146
- };
33147
- }
33148
- if (entry.kind === "plugin-rack") return {
33149
- id: entry.id,
33150
- kind: "plugin-rack",
33151
- tab: entry.tab === "uninstall" ? "uninstall" : "install",
33152
- width
33153
- };
33154
- if (entry.kind === "injected-doc") return {
33155
- id: entry.id,
33156
- kind: "injected-doc",
33157
- sourceSessionId: entry.sourceSessionId,
33158
- displayPath: entry.displayPath,
33159
- width
33160
- };
33161
- if (entry.kind === "workspace-explorer") {
33162
- const home = entry.home === "dsi" || entry.home === "dsh" ? entry.home : void 0;
33163
- return {
33164
- id: entry.id,
33165
- kind: "workspace-explorer",
33166
- sessionId: entry.sessionId ?? null,
33167
- root: entry.root,
33168
- ...typeof entry.title === "string" && entry.title.length > 0 ? { title: entry.title } : {},
33169
- ...home !== void 0 ? { home } : {},
33170
- expanded: clampExpanded(entry.expanded),
33171
- tab: entry.tab === "changes" ? "changes" : "explorer",
33172
- collapsedRepos: clampExpanded(entry.collapsedRepos),
33173
- openTabs: clampExpanded(entry.openTabs),
33174
- activeFile: typeof entry.activeFile === "string" ? entry.activeFile : null,
33175
- width
33176
- };
33177
- }
33178
- if (entry.kind === "workspace-file") return {
33179
- id: entry.id,
33180
- kind: "workspace-file",
33181
- sessionId: entry.sessionId,
33182
- path: entry.path,
33183
- explorerPanelId: typeof entry.explorerPanelId === "string" && entry.explorerPanelId.length > 0 ? entry.explorerPanelId : null,
33184
- view: entry.view === "diff" ? "diff" : "edit",
33185
- width
33186
- };
33187
- return {
33188
- ...entry,
33189
- id: entry.id,
33190
- kind: "conversation",
33191
- sessionId: entry.sessionId,
33192
- agentPreset: entry.agentPreset ?? null,
33193
- width
33194
- };
33195
- }).filter((entry) => {
33196
- if (seen.has(entry.id)) return false;
33197
- seen.add(entry.id);
33198
- return true;
33199
- });
33200
- }
33201
- /**
33202
- * Read the workspace state from localStorage.
33203
- * @param profile sanitized workspace profile (null/omitted = default desk).
33204
- * @returns stored state when parseable (sanitized), defaults when absent,
33205
- * junk, or SSR. A `selectedPanelId` pointing at a missing panel
33206
- * sanitizes to the first panel's id (null when no panels remain).
33207
- */
33208
- function loadPanelPrefs(profile) {
33209
- if (typeof localStorage === "undefined") return defaultPanelPrefs();
33210
- try {
33211
- const raw = localStorage.getItem(panelPrefsKey(profile));
33212
- if (!raw) return defaultPanelPrefs();
33213
- const parsed = JSON.parse(raw);
33214
- if (parsed === null || typeof parsed !== "object") return defaultPanelPrefs();
33215
- if (!Array.isArray(parsed.panels)) return defaultPanelPrefs();
33216
- const panels = sanitizePanels(parsed.panels);
33217
- const panelWidth = typeof parsed.panelWidth === "number" && Number.isFinite(parsed.panelWidth) ? clampPanelWidth(parsed.panelWidth) : defaultPanelWidth();
33218
- const zoom = typeof parsed.zoom === "number" && Number.isFinite(parsed.zoom) ? clampPanelZoom(parsed.zoom) : 1;
33219
- const treePct = typeof parsed.treePct === "number" && Number.isFinite(parsed.treePct) ? clampTreePct(parsed.treePct) : 30;
33220
- return {
33221
- panels,
33222
- selectedPanelId: typeof parsed.selectedPanelId === "string" && panels.some((panel) => panel.id === parsed.selectedPanelId) ? parsed.selectedPanelId : panels[0]?.id ?? null,
33223
- panelWidth,
33224
- zoom,
33225
- treePct
33226
- };
33227
- } catch {
33228
- return defaultPanelPrefs();
33229
- }
33230
- }
33231
- //#endregion
33232
33622
  //#region src/lib/components/workspace-explorer/WorkspaceExplorerButtons.svelte
33233
33623
  function WorkspaceExplorerButtons($$renderer, $$props) {
33234
33624
  $$renderer.component(($$renderer) => {
@@ -34134,6 +34524,19 @@ function PanelsZoom($$renderer, $$props) {
34134
34524
  });
34135
34525
  }
34136
34526
  //#endregion
34527
+ //#region src/lib/components/panels/PanelClose.svelte
34528
+ function PanelClose($$renderer, $$props) {
34529
+ $$renderer.component(($$renderer) => {
34530
+ let { sessionId, lens = false} = $$props;
34531
+ $$renderer.push(`<button data-svelte="PanelClose" type="button" class="hdr-btn"${attr("disabled", lens, true)} data-testid="panel-close"${attr("aria-label", t(() => closepanelnamed2({ id: sessionId })))}${attr("title", t(closepanelhint2))}>`);
34532
+ X($$renderer, {
34533
+ size: 12,
34534
+ "aria-hidden": "true"
34535
+ });
34536
+ $$renderer.push(`<!----></button>`);
34537
+ });
34538
+ }
34539
+ //#endregion
34137
34540
  //#region src/lib/components/panels/PanelHeader.svelte
34138
34541
  function PanelHeader($$renderer, $$props) {
34139
34542
  $$renderer.component(($$renderer) => {
@@ -34183,12 +34586,9 @@ function PanelHeader($$renderer, $$props) {
34183
34586
  $$renderer.push(`<!--]--> `);
34184
34587
  if (onremove || lens) {
34185
34588
  $$renderer.push("<!--[0-->");
34186
- $$renderer.push(`<button type="button" class="hdr-btn svelte-1xanvl9"${attr("disabled", lens, true)} data-testid="panel-close"${attr("aria-label", t(() => closepanelnamed2({ id: sessionId })))}${attr("title", t(closepanelhint2))}>`);
34187
- X($$renderer, {
34188
- size: 12,
34189
- "aria-hidden": "true"
34190
- });
34191
- $$renderer.push(`<!----></button>`);
34589
+ PanelClose($$renderer, {
34590
+ sessionId,
34591
+ lens});
34192
34592
  } else $$renderer.push("<!--[-1-->");
34193
34593
  $$renderer.push(`<!--]--></span></div>`);
34194
34594
  });
@@ -34329,70 +34729,6 @@ function EmptyFloor($$renderer, $$props) {
34329
34729
  });
34330
34730
  }
34331
34731
  //#endregion
34332
- //#region src/lib/components/panels/control-bar/ControlBar.svelte
34333
- function ControlBar($$renderer, $$props) {
34334
- $$renderer.component(($$renderer) => {
34335
- /**
34336
- * ControlBar — the floor's hover tray (Panel Floor W5 task 5.1,
34337
- * ADR-0006 R7; OCI ControlBar precedent, minus its orchestration
34338
- * organs). Pinned top-right over the panels viewport; hovering the
34339
- * gear trigger reveals the tray, leaving hides it after 250ms (the
34340
- * timer clears on re-enter, so grazing the tray keeps it open).
34341
- *
34342
- * This file owns the trigger + the reveal/hide lifecycle; the tray
34343
- * body — the floor controls + the action-button row — is
34344
- * ControlBarTray:
34345
- * - AddPanel — paste a sessionId → two verbs: Add appends a new
34346
- * panel beside the focus, Rplc replaces the ACTIVE
34347
- * panel in place (both registry actions)
34348
- * - SliderWidth — uniform-width preset 480..860 (`onresizeall` sets
34349
- * every panel + the preset LIVE per thumb move)
34350
- * - SliderZoom — floor zoom 25%..125% step 5 ($bindable zoom)
34351
- * - TrayButtons — the tray's button row (the floor canvas-capture:
34352
- * `captureContainer` flows the page-owned floor
34353
- * element down to its CanvasCopyButton)
34354
- *
34355
- * Deliberately ABSENT (ADR-0006 R7): RegisterFeature and a home link —
34356
- * DSI has no orchestration backend for the former, and the sidebar
34357
- * owns Home for the latter. The OCI tray ships both; this floor does not.
34358
- *
34359
- * Communication: AddPanel fires `addPanelFromSidebar` /
34360
- * `replaceSelectedFromRegistry` through the panel registry — the
34361
- * tray is a LEAF like a spine row, and the
34362
- * registry is the only leaf→root action channel (commitment 2). Zoom
34363
- * is a `$bindable` prop the route owns; every bound routes through
34364
- * panel-prefs clamps (one clamp site, commitment 3). `onresizeall` is
34365
- * a ControlBar-specific prop: the tray is a DIRECT child of the
34366
- * workspace route (not a sidebar intermediate), so prop wiring here
34367
- * breaks no commitment.
34368
- */
34369
- let { panelWidth = 730, zoom = 1, onresizeall, captureContainer = null } = $$props;
34370
- let open = false;
34371
- let $$settled = true;
34372
- let $$inner_renderer;
34373
- function $$render_inner($$renderer) {
34374
- $$renderer.push(`<div data-svelte="ControlBar"${attr_class("controlbar svelte-vs008k", void 0, { "open": open })} role="toolbar"${attr("aria-label", t(floorcontrols1))} aria-orientation="vertical" tabindex="-1" data-testid="controlbar"${attr("data-open", open)}><button type="button" class="trigger svelte-vs008k" data-testid="controlbar-trigger"${attr("aria-label", t(floorcontrols1))}${attr("aria-expanded", open)}>`);
34375
- Settings($$renderer, {
34376
- size: 14,
34377
- "aria-hidden": "true"
34378
- });
34379
- $$renderer.push(`<!----></button> `);
34380
- $$renderer.push("<!--[-1-->");
34381
- $$renderer.push(`<!--]--></div>`);
34382
- }
34383
- do {
34384
- $$settled = true;
34385
- $$inner_renderer = $$renderer.copy();
34386
- $$render_inner($$inner_renderer);
34387
- } while (!$$settled);
34388
- $$renderer.subsume($$inner_renderer);
34389
- bind_props($$props, {
34390
- panelWidth,
34391
- zoom
34392
- });
34393
- });
34394
- }
34395
- //#endregion
34396
34732
  //#region src/lib/utils/sidebar-prefs.ts
34397
34733
  /**
34398
34734
  * Sidebar preferences — persisted layout state for the conversation page's
@@ -34617,6 +34953,34 @@ function focusedInsertionSlot(panels, selectedIndex, parentOf) {
34617
34953
  const headIndex = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === head);
34618
34954
  return headIndex >= 0 ? Math.min(headIndex, focus) : focus;
34619
34955
  }
34956
+ /**
34957
+ * Lineage-order slot below an OPEN spawner (operator spec, 2026-08-28):
34958
+ * after the spawner AND after every already-adopted sibling that PRECEDES
34959
+ * the child in wire order — plus those siblings' own panels (an adopted
34960
+ * subtree travels whole). Ghost clicks and sub-agent replacements keep the
34961
+ * family region in the ghost list's order regardless of arrival sequence:
34962
+ * adopting sub2 then sub3 lands |A|sub2|sub3|, a late sub1 slots back to
34963
+ * |A|sub1|sub2|sub3|.
34964
+ */
34965
+ function lineagePinnedSlot(panels, childrenByParent, parentSessionId, childSessionId) {
34966
+ let base = panels.findIndex((p) => p.kind === "conversation" && p.sessionId === parentSessionId) + 1;
34967
+ base = docTailEnd(panels, base - 1, parentSessionId) + 1;
34968
+ const siblings = childrenByParent.get(parentSessionId) ?? [];
34969
+ const mine = siblings.findIndex((s) => s.sessionId === childSessionId);
34970
+ const closure = new Set(siblings.slice(0, mine < 0 ? siblings.length : mine).map((s) => s.sessionId));
34971
+ const stack = [...closure];
34972
+ while (stack.length > 0) for (const k of childrenByParent.get(stack.pop()) ?? []) if (!closure.has(k.sessionId)) {
34973
+ closure.add(k.sessionId);
34974
+ stack.push(k.sessionId);
34975
+ }
34976
+ let index = base;
34977
+ while (index < panels.length) {
34978
+ const at = panels[index];
34979
+ if (at.kind !== "conversation" || !closure.has(at.sessionId)) break;
34980
+ index++;
34981
+ }
34982
+ return index;
34983
+ }
34620
34984
  //#endregion
34621
34985
  //#region src/lib/services/panels/floor-registry/chrome.ts
34622
34986
  /**
@@ -34650,14 +35014,15 @@ function openWorkspaceExplorer(sessionId, workspace) {
34650
35014
  * browser panel across the floor — the terminal singleton rule (Terminal
34651
35015
  * Desk ADR D2). A click on an open browser FOCUSES it; a miss inserts at
34652
35016
  * the focused slot. */
34653
- function openBrowserPanel(index, keepSelection = false) {
34654
- const open = st.panels.find((p) => p.kind === "browser");
35017
+ function openBrowserPanel(index, keepSelection = false, width, openedBy, sessionId) {
35018
+ const open = st.panels.find((p) => p.kind === "browser" && p.openedBy === openedBy);
34655
35019
  if (open) {
34656
35020
  st.selectedPanelId = open.id;
34657
35021
  return;
34658
35022
  }
34659
35023
  const prevSelected = st.selectedPanelId;
34660
- insertPanel(makeBrowserPanel(), index === void 0 ? insertionSlot() : Math.max(0, Math.min(index, st.panels.length)));
35024
+ const slot = index !== void 0 ? Math.max(0, Math.min(index, st.panels.length)) : openedBy !== void 0 && d$4().getLineageFacts().childrenByParent ? pinnedSlotIndex(openedBy, "browser") : insertionSlot();
35025
+ insertPanel(makeBrowserPanel(width, openedBy, sessionId), slot);
34661
35026
  if (keepSelection && prevSelected !== null && prevSelected !== void 0) {
34662
35027
  if (st.panels.some((p) => p.id === prevSelected)) st.selectedPanelId = prevSelected;
34663
35028
  }
@@ -34828,6 +35193,9 @@ function removePanel(panelId, opts = {}) {
34828
35193
  }
34829
35194
  }
34830
35195
  const doomedPanelIds = new SvelteSet();
35196
+ if (opts.family !== false && rootSessionId !== null) {
35197
+ for (const p of st.panels) if (p.kind === "browser" && p.openedBy !== void 0 && doomed.has(p.openedBy)) doomedPanelIds.add(p.id);
35198
+ }
34831
35199
  if (opts.family !== false && root.kind === "workspace-explorer") {
34832
35200
  for (const p of st.panels) if (p.kind === "workspace-file" && (p.explorerPanelId === root.id || p.explorerPanelId === null && p.sessionId === root.sessionId)) doomedPanelIds.add(p.id);
34833
35201
  }
@@ -34924,11 +35292,18 @@ function makePanel(sessionId, agentPreset) {
34924
35292
  width: clampPanelWidth(d$1().getPanelWidth())
34925
35293
  };
34926
35294
  }
34927
- function makeBrowserPanel() {
35295
+ /** width: the reload-restored panel re-mints at the width the operator
35296
+ * dragged before (browser-slot persistence, RCA 2026-10-22); absent for
35297
+ * chip clicks and legacy slots — the honest-slider preset.
35298
+ * openedBy: the opener session's provenance, recorded ONCE at spawn and
35299
+ * never rewritten (ADR-0036 D1/D2); undefined = no conversation context. */
35300
+ function makeBrowserPanel(width, openedBy, sessionId) {
34928
35301
  return {
34929
35302
  id: nextPanelId(),
34930
35303
  kind: "browser",
34931
- width: clampPanelWidth(d$1().getPanelWidth())
35304
+ width: clampPanelWidth(width ?? d$1().getPanelWidth()),
35305
+ ...openedBy !== void 0 ? { openedBy } : {},
35306
+ ...sessionId !== void 0 ? { browserSessionId: sessionId } : {}
34932
35307
  };
34933
35308
  }
34934
35309
  function makeWorkspaceExplorerPanel(sessionId, root) {
@@ -34950,6 +35325,9 @@ function insertPanel(entry, index) {
34950
35325
  function insertionSlot() {
34951
35326
  return focusedInsertionSlot(st.panels, selectedIndex_(), d$1().getMoveFacts().parentOf);
34952
35327
  }
35328
+ function pinnedSlotIndex(parentSessionId, childSessionId) {
35329
+ return lineagePinnedSlot(st.panels, d$1().getLineageFacts().childrenByParent, parentSessionId, childSessionId);
35330
+ }
34953
35331
  //#endregion
34954
35332
  //#region src/lib/services/panels/floor-registry.svelte.ts
34955
35333
  var deps = null;
@@ -35166,6 +35544,7 @@ function seedColdOf(data) {
35166
35544
  //#region src/routes/+page.svelte
35167
35545
  function _page($$renderer, $$props) {
35168
35546
  $$renderer.component(($$renderer) => {
35547
+ var $$store_subs;
35169
35548
  /**
35170
35549
  * Workspace route — the floor SHELL (ADR-0019 W3): configure the
35171
35550
  * floor-registry dep seam once, seed/restore through initFloor,
@@ -35199,16 +35578,30 @@ function _page($$renderer, $$props) {
35199
35578
  initFloor(seededInitially ? [makePanel(seedSessionId(), seedCold?.agentPreset ?? null)] : initialPrefs.panels, seededInitially ? null : initialPrefs.selectedPanelId);
35200
35579
  if (seededInitially) selectPanel(floor.panels[0]?.id ?? null);
35201
35580
  if (seededInitially && seedCold !== void 0) pageFeeds.primeSeed(seedSessionId(), seedCold);
35202
- (async () => {
35581
+ async function restoreBrowserGlasses() {
35203
35582
  try {
35204
- const persisted = readPersistedId();
35205
- if (!persisted) return;
35206
35583
  if (floor.panels.some((panel) => panel.kind === "browser")) return;
35207
35584
  const res = await fetch("/api/browser");
35208
35585
  if (!res.ok) return;
35209
- if ((await res.json()).sessions?.some((s) => s.id === persisted)) openBrowserPanel(readPersistedIndex() ?? void 0, true);
35586
+ const body = await res.json();
35587
+ const live = new Set((body.sessions ?? []).map((s) => s.id));
35588
+ const slots = readBrowserSlots();
35589
+ const dead = slots.filter((s) => s.sessionId && !live.has(s.sessionId));
35590
+ if (dead.length > 0) persistBrowserSlots(slots.filter((s) => !dead.includes(s)));
35591
+ for (const slot of slots) {
35592
+ if (slot.folded) continue;
35593
+ if (!slot.sessionId || !live.has(slot.sessionId)) continue;
35594
+ summonGlass(slot);
35595
+ }
35210
35596
  } catch {}
35211
- })();
35597
+ }
35598
+ /** Bring ONE folded glass back (the summon; the replace verb is the
35599
+ * same re-bind — the glass owns its slot identity, D4). */
35600
+ function summonGlass(glass) {
35601
+ openBrowserPanel(glass.index, true, glass.width, glass.openedBy, glass.sessionId);
35602
+ }
35603
+ primeBrowserSlotStore();
35604
+ restoreBrowserGlasses();
35212
35605
  const panels = derived(() => floor.panels);
35213
35606
  const selectedPanelId = derived(() => floor.selectedPanelId);
35214
35607
  const selected = derived(() => floor.selected);
@@ -35221,6 +35614,29 @@ function _page($$renderer, $$props) {
35221
35614
  const spineWorkspaces = derived(() => floorReads.spineWorkspaces);
35222
35615
  const lineageFacts = derived(() => floorReads.lineageFacts);
35223
35616
  const conversationPanels = derived(() => floorReads.conversationPanels);
35617
+ const allFoldedGlassRows = derived(() => store_get($$store_subs ??= {}, "$browserSlotStore", browserSlotStore).filter((s) => s.folded && s.sessionId && s.openedBy).map((s) => ({
35618
+ panel: {
35619
+ id: "glass-" + s.sessionId,
35620
+ kind: "conversation",
35621
+ sessionId: s.sessionId,
35622
+ agentPreset: null,
35623
+ width: 0
35624
+ },
35625
+ title: "Browser — " + (panelTitleFor(s.openedBy) ?? s.openedBy),
35626
+ workspace: null,
35627
+ dead: false,
35628
+ running: true,
35629
+ depth: (lineageFacts().depthById.get(s.openedBy) ?? 0) + 1,
35630
+ runningDescendants: 0,
35631
+ kind: "ghost",
35632
+ parentSessionId: s.openedBy,
35633
+ siblingIndex: -1
35634
+ })));
35635
+ const spineGlassRows = derived(() => allFoldedGlassRows().filter((g) => !conversationPanels().some((p) => p.sessionId === g.parentSessionId)).map((g) => ({
35636
+ sessionId: g.panel.sessionId,
35637
+ openedBy: g.parentSessionId,
35638
+ title: g.title ?? "Browser"
35639
+ })));
35224
35640
  const sidebarPlacement = derived(() => appConfig().sidebar.placement);
35225
35641
  const sidebarInZoom = derived(() => sidebarPlacement() === "panels-zoom" && !pagePrefs.sidebarCollapsed);
35226
35642
  pageFeeds.installFeeds(() => conversationPanels().map((p) => p.sessionId));
@@ -35240,6 +35656,10 @@ function _page($$renderer, $$props) {
35240
35656
  function toggleSidebar() {
35241
35657
  pagePrefs.sidebarCollapsed = !pagePrefs.sidebarCollapsed;
35242
35658
  }
35659
+ function startSidebarResize(e) {
35660
+ e.preventDefault();
35661
+ e.clientX, pagePrefs.sidebarWidth;
35662
+ }
35243
35663
  function sidebarRail($$renderer) {
35244
35664
  AppSidebar($$renderer, {
35245
35665
  profile: activeProfile,
@@ -35249,7 +35669,26 @@ function _page($$renderer, $$props) {
35249
35669
  currentSessionId: selected()?.kind === "conversation" ? selected().sessionId : "",
35250
35670
  paneledSessionIds: conversationPanels().map((p) => p.sessionId),
35251
35671
  captureContainer: pageRoot,
35252
- onToggleCollapse: toggleSidebar});
35672
+ trayContainer: floorEl,
35673
+ glassRows: spineGlassRows(),
35674
+ onresizeall: resizeAll,
35675
+ onToggleCollapse: toggleSidebar,
35676
+ onResizeStart: startSidebarResize,
35677
+ get panelWidth() {
35678
+ return pagePrefs.panelWidth;
35679
+ },
35680
+ set panelWidth($$value) {
35681
+ pagePrefs.panelWidth = $$value;
35682
+ $$settled = false;
35683
+ },
35684
+ get zoom() {
35685
+ return pagePrefs.zoom;
35686
+ },
35687
+ set zoom($$value) {
35688
+ pagePrefs.zoom = $$value;
35689
+ $$settled = false;
35690
+ }
35691
+ });
35253
35692
  }
35254
35693
  function panelBody($$renderer, panel, floorPanelId, hostClose) {
35255
35694
  if (panel.kind === "terminal") {
@@ -35263,10 +35702,12 @@ function _page($$renderer, $$props) {
35263
35702
  $$renderer.push(`<!----></div>`);
35264
35703
  } else if (panel.kind === "browser") {
35265
35704
  $$renderer.push("<!--[1-->");
35266
- $$renderer.push(`<div class="panel-manager-body svelte-1uha8ag" data-testid="panel-browser-slot">`);
35267
- BrowserPanel($$renderer, {
35268
- browserSessionId: "browserSessionId" in panel ? panel.browserSessionId : void 0});
35269
- $$renderer.push(`<!----></div>`);
35705
+ BrowserManager($$renderer, {
35706
+ browserSessionId: "browserSessionId" in panel ? panel.browserSessionId : void 0,
35707
+ onclose: () => removePanel(panel.id),
35708
+ openedBy: panel.openedBy,
35709
+ parentTitle: panel.openedBy ? panelTitleFor(panel.openedBy) ?? panel.openedBy : null,
35710
+ depth: panel.openedBy ? lineageFacts().depthById.get(panel.openedBy) ?? 0 : 0});
35270
35711
  } else if (panel.kind === "injected-doc") {
35271
35712
  $$renderer.push("<!--[2-->");
35272
35713
  $$renderer.push(`<div class="panel-manager-body svelte-1uha8ag">`);
@@ -35384,7 +35825,7 @@ function _page($$renderer, $$props) {
35384
35825
  a2aRows: a2aRowsFor(p.sessionId),
35385
35826
  a2aTitles: spineTitles(),
35386
35827
  onOpenExplorer: openWorkspaceExplorer,
35387
- onOpenBrowser: lineageFacts().subagentIds.has(p.sessionId) ? void 0 : () => openBrowserPanel(),
35828
+ onOpenBrowser: lineageFacts().subagentIds.has(p.sessionId) ? void 0 : () => openBrowserPanel(void 0, false, void 0, p.sessionId),
35388
35829
  focusComposer: p.id === focusComposerId(),
35389
35830
  oncomposerfocus: () => clearFocusComposer(),
35390
35831
  focused: p.id === selectedPanelId()
@@ -35413,7 +35854,7 @@ function _page($$renderer, $$props) {
35413
35854
  a2aTitles: spineTitles(),
35414
35855
  onNeedCold: (sid) => void ensureCold(sid),
35415
35856
  onOpenExplorer: openWorkspaceExplorer,
35416
- onOpenBrowser: lineageFacts().subagentIds.has(p.sessionId) ? void 0 : () => openBrowserPanel(),
35857
+ onOpenBrowser: lineageFacts().subagentIds.has(p.sessionId) ? void 0 : () => openBrowserPanel(void 0, false, void 0, p.sessionId),
35417
35858
  focusComposer: p.id === focusComposerId(),
35418
35859
  oncomposerfocus: () => clearFocusComposer(),
35419
35860
  focused: p.id === selectedPanelId()
@@ -35437,26 +35878,7 @@ function _page($$renderer, $$props) {
35437
35878
  $$renderer.push("<!--[0-->");
35438
35879
  sidebarRail($$renderer);
35439
35880
  } else $$renderer.push("<!--[-1-->");
35440
- $$renderer.push(`<!--]--> `);
35441
- ControlBar($$renderer, {
35442
- onresizeall: resizeAll,
35443
- captureContainer: floorEl,
35444
- get panelWidth() {
35445
- return pagePrefs.panelWidth;
35446
- },
35447
- set panelWidth($$value) {
35448
- pagePrefs.panelWidth = $$value;
35449
- $$settled = false;
35450
- },
35451
- get zoom() {
35452
- return pagePrefs.zoom;
35453
- },
35454
- set zoom($$value) {
35455
- pagePrefs.zoom = $$value;
35456
- $$settled = false;
35457
- }
35458
- });
35459
- $$renderer.push(`<!----> <div class="floor svelte-1uha8ag">`);
35881
+ $$renderer.push(`<!--]--> <div class="floor svelte-1uha8ag">`);
35460
35882
  PanelsZoom($$renderer, {
35461
35883
  zoom: pagePrefs.zoom,
35462
35884
  panels: panels(),
@@ -35515,8 +35937,9 @@ function _page($$renderer, $$props) {
35515
35937
  $$render_inner($$inner_renderer);
35516
35938
  } while (!$$settled);
35517
35939
  $$renderer.subsume($$inner_renderer);
35940
+ if ($$store_subs) unsubscribe_stores($$store_subs);
35518
35941
  });
35519
35942
  }
35520
35943
 
35521
35944
  export { _page as default };
35522
- //# sourceMappingURL=_page.svelte.js-hjZYZCe1.js.map
35945
+ //# sourceMappingURL=_page.svelte.js-CKY8hz9T.js.map