@jxsuite/studio 4.1.0 → 5.0.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/README.md +1 -1
- package/data/elements-meta.json +5 -15
- package/data/html-meta.json +51 -6
- package/dist/chunks/{2020-n6tfwnda.js → 2020-g79n01v9.js} +14 -14
- package/dist/chunks/action-group-sp6tna8v.js +118 -0
- package/dist/chunks/adapter-nj7tnjfn.js +13 -0
- package/dist/chunks/cf-account-picker-fyf63pmy.js +495 -0
- package/dist/chunks/cf-settings-71bv5zn9.js +20 -0
- package/dist/chunks/color-area-v17df7b8.js +154 -0
- package/dist/chunks/color-field-anwybnkn.js +208 -0
- package/dist/chunks/color-slider-44ez96fc.js +79 -0
- package/dist/chunks/color-xgg3kfw7.js +35 -0
- package/dist/chunks/combobox-5e7jev23.js +236 -0
- package/dist/chunks/components-2w1f35qs.js +18 -0
- package/dist/chunks/content-source-3wqeas45.js +46 -0
- package/dist/chunks/convert-to-repeater-d0cgnb6d.js +639 -0
- package/dist/chunks/dialog-s890b918.js +85 -0
- package/dist/chunks/editor-3qhbg51x.js +35 -0
- package/dist/chunks/entry-commands-d6g50j7s.js +42 -0
- package/dist/chunks/extension-sections-fy7q4fe9.js +651 -0
- package/dist/chunks/file-ops-3btj6st0.js +47 -0
- package/dist/chunks/files-ef8fgs3w.js +72 -0
- package/dist/chunks/format-host-3c33nwr8.js +40 -0
- package/dist/chunks/fs-events-13tj6sex.js +18 -0
- package/dist/chunks/git-panel-4wp2rn9b.js +49 -0
- package/dist/chunks/icons-yy10wk9r.js +402 -0
- package/dist/chunks/iframe-entry-15g3n6xv.js +114 -0
- package/dist/chunks/iframe-entry-b27vq81n.js +203 -0
- package/dist/chunks/iframe-entry-nbyxee1y.js +154 -0
- package/dist/chunks/iframe-host-jgesd924.js +115 -0
- package/dist/chunks/{index-wcnt3r9g.js → index-mdmyx6s2.js} +11 -11
- package/dist/chunks/index-n4j7qxyr.js +109 -0
- package/dist/chunks/{javascript-q8kpmfah.js → javascript-xvrfrpkb.js} +8 -9
- package/dist/chunks/{jsonMode-dkr040ga.js → jsonMode-tyx2kj6p.js} +526 -527
- package/dist/chunks/jx-validate-j0wxk72w.js +15 -0
- package/dist/chunks/layers-1nq5evsh.js +29 -0
- package/dist/chunks/layers-panel-ar2gh90g.js +40 -0
- package/dist/chunks/listbox-6v2ac08j.js +44 -0
- package/dist/chunks/lit-html-m244w9nh.js +17 -0
- package/dist/chunks/media-picker-2qbetq73.js +33 -0
- package/dist/chunks/menu-q99sy9rf.js +285 -0
- package/dist/chunks/menu-rf7n72mf.js +14 -0
- package/dist/chunks/{monaco-binding-rj6xpx9y.js → monaco-binding-sw6en4dw.js} +12 -15
- package/dist/chunks/monaco-buffer-bhy3hb6m.js +24 -0
- package/dist/chunks/{monaco-setup-vx9bhp1d.js → monaco-setup-8r62fj6y.js} +27 -37
- package/dist/chunks/notify-628t0anh.js +21 -0
- package/dist/chunks/number-field-5cndyft8.js +72 -0
- package/dist/chunks/parse-0zx14y15.js +10 -0
- package/dist/chunks/platform-ts9cg1a4.js +15 -0
- package/dist/chunks/popover-47z3ytva.js +25 -0
- package/dist/chunks/publish-panel-98f7sr3g.js +27 -0
- package/dist/chunks/quick-search-srzz83k2.js +45 -0
- package/dist/chunks/references-n553f0e1.js +26 -0
- package/dist/chunks/right-panel-rrgx2dn3.js +45 -0
- package/dist/chunks/section-registry-5pq32t0q.js +25 -0
- package/dist/chunks/select-2r16ez41.js +49 -0
- package/dist/chunks/split-g7nm1rgb.js +191 -0
- package/dist/chunks/studio-0hqbpbd0.js +62 -0
- package/dist/chunks/studio-18avnhjp.js +2430 -0
- package/dist/chunks/{studio-kryvsw6j.js → studio-1k325pwf.js} +48 -48
- package/dist/chunks/{studio-msd847zn.js → studio-25hkdrkw.js} +357 -86
- package/dist/chunks/studio-26d7myd1.js +258 -0
- package/dist/chunks/{studio-4r1pv6zf.js → studio-38emantv.js} +22 -5
- package/dist/chunks/studio-3gxv60tn.js +422 -0
- package/dist/chunks/{studio-fbf92ckf.js → studio-3hxkd92t.js} +275 -163
- package/dist/chunks/studio-4m0haseg.js +185 -0
- package/dist/chunks/{studio-ynxwkrzd.js → studio-4wbb9ct0.js} +1953 -1785
- package/dist/chunks/{studio-1wagb550.js → studio-5523bpzt.js} +11 -11
- package/dist/chunks/{studio-x4qkdat5.js → studio-57mf66w2.js} +1749 -2481
- package/dist/chunks/studio-5jy81t3s.js +17463 -0
- package/dist/chunks/studio-5rt7sqa0.js +1168 -0
- package/dist/chunks/studio-5te5tqng.js +81 -0
- package/dist/chunks/{studio-p4ngc046.js → studio-6ewqv390.js} +9 -9
- package/dist/chunks/{studio-9ab8gqsm.js → studio-6nzaktvy.js} +7 -7
- package/dist/chunks/studio-6xa21ej9.js +28 -0
- package/dist/chunks/{studio-5ehhackm.js → studio-727xh0e3.js} +19 -19
- package/dist/chunks/{studio-ahn3pdgx.js → studio-79y5sz51.js} +52 -38
- package/dist/chunks/{studio-gcye7qka.js → studio-7e11jpa9.js} +21 -21
- package/dist/chunks/{studio-q7v10cg2.js → studio-7r42c6ca.js} +17 -7
- package/dist/chunks/studio-7xptrg0j.js +954 -0
- package/dist/chunks/{studio-cft9r4r7.js → studio-81390n6f.js} +22 -22
- package/dist/chunks/{studio-q43a5y9h.js → studio-8jn5yhyn.js} +16 -16
- package/dist/chunks/studio-8qtrpzr3.js +839 -0
- package/dist/chunks/{studio-dg7zyt5x.js → studio-98b3z07n.js} +1 -1
- package/dist/chunks/studio-9xxz1q69.js +1356 -0
- package/dist/chunks/studio-akbww0j5.js +13615 -0
- package/dist/chunks/{studio-x813zp48.js → studio-arfpe389.js} +30 -30
- package/dist/chunks/{studio-trxrabqf.js → studio-az3zd49t.js} +475 -283
- package/dist/chunks/studio-cpvb2ed1.js +1171 -0
- package/dist/chunks/{studio-f3wqjtk5.js → studio-d0300636.js} +5076 -18
- package/dist/chunks/{studio-bgxt93ga.js → studio-d055c37e.js} +13 -13
- package/dist/chunks/studio-d1ja2x9r.js +362 -0
- package/dist/chunks/{studio-b9b6t1dg.js → studio-e1f24eff.js} +5664 -5666
- package/dist/chunks/{studio-3bqpr0zh.js → studio-em9nw8eh.js} +299 -110
- package/dist/chunks/studio-erdatzvp.js +1190 -0
- package/dist/chunks/{studio-g1wdche0.js → studio-esrxgwh6.js} +80 -80
- package/dist/chunks/studio-fabxk6r9.js +2811 -0
- package/dist/chunks/{studio-3n4ndrzh.js → studio-h4nvnjvz.js} +303 -303
- package/dist/chunks/{studio-dm19q43r.js → studio-hhvs4sdg.js} +10 -10
- package/dist/chunks/{studio-q9jd1a60.js → studio-kcgff4r0.js} +15 -18
- package/dist/chunks/{studio-685s0ytq.js → studio-nqgawfks.js} +572 -572
- package/dist/chunks/{studio-xrapxwwc.js → studio-p1dw8wfw.js} +55 -29
- package/dist/chunks/studio-pm6hgy7r.js +4763 -0
- package/dist/chunks/studio-q0a3yk4n.js +33 -0
- package/dist/chunks/{studio-wcy8trfx.js → studio-q3x06vmj.js} +23 -22
- package/dist/chunks/studio-qahxvnan.js +111 -0
- package/dist/chunks/{studio-53n8bp04.js → studio-r6xmmtfh.js} +1 -1
- package/dist/chunks/{studio-2b946z26.js → studio-rkstz940.js} +3658 -6134
- package/dist/chunks/{studio-tvgyw9xw.js → studio-rnyf3f8e.js} +83 -250
- package/dist/chunks/studio-rpyah2ew.js +2805 -0
- package/dist/chunks/studio-rs76k67r.js +12 -0
- package/dist/chunks/{studio-jkftxyvz.js → studio-s6z853aq.js} +5858 -2152
- package/dist/chunks/{studio-6yw4y3my.js → studio-sgdc5pfc.js} +8 -8
- package/dist/chunks/studio-tb3ev01j.js +763 -0
- package/dist/chunks/{studio-t5wqwzdh.js → studio-tjjk7jvk.js} +1 -1
- package/dist/chunks/studio-vhv5tgxm.js +51 -0
- package/dist/chunks/{studio-qjy2z9bv.js → studio-vz68yn9e.js} +32 -137
- package/dist/chunks/studio-wqpngpsk.js +174 -0
- package/dist/chunks/{studio-b55y6ewx.js → studio-x3rty6tr.js} +58 -58
- package/dist/chunks/studio-xsykpvsh.js +8448 -0
- package/dist/chunks/{studio-92ab2jqe.js → studio-y9zedyjn.js} +14 -14
- package/dist/chunks/studio-yxa81f45.js +12 -0
- package/dist/chunks/swatch-group-t0n6h2rn.js +139 -0
- package/dist/chunks/tabs-wbp1cdws.js +156 -0
- package/dist/chunks/textfield-abk6qyee.js +73 -0
- package/dist/chunks/toast-780pvfbv.js +27 -0
- package/dist/chunks/toast-host-3sqv46sn.js +116 -0
- package/dist/chunks/toolbar-xbh5bhtr.js +194 -0
- package/dist/chunks/tooltip-eptb614w.js +369 -0
- package/dist/chunks/tree-a73p0pqm.js +367 -0
- package/dist/chunks/{tsMode-76kr53q0.js → tsMode-jy4m86rv.js} +83 -85
- package/dist/chunks/workspace-wrew8zk4.js +69 -0
- package/dist/chunks/{ws-client-6shc5rze.js → ws-client-ny4zc51r.js} +20 -22
- package/dist/iframe-entry.js +1780 -667
- package/dist/studio.js +52819 -36480
- package/dist/workers/editor.worker.js +1040 -1085
- package/dist/workers/json.worker.js +1946 -1991
- package/dist/workers/ts.worker.js +4960 -5005
- package/index.html +1 -0
- package/package.json +18 -52
- package/src/about/about-modal.ts +72 -123
- package/src/browse/library-commands.ts +20 -49
- package/src/browse/library-layouts.ts +87 -218
- package/src/browse/library-pane.ts +406 -438
- package/src/canvas/canvas-helpers.ts +0 -49
- package/src/canvas/canvas-live-render.ts +25 -2
- package/src/canvas/canvas-render.ts +666 -231
- package/src/canvas/canvas-surface.ts +0 -1
- package/src/canvas/canvas-utils.ts +310 -117
- package/src/canvas/dialog-path.ts +76 -0
- package/src/canvas/dialog-state.ts +67 -0
- package/src/canvas/diff-marks.ts +252 -0
- package/src/canvas/diff-toolbar.ts +349 -0
- package/src/canvas/diff-view.ts +123 -0
- package/src/canvas/edit-width-drag.ts +187 -0
- package/src/canvas/edit-width.ts +150 -0
- package/src/canvas/iframe-entry.ts +125 -8
- package/src/canvas/iframe-host.ts +271 -15
- package/src/canvas/iframe-interaction.ts +133 -13
- package/src/canvas/iframe-patch.ts +7 -1
- package/src/canvas/iframe-position.ts +5 -6
- package/src/canvas/iframe-protocol.ts +106 -5
- package/src/canvas/iframe-render.ts +373 -5
- package/src/canvas/iframe-slash.ts +2 -2
- package/src/canvas/iframe-subtree.ts +5 -8
- package/src/canvas/path-mapping.ts +25 -3
- package/src/canvas/popover-path.ts +106 -0
- package/src/canvas/popover-state.ts +155 -0
- package/src/canvas/surface-registry.ts +21 -10
- package/src/collab/presence-chips.ts +51 -73
- package/src/commands/app-commands.ts +10 -0
- package/src/commands/budget.ts +12 -0
- package/src/commands/command-args.ts +369 -6
- package/src/commands/defaults.ts +44 -14
- package/src/commands/live-context.ts +11 -5
- package/src/commands/reference.ts +12 -2
- package/src/commands/registry.ts +165 -8
- package/src/content/entry-commands.ts +16 -23
- package/src/content/entry-editor.ts +144 -101
- package/src/editor/canvas-slash-bridge.ts +2 -2
- package/src/editor/context-menu.ts +49 -200
- package/src/editor/convert-to-component.ts +42 -82
- package/src/editor/convert-to-repeater.ts +132 -173
- package/src/editor/inline-edit.ts +11 -0
- package/src/editor/shortcuts.ts +65 -26
- package/src/editor/slash-menu.ts +150 -157
- package/src/files/file-ops.ts +117 -11
- package/src/files/files.ts +943 -660
- package/src/files/json-layout.ts +474 -0
- package/src/files/serialize-document.ts +8 -1
- package/src/format/convert-file.ts +16 -10
- package/src/format/format-host.ts +50 -1
- package/src/github/github-auth.ts +71 -29
- package/src/github/github-publish.ts +57 -65
- package/src/grid/cell-editors.ts +46 -4
- package/src/grid/cell-popovers.ts +65 -62
- package/src/grid/grid-open.ts +86 -64
- package/src/grid/grid-panel.ts +390 -469
- package/src/grid/redirects-grid.ts +40 -44
- package/src/hosting/layout.ts +2 -0
- package/src/i18n/i18n-commands.ts +127 -62
- package/src/media/media-pane.ts +83 -133
- package/src/new-project/add-repo-modal.ts +125 -176
- package/src/new-project/import-tab.ts +181 -252
- package/src/new-project/location-fields.ts +93 -160
- package/src/new-project/new-project-modal.ts +376 -367
- package/src/packages/jxsuite-update.ts +76 -18
- package/src/panels/activity-panel.ts +137 -104
- package/src/panels/ai-chat/chat-markdown.ts +15 -13
- package/src/panels/ai-chat/chat-view.ts +307 -472
- package/src/panels/ai-chat/composer.ts +175 -163
- package/src/panels/ai-chat/sessions-view.ts +31 -62
- package/src/panels/ai-panel.ts +210 -185
- package/src/panels/block-action-bar.ts +437 -643
- package/src/panels/bottom-dock.ts +112 -47
- package/src/panels/chat-panel.ts +19 -14
- package/src/panels/data-explorer.ts +141 -102
- package/src/panels/data-grid.ts +183 -150
- package/src/panels/dnd.ts +75 -20
- package/src/panels/editors.ts +26 -19
- package/src/panels/elements-panel.ts +243 -186
- package/src/panels/empty-state.ts +26 -42
- package/src/panels/events-panel.ts +950 -729
- package/src/panels/formula-workspace.ts +434 -207
- package/src/panels/frontmatter-fields.ts +185 -153
- package/src/panels/frontmatter-panel.ts +411 -187
- package/src/panels/git-diff-open.ts +148 -0
- package/src/panels/git-panel.ts +679 -605
- package/src/panels/head-panel.ts +617 -435
- package/src/panels/i18n-panel.ts +153 -133
- package/src/panels/imports-panel.ts +388 -414
- package/src/panels/jump-bar.ts +174 -147
- package/src/panels/layers-panel.ts +785 -671
- package/src/panels/left-panel.ts +166 -94
- package/src/panels/navigator-panels.ts +1 -1
- package/src/panels/pane-context.ts +529 -677
- package/src/panels/pane-grid.ts +265 -241
- package/src/panels/panel-registry.ts +15 -4
- package/src/panels/problems-panel.ts +177 -99
- package/src/panels/properties-panel.ts +1525 -966
- package/src/panels/provenance.ts +31 -113
- package/src/panels/quick-search.ts +217 -172
- package/src/panels/right-panel.ts +135 -169
- package/src/panels/seo-modal.ts +346 -167
- package/src/panels/settings-menu.ts +109 -646
- package/src/panels/settings-pane.ts +102 -86
- package/src/panels/signals-panel.ts +1318 -1172
- package/src/panels/statement-editor.ts +556 -428
- package/src/panels/style-panel.ts +1367 -630
- package/src/panels/style-utils.ts +36 -32
- package/src/panels/stylebook-doc.ts +30 -73
- package/src/panels/stylebook-layers-panel.ts +202 -109
- package/src/panels/stylebook-panel.ts +124 -88
- package/src/panels/tab-strip.ts +385 -287
- package/src/platforms/cloud.ts +106 -16
- package/src/platforms/devserver.ts +13 -0
- package/src/publish/deploy-checklist.ts +124 -78
- package/src/publish/publish-commands.ts +4 -12
- package/src/publish/publish-panel.ts +134 -328
- package/src/services/a11y-report.ts +100 -43
- package/src/services/ai-command-tools.ts +468 -0
- package/src/services/ai-session-store.ts +6 -0
- package/src/services/ai-system-prompt.ts +110 -18
- package/src/services/ai-tools.ts +28 -143
- package/src/services/ai-write-report.ts +174 -0
- package/src/services/document-assistant.ts +50 -2
- package/src/services/idle.ts +16 -14
- package/src/services/live-surfaces.ts +237 -0
- package/src/services/model-uri.ts +75 -0
- package/src/services/notify.ts +63 -6
- package/src/services/popover-report.ts +325 -0
- package/src/services/surface-registry.ts +56 -0
- package/src/services/tool-executor.ts +6 -0
- package/src/settings/contexts-section.ts +301 -299
- package/src/settings/contributed-section.ts +361 -222
- package/src/settings/css-vars-editor.ts +320 -325
- package/src/settings/defs-editor.ts +572 -535
- package/src/settings/dependencies-editor.ts +79 -107
- package/src/settings/extension-commands.ts +379 -0
- package/src/settings/extension-rows.ts +113 -0
- package/src/settings/extensions-section.ts +287 -0
- package/src/settings/general-settings.ts +130 -181
- package/src/settings/head-editor.ts +10 -305
- package/src/settings/locales-section.ts +146 -196
- package/src/settings/preferences-accounts.ts +1 -1
- package/src/settings/preferences-dialog.ts +350 -330
- package/src/settings/project-sections.ts +108 -181
- package/src/settings/schema-field-ui.ts +11 -399
- package/src/settings/section-registry.ts +8 -1
- package/src/settings/settings-document.ts +31 -29
- package/src/shell/tree.ts +42 -87
- package/src/shell.ts +48 -36
- package/src/studio.ts +90 -67
- package/src/style/token-ref.ts +0 -39
- package/src/surfaces/about.json +263 -0
- package/src/surfaces/about.ts +163 -0
- package/src/surfaces/add-repo.json +486 -0
- package/src/surfaces/add-repo.ts +236 -0
- package/src/surfaces/ai-chat.json +2185 -0
- package/src/surfaces/ai-chat.ts +549 -0
- package/src/surfaces/ai-credentials-form.json +332 -0
- package/src/surfaces/ai-credentials-form.ts +151 -0
- package/src/surfaces/ai-managed-connect.json +108 -0
- package/src/surfaces/ai-managed-connect.ts +123 -0
- package/src/surfaces/ai-model-picker.json +114 -0
- package/src/surfaces/ai-model-picker.ts +139 -0
- package/src/surfaces/block-action-bar.json +640 -0
- package/src/surfaces/block-action-bar.ts +368 -0
- package/src/surfaces/bottom-dock.json +154 -0
- package/src/surfaces/bottom-dock.ts +125 -0
- package/src/surfaces/canvas-stage.json +444 -0
- package/src/surfaces/canvas-stage.ts +253 -0
- package/src/surfaces/cf-account-picker.json +300 -0
- package/src/surfaces/cf-account-picker.ts +214 -0
- package/src/surfaces/commandbar.json +984 -0
- package/src/{panels/toolbar.ts → surfaces/commandbar.ts} +414 -389
- package/src/surfaces/convert-repeater.json +309 -0
- package/src/surfaces/convert-repeater.ts +276 -0
- package/src/surfaces/data-actions.json +157 -0
- package/src/surfaces/data-actions.ts +115 -0
- package/src/surfaces/dialog.json +289 -0
- package/src/surfaces/dialog.ts +343 -0
- package/src/surfaces/diff-toolbar.json +266 -0
- package/src/surfaces/diff-toolbar.ts +139 -0
- package/src/surfaces/doc-header.json +739 -0
- package/src/surfaces/doc-header.ts +223 -0
- package/src/surfaces/empty-state.json +152 -0
- package/src/surfaces/empty-state.ts +173 -0
- package/src/surfaces/entry-editor.json +281 -0
- package/src/surfaces/entry-editor.ts +191 -0
- package/src/surfaces/expression-editor.json +977 -0
- package/src/surfaces/expression-editor.ts +266 -0
- package/src/surfaces/files-panel.json +592 -0
- package/src/surfaces/files-panel.ts +301 -0
- package/src/surfaces/formula-palette.json +364 -0
- package/src/surfaces/formula-palette.ts +340 -0
- package/src/surfaces/git-panel.json +1822 -0
- package/src/surfaces/git-panel.ts +272 -0
- package/src/surfaces/github-auth.json +112 -0
- package/src/surfaces/github-auth.ts +139 -0
- package/src/surfaces/github-publish.json +206 -0
- package/src/surfaces/github-publish.ts +169 -0
- package/src/surfaces/grid-cell.json +218 -0
- package/src/surfaces/grid-cell.ts +120 -0
- package/src/surfaces/grid-open.json +147 -0
- package/src/surfaces/grid-open.ts +87 -0
- package/src/surfaces/grid-panel.json +706 -0
- package/src/surfaces/grid-panel.ts +206 -0
- package/src/surfaces/grid-replace.json +179 -0
- package/src/surfaces/grid-replace.ts +89 -0
- package/src/surfaces/grid-views.json +446 -0
- package/src/surfaces/grid-views.ts +172 -0
- package/src/surfaces/inspector-dock.json +187 -0
- package/src/surfaces/inspector-dock.ts +161 -0
- package/src/surfaces/jump-bar.json +243 -0
- package/src/surfaces/jump-bar.ts +129 -0
- package/src/surfaces/jxsuite-update.json +146 -0
- package/src/surfaces/jxsuite-update.ts +149 -0
- package/src/surfaces/library-pane.json +1509 -0
- package/src/surfaces/library-pane.ts +363 -0
- package/src/surfaces/logic-panel.json +1949 -0
- package/src/surfaces/logic-panel.ts +369 -0
- package/src/surfaces/logic-workspace.json +685 -0
- package/src/surfaces/logic-workspace.ts +238 -0
- package/src/surfaces/media-browser.json +303 -0
- package/src/surfaces/media-browser.ts +145 -0
- package/src/surfaces/media-field.json +163 -0
- package/src/surfaces/media-field.ts +116 -0
- package/src/surfaces/media-pane.json +513 -0
- package/src/surfaces/media-pane.ts +197 -0
- package/src/surfaces/menu.json +232 -0
- package/src/surfaces/menu.ts +375 -0
- package/src/surfaces/navigator-dock.json +113 -0
- package/src/surfaces/navigator-dock.ts +130 -0
- package/src/surfaces/new-project.json +1654 -0
- package/src/surfaces/new-project.ts +411 -0
- package/src/surfaces/palette.json +535 -0
- package/src/surfaces/pane-context.json +1497 -0
- package/src/surfaces/pane-context.ts +513 -0
- package/src/surfaces/pane-grid.json +188 -0
- package/src/surfaces/pane-grid.ts +250 -0
- package/src/surfaces/panel-activity.json +563 -0
- package/src/surfaces/panel-activity.ts +187 -0
- package/src/surfaces/panel-data.json +141 -0
- package/src/surfaces/panel-data.ts +156 -0
- package/src/surfaces/panel-deploy-checklist.json +283 -0
- package/src/surfaces/panel-deploy-checklist.ts +175 -0
- package/src/surfaces/panel-elements.json +367 -0
- package/src/surfaces/panel-elements.ts +175 -0
- package/src/surfaces/panel-i18n.json +569 -0
- package/src/surfaces/panel-i18n.ts +212 -0
- package/src/surfaces/panel-imports.json +780 -0
- package/src/surfaces/panel-imports.ts +209 -0
- package/src/surfaces/panel-outline.json +734 -0
- package/src/surfaces/panel-outline.ts +326 -0
- package/src/surfaces/panel-page.json +1017 -0
- package/src/surfaces/panel-page.ts +240 -0
- package/src/surfaces/panel-problems.json +497 -0
- package/src/surfaces/panel-problems.ts +157 -0
- package/src/surfaces/panel-signals.json +1973 -0
- package/src/surfaces/panel-signals.ts +436 -0
- package/src/surfaces/panel-stylebook-layers.json +317 -0
- package/src/surfaces/panel-stylebook-layers.ts +155 -0
- package/src/surfaces/preferences.json +850 -0
- package/src/surfaces/preferences.ts +332 -0
- package/src/surfaces/progress-modal.json +110 -0
- package/src/surfaces/progress-modal.ts +150 -0
- package/src/surfaces/properties-panel.json +1578 -0
- package/src/surfaces/properties-panel.ts +337 -0
- package/src/surfaces/publish.json +945 -0
- package/src/surfaces/publish.ts +392 -0
- package/src/surfaces/push-plan.json +110 -0
- package/src/surfaces/push-plan.ts +87 -0
- package/src/surfaces/rail.json +255 -0
- package/src/surfaces/rail.ts +206 -0
- package/src/surfaces/reference-field.json +191 -0
- package/src/surfaces/reference-field.ts +168 -0
- package/src/surfaces/schema-builder.json +1011 -0
- package/src/surfaces/schema-builder.ts +181 -0
- package/src/surfaces/schema-form.json +1013 -0
- package/src/surfaces/schema-form.ts +287 -0
- package/src/surfaces/secret-field.json +60 -0
- package/src/surfaces/secret-field.ts +146 -0
- package/src/surfaces/seo.json +932 -0
- package/src/surfaces/seo.ts +334 -0
- package/src/surfaces/settings-contexts.json +538 -0
- package/src/surfaces/settings-contexts.ts +196 -0
- package/src/surfaces/settings-contributed.json +489 -0
- package/src/surfaces/settings-contributed.ts +199 -0
- package/src/surfaces/settings-css-vars.json +820 -0
- package/src/surfaces/settings-css-vars.ts +199 -0
- package/src/surfaces/settings-defs.json +1363 -0
- package/src/surfaces/settings-defs.ts +232 -0
- package/src/surfaces/settings-deploy.json +108 -0
- package/src/surfaces/settings-deploy.ts +135 -0
- package/src/surfaces/settings-extensions.json +486 -0
- package/src/surfaces/settings-extensions.ts +201 -0
- package/src/surfaces/settings-head.json +779 -0
- package/src/surfaces/settings-head.ts +397 -0
- package/src/surfaces/settings-locales.json +420 -0
- package/src/surfaces/settings-locales.ts +137 -0
- package/src/surfaces/settings-overview.json +423 -0
- package/src/surfaces/settings-overview.ts +218 -0
- package/src/surfaces/settings-packages.json +549 -0
- package/src/surfaces/settings-packages.ts +158 -0
- package/src/surfaces/settings-pane.json +147 -0
- package/src/surfaces/settings-pane.ts +150 -0
- package/src/surfaces/settings-rawjson.json +104 -0
- package/src/surfaces/settings-rawjson.ts +107 -0
- package/src/surfaces/shell.json +99 -0
- package/src/surfaces/shell.ts +30 -0
- package/src/surfaces/slash-menu.json +180 -0
- package/src/surfaces/slash-menu.ts +272 -0
- package/src/surfaces/statements.json +607 -0
- package/src/surfaces/statements.ts +238 -0
- package/src/surfaces/statusbar.json +94 -0
- package/src/{panels → surfaces}/statusbar.ts +163 -90
- package/src/surfaces/style-panel.json +1643 -0
- package/src/surfaces/style-panel.ts +338 -0
- package/src/surfaces/stylebook-chrome.json +122 -0
- package/src/surfaces/stylebook-chrome.ts +122 -0
- package/src/surfaces/tab-strip.json +448 -0
- package/src/surfaces/tab-strip.ts +200 -0
- package/src/surfaces/target-line.json +419 -0
- package/src/surfaces/target-line.ts +349 -0
- package/src/surfaces/toasts.json +189 -0
- package/src/surfaces/welcome.json +571 -0
- package/src/surfaces/welcome.ts +338 -0
- package/src/tabs/project-config.ts +71 -15
- package/src/tabs/tab.ts +52 -1
- package/src/types.ts +22 -0
- package/src/ui/ai-credentials-form.ts +104 -100
- package/src/ui/ai-managed-connect.ts +54 -36
- package/src/ui/ai-model-picker.ts +75 -47
- package/src/ui/cf-account-picker.ts +76 -141
- package/src/ui/color-selector.ts +80 -301
- package/src/ui/dynamic-slot.ts +43 -283
- package/src/ui/expression-editor.ts +797 -576
- package/src/ui/field-input.ts +8 -181
- package/src/ui/form-controls.ts +838 -366
- package/src/ui/formula-chips.ts +80 -85
- package/src/ui/kit.ts +33 -0
- package/src/ui/layers.ts +293 -593
- package/src/ui/media-picker.ts +203 -284
- package/src/ui/panel-resize.ts +251 -64
- package/src/ui/popover-surface.ts +153 -0
- package/src/ui/progress-modal.ts +22 -60
- package/src/ui/regions.ts +13 -7
- package/src/ui/schema-form.ts +906 -440
- package/src/ui/surface.ts +122 -0
- package/src/ui/unit-selector.ts +15 -131
- package/src/ui/value-source.ts +14 -0
- package/src/ui/virtual-window.ts +88 -9
- package/src/utils/canvas-media.ts +72 -0
- package/src/utils/edit-display.ts +7 -2
- package/src/utils/element-selectors.ts +69 -0
- package/src/utils/link-target.ts +2 -2
- package/src/utils/studio-utils.ts +0 -10
- package/src/view.ts +15 -0
- package/src/workspace/pane-derive.ts +36 -14
- package/src/workspace/workspace.ts +15 -6
- package/styles/canvas.css +72 -467
- package/styles/forced-colors.css +48 -82
- package/styles/forced-colors.json +41 -0
- package/styles/inspector.css +2 -1301
- package/styles/overlays.css +96 -2823
- package/styles/panels.css +6 -1423
- package/styles/shell-frame.css +272 -0
- package/styles/shell-frame.json +227 -0
- package/styles/shell.css +0 -1568
- package/styles/tokens.css +65 -136
- package/styles/tokens.json +84 -0
- package/dist/chunks/Overlay.dev-etqjwyf9.js +0 -23
- package/dist/chunks/adapter-jkcyhgtr.js +0 -14
- package/dist/chunks/cf-account-picker-en86v7x7.js +0 -163
- package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
- package/dist/chunks/components-zmx10t62.js +0 -21
- package/dist/chunks/content-source-48xg86wj.js +0 -49
- package/dist/chunks/convert-to-repeater-43tfn7cs.js +0 -268
- package/dist/chunks/editor-cptq5jp1.js +0 -37
- package/dist/chunks/entry-commands-e63f1xc0.js +0 -46
- package/dist/chunks/extension-sections-txv08xxg.js +0 -311
- package/dist/chunks/file-ops-wbh4hhsx.js +0 -48
- package/dist/chunks/files-3kfkwhk4.js +0 -93
- package/dist/chunks/flow-qbfgrpeh.js +0 -634
- package/dist/chunks/focus-trap.esm-9t2drp0g.js +0 -1025
- package/dist/chunks/focus-visible-6eg70z18.js +0 -167
- package/dist/chunks/format-host-w16q81tf.js +0 -43
- package/dist/chunks/fs-events-hrrg328g.js +0 -21
- package/dist/chunks/git-panel-532whr1y.js +0 -59
- package/dist/chunks/index-zqxmqxmh.js +0 -109
- package/dist/chunks/jx-validate-8er6gbxy.js +0 -19
- package/dist/chunks/layers-panel-07hm7g5j.js +0 -67
- package/dist/chunks/layers-q75tj2mf.js +0 -45
- package/dist/chunks/lit-html-j055mkh8.js +0 -22
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +0 -25
- package/dist/chunks/notify-yjm79n4s.js +0 -30
- package/dist/chunks/parse-24d0gay3.js +0 -19
- package/dist/chunks/platform-ba1j1gbh.js +0 -16
- package/dist/chunks/publish-panel-v9st96ja.js +0 -29
- package/dist/chunks/quick-search-zccjp6gv.js +0 -66
- package/dist/chunks/references-3qx8fkyp.js +0 -29
- package/dist/chunks/right-panel-vm3b6kwy.js +0 -46
- package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
- package/dist/chunks/sp-overlay.dev-qgvhnxf4.js +0 -18
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +0 -15
- package/dist/chunks/sp-tooltip.dev-4sastdnw.js +0 -15
- package/dist/chunks/sp-tray.dev-tkbscm06.js +0 -199
- package/dist/chunks/studio-0a5qrh01.js +0 -3
- package/dist/chunks/studio-0bq6eh7v.js +0 -12
- package/dist/chunks/studio-18j3bmcz.js +0 -14
- package/dist/chunks/studio-1ngbejft.js +0 -181
- package/dist/chunks/studio-4b5z1x43.js +0 -424
- package/dist/chunks/studio-4n2b5gzb.js +0 -357
- package/dist/chunks/studio-4rzyrtjz.js +0 -426
- package/dist/chunks/studio-5fx3g9jg.js +0 -4325
- package/dist/chunks/studio-5myknm0a.js +0 -1903
- package/dist/chunks/studio-7afd21nw.js +0 -67
- package/dist/chunks/studio-7nte2hj4.js +0 -613
- package/dist/chunks/studio-8jpzqk5s.js +0 -128
- package/dist/chunks/studio-8q517dfr.js +0 -24
- package/dist/chunks/studio-950k0dx6.js +0 -6939
- package/dist/chunks/studio-9n4mrmnb.js +0 -184
- package/dist/chunks/studio-a0tswy0w.js +0 -58
- package/dist/chunks/studio-ahnzd9rg.js +0 -23
- package/dist/chunks/studio-cmb03bg5.js +0 -13890
- package/dist/chunks/studio-d1ks410h.js +0 -175
- package/dist/chunks/studio-ef49pnzx.js +0 -387
- package/dist/chunks/studio-eqq1w638.js +0 -113
- package/dist/chunks/studio-ew0djmhg.js +0 -341
- package/dist/chunks/studio-gfgb8pe6.js +0 -1652
- package/dist/chunks/studio-h4rxscfc.js +0 -10
- package/dist/chunks/studio-jc994dnr.js +0 -1066
- package/dist/chunks/studio-kyh9avvv.js +0 -28
- package/dist/chunks/studio-m3hwn7y1.js +0 -120
- package/dist/chunks/studio-mtfnaqs9.js +0 -659
- package/dist/chunks/studio-n6j9t10q.js +0 -318
- package/dist/chunks/studio-n8b9wqwy.js +0 -291
- package/dist/chunks/studio-qcy05dp8.js +0 -1743
- package/dist/chunks/studio-qq6jym04.js +0 -53
- package/dist/chunks/studio-rdx8jcxy.js +0 -83
- package/dist/chunks/studio-rn9ta2mz.js +0 -111
- package/dist/chunks/studio-t7qb9db8.js +0 -665
- package/dist/chunks/studio-tmc5emrw.js +0 -8
- package/dist/chunks/studio-z6nw2ek2.js +0 -18
- package/dist/chunks/studio-zczps70q.js +0 -378
- package/dist/chunks/studio-zqazsct2.js +0 -1823
- package/dist/chunks/workspace-fjx52ryv.js +0 -89
- package/src/panels/activity-bar.ts +0 -251
- package/src/panels/panel-scheduler.ts +0 -219
- package/src/panels/style-inputs.ts +0 -237
- package/src/panels/target-line.ts +0 -274
- package/src/panels/welcome-screen.ts +0 -346
- package/src/ui/button-group.ts +0 -102
- package/src/ui/choice-field.ts +0 -72
- package/src/ui/field-row.ts +0 -249
- package/src/ui/formula-palette.ts +0 -213
- package/src/ui/icons.ts +0 -52
- package/src/ui/jx-theme.ts +0 -130
- package/src/ui/spectrum.ts +0 -340
- package/src/ui/value-selector.ts +0 -174
- package/src/ui/widgets.ts +0 -172
- /package/dist/chunks/{javascript-s56a8dpk.css → javascript-4abzjcy9.css} +0 -0
- /package/dist/chunks/{jsonMode-zr9sw0c7.css → jsonMode-vvmpmk34.css} +0 -0
- /package/dist/chunks/{monaco-setup-pav68602.css → monaco-setup-8vbyqnhr.css} +0 -0
- /package/dist/chunks/{tsMode-ef187txx.css → tsMode-hnxxb73h.css} +0 -0
|
@@ -8,10 +8,8 @@
|
|
|
8
8
|
* colour-scheme switch and feature toggles (rendering state), and Export (a mode action). Plan §3.2
|
|
9
9
|
* ⑦ replaces them with three axes that each say what they are:
|
|
10
10
|
*
|
|
11
|
-
* - **Editor kind** — `Canvas ⌄`, offering only the kinds this document declares
|
|
12
|
-
*
|
|
13
|
-
* document with one kind renders the name as text rather than as a dropdown that cannot go
|
|
14
|
-
* anywhere.
|
|
11
|
+
* - **Editor kind** — `Canvas ⌄`, offering only the kinds this document declares. A document with one
|
|
12
|
+
* kind renders the name as text rather than as a dropdown that cannot go anywhere.
|
|
15
13
|
* - **Canvas view** — `Edit │ Design` as a radio ({@link canvasBaseViewsFor}) with a **Preview**
|
|
16
14
|
* toggle beside it ({@link previewStateOf}), and none at all in a pane that may not host the
|
|
17
15
|
* Canvas. The two are drawn apart because they are two axes: preview is a flag over an edit or
|
|
@@ -27,23 +25,25 @@
|
|
|
27
25
|
* because these are values you type and everything in the rendering-context popover is something
|
|
28
26
|
* you pick — and because a row of text fields is what made the 28px band unreadable.
|
|
29
27
|
*
|
|
28
|
+
* **This module is the FLOW; `surfaces/pane-context.json` is the markup.** Everything below decides
|
|
29
|
+
* — which kinds a document declares, what a lens may never write, which pane's stage a zoom verb
|
|
30
|
+
* lands on, what a route param's candidates are, what a typed test prop parses as — and hands one
|
|
31
|
+
* projection of words and keyed rows to `surfaces/pane-context.ts`, one mount per pane. Nothing
|
|
32
|
+
* here renders.
|
|
33
|
+
*
|
|
30
34
|
* **The bar is not a grid row.** It renders inside the pane's own cell (`#pane-chrome`, stacked
|
|
31
35
|
* over `#canvas-wrap`), because a per-pane surface cannot be a row of the application grid — the
|
|
32
36
|
* second pane would have no way to have one. The stage is offset by {@link PANE_CONTEXT_VAR} rather
|
|
33
37
|
* than by a track, and the zoom pod floats bottom-right over the canvas exactly as §3.2 ⑩ asks.
|
|
34
38
|
*
|
|
35
|
-
* **The read-only banner rides with the bar.** `collab/
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
+
* **The read-only banner rides with the bar.** `collab/collab-state.ts` says whether a guest may
|
|
40
|
+
* write (§7.4); the surface that owes them the sentence is this one, because it is the per-pane,
|
|
41
|
+
* per-document chrome that sits directly above the editing surface. It is stacked under the bar
|
|
42
|
+
* inside the document's `[part="band"]` and the offset is MEASURED from that band
|
|
39
43
|
* ({@link applyPaneContextOffset}), so a two-line banner pushes the stage down instead of covering
|
|
40
44
|
* the document it is warning you about.
|
|
41
|
-
*
|
|
42
|
-
* Module shape follows `tab-strip.ts`: mount(host, ctx) → effectScope/effect → render().
|
|
43
45
|
*/
|
|
44
46
|
|
|
45
|
-
import { html, render as litRender, nothing } from "lit-html";
|
|
46
|
-
import { ref } from "lit-html/directives/ref.js";
|
|
47
47
|
import { projectState, updateUi } from "../store";
|
|
48
48
|
import { effect, effectScope } from "../reactivity";
|
|
49
49
|
import { PRIMARY_PANE, focusPane, workspace } from "../workspace/workspace";
|
|
@@ -63,8 +63,9 @@ import {
|
|
|
63
63
|
pinRefusal,
|
|
64
64
|
presetRefusal,
|
|
65
65
|
} from "../workspace/pane-derive";
|
|
66
|
-
import {
|
|
67
|
-
import {
|
|
66
|
+
import { openMenu } from "../surfaces/menu";
|
|
67
|
+
import { emptyPaneContextView, mountPaneContextSurface } from "../surfaces/pane-context";
|
|
68
|
+
import { PANE_SELECTOR } from "../surfaces/pane-grid";
|
|
68
69
|
import { paneRegion } from "../ui/regions";
|
|
69
70
|
import {
|
|
70
71
|
fitToScreen,
|
|
@@ -81,7 +82,6 @@ import {
|
|
|
81
82
|
} from "../canvas/canvas-utils";
|
|
82
83
|
import { editorKindOf, editorKindsOf, modeForEditorKind } from "../tabs/tab";
|
|
83
84
|
import { collabState } from "../collab/collab-state";
|
|
84
|
-
import { readOnlyBannerTemplate } from "../collab/presence-chips";
|
|
85
85
|
import { activeRegistry } from "../commands/active-registry";
|
|
86
86
|
import { getEffectiveLayoutPath, getEffectiveLocales, getEffectiveMedia } from "../site-context";
|
|
87
87
|
import { localeLabel, localeOfPath } from "@jxsuite/schema/locale";
|
|
@@ -97,7 +97,15 @@ import type { Tab } from "../tabs/tab";
|
|
|
97
97
|
import type { ResolvedI18n } from "@jxsuite/schema/locale";
|
|
98
98
|
import type { JsonValue } from "../types";
|
|
99
99
|
import type { EffectScope } from "@vue/reactivity";
|
|
100
|
-
import type {
|
|
100
|
+
import type { MenuHandle, MenuRowProjection } from "../surfaces/menu";
|
|
101
|
+
import { rectOf } from "../utils/geometry";
|
|
102
|
+
import type {
|
|
103
|
+
PaneContextActions,
|
|
104
|
+
PaneContextChoice,
|
|
105
|
+
PaneContextSurfaceHandle,
|
|
106
|
+
PaneContextToggle,
|
|
107
|
+
PaneContextView,
|
|
108
|
+
} from "../surfaces/pane-context";
|
|
101
109
|
|
|
102
110
|
/**
|
|
103
111
|
* The CSS variable the stage is offset by while a context bar is on screen.
|
|
@@ -117,10 +125,10 @@ export interface PaneContextCtx {
|
|
|
117
125
|
* There is no `getCanvasMode` here, and there cannot be one.
|
|
118
126
|
*
|
|
119
127
|
* It answered for the FOCUSED pane — `studio.ts` composes it from `workspace.activePaneId` — and
|
|
120
|
-
* this bar is drawn once per pane, from `tabOfPane(paneId)`. One reader was left:
|
|
121
|
-
* which is why entering Code in EITHER pane put an Export button in BOTH bars. Every
|
|
122
|
-
* question this module asks is now asked of the pane it is drawing: `canvasModeOfTab(tab)`
|
|
123
|
-
* the tab's own effective mode, `canvasModeOfPane(paneId)` for the stage's.
|
|
128
|
+
* this bar is drawn once per pane, from `tabOfPane(paneId)`. One reader was left: the Export
|
|
129
|
+
* control, which is why entering Code in EITHER pane put an Export button in BOTH bars. Every
|
|
130
|
+
* mode question this module asks is now asked of the pane it is drawing: `canvasModeOfTab(tab)`
|
|
131
|
+
* for the tab's own effective mode, `canvasModeOfPane(paneId)` for the stage's.
|
|
124
132
|
*/
|
|
125
133
|
/**
|
|
126
134
|
* Write the BASE mode of the tab it is GIVEN. The editor-kind dropdown and the view control both
|
|
@@ -150,6 +158,16 @@ export interface PaneContextCtx {
|
|
|
150
158
|
*/
|
|
151
159
|
const _hosts = new Map<string, HTMLElement>();
|
|
152
160
|
|
|
161
|
+
/**
|
|
162
|
+
* Each pane's mounted document, keyed the same way and for the same reason.
|
|
163
|
+
*
|
|
164
|
+
* A surface handle is per-pane state as much as a host is: it holds that pane's scope, its two
|
|
165
|
+
* popovers and whether the "resolving with" one is showing. A module-level `let` here would give
|
|
166
|
+
* the second pane's `canvas.setResolvingOpen` the first pane's panel — the exact shape
|
|
167
|
+
* `scripts/check-pane-singletons.ts` exists to refuse.
|
|
168
|
+
*/
|
|
169
|
+
const _surfaces = new Map<string, PaneContextSurfaceHandle>();
|
|
170
|
+
|
|
153
171
|
let _ctx: PaneContextCtx | null = null;
|
|
154
172
|
|
|
155
173
|
let _scope: EffectScope | null = null;
|
|
@@ -182,7 +200,7 @@ export function attachPaneChromeHost(paneId: string, host: HTMLElement | null):
|
|
|
182
200
|
return;
|
|
183
201
|
}
|
|
184
202
|
if (previous) {
|
|
185
|
-
|
|
203
|
+
disposePane(paneId);
|
|
186
204
|
applyPaneContextOffset(0, previous);
|
|
187
205
|
}
|
|
188
206
|
if (host) {
|
|
@@ -193,6 +211,12 @@ export function attachPaneChromeHost(paneId: string, host: HTMLElement | null):
|
|
|
193
211
|
}
|
|
194
212
|
}
|
|
195
213
|
|
|
214
|
+
/** Take one pane's document down and forget it. Idempotent. */
|
|
215
|
+
function disposePane(paneId: string): void {
|
|
216
|
+
_surfaces.get(paneId)?.dispose();
|
|
217
|
+
_surfaces.delete(paneId);
|
|
218
|
+
}
|
|
219
|
+
|
|
196
220
|
/**
|
|
197
221
|
* Mount the pane chrome. Idempotent.
|
|
198
222
|
*
|
|
@@ -214,17 +238,15 @@ export function mount(host: HTMLElement, ctx: PaneContextCtx) {
|
|
|
214
238
|
`activeTab` alone left the side pane's bar frozen describing whatever it last drew. */
|
|
215
239
|
for (const pane of workspace.panes) {
|
|
216
240
|
/* NO DERIVATION READS HERE, and the reason is one rule rather than a tally. `render()`
|
|
217
|
-
runs inside this effect and
|
|
218
|
-
is ALREADY a dependency and restating it as a `void` line chooses nothing:
|
|
219
|
-
picks the bar's shape, `activeMediaOfPane` reads `kind`/`preset`/`media`
|
|
220
|
-
axis, `zoomOf` reads `derived.zoom` for the pod, and {@link
|
|
241
|
+
runs inside this effect and projects EVERY attached pane, so whatever the projection
|
|
242
|
+
reads is ALREADY a dependency and restating it as a `void` line chooses nothing:
|
|
243
|
+
`derived.kind` picks the bar's shape, `activeMediaOfPane` reads `kind`/`preset`/`media`
|
|
244
|
+
for the Context axis, `zoomOf` reads `derived.zoom` for the pod, and {@link podFor} asks
|
|
221
245
|
`canvasModeOfPane`, which for a lens answers `derived.mode` and decides whether the pod
|
|
222
246
|
is drawn at all — pinned by lens-chrome's "the pod is drawn from the LENS's mode".
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
The stage is the surface that needs those two declared, because `renderCanvasImpl` runs
|
|
227
|
-
in a rAF rather than in an effect — see `studio.ts`. */
|
|
247
|
+
`status` and `reason` really are unread here, so tracking them only repainted this bar
|
|
248
|
+
for a change it does not draw. The stage is the surface that needs those two declared,
|
|
249
|
+
because `renderCanvasImpl` runs in a rAF rather than in an effect — see `studio.ts`. */
|
|
228
250
|
const tab = tabOfPane(pane.id);
|
|
229
251
|
if (!tab) {
|
|
230
252
|
continue;
|
|
@@ -260,7 +282,9 @@ export function mount(host: HTMLElement, ctx: PaneContextCtx) {
|
|
|
260
282
|
export function unmount() {
|
|
261
283
|
_scope?.stop();
|
|
262
284
|
_scope = null;
|
|
263
|
-
|
|
285
|
+
dismissPresetMenu();
|
|
286
|
+
for (const [paneId, host] of _hosts) {
|
|
287
|
+
disposePane(paneId);
|
|
264
288
|
applyPaneContextOffset(0, host);
|
|
265
289
|
}
|
|
266
290
|
_hosts.clear();
|
|
@@ -284,7 +308,7 @@ export function unmount() {
|
|
|
284
308
|
* @param {HTMLElement | null} [host] The bar's host. Its cell takes the variable when it has one.
|
|
285
309
|
*/
|
|
286
310
|
export function applyPaneContextOffset(height: number, host?: HTMLElement | null): void {
|
|
287
|
-
const target = host?.closest<HTMLElement>(
|
|
311
|
+
const target = host?.closest<HTMLElement>(PANE_SELECTOR) ?? document.documentElement;
|
|
288
312
|
target.style.setProperty(PANE_CONTEXT_VAR, `${height}px`);
|
|
289
313
|
}
|
|
290
314
|
|
|
@@ -295,10 +319,13 @@ export function applyPaneContextOffset(height: number, host?: HTMLElement | null
|
|
|
295
319
|
* knows how many lines that is. `offsetHeight` is 0 in a DOM with no layout engine (every unit
|
|
296
320
|
* test), so the bar's declared height is the floor: the offset is then exactly what it was before
|
|
297
321
|
* banners existed, which is the honest answer when nothing has been laid out.
|
|
322
|
+
*
|
|
323
|
+
* The band is the DOCUMENT's, handed back by the mount rather than found by selector — a surface
|
|
324
|
+
* that re-queries its own markup is holding a node that the next render may already have replaced
|
|
325
|
+
* (guidelines §9.4).
|
|
298
326
|
*/
|
|
299
|
-
function topBandHeight(
|
|
300
|
-
|
|
301
|
-
return Math.max(band?.offsetHeight ?? 0, PANE_CONTEXT_HEIGHT);
|
|
327
|
+
function topBandHeight(surface: PaneContextSurfaceHandle): number {
|
|
328
|
+
return Math.max(surface.band()?.offsetHeight ?? 0, PANE_CONTEXT_HEIGHT);
|
|
302
329
|
}
|
|
303
330
|
|
|
304
331
|
/**
|
|
@@ -316,14 +343,14 @@ function wantsContextBar(tab: Tab): boolean {
|
|
|
316
343
|
return tab.session.ui.canvasMode !== "settings";
|
|
317
344
|
}
|
|
318
345
|
|
|
319
|
-
/**
|
|
346
|
+
/** Project every attached pane's chrome. */
|
|
320
347
|
export function render() {
|
|
321
348
|
for (const [paneId, host] of _hosts) {
|
|
322
349
|
renderPane(paneId, host);
|
|
323
350
|
}
|
|
324
351
|
}
|
|
325
352
|
|
|
326
|
-
/**
|
|
353
|
+
/** Project one pane's chrome into its own mount, from its own tab. */
|
|
327
354
|
function renderPane(paneId: string, host: HTMLElement) {
|
|
328
355
|
if (!_ctx) {
|
|
329
356
|
return;
|
|
@@ -331,8 +358,32 @@ function renderPane(paneId: string, host: HTMLElement) {
|
|
|
331
358
|
try {
|
|
332
359
|
const tab = tabOfPane(paneId);
|
|
333
360
|
const show = Boolean(tab) && wantsContextBar(tab as Tab);
|
|
334
|
-
|
|
335
|
-
|
|
361
|
+
if (!show) {
|
|
362
|
+
disposePane(paneId);
|
|
363
|
+
applyPaneContextOffset(0, host);
|
|
364
|
+
return;
|
|
365
|
+
}
|
|
366
|
+
const view = viewFor(tab as Tab, paneId, _ctx);
|
|
367
|
+
let surface = _surfaces.get(paneId);
|
|
368
|
+
if (!surface || !surface.connected()) {
|
|
369
|
+
disposePane(paneId);
|
|
370
|
+
surface = mountPaneContextSurface(paneId, host, view, actionsFor(paneId, _ctx));
|
|
371
|
+
_surfaces.set(paneId, surface);
|
|
372
|
+
/* The band cannot be measured until the document has drawn it, so the first offset lands
|
|
373
|
+
after the mount settles; every later projection writes it synchronously from the standing
|
|
374
|
+
band. The stage is offset by the declared height in between, which is what it was before
|
|
375
|
+
banners existed. */
|
|
376
|
+
applyPaneContextOffset(PANE_CONTEXT_HEIGHT, host);
|
|
377
|
+
void surface.ready.then(() => {
|
|
378
|
+
const live = _surfaces.get(paneId);
|
|
379
|
+
if (live) {
|
|
380
|
+
applyPaneContextOffset(topBandHeight(live), host);
|
|
381
|
+
}
|
|
382
|
+
});
|
|
383
|
+
return;
|
|
384
|
+
}
|
|
385
|
+
surface.update(view);
|
|
386
|
+
applyPaneContextOffset(topBandHeight(surface), host);
|
|
336
387
|
} catch (error) {
|
|
337
388
|
console.error("pane-context render error:", error);
|
|
338
389
|
}
|
|
@@ -341,36 +392,20 @@ function renderPane(paneId: string, host: HTMLElement) {
|
|
|
341
392
|
// ─── The preset menu · the first renderer of `context/pane` ──────────────────
|
|
342
393
|
|
|
343
394
|
/** The open preset menu, if any. One at a time, like every other menu in this shell. */
|
|
344
|
-
let _presetMenu:
|
|
395
|
+
let _presetMenu: MenuHandle | null = null;
|
|
345
396
|
|
|
346
397
|
/** Close the preset menu, if it is open. Idempotent. */
|
|
347
398
|
export function dismissPresetMenu(): void {
|
|
348
|
-
_presetMenu?.
|
|
399
|
+
_presetMenu?.close();
|
|
349
400
|
_presetMenu = null;
|
|
350
401
|
}
|
|
351
402
|
|
|
352
403
|
/**
|
|
353
|
-
*
|
|
404
|
+
* Open the preset menu, from the ⟲ trigger in the bar's leading slot — the one that was empty.
|
|
354
405
|
*
|
|
355
|
-
*
|
|
356
|
-
* the menu is about THIS PANE and the bar is the pane's own chrome; the alternative homes
|
|
357
|
-
* strip, the jump bar) are about a document and an address respectively.
|
|
358
|
-
*/
|
|
359
|
-
function presetTriggerTpl(paneId: string): TemplateResult {
|
|
360
|
-
return html`<button
|
|
361
|
-
class="pc-derive-trigger"
|
|
362
|
-
aria-haspopup="menu"
|
|
363
|
-
title="Show something beside this pane"
|
|
364
|
-
@click=${(event: MouseEvent) => {
|
|
365
|
-
openPresetMenu(event, paneId);
|
|
366
|
-
}}
|
|
367
|
-
>
|
|
368
|
-
<span aria-hidden="true">⟲</span>
|
|
369
|
-
</button>`;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
|
-
/**
|
|
373
|
-
* Open the preset menu.
|
|
406
|
+
* The leading slot existed to push the three axes right. It is where §18.4's preset menu goes
|
|
407
|
+
* because the menu is about THIS PANE and the bar is the pane's own chrome; the alternative homes
|
|
408
|
+
* (the tab strip, the jump bar) are about a document and an address respectively.
|
|
374
409
|
*
|
|
375
410
|
* **There is no `pane.showDerivePresets`.** §13.5, quoted verbatim in `canvas/canvas-render.ts`:
|
|
376
411
|
* opening a menu to press an item names a CONTROL; the item is the command. Every row here runs
|
|
@@ -378,53 +413,81 @@ function presetTriggerTpl(paneId: string): TemplateResult {
|
|
|
378
413
|
* addresses those ids directly rather than driving this widget. Its region is
|
|
379
414
|
* `overlay.menu:derive-presets`, which `ui/layers.ts` derives from the slot key — no budget is
|
|
380
415
|
* spent.
|
|
416
|
+
*
|
|
417
|
+
* **It is the kit menu, not a second one.** `surfaces/menu.ts` already owns roving focus,
|
|
418
|
+
* typeahead, light dismissal, the disabled row's `requires` sentence and the Escape that closes it
|
|
419
|
+
* (guidelines §12.5); this hands it rows.
|
|
381
420
|
*/
|
|
382
|
-
function openPresetMenu(
|
|
383
|
-
event.preventDefault();
|
|
384
|
-
event.stopPropagation();
|
|
421
|
+
function openPresetMenu(paneId: string, anchor: HTMLElement): void {
|
|
385
422
|
dismissPresetMenu();
|
|
386
423
|
const registry = activeRegistry();
|
|
387
424
|
if (!registry) {
|
|
388
425
|
return;
|
|
389
426
|
}
|
|
390
|
-
const anchor = rectOf(event.currentTarget as HTMLElement);
|
|
391
|
-
const left = Math.round(Math.min(anchor.left, window.innerWidth - 4));
|
|
392
|
-
const top = Math.round(anchor.bottom);
|
|
393
427
|
const rows = presetRows(paneId);
|
|
394
|
-
_presetMenu =
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
role="menuitem"
|
|
400
|
-
?disabled=${row.disabled !== null}
|
|
401
|
-
title=${row.disabled ? `requires ${row.disabled}` : row.label}
|
|
402
|
-
@click=${() => {
|
|
403
|
-
dismissPresetMenu();
|
|
404
|
-
if (row.disabled === null) {
|
|
405
|
-
/* THE PANE THE MENU IS ABOUT, before the verb that resolves the focus runs.
|
|
406
|
-
See {@link PresetRow.pane}: all three of these commands take the focused pane as
|
|
407
|
-
their subject, and a pointer gesture on this pane has already focused it — but a
|
|
408
|
-
keyboard activation has not, because `panels/pane-grid.ts` focuses on
|
|
409
|
-
pointerdown. Without this line the secondary pane's menu derived from the
|
|
410
|
-
primary and its Unsplit closed the wrong pane, by keyboard only. */
|
|
411
|
-
focusPane(row.pane);
|
|
412
|
-
void registry.run(row.command, row.args);
|
|
413
|
-
}
|
|
414
|
-
}}
|
|
415
|
-
>${row.label}</sp-menu-item
|
|
416
|
-
>`,
|
|
417
|
-
)}
|
|
418
|
-
</sp-menu>
|
|
419
|
-
</sp-popover>`,
|
|
420
|
-
{
|
|
421
|
-
dismissOnOutsideClick: true,
|
|
422
|
-
onDismiss: () => {
|
|
423
|
-
_presetMenu = null;
|
|
424
|
-
},
|
|
425
|
-
region: "derive-presets",
|
|
428
|
+
_presetMenu = openMenu({
|
|
429
|
+
label: "Show beside this pane",
|
|
430
|
+
opener: anchor,
|
|
431
|
+
onClosed: () => {
|
|
432
|
+
_presetMenu = null;
|
|
426
433
|
},
|
|
427
|
-
|
|
434
|
+
place: (box) => {
|
|
435
|
+
const at = rectOf(anchor);
|
|
436
|
+
return {
|
|
437
|
+
x: Math.round(Math.min(at.left, window.innerWidth - box.width - 4)),
|
|
438
|
+
y: Math.round(at.bottom),
|
|
439
|
+
};
|
|
440
|
+
},
|
|
441
|
+
region: "derive-presets",
|
|
442
|
+
rows: rows.map((row) => menuRow(row)),
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* One preset row, as the kit menu reads it: an identity, a sentence, and what pressing it runs.
|
|
448
|
+
*
|
|
449
|
+
* **There is no second refusal inside `run`, and the lit version's is deliberately gone.** The
|
|
450
|
+
* handler used to re-ask `row.disabled !== null` before doing anything, which was a second answer
|
|
451
|
+
* to the question the line above it already settles: `jx-menu-item` writes `aria-disabled` from
|
|
452
|
+
* this flag and raises no `select` while it is true, so a refused row never reaches `run` at all.
|
|
453
|
+
* Keeping both left the projection's own flag with nothing able to tell right from wrong about it —
|
|
454
|
+
* `check-lens-mutants.ts` said so, by surviving.
|
|
455
|
+
*/
|
|
456
|
+
function menuRow(row: PresetRow): MenuRowProjection {
|
|
457
|
+
return {
|
|
458
|
+
destructive: false,
|
|
459
|
+
disabled: row.disabled !== null,
|
|
460
|
+
dividerAbove: false,
|
|
461
|
+
id: rowId(row),
|
|
462
|
+
...(row.disabled === null ? {} : { requires: row.disabled }),
|
|
463
|
+
run: () => {
|
|
464
|
+
/* THE PANE THE MENU IS ABOUT, before the verb that resolves the focus runs.
|
|
465
|
+
See {@link PresetRow.pane}: all three of these commands take the focused pane as their
|
|
466
|
+
subject, and a pointer gesture on this pane has already focused it — but a keyboard
|
|
467
|
+
activation has not, because `panels/pane-grid.ts` focuses on pointerdown. Without this line
|
|
468
|
+
the secondary pane's menu derived from the primary and its Unsplit closed the wrong pane,
|
|
469
|
+
by keyboard only. */
|
|
470
|
+
focusPane(row.pane);
|
|
471
|
+
void activeRegistry()?.run(row.command, row.args);
|
|
472
|
+
},
|
|
473
|
+
title: row.label,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
/**
|
|
478
|
+
* A row's identity in the menu, which is not simply its command: `pane.derive` appears once per
|
|
479
|
+
* projection, once per declared breakpoint and once per declared locale, and a keyed `$map` cannot
|
|
480
|
+
* reconcile three rows that all call themselves `pane.derive`.
|
|
481
|
+
*/
|
|
482
|
+
function rowId(row: PresetRow): string {
|
|
483
|
+
const parts = [row.command];
|
|
484
|
+
for (const key of ["preset", "media", "locale"] as const) {
|
|
485
|
+
const value = row.args[key];
|
|
486
|
+
if (typeof value === "string") {
|
|
487
|
+
parts.push(value);
|
|
488
|
+
}
|
|
489
|
+
}
|
|
490
|
+
return parts.join(":");
|
|
428
491
|
}
|
|
429
492
|
|
|
430
493
|
/** One row of the preset menu: a command, its arguments, and why it cannot run when it cannot. */
|
|
@@ -547,18 +610,10 @@ export function presetRows(paneId: string): PresetRow[] {
|
|
|
547
610
|
return rows;
|
|
548
611
|
}
|
|
549
612
|
|
|
550
|
-
|
|
551
|
-
function axisTpl(label: string, control: TemplateResult): TemplateResult {
|
|
552
|
-
return html`
|
|
553
|
-
<div class="pc-axis">
|
|
554
|
-
<span class="pc-axis-label">${label}</span>
|
|
555
|
-
${control}
|
|
556
|
-
</div>
|
|
557
|
-
`;
|
|
558
|
-
}
|
|
613
|
+
// ─── The projection ──────────────────────────────────────────────────────────
|
|
559
614
|
|
|
560
615
|
/**
|
|
561
|
-
*
|
|
616
|
+
* Everything one pane's chrome draws, as words and keyed rows.
|
|
562
617
|
*
|
|
563
618
|
* There is no takeover branch. Opening a function body or a formula reveals the dock's Logic tab
|
|
564
619
|
* (P8) and leaves the canvas standing underneath it, so the axes still describe the document on the
|
|
@@ -569,8 +624,7 @@ function axisTpl(label: string, control: TemplateResult): TemplateResult {
|
|
|
569
624
|
* and the Logic tab's own header carries the Close. This bar drew a second Back and a second trail
|
|
570
625
|
* beside both of them.
|
|
571
626
|
*/
|
|
572
|
-
function
|
|
573
|
-
const kind = editorKindOf(tab);
|
|
627
|
+
function viewFor(tab: Tab, paneId: string, ctx: PaneContextCtx): PaneContextView {
|
|
574
628
|
/* A LENS suppresses the two axes that WRITE. Editor kind and Canvas view both land in
|
|
575
629
|
`ctx.setCanvasMode(tab, …)`, and that tab belongs to the pane beside this one — so a control
|
|
576
630
|
drawn in the lens would flip the document the author is editing. The lens's own mode is the
|
|
@@ -578,34 +632,52 @@ function paneChromeTemplate(tab: Tab, paneId: string, ctx: PaneContextCtx): Temp
|
|
|
578
632
|
a static summary for the same reason (it writes `updateUi(tab, …)`), and the zoom pod stays
|
|
579
633
|
because zoom is the one view fact a lens genuinely owns. */
|
|
580
634
|
const lens = derivationOfPane(paneId)?.kind === "lens";
|
|
635
|
+
const readOnly = collabState(tab).active && collabState(tab).readOnly;
|
|
636
|
+
const view: PaneContextView = {
|
|
637
|
+
...baseView(paneId),
|
|
638
|
+
/* …and NO ⟲ trigger in a lens. Every projection row in the menu it opens is permanently
|
|
639
|
+
disabled from there (a derived pane cannot derive again) and the breakpoint rows are
|
|
640
|
+
suppressed outright, leaving one live row — Unsplit — which the derivation chip's ✕ in this
|
|
641
|
+
pane's own strip already runs. A control that can do nothing from where it is drawn is the
|
|
642
|
+
class this phase has deleted three times. A COMPANION keeps it: "Keep This Document" is live
|
|
643
|
+
there and it is genuinely about that pane. The leading slot stays either way: it is what
|
|
644
|
+
pushes the axes right, and dropping it would move the one control a lens does draw. */
|
|
645
|
+
presetState: lens ? "hidden" : "shown",
|
|
646
|
+
/* A lens draws no banner: the projection would be announcing a collaboration session it is not
|
|
647
|
+
in, twice on one screen, about a document it does not own. */
|
|
648
|
+
bannerState: !lens && readOnly ? "shown" : "hidden",
|
|
649
|
+
barMode: lens ? "lens" : "full",
|
|
650
|
+
...podFor(tab, paneId),
|
|
651
|
+
};
|
|
652
|
+
if (lens) {
|
|
653
|
+
view.summary = lensSummary(paneId);
|
|
654
|
+
return view;
|
|
655
|
+
}
|
|
656
|
+
Object.assign(view, editorAxis(tab));
|
|
657
|
+
if (editorKindOf(tab) === "canvas") {
|
|
658
|
+
Object.assign(view, viewAxis(tab));
|
|
659
|
+
}
|
|
660
|
+
Object.assign(view, contextAxis(tab, paneId, ctx));
|
|
661
|
+
/* THIS tab's effective mode. `ctx.getCanvasMode()` answered for the focused pane, so a document
|
|
662
|
+
opened as Code in either pane put an Export button in the OTHER pane's bar as well — over a
|
|
663
|
+
document that is not the one the button exports. */
|
|
664
|
+
view.exportState = canvasModeOfTab(tab) === "source" ? "shown" : "hidden";
|
|
665
|
+
return view;
|
|
666
|
+
}
|
|
581
667
|
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
lens ? nothing : presetTriggerTpl(paneId)
|
|
596
|
-
}
|
|
597
|
-
</div>
|
|
598
|
-
${
|
|
599
|
-
lens
|
|
600
|
-
? renderingSummaryTpl(paneId)
|
|
601
|
-
: html`${editorKindTpl(tab, ctx)} ${kind === "canvas" ? viewTpl(tab, ctx) : nothing}
|
|
602
|
-
${renderingContextTpl(tab, paneId, ctx)} ${exportTpl(tab, ctx)}`
|
|
603
|
-
}
|
|
604
|
-
</div>
|
|
605
|
-
${lens ? nothing : readOnlyBannerTemplate(tab)}
|
|
606
|
-
</div>
|
|
607
|
-
${zoomPodTpl(tab, paneId)}
|
|
608
|
-
`;
|
|
668
|
+
/**
|
|
669
|
+
* The projection before any axis has spoken: the two regions, and everything else absent.
|
|
670
|
+
*
|
|
671
|
+
* The empty shape comes from the SURFACE — one list of fields, beside the interface that declares
|
|
672
|
+
* them — because two hand-kept copies of a 36-field record is exactly how a new field ends up
|
|
673
|
+
* projected in one place and forgotten in the other.
|
|
674
|
+
*/
|
|
675
|
+
function baseView(paneId: string): PaneContextView {
|
|
676
|
+
return {
|
|
677
|
+
...emptyPaneContextView(),
|
|
678
|
+
contextRegion: paneRegion(paneId, "context"),
|
|
679
|
+
zoomRegion: paneRegion(paneId, "zoom"),
|
|
680
|
+
};
|
|
609
681
|
}
|
|
610
682
|
|
|
611
683
|
/**
|
|
@@ -616,7 +688,7 @@ function paneChromeTemplate(tab: Tab, paneId: string, ctx: PaneContextCtx): Temp
|
|
|
616
688
|
* screen — but the popover behind it writes the source tab's `session.ui`, which is the one thing a
|
|
617
689
|
* lens must never do.
|
|
618
690
|
*/
|
|
619
|
-
function
|
|
691
|
+
function lensSummary(paneId: string): string {
|
|
620
692
|
/* No `ctx.parseMediaEntries` here. It parsed the document's whole `$media` map, discarded the
|
|
621
693
|
result through `void sizeBreakpoints` and printed the media NAME — a parse per lens-bar render
|
|
622
694
|
for a value that was never read. `mediaDisplayName` is the only lookup this line needs. */
|
|
@@ -632,15 +704,14 @@ function renderingSummaryTpl(paneId: string): TemplateResult {
|
|
|
632
704
|
document's own language, for the reason the trigger beside it is: a pane drawing the file it
|
|
633
705
|
has open in the language that file is written in has nothing to report. */
|
|
634
706
|
const locale = localeOf(tabOfPane(paneId));
|
|
635
|
-
|
|
707
|
+
return `${media ? mediaDisplayName(media) : "Base"}${
|
|
636
708
|
locale?.overridden ? ` · ${localeLabel(locale.effective)}` : ""
|
|
637
709
|
}`;
|
|
638
|
-
return axisTpl("Context", html`<span class="pc-static">${stated}</span>`);
|
|
639
710
|
}
|
|
640
711
|
|
|
641
712
|
// ─── Axis 1 · Editor kind ────────────────────────────────────────────────────
|
|
642
713
|
|
|
643
|
-
function
|
|
714
|
+
function editorAxis(tab: Tab): Partial<PaneContextView> {
|
|
644
715
|
/* What the DOCUMENT declares, and nothing else. This read `hostableKindsOf` — the declared kinds
|
|
645
716
|
narrowed by what the tab's pane was allowed to host — so a page in the side pane was offered
|
|
646
717
|
Code and not Design. The pane cap is gone; a control that cannot go anywhere is still the
|
|
@@ -650,43 +721,23 @@ function editorKindTpl(tab: Tab, ctx: PaneContextCtx): TemplateResult {
|
|
|
650
721
|
if (kinds.length < 2) {
|
|
651
722
|
// One kind is not a choice. Rendering it as a dropdown would be a control that cannot move —
|
|
652
723
|
// Which is the defect this axis exists to remove, in miniature.
|
|
653
|
-
return
|
|
724
|
+
return { editorLabel: EDITOR_KIND_LABELS[current], editorMode: "static" };
|
|
654
725
|
}
|
|
655
|
-
return
|
|
656
|
-
"
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
quiet
|
|
661
|
-
class="pc-editor-kind"
|
|
662
|
-
label="Editor"
|
|
663
|
-
value=${current}
|
|
664
|
-
@change=${(e: Event) => {
|
|
665
|
-
const kind = (e.target as HTMLInputElement).value as EditorKind;
|
|
666
|
-
const mode = modeForEditorKind(tab, kind);
|
|
667
|
-
if (!mode) {
|
|
668
|
-
return;
|
|
669
|
-
}
|
|
670
|
-
tab.session.ui.preview = false;
|
|
671
|
-
ctx.setCanvasMode(tab, mode);
|
|
672
|
-
}}
|
|
673
|
-
>
|
|
674
|
-
${kinds.map(
|
|
675
|
-
(kind) => html`<sp-menu-item value=${kind}>${EDITOR_KIND_LABELS[kind]}</sp-menu-item>`,
|
|
676
|
-
)}
|
|
677
|
-
</sp-picker>
|
|
678
|
-
`,
|
|
679
|
-
);
|
|
726
|
+
return {
|
|
727
|
+
editorMode: "picker",
|
|
728
|
+
editorOptions: kinds.map((kind) => ({ label: EDITOR_KIND_LABELS[kind], value: kind })),
|
|
729
|
+
editorValue: current,
|
|
730
|
+
};
|
|
680
731
|
}
|
|
681
732
|
|
|
682
733
|
// ─── Axis 2 · Canvas view ────────────────────────────────────────────────────
|
|
683
734
|
|
|
684
|
-
function
|
|
735
|
+
function viewAxis(tab: Tab): Partial<PaneContextView> {
|
|
685
736
|
// Every pane draws a live Canvas, so this is no longer narrowed by WHERE the tab is — only by
|
|
686
737
|
// What the document declares. A document with no Canvas view still draws no view group.
|
|
687
738
|
const views = canvasBaseViewsFor(tab);
|
|
688
739
|
if (views.length === 0) {
|
|
689
|
-
return
|
|
740
|
+
return {};
|
|
690
741
|
}
|
|
691
742
|
const current = canvasBaseViewOf(tab);
|
|
692
743
|
const preview = previewStateOf(tab);
|
|
@@ -698,54 +749,21 @@ function viewTpl(tab: Tab, ctx: PaneContextCtx): TemplateResult | typeof nothing
|
|
|
698
749
|
* say which mode you were previewing — or which one Escape would return you to. Now the radio
|
|
699
750
|
* marks the base the whole time and the toggle sits beside it, pressed.
|
|
700
751
|
*/
|
|
701
|
-
return
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
</sp-action-button>
|
|
717
|
-
`,
|
|
718
|
-
)}
|
|
719
|
-
</sp-action-group>
|
|
720
|
-
${
|
|
721
|
-
preview.available
|
|
722
|
-
? html`
|
|
723
|
-
<sp-action-button
|
|
724
|
-
size="s"
|
|
725
|
-
class="pc-preview-toggle"
|
|
726
|
-
toggles
|
|
727
|
-
?selected=${preview.on}
|
|
728
|
-
aria-pressed=${preview.on ? "true" : "false"}
|
|
729
|
-
title=${
|
|
730
|
-
preview.on
|
|
731
|
-
? `Stop previewing — back to ${CANVAS_VIEW_LABELS[current ?? "edit"]}`
|
|
732
|
-
: "Preview: the page as it ships, with editing off"
|
|
733
|
-
}
|
|
734
|
-
@click=${() => {
|
|
735
|
-
setCanvasView(
|
|
736
|
-
tab,
|
|
737
|
-
preview.on ? (current ?? "edit") : "preview",
|
|
738
|
-
ctx.setCanvasMode,
|
|
739
|
-
);
|
|
740
|
-
}}
|
|
741
|
-
>
|
|
742
|
-
${CANVAS_VIEW_LABELS.preview}
|
|
743
|
-
</sp-action-button>
|
|
744
|
-
`
|
|
745
|
-
: nothing
|
|
746
|
-
}
|
|
747
|
-
`,
|
|
748
|
-
);
|
|
752
|
+
return {
|
|
753
|
+
previewHint: preview.on
|
|
754
|
+
? `Stop previewing — back to ${CANVAS_VIEW_LABELS[current ?? "edit"]}`
|
|
755
|
+
: "Preview: the page as it ships, with editing off",
|
|
756
|
+
previewOn: preview.on,
|
|
757
|
+
previewState: preview.available ? "shown" : "hidden",
|
|
758
|
+
views: views.map((value) => ({
|
|
759
|
+
checked: value === current ? "true" : "false",
|
|
760
|
+
hint: `Show this document in ${CANVAS_VIEW_LABELS[value]}`,
|
|
761
|
+
key: value,
|
|
762
|
+
label: CANVAS_VIEW_LABELS[value],
|
|
763
|
+
value,
|
|
764
|
+
})),
|
|
765
|
+
viewState: "shown",
|
|
766
|
+
};
|
|
749
767
|
}
|
|
750
768
|
|
|
751
769
|
// ─── Axis 3 · Rendering context ──────────────────────────────────────────────
|
|
@@ -775,9 +793,9 @@ interface PaneLocale {
|
|
|
775
793
|
/**
|
|
776
794
|
* The language axis for a tab, or `null` when the project has no language question to ask.
|
|
777
795
|
*
|
|
778
|
-
* Called at
|
|
779
|
-
* so a locale added in Settings reaches this bar on the next paint and a module-level copy
|
|
780
|
-
* still be describing the project that was open when this file loaded.
|
|
796
|
+
* Called at PROJECTION time and never cached: `projectState` is replaced wholesale rather than
|
|
797
|
+
* mutated, so a locale added in Settings reaches this bar on the next paint and a module-level copy
|
|
798
|
+
* would still be describing the project that was open when this file loaded.
|
|
781
799
|
*
|
|
782
800
|
* A single declared locale is the same as none for this control — there is nothing to switch TO,
|
|
783
801
|
* and "groups a document declares nothing for are absent" is the rule the whole popover follows.
|
|
@@ -798,7 +816,7 @@ function localeOf(tab: Tab | null): PaneLocale | null {
|
|
|
798
816
|
return { effective, i18n, overridden: effective !== own, pathLocale };
|
|
799
817
|
}
|
|
800
818
|
|
|
801
|
-
function
|
|
819
|
+
function contextAxis(tab: Tab, paneId: string, ctx: PaneContextCtx): Partial<PaneContextView> {
|
|
802
820
|
const { ui } = tab.session;
|
|
803
821
|
const { featureQueries, sizeBreakpoints } = ctx.parseMediaEntries(
|
|
804
822
|
getEffectiveMedia(tab.doc.document?.$media as Record<string, string> | undefined),
|
|
@@ -813,7 +831,7 @@ function renderingContextTpl(tab: Tab, paneId: string, ctx: PaneContextCtx): Tem
|
|
|
813
831
|
/* The language joins the trigger ONLY when the pane is not drawing the document's own — a French
|
|
814
832
|
page open in a French pane is not a rendering context worth three more characters, and a bar
|
|
815
833
|
that grew a third term in every multilingual project would stop reading as a state. */
|
|
816
|
-
const
|
|
834
|
+
const contextSummary = [
|
|
817
835
|
sizeLabel,
|
|
818
836
|
schemeQueries.length > 0 ? schemeLabel : null,
|
|
819
837
|
locale?.overridden ? localeLabel(locale.effective) : null,
|
|
@@ -821,285 +839,74 @@ function renderingContextTpl(tab: Tab, paneId: string, ctx: PaneContextCtx): Tem
|
|
|
821
839
|
.filter((part) => part !== null)
|
|
822
840
|
.join(" · ");
|
|
823
841
|
const hasLayout = isPageDoc(tab) && Boolean(getEffectiveLayoutPath(tab.doc.document?.$layout));
|
|
824
|
-
const
|
|
842
|
+
const page = isPageDoc(tab);
|
|
843
|
+
const params = page ? paramRows(tab) : [];
|
|
844
|
+
const props = page ? [] : propRows(tab, paneId);
|
|
825
845
|
/* How many of those fields carry a value. The trigger has to say something true at a glance, the
|
|
826
846
|
way the Context trigger says `Base · Auto` — a chevron with no reading is a control you have to
|
|
827
847
|
open in order to learn whether it was worth opening. */
|
|
828
|
-
const resolvingSet =
|
|
848
|
+
const resolvingSet = page
|
|
829
849
|
? Object.values(ui.previewParams ?? {}).filter((v) => v !== "" && v !== undefined).length
|
|
830
850
|
: Object.keys(ui.previewProps ?? {}).length;
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
title=${title}
|
|
862
|
-
?selected=${scheme === value}
|
|
863
|
-
@click=${() =>
|
|
864
|
-
runContextCommand(paneId, "canvas.setColorScheme", {
|
|
865
|
-
scheme: value,
|
|
866
|
-
})}
|
|
867
|
-
>
|
|
868
|
-
${label}
|
|
869
|
-
</sp-action-button>
|
|
870
|
-
`,
|
|
871
|
-
)}
|
|
872
|
-
</sp-action-group>
|
|
873
|
-
`,
|
|
874
|
-
)
|
|
875
|
-
: nothing
|
|
876
|
-
}
|
|
877
|
-
${locale ? localeGroupTpl(paneId, locale) : nothing}
|
|
878
|
-
${
|
|
879
|
-
plainQueries.length > 0
|
|
880
|
-
? groupTpl(
|
|
881
|
-
"Features",
|
|
882
|
-
html`
|
|
883
|
-
<sp-action-group compact size="s">
|
|
884
|
-
${plainQueries.map(
|
|
885
|
-
({ name, query }) => html`
|
|
886
|
-
<sp-action-button
|
|
887
|
-
size="s"
|
|
888
|
-
toggles
|
|
889
|
-
title=${query}
|
|
890
|
-
?selected=${Boolean(ui.featureToggles[name])}
|
|
891
|
-
@click=${() => {
|
|
892
|
-
updateUi(tab, "featureToggles", {
|
|
893
|
-
...tab.session.ui.featureToggles,
|
|
894
|
-
[name]: !tab.session.ui.featureToggles[name],
|
|
895
|
-
});
|
|
896
|
-
}}
|
|
897
|
-
>
|
|
898
|
-
${mediaDisplayName(name)}
|
|
899
|
-
</sp-action-button>
|
|
900
|
-
`,
|
|
901
|
-
)}
|
|
902
|
-
</sp-action-group>
|
|
903
|
-
`,
|
|
904
|
-
)
|
|
905
|
-
: nothing
|
|
906
|
-
}
|
|
907
|
-
${
|
|
908
|
-
hasLayout
|
|
909
|
-
? groupTpl(
|
|
910
|
-
"Layout",
|
|
911
|
-
html`
|
|
912
|
-
<sp-switch
|
|
913
|
-
size="s"
|
|
914
|
-
class="pc-layout-switch"
|
|
915
|
-
?checked=${ui.showLayout !== false}
|
|
916
|
-
@change=${() =>
|
|
917
|
-
runContextCommand(paneId, "canvas.setLayoutVisible", {
|
|
918
|
-
visible: tab.session.ui.showLayout === false,
|
|
919
|
-
})}
|
|
920
|
-
>
|
|
921
|
-
Show layout elements
|
|
922
|
-
</sp-switch>
|
|
923
|
-
`,
|
|
924
|
-
)
|
|
925
|
-
: nothing
|
|
926
|
-
}
|
|
927
|
-
<button
|
|
928
|
-
class="pc-ctx-manage"
|
|
929
|
-
type="button"
|
|
930
|
-
title="Breakpoints, schemes and feature queries are defined in Project Settings › Contexts"
|
|
931
|
-
@click=${() => {
|
|
932
|
-
void activeRegistry()?.run("settings.open", { section: "contexts" });
|
|
933
|
-
}}
|
|
934
|
-
>
|
|
935
|
-
Manage contexts…
|
|
936
|
-
</button>
|
|
937
|
-
</div>
|
|
938
|
-
</sp-popover>
|
|
939
|
-
</overlay-trigger>
|
|
940
|
-
`,
|
|
941
|
-
);
|
|
942
|
-
}
|
|
943
|
-
|
|
944
|
-
/**
|
|
945
|
-
* The document DATA a render resolves against — route params, component test props — in a popover.
|
|
946
|
-
*
|
|
947
|
-
* §4.2 folds these in behind the words "resolving with…", and this is where that lands: the phrase
|
|
948
|
-
* is the popover's header and the fields are a vertical stack under it, the same shape as the
|
|
949
|
-
* rendering-context popover beside it.
|
|
950
|
-
*
|
|
951
|
-
* They used to sit OPEN on the bar, and the argument for that was the screenshot contract (§13.1):
|
|
952
|
-
* behind a click, the one first-hour flow the docs teach — typing a test prop on
|
|
953
|
-
* `start/first-component` — costs a second gesture, and the manifest's input budget may only
|
|
954
|
-
* ratchet down. The answer is not to keep n text fields on a 28px band that also carries the
|
|
955
|
-
* editor, the view and the rendering context; it is that a transient surface opens by COMMAND (plan
|
|
956
|
-
* §13.2), so the shot spends a `cmd` step rather than a selector and the input budget is untouched.
|
|
957
|
-
* `canvas.setResolvingOpen` is that command, and it is the same door the pointer uses.
|
|
958
|
-
*
|
|
959
|
-
* @param {string} paneId
|
|
960
|
-
* @param {TemplateResult | typeof nothing} body
|
|
961
|
-
* @param {number} setCount — how many fields carry a value, for the trigger's summary
|
|
962
|
-
*/
|
|
963
|
-
function resolvingTpl(
|
|
964
|
-
paneId: string,
|
|
965
|
-
body: TemplateResult | typeof nothing,
|
|
966
|
-
setCount: number,
|
|
967
|
-
): TemplateResult | typeof nothing {
|
|
968
|
-
if (body === nothing) {
|
|
969
|
-
return nothing;
|
|
970
|
-
}
|
|
971
|
-
return html`
|
|
972
|
-
<overlay-trigger
|
|
973
|
-
placement="bottom-end"
|
|
974
|
-
triggered-by="click"
|
|
975
|
-
${ref((el: Element | undefined) => {
|
|
976
|
-
if (el) {
|
|
977
|
-
_resolvingTrigger.set(paneId, el as HTMLElement & { open?: string | undefined });
|
|
978
|
-
}
|
|
979
|
-
})}
|
|
980
|
-
>
|
|
981
|
-
<sp-action-button
|
|
982
|
-
slot="trigger"
|
|
983
|
-
size="s"
|
|
984
|
-
quiet
|
|
985
|
-
class="pc-resolving-trigger"
|
|
986
|
-
title="The values this document is being rendered with"
|
|
987
|
-
>
|
|
988
|
-
${setCount > 0 ? `${setCount} set` : "Defaults"} ⌄
|
|
989
|
-
</sp-action-button>
|
|
990
|
-
<sp-popover slot="click-content" tip class="pc-context-popover">
|
|
991
|
-
<div class="pc-ctx">${groupTpl("resolving with", body as TemplateResult)}</div>
|
|
992
|
-
</sp-popover>
|
|
993
|
-
</overlay-trigger>
|
|
994
|
-
`;
|
|
995
|
-
}
|
|
996
|
-
|
|
997
|
-
/**
|
|
998
|
-
* Each pane's "resolving with" `overlay-trigger`, which IS the open state.
|
|
999
|
-
*
|
|
1000
|
-
* No parallel `Set`. The overlay owns whether it is showing — it opens on a pointer click without
|
|
1001
|
-
* telling anyone first — so a second record of the same fact could only ever be the stale one. The
|
|
1002
|
-
* element's `open` property is what the pointer writes and what the command writes, and reading it
|
|
1003
|
-
* back is how anything else asks.
|
|
1004
|
-
*/
|
|
1005
|
-
const _resolvingTrigger = new Map<string, HTMLElement & { open?: string | undefined }>();
|
|
1006
|
-
|
|
1007
|
-
/** Whether this pane's resolving popover is open. Exported for the tests. */
|
|
1008
|
-
export function isResolvingOpen(paneId: string): boolean {
|
|
1009
|
-
return _resolvingTrigger.get(paneId)?.open === "click";
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
/**
|
|
1013
|
-
* Open or close it.
|
|
1014
|
-
*
|
|
1015
|
-
* A named end state rather than a toggle, so `canvas.setResolvingOpen { open: false }` means the
|
|
1016
|
-
* same thing twice and a screenshot can photograph it (§13's R1).
|
|
1017
|
-
*
|
|
1018
|
-
* It writes the element and does NOT re-render the bar. `overlay-trigger` moves the popover into an
|
|
1019
|
-
* overlay portal while it is open and restores it on close; re-rendering this template in the
|
|
1020
|
-
* middle of that leaves the moved copy painted over the canvas with no way to dismiss it. The
|
|
1021
|
-
* overlay owns its own visibility — the only thing the bar redraws for is the trigger's summary,
|
|
1022
|
-
* and that changes when a VALUE changes, which already renders.
|
|
1023
|
-
*/
|
|
1024
|
-
export function setResolvingOpen(paneId: string, open: boolean): void {
|
|
1025
|
-
const trigger = _resolvingTrigger.get(paneId);
|
|
1026
|
-
if (trigger && isResolvingOpen(paneId) !== open) {
|
|
1027
|
-
trigger.open = open ? "click" : undefined;
|
|
1028
|
-
}
|
|
1029
|
-
}
|
|
1030
|
-
|
|
1031
|
-
/** Forget the held triggers — a fresh window, and the tests. */
|
|
1032
|
-
export function resetResolvingOpen(): void {
|
|
1033
|
-
_resolvingTrigger.clear();
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
/** One labelled group inside the rendering-context popover. */
|
|
1037
|
-
function groupTpl(label: string, body: TemplateResult): TemplateResult {
|
|
1038
|
-
return html`
|
|
1039
|
-
<div class="pc-ctx-group">
|
|
1040
|
-
<span class="pc-ctx-label">${label}</span>
|
|
1041
|
-
${body}
|
|
1042
|
-
</div>
|
|
1043
|
-
`;
|
|
851
|
+
const hasResolving = page ? params.length > 0 : props.length > 0;
|
|
852
|
+
|
|
853
|
+
return {
|
|
854
|
+
contextSummary,
|
|
855
|
+
featureState: plainQueries.length > 0 ? "shown" : "hidden",
|
|
856
|
+
features: plainQueries.map(({ name, query }) => ({
|
|
857
|
+
hint: query,
|
|
858
|
+
key: name,
|
|
859
|
+
label: mediaDisplayName(name),
|
|
860
|
+
selected: Boolean(ui.featureToggles[name]),
|
|
861
|
+
value: name,
|
|
862
|
+
})) satisfies PaneContextToggle[],
|
|
863
|
+
layoutOn: ui.showLayout !== false,
|
|
864
|
+
layoutState: hasLayout ? "shown" : "hidden",
|
|
865
|
+
...localeGroup(locale),
|
|
866
|
+
params,
|
|
867
|
+
props,
|
|
868
|
+
resolvingKind: page ? "params" : "props",
|
|
869
|
+
resolvingLabel: resolvingSet > 0 ? `${resolvingSet} set` : "Defaults",
|
|
870
|
+
resolvingState: hasResolving ? "shown" : "hidden",
|
|
871
|
+
schemes: SCHEMES.map(([value, label, hint]) => ({
|
|
872
|
+
checked: scheme === value ? "true" : "false",
|
|
873
|
+
hint,
|
|
874
|
+
key: value,
|
|
875
|
+
label,
|
|
876
|
+
value,
|
|
877
|
+
})) satisfies PaneContextChoice[],
|
|
878
|
+
schemeState: schemeQueries.length > 0 ? "shown" : "hidden",
|
|
879
|
+
sizes: sizeRows(sizeBreakpoints, activeMedia),
|
|
880
|
+
};
|
|
1044
881
|
}
|
|
1045
882
|
|
|
1046
883
|
/**
|
|
1047
884
|
* The size segment: the base width plus every declared size breakpoint.
|
|
1048
885
|
*
|
|
1049
886
|
* It writes `ui.activeMedia`, the same field a canvas panel header click writes — one axis, one
|
|
1050
|
-
* field, two ways in.
|
|
1051
|
-
|
|
1052
|
-
/**
|
|
1053
|
-
* Run a rendering-context verb through the registry.
|
|
1054
|
-
*
|
|
1055
|
-
* These four controls wrote `session.ui` directly through `updateUi`, which is why none of the
|
|
1056
|
-
* three axes was a command: the popover WAS the capability, and the palette, the assistant and
|
|
1057
|
-
* `__jxAutomation` had no name for it. Going through the registry makes the control and the verb
|
|
1058
|
-
* one thing (§2, principle 1) and gets the breakpoint refusal for free.
|
|
887
|
+
* field, two ways in. The base is the empty string on the wire, because that is what "no breakpoint
|
|
888
|
+
* applied" is: `canvas.setBreakpoint` reads it back as `null`.
|
|
1059
889
|
*/
|
|
1060
|
-
function
|
|
1061
|
-
// THIS pane, named. The bar is drawn once per pane and the side bar's controls write the side
|
|
1062
|
-
// Pane's tab; a verb defaulting to the focused pane would have made the side bar edit the
|
|
1063
|
-
// Foreground document the moment its control became a command.
|
|
1064
|
-
void activeRegistry()?.run(id, { ...args, pane: paneId });
|
|
1065
|
-
}
|
|
1066
|
-
|
|
1067
|
-
function sizeGroupTpl(
|
|
1068
|
-
paneId: string,
|
|
890
|
+
function sizeRows(
|
|
1069
891
|
breakpoints: { name: string; width: number }[],
|
|
1070
892
|
activeMedia: string | null,
|
|
1071
|
-
):
|
|
1072
|
-
return
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
<sp-action-button
|
|
1089
|
-
size="s"
|
|
1090
|
-
role="radio"
|
|
1091
|
-
aria-checked=${activeMedia === name ? "true" : "false"}
|
|
1092
|
-
title=${`${mediaDisplayName(name)} — ${width}px`}
|
|
1093
|
-
?selected=${activeMedia === name}
|
|
1094
|
-
@click=${() => runContextCommand(paneId, "canvas.setBreakpoint", { media: name })}
|
|
1095
|
-
>
|
|
1096
|
-
${mediaDisplayName(name)}
|
|
1097
|
-
</sp-action-button>
|
|
1098
|
-
`,
|
|
1099
|
-
)}
|
|
1100
|
-
</sp-action-group>
|
|
1101
|
-
`,
|
|
1102
|
-
);
|
|
893
|
+
): PaneContextChoice[] {
|
|
894
|
+
return [
|
|
895
|
+
{
|
|
896
|
+
checked: activeMedia === null ? "true" : "false",
|
|
897
|
+
hint: "The base rendering, with no breakpoint applied",
|
|
898
|
+
key: "--base",
|
|
899
|
+
label: "Base",
|
|
900
|
+
value: "",
|
|
901
|
+
},
|
|
902
|
+
...breakpoints.map(({ name, width }) => ({
|
|
903
|
+
checked: activeMedia === name ? ("true" as const) : ("false" as const),
|
|
904
|
+
hint: `${mediaDisplayName(name)} — ${width}px`,
|
|
905
|
+
key: name,
|
|
906
|
+
label: mediaDisplayName(name),
|
|
907
|
+
value: name,
|
|
908
|
+
})),
|
|
909
|
+
];
|
|
1103
910
|
}
|
|
1104
911
|
|
|
1105
912
|
/**
|
|
@@ -1120,57 +927,25 @@ function sizeGroupTpl(
|
|
|
1120
927
|
* has no room for a second visual state, and the author needs to know which entry is "no override"
|
|
1121
928
|
* before clicking, not after.
|
|
1122
929
|
*/
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
"
|
|
1126
|
-
html`
|
|
1127
|
-
<sp-action-group
|
|
1128
|
-
compact
|
|
1129
|
-
size="s"
|
|
1130
|
-
title="The language this pane renders as — its lang and direction only. The text is whatever file is open."
|
|
1131
|
-
>
|
|
1132
|
-
${locale.i18n.locales.map(
|
|
1133
|
-
(tag) => html`
|
|
1134
|
-
<sp-action-button
|
|
1135
|
-
size="s"
|
|
1136
|
-
role="radio"
|
|
1137
|
-
aria-checked=${locale.effective === tag ? "true" : "false"}
|
|
1138
|
-
title=${
|
|
1139
|
-
tag === locale.pathLocale
|
|
1140
|
-
? `${localeLabel(tag)} — the language of the file this pane has open`
|
|
1141
|
-
: `Render as ${localeLabel(tag)} (${tag})`
|
|
1142
|
-
}
|
|
1143
|
-
?selected=${locale.effective === tag}
|
|
1144
|
-
@click=${() => runContextCommand(paneId, "i18n.switchLocale", { locale: tag })}
|
|
1145
|
-
>
|
|
1146
|
-
${localeLabel(tag)}
|
|
1147
|
-
</sp-action-button>
|
|
1148
|
-
`,
|
|
1149
|
-
)}
|
|
1150
|
-
</sp-action-group>
|
|
1151
|
-
`,
|
|
1152
|
-
);
|
|
1153
|
-
}
|
|
1154
|
-
|
|
1155
|
-
// ─── The mode action ─────────────────────────────────────────────────────────
|
|
1156
|
-
// Export is not an axis, and it is the one control in the old bar with nowhere to go yet: retiring
|
|
1157
|
-
// It without a command name, a chord and a residue would be deletion, not consolidation (§2
|
|
1158
|
-
// Principle 9). It stays in the Code view's trailing slot until the Code editor kind owns an action
|
|
1159
|
-
// Strip of its own.
|
|
1160
|
-
|
|
1161
|
-
function exportTpl(tab: Tab, ctx: PaneContextCtx): TemplateResult | typeof nothing {
|
|
1162
|
-
/* THIS tab's effective mode. `ctx.getCanvasMode()` answered for the focused pane, so a document
|
|
1163
|
-
opened as Code in either pane put an Export button in the OTHER pane's bar as well — over a
|
|
1164
|
-
document that is not the one the button exports. */
|
|
1165
|
-
if (canvasModeOfTab(tab) !== "source") {
|
|
1166
|
-
return nothing;
|
|
930
|
+
function localeGroup(locale: PaneLocale | null): Partial<PaneContextView> {
|
|
931
|
+
if (!locale) {
|
|
932
|
+
return { localeState: "hidden" };
|
|
1167
933
|
}
|
|
1168
|
-
return
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
934
|
+
return {
|
|
935
|
+
localeHint:
|
|
936
|
+
"The language this pane renders as — its lang and direction only. The text is whatever file is open.",
|
|
937
|
+
locales: locale.i18n.locales.map((tag) => ({
|
|
938
|
+
checked: locale.effective === tag ? "true" : "false",
|
|
939
|
+
hint:
|
|
940
|
+
tag === locale.pathLocale
|
|
941
|
+
? `${localeLabel(tag)} — the language of the file this pane has open`
|
|
942
|
+
: `Render as ${localeLabel(tag)} (${tag})`,
|
|
943
|
+
key: tag,
|
|
944
|
+
label: localeLabel(tag),
|
|
945
|
+
value: tag,
|
|
946
|
+
})),
|
|
947
|
+
localeState: "shown",
|
|
948
|
+
};
|
|
1174
949
|
}
|
|
1175
950
|
|
|
1176
951
|
// ─── ⑩ The floating zoom pod ─────────────────────────────────────────────────
|
|
@@ -1191,6 +966,9 @@ function fitChoiceValue(fit: FitMode): string {
|
|
|
1191
966
|
return fit;
|
|
1192
967
|
}
|
|
1193
968
|
|
|
969
|
+
/** The modes whose stage is the panzoom surface — the only ones with a fit to state. */
|
|
970
|
+
const STAGE_ZOOM_MODES = new Set(["design", "stylebook", "git-diff"]);
|
|
971
|
+
|
|
1194
972
|
/**
|
|
1195
973
|
* Zoom and fit, floating over the canvas bottom-right.
|
|
1196
974
|
*
|
|
@@ -1199,7 +977,7 @@ function fitChoiceValue(fit: FitMode): string {
|
|
|
1199
977
|
* fit, because a fit is a statement about an artboard. Preview is deliberately absent: its frame is
|
|
1200
978
|
* a real viewport that scrolls its own document, so there is nothing to zoom.
|
|
1201
979
|
*/
|
|
1202
|
-
function
|
|
980
|
+
function podFor(tab: Tab, paneId: string): Partial<PaneContextView> {
|
|
1203
981
|
/* THIS pane's mode. `ctx.getCanvasMode()` answers for the focused pane, so the unfocused pod
|
|
1204
982
|
offered an `editZoom` control over a stage drawing Design — and hid the fit that stage has. */
|
|
1205
983
|
const mode = canvasModeOfPane(paneId);
|
|
@@ -1209,91 +987,205 @@ function zoomPodTpl(tab: Tab, paneId: string): TemplateResult | typeof nothing {
|
|
|
1209
987
|
const surface = surfaceForPane(paneId);
|
|
1210
988
|
if (mode === "edit") {
|
|
1211
989
|
const editZoom = tab.session.ui.editZoom ?? 1;
|
|
1212
|
-
return
|
|
1213
|
-
paneId,
|
|
1214
|
-
html`
|
|
1215
|
-
${zoomButton("Zoom out (Ctrl+-)", "−", () =>
|
|
1216
|
-
setEditZoom((tab.session.ui.editZoom ?? 1) / 1.2, surface),
|
|
1217
|
-
)}
|
|
1218
|
-
<sp-action-button
|
|
1219
|
-
size="s"
|
|
1220
|
-
class="pc-zoom-label"
|
|
1221
|
-
title="Reset to 100% (Ctrl+0)"
|
|
1222
|
-
@click=${() => setEditZoom(1, surface)}
|
|
1223
|
-
>
|
|
1224
|
-
${Math.round(editZoom * 100)}%
|
|
1225
|
-
</sp-action-button>
|
|
1226
|
-
${zoomButton("Zoom in (Ctrl+=)", "+", () =>
|
|
1227
|
-
setEditZoom((tab.session.ui.editZoom ?? 1) * 1.2, surface),
|
|
1228
|
-
)}
|
|
1229
|
-
`,
|
|
1230
|
-
);
|
|
990
|
+
return { podState: "shown", zoomLabel: `${Math.round(editZoom * 100)}%` };
|
|
1231
991
|
}
|
|
1232
|
-
if (mode
|
|
1233
|
-
return
|
|
992
|
+
if (!STAGE_ZOOM_MODES.has(mode)) {
|
|
993
|
+
return { podState: "hidden" };
|
|
1234
994
|
}
|
|
1235
995
|
const zoom = stageZoom(surface);
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
class="pc-zoom-label"
|
|
1244
|
-
title="Reset to 100% (Ctrl+0)"
|
|
1245
|
-
@click=${() => resetZoom(surface)}
|
|
1246
|
-
>
|
|
1247
|
-
${Math.round(zoom * 100)}%
|
|
1248
|
-
</sp-action-button>
|
|
1249
|
-
${zoomButton("Zoom in (Ctrl+=)", "+", () => setUserZoom(stageZoom(surface) * 1.2, surface))}
|
|
1250
|
-
<sp-picker
|
|
1251
|
-
size="s"
|
|
1252
|
-
quiet
|
|
1253
|
-
class="pc-fit"
|
|
1254
|
-
label="Fit"
|
|
1255
|
-
value=${chosen}
|
|
1256
|
-
title="How this document is framed"
|
|
1257
|
-
@change=${(e: Event) => {
|
|
1258
|
-
const { value } = e.target as HTMLInputElement;
|
|
1259
|
-
const choice = FIT_CHOICES.find((entry) => entry.value === value);
|
|
1260
|
-
if (!choice) {
|
|
1261
|
-
return;
|
|
1262
|
-
}
|
|
1263
|
-
// "Fit page" from the control may magnify a small artboard past life size; the fit APPLIED
|
|
1264
|
-
// On arrival never does. Same declared state, two caps — see fitToScreen's maxZoom.
|
|
1265
|
-
if (choice.fit === "page") {
|
|
1266
|
-
fitToScreen({ surface });
|
|
1267
|
-
return;
|
|
1268
|
-
}
|
|
1269
|
-
setFit(choice.fit, surface);
|
|
1270
|
-
}}
|
|
1271
|
-
>
|
|
1272
|
-
${FIT_CHOICES.map(
|
|
1273
|
-
({ value, label }) => html`<sp-menu-item value=${value}>${label}</sp-menu-item>`,
|
|
1274
|
-
)}
|
|
1275
|
-
</sp-picker>
|
|
1276
|
-
`,
|
|
1277
|
-
);
|
|
996
|
+
return {
|
|
997
|
+
fitOptions: FIT_CHOICES.map(({ label, value }) => ({ label, value })),
|
|
998
|
+
fitState: "shown",
|
|
999
|
+
fitValue: fitChoiceValue(getFit(surface)),
|
|
1000
|
+
podState: "shown",
|
|
1001
|
+
zoomLabel: `${Math.round(zoom * 100)}%`,
|
|
1002
|
+
};
|
|
1278
1003
|
}
|
|
1279
1004
|
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1005
|
+
// ─── What the controls do ────────────────────────────────────────────────────
|
|
1006
|
+
|
|
1007
|
+
/**
|
|
1008
|
+
* Run a rendering-context verb through the registry.
|
|
1009
|
+
*
|
|
1010
|
+
* These four controls wrote `session.ui` directly through `updateUi`, which is why none of the
|
|
1011
|
+
* three axes was a command: the popover WAS the capability, and the palette, the assistant and
|
|
1012
|
+
* `__jxAutomation` had no name for it. Going through the registry makes the control and the verb
|
|
1013
|
+
* one thing (§2, principle 1) and gets the breakpoint refusal for free.
|
|
1014
|
+
*/
|
|
1015
|
+
function runContextCommand(paneId: string, id: string, args: Record<string, unknown>): void {
|
|
1016
|
+
// THIS pane, named. The bar is drawn once per pane and the side bar's controls write the side
|
|
1017
|
+
// Pane's tab; a verb defaulting to the focused pane would have made the side bar edit the
|
|
1018
|
+
// Foreground document the moment its control became a command.
|
|
1019
|
+
void activeRegistry()?.run(id, { ...args, pane: paneId });
|
|
1286
1020
|
}
|
|
1287
1021
|
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1022
|
+
/**
|
|
1023
|
+
* Every gesture the pane's chrome offers, bound to the pane it is drawn for.
|
|
1024
|
+
*
|
|
1025
|
+
* Built once per mount rather than per projection: the document holds these by reference, and a new
|
|
1026
|
+
* closure on every paint would replace a listener the reader may be mid-press on.
|
|
1027
|
+
*/
|
|
1028
|
+
function actionsFor(paneId: string, ctx: PaneContextCtx): PaneContextActions {
|
|
1029
|
+
/** The tab this pane is drawing, at the moment the gesture happens rather than when it was drawn. */
|
|
1030
|
+
const tabNow = (): Tab | null => tabOfPane(paneId);
|
|
1031
|
+
return {
|
|
1032
|
+
chooseEditor: (kind) => {
|
|
1033
|
+
const tab = tabNow();
|
|
1034
|
+
if (!tab) {
|
|
1035
|
+
return;
|
|
1036
|
+
}
|
|
1037
|
+
const mode = modeForEditorKind(tab, kind as EditorKind);
|
|
1038
|
+
if (!mode) {
|
|
1039
|
+
return;
|
|
1040
|
+
}
|
|
1041
|
+
tab.session.ui.preview = false;
|
|
1042
|
+
ctx.setCanvasMode(tab, mode);
|
|
1043
|
+
},
|
|
1044
|
+
chooseFit: (value) => {
|
|
1045
|
+
const choice = FIT_CHOICES.find((entry) => entry.value === value);
|
|
1046
|
+
if (!choice) {
|
|
1047
|
+
return;
|
|
1048
|
+
}
|
|
1049
|
+
const surface = surfaceForPane(paneId);
|
|
1050
|
+
// "Fit page" from the control may magnify a small artboard past life size; the fit APPLIED
|
|
1051
|
+
// On arrival never does. Same declared state, two caps — see fitToScreen's maxZoom.
|
|
1052
|
+
if (choice.fit === "page") {
|
|
1053
|
+
fitToScreen({ surface });
|
|
1054
|
+
return;
|
|
1055
|
+
}
|
|
1056
|
+
setFit(choice.fit, surface);
|
|
1057
|
+
},
|
|
1058
|
+
chooseView: (value) => {
|
|
1059
|
+
const tab = tabNow();
|
|
1060
|
+
if (tab) {
|
|
1061
|
+
setCanvasView(tab, value as CanvasView, ctx.setCanvasMode);
|
|
1062
|
+
}
|
|
1063
|
+
},
|
|
1064
|
+
exportFile: ctx.exportFile,
|
|
1065
|
+
manageContexts: () => {
|
|
1066
|
+
void activeRegistry()?.run("settings.open", { section: "contexts" });
|
|
1067
|
+
},
|
|
1068
|
+
openPresetMenu: (anchor) => {
|
|
1069
|
+
openPresetMenu(paneId, anchor);
|
|
1070
|
+
},
|
|
1071
|
+
setBreakpoint: (media) => {
|
|
1072
|
+
// The base row's value is the empty string, which the command reads back as "no breakpoint".
|
|
1073
|
+
runContextCommand(paneId, "canvas.setBreakpoint", { media: media === "" ? null : media });
|
|
1074
|
+
},
|
|
1075
|
+
setLocale: (locale) => {
|
|
1076
|
+
runContextCommand(paneId, "i18n.switchLocale", { locale });
|
|
1077
|
+
},
|
|
1078
|
+
setParam: (name, value) => {
|
|
1079
|
+
// Through the registry, like every control in the popover beside this one.
|
|
1080
|
+
runContextCommand(paneId, "canvas.setRouteParam", { name, value });
|
|
1081
|
+
},
|
|
1082
|
+
setProp: (name, raw) => {
|
|
1083
|
+
// The command owns the write; this control owns only the parse, because "what a typed string
|
|
1084
|
+
// Means" is a fact about a text field and not about the value.
|
|
1085
|
+
runContextCommand(paneId, "canvas.setTestProp", {
|
|
1086
|
+
name,
|
|
1087
|
+
value: raw === "" ? null : parsePropValue(raw),
|
|
1088
|
+
});
|
|
1089
|
+
},
|
|
1090
|
+
setScheme: (scheme) => {
|
|
1091
|
+
runContextCommand(paneId, "canvas.setColorScheme", { scheme });
|
|
1092
|
+
},
|
|
1093
|
+
toggleFeature: (name) => {
|
|
1094
|
+
const tab = tabNow();
|
|
1095
|
+
if (tab) {
|
|
1096
|
+
updateUi(tab, "featureToggles", {
|
|
1097
|
+
...tab.session.ui.featureToggles,
|
|
1098
|
+
[name]: !tab.session.ui.featureToggles[name],
|
|
1099
|
+
});
|
|
1100
|
+
}
|
|
1101
|
+
},
|
|
1102
|
+
toggleLayout: () => {
|
|
1103
|
+
runContextCommand(paneId, "canvas.setLayoutVisible", {
|
|
1104
|
+
visible: tabNow()?.session.ui.showLayout === false,
|
|
1105
|
+
});
|
|
1106
|
+
},
|
|
1107
|
+
togglePreview: () => {
|
|
1108
|
+
const tab = tabNow();
|
|
1109
|
+
if (!tab) {
|
|
1110
|
+
return;
|
|
1111
|
+
}
|
|
1112
|
+
const preview = previewStateOf(tab);
|
|
1113
|
+
const current = canvasBaseViewOf(tab);
|
|
1114
|
+
setCanvasView(tab, preview.on ? (current ?? "edit") : "preview", ctx.setCanvasMode);
|
|
1115
|
+
},
|
|
1116
|
+
zoomIn: () => {
|
|
1117
|
+
const tab = tabNow();
|
|
1118
|
+
const surface = surfaceForPane(paneId);
|
|
1119
|
+
if (canvasModeOfPane(paneId) === "edit") {
|
|
1120
|
+
setEditZoom((tab?.session.ui.editZoom ?? 1) * 1.2, surface);
|
|
1121
|
+
return;
|
|
1122
|
+
}
|
|
1123
|
+
setUserZoom(stageZoom(surface) * 1.2, surface);
|
|
1124
|
+
},
|
|
1125
|
+
zoomOut: () => {
|
|
1126
|
+
const tab = tabNow();
|
|
1127
|
+
const surface = surfaceForPane(paneId);
|
|
1128
|
+
if (canvasModeOfPane(paneId) === "edit") {
|
|
1129
|
+
setEditZoom((tab?.session.ui.editZoom ?? 1) / 1.2, surface);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
setUserZoom(stageZoom(surface) / 1.2, surface);
|
|
1133
|
+
},
|
|
1134
|
+
zoomReset: () => {
|
|
1135
|
+
const surface = surfaceForPane(paneId);
|
|
1136
|
+
if (canvasModeOfPane(paneId) === "edit") {
|
|
1137
|
+
setEditZoom(1, surface);
|
|
1138
|
+
return;
|
|
1139
|
+
}
|
|
1140
|
+
resetZoom(surface);
|
|
1141
|
+
},
|
|
1142
|
+
};
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
// ─── The resolving-with popover's open state ─────────────────────────────────
|
|
1146
|
+
|
|
1147
|
+
/**
|
|
1148
|
+
* Whether this pane's resolving popover is open. Exported for the tests.
|
|
1149
|
+
*
|
|
1150
|
+
* The MOUNT owns the answer, not this module: the panel is a `jx-popover` in that pane's document
|
|
1151
|
+
* and the platform can close it without telling anyone (a light dismiss, an Escape, a second panel
|
|
1152
|
+
* taking the top layer). The handle mirrors the platform's own `toggle`, so a pane with no chrome —
|
|
1153
|
+
* no tab, or Project Settings — is closed by definition.
|
|
1154
|
+
*/
|
|
1155
|
+
export function isResolvingOpen(paneId: string): boolean {
|
|
1156
|
+
return _surfaces.get(paneId)?.isResolvingOpen() ?? false;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* Open or close it.
|
|
1161
|
+
*
|
|
1162
|
+
* A named end state rather than a toggle, so `canvas.setResolvingOpen { open: false }` means the
|
|
1163
|
+
* same thing twice and a screenshot can photograph it (§13's R1).
|
|
1164
|
+
*
|
|
1165
|
+
* It writes the panel and does NOT re-project the bar. A popover moves into the top layer while it
|
|
1166
|
+
* is open; nothing about the bar changes when it does, and the only thing the bar redraws for is
|
|
1167
|
+
* the trigger's summary, which changes when a VALUE changes and already projects.
|
|
1168
|
+
*/
|
|
1169
|
+
export function setResolvingOpen(paneId: string, open: boolean): void {
|
|
1170
|
+
// Asked through {@link isResolvingOpen}, so "is it already what you are asking for" has one
|
|
1171
|
+
// Answer in this module rather than one here and another in the mount.
|
|
1172
|
+
if (isResolvingOpen(paneId) === open) {
|
|
1173
|
+
return;
|
|
1174
|
+
}
|
|
1175
|
+
_surfaces.get(paneId)?.setResolvingOpen(open);
|
|
1176
|
+
}
|
|
1177
|
+
|
|
1178
|
+
/** Forget the mounted chrome — a fresh window, and the tests. */
|
|
1179
|
+
export function resetResolvingOpen(): void {
|
|
1180
|
+
for (const surface of _surfaces.values()) {
|
|
1181
|
+
surface.dispose();
|
|
1182
|
+
}
|
|
1183
|
+
_surfaces.clear();
|
|
1292
1184
|
}
|
|
1293
1185
|
|
|
1294
1186
|
// ─── Dynamic route-param pickers ─────────────────────────────────────────────
|
|
1295
1187
|
// Candidate values load asynchronously (ContentCollection resolution / data-file read); the module
|
|
1296
|
-
// Caches the last result per (documentPath, $paths) and re-
|
|
1188
|
+
// Caches the last result per (documentPath, $paths) and re-projects when it lands — the same lazy
|
|
1297
1189
|
// Fill pattern as head-panel's loadLayoutEntries. When values arrive, any param without a chosen
|
|
1298
1190
|
// Value auto-selects the first candidate (matching the compiler, whose first expanded route is the
|
|
1299
1191
|
// First path entry).
|
|
@@ -1304,14 +1196,14 @@ function zoomButton(title: string, glyph: string, onClick: () => void): Template
|
|
|
1304
1196
|
* **A Map, not one slot, and that is a livelock fix rather than a cache-size preference.** It was
|
|
1305
1197
|
* `_paramValues` + `_paramValuesKey`, holding exactly ONE result, while {@link render} loops every
|
|
1306
1198
|
* attached pane. Two panes on documents with different keys evicted each other on every pass: pane
|
|
1307
|
-
* A's
|
|
1199
|
+
* A's projection stored A's key and cleared the value, pane B's in the same loop stored B's, and
|
|
1308
1200
|
* whichever load landed found its key gone — or, once the "still shown somewhere" guard let it
|
|
1309
1201
|
* through, stored its value and called `render()`, which re-issued both loads again. An unbounded
|
|
1310
1202
|
* microtask chain: no rAF, no paint, no input. `⌘\` with two pages under dynamic routes was enough,
|
|
1311
1203
|
* and the probe never returned.
|
|
1312
1204
|
*
|
|
1313
|
-
* `_paramLoading` is what stops a re-
|
|
1314
|
-
* map is what stops one pane's answer erasing the other's.
|
|
1205
|
+
* `_paramLoading` is what stops a re-projection re-issuing a load that has not landed yet; the
|
|
1206
|
+
* value map is what stops one pane's answer erasing the other's.
|
|
1315
1207
|
*/
|
|
1316
1208
|
const _paramValues = new Map<string, ParamValues>();
|
|
1317
1209
|
|
|
@@ -1377,47 +1269,29 @@ function autoSelectParams(tab: Tab, values: ParamValues) {
|
|
|
1377
1269
|
}
|
|
1378
1270
|
|
|
1379
1271
|
/**
|
|
1272
|
+
* One picker per route param the document declares, in the order the path names them.
|
|
1273
|
+
*
|
|
1380
1274
|
* @param {Tab} tab
|
|
1381
|
-
* @returns {TemplateResult | typeof nothing}
|
|
1382
1275
|
*/
|
|
1383
|
-
function
|
|
1276
|
+
function paramRows(tab: Tab): PaneContextView["params"] {
|
|
1384
1277
|
const values = paramValuesFor(tab);
|
|
1385
1278
|
const names = new Set(dynamicRouteParams(tab.documentPath));
|
|
1386
1279
|
for (const name of Object.keys(values ?? {})) {
|
|
1387
1280
|
names.add(name);
|
|
1388
1281
|
}
|
|
1389
|
-
if (names.size === 0) {
|
|
1390
|
-
return nothing;
|
|
1391
|
-
}
|
|
1392
1282
|
const { previewParams } = tab.session.ui;
|
|
1393
|
-
return
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
label=${name}
|
|
1401
|
-
title=${`Preview value for [${name}]`}
|
|
1402
|
-
value=${previewParams?.[name] ?? ""}
|
|
1403
|
-
@change=${(e: Event) => {
|
|
1404
|
-
const { value } = e.target as HTMLInputElement;
|
|
1405
|
-
// Through the registry, like every control in the popover beside this one.
|
|
1406
|
-
runContextCommand(paneId, "canvas.setRouteParam", { name, value });
|
|
1407
|
-
}}
|
|
1408
|
-
>
|
|
1409
|
-
${(values?.[name] ?? []).map(
|
|
1410
|
-
(v: string) => html`<sp-menu-item value=${v}>${v}</sp-menu-item>`,
|
|
1411
|
-
)}
|
|
1412
|
-
</sp-picker>
|
|
1413
|
-
`,
|
|
1414
|
-
)}
|
|
1415
|
-
`;
|
|
1283
|
+
return [...names].map((name) => ({
|
|
1284
|
+
key: name,
|
|
1285
|
+
label: `Preview value for [${name}]`,
|
|
1286
|
+
name,
|
|
1287
|
+
options: (values?.[name] ?? []).map((value: string) => ({ label: value, value })),
|
|
1288
|
+
value: previewParams?.[name] ?? "",
|
|
1289
|
+
}));
|
|
1416
1290
|
}
|
|
1417
1291
|
|
|
1418
1292
|
// ─── Component test-prop fields ──────────────────────────────────────────────
|
|
1419
1293
|
// The previewParams mirror for component docs: one small field per prop entry (the doc's
|
|
1420
|
-
// Plain-data state entries), committed on change so typing never re-
|
|
1294
|
+
// Plain-data state entries), committed on change so typing never re-projects the bar mid-edit. A
|
|
1421
1295
|
// Value parses as JSON when it can (numbers, booleans, arrays) and falls back to the raw string;
|
|
1422
1296
|
// Clearing a field removes the override so the prop returns to its authored default.
|
|
1423
1297
|
|
|
@@ -1436,42 +1310,20 @@ function parsePropValue(raw: string): JsonValue {
|
|
|
1436
1310
|
/**
|
|
1437
1311
|
* @param {Tab} tab
|
|
1438
1312
|
* @param {string} paneId
|
|
1439
|
-
* @returns {TemplateResult | typeof nothing}
|
|
1440
1313
|
*/
|
|
1441
|
-
function
|
|
1314
|
+
function propRows(tab: Tab, paneId: string): PaneContextView["props"] {
|
|
1442
1315
|
const doc = tab.doc.document;
|
|
1443
1316
|
if (!isComponentDoc(doc)) {
|
|
1444
|
-
return
|
|
1445
|
-
}
|
|
1446
|
-
const entries = componentPropEntries(doc);
|
|
1447
|
-
if (entries.length === 0) {
|
|
1448
|
-
return nothing;
|
|
1317
|
+
return [];
|
|
1449
1318
|
}
|
|
1450
1319
|
const { previewProps } = tab.session.ui;
|
|
1451
1320
|
const display = (v: JsonValue | undefined) =>
|
|
1452
1321
|
v === undefined ? "" : typeof v === "string" ? v : JSON.stringify(v);
|
|
1453
|
-
return
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
data-jx-region=${paneRegion(paneId, `prop:${name}`)}
|
|
1461
|
-
placeholder=${name}
|
|
1462
|
-
title=${`Test value for ${name}`}
|
|
1463
|
-
.value=${display(previewProps?.[name])}
|
|
1464
|
-
@change=${(e: Event) => {
|
|
1465
|
-
const raw = (e.target as HTMLInputElement).value;
|
|
1466
|
-
// The command owns the write; this control owns only the parse, because "what a typed
|
|
1467
|
-
// String means" is a fact about a text field and not about the value.
|
|
1468
|
-
runContextCommand(paneId, "canvas.setTestProp", {
|
|
1469
|
-
name,
|
|
1470
|
-
value: raw === "" ? null : parsePropValue(raw),
|
|
1471
|
-
});
|
|
1472
|
-
}}
|
|
1473
|
-
></sp-textfield>
|
|
1474
|
-
`,
|
|
1475
|
-
)}
|
|
1476
|
-
`;
|
|
1322
|
+
return componentPropEntries(doc).map(({ name }) => ({
|
|
1323
|
+
key: name,
|
|
1324
|
+
label: `Test value for ${name}`,
|
|
1325
|
+
name,
|
|
1326
|
+
region: paneRegion(paneId, `prop:${name}`),
|
|
1327
|
+
value: display(previewProps?.[name]),
|
|
1328
|
+
}));
|
|
1477
1329
|
}
|