@jxsuite/studio 5.5.0 → 5.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/LICENSE +1 -1
  2. package/dist/chunks/{2020-g8mwpbkq.js → 2020-cs2sb9t1.js} +3 -3
  3. package/dist/chunks/adapter-y5gdq19c.js +4 -0
  4. package/dist/chunks/cf-account-picker-882a7ecm.js +4 -0
  5. package/dist/chunks/cf-settings-20dznc3j.js +4 -0
  6. package/dist/chunks/components-q037dya7.js +4 -0
  7. package/dist/chunks/content-source-37x3vsap.js +4 -0
  8. package/dist/chunks/convert-to-repeater-grfep2w8.js +4 -0
  9. package/dist/chunks/editor-j5sq149q.js +4 -0
  10. package/dist/chunks/entry-commands-c5h3nqck.js +4 -0
  11. package/dist/chunks/extension-sections-dp8endhx.js +4 -0
  12. package/dist/chunks/file-ops-937cvqk6.js +4 -0
  13. package/dist/chunks/files-tznwe1p1.js +4 -0
  14. package/dist/chunks/format-host-e2pjvd25.js +4 -0
  15. package/dist/chunks/fs-events-fv64ak3s.js +4 -0
  16. package/dist/chunks/git-panel-gb713ttc.js +4 -0
  17. package/dist/chunks/grid-view-5amhg604.js +51 -0
  18. package/dist/chunks/grid-view-5mqv1cbd.css +1 -0
  19. package/dist/chunks/iframe-host-t819mcg5.js +4 -0
  20. package/dist/chunks/{index-arg188jq.js → index-9ydfe04y.js} +3 -3
  21. package/dist/chunks/index-m0x5ztax.js +4 -0
  22. package/dist/chunks/{javascript-zcdmye2k.js → javascript-d9pqgc2p.js} +2 -2
  23. package/dist/chunks/{jsonMode-6x5e1nbf.js → jsonMode-800yvbem.js} +5 -5
  24. package/dist/chunks/jx-validate-zc9zp77s.js +4 -0
  25. package/dist/chunks/layers-4bsr82wz.js +4 -0
  26. package/dist/chunks/layers-panel-b6shnaax.js +4 -0
  27. package/dist/chunks/lit-html-cfn61dn7.js +4 -0
  28. package/dist/chunks/media-picker-ej0kdh1j.js +4 -0
  29. package/dist/chunks/menu-rtxf9chb.js +4 -0
  30. package/dist/chunks/{monaco-binding-4az2kh5p.js → monaco-binding-62dtmgbf.js} +3 -3
  31. package/dist/chunks/monaco-buffer-nn4dtx27.js +4 -0
  32. package/dist/chunks/{monaco-setup-d7mwkcx3.js → monaco-setup-tj13ndtm.js} +3 -3
  33. package/dist/chunks/notify-ahs57a2z.js +4 -0
  34. package/dist/chunks/platform-v9xyfkbx.js +4 -0
  35. package/dist/chunks/publish-panel-bq36xcmj.js +4 -0
  36. package/dist/chunks/quick-search-zm00h9dh.js +4 -0
  37. package/dist/chunks/references-99ws0b0m.js +4 -0
  38. package/dist/chunks/right-panel-pq2f71q3.js +4 -0
  39. package/dist/chunks/section-registry-b2yzp9sw.js +4 -0
  40. package/dist/chunks/studio-01kbbqg3.js +928 -0
  41. package/dist/chunks/{studio-hf4qdjc8.js → studio-01qza1n3.js} +4 -4
  42. package/dist/chunks/{studio-pkkw32j0.js → studio-03wwfvyt.js} +4 -4
  43. package/dist/chunks/{studio-7gqyhjns.js → studio-0bbxhhce.js} +4 -4
  44. package/dist/chunks/studio-0k9mvpgj.js +14 -0
  45. package/dist/chunks/studio-0scebvb9.js +6 -0
  46. package/dist/chunks/studio-1a99w39s.js +5 -0
  47. package/dist/chunks/studio-1wvfxkyy.js +5 -0
  48. package/dist/chunks/studio-1x8x26k3.js +5 -0
  49. package/dist/chunks/studio-222rqyyc.js +5 -0
  50. package/dist/chunks/studio-4xppwncr.js +4 -0
  51. package/dist/chunks/{studio-xcng8nx7.js → studio-54g077x3.js} +5 -5
  52. package/dist/chunks/studio-56z7evx4.js +10 -0
  53. package/dist/chunks/studio-5e9zsamh.js +9 -0
  54. package/dist/chunks/studio-65fhspdd.js +5 -0
  55. package/dist/chunks/studio-693nhyky.js +5 -0
  56. package/dist/chunks/{studio-7jazdy6y.js → studio-7pgy3xaf.js} +4 -4
  57. package/dist/chunks/studio-888knnkc.js +5 -0
  58. package/dist/chunks/studio-9mqfamps.js +5 -0
  59. package/dist/chunks/studio-a7xqhc3n.js +5 -0
  60. package/dist/chunks/studio-aje966s9.js +5 -0
  61. package/dist/chunks/studio-aqmph6k4.js +5 -0
  62. package/dist/chunks/studio-c6w3tq7t.js +5 -0
  63. package/dist/chunks/{studio-6j2jn55a.js → studio-c6zdm25z.js} +8 -8
  64. package/dist/chunks/{studio-f8jyp45j.js → studio-c9d3pxf7.js} +4 -4
  65. package/dist/chunks/studio-ceb34dxj.js +5 -0
  66. package/dist/chunks/studio-cvbkkfqb.js +5 -0
  67. package/dist/chunks/studio-cxt150pj.js +7 -0
  68. package/dist/chunks/studio-dkn3x3hz.js +5 -0
  69. package/dist/chunks/studio-e6re3rk1.js +5 -0
  70. package/dist/chunks/{studio-5n1km79v.js → studio-edbtvcd1.js} +4 -4
  71. package/dist/chunks/{studio-xc8ebsts.js → studio-eh0rac07.js} +4 -4
  72. package/dist/chunks/{studio-625ezz59.js → studio-evwgp1ym.js} +4 -4
  73. package/dist/chunks/{studio-he2fhrgp.js → studio-f195h2hq.js} +4 -4
  74. package/dist/chunks/studio-fqq890ap.js +4 -0
  75. package/dist/chunks/studio-frb0qbf7.js +6 -0
  76. package/dist/chunks/studio-gfn1kpxj.js +6 -0
  77. package/dist/chunks/studio-gh97gngt.js +5 -0
  78. package/dist/chunks/{studio-awqghqxv.js → studio-gp02nqnw.js} +4 -4
  79. package/dist/chunks/{studio-x9sagq2d.js → studio-hjbg8d5y.js} +7 -7
  80. package/dist/chunks/studio-j6gnhvaa.js +5 -0
  81. package/dist/chunks/studio-mwxgyn9h.js +7 -0
  82. package/dist/chunks/{studio-x5esgzct.js → studio-n78gjfy8.js} +4 -4
  83. package/dist/chunks/studio-nbxqcgvf.js +5 -0
  84. package/dist/chunks/{studio-p1eggxcf.js → studio-nt06yybj.js} +4 -4
  85. package/dist/chunks/{studio-hc9q4j24.js → studio-p7c1agz0.js} +5 -5
  86. package/dist/chunks/{studio-8r4y2x0z.js → studio-pwqfmt4p.js} +4 -4
  87. package/dist/chunks/{studio-5wfrgee9.js → studio-r3kaka4d.js} +4 -4
  88. package/dist/chunks/studio-semd1s8s.js +5 -0
  89. package/dist/chunks/studio-skzanb0k.js +5 -0
  90. package/dist/chunks/studio-spf2skgc.js +5 -0
  91. package/dist/chunks/{studio-b4epb26c.js → studio-st75kn99.js} +7 -7
  92. package/dist/chunks/{studio-eq27rrr0.js → studio-sxc0zvpt.js} +3 -3
  93. package/dist/chunks/studio-t1eedsyx.js +4 -0
  94. package/dist/chunks/{studio-73czgk6a.js → studio-tp642n5g.js} +4 -4
  95. package/dist/chunks/studio-tw7rre6s.js +5 -0
  96. package/dist/chunks/{studio-7by9a93k.js → studio-tzegmy4w.js} +4 -4
  97. package/dist/chunks/studio-vavmwshr.js +5 -0
  98. package/dist/chunks/studio-wma2c6g8.js +5 -0
  99. package/dist/chunks/studio-x92tgnee.js +64 -0
  100. package/dist/chunks/studio-xk0z6msk.js +5 -0
  101. package/dist/chunks/{studio-c4pnt1c6.js → studio-xry7tk3z.js} +5 -5
  102. package/dist/chunks/studio-xta2vxp6.js +9 -0
  103. package/dist/chunks/{studio-21enxdr8.js → studio-xy3tv3az.js} +4 -4
  104. package/dist/chunks/studio-y7jehjrp.js +5 -0
  105. package/dist/chunks/{studio-k1eaqyzm.js → studio-yr04mv2j.js} +4 -4
  106. package/dist/chunks/{studio-mhhc9btt.js → studio-zd4kayn2.js} +4 -4
  107. package/dist/chunks/studio-zpnk016a.js +16 -0
  108. package/dist/chunks/{tsMode-rtwt7w1a.js → tsMode-k9s9fcag.js} +5 -5
  109. package/dist/chunks/workspace-b69mgm0e.js +4 -0
  110. package/dist/chunks/ws-client-0ja08haa.js +4 -0
  111. package/dist/iframe-entry.js +14 -14
  112. package/dist/manifest.json +62 -59
  113. package/dist/studio.css +1 -1
  114. package/dist/studio.js +121 -123
  115. package/package.json +5 -5
  116. package/src/account/action-flow.ts +294 -0
  117. package/src/account/action-required.ts +68 -0
  118. package/src/account-status.ts +29 -1
  119. package/src/browse/library-model.ts +3 -1
  120. package/src/browse/library-preview.ts +6 -2
  121. package/src/canvas/asset-refs.ts +3 -1
  122. package/src/canvas/asset-resolve.ts +79 -1
  123. package/src/canvas/iframe-entry.ts +38 -1
  124. package/src/canvas/iframe-host.ts +85 -0
  125. package/src/canvas/iframe-protocol.ts +13 -0
  126. package/src/canvas/stage-scrollbar.ts +7 -7
  127. package/src/files/asset-versions.ts +294 -0
  128. package/src/files/file-ops.ts +5 -8
  129. package/src/files/files.ts +61 -9
  130. package/src/files/fs-events.ts +279 -28
  131. package/src/files/media-meta.ts +2 -1
  132. package/src/files/media-paths.ts +5 -2
  133. package/src/files/media-upload.ts +5 -1
  134. package/src/new-project/add-repo-modal.ts +12 -1
  135. package/src/new-project/new-project-modal.ts +12 -1
  136. package/src/panels/git-panel.ts +25 -2
  137. package/src/panels/jump-bar.ts +132 -158
  138. package/src/panels/pane-context.ts +8 -8
  139. package/src/panels/pane-grid.ts +18 -26
  140. package/src/panels/style-panel.ts +2 -2
  141. package/src/platform-errors.ts +61 -1
  142. package/src/platforms/cloud.ts +389 -50
  143. package/src/platforms/devserver.ts +17 -1
  144. package/src/publish/pages-service.ts +17 -7
  145. package/src/services/ai-models.ts +30 -6
  146. package/src/services/ai-system-prompt.ts +81 -51
  147. package/src/services/context-manager.ts +41 -70
  148. package/src/services/document-assistant.ts +12 -3
  149. package/src/services/harness/chat-reducer.ts +115 -0
  150. package/src/services/harness/turn-hooks.ts +50 -0
  151. package/src/services/tool-executor.ts +69 -288
  152. package/src/services/tool-outcomes.ts +12 -10
  153. package/src/settings/preferences-accounts.ts +42 -3
  154. package/src/settings/preferences-dialog.ts +8 -1
  155. package/src/studio.ts +20 -6
  156. package/src/surfaces/block-action-bar.json +6 -1
  157. package/src/surfaces/dialog.ts +5 -0
  158. package/src/surfaces/jump-bar.json +9 -12
  159. package/src/surfaces/jump-bar.ts +15 -16
  160. package/src/surfaces/pane-context.json +2 -2
  161. package/src/surfaces/pane-grid.json +8 -25
  162. package/src/surfaces/pane-grid.ts +7 -8
  163. package/src/surfaces/statusbar.json +71 -3
  164. package/src/surfaces/statusbar.ts +66 -139
  165. package/src/surfaces/style-panel.json +3 -3
  166. package/src/surfaces/style-panel.ts +11 -1
  167. package/src/surfaces/welcome.json +86 -1
  168. package/src/surfaces/welcome.ts +61 -2
  169. package/src/types.ts +84 -2
  170. package/src/ui/ai-managed-connect.ts +41 -4
  171. package/src/ui/media-picker.ts +2 -1
  172. package/dist/chunks/adapter-bsbs78x9.js +0 -4
  173. package/dist/chunks/cf-account-picker-gnm7xeqb.js +0 -4
  174. package/dist/chunks/cf-settings-9y6jyscp.js +0 -4
  175. package/dist/chunks/components-s51h3b7c.js +0 -4
  176. package/dist/chunks/content-source-ty3be0qg.js +0 -4
  177. package/dist/chunks/convert-to-repeater-c3e7q61z.js +0 -4
  178. package/dist/chunks/editor-t002b41m.js +0 -4
  179. package/dist/chunks/entry-commands-sb72e9gy.js +0 -4
  180. package/dist/chunks/extension-sections-ktn2ntnj.js +0 -4
  181. package/dist/chunks/file-ops-78zeytkm.js +0 -4
  182. package/dist/chunks/files-gatzrz8v.js +0 -4
  183. package/dist/chunks/format-host-r59b1651.js +0 -4
  184. package/dist/chunks/fs-events-f8h91ndt.js +0 -4
  185. package/dist/chunks/git-panel-y12zf7nj.js +0 -4
  186. package/dist/chunks/grid-view-rfd8b2pg.js +0 -51
  187. package/dist/chunks/grid-view-tafenfm2.css +0 -1
  188. package/dist/chunks/iframe-host-vsrsj20g.js +0 -4
  189. package/dist/chunks/index-5aghyrfh.js +0 -4
  190. package/dist/chunks/jx-validate-653krkrf.js +0 -4
  191. package/dist/chunks/layers-32p8gtae.js +0 -4
  192. package/dist/chunks/layers-panel-rs58m105.js +0 -4
  193. package/dist/chunks/lit-html-54bmkwz3.js +0 -4
  194. package/dist/chunks/media-picker-5bb04wct.js +0 -4
  195. package/dist/chunks/menu-cbha7w2e.js +0 -4
  196. package/dist/chunks/monaco-buffer-9vbbyfr1.js +0 -4
  197. package/dist/chunks/notify-dc1nz5ar.js +0 -4
  198. package/dist/chunks/platform-2m49s1zf.js +0 -4
  199. package/dist/chunks/publish-panel-nx3ycmma.js +0 -4
  200. package/dist/chunks/quick-search-yzrzh9x3.js +0 -4
  201. package/dist/chunks/references-vx7mbj9f.js +0 -4
  202. package/dist/chunks/right-panel-7jzpbxja.js +0 -4
  203. package/dist/chunks/section-registry-gjryse41.js +0 -4
  204. package/dist/chunks/studio-3znfhcxg.js +0 -5
  205. package/dist/chunks/studio-58qam27v.js +0 -4
  206. package/dist/chunks/studio-6j59fwaw.js +0 -5
  207. package/dist/chunks/studio-7z69g1gx.js +0 -5
  208. package/dist/chunks/studio-93j3v6zx.js +0 -5
  209. package/dist/chunks/studio-9gf6v8zy.js +0 -5
  210. package/dist/chunks/studio-9rhmettf.js +0 -4
  211. package/dist/chunks/studio-ac5aq677.js +0 -10
  212. package/dist/chunks/studio-as65vbns.js +0 -5
  213. package/dist/chunks/studio-bkv0sga4.js +0 -6
  214. package/dist/chunks/studio-bttmqef4.js +0 -5
  215. package/dist/chunks/studio-c3y9d41k.js +0 -5
  216. package/dist/chunks/studio-ccegp7gz.js +0 -9
  217. package/dist/chunks/studio-chxabn98.js +0 -6
  218. package/dist/chunks/studio-cktcdsxz.js +0 -16
  219. package/dist/chunks/studio-cnspfke5.js +0 -5
  220. package/dist/chunks/studio-de3p1ktm.js +0 -5
  221. package/dist/chunks/studio-fsbcn530.js +0 -928
  222. package/dist/chunks/studio-gftga7a3.js +0 -64
  223. package/dist/chunks/studio-hf1rpcq0.js +0 -4
  224. package/dist/chunks/studio-hz02hq70.js +0 -5
  225. package/dist/chunks/studio-jnb285we.js +0 -5
  226. package/dist/chunks/studio-jsv3s3s1.js +0 -5
  227. package/dist/chunks/studio-ks7cksh0.js +0 -5
  228. package/dist/chunks/studio-nptx0dbs.js +0 -5
  229. package/dist/chunks/studio-pg0z7k98.js +0 -5
  230. package/dist/chunks/studio-pve0b2f2.js +0 -5
  231. package/dist/chunks/studio-q7zmjv3b.js +0 -6
  232. package/dist/chunks/studio-rpmvw7kj.js +0 -5
  233. package/dist/chunks/studio-rwhss52s.js +0 -5
  234. package/dist/chunks/studio-sbg3g3h0.js +0 -5
  235. package/dist/chunks/studio-sq1bn60g.js +0 -14
  236. package/dist/chunks/studio-vwz7rma2.js +0 -5
  237. package/dist/chunks/studio-wa55e8gf.js +0 -5
  238. package/dist/chunks/studio-x0mzdha2.js +0 -5
  239. package/dist/chunks/studio-xrgpakvc.js +0 -9
  240. package/dist/chunks/studio-xt3djk1w.js +0 -7
  241. package/dist/chunks/studio-yh3nggx1.js +0 -7
  242. package/dist/chunks/studio-zmecp6hq.js +0 -5
  243. package/dist/chunks/workspace-g6dvaete.js +0 -4
  244. package/dist/chunks/ws-client-shf41j5p.js +0 -4
