@jxsuite/studio 5.6.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.
- package/dist/chunks/{2020-zvax9fmb.js → 2020-cs2sb9t1.js} +3 -3
- package/dist/chunks/adapter-y5gdq19c.js +4 -0
- package/dist/chunks/{cf-account-picker-zfycs4k2.js → cf-account-picker-882a7ecm.js} +3 -3
- package/dist/chunks/cf-settings-20dznc3j.js +4 -0
- package/dist/chunks/components-q037dya7.js +4 -0
- package/dist/chunks/content-source-37x3vsap.js +4 -0
- package/dist/chunks/{convert-to-repeater-x19jr6c7.js → convert-to-repeater-grfep2w8.js} +3 -3
- package/dist/chunks/editor-j5sq149q.js +4 -0
- package/dist/chunks/entry-commands-c5h3nqck.js +4 -0
- package/dist/chunks/{extension-sections-y90sa0n4.js → extension-sections-dp8endhx.js} +3 -3
- package/dist/chunks/file-ops-937cvqk6.js +4 -0
- package/dist/chunks/files-tznwe1p1.js +4 -0
- package/dist/chunks/format-host-e2pjvd25.js +4 -0
- package/dist/chunks/fs-events-fv64ak3s.js +4 -0
- package/dist/chunks/git-panel-gb713ttc.js +4 -0
- package/dist/chunks/{grid-view-d9mybhn0.js → grid-view-5amhg604.js} +10 -10
- package/dist/chunks/iframe-host-t819mcg5.js +4 -0
- package/dist/chunks/{index-vnwy9d44.js → index-9ydfe04y.js} +3 -3
- package/dist/chunks/index-m0x5ztax.js +4 -0
- package/dist/chunks/{javascript-f0nrbdjb.js → javascript-d9pqgc2p.js} +2 -2
- package/dist/chunks/{jsonMode-nfa90dtf.js → jsonMode-800yvbem.js} +4 -4
- package/dist/chunks/jx-validate-zc9zp77s.js +4 -0
- package/dist/chunks/layers-4bsr82wz.js +4 -0
- package/dist/chunks/layers-panel-b6shnaax.js +4 -0
- package/dist/chunks/lit-html-cfn61dn7.js +4 -0
- package/dist/chunks/media-picker-ej0kdh1j.js +4 -0
- package/dist/chunks/menu-rtxf9chb.js +4 -0
- package/dist/chunks/{monaco-binding-antn79jf.js → monaco-binding-62dtmgbf.js} +3 -3
- package/dist/chunks/monaco-buffer-nn4dtx27.js +4 -0
- package/dist/chunks/{monaco-setup-k8s40vwg.js → monaco-setup-tj13ndtm.js} +3 -3
- package/dist/chunks/notify-ahs57a2z.js +4 -0
- package/dist/chunks/platform-v9xyfkbx.js +4 -0
- package/dist/chunks/publish-panel-bq36xcmj.js +4 -0
- package/dist/chunks/quick-search-zm00h9dh.js +4 -0
- package/dist/chunks/references-99ws0b0m.js +4 -0
- package/dist/chunks/right-panel-pq2f71q3.js +4 -0
- package/dist/chunks/section-registry-b2yzp9sw.js +4 -0
- package/dist/chunks/{studio-y17505ge.js → studio-01kbbqg3.js} +207 -207
- package/dist/chunks/{studio-gxna6yta.js → studio-01qza1n3.js} +4 -4
- package/dist/chunks/{studio-n2e133d6.js → studio-03wwfvyt.js} +4 -4
- package/dist/chunks/{studio-09n3fyy0.js → studio-0bbxhhce.js} +4 -4
- package/dist/chunks/{studio-dknr8m7b.js → studio-0k9mvpgj.js} +7 -7
- package/dist/chunks/{studio-s85vah66.js → studio-0scebvb9.js} +5 -5
- package/dist/chunks/studio-1a99w39s.js +5 -0
- package/dist/chunks/{studio-eegyf5vg.js → studio-1wvfxkyy.js} +4 -4
- package/dist/chunks/{studio-ft8pfpp0.js → studio-1x8x26k3.js} +4 -4
- package/dist/chunks/{studio-28pz4s1k.js → studio-222rqyyc.js} +4 -4
- package/dist/chunks/{studio-6exg5z4x.js → studio-4xppwncr.js} +3 -3
- package/dist/chunks/{studio-0c3fpdqj.js → studio-54g077x3.js} +5 -5
- package/dist/chunks/{studio-z47mm077.js → studio-56z7evx4.js} +6 -6
- package/dist/chunks/{studio-zkkdbbvd.js → studio-5e9zsamh.js} +6 -6
- package/dist/chunks/{studio-4yztve2t.js → studio-65fhspdd.js} +4 -4
- package/dist/chunks/{studio-b29h7g7s.js → studio-693nhyky.js} +4 -4
- package/dist/chunks/{studio-654fxcr0.js → studio-7pgy3xaf.js} +4 -4
- package/dist/chunks/{studio-k9pwvmz8.js → studio-888knnkc.js} +4 -4
- package/dist/chunks/{studio-b5rxwkj2.js → studio-9mqfamps.js} +4 -4
- package/dist/chunks/{studio-011em5cf.js → studio-a7xqhc3n.js} +4 -4
- package/dist/chunks/{studio-mf41717j.js → studio-aje966s9.js} +4 -4
- package/dist/chunks/studio-aqmph6k4.js +5 -0
- package/dist/chunks/studio-c6w3tq7t.js +5 -0
- package/dist/chunks/{studio-tzs6njnb.js → studio-c6zdm25z.js} +7 -7
- package/dist/chunks/{studio-1pxvgx1t.js → studio-c9d3pxf7.js} +4 -4
- package/dist/chunks/{studio-pvj970st.js → studio-ceb34dxj.js} +4 -4
- package/dist/chunks/studio-cvbkkfqb.js +5 -0
- package/dist/chunks/{studio-sadckcpp.js → studio-cxt150pj.js} +5 -5
- package/dist/chunks/{studio-891t1tv9.js → studio-dkn3x3hz.js} +4 -4
- package/dist/chunks/studio-e6re3rk1.js +5 -0
- package/dist/chunks/{studio-9n2j9nqk.js → studio-edbtvcd1.js} +4 -4
- package/dist/chunks/{studio-wr0pq67n.js → studio-eh0rac07.js} +4 -4
- package/dist/chunks/{studio-mm8x32gg.js → studio-evwgp1ym.js} +4 -4
- package/dist/chunks/{studio-9tkaykt5.js → studio-f195h2hq.js} +4 -4
- package/dist/chunks/{studio-0nm2skex.js → studio-fqq890ap.js} +3 -3
- package/dist/chunks/{studio-02y9cb4h.js → studio-frb0qbf7.js} +5 -5
- package/dist/chunks/{studio-je15p5v8.js → studio-gfn1kpxj.js} +5 -5
- package/dist/chunks/{studio-ymsjnvp9.js → studio-gh97gngt.js} +4 -4
- package/dist/chunks/{studio-zkvyd3ev.js → studio-gp02nqnw.js} +4 -4
- package/dist/chunks/{studio-5fc0728f.js → studio-hjbg8d5y.js} +7 -7
- package/dist/chunks/{studio-dtmdjmqb.js → studio-j6gnhvaa.js} +4 -4
- package/dist/chunks/{studio-d007j0z3.js → studio-mwxgyn9h.js} +6 -6
- package/dist/chunks/{studio-36s6qjgq.js → studio-n78gjfy8.js} +4 -4
- package/dist/chunks/studio-nbxqcgvf.js +5 -0
- package/dist/chunks/{studio-ks0ecgjx.js → studio-nt06yybj.js} +4 -4
- package/dist/chunks/{studio-ebt49j3c.js → studio-p7c1agz0.js} +5 -5
- package/dist/chunks/{studio-y164brht.js → studio-pwqfmt4p.js} +4 -4
- package/dist/chunks/{studio-6zyt56ek.js → studio-r3kaka4d.js} +4 -4
- package/dist/chunks/{studio-tj28pxa1.js → studio-semd1s8s.js} +4 -4
- package/dist/chunks/studio-skzanb0k.js +5 -0
- package/dist/chunks/{studio-z1vdxq0e.js → studio-spf2skgc.js} +4 -4
- package/dist/chunks/{studio-7w04zw8h.js → studio-st75kn99.js} +5 -5
- package/dist/chunks/{studio-dmymvk2j.js → studio-sxc0zvpt.js} +3 -3
- package/dist/chunks/studio-t1eedsyx.js +4 -0
- package/dist/chunks/{studio-153mwkyq.js → studio-tp642n5g.js} +4 -4
- package/dist/chunks/{studio-r7yeeqc4.js → studio-tw7rre6s.js} +4 -4
- package/dist/chunks/{studio-t0v47dpc.js → studio-tzegmy4w.js} +4 -4
- package/dist/chunks/studio-vavmwshr.js +5 -0
- package/dist/chunks/{studio-805fwp9k.js → studio-wma2c6g8.js} +4 -4
- package/dist/chunks/studio-x92tgnee.js +64 -0
- package/dist/chunks/studio-xk0z6msk.js +5 -0
- package/dist/chunks/{studio-njmg0e73.js → studio-xry7tk3z.js} +5 -5
- package/dist/chunks/{studio-sgzss898.js → studio-xta2vxp6.js} +7 -7
- package/dist/chunks/{studio-jsr14n1s.js → studio-xy3tv3az.js} +4 -4
- package/dist/chunks/{studio-6k1eswp9.js → studio-y7jehjrp.js} +4 -4
- package/dist/chunks/{studio-81twpmzj.js → studio-yr04mv2j.js} +4 -4
- package/dist/chunks/{studio-gj4j9e7k.js → studio-zd4kayn2.js} +4 -4
- package/dist/chunks/{studio-w8w9frjy.js → studio-zpnk016a.js} +12 -12
- package/dist/chunks/{tsMode-eq26t9gs.js → tsMode-k9s9fcag.js} +4 -4
- package/dist/chunks/workspace-b69mgm0e.js +4 -0
- package/dist/chunks/{ws-client-3gxqp06s.js → ws-client-0ja08haa.js} +3 -3
- package/dist/manifest.json +62 -62
- package/dist/studio.js +95 -95
- package/package.json +3 -3
- package/src/canvas/stage-scrollbar.ts +7 -7
- package/src/files/file-ops.ts +5 -8
- package/src/panels/jump-bar.ts +132 -158
- package/src/panels/pane-context.ts +8 -8
- package/src/panels/pane-grid.ts +18 -26
- package/src/panels/style-panel.ts +2 -2
- package/src/services/context-manager.ts +41 -70
- package/src/services/document-assistant.ts +12 -3
- package/src/services/harness/chat-reducer.ts +115 -0
- package/src/services/harness/turn-hooks.ts +50 -0
- package/src/services/tool-executor.ts +69 -288
- package/src/services/tool-outcomes.ts +12 -10
- package/src/studio.ts +2 -5
- package/src/surfaces/block-action-bar.json +6 -1
- package/src/surfaces/jump-bar.json +9 -12
- package/src/surfaces/jump-bar.ts +15 -16
- package/src/surfaces/pane-context.json +2 -2
- package/src/surfaces/pane-grid.json +8 -25
- package/src/surfaces/pane-grid.ts +7 -8
- package/src/surfaces/statusbar.json +71 -3
- package/src/surfaces/statusbar.ts +66 -139
- package/src/surfaces/style-panel.json +3 -3
- package/src/surfaces/style-panel.ts +11 -1
- package/dist/chunks/adapter-6zhjbpmd.js +0 -4
- package/dist/chunks/cf-settings-fpa485q3.js +0 -4
- package/dist/chunks/components-b0vh4sq8.js +0 -4
- package/dist/chunks/content-source-z5t0d0h9.js +0 -4
- package/dist/chunks/editor-7e23n4a8.js +0 -4
- package/dist/chunks/entry-commands-5cm6aba2.js +0 -4
- package/dist/chunks/file-ops-hv7gf757.js +0 -4
- package/dist/chunks/files-kb0epdxf.js +0 -4
- package/dist/chunks/format-host-eg1wy2bp.js +0 -4
- package/dist/chunks/fs-events-ky3vxfmh.js +0 -4
- package/dist/chunks/git-panel-yyg3bzp2.js +0 -4
- package/dist/chunks/iframe-host-9wdf0hes.js +0 -4
- package/dist/chunks/index-2tf0m2ne.js +0 -4
- package/dist/chunks/jx-validate-7w11db9v.js +0 -4
- package/dist/chunks/layers-7zasrpse.js +0 -4
- package/dist/chunks/layers-panel-9nzc4fdq.js +0 -4
- package/dist/chunks/lit-html-5qr7nbng.js +0 -4
- package/dist/chunks/media-picker-maevn9d4.js +0 -4
- package/dist/chunks/menu-wq6q0v3y.js +0 -4
- package/dist/chunks/monaco-buffer-9ry2rn5x.js +0 -4
- package/dist/chunks/notify-g91gjd6w.js +0 -4
- package/dist/chunks/platform-jdwjp2vt.js +0 -4
- package/dist/chunks/publish-panel-8yym447p.js +0 -4
- package/dist/chunks/quick-search-9bzm9kh3.js +0 -4
- package/dist/chunks/references-znssfqvq.js +0 -4
- package/dist/chunks/right-panel-czbzktm8.js +0 -4
- package/dist/chunks/section-registry-0rtq73qs.js +0 -4
- package/dist/chunks/studio-0mm38y62.js +0 -5
- package/dist/chunks/studio-27tc8q0h.js +0 -5
- package/dist/chunks/studio-7djymzt2.js +0 -5
- package/dist/chunks/studio-ant1zbp3.js +0 -5
- package/dist/chunks/studio-eb5sty2k.js +0 -5
- package/dist/chunks/studio-frv7vjhw.js +0 -5
- package/dist/chunks/studio-mb8jhtr4.js +0 -64
- package/dist/chunks/studio-nvykrgbj.js +0 -4
- package/dist/chunks/studio-nw4q9sdn.js +0 -5
- package/dist/chunks/studio-p6avneaq.js +0 -5
- package/dist/chunks/studio-vz7b43ng.js +0 -5
- package/dist/chunks/workspace-emt8454d.js +0 -4
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://jxsuite.com/schema/v1",
|
|
3
3
|
"$id": "StudioPaneGrid",
|
|
4
|
-
"$description": "The pane grid (studio.md §18.1): one cell per pane, with a splitter before every cell but the first. Every box here is EMPTY by design — the
|
|
4
|
+
"$description": "The pane grid (studio.md §18.1): one cell per pane, with a splitter before every cell but the first. Every box here is EMPTY by design — the FOUR inside a cell are hosts other modules fill (`panels/tab-strip.ts` the strip, `panels/pane-context.ts` the chrome layer, `canvas/canvas-render.ts` the stage, `panels/pane-grid.ts` itself the right-edge drop zone), and the stage is an ISLAND in the studio-ui-guidelines.md §9.4 sense: the canvas host mounts a cross-origin `<iframe>` into it, so nothing this document draws may ever go inside it. What the document owns is the STRUCTURE and the STYLE — the cell's own two-row grid, the three row-2 children that share its lower cell, and the splitter — which is `styles/shell.css`'s `.pane*` block and `styles/canvas.css`'s `.pane-stage` rule, carried here and keyed on `part`. Two things are load-bearing and easy to undo. (1) **A row that moves reloads an iframe**, so the keyed `$map` is not an optimisation: `workspace.panes[0]` is always the primary and `MAX_PANES` is 2, the key is the pane id, and the runtime's forward-cursor pass therefore never moves a kept row — gaining or losing the side pane leaves the primary's node, and the frame inside it, untouched. (2) **The splitter must sit BETWEEN two cells and inside neither**, so each row is a `display: contents` wrapper holding its own leading splitter and its cell: the wrapper generates no box, so the splitter and the two cells are the grid items of `#pane-grid` in DOM order, and a pointer landing on the splitter is in no cell at all. Every selector below is a `>` chain from the root, because a pane's stage hosts other surface documents and a bare `[part=\"row\"]` descendant rule would reach into them.",
|
|
5
5
|
"tagName": "div",
|
|
6
6
|
"attributes": {
|
|
7
7
|
"part": "grid",
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
"zIndex": "10"
|
|
28
28
|
},
|
|
29
29
|
"& > [part=\"row\"] > [part=\"pane\"]": {
|
|
30
|
-
"$description": "ONE PANE, and one COLUMN, declared.
|
|
30
|
+
"$description": "ONE PANE, and one COLUMN, declared. Three children name `grid-row: 2` in order to share the lower cell, and auto-placement answers that by inventing an implicit column each: the chrome layer, the stage and the drop zone lined up side by side (with a fourth, the jump bar, that has since moved to the status bar), the stage taking a fraction of the pane and pinning the document to the right edge. `grid-template-columns` is what makes \"share the cell\" mean it, and every row-2 child restates `grid-column: 1` so a future fourth one cannot re-open the same hole. No unit test can see this — happy-dom does no layout, so every rect is zero — which is why the screenshot lane is what caught it.",
|
|
31
31
|
"display": "grid",
|
|
32
32
|
"gridTemplateColumns": "minmax(0, 1fr)",
|
|
33
33
|
"gridTemplateRows": "auto minmax(0, 1fr)",
|
|
@@ -44,21 +44,10 @@
|
|
|
44
44
|
"$description": "A pane with nothing to say costs no band. The strip host is empty until `panels/tab-strip.ts` mounts a document into it, and empty again the moment it disposes.",
|
|
45
45
|
"display": "none"
|
|
46
46
|
},
|
|
47
|
-
"& > [part=\"row\"] > [part=\"pane\"] > [part=\"jump\"]": {
|
|
48
|
-
"$description": "The jump bar. Like the chrome layer below it this shares the pane's lower cell rather than owning a row, so the two bars stack OVER the stage instead of pushing it down. `align-self: start` is what keeps it the height of its own content instead of the height of the cell, so the bar never covers the stage it sits above.",
|
|
49
|
-
"gridRow": "2",
|
|
50
|
-
"gridColumn": "1",
|
|
51
|
-
"alignSelf": "start",
|
|
52
|
-
"position": "relative",
|
|
53
|
-
"zIndex": "3",
|
|
54
|
-
"minWidth": "0",
|
|
55
|
-
"maxWidth": "100%"
|
|
56
|
-
},
|
|
57
47
|
"& > [part=\"row\"] > [part=\"pane\"] > [part=\"chrome\"]": {
|
|
58
|
-
"$description": "The pane context bar and the floating zoom pod. The layer itself is inert and only its children take the pointer; `surfaces/pane-context.json` owns what they look like.
|
|
48
|
+
"$description": "The pane context bar and the floating zoom pod. The layer itself is inert and only its children take the pointer; `surfaces/pane-context.json` owns what they look like.",
|
|
59
49
|
"gridRow": "2",
|
|
60
50
|
"gridColumn": "1",
|
|
61
|
-
"marginTop": "var(--jump-bar-h, 0px)",
|
|
62
51
|
"position": "relative",
|
|
63
52
|
"zIndex": "2",
|
|
64
53
|
"minWidth": "0",
|
|
@@ -71,10 +60,10 @@
|
|
|
71
60
|
"pointerEvents": "auto"
|
|
72
61
|
},
|
|
73
62
|
"& > [part=\"row\"] > [part=\"pane\"] > [part=\"pane-stage\"]": {
|
|
74
|
-
"$description": "The stage: the pane's own axis (`styles/canvas.css`) over its placement in the cell (`styles/shell.css`). Offset by the
|
|
63
|
+
"$description": "The stage: the pane's own axis (`styles/canvas.css`) over its placement in the cell (`styles/shell.css`). Offset by the context bar stacked over this cell — `--pane-context-h`, written by `panels/pane-context.ts` onto the CELL, which is why this reads it by cascade — so the bar never covers the document, and by zero when no tab is open, so the welcome screen does not sit under a band nothing explains. A pane's address used to be a second bar stacked here (`--jump-bar-h`); it is the status bar's breadcrumb trail now, and costs the stage nothing.",
|
|
75
64
|
"gridRow": "2",
|
|
76
65
|
"gridColumn": "1",
|
|
77
|
-
"marginTop": "
|
|
66
|
+
"marginTop": "var(--pane-context-h, 0px)",
|
|
78
67
|
"position": "relative",
|
|
79
68
|
"overflow": "auto",
|
|
80
69
|
"background": "var(--bg-panel)",
|
|
@@ -84,7 +73,7 @@
|
|
|
84
73
|
"alignItems": "flex-start"
|
|
85
74
|
},
|
|
86
75
|
"& > [part=\"row\"] > [part=\"pane\"] > [part=\"drop-zone\"]": {
|
|
87
|
-
"$description": "The right-edge target that creates a second pane (§18.1). Sits ABOVE the stage (`z-index: 4` against the stage's own stacking,
|
|
76
|
+
"$description": "The right-edge target that creates a second pane (§18.1). Sits ABOVE the stage (`z-index: 4` against the stage's own stacking, chrome's 2) so a drag reaches it before it reaches the cross-origin iframe underneath, which would otherwise swallow every drag event with no affordance to say so. `pointer-events: none` when unarmed — a mouse doing anything other than dragging a tab or a file must pass straight through to the stage — and `auto` only while `data-armed` is set, which `panels/pane-grid.ts`'s own arming monitor writes for the DURATION of a drag it recognises, never for a click.",
|
|
88
77
|
"gridRow": "2",
|
|
89
78
|
"gridColumn": "1",
|
|
90
79
|
"position": "relative",
|
|
@@ -108,7 +97,7 @@
|
|
|
108
97
|
},
|
|
109
98
|
"children": [
|
|
110
99
|
{
|
|
111
|
-
"$description": "Keyed on the PANE ID, and that is the whole of §18.1 rule 1's structural half: the key is what makes the runtime MOVE a kept row rather than rebuild it, and re-parenting is not a move for an `<iframe>` — it reloads, dropping its `iframe-channel` connection, its shadow document and every `ready` panel with it. A row renders complete (its cell, its
|
|
100
|
+
"$description": "Keyed on the PANE ID, and that is the whole of §18.1 rule 1's structural half: the key is what makes the runtime MOVE a kept row rather than rebuild it, and re-parenting is not a move for an `<iframe>` — it reloads, dropping its `iframe-channel` connection, its shadow document and every `ready` panel with it. A row renders complete (its cell, its four hosts and its splitter) inside the row's own effect scope before it is inserted, so there is no frame in which a cell exists with no stage inside it.",
|
|
112
101
|
"$prototype": "Array",
|
|
113
102
|
"items": { "$ref": "#/state/rows" },
|
|
114
103
|
"key": { "$ref": "$map/item/id" },
|
|
@@ -181,13 +170,7 @@
|
|
|
181
170
|
}
|
|
182
171
|
},
|
|
183
172
|
{
|
|
184
|
-
"$description": "The
|
|
185
|
-
"tagName": "div",
|
|
186
|
-
"attributes": {
|
|
187
|
-
"part": "jump"
|
|
188
|
-
}
|
|
189
|
-
},
|
|
190
|
-
{
|
|
173
|
+
"$description": "The context bar is HANDED this box rather than resolving a region into it. It cannot resolve one the way the strip does: nothing inside the strip re-stamps `pane.<id>/tabs`, while the context bar stamps `pane.<id>/context` on markup it renders INSIDE this div — so a region on the wrapper as well would put the same id on two nested elements, and `resolveRegion` takes the LAST match. Sixty shots crop `pane.primary/context`; a second, larger element carrying it is a silently widened crop.",
|
|
191
174
|
"tagName": "div",
|
|
192
175
|
"attributes": {
|
|
193
176
|
"part": "chrome"
|
|
@@ -35,10 +35,10 @@ registerSurface("pane-grid", paneGridDoc as unknown as JxDocument);
|
|
|
35
35
|
/**
|
|
36
36
|
* The `part` a pane's CELL carries.
|
|
37
37
|
*
|
|
38
|
-
* `panels/
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
*
|
|
38
|
+
* `panels/pane-context.ts` writes a `--*-h` custom property onto the cell so the stage can read it
|
|
39
|
+
* back by cascade, and it is handed a box inside the cell rather than the cell itself — so it
|
|
40
|
+
* resolves the cell with {@link PANE_SELECTOR}. It replaces a `.pane` class: a converted surface
|
|
41
|
+
* emits none, and a `part` is the hook a document offers instead.
|
|
42
42
|
*/
|
|
43
43
|
export const PANE_PART = "pane";
|
|
44
44
|
|
|
@@ -63,13 +63,12 @@ export const PANE_SELECTOR = `[part="${PANE_PART}"]`;
|
|
|
63
63
|
*/
|
|
64
64
|
export const STAGE_SELECTOR = `[part="${STAGE_PART}"]`;
|
|
65
65
|
|
|
66
|
-
/** The
|
|
67
|
-
export type PaneCellPart = "pane" | "strip" | "
|
|
66
|
+
/** The five boxes a cell is made of, in the order the document draws them. */
|
|
67
|
+
export type PaneCellPart = "pane" | "strip" | "chrome" | "stage" | "dropZone";
|
|
68
68
|
|
|
69
69
|
const CELL_PARTS: ReadonlyMap<string, PaneCellPart> = new Map([
|
|
70
70
|
["pane", "pane"],
|
|
71
71
|
["strip", "strip"],
|
|
72
|
-
["jump", "jump"],
|
|
73
72
|
["chrome", "chrome"],
|
|
74
73
|
["pane-stage", "stage"],
|
|
75
74
|
["drop-zone", "dropZone"],
|
|
@@ -234,7 +233,7 @@ export function mountPaneGridSurface(
|
|
|
234
233
|
const ready = mountSurface("pane-grid", scope, host, {
|
|
235
234
|
/* No `instanceof` narrowing and no "is there a pane" guard, and both absences are structural.
|
|
236
235
|
`pane-grid.json` draws no text and no `textContent`, so every node the runtime reports here
|
|
237
|
-
is an element; and the
|
|
236
|
+
is an element; and the five cell parts exist only inside the repeater's rows, so a `$map`
|
|
238
237
|
scope is always there to name the pane. A guard on either would be a branch the document
|
|
239
238
|
cannot reach — which is a line no test could ever cover, which is a claim nothing checks. */
|
|
240
239
|
onNodeCreated: (element, _path, def, state) => {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://jxsuite.com/schema/v1",
|
|
3
3
|
"$id": "StudioStatusbar",
|
|
4
|
-
"$description": "The status bar: ambient state in the shell's level order, PROJECT then DOCUMENT then SELECTION. The adapter hands in
|
|
4
|
+
"$description": "The status bar: ambient state in the shell's level order, PROJECT then DOCUMENT then SELECTION. The adapter hands in the fields either side of the DOCUMENT field (`leading` and `trailing`), each with its region and its items; an item with a command is a button that runs it through `run`, and an item without one is a readout. The DOCUMENT field between them is the `trail` slot, an empty box `panels/jump-bar.ts` mounts the breadcrumb trail into. Nothing here decides what the bar says.",
|
|
5
5
|
"tagName": "div",
|
|
6
6
|
"attributes": { "part": "bar" },
|
|
7
7
|
"style": {
|
|
@@ -17,7 +17,17 @@
|
|
|
17
17
|
"padding": "0 var(--jx-space-3)"
|
|
18
18
|
},
|
|
19
19
|
"& > [part=\"field\"]:last-child": { "overflow": "hidden" },
|
|
20
|
-
"& > [
|
|
20
|
+
"& > :not([hidden]) ~ :not([hidden])": {
|
|
21
|
+
"$description": "A rule between fields rather than a run of \"|\" characters: the boundary between PROJECT, DOCUMENT and SELECTION is structural. Every visible field after a visible field draws one, so a hidden DOCUMENT slot leaves no doubled rule behind.",
|
|
22
|
+
"borderLeft": "1px solid var(--jx-border)"
|
|
23
|
+
},
|
|
24
|
+
"& > [part=\"trail\"]": {
|
|
25
|
+
"$description": "The DOCUMENT field, filled by `panels/jump-bar.ts`. It takes the width the other two fields leave, so a deep trail scrolls inside it instead of pushing the SELECTION field off the bar.",
|
|
26
|
+
"display": "flex",
|
|
27
|
+
"flex": "1",
|
|
28
|
+
"minWidth": "0"
|
|
29
|
+
},
|
|
30
|
+
"& > [part=\"trail\"][hidden]": { "display": "none" },
|
|
21
31
|
"& [part=\"item\"], & [part=\"state\"]": {
|
|
22
32
|
"maxWidth": "32ch",
|
|
23
33
|
"overflow": "hidden",
|
|
@@ -43,7 +53,65 @@
|
|
|
43
53
|
"children": [
|
|
44
54
|
{
|
|
45
55
|
"$prototype": "Array",
|
|
46
|
-
"items": { "$ref": "#/state/
|
|
56
|
+
"items": { "$ref": "#/state/leading" },
|
|
57
|
+
"key": { "$ref": "$map/item/id" },
|
|
58
|
+
"map": {
|
|
59
|
+
"tagName": "div",
|
|
60
|
+
"attributes": { "part": "field", "data-jx-region": "${$map.item.region}" },
|
|
61
|
+
"children": [
|
|
62
|
+
{
|
|
63
|
+
"$prototype": "Array",
|
|
64
|
+
"items": { "$ref": "$map/item/items" },
|
|
65
|
+
"key": { "$ref": "$map/item/key" },
|
|
66
|
+
"map": {
|
|
67
|
+
"tagName": "span",
|
|
68
|
+
"attributes": { "part": "slot" },
|
|
69
|
+
"style": { "display": "contents" },
|
|
70
|
+
"$switch": { "$ref": "$map/item/kind" },
|
|
71
|
+
"cases": {
|
|
72
|
+
"button": {
|
|
73
|
+
"tagName": "button",
|
|
74
|
+
"attributes": {
|
|
75
|
+
"part": "item",
|
|
76
|
+
"type": "button",
|
|
77
|
+
"title": "${$map.item.title}",
|
|
78
|
+
"disabled": { "$ref": "$map/item/disabled" }
|
|
79
|
+
},
|
|
80
|
+
"textContent": "${$map.item.label}",
|
|
81
|
+
"onclick": {
|
|
82
|
+
"$prototype": "Function",
|
|
83
|
+
"body": [
|
|
84
|
+
{
|
|
85
|
+
"operator": "call",
|
|
86
|
+
"target": { "$ref": "#/state/run" },
|
|
87
|
+
"value": [{ "$ref": "$map/item/key" }]
|
|
88
|
+
}
|
|
89
|
+
]
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
"state": {
|
|
93
|
+
"tagName": "span",
|
|
94
|
+
"attributes": { "part": "state", "title": "${$map.item.title}" },
|
|
95
|
+
"textContent": "${$map.item.label}"
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"$description": "The DOCUMENT field's slot. Empty by design: the jump bar is handed this box and mounts its own document into it, so nothing here may ever render a child. `aria-live=\"off\"` because the bar's host is the app's polite status region, and a trail that changes on every click in the canvas would be read aloud on every click; the trail is a `nav` a reader visits, not news.",
|
|
105
|
+
"tagName": "div",
|
|
106
|
+
"attributes": {
|
|
107
|
+
"part": "trail",
|
|
108
|
+
"aria-live": "off",
|
|
109
|
+
"hidden": "${state.hasDocument ? null : ''}"
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"$prototype": "Array",
|
|
114
|
+
"items": { "$ref": "#/state/trailing" },
|
|
47
115
|
"key": { "$ref": "$map/item/id" },
|
|
48
116
|
"map": {
|
|
49
117
|
"tagName": "div",
|
|
@@ -1,68 +1,60 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* The status bar — ambient state, in scope order, and
|
|
3
|
+
* The status bar — ambient state, in scope order, and the breadcrumb trail between it.
|
|
4
4
|
*
|
|
5
5
|
* What this replaces: a bar built by `innerHTML` string concatenation with a three-character
|
|
6
6
|
* escaper, carrying whatever the last of 78 `statusMessage()` calls had said, for three seconds.
|
|
7
7
|
* Transient messages have left entirely for the toast host (`ui/layers.ts`) and the Problems list
|
|
8
8
|
* (`services/notify.ts`); what remains here is state that is TRUE for as long as it is shown.
|
|
9
9
|
*
|
|
10
|
-
* **Three
|
|
11
|
-
*
|
|
10
|
+
* **Three fields, in the shell's own left-to-right level order** (§16.2), so the bar restates the
|
|
11
|
+
* containment model on every glance:
|
|
12
12
|
*
|
|
13
13
|
* ```text
|
|
14
|
-
* PROJECT ‖ DOCUMENT
|
|
15
|
-
* name · branch ↑n↓n · problems ‖
|
|
14
|
+
* PROJECT ‖ DOCUMENT ‖ SELECTION
|
|
15
|
+
* name · branch ↑n↓n · problems ‖ file › ancestor › … › element ⌃ ‖ count · style rule
|
|
16
16
|
* ```
|
|
17
17
|
*
|
|
18
|
-
* The
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
* the
|
|
18
|
+
* **The DOCUMENT field is the jump bar** (`panels/jump-bar.ts`): the focused pane's address, every
|
|
19
|
+
* step a button that selects it. This module renders the fields either side of it and leaves the
|
|
20
|
+
* middle as an empty `trail` slot, which the jump bar is handed once this document has mounted. The
|
|
21
|
+
* field used to hold the file path, the view name and a save word; the trail's first step is the
|
|
22
|
+
* same file path with the same `palette.openFiles` command, the view is the pane context bar's (a
|
|
23
|
+
* second copy at 11px is the chrome duplication studio-ui-guidelines.md §12.2's budget exists to
|
|
24
|
+
* prevent), and unsaved changes are stated by the tab strip's dirty marker and the Command Bar's
|
|
25
|
+
* Save. A trail of ancestors is worth more on this row than any of the three.
|
|
22
26
|
*
|
|
23
27
|
* `statusbar/project`, `statusbar/document` and `statusbar/selection` are three placements the
|
|
24
28
|
* level × placement matrix already declares, each admitting exactly one level — so the bar's
|
|
25
29
|
* mixedness is structural rather than an exemption.
|
|
26
30
|
*
|
|
27
|
-
* **The effective view, not a mode string.** The bar used to print "Content Mode" from
|
|
28
|
-
* `tab.doc.mode` while the Command Bar printed "Design" from `canvasMode` — two surfaces reading
|
|
29
|
-
* two fields to answer one question. Both now read `registry.context()`, which is the same record
|
|
30
|
-
* every `when` predicate reads, so they cannot disagree again.
|
|
31
|
-
*
|
|
32
31
|
* **Every interactive item is a command.** There are no click handlers in this file: an item names
|
|
33
32
|
* a command id, and renders as a button only when the registry has that command and its `when`
|
|
34
33
|
* holds. That is what let an item with no command yet — the peers count, before the `Collaborate:`
|
|
35
34
|
* family was registered — sit in the template and start rendering the day its command arrived, with
|
|
36
|
-
* no edit here. The
|
|
37
|
-
* the pane context bar; a second mode picker in 24px would be the chrome duplication
|
|
38
|
-
* studio-ui-guidelines.md §12.2's budget exists to prevent), the save wording once a document IS
|
|
39
|
-
* saved, and the stylebook selector.
|
|
35
|
+
* no edit here. The one readout that is NOT a button is the stylebook selector.
|
|
40
36
|
*
|
|
41
37
|
* The bar is the `statusbar` surface (`surfaces/statusbar.json`): this module is the projection —
|
|
42
|
-
*
|
|
43
|
-
*
|
|
38
|
+
* fields of items, each item a button with a command or a readout without — and the surface draws
|
|
39
|
+
* it. The scope is reactive, so one effect recomputes the projection from the state the bar
|
|
44
40
|
* renders; nothing repaints.
|
|
45
41
|
*
|
|
46
42
|
* @docs studio/interface
|
|
47
43
|
*/
|
|
48
44
|
|
|
49
45
|
import { projectState, statusbarEl } from "../store";
|
|
50
|
-
import {
|
|
46
|
+
import { attachJumpBarHost, mountJumpBar, unmountJumpBar } from "../panels/jump-bar";
|
|
51
47
|
import { shell } from "../shell";
|
|
52
48
|
import { effect, effectScope, reactive } from "../reactivity";
|
|
53
49
|
import { activeTab } from "../workspace/workspace";
|
|
54
|
-
import { EDITOR_KIND_LABELS } from "../commands/context";
|
|
55
50
|
import { activeRegistry } from "../commands/active-registry";
|
|
56
51
|
import { runReported } from "../commands/run-reported";
|
|
57
52
|
import { deployStatusItem } from "../publish/deploy-checklist";
|
|
58
53
|
import { collabState } from "../collab/collab-state";
|
|
59
54
|
import { problemCount, problems } from "../services/notify";
|
|
60
|
-
import { now } from "../services/clock";
|
|
61
|
-
import { relativeTime } from "../panels/ai-chat/sessions-view";
|
|
62
55
|
import { mountSurface, registerSurface } from "../ui/surface";
|
|
63
56
|
import statusbarDoc from "./statusbar.json";
|
|
64
57
|
import type { CommandRegistry } from "../commands/registry";
|
|
65
|
-
import type { EditorKind } from "../commands/context";
|
|
66
58
|
import type { EffectScope } from "@vue/reactivity";
|
|
67
59
|
import type { JxDocument } from "@jxsuite/schema/types";
|
|
68
60
|
import type { SurfaceHandle } from "../ui/surface";
|
|
@@ -71,28 +63,6 @@ registerSurface("statusbar", statusbarDoc as unknown as JxDocument);
|
|
|
71
63
|
|
|
72
64
|
let _scope: EffectScope | null = null;
|
|
73
65
|
|
|
74
|
-
/**
|
|
75
|
-
* Document path → when it was last written, from the {@link now} seam.
|
|
76
|
-
*
|
|
77
|
-
* Kept here rather than on the `Tab` record for one reason: a save time is something the STATUS BAR
|
|
78
|
-
* observes, not something a document is. `files/file-ops.ts` reports it after a successful write —
|
|
79
|
-
* the same place the old `statusMessage("Saved …")` was raised from, so the fact has not moved, it
|
|
80
|
-
* has only stopped being a message that erases itself.
|
|
81
|
-
*/
|
|
82
|
-
const _savedAt = new Map<string, number>();
|
|
83
|
-
|
|
84
|
-
/** Record a successful write. Called by the save path; keyed by document path. */
|
|
85
|
-
export function noteDocumentSaved(path: string | null | undefined): void {
|
|
86
|
-
if (path) {
|
|
87
|
-
_savedAt.set(path, now());
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
/** Forget every recorded save. Project close and the tests both want a clean slate. */
|
|
92
|
-
export function forgetSavedTimes(): void {
|
|
93
|
-
_savedAt.clear();
|
|
94
|
-
}
|
|
95
|
-
|
|
96
66
|
// ─── Items ───────────────────────────────────────────────────────────────────
|
|
97
67
|
|
|
98
68
|
/** One thing the bar can show. A `command` makes it a button; without one it is a readout. */
|
|
@@ -248,81 +218,18 @@ function projectField_(registry: CommandRegistry | null): ProjectedField | null
|
|
|
248
218
|
]);
|
|
249
219
|
}
|
|
250
220
|
|
|
251
|
-
// ─── DOCUMENT ────────────────────────────────────────────────────────────────
|
|
252
|
-
|
|
253
|
-
/**
|
|
254
|
-
* What the pane is showing, in the words the pane context bar uses.
|
|
255
|
-
*
|
|
256
|
-
* Derived from the command context — `editor.kind` and `canvas.view` — so it is the SAME two facts
|
|
257
|
-
* the Command Bar, the keyboard scope stack and every `when` predicate read.
|
|
258
|
-
*/
|
|
259
|
-
export function viewLabel(kind: EditorKind, view: string): string {
|
|
260
|
-
if (kind === "none") {
|
|
261
|
-
return "";
|
|
262
|
-
}
|
|
263
|
-
if (kind !== "canvas") {
|
|
264
|
-
return EDITOR_KIND_LABELS[kind];
|
|
265
|
-
}
|
|
266
|
-
return view.charAt(0).toUpperCase() + view.slice(1);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
/**
|
|
270
|
-
* The save state, in words.
|
|
271
|
-
*
|
|
272
|
-
* A dot glyph said one bit and required a legend nobody has. These four sentences say which of the
|
|
273
|
-
* four states the document is in, and the one that needs an action IS the action: "Unsaved changes"
|
|
274
|
-
* renders as the `file.save` button.
|
|
275
|
-
*/
|
|
276
|
-
function saveItem(dirty: boolean, readOnly: boolean, savedAt: number | undefined): StatusItem {
|
|
277
|
-
if (readOnly) {
|
|
278
|
-
return { command: null, label: "Read-only", title: "A collaborator owns this session's file" };
|
|
279
|
-
}
|
|
280
|
-
if (dirty) {
|
|
281
|
-
return { command: "file.save", label: "Unsaved changes" };
|
|
282
|
-
}
|
|
283
|
-
return savedAt === undefined
|
|
284
|
-
? { command: null, label: "Saved" }
|
|
285
|
-
: { command: null, label: `Saved ${relativeTime(savedAt)}` };
|
|
286
|
-
}
|
|
287
|
-
|
|
288
|
-
function documentField(registry: CommandRegistry | null): ProjectedField | null {
|
|
289
|
-
const tab = activeTab.value;
|
|
290
|
-
if (!tab) {
|
|
291
|
-
return null;
|
|
292
|
-
}
|
|
293
|
-
const ctx = registry?.context() ?? null;
|
|
294
|
-
const view = ctx ? viewLabel(ctx.editor.kind, ctx.canvas.view) : "";
|
|
295
|
-
const collab = collabState(tab);
|
|
296
|
-
const savedAt = tab.documentPath === null ? undefined : _savedAt.get(tab.documentPath);
|
|
297
|
-
return projectField(registry, "document", [
|
|
298
|
-
{
|
|
299
|
-
command: "palette.openFiles",
|
|
300
|
-
label: documentLabel(tab.documentPath),
|
|
301
|
-
title: tab.documentPath ?? "Not saved to disk yet",
|
|
302
|
-
},
|
|
303
|
-
view
|
|
304
|
-
? { command: null, label: view, title: "The pane's view — change it on the pane context bar" }
|
|
305
|
-
: null,
|
|
306
|
-
saveItem(tab.doc.dirty, collab.readOnly, savedAt),
|
|
307
|
-
]);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
221
|
// ─── SELECTION ───────────────────────────────────────────────────────────────
|
|
311
222
|
|
|
312
223
|
/**
|
|
313
224
|
* What is selected — the COUNT, and nothing that is an address.
|
|
314
225
|
*
|
|
315
|
-
*
|
|
316
|
-
*
|
|
317
|
-
*
|
|
318
|
-
*
|
|
319
|
-
*
|
|
320
|
-
* What is left is what the jump bar CANNOT say, and both are ambient state in the §16.2 sense: how
|
|
321
|
-
* many things are selected (a count is not a path, and the jump bar names only the primary), and
|
|
322
|
-
* which style rule the Stylebook is editing (a CSS selector is not a node in this document).
|
|
226
|
+
* The ancestor trail is the DOCUMENT field to its left (`panels/jump-bar.ts`). What is left here is
|
|
227
|
+
* what a trail CANNOT say, and both are ambient state in the §16.2 sense: how many things are
|
|
228
|
+
* selected (a count is not a path, and the jump bar names only the primary), and which style rule
|
|
229
|
+
* the Stylebook is editing (a CSS selector is not a node in this document).
|
|
323
230
|
*
|
|
324
|
-
* A single selection therefore leaves this field empty. That is deliberate — the
|
|
325
|
-
*
|
|
231
|
+
* A single selection therefore leaves this field empty. That is deliberate — the trail's leaf
|
|
232
|
+
* states it permanently, with its ancestors, one field to the left.
|
|
326
233
|
*/
|
|
327
234
|
function selectionField(registry: CommandRegistry | null): ProjectedField | null {
|
|
328
235
|
const paths = activeTab.value?.session.selection ?? [];
|
|
@@ -334,7 +241,7 @@ function selectionField(registry: CommandRegistry | null): ProjectedField | null
|
|
|
334
241
|
{
|
|
335
242
|
command: null,
|
|
336
243
|
label: `${paths.length} selected`,
|
|
337
|
-
title: `${paths.length} elements are selected; the
|
|
244
|
+
title: `${paths.length} elements are selected; the trail names the primary`,
|
|
338
245
|
},
|
|
339
246
|
])
|
|
340
247
|
: null;
|
|
@@ -354,16 +261,18 @@ function selectionField(registry: CommandRegistry | null): ProjectedField | null
|
|
|
354
261
|
|
|
355
262
|
// ─── The bar ─────────────────────────────────────────────────────────────────
|
|
356
263
|
|
|
357
|
-
/**
|
|
358
|
-
function
|
|
359
|
-
|
|
360
|
-
return [projectField_(registry), documentField(registry), selectionField(registry)].filter(
|
|
361
|
-
(field): field is ProjectedField => field !== null,
|
|
362
|
-
);
|
|
264
|
+
/** Drop the absent fields. */
|
|
265
|
+
function present(fields: readonly (ProjectedField | null)[]): ProjectedField[] {
|
|
266
|
+
return fields.filter((field): field is ProjectedField => field !== null);
|
|
363
267
|
}
|
|
364
268
|
|
|
365
269
|
interface StatusbarScope extends Record<string, unknown> {
|
|
366
|
-
fields:
|
|
270
|
+
/** The fields before the DOCUMENT slot: PROJECT. */
|
|
271
|
+
leading: ProjectedField[];
|
|
272
|
+
/** Whether the DOCUMENT slot has a trail to show. */
|
|
273
|
+
hasDocument: boolean;
|
|
274
|
+
/** The fields after it: SELECTION. */
|
|
275
|
+
trailing: ProjectedField[];
|
|
367
276
|
run: (key: string) => void;
|
|
368
277
|
}
|
|
369
278
|
|
|
@@ -374,26 +283,40 @@ let _handle: SurfaceHandle | null = null;
|
|
|
374
283
|
/** The reactive scope the surface reads, made once. */
|
|
375
284
|
function state(): StatusbarScope {
|
|
376
285
|
_state ??= reactive({
|
|
377
|
-
|
|
286
|
+
hasDocument: false,
|
|
287
|
+
leading: [],
|
|
378
288
|
run: (key: string) => {
|
|
379
|
-
const item = _state?.
|
|
289
|
+
const item = [...(_state?.leading ?? []), ...(_state?.trailing ?? [])]
|
|
290
|
+
.flatMap((field) => field.items)
|
|
291
|
+
.find((i) => i.key === key);
|
|
380
292
|
const registry = activeRegistry();
|
|
381
293
|
if (item?.command && registry) {
|
|
382
294
|
void runReported(registry, item.command, item.args as never, "Status Bar");
|
|
383
295
|
}
|
|
384
296
|
},
|
|
297
|
+
trailing: [],
|
|
385
298
|
}) as StatusbarScope;
|
|
386
299
|
return _state;
|
|
387
300
|
}
|
|
388
301
|
|
|
389
|
-
/**
|
|
302
|
+
/**
|
|
303
|
+
* Mount the surface into the bar's host, once, and hand its DOCUMENT slot to the jump bar.
|
|
304
|
+
*
|
|
305
|
+
* The slot is a box this document draws empty; the jump bar mounts its own document into it, which
|
|
306
|
+
* is the same hand-over the pane grid gives the tab strip and the context bar.
|
|
307
|
+
*/
|
|
390
308
|
function ensureMounted(): void {
|
|
391
309
|
if (_mount || !statusbarEl) {
|
|
392
310
|
return;
|
|
393
311
|
}
|
|
394
|
-
|
|
395
|
-
|
|
312
|
+
const mount = mountSurface("statusbar", state(), statusbarEl);
|
|
313
|
+
_mount = mount;
|
|
314
|
+
void mount.then((handle) => {
|
|
315
|
+
if (_mount !== mount) {
|
|
316
|
+
return;
|
|
317
|
+
}
|
|
396
318
|
_handle = handle;
|
|
319
|
+
attachJumpBarHost(statusbarEl.querySelector<HTMLElement>('[part="trail"]'));
|
|
397
320
|
});
|
|
398
321
|
}
|
|
399
322
|
|
|
@@ -402,13 +325,23 @@ function ensureMounted(): void {
|
|
|
402
325
|
* mounting the effect.
|
|
403
326
|
*/
|
|
404
327
|
export function renderStatusbar(): void {
|
|
405
|
-
|
|
328
|
+
const registry = activeRegistry();
|
|
329
|
+
const scope = state();
|
|
330
|
+
scope.leading = present([projectField_(registry)]);
|
|
331
|
+
scope.hasDocument = activeTab.value !== null;
|
|
332
|
+
scope.trailing = present([selectionField(registry)]);
|
|
406
333
|
ensureMounted();
|
|
407
334
|
}
|
|
408
335
|
|
|
409
|
-
/**
|
|
336
|
+
/**
|
|
337
|
+
* Subscribe the bar to the state it renders, and its DOCUMENT field's trail with it. Idempotent.
|
|
338
|
+
*
|
|
339
|
+
* The trail is this bar's field, so this bar owns its lifecycle: the jump bar's effect starts here
|
|
340
|
+
* and stops in {@link unmountStatusbar}, and its host arrives from {@link ensureMounted}.
|
|
341
|
+
*/
|
|
410
342
|
export function mountStatusbar(): void {
|
|
411
343
|
unmountStatusbar();
|
|
344
|
+
mountJumpBar();
|
|
412
345
|
_scope = effectScope();
|
|
413
346
|
_scope.run(() => {
|
|
414
347
|
effect(() => {
|
|
@@ -420,15 +353,8 @@ export function mountStatusbar(): void {
|
|
|
420
353
|
void problems.length;
|
|
421
354
|
const tab = activeTab.value;
|
|
422
355
|
if (tab) {
|
|
423
|
-
void tab.
|
|
424
|
-
void tab.doc.dirty;
|
|
425
|
-
void tab.doc.mode;
|
|
426
|
-
void tab.documentPath;
|
|
427
|
-
void tab.session.selection.map((path) => path.join("/")).join("|");
|
|
428
|
-
void tab.session.ui.canvasMode;
|
|
429
|
-
void tab.session.ui.preview;
|
|
356
|
+
void tab.session.selection.length;
|
|
430
357
|
void collabState(tab).peers.length;
|
|
431
|
-
void collabState(tab).readOnly;
|
|
432
358
|
}
|
|
433
359
|
renderStatusbar();
|
|
434
360
|
});
|
|
@@ -440,6 +366,7 @@ export function unmountStatusbar(): void {
|
|
|
440
366
|
_scope = null;
|
|
441
367
|
const pending = _mount;
|
|
442
368
|
_mount = null;
|
|
369
|
+
unmountJumpBar();
|
|
443
370
|
if (_handle) {
|
|
444
371
|
_handle.dispose();
|
|
445
372
|
_handle = null;
|
|
@@ -1013,6 +1013,7 @@
|
|
|
1013
1013
|
{
|
|
1014
1014
|
"tagName": "jx-action-group",
|
|
1015
1015
|
"$props": {
|
|
1016
|
+
"selects": "single",
|
|
1016
1017
|
"compact": true,
|
|
1017
1018
|
"label": {
|
|
1018
1019
|
"$ref": "$map/item/label"
|
|
@@ -1044,9 +1045,8 @@
|
|
|
1044
1045
|
"hint": {
|
|
1045
1046
|
"$ref": "$map/item/title"
|
|
1046
1047
|
},
|
|
1047
|
-
"
|
|
1048
|
-
|
|
1049
|
-
"$ref": "$map/item/selected"
|
|
1048
|
+
"checked": {
|
|
1049
|
+
"$ref": "$map/item/checked"
|
|
1050
1050
|
}
|
|
1051
1051
|
},
|
|
1052
1052
|
"onclick": {
|
|
@@ -108,7 +108,17 @@ export interface StyleButtonView extends Record<string, unknown> {
|
|
|
108
108
|
icon: string;
|
|
109
109
|
/** The abbreviation drawn when there is no glyph. Empty when there is one, and its span hidden. */
|
|
110
110
|
text: string;
|
|
111
|
-
|
|
111
|
+
/**
|
|
112
|
+
* `"true"` / `"false"` — a row's buttons are RADIOS, one choice of several, and never toggles.
|
|
113
|
+
*
|
|
114
|
+
* The value is a fact about the document, so the button may draw it and must not decide it. With
|
|
115
|
+
* the kit's `toggles` on, the button flipped its own `selected` in a click listener that ran
|
|
116
|
+
* AFTER the row's: a real click flushes microtasks between listeners, so the projection had
|
|
117
|
+
* already drawn the committed value when the flip inverted it, and Display read unset after
|
|
118
|
+
* pressing Flex and set after pressing it again. `checked` draws the state without the button
|
|
119
|
+
* ever writing it.
|
|
120
|
+
*/
|
|
121
|
+
checked: "true" | "false";
|
|
112
122
|
}
|
|
113
123
|
|
|
114
124
|
/**
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{Df,D_,A_,v$,N_}from"./studio-vz7b43ng.js";import"./studio-tj28pxa1.js";import"./studio-6k1eswp9.js";import"./studio-0nm2skex.js";export{N_ as clearCfConnection,A_ as getCfAccountId,Df as getCfToken,v$ as setCfAccountId,D_ as setCfToken};
|
|
2
|
-
|
|
3
|
-
//# debugId=88A9B73C42F1EA0C64756E2164756E21
|
|
4
|
-
//# sourceMappingURL=cf-settings-fpa485q3.js.map
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{at,Dl,FC,fm,xc}from"./studio-y164brht.js";import"./studio-6k1eswp9.js";export{fm as buildComponentInstance,at as componentRegistry,xc as computeRelativePath,Dl as loadComponentRegistry,FC as noteComponentSaved};
|
|
2
|
-
|
|
3
|
-
//# debugId=1441451654480D2F64756E2164756E21
|
|
4
|
-
//# sourceMappingURL=components-b0vh4sq8.js.map
|
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{vb,Pw,I1,R1,$1,D1}from"./studio-4yztve2t.js";import"./studio-eb5sty2k.js";import"./studio-b5rxwkj2.js";import"./studio-02y9cb4h.js";import"./studio-sgzss898.js";import"./studio-sadckcpp.js";import"./studio-28pz4s1k.js";import"./studio-njmg0e73.js";import"./studio-81twpmzj.js";import"./studio-pvj970st.js";import"./studio-w8w9frjy.js";import"./studio-y164brht.js";import"./studio-tj28pxa1.js";import"./studio-ant1zbp3.js";import"./studio-011em5cf.js";import"./studio-k9pwvmz8.js";import"./studio-27tc8q0h.js";import"./studio-654fxcr0.js";import"./studio-7w04zw8h.js";import"./studio-ft8pfpp0.js";import"./studio-6k1eswp9.js";import"./studio-ks0ecgjx.js";import"./studio-z1vdxq0e.js";import"./studio-dmymvk2j.js";import"./studio-dknr8m7b.js";import"./studio-b29h7g7s.js";import"./studio-ery42sqr.js";import"./studio-0nm2skex.js";export{Pw as collectionDirs,vb as collectionInfo,$1 as createCollectionSource,D1 as createPagesSource,I1 as listCollectionEntryIds,R1 as reloadDraftAwareGrids};
|
|
2
|
-
|
|
3
|
-
//# debugId=43775DBD531D604D64756E2164756E21
|
|
4
|
-
//# sourceMappingURL=content-source-z5t0d0h9.js.map
|