@@ -1,67 +1,79 @@
1
1
  /// <reference lib="dom" />
2
2
  /**
3
- * The jump bar — the pane's address, and every place you can go from it.
3
+ * The jump bar — the focused pane's address, as a breadcrumb trail along the foot of the window.
4
4
  *
5
- * **The two half-breadcrumbs this replaces.** Studio grew two trails, neither of which could say
5
+ * **Where it lives, and why it moved.** The trail is the status bar's DOCUMENT field: the file,
6
+ * then every ancestor of the selected element, leaf last, each one a button that selects it. That
7
+ * is the pattern the Gutenberg block editor taught a generation of authors, and it is where they
8
+ * look for it. It used to be a 10px row across the top of every pane, between the tab strip and the
9
+ * context bar, and in that position it read as part of the tab strip rather than as a control: the
10
+ * address was on screen and authors reported it missing. It also cost every pane 24px of stage
11
+ * height. At the foot it costs nothing, because the status bar's row is already there, and the
12
+ * field it replaced (the file path, a view name the context bar already prints, and a save word the
13
+ * tab strip's dirty marker and the Command Bar's Save button already state) was the bar's least
14
+ * useful content.
15
+ *
16
+ * **One bar, for the focused pane.** The status bar is one row for the whole window, so the trail
17
+ * addresses the pane that has focus, which is the pane the Inspector, the Outline and the keyboard
18
+ * already address. Clicking into the other pane re-points it.
19
+ *
20
+ * **The two half-breadcrumbs this replaced.** Studio grew two trails, neither of which could say
6
21
  * where you were:
7
22
  *
8
23
  * 1. `panels/pane-context.ts`'s `navTpl()` (the old `#tab-bar` breadcrumb, `tab-bar.ts:129-185`) — a
9
24
  * `Back` button plus one span per `session.documentStack` frame. It appeared ONLY while you were
10
- * inside a sub-document, printed the frame's file basename (which is the SAME basename for a
11
- * `$map` template, so it read `index.json › index.json`), and knew nothing about the project
12
- * above it or the selection below it. It is deleted, and so is the stack it walked: nothing ever
13
- * pushed a frame, so it could only ever draw its empty branch.
14
- * 2. `surfaces/statusbar.ts`'s selection field — a clickable ancestor trail, `selection.set` per
15
- * crumb. It appeared ONLY while something was selected, and knew nothing about the document it
16
- * was inside.
25
+ * inside a sub-document and knew nothing about the selection below it. It is deleted, and so is
26
+ * the stack it walked: nothing ever pushed a frame, so it could only ever draw its empty
27
+ * branch.
28
+ * 2. `surfaces/statusbar.ts`'s selection field — a clickable ancestor trail that appeared ONLY while
29
+ * something was selected and knew nothing about the document it was inside.
17
30
  *
18
- * Between them they never rendered at the same time as each other and never rendered the whole
19
- * address, so neither one was a place to look. **This bar is the whole chain**, always:
31
+ * This bar is the whole chain, always:
20
32
  *
21
33
  * ```text
22
- * ◈ Site › pages/blog/[slug].json › article › h1 ⌄
23
- * project file selection ancestors
34
+ * Jx Suite ⑂ main ‖ pages/blog/[slug].json › article › header › h1 ⌃ ‖ 3 selected
35
+ * PROJECT field ‖ this bar: file, then the selection's ancestors ‖ SELECTION field
24
36
  * ```
25
37
  *
26
- * **The status bar keeps only what an address cannot say.** `studio.md` §16.2 gives the bar AMBIENT
27
- * STATE, and an ancestor trail in a second place is exactly the duplication the shell redesign
28
- * exists to remove — three breadcrumbs would be worse than the two we started with. So the trail
29
- * moves here whole, and `statusbar/selection` keeps the two facts this bar cannot state: HOW MANY
30
- * things are selected (a count is not a path) and which Stylebook rule the Style panel is editing
31
- * (a selector is not a node).
38
+ * The project is not a step of the trail: the PROJECT field directly to its left already names it,
39
+ * with the same `project.openRecent` command, and two adjacent buttons saying one thing is the
40
+ * duplication studio-ui-guidelines.md §12.2's chrome budget forbids. `statusbar/selection` keeps
41
+ * the two facts a trail cannot state: HOW MANY things are selected (a count is not a path) and
42
+ * which Stylebook rule the Style panel is editing (a selector is not a node).
32
43
  *
33
44
  * **Every interactive item is a command, resolved from the registry.** There is no click handler in
34
45
  * this file that names behaviour: a segment names a command id and its args, and the registry
35
- * supplies the title, the chord, the enablement and the run. Three ids carry the whole bar —
36
- * `project.openRecent`, `palette.openFiles` and `selection.set` — and this file declares none of
37
- * them. It did declare a fourth, `document.setStackLevel`, for the sub-document stack; the stack
38
- * had no way in and both are gone.
46
+ * supplies the title, the chord, the enablement and the run. Two ids carry the whole bar —
47
+ * `palette.openFiles` (or `pane.pin` in a derived pane) and `selection.set` — and this file
48
+ * declares none of them.
39
49
  *
40
50
  * **A segment whose command the registry does not have becomes a readout, it does not disappear.**
41
- * That is the one place this bar deliberately differs from the status bar, where an unavailable
42
- * item vanishes. An address with a hole in it is a lie about containment; a readout is merely a
43
- * step you cannot take.
51
+ * That is the one place this bar deliberately differs from the status bar's own items, where an
52
+ * unavailable item vanishes. An address with a hole in it is a lie about containment; a readout is
53
+ * merely a step you cannot take.
44
54
  *
45
55
  * **This file is the FLOW; the markup is a document.** `surfaces/jump-bar.json` owns the bar's
46
- * structure, its ARIA and every value in its style, and `surfaces/jump-bar.ts` mounts one per pane
47
- * (studio-ui-guidelines.md §6, §9.3). What stays here is the part that is a decision: which pane
48
- * this bar is about, what its address is, which of its steps the registry can run, and what a
49
- * chevron opens — and the chevron opens the KIT MENU, `surfaces/menu.ts`, because a list of
50
- * commands is what that surface already is (studio-ui-guidelines.md §12.5).
56
+ * structure, its ARIA and every value in its style, and `surfaces/jump-bar.ts` mounts it into the
57
+ * host the status bar's document leaves for it (studio-ui-guidelines.md §6, §9.3). What stays here
58
+ * is the part that is a decision: which pane this bar is about, what its address is, which of its
59
+ * steps the registry can run, and what a chevron opens — and the chevron opens the KIT MENU,
60
+ * `surfaces/menu.ts`, because a list of commands is what that surface already is
61
+ * (studio-ui-guidelines.md §12.5).
62
+ *
63
+ * @docs studio/interface
51
64
  */
52
65
 
53
66
  import { displayTagName } from "@jxsuite/schema/guards";
54
67
  import { childList, getNodeAtPath, nodeLabel, pathsEqual, projectState } from "../store";
55
68
  import { effect, effectScope } from "../reactivity";
56
- import { PRIMARY_PANE, workspace } from "../workspace/workspace";
69
+ import { activePane, workspace } from "../workspace/workspace";
57
70
  import { derivationOfPane, tabOfPane } from "../canvas/canvas-surface";
58
- import { paneRegion } from "../ui/regions";
59
71
  import { primarySelection } from "../tabs/selection";
60
72
  import { activeRegistry } from "../commands/active-registry";
61
73
  import { runReported } from "../commands/run-reported";
62
74
  import { openMenu } from "../surfaces/menu";
75
+ import { rectOf } from "../utils/geometry";
63
76
  import { mountJumpBarSurface } from "../surfaces/jump-bar";
64
- import { PANE_SELECTOR } from "../surfaces/pane-grid";
65
77
  import type { CommandArgs, CommandRegistry } from "../commands/registry";
66
78
  import type { FormulaEditDef, FunctionEditDef } from "../types";
67
79
  import type { JxPath } from "../state";
@@ -71,11 +83,17 @@ import type { Tab } from "../tabs/tab";
71
83
  import type { PaneDerivation } from "../workspace/workspace";
72
84
  import type { EffectScope } from "@vue/reactivity";
73
85
 
74
- /** The CSS variable the stage is offset by while the jump bar is on screen. */
75
- export const JUMP_BAR_VAR = "--jump-bar-h";
86
+ /**
87
+ * The region the trail stamps on itself. It is the status bar's DOCUMENT field: the document, and
88
+ * where in it you are.
89
+ */
90
+ export const JUMP_BAR_REGION = "statusbar/document";
76
91
 
77
- /** The bar's height. Declared here because the offset projection and the document must agree. */
78
- const JUMP_BAR_HEIGHT = 24;
92
+ /**
93
+ * How a segment's alternatives hang from its chevron: ABOVE it, leading edges aligned. The bar sits
94
+ * on the window's floor, so the kit menu's default (below the opener) would open off screen.
95
+ */
96
+ const ABOVE_OPENER = "block-start span-inline-end";
79
97
 
80
98
  // ─── The model ───────────────────────────────────────────────────────────────
81
99
 
@@ -84,7 +102,7 @@ const JUMP_BAR_HEIGHT = 24;
84
102
  * step of the address without matching its rendered text, which is derived (the shot contract's R1,
85
103
  * `scripts/screenshots/README.md`).
86
104
  */
87
- export type JumpSegmentKind = "project" | "file" | "editor" | "node";
105
+ export type JumpSegmentKind = "file" | "editor" | "node";
88
106
 
89
107
  /** One alternative in a segment's dropdown. Each is a command, exactly like the segment itself. */
90
108
  export interface JumpChoice {
@@ -111,12 +129,12 @@ export interface JumpSegment {
111
129
  // ─── Labels ──────────────────────────────────────────────────────────────────
112
130
 
113
131
  /**
114
- * The document's path with the project root taken off — the root is the segment before it.
132
+ * The document's path with the project root taken off — the root is the PROJECT field to its left.
115
133
  *
116
134
  * Lives here rather than in `statusbar.ts`, where it started: the jump bar is the surface whose
117
- * whole job is naming the containment chain, and the status bar's DOCUMENT field is a second reader
118
- * of the same fact. Having the reader own it also kept `mock.module("surfaces/statusbar")` — which
119
- * six bootstrap tests do — from deciding whether the jump bar can name a file.
135
+ * whole job is naming the containment chain. Having the reader own it also keeps
136
+ * `mock.module("surfaces/statusbar")` — which six bootstrap tests do — from deciding whether the
137
+ * jump bar can name a file.
120
138
  */
121
139
  export function documentLabel(path: string | null): string {
122
140
  if (!path) {
@@ -235,22 +253,12 @@ export function jumpSegments(
235
253
  if (!tab) {
236
254
  return [];
237
255
  }
238
- const segments: JumpSegment[] = [];
239
- const project = projectState;
240
- if (project) {
241
- segments.push({
242
- choices: [],
243
- command: "project.openRecent",
244
- kind: "project",
245
- label: project.name,
246
- title: project.projectRoot,
247
- });
248
- }
249
-
250
- // ONE document per tab. This used to walk `session.documentStack` and emit a `subdocument`
251
- // Segment per frame, each one a `document.setStackLevel` you could click back to — but nothing
252
- // In `src/` ever pushed a frame, so the loop had exactly one iteration in the shipped app and the
253
- // Address it drew was always this line. A tab holds a document; drilling in opens another tab.
256
+ // ONE document per tab, and no project segment before it: the status bar's PROJECT field sits
257
+ // Directly to the left of this trail and already names the project, with the same
258
+ // `project.openRecent` command. The document segment used to follow a `subdocument` segment per
259
+ // `session.documentStack` frame, each one a `document.setStackLevel` you could click back to —
260
+ // But nothing in `src/` ever pushed a frame. A tab holds a document; drilling in opens another
261
+ // Tab.
254
262
  /* In a DERIVED pane the leading verb is Keep, not Open.
255
263
  The address is still the document — a lens draws the source pane's, a companion its own — but
256
264
  the question an author has about a pane that is following something is "can I stop it
@@ -258,13 +266,15 @@ export function jumpSegments(
258
266
  lens: `projectStep` already renders a step whose command is disabled as a disabled button with
259
267
  `disabledReason` in the tooltip, so the sentence explaining why Code, Diff and breakpoint views
260
268
  cannot be pinned arrives for free. */
261
- segments.push({
262
- choices: [],
263
- command: derived ? "pane.pin" : "palette.openFiles",
264
- kind: "file",
265
- label: documentLabel(tab.documentPath),
266
- title: tab.documentPath ?? "Not saved to disk yet",
267
- });
269
+ const segments: JumpSegment[] = [
270
+ {
271
+ choices: [],
272
+ command: derived ? "pane.pin" : "palette.openFiles",
273
+ kind: "file",
274
+ label: documentLabel(tab.documentPath),
275
+ title: tab.documentPath ?? "Not saved to disk yet",
276
+ },
277
+ ];
268
278
 
269
279
  // A logic editor is the leaf of the address, and it has no selection under it: the Monaco buffer
270
280
  // And the formula workspace are editing a definition, not a node of the document tree.
@@ -377,8 +387,8 @@ function projectStep(
377
387
 
378
388
  // ─── Rendering ───────────────────────────────────────────────────────────────
379
389
 
380
- /** One pane's bar: the document mounted in its cell, and the address it last drew. */
381
- interface PaneBar {
390
+ /** The bar: the document mounted in the status bar's slot, and the address it last drew. */
391
+ interface Bar {
382
392
  host: HTMLElement;
383
393
  surface: JumpBarSurface;
384
394
  /** The segments behind the projection, by key — what `run` and `choose` resolve against. */
@@ -386,14 +396,13 @@ interface PaneBar {
386
396
  }
387
397
 
388
398
  /**
389
- * Where each pane's bar is mounted. One entry per drawn cell.
399
+ * Where the bar is mounted, once the status bar has a slot for it.
390
400
  *
391
- * A Map rather than a `let _host`, because the bar addresses a PANE: it prints where you are, and
392
- * with two panes on screen there are two answers. `panels/pane-grid.ts` attaches a cell's
393
- * `.pane-jump` as the cell is built and detaches it as the cell is disposed, which is the same
394
- * hand-over `panels/frontmatter-panel.ts` takes from the stage.
401
+ * ONE bar, not one per pane: the status bar is one row for the window, so the trail addresses the
402
+ * focused pane. `surfaces/statusbar.ts` attaches its `trail` slot when its document mounts and
403
+ * detaches it when it is disposed.
395
404
  */
396
- const _bars = new Map<string, PaneBar>();
405
+ let _bar: Bar | null = null;
397
406
 
398
407
  let _scope: EffectScope | null = null;
399
408
 
@@ -414,6 +423,9 @@ export function dismissJumpMenu(): void {
414
423
  * nothing here draws a panel: `surfaces/menu.ts` owns the roving caret, typeahead, Escape and light
415
424
  * dismissal, because a list of commands is what that surface already is: a second one is the defect
416
425
  * studio-ui-guidelines.md §12.5 names.
426
+ *
427
+ * The menu hangs ABOVE the chevron. The bar is the window's floor, so there is nothing below it;
428
+ * `place` is the same answer in coordinates, for an engine that cannot anchor a popover.
417
429
  */
418
430
  function openChoices(segment: JumpSegment, anchor: HTMLElement | null): void {
419
431
  const registry = activeRegistry();
@@ -442,6 +454,8 @@ function openChoices(segment: JumpSegment, anchor: HTMLElement | null): void {
442
454
  }
443
455
  },
444
456
  opener: anchor,
457
+ placement: ABOVE_OPENER,
458
+ ...(anchor ? { place: (box: DOMRect) => aboveOpener(anchor, box) } : {}),
445
459
  region: "jump-bar",
446
460
  rows,
447
461
  });
@@ -449,33 +463,17 @@ function openChoices(segment: JumpSegment, anchor: HTMLElement | null): void {
449
463
  }
450
464
 
451
465
  /**
452
- * Keep the stage clear of the bar.
453
- *
454
- * The same one-projection-one-variable shape `pane-context.ts` uses for its own band, and for the
455
- * same reason: the stage's offset is the SUM of the two, so a pane with no tab open (no bar) costs
456
- * the welcome screen no dead band.
457
- *
458
- * **The variable is written on the PANE, not on `:root`** — when there is a pane to write it on.
459
- * Two cells have two bars and two stages, and one document-level number would offset both by
460
- * whichever pane painted last: a side pane showing a document would push the primary's welcome
461
- * screen down by 24px, and closing the last tab in one pane would lift the other's document under
462
- * its own bar. `.pane-stage` reads it from its cell by cascade. A host outside a cell (the tests,
463
- * and the bare offset callers) still writes the root, which is exactly what it meant before.
464
- *
465
- * @param {number} height Bar height in px. `0` when the pane has no address to print.
466
- * @param {HTMLElement | null} [host] The bar's host. Its cell takes the variable when it has one.
466
+ * The menu's corner when it hangs above `anchor`: its bottom on the anchor's top, leading edges
467
+ * aligned.
467
468
  */
468
- export function applyJumpBarOffset(height: number, host?: HTMLElement | null): void {
469
- const target = host?.closest<HTMLElement>(PANE_SELECTOR) ?? document.documentElement;
470
- target.style.setProperty(JUMP_BAR_VAR, `${height}px`);
469
+ export function aboveOpener(anchor: HTMLElement, box: DOMRect): { x: number; y: number } {
470
+ const rect = rectOf(anchor);
471
+ return { x: Math.round(rect.left), y: Math.max(0, Math.round(rect.top - box.height)) };
471
472
  }
472
473
 
473
- /**
474
- * The address one pane's document should be showing, and the segments behind it.
475
- *
476
- * @param {string} paneId
477
- */
478
- function projectPane(paneId: string): { steps: ProjectedStep[]; segments: JumpSegment[] } {
474
+ /** The address of the focused pane, and the segments behind it. */
475
+ function projectFocused(): { steps: ProjectedStep[]; segments: JumpSegment[] } {
476
+ const paneId = activePane().id;
479
477
  const segments = jumpSegments(tabOfPane(paneId), derivationOfPane(paneId));
480
478
  const registry = activeRegistry();
481
479
  return {
@@ -486,65 +484,52 @@ function projectPane(paneId: string): { steps: ProjectedStep[]; segments: JumpSe
486
484
  };
487
485
  }
488
486
 
489
- /**
490
- * Paint one pane's bar. Exported because the bootstrap paints once before mounting the effect.
491
- *
492
- * @param {string} [paneId] Defaults to every attached pane — what "render the jump bar" means when
493
- * the caller is a lifecycle rather than a pane.
494
- */
495
- export function renderJumpBar(paneId?: string): void {
496
- if (_bars.size === 0) {
487
+ /** Paint the bar. Exported because the bootstrap paints once before mounting the effect. */
488
+ export function renderJumpBar(): void {
489
+ if (!_bar) {
497
490
  return;
498
491
  }
499
492
  // The address changed, so an open menu is describing a place that may no longer be on the bar.
500
493
  // Repaints happen only when tracked state moves, so this cannot close a menu you are reading.
501
494
  dismissJumpMenu();
502
- for (const [id, bar] of _bars) {
503
- if (paneId !== undefined && id !== paneId) {
504
- continue;
505
- }
506
- const { segments, steps } = projectPane(id);
507
- bar.segments = new Map(steps.map((step, i) => [step.key, segments[i]!]));
508
- bar.surface.update(steps);
509
- applyJumpBarOffset(steps.length === 0 ? 0 : JUMP_BAR_HEIGHT, bar.host);
510
- }
495
+ const { segments, steps } = projectFocused();
496
+ _bar.segments = new Map(steps.map((step, i) => [step.key, segments[i]!]));
497
+ _bar.surface.update(steps);
511
498
  }
512
499
 
513
500
  /**
514
- * Give a pane's bar somewhere to paint, or take it away.
501
+ * Give the bar somewhere to paint, or take it away.
515
502
  *
516
- * Called by `panels/pane-grid.ts` as a cell is built and as it is disposed. Detaching disposes the
517
- * mount first: a cell being removed still has this bar's document in it, and the runtime that owns
518
- * that DOM is about to be unreachable.
503
+ * Called by `surfaces/statusbar.ts` as its document mounts and as it is disposed. Detaching
504
+ * disposes the mount first: the slot being removed still has this bar's document in it, and the
505
+ * runtime that owns that DOM is about to be unreachable.
519
506
  *
520
- * @param {string} paneId
521
507
  * @param {HTMLElement | null} host
522
508
  */
523
- export function attachJumpBarHost(paneId: string, host: HTMLElement | null): void {
524
- const previous = _bars.get(paneId);
525
- if (previous?.host === host) {
509
+ export function attachJumpBarHost(host: HTMLElement | null): void {
510
+ if (_bar?.host === host) {
526
511
  return;
527
512
  }
528
- if (previous) {
529
- previous.surface.dispose();
530
- applyJumpBarOffset(0, previous.host);
531
- _bars.delete(paneId);
513
+ if (_bar) {
514
+ dismissJumpMenu();
515
+ _bar.surface.dispose();
516
+ _bar = null;
532
517
  }
533
518
  if (!host) {
534
519
  return;
535
520
  }
536
- const bar: PaneBar = {
521
+ _bar = {
537
522
  host,
538
523
  segments: new Map(),
539
- surface: mountJumpBarSurface(host, paneRegion(paneId, "jump"), {
524
+ surface: mountJumpBarSurface(host, JUMP_BAR_REGION, {
540
525
  choose: (key, anchor) => {
541
- const segment = _bars.get(paneId)?.segments.get(key);
526
+ const segment = _bar?.segments.get(key);
542
527
  if (segment) {
543
528
  openChoices(segment, anchor);
544
529
  }
545
530
  },
546
531
  run: (key) => {
547
- const segment = _bars.get(paneId)?.segments.get(key);
532
+ const segment = _bar?.segments.get(key);
548
533
  const registry = activeRegistry();
549
534
  if (segment?.command && registry) {
550
535
  void runReported(registry, segment.command, segment.args, "Jump Bar");
@@ -552,22 +537,18 @@ export function attachJumpBarHost(paneId: string, host: HTMLElement | null): voi
552
537
  },
553
538
  }),
554
539
  };
555
- _bars.set(paneId, bar);
556
- renderJumpBar(paneId);
540
+ renderJumpBar();
557
541
  }
558
542
 
559
543
  /**
560
- * Subscribe the bar to the state it renders. Idempotent.
544
+ * Subscribe the bar to the state it renders. Idempotent: a second call replaces the effect.
561
545
  *
562
- * `host` is the PRIMARY pane's, the same bargain `panels/tab-strip.ts`'s `mount` makes: the
563
- * bootstrap holds the primary's cell and hands it over, and every other pane's host arrives from
564
- * the grid through {@link attachJumpBarHost}.
565
- *
566
- * @param {HTMLElement} host
546
+ * Called by `surfaces/statusbar.ts`'s `mountStatusbar`, which owns the bar's lifecycle because the
547
+ * trail is its DOCUMENT field. The host arrives separately ({@link attachJumpBarHost}), and either
548
+ * may come first: the effect paints whatever host is attached, and attaching paints at once.
567
549
  */
568
- export function mountJumpBar(host: HTMLElement): void {
569
- unmountJumpBar();
570
- attachJumpBarHost(PRIMARY_PANE, host);
550
+ export function mountJumpBar(): void {
551
+ _scope?.stop();
571
552
  _scope = effectScope();
572
553
  _scope.run(() => {
573
554
  effect(() => {
@@ -575,14 +556,11 @@ export function mountJumpBar(host: HTMLElement): void {
575
556
  // Reading it here is what repaints the bar from a skeleton into the real thing.
576
557
  void activeRegistry();
577
558
  void projectState?.name;
578
- /* EVERY pane's tab, not the focused one's. Two bars print two addresses, and the side pane's
579
- has to repaint when its own document moves — an effect that tracked `activeTab` alone left
580
- the unfocused bar frozen on whatever it last said. */
581
- for (const pane of workspace.panes) {
582
- const tab = tabOfPane(pane.id);
583
- if (!tab) {
584
- continue;
585
- }
559
+ // The FOCUSED pane's tab: clicking into the other pane re-points the trail.
560
+ void workspace.activePaneId;
561
+ void workspace.panes.length;
562
+ const tab = tabOfPane(activePane().id);
563
+ if (tab) {
586
564
  void tab.doc.document;
587
565
  void tab.documentPath;
588
566
  void tab.session.selection.map((path) => path.join("/")).join("|");
@@ -594,21 +572,17 @@ export function mountJumpBar(host: HTMLElement): void {
594
572
  });
595
573
  }
596
574
 
575
+ /** Stop the effect and take the bar down. */
597
576
  export function unmountJumpBar(): void {
598
577
  dismissJumpMenu();
599
578
  _scope?.stop();
600
579
  _scope = null;
601
- for (const bar of _bars.values()) {
602
- bar.surface.dispose();
603
- applyJumpBarOffset(0, bar.host);
604
- }
605
- _bars.clear();
606
- applyJumpBarOffset(0);
580
+ attachJumpBarHost(null);
607
581
  }
608
582
 
609
583
  // ─── This bar contributes no commands ────────────────────────────────────────
610
584
  // It declared one, `document.setStackLevel` — "leave a sub-document by naming the level you want to
611
585
  // Be at". Its `enablement` read `session.documentStack.length > 0`, a stack nothing could ever push
612
586
  // To, so the command was permanently disabled and its palette entry permanently unreachable. Every
613
- // Id the bar renders now belongs to the surface that owns the behaviour: `project.openRecent`,
614
- // `palette.openFiles`, `selection.set`.
587
+ // Id the bar renders now belongs to the surface that owns the behaviour: `palette.openFiles`,
588
+ // `pane.pin`, `selection.set`.
@@ -303,10 +303,9 @@ export function unmount() {
303
303
  * welcome screen (no tab, no bar) does not open under a 28px gap nothing explains, and a read-only
304
304
  * banner does not sit on top of the document it is warning you about.
305
305
  *
306
- * **On the PANE, not on `:root`**, for the reason `panels/jump-bar.ts` gives at the same seam: two
307
- * cells have two bands of different heights, and a single document-level number offsets both stages
308
- * by whichever pane painted last. A host outside a cell writes the root, which is what it meant
309
- * when the shell had one bar.
306
+ * **On the PANE, not on `:root`**: two cells have two bands of different heights, and a single
307
+ * document-level number offsets both stages by whichever pane painted last. A host outside a cell
308
+ * writes the root, which is what it meant when the shell had one bar.
310
309
  *
311
310
  * @param {number} height Band height in px. `0` when the pane has no chrome.
312
311
  * @param {HTMLElement | null} [host] The bar's host. Its cell takes the variable when it has one.
@@ -409,7 +408,8 @@ export function dismissPresetMenu(): void {
409
408
  *
410
409
  * The leading slot existed to push the three axes right. It is where §18.4's preset menu goes
411
410
  * because the menu is about THIS PANE and the bar is the pane's own chrome; the alternative homes
412
- * (the tab strip, the jump bar) are about a document and an address respectively.
411
+ * (the tab strip, the status bar's breadcrumb trail) are about a document and an address
412
+ * respectively.
413
413
  *
414
414
  * **There is no `pane.showDerivePresets`.** §13.5, quoted verbatim in `canvas/canvas-render.ts`:
415
415
  * opening a menu to press an item names a CONTROL; the item is the command. Every row here runs
@@ -625,9 +625,9 @@ export function presetRows(paneId: string): PresetRow[] {
625
625
  * the stage and the zoom pod still has something to zoom. Suppressing them while the dock was open
626
626
  * removed the controls for the very document the reader could still see.
627
627
  *
628
- * **There is no breadcrumb either.** The address is the jump bar's job — `panels/jump-bar.ts`, one
629
- * row above — and the Logic tab's own header carries the Close. This bar drew a second Back and a
630
- * second trail beside both of them.
628
+ * **There is no breadcrumb either.** The address is the jump bar's job — `panels/jump-bar.ts`, the
629
+ * status bar's trail — and the Logic tab's own header carries the Close. This bar drew a second
630
+ * Back and a second trail beside both of them.
631
631
  */
632
632
  function viewFor(tab: Tab, paneId: string, ctx: PaneContextCtx): PaneContextView {
633
633
  /* A LENS suppresses the two axes that WRITE. Editor kind and Canvas view both land in
@@ -5,27 +5,28 @@
5
5
  * `workspace.panes` has been a real data model since the shell redesign's chrome phase: an ordered
6
6
  * list, a focused id, a split, an unsplit. Nothing drew it. `index.html` declared ONE each of
7
7
  * `#tab-strip`, `#jump-bar`, `#pane-chrome` and `#canvas-wrap` as flat siblings in `#app`'s grid —
8
- * four surfaces that belong to a PANE, laid out as rows and columns of the APPLICATION — so the
9
- * shell could model two panes and had somewhere to put exactly one. §18.3's stage handover was the
8
+ * surfaces that belong to a PANE, laid out as rows and columns of the APPLICATION — so the shell
9
+ * could model two panes and had somewhere to put exactly one. §18.3's stage handover was the
10
10
  * workaround: one stage, taken by whichever pane had focus, releasing the loser's artboards on the
11
11
  * way past. This module is what that scaffolding was standing in for, and the handover is deleted
12
12
  * with it.
13
13
  *
14
14
  * **The document owns the frame; this module owns the decisions.** `surfaces/pane-grid.json` is the
15
- * cells, the five boxes inside each one, the splitter between two of them and every rule that lays
15
+ * cells, the four boxes inside each one, the splitter between two of them and every rule that lays
16
16
  * them out; `surfaces/pane-grid.ts` is the mount. What is left here is the flow: which panes there
17
17
  * are, what a cell's stage is furnished with, in what ORDER a departing cell is taken apart, and
18
18
  * what the grid's own tracks are. Everything a cell CONTAINS still arrives through the module that
19
19
  * owns it — `canvas/canvas-render.ts` is the render root of the stage and this document puts
20
- * nothing inside it, `panels/jump-bar.ts` and `panels/pane-context.ts` are handed the jump and
21
- * chrome boxes, `panels/tab-strip.ts` is handed the strip, and this module makes the fifth — the
22
- * right-edge drop zone — a pragmatic target itself.
20
+ * nothing inside it, `panels/pane-context.ts` is handed the chrome box, `panels/tab-strip.ts` is
21
+ * handed the strip, and this module makes the fourth — the right-edge drop zone — a pragmatic
22
+ * target itself. (The jump bar had a box here too, until it moved to the status bar: a pane's
23
+ * address is now the focused pane's breadcrumb trail along the foot of the window.)
23
24
  *
24
25
  * **Three properties survived the conversion, and each is structural rather than remembered:**
25
26
  *
26
27
  * 1. _A pane is complete before it is published._ The runtime builds a `$map` row inside its own
27
- * effect scope and inserts it afterwards, so the five `cellPart` calls below — the surface
28
- * record, the gestures, the two bars' mounts, the pane-focus listener, the drop zone's own
28
+ * effect scope and inserts it afterwards, so the `cellPart` calls below — the surface record,
29
+ * the gestures, the context bar's mount, the pane-focus listener, the drop zone's own
29
30
  * registration — all happen while the cell is still detached. There is no frame in which a cell
30
31
  * exists with no stage inside it.
31
32
  * 2. _A pane's node is never re-parented._ The repeater is keyed on the pane id and the runtime's
@@ -81,7 +82,6 @@ import { releaseCanvasHosts } from "../canvas/iframe-host";
81
82
  import { installStageGestures } from "../editor/shortcuts";
82
83
  import { scheduleCanvasRender } from "../canvas/canvas-render";
83
84
  import { paneRegion, paneStripRegion } from "../ui/regions";
84
- import { attachJumpBarHost } from "./jump-bar";
85
85
  import { attachPaneChromeHost } from "./pane-context";
86
86
  import { MAX_PANES, focusPane, workspace } from "../workspace/workspace";
87
87
  import { mountPaneGridSurface } from "../surfaces/pane-grid";
@@ -89,12 +89,11 @@ import type { EffectScope } from "@vue/reactivity";
89
89
  import type { CanvasSurface } from "../canvas/canvas-surface";
90
90
  import type { PaneCellPart, PaneGridRow, PaneGridSurface } from "../surfaces/pane-grid";
91
91
 
92
- /** One drawn pane: its root and the five surfaces inside it. */
92
+ /** One drawn pane: its root and the four surfaces inside it. */
93
93
  export interface PaneCell {
94
94
  paneId: string;
95
95
  root: HTMLElement;
96
96
  strip: HTMLElement;
97
- jump: HTMLElement;
98
97
  chrome: HTMLElement;
99
98
  stage: HTMLElement;
100
99
  dropZone: HTMLElement;
@@ -162,7 +161,6 @@ function cellState(paneId: string): CellState {
162
161
  const cell: CellState = {
163
162
  chrome: null as unknown as HTMLElement,
164
163
  dropZone: null as unknown as HTMLElement,
165
- jump: null as unknown as HTMLElement,
166
164
  paneId,
167
165
  releaseDropZone: null,
168
166
  releaseGestures: null,
@@ -283,15 +281,15 @@ function attachDropZone(paneId: string, element: HTMLElement): () => void {
283
281
  }
284
282
 
285
283
  /**
286
- * One of a cell's six boxes has been created. Fill the record in, and wire what belongs to it.
284
+ * One of a cell's five boxes has been created. Fill the record in, and wire what belongs to it.
287
285
  *
288
- * The two bars are HANDED their host rather than resolving a region, the same way
289
- * `panels/frontmatter-panel.ts` is handed the stage. They cannot resolve one the way the tab strip
286
+ * The context bar is HANDED its host rather than resolving a region, the same way
287
+ * `panels/frontmatter-panel.ts` is handed the stage. It cannot resolve one the way the tab strip
290
288
  * does: the strip's host carries `pane.<id>/tabs` and nothing inside it re-stamps that id, while
291
- * the jump bar and the context bar both stamp `pane.<id>/jump` and `pane.<id>/context` on markup
292
- * they render INSIDE their two boxes — so a region on the wrapper as well would put the same id on
293
- * two nested elements, and `resolveRegion` takes the LAST match. Sixty shots crop
294
- * `pane.primary/context`; a second, larger element carrying it is a silently widened crop.
289
+ * the context bar stamps `pane.<id>/context` on markup it renders INSIDE its box — so a region on
290
+ * the wrapper as well would put the same id on two nested elements, and `resolveRegion` takes the
291
+ * LAST match. Sixty shots crop `pane.primary/context`; a second, larger element carrying it is a
292
+ * silently widened crop.
295
293
  */
296
294
  function cellPart(paneId: string, part: PaneCellPart, element: HTMLElement): void {
297
295
  const cell = cellState(paneId);
@@ -304,11 +302,6 @@ function cellPart(paneId: string, part: PaneCellPart, element: HTMLElement): voi
304
302
  cell.strip = element;
305
303
  return;
306
304
  }
307
- if (part === "jump") {
308
- cell.jump = element;
309
- attachJumpBarHost(paneId, element);
310
- return;
311
- }
312
305
  if (part === "chrome") {
313
306
  cell.chrome = element;
314
307
  attachPaneChromeHost(paneId, element);
@@ -333,7 +326,7 @@ function cellPart(paneId: string, part: PaneCellPart, element: HTMLElement): voi
333
326
  *
334
327
  * The order is the document's own, and each step needs the one before it:
335
328
  *
336
- * - The two bars go first, each disposing a mount whose DOM is inside this cell — the runtime that
329
+ * - The context bar goes first, disposing a mount whose DOM is inside this cell — the runtime that
337
330
  * owns it is about to be unreachable.
338
331
  * - `releaseCanvasHosts` goes before `disposePaneSurface` and while `stage` still CONTAINS its
339
332
  * frames, which is the whole reason it can find them. A frame released later is noticed only by
@@ -345,7 +338,6 @@ function cellPart(paneId: string, part: PaneCellPart, element: HTMLElement): voi
345
338
  function disposeCell(cell: CellState): void {
346
339
  cell.releaseDropZone?.();
347
340
  cell.releaseDropZone = null;
348
- attachJumpBarHost(cell.paneId, null);
349
341
  attachPaneChromeHost(cell.paneId, null);
350
342
  cell.releaseGestures?.();
351
343
  cell.releaseGestures = null;