@jxsuite/studio 4.0.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-xp8vnwma.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-nj44jpzj.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-rws7k4br.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-hbtp0eh1.js → studio-25hkdrkw.js} +356 -85
- package/dist/chunks/studio-26d7myd1.js +258 -0
- package/dist/chunks/{studio-p8j86wag.js → studio-38emantv.js} +22 -5
- package/dist/chunks/studio-3gxv60tn.js +422 -0
- package/dist/chunks/{studio-8swrjbny.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-gv8vvshc.js → studio-5523bpzt.js} +11 -11
- package/dist/chunks/{studio-5q69gfag.js → studio-57mf66w2.js} +1753 -2484
- 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-eenfkt4e.js → studio-79y5sz51.js} +52 -38
- package/dist/chunks/{studio-4q0wznzf.js → studio-7e11jpa9.js} +461 -145
- 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-arfpe389.js +195 -0
- package/dist/chunks/{studio-2zkz8evn.js → studio-az3zd49t.js} +473 -281
- package/dist/chunks/studio-cpvb2ed1.js +1171 -0
- package/dist/chunks/{studio-9r6j5ev1.js → studio-d0300636.js} +5078 -19
- 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-4pqqxbrt.js → studio-em9nw8eh.js} +301 -112
- 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-hhvs4sdg.js +195 -0
- package/dist/chunks/{studio-68n67094.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-kjjsjak4.js → studio-rkstz940.js} +3661 -6136
- 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-yxrkyv8a.js → studio-s6z853aq.js} +12108 -11766
- package/dist/chunks/{studio-sf7zjgv1.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-htsf0w6m.js → studio-vz68yn9e.js} +31 -154
- package/dist/chunks/studio-wqpngpsk.js +174 -0
- package/dist/chunks/{studio-eesa1myv.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 +1812 -698
- package/dist/manifest.json +7 -0
- package/dist/studio.js +52900 -36573
- 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/favicon.ico +0 -0
- package/index.html +2 -0
- package/package.json +22 -55
- package/src/about/about-modal.ts +73 -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 +949 -664
- package/src/files/json-layout.ts +474 -0
- package/src/files/media-paths.ts +13 -42
- package/src/files/media-usage.ts +41 -131
- 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/document.ts +2 -0
- package/src/hosting/layout.ts +10 -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 +201 -250
- package/src/new-project/location-fields.ts +101 -165
- 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 -153
- 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 +347 -86
- 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 +165 -299
- package/src/services/a11y-report.ts +100 -43
- package/src/services/ai-command-tools.ts +468 -0
- package/src/services/ai-import-tools.ts +50 -12
- package/src/services/ai-models.ts +108 -1
- 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/context-manager.ts +12 -1
- 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 +17 -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 +193 -12
- package/src/settings/preferences-dialog.ts +384 -307
- 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 +49 -3
- package/src/ui/ai-credentials-form.ts +104 -100
- package/src/ui/ai-managed-connect.ts +116 -49
- package/src/ui/ai-model-picker.ts +102 -47
- package/src/ui/cf-account-picker.ts +135 -0
- 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 -1532
- package/styles/tokens.css +65 -136
- package/styles/tokens.json +84 -0
- package/dist/chunks/Overlay.dev-723bv0df.js +0 -23
- package/dist/chunks/adapter-jkcyhgtr.js +0 -14
- package/dist/chunks/cf-settings-45fgyx8y.js +0 -21
- package/dist/chunks/components-zmx10t62.js +0 -21
- package/dist/chunks/content-source-t88vb2dr.js +0 -49
- package/dist/chunks/convert-to-repeater-dhw1nrz9.js +0 -268
- package/dist/chunks/editor-cptq5jp1.js +0 -37
- package/dist/chunks/entry-commands-6z6bhxg0.js +0 -46
- package/dist/chunks/extension-sections-3ydgjbdy.js +0 -311
- package/dist/chunks/file-ops-rskmhxyq.js +0 -48
- package/dist/chunks/files-0cratsr0.js +0 -92
- package/dist/chunks/flow-qbfgrpeh.js +0 -634
- package/dist/chunks/focus-trap.esm-ggawf1hy.js +0 -1024
- package/dist/chunks/focus-visible-6eg70z18.js +0 -167
- package/dist/chunks/format-host-tgcmfs4p.js +0 -43
- package/dist/chunks/fs-events-hrrg328g.js +0 -21
- package/dist/chunks/git-panel-0yywyk76.js +0 -58
- package/dist/chunks/index-zqxmqxmh.js +0 -109
- package/dist/chunks/jx-validate-8q2g05x1.js +0 -19
- package/dist/chunks/layers-panel-z3tqe5cd.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-54jvhskq.js +0 -28
- package/dist/chunks/quick-search-cgfmjwq5.js +0 -65
- package/dist/chunks/references-3qx8fkyp.js +0 -29
- package/dist/chunks/right-panel-cqp8bc4v.js +0 -46
- package/dist/chunks/section-registry-sx1ykvkw.js +0 -30
- package/dist/chunks/sp-overlay.dev-yztmtrs1.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-xcvpnqr0.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-18j3bmcz.js +0 -14
- package/dist/chunks/studio-1hbqnt19.js +0 -1823
- package/dist/chunks/studio-4rzyrtjz.js +0 -426
- package/dist/chunks/studio-5rywrkxz.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-9c4k4vhv.js +0 -357
- package/dist/chunks/studio-9n4mrmnb.js +0 -184
- package/dist/chunks/studio-a0tswy0w.js +0 -58
- package/dist/chunks/studio-ahjfdy6e.js +0 -387
- package/dist/chunks/studio-ahnzd9rg.js +0 -23
- package/dist/chunks/studio-bw5dz6km.js +0 -608
- package/dist/chunks/studio-cb12yz9w.js +0 -15055
- package/dist/chunks/studio-d1ks410h.js +0 -175
- package/dist/chunks/studio-eqq1w638.js +0 -113
- package/dist/chunks/studio-ex8zyp98.js +0 -1813
- package/dist/chunks/studio-h4rxscfc.js +0 -10
- package/dist/chunks/studio-kyh9avvv.js +0 -28
- package/dist/chunks/studio-kyk2v92a.js +0 -659
- package/dist/chunks/studio-m3hwn7y1.js +0 -120
- package/dist/chunks/studio-mmv72a27.js +0 -4344
- package/dist/chunks/studio-n8b9wqwy.js +0 -291
- package/dist/chunks/studio-pdfjntbk.js +0 -6939
- package/dist/chunks/studio-qq6jym04.js +0 -53
- package/dist/chunks/studio-r1r7afxx.js +0 -1066
- 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-txwkm6p9.js +0 -12
- package/dist/chunks/studio-v38hmvx1.js +0 -390
- package/dist/chunks/studio-vadzgkrz.js +0 -318
- package/dist/chunks/studio-wx5609xd.js +0 -341
- package/dist/chunks/studio-z6nw2ek2.js +0 -18
- package/dist/chunks/studio-zczps70q.js +0 -378
- package/dist/chunks/workspace-xpres40v.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
|
@@ -1,616 +1,653 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Data Shapes — the editor for project-level `$defs` (JSON Schema type definitions).
|
|
4
4
|
*
|
|
5
5
|
* Manages entries in project.json `$defs` — reusable type schemas for external datasets, API
|
|
6
6
|
* responses, CMS payloads, etc. Same concept as component-level `$defs` but scoped to the entire
|
|
7
|
-
* project. The on-disk key stays `$defs` and the section key stays `definitions
|
|
7
|
+
* project. The on-disk key stays `$defs` and the section key stays `definitions`; the surface is
|
|
8
|
+
* named for neither, because a surface is named for what it draws.
|
|
8
9
|
*
|
|
9
|
-
* **The reference field type is complete here
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
10
|
+
* **The reference field type is complete here.** `schema-field-ui.ts` has always been able to draw
|
|
11
|
+
* the target picker, and `ui/form-controls.ts` (the content-types builder) has always passed it the
|
|
12
|
+
* available content types — this editor never did, so choosing "reference" emitted a bare
|
|
13
|
+
* `#/content/` that pointed at nothing and offered no way to say what it pointed at. The list is
|
|
14
|
+
* read straight off the live config's `content` map, which is where a `#/content/<type>` pointer
|
|
15
|
+
* resolves.
|
|
16
|
+
*
|
|
17
|
+
* **The markup left.** The section is the `settings-defs` surface (`surfaces/settings-defs.json`),
|
|
18
|
+
* mounted by `surfaces/settings-defs.ts`; what is here is the section itself — what a shape is,
|
|
19
|
+
* what a field may be renamed to, and what reaches `project.json`. `renderDefsEditor` is unchanged
|
|
20
|
+
* as a contract: the registry hands a container to a `render`, and this one mounts a document into
|
|
21
|
+
* it instead of rendering lit.
|
|
22
|
+
*
|
|
23
|
+
* Three things the conversion settled, and each was a defect rather than a translation:
|
|
24
|
+
*
|
|
25
|
+
* - **The field cards are this section's own now.** They were `schema-field-ui.ts`'s lit templates,
|
|
26
|
+
* shared with the content-types builder, and that sharing is what made every card a Spectrum one.
|
|
27
|
+
* That module is untouched and still draws the builder's cards; this section draws its own out of
|
|
28
|
+
* the kit, so the two surfaces can move one at a time. Nothing was deleted from it.
|
|
29
|
+
* - **The nested add row no longer reads itself.** It found its own name field and its own picker
|
|
30
|
+
* with `closest()` and `querySelector` at click time — a node found by selector is real only
|
|
31
|
+
* until the next render — so what the reader had typed lived in the DOM and nowhere else. Each
|
|
32
|
+
* object field now carries its draft in this section's own state ({@link DefsUi.drafts}), which
|
|
33
|
+
* is what lets a redraw arrive mid-typing without taking the half-typed name away.
|
|
34
|
+
* - **A refused rename snaps back because the scope moves, not because a handler writes to an
|
|
35
|
+
* input.** The lit version assigned `target.value = fieldName` from inside the change handler. A
|
|
36
|
+
* document's binding only writes when the scope value CHANGES, and after a refusal the scope
|
|
37
|
+
* still holds what is on disk — so every setter says what the control now holds first
|
|
38
|
+
* ({@link say}), and the snap-back is then a real move.
|
|
39
|
+
*
|
|
40
|
+
* @docs studio/projects/settings
|
|
15
41
|
*/
|
|
16
42
|
|
|
17
|
-
import { html, render as litRender } from "lit-html";
|
|
18
43
|
import { projectState } from "../store";
|
|
19
44
|
import { commitProjectConfig } from "../tabs/project-config";
|
|
20
|
-
import {
|
|
21
|
-
|
|
22
|
-
|
|
45
|
+
import { camelToLabel } from "../utils/studio-utils";
|
|
46
|
+
import {
|
|
47
|
+
FIELD_TYPES,
|
|
48
|
+
FORMAT_OPTIONS,
|
|
49
|
+
detectFieldFormat,
|
|
50
|
+
detectFieldType,
|
|
51
|
+
schemaForType,
|
|
52
|
+
} from "./schema-field-ui";
|
|
53
|
+
import { mountDefsSurface } from "../surfaces/settings-defs";
|
|
54
|
+
|
|
55
|
+
import type { SchemaProperty } from "./schema-field-ui";
|
|
56
|
+
import type {
|
|
57
|
+
DefsActions,
|
|
58
|
+
DefsChoice,
|
|
59
|
+
DefsFieldView,
|
|
60
|
+
DefsSurfaceHandle,
|
|
61
|
+
DefsView,
|
|
62
|
+
NestedFieldView,
|
|
63
|
+
} from "../surfaces/settings-defs";
|
|
23
64
|
import type { ContentTypeSchema, ContentTypeSchemaField } from "@jxsuite/schema/types";
|
|
24
65
|
|
|
25
|
-
// ───
|
|
66
|
+
// ─── The pickers ──────────────────────────────────────────────────────────────
|
|
26
67
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
let newFieldState = { format: "", name: "", required: false, type: "string" };
|
|
30
|
-
let showNewDef = false;
|
|
31
|
-
let newDefName = "";
|
|
68
|
+
/** The field types, as picker rows. One array, made once: an equal write re-runs no binding. */
|
|
69
|
+
const TYPE_ROWS: DefsChoice[] = FIELD_TYPES.map((type) => ({ label: type, value: type }));
|
|
32
70
|
|
|
33
|
-
|
|
71
|
+
/** The formats, with the empty one named: a blank row says nothing about what choosing it does. */
|
|
72
|
+
const FORMAT_ROWS: DefsChoice[] = FORMAT_OPTIONS.map((format) => ({
|
|
73
|
+
label: format || "(none)",
|
|
74
|
+
value: format,
|
|
75
|
+
}));
|
|
34
76
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
*
|
|
38
|
-
* Every handler below edits `projectState.projectConfig.$defs` directly and then says so here. The
|
|
39
|
-
* predecessor was this module's own writer — `JSON.stringify(config, null, "\t")` straight to
|
|
40
|
-
* `platform.writeFile`, called as `void saveProjectConfig()` at fourteen sites. It re-indented the
|
|
41
|
-
* whole file (every `project.json` on disk uses two spaces), it recorded nothing an undo could
|
|
42
|
-
* reach, and a rejected write became an unhandled rejection while the form went on showing a data
|
|
43
|
-
* shape that was never saved.
|
|
44
|
-
*
|
|
45
|
-
* `commitProjectConfig` transacts the mutation onto the configuration document and reports its own
|
|
46
|
-
* failures as Problems, so there is nothing left here to await or to swallow.
|
|
47
|
-
*/
|
|
48
|
-
function persist(): void {
|
|
49
|
-
void commitProjectConfig();
|
|
50
|
-
}
|
|
77
|
+
/** What a new field starts as. */
|
|
78
|
+
const BLANK_FIELD = { format: "", name: "", required: false, type: "string" };
|
|
51
79
|
|
|
52
|
-
// ───
|
|
80
|
+
// ─── Per-container state ──────────────────────────────────────────────────────
|
|
53
81
|
|
|
54
|
-
/**
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
82
|
+
/** One object field's unsubmitted nested field. */
|
|
83
|
+
interface Draft {
|
|
84
|
+
name: string;
|
|
85
|
+
type: string;
|
|
58
86
|
}
|
|
59
87
|
|
|
60
88
|
/**
|
|
61
|
-
*
|
|
62
|
-
* resolves through `#/$context/content`, read here without a schema-form context to go through.
|
|
89
|
+
* What the reader is part-way through, per rendered container.
|
|
63
90
|
*
|
|
64
|
-
*
|
|
91
|
+
* Per container rather than per module, for the reason `general-settings.ts` and
|
|
92
|
+
* `contexts-section.ts` keep their errors that way: Project Settings is a pane document and a pane
|
|
93
|
+
* can be split, so two containers may be showing this section at once. Module state would make the
|
|
94
|
+
* second one take the first one's selection away the moment either was redrawn.
|
|
65
95
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
96
|
+
interface DefsUi {
|
|
97
|
+
selected: string | null;
|
|
98
|
+
newOpen: boolean;
|
|
99
|
+
newName: string;
|
|
100
|
+
addOpen: boolean;
|
|
101
|
+
add: { format: string; name: string; required: boolean; type: string };
|
|
102
|
+
/** Each object field's nested add row, by the field's name on disk. */
|
|
103
|
+
drafts: Map<string, Draft>;
|
|
104
|
+
/**
|
|
105
|
+
* The ONE control whose value has parted from what is on disk, and what it holds.
|
|
106
|
+
*
|
|
107
|
+
* One at a time is the whole truth of it: a `change` commits one control, and the decision about
|
|
108
|
+
* it is synchronous, so there is never a second control in flight. See {@link say}.
|
|
109
|
+
*/
|
|
110
|
+
echo: { id: string; value: string | boolean } | null;
|
|
72
111
|
}
|
|
73
112
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
113
|
+
const uis = new WeakMap<HTMLElement, DefsUi>();
|
|
114
|
+
|
|
115
|
+
/** The mounted surface, per container — see {@link renderDefsEditor}. */
|
|
116
|
+
const mounts = new WeakMap<HTMLElement, DefsSurfaceHandle>();
|
|
117
|
+
|
|
118
|
+
/** This container's state, made on first use. */
|
|
119
|
+
function uiFor(container: HTMLElement): DefsUi {
|
|
120
|
+
let ui = uis.get(container);
|
|
121
|
+
if (!ui) {
|
|
122
|
+
ui = {
|
|
123
|
+
add: { ...BLANK_FIELD },
|
|
124
|
+
addOpen: false,
|
|
125
|
+
drafts: new Map(),
|
|
126
|
+
echo: null,
|
|
127
|
+
newName: "",
|
|
128
|
+
newOpen: false,
|
|
129
|
+
selected: null,
|
|
130
|
+
};
|
|
131
|
+
uis.set(container, ui);
|
|
89
132
|
}
|
|
90
|
-
|
|
91
|
-
return;
|
|
92
|
-
} // Already exists
|
|
93
|
-
|
|
94
|
-
config.$defs[name] = {
|
|
95
|
-
properties: {},
|
|
96
|
-
required: [],
|
|
97
|
-
type: "object",
|
|
98
|
-
};
|
|
99
|
-
|
|
100
|
-
selectedDef = name;
|
|
101
|
-
showNewDef = false;
|
|
102
|
-
newDefName = "";
|
|
103
|
-
rerender();
|
|
104
|
-
persist();
|
|
133
|
+
return ui;
|
|
105
134
|
}
|
|
106
135
|
|
|
107
|
-
/**
|
|
108
|
-
function
|
|
109
|
-
|
|
110
|
-
if (!name || !selectedDef) {
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const def = getSelectedDef();
|
|
115
|
-
if (!def) {
|
|
116
|
-
return;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
if (!def.properties) {
|
|
120
|
-
def.properties = {};
|
|
121
|
-
}
|
|
122
|
-
def.properties[name] = schemaForType(newFieldState.type, newFieldState.format || undefined);
|
|
123
|
-
|
|
124
|
-
if (newFieldState.required) {
|
|
125
|
-
if (!def.required) {
|
|
126
|
-
def.required = [];
|
|
127
|
-
}
|
|
128
|
-
if (!def.required.includes(name)) {
|
|
129
|
-
def.required.push(name);
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
showAddField = false;
|
|
134
|
-
newFieldState = { format: "", name: "", required: false, type: "string" };
|
|
135
|
-
rerender();
|
|
136
|
-
persist();
|
|
136
|
+
/** The id of a field's name control. */
|
|
137
|
+
function nameId(key: string): string {
|
|
138
|
+
return `name:${key}`;
|
|
137
139
|
}
|
|
138
140
|
|
|
139
|
-
/**
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
*/
|
|
143
|
-
function handleDeleteField(fieldName: string, rerender: () => void) {
|
|
144
|
-
const def = getSelectedDef();
|
|
145
|
-
if (!def?.properties) {
|
|
146
|
-
return;
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
delete def.properties[fieldName];
|
|
150
|
-
if (def.required) {
|
|
151
|
-
def.required = def.required.filter((r: string) => r !== fieldName);
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
rerender();
|
|
155
|
-
persist();
|
|
141
|
+
/** The id of a field's required switch. */
|
|
142
|
+
function requiredId(key: string): string {
|
|
143
|
+
return `required:${key}`;
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
*/
|
|
162
|
-
function handleToggleRequired(fieldName: string, rerender: () => void) {
|
|
163
|
-
const def = getSelectedDef();
|
|
164
|
-
if (!def) {
|
|
165
|
-
return;
|
|
166
|
-
}
|
|
167
|
-
if (!def.required) {
|
|
168
|
-
def.required = [];
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
const idx = def.required.indexOf(fieldName);
|
|
172
|
-
if (idx !== -1) {
|
|
173
|
-
def.required.splice(idx, 1);
|
|
174
|
-
} else {
|
|
175
|
-
def.required.push(fieldName);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
rerender();
|
|
179
|
-
persist();
|
|
146
|
+
/** A child is addressed by both halves, because two objects may hold the same child name. */
|
|
147
|
+
function nestedId(parent: string, key: string): string {
|
|
148
|
+
return `${parent} ${key}`;
|
|
180
149
|
}
|
|
181
150
|
|
|
182
151
|
/**
|
|
183
|
-
*
|
|
184
|
-
*
|
|
185
|
-
*
|
|
152
|
+
* Say what a control now holds, before anything is decided about it.
|
|
153
|
+
*
|
|
154
|
+
* This is what `live()` did for the lit templates. A document's binding writes only when the SCOPE
|
|
155
|
+
* value changes, and after a refusal the scope still holds the value on disk — so without this
|
|
156
|
+
* nothing is written back and the refused text stays in the field. Echoing first makes the
|
|
157
|
+
* projection that follows a real change.
|
|
186
158
|
*/
|
|
187
|
-
function
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
return;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
const newProps: Record<string, ContentTypeSchemaField> = {};
|
|
194
|
-
for (const [key, val] of Object.entries(def.properties)) {
|
|
195
|
-
newProps[key === oldName ? newName : key] = val;
|
|
196
|
-
}
|
|
197
|
-
def.properties = newProps;
|
|
198
|
-
|
|
199
|
-
if (def.required) {
|
|
200
|
-
def.required = def.required.map((r: string) => (r === oldName ? newName : r));
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
rerender();
|
|
204
|
-
persist();
|
|
159
|
+
function say(container: HTMLElement, id: string, value: string | boolean): void {
|
|
160
|
+
uiFor(container).echo = { id, value };
|
|
161
|
+
renderDefsEditor(container);
|
|
205
162
|
}
|
|
206
163
|
|
|
207
|
-
/**
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
* @param {() => void} rerender
|
|
211
|
-
*/
|
|
212
|
-
function handleChangeType(fieldName: string, newType: string, rerender: () => void) {
|
|
213
|
-
const def = getSelectedDef();
|
|
214
|
-
if (!def?.properties?.[fieldName]) {
|
|
215
|
-
return;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const oldFormat =
|
|
219
|
-
newType === "string" || newType === "array"
|
|
220
|
-
? detectFieldFormat(def.properties[fieldName])
|
|
221
|
-
: undefined;
|
|
222
|
-
def.properties[fieldName] = schemaForType(newType, oldFormat || undefined);
|
|
223
|
-
rerender();
|
|
224
|
-
persist();
|
|
164
|
+
/** Forget the echo: every control shows what the file says again. */
|
|
165
|
+
function hush(container: HTMLElement): void {
|
|
166
|
+
uiFor(container).echo = null;
|
|
225
167
|
}
|
|
226
168
|
|
|
227
|
-
/**
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
* builder are the same value.
|
|
231
|
-
*
|
|
232
|
-
* @param {string} fieldName
|
|
233
|
-
* @param {string} target
|
|
234
|
-
* @param {() => void} rerender
|
|
235
|
-
*/
|
|
236
|
-
function handleChangeRefTarget(fieldName: string, target: string, rerender: () => void) {
|
|
237
|
-
const def = getSelectedDef();
|
|
238
|
-
if (!def?.properties?.[fieldName]) {
|
|
239
|
-
return;
|
|
240
|
-
}
|
|
241
|
-
def.properties[fieldName] = { $ref: `#/content/${target}` } as ContentTypeSchemaField;
|
|
242
|
-
rerender();
|
|
243
|
-
persist();
|
|
169
|
+
/** What a control shows: what it was last told it holds, or what is on disk. */
|
|
170
|
+
function shown<T extends string | boolean>(ui: DefsUi, id: string, disk: T): T {
|
|
171
|
+
return ui.echo?.id === id ? (ui.echo.value as T) : disk;
|
|
244
172
|
}
|
|
245
173
|
|
|
174
|
+
// ─── Persistence ──────────────────────────────────────────────────────────────
|
|
175
|
+
|
|
246
176
|
/**
|
|
247
|
-
*
|
|
248
|
-
*
|
|
249
|
-
*
|
|
177
|
+
* Commit the configuration this editor has just mutated in place.
|
|
178
|
+
*
|
|
179
|
+
* Every handler below edits `projectState.projectConfig.$defs` directly and then says so here. The
|
|
180
|
+
* predecessor was this module's own writer — `JSON.stringify(config, null, "\t")` straight to
|
|
181
|
+
* `platform.writeFile`, called as `void saveProjectConfig()` at fourteen sites. It re-indented the
|
|
182
|
+
* whole file (every `project.json` on disk uses two spaces), it recorded nothing an undo could
|
|
183
|
+
* reach, and a rejected write became an unhandled rejection while the form went on showing a data
|
|
184
|
+
* shape that was never saved.
|
|
185
|
+
*
|
|
186
|
+
* `commitProjectConfig` transacts the mutation onto the configuration document and reports its own
|
|
187
|
+
* failures as Problems, so there is nothing left here to await or to swallow.
|
|
250
188
|
*/
|
|
251
|
-
function
|
|
252
|
-
|
|
253
|
-
if (!def?.properties?.[fieldName]) {
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const prop = def.properties[fieldName];
|
|
258
|
-
const type = prop.type || "string";
|
|
259
|
-
def.properties[fieldName] = schemaForType(type, format || undefined);
|
|
260
|
-
rerender();
|
|
261
|
-
persist();
|
|
189
|
+
function persist(): void {
|
|
190
|
+
void commitProjectConfig();
|
|
262
191
|
}
|
|
263
192
|
|
|
264
|
-
// ───
|
|
193
|
+
// ─── The project, as it stands now ────────────────────────────────────────────
|
|
265
194
|
|
|
266
195
|
/**
|
|
267
|
-
*
|
|
268
|
-
*
|
|
269
|
-
*
|
|
196
|
+
* The live `$defs`, read at the moment it is needed.
|
|
197
|
+
*
|
|
198
|
+
* The lit version closed over the config at render time and rebuilt every handler on every render,
|
|
199
|
+
* so a stale closure was impossible only because there were no long-lived ones. The handlers now
|
|
200
|
+
* outlive a projection, which makes reading through to the store the thing that keeps them honest.
|
|
270
201
|
*/
|
|
271
|
-
function
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
rerender: () => void,
|
|
275
|
-
) {
|
|
276
|
-
const def = getSelectedDef();
|
|
277
|
-
const parent = def?.properties?.[parentName];
|
|
278
|
-
if (!parent) {
|
|
279
|
-
return;
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
if (!parent.properties) {
|
|
283
|
-
parent.properties = {};
|
|
284
|
-
}
|
|
285
|
-
parent.properties[fieldState.name] = schemaForType(fieldState.type);
|
|
286
|
-
|
|
287
|
-
if (fieldState.required) {
|
|
288
|
-
if (!parent.required) {
|
|
289
|
-
parent.required = [];
|
|
290
|
-
}
|
|
291
|
-
if (!parent.required.includes(fieldState.name)) {
|
|
292
|
-
parent.required.push(fieldState.name);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
202
|
+
function currentDefs(): Record<string, ContentTypeSchema> | undefined {
|
|
203
|
+
return projectState?.projectConfig?.$defs as Record<string, ContentTypeSchema> | undefined;
|
|
204
|
+
}
|
|
295
205
|
|
|
296
|
-
|
|
297
|
-
|
|
206
|
+
/** The selected shape's schema, or `undefined` when nothing is selected or it has gone. */
|
|
207
|
+
function selectedDef(ui: DefsUi): ContentTypeSchema | undefined {
|
|
208
|
+
return ui.selected === null ? undefined : currentDefs()?.[ui.selected];
|
|
298
209
|
}
|
|
299
210
|
|
|
300
211
|
/**
|
|
301
|
-
*
|
|
302
|
-
*
|
|
303
|
-
* @param {() => void} rerender
|
|
212
|
+
* The content types a `reference` field can point at — the same list the content-types builder
|
|
213
|
+
* resolves through `#/$context/content`, read here without a schema-form context to go through.
|
|
304
214
|
*/
|
|
305
|
-
function
|
|
306
|
-
const
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
delete parent.properties[childName];
|
|
313
|
-
if (parent.required) {
|
|
314
|
-
parent.required = parent.required.filter((r: string) => r !== childName);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
rerender();
|
|
318
|
-
persist();
|
|
215
|
+
function contentTypeNames(): string[] {
|
|
216
|
+
const content = (projectState?.projectConfig as Record<string, unknown> | null | undefined)
|
|
217
|
+
?.content;
|
|
218
|
+
return content && typeof content === "object" && !Array.isArray(content)
|
|
219
|
+
? Object.keys(content)
|
|
220
|
+
: [];
|
|
319
221
|
}
|
|
320
222
|
|
|
321
|
-
/**
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
* @param {() => void} rerender
|
|
325
|
-
*/
|
|
326
|
-
function handleToggleNestedRequired(parentName: string, childName: string, rerender: () => void) {
|
|
327
|
-
const def = getSelectedDef();
|
|
328
|
-
const parent = def?.properties?.[parentName];
|
|
329
|
-
if (!parent) {
|
|
330
|
-
return;
|
|
331
|
-
}
|
|
332
|
-
if (!parent.required) {
|
|
333
|
-
parent.required = [];
|
|
334
|
-
}
|
|
223
|
+
/** The last target list, so an unchanged one is the same array and re-runs no binding. */
|
|
224
|
+
let targetRows: DefsChoice[] = [];
|
|
225
|
+
let targetNames = " ";
|
|
335
226
|
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
227
|
+
/** The reference targets, as picker rows. */
|
|
228
|
+
function targets(): DefsChoice[] {
|
|
229
|
+
const names = contentTypeNames();
|
|
230
|
+
const id = names.join(" ");
|
|
231
|
+
if (id !== targetNames) {
|
|
232
|
+
targetNames = id;
|
|
233
|
+
targetRows = names.map((name) => ({ label: name, value: name }));
|
|
341
234
|
}
|
|
342
|
-
|
|
343
|
-
rerender();
|
|
344
|
-
persist();
|
|
235
|
+
return targetRows;
|
|
345
236
|
}
|
|
346
237
|
|
|
347
|
-
|
|
348
|
-
* @param {string} parentName
|
|
349
|
-
* @param {string} oldChild
|
|
350
|
-
* @param {string} newChild
|
|
351
|
-
* @param {() => void} rerender
|
|
352
|
-
*/
|
|
353
|
-
function handleRenameNested(
|
|
354
|
-
parentName: string,
|
|
355
|
-
oldChild: string,
|
|
356
|
-
newChild: string,
|
|
357
|
-
rerender: () => void,
|
|
358
|
-
) {
|
|
359
|
-
const def = getSelectedDef();
|
|
360
|
-
const parent = def?.properties?.[parentName];
|
|
361
|
-
if (!parent?.properties || !newChild || parent.properties[newChild]) {
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
238
|
+
// ─── Projection ───────────────────────────────────────────────────────────────
|
|
364
239
|
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
parent.properties = newProps;
|
|
370
|
-
|
|
371
|
-
if (parent.required) {
|
|
372
|
-
parent.required = parent.required.map((r: string) => (r === oldChild ? newChild : r));
|
|
373
|
-
}
|
|
240
|
+
/** Whether a type carries a format at all — only `string` and `array` do. */
|
|
241
|
+
function takesFormat(type: string): boolean {
|
|
242
|
+
return type === "string" || type === "array";
|
|
243
|
+
}
|
|
374
244
|
|
|
375
|
-
|
|
376
|
-
|
|
245
|
+
/** One child of an object field, as the document draws it. */
|
|
246
|
+
function nestedView(
|
|
247
|
+
ui: DefsUi,
|
|
248
|
+
parent: string,
|
|
249
|
+
key: string,
|
|
250
|
+
schema: SchemaProperty,
|
|
251
|
+
required: boolean,
|
|
252
|
+
): NestedFieldView {
|
|
253
|
+
const id = nestedId(parent, key);
|
|
254
|
+
const type = detectFieldType(schema);
|
|
255
|
+
return {
|
|
256
|
+
format: detectFieldFormat(schema),
|
|
257
|
+
hasFormat: takesFormat(type),
|
|
258
|
+
key,
|
|
259
|
+
name: shown(ui, nameId(id), key),
|
|
260
|
+
parent,
|
|
261
|
+
required: shown(ui, requiredId(id), required),
|
|
262
|
+
type,
|
|
263
|
+
};
|
|
377
264
|
}
|
|
378
265
|
|
|
379
|
-
/**
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
266
|
+
/** One field of the selected shape, as the document draws it. */
|
|
267
|
+
function fieldView(
|
|
268
|
+
ui: DefsUi,
|
|
269
|
+
key: string,
|
|
270
|
+
schema: SchemaProperty,
|
|
271
|
+
required: boolean,
|
|
272
|
+
hasTargets: boolean,
|
|
273
|
+
): DefsFieldView {
|
|
274
|
+
const type = detectFieldType(schema);
|
|
275
|
+
const nested = type === "object";
|
|
276
|
+
const draft = ui.drafts.get(key);
|
|
277
|
+
const childRequired = schema.required ?? [];
|
|
278
|
+
return {
|
|
279
|
+
children: nested
|
|
280
|
+
? Object.entries(schema.properties ?? {}).map(([child, sub]) =>
|
|
281
|
+
nestedView(ui, key, child, sub, childRequired.includes(child)),
|
|
282
|
+
)
|
|
283
|
+
: [],
|
|
284
|
+
draftName: draft?.name ?? "",
|
|
285
|
+
draftType: draft?.type ?? "string",
|
|
286
|
+
/* A reference with nothing to point at draws no picker: a control whose list is empty offers
|
|
287
|
+
the reader no answer, which is why the lit card gated on the same thing. */
|
|
288
|
+
extra: type === "reference" && hasTargets ? "reference" : nested ? "nested" : "none",
|
|
289
|
+
format: detectFieldFormat(schema),
|
|
290
|
+
hasFormat: takesFormat(type),
|
|
291
|
+
key,
|
|
292
|
+
label: camelToLabel(key),
|
|
293
|
+
name: shown(ui, nameId(key), key),
|
|
294
|
+
refTarget: schema.$ref ? schema.$ref.replace(/^#\/[^/]+\//, "") : "",
|
|
295
|
+
required: shown(ui, requiredId(key), required),
|
|
296
|
+
type,
|
|
297
|
+
};
|
|
298
|
+
}
|
|
396
299
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
300
|
+
/** The whole section, as one value the document can be handed. */
|
|
301
|
+
function project(container: HTMLElement): DefsView {
|
|
302
|
+
const ui = uiFor(container);
|
|
303
|
+
const defs = currentDefs() ?? {};
|
|
304
|
+
const def = selectedDef(ui);
|
|
305
|
+
const rows = targets();
|
|
306
|
+
const properties = def?.properties ?? {};
|
|
307
|
+
const required = def?.required ?? [];
|
|
308
|
+
return {
|
|
309
|
+
addFormat: ui.add.format,
|
|
310
|
+
addHasFormat: takesFormat(ui.add.type),
|
|
311
|
+
addName: ui.add.name,
|
|
312
|
+
addRequired: ui.add.required,
|
|
313
|
+
addState: ui.addOpen && def ? "open" : "closed",
|
|
314
|
+
addType: ui.add.type,
|
|
315
|
+
editorState: def ? "editing" : "empty",
|
|
316
|
+
fields: Object.entries(properties).map(([key, schema]) =>
|
|
317
|
+
fieldView(ui, key, schema as SchemaProperty, required.includes(key), rows.length > 0),
|
|
318
|
+
),
|
|
319
|
+
formatOptions: FORMAT_ROWS,
|
|
320
|
+
newName: ui.newName,
|
|
321
|
+
newState: ui.newOpen ? "open" : "closed",
|
|
322
|
+
selected: def && ui.selected !== null ? ui.selected : "",
|
|
323
|
+
shapes: Object.keys(defs).map((name) => ({ name, selected: name === ui.selected })),
|
|
324
|
+
targets: rows,
|
|
325
|
+
typeOptions: TYPE_ROWS,
|
|
326
|
+
};
|
|
404
327
|
}
|
|
405
328
|
|
|
329
|
+
// ─── The decisions ────────────────────────────────────────────────────────────
|
|
330
|
+
|
|
406
331
|
/**
|
|
407
|
-
*
|
|
408
|
-
*
|
|
409
|
-
* @param {string} format
|
|
410
|
-
* @param {() => void} rerender
|
|
332
|
+
* What every control in one container does. Built once, when that container's surface is mounted,
|
|
333
|
+
* and read through to the store on every call.
|
|
411
334
|
*/
|
|
412
|
-
function
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
)
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
335
|
+
function actionsFor(container: HTMLElement): DefsActions {
|
|
336
|
+
const ui = uiFor(container);
|
|
337
|
+
const redraw = (): void => renderDefsEditor(container);
|
|
338
|
+
|
|
339
|
+
/** The parent object a nested edit is about, or `undefined` when it has gone underneath. */
|
|
340
|
+
const parentOf = (parent: string): SchemaProperty | undefined =>
|
|
341
|
+
selectedDef(ui)?.properties?.[parent] as SchemaProperty | undefined;
|
|
342
|
+
|
|
343
|
+
/** One object field's add-row draft, made on first use. */
|
|
344
|
+
const draftOf = (parent: string): Draft => {
|
|
345
|
+
let draft = ui.drafts.get(parent);
|
|
346
|
+
if (!draft) {
|
|
347
|
+
draft = { name: "", type: "string" };
|
|
348
|
+
ui.drafts.set(parent, draft);
|
|
349
|
+
}
|
|
350
|
+
return draft;
|
|
351
|
+
};
|
|
423
352
|
|
|
424
|
-
|
|
425
|
-
const
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
353
|
+
/** A property map with one key renamed, rebuilt in place so a rename never reorders the list. */
|
|
354
|
+
const renameKey = <T>(properties: Record<string, T>, from: string, to: string): void => {
|
|
355
|
+
const next: Record<string, T> = {};
|
|
356
|
+
for (const [key, value] of Object.entries(properties)) {
|
|
357
|
+
next[key === from ? to : key] = value;
|
|
358
|
+
delete properties[key];
|
|
359
|
+
}
|
|
360
|
+
Object.assign(properties, next);
|
|
361
|
+
};
|
|
430
362
|
|
|
431
|
-
/**
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
363
|
+
/** Add or remove a name from a `required` list, made on first use. */
|
|
364
|
+
const markRequired = (
|
|
365
|
+
holder: { required?: string[] | undefined },
|
|
366
|
+
key: string,
|
|
367
|
+
required: boolean,
|
|
368
|
+
): void => {
|
|
369
|
+
if (!holder.required) {
|
|
370
|
+
holder.required = [];
|
|
371
|
+
}
|
|
372
|
+
const at = holder.required.indexOf(key);
|
|
373
|
+
if (required && at === -1) {
|
|
374
|
+
holder.required.push(key);
|
|
375
|
+
}
|
|
376
|
+
if (!required && at !== -1) {
|
|
377
|
+
holder.required.splice(at, 1);
|
|
378
|
+
}
|
|
379
|
+
};
|
|
380
|
+
|
|
381
|
+
/** A refusal: the control goes back to what the file says, and nothing is written. */
|
|
382
|
+
const refuse = (): void => {
|
|
383
|
+
hush(container);
|
|
384
|
+
redraw();
|
|
385
|
+
};
|
|
440
386
|
|
|
441
|
-
|
|
442
|
-
|
|
387
|
+
/** A write went through: forget the echo, redraw from the file, and commit it. */
|
|
388
|
+
const done = (): void => {
|
|
389
|
+
hush(container);
|
|
390
|
+
redraw();
|
|
391
|
+
persist();
|
|
392
|
+
};
|
|
443
393
|
|
|
444
|
-
|
|
445
|
-
|
|
394
|
+
return {
|
|
395
|
+
addNested(parent) {
|
|
396
|
+
const draft = draftOf(parent);
|
|
397
|
+
const name = draft.name.trim();
|
|
398
|
+
const holder = parentOf(parent);
|
|
399
|
+
if (!name || !holder) {
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
if (!holder.properties) {
|
|
403
|
+
holder.properties = {};
|
|
404
|
+
}
|
|
405
|
+
holder.properties[name] = schemaForType(draft.type || "string");
|
|
406
|
+
/* The name clears and the type does not: adding three strings in a row is the ordinary case,
|
|
407
|
+
and re-choosing the type each time would be the editor asking what it just heard. */
|
|
408
|
+
draft.name = "";
|
|
409
|
+
done();
|
|
410
|
+
},
|
|
411
|
+
cancelAdd() {
|
|
412
|
+
ui.addOpen = false;
|
|
413
|
+
ui.add = { ...BLANK_FIELD };
|
|
414
|
+
redraw();
|
|
415
|
+
},
|
|
416
|
+
cancelNew() {
|
|
417
|
+
ui.newOpen = false;
|
|
418
|
+
ui.newName = "";
|
|
419
|
+
redraw();
|
|
420
|
+
},
|
|
421
|
+
confirmAdd() {
|
|
422
|
+
const name = ui.add.name.trim();
|
|
423
|
+
const def = selectedDef(ui);
|
|
424
|
+
if (!name || !def) {
|
|
425
|
+
return;
|
|
426
|
+
}
|
|
427
|
+
if (!def.properties) {
|
|
428
|
+
def.properties = {};
|
|
429
|
+
}
|
|
430
|
+
def.properties[name] = schemaForType(ui.add.type, ui.add.format || undefined);
|
|
431
|
+
if (ui.add.required) {
|
|
432
|
+
markRequired(def, name, true);
|
|
433
|
+
}
|
|
434
|
+
ui.addOpen = false;
|
|
435
|
+
ui.add = { ...BLANK_FIELD };
|
|
436
|
+
done();
|
|
437
|
+
},
|
|
438
|
+
createNew() {
|
|
439
|
+
const name = ui.newName.trim();
|
|
440
|
+
const config = projectState?.projectConfig;
|
|
441
|
+
if (!name || !config) {
|
|
442
|
+
return;
|
|
443
|
+
}
|
|
444
|
+
if (!config.$defs) {
|
|
445
|
+
config.$defs = {};
|
|
446
|
+
}
|
|
447
|
+
if (config.$defs[name]) {
|
|
448
|
+
return;
|
|
449
|
+
}
|
|
450
|
+
config.$defs[name] = { properties: {}, required: [], type: "object" };
|
|
451
|
+
ui.selected = name;
|
|
452
|
+
ui.newOpen = false;
|
|
453
|
+
ui.newName = "";
|
|
454
|
+
done();
|
|
455
|
+
},
|
|
456
|
+
editAddFormat(value) {
|
|
457
|
+
ui.add.format = value;
|
|
458
|
+
redraw();
|
|
459
|
+
},
|
|
460
|
+
editAddName(value) {
|
|
461
|
+
ui.add.name = value;
|
|
462
|
+
redraw();
|
|
463
|
+
},
|
|
464
|
+
editAddRequired(required) {
|
|
465
|
+
ui.add.required = required;
|
|
466
|
+
redraw();
|
|
467
|
+
},
|
|
468
|
+
editAddType(value) {
|
|
469
|
+
ui.add.type = value;
|
|
470
|
+
redraw();
|
|
471
|
+
},
|
|
472
|
+
editDraftName(parent, value) {
|
|
473
|
+
draftOf(parent).name = value;
|
|
474
|
+
redraw();
|
|
475
|
+
},
|
|
476
|
+
editDraftType(parent, value) {
|
|
477
|
+
draftOf(parent).type = value;
|
|
478
|
+
redraw();
|
|
479
|
+
},
|
|
480
|
+
editNew(value) {
|
|
481
|
+
ui.newName = value;
|
|
482
|
+
redraw();
|
|
483
|
+
},
|
|
484
|
+
openAdd() {
|
|
485
|
+
ui.addOpen = true;
|
|
486
|
+
ui.add = { ...BLANK_FIELD };
|
|
487
|
+
redraw();
|
|
488
|
+
},
|
|
489
|
+
openNew() {
|
|
490
|
+
ui.newOpen = true;
|
|
491
|
+
ui.newName = "";
|
|
492
|
+
redraw();
|
|
493
|
+
},
|
|
494
|
+
removeField(key) {
|
|
495
|
+
const def = selectedDef(ui);
|
|
496
|
+
if (!def?.properties) {
|
|
497
|
+
return;
|
|
498
|
+
}
|
|
499
|
+
delete def.properties[key];
|
|
500
|
+
if (def.required) {
|
|
501
|
+
def.required = def.required.filter((name) => name !== key);
|
|
502
|
+
}
|
|
503
|
+
done();
|
|
504
|
+
},
|
|
505
|
+
removeNested(parent, key) {
|
|
506
|
+
const holder = parentOf(parent);
|
|
507
|
+
if (!holder?.properties) {
|
|
508
|
+
return;
|
|
509
|
+
}
|
|
510
|
+
delete holder.properties[key];
|
|
511
|
+
if (holder.required) {
|
|
512
|
+
holder.required = holder.required.filter((name) => name !== key);
|
|
513
|
+
}
|
|
514
|
+
done();
|
|
515
|
+
},
|
|
516
|
+
removeShape() {
|
|
517
|
+
const defs = currentDefs();
|
|
518
|
+
if (ui.selected === null || !defs?.[ui.selected]) {
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
delete defs[ui.selected];
|
|
522
|
+
ui.selected = null;
|
|
523
|
+
ui.addOpen = false;
|
|
524
|
+
done();
|
|
525
|
+
},
|
|
526
|
+
renameField(key, value) {
|
|
527
|
+
say(container, nameId(key), value);
|
|
528
|
+
const def = selectedDef(ui);
|
|
529
|
+
const name = value.trim();
|
|
530
|
+
if (!def?.properties || !name || name === key || def.properties[name]) {
|
|
531
|
+
refuse();
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
renameKey(def.properties, key, name);
|
|
535
|
+
if (def.required) {
|
|
536
|
+
def.required = def.required.map((entry) => (entry === key ? name : entry));
|
|
537
|
+
}
|
|
538
|
+
done();
|
|
539
|
+
},
|
|
540
|
+
renameNested(parent, key, value) {
|
|
541
|
+
say(container, nameId(nestedId(parent, key)), value);
|
|
542
|
+
const holder = parentOf(parent);
|
|
543
|
+
const name = value.trim();
|
|
544
|
+
if (!holder?.properties || !name || name === key || holder.properties[name]) {
|
|
545
|
+
refuse();
|
|
546
|
+
return;
|
|
547
|
+
}
|
|
548
|
+
renameKey(holder.properties, key, name);
|
|
549
|
+
if (holder.required) {
|
|
550
|
+
holder.required = holder.required.map((entry) => (entry === key ? name : entry));
|
|
551
|
+
}
|
|
552
|
+
done();
|
|
553
|
+
},
|
|
554
|
+
select(name) {
|
|
555
|
+
ui.selected = name;
|
|
556
|
+
ui.addOpen = false;
|
|
557
|
+
redraw();
|
|
558
|
+
},
|
|
559
|
+
setFormat(key, value) {
|
|
560
|
+
const def = selectedDef(ui);
|
|
561
|
+
const property = def?.properties?.[key];
|
|
562
|
+
if (!def?.properties || !property) {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
def.properties[key] = schemaForType(property.type || "string", value || undefined);
|
|
566
|
+
done();
|
|
567
|
+
},
|
|
568
|
+
setNestedFormat(parent, key, value) {
|
|
569
|
+
const holder = parentOf(parent);
|
|
570
|
+
const property = holder?.properties?.[key];
|
|
571
|
+
if (!holder?.properties || !property) {
|
|
572
|
+
return;
|
|
573
|
+
}
|
|
574
|
+
holder.properties[key] = schemaForType(property.type || "string", value || undefined);
|
|
575
|
+
done();
|
|
576
|
+
},
|
|
577
|
+
setNestedRequired(parent, key, required) {
|
|
578
|
+
say(container, requiredId(nestedId(parent, key)), required);
|
|
579
|
+
const holder = parentOf(parent);
|
|
580
|
+
if (!holder) {
|
|
581
|
+
refuse();
|
|
582
|
+
return;
|
|
583
|
+
}
|
|
584
|
+
markRequired(holder, key, required);
|
|
585
|
+
done();
|
|
586
|
+
},
|
|
587
|
+
setNestedType(parent, key, value) {
|
|
588
|
+
const holder = parentOf(parent);
|
|
589
|
+
const property = holder?.properties?.[key];
|
|
590
|
+
if (!holder?.properties || !property) {
|
|
591
|
+
return;
|
|
592
|
+
}
|
|
593
|
+
/* A format survives a move between the two types that carry one, and is dropped by any type
|
|
594
|
+
that does not: `schemaForType` is handed the old format only when the new type takes it. */
|
|
595
|
+
const format = takesFormat(value) ? detectFieldFormat(property) : undefined;
|
|
596
|
+
holder.properties[key] = schemaForType(value, format || undefined);
|
|
597
|
+
done();
|
|
598
|
+
},
|
|
599
|
+
setRequired(key, required) {
|
|
600
|
+
say(container, requiredId(key), required);
|
|
601
|
+
const def = selectedDef(ui);
|
|
602
|
+
if (!def) {
|
|
603
|
+
refuse();
|
|
604
|
+
return;
|
|
605
|
+
}
|
|
606
|
+
markRequired(def, key, required);
|
|
607
|
+
done();
|
|
608
|
+
},
|
|
609
|
+
setTarget(key, value) {
|
|
610
|
+
const def = selectedDef(ui);
|
|
611
|
+
if (!def?.properties?.[key]) {
|
|
612
|
+
return;
|
|
613
|
+
}
|
|
614
|
+
/* The pointer form is `#/content/<type>` — the same one `ui/form-controls.ts` writes, so a
|
|
615
|
+
reference authored here and one authored in the content-types builder are the same value. */
|
|
616
|
+
def.properties[key] = { $ref: `#/content/${value}` } as ContentTypeSchemaField;
|
|
617
|
+
done();
|
|
618
|
+
},
|
|
619
|
+
setType(key, value) {
|
|
620
|
+
const def = selectedDef(ui);
|
|
621
|
+
const property = def?.properties?.[key];
|
|
622
|
+
if (!def?.properties || !property) {
|
|
623
|
+
return;
|
|
624
|
+
}
|
|
625
|
+
const format = takesFormat(value) ? detectFieldFormat(property) : undefined;
|
|
626
|
+
def.properties[key] = schemaForType(value, format || undefined);
|
|
627
|
+
done();
|
|
628
|
+
},
|
|
629
|
+
};
|
|
446
630
|
}
|
|
447
631
|
|
|
448
632
|
// ─── Render ───────────────────────────────────────────────────────────────────
|
|
449
633
|
|
|
450
634
|
/**
|
|
451
|
-
* Render
|
|
635
|
+
* Render Project Settings › Data Shapes into `container`.
|
|
636
|
+
*
|
|
637
|
+
* One mount per container, re-used for every subsequent call. The host calls this again for any
|
|
638
|
+
* change it notices, and a section whose container has meanwhile been given to a DIFFERENT section
|
|
639
|
+
* finds its document gone — `litRender` into the same node replaces everything in it — so the mount
|
|
640
|
+
* is rebuilt exactly when it has actually been evicted, and disposed on the way out rather than
|
|
641
|
+
* left running over detached nodes.
|
|
452
642
|
*
|
|
453
643
|
* @param {HTMLElement} container
|
|
454
644
|
*/
|
|
455
|
-
export function renderDefsEditor(container: HTMLElement) {
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
<div class="settings-list-panel">
|
|
464
|
-
${defNames.map(
|
|
465
|
-
(name) => html`
|
|
466
|
-
<sp-action-button
|
|
467
|
-
size="s"
|
|
468
|
-
?selected=${selectedDef === name}
|
|
469
|
-
@click=${() => {
|
|
470
|
-
selectedDef = name;
|
|
471
|
-
showAddField = false;
|
|
472
|
-
rerender();
|
|
473
|
-
}}
|
|
474
|
-
>
|
|
475
|
-
${name}
|
|
476
|
-
</sp-action-button>
|
|
477
|
-
`,
|
|
478
|
-
)}
|
|
479
|
-
${
|
|
480
|
-
showNewDef
|
|
481
|
-
? html`
|
|
482
|
-
<div class="settings-inline-form">
|
|
483
|
-
<sp-textfield
|
|
484
|
-
size="s"
|
|
485
|
-
placeholder="ProductReview"
|
|
486
|
-
.value=${newDefName}
|
|
487
|
-
@input=${(e: Event) => {
|
|
488
|
-
newDefName = (e.target as HTMLInputElement).value;
|
|
489
|
-
}}
|
|
490
|
-
@keydown=${(e: KeyboardEvent) => {
|
|
491
|
-
if (e.key === "Enter") {
|
|
492
|
-
handleNewDef(rerender);
|
|
493
|
-
}
|
|
494
|
-
if (e.key === "Escape") {
|
|
495
|
-
showNewDef = false;
|
|
496
|
-
rerender();
|
|
497
|
-
}
|
|
498
|
-
}}
|
|
499
|
-
></sp-textfield>
|
|
500
|
-
<sp-action-button size="s" @click=${() => handleNewDef(rerender)}>
|
|
501
|
-
Create
|
|
502
|
-
</sp-action-button>
|
|
503
|
-
</div>
|
|
504
|
-
`
|
|
505
|
-
: html`
|
|
506
|
-
<sp-action-button
|
|
507
|
-
size="s"
|
|
508
|
-
quiet
|
|
509
|
-
@click=${() => {
|
|
510
|
-
showNewDef = true;
|
|
511
|
-
rerender();
|
|
512
|
-
}}
|
|
513
|
-
>
|
|
514
|
-
<sp-icon-add slot="icon"></sp-icon-add> New Data Shape
|
|
515
|
-
</sp-action-button>
|
|
516
|
-
`
|
|
517
|
-
}
|
|
518
|
-
</div>
|
|
519
|
-
`;
|
|
520
|
-
|
|
521
|
-
// Right column — schema editor
|
|
522
|
-
let editorTpl;
|
|
523
|
-
if (!selectedDef || !defs[selectedDef]) {
|
|
524
|
-
editorTpl = html`<div class="settings-empty-state">
|
|
525
|
-
Pick a data shape on the left, or create one.
|
|
526
|
-
</div>`;
|
|
527
|
-
} else {
|
|
528
|
-
const def = defs[selectedDef] as ContentTypeSchema;
|
|
529
|
-
const properties = def.properties || {};
|
|
530
|
-
const required = def.required || [];
|
|
531
|
-
|
|
532
|
-
const handlers: FieldHandlers = {
|
|
533
|
-
onAddNestedField: (p: string, s: { name: string; type: string; required: boolean }) =>
|
|
534
|
-
handleAddNestedField(p, s, rerender),
|
|
535
|
-
onChangeFormat: (n: string, f: string) => handleChangeFormat(n, f, rerender),
|
|
536
|
-
onChangeNestedFormat: (p: string, c: string, f: string) =>
|
|
537
|
-
handleChangeNestedFormat(p, c, f, rerender),
|
|
538
|
-
onChangeNestedType: (p: string, c: string, t: string) =>
|
|
539
|
-
handleChangeNestedType(p, c, t, rerender),
|
|
540
|
-
onChangeRefTarget: (n: string, t: string) => handleChangeRefTarget(n, t, rerender),
|
|
541
|
-
onChangeType: (n: string, t: string) => handleChangeType(n, t, rerender),
|
|
542
|
-
onDelete: (n: string) => handleDeleteField(n, rerender),
|
|
543
|
-
onDeleteNested: (p: string, c: string) => handleDeleteNested(p, c, rerender),
|
|
544
|
-
onRename: (oldN: string, newN: string) => handleRenameField(oldN, newN, rerender),
|
|
545
|
-
onRenameNested: (p: string, o: string, n: string) => handleRenameNested(p, o, n, rerender),
|
|
546
|
-
onToggleNestedRequired: (p: string, c: string) => handleToggleNestedRequired(p, c, rerender),
|
|
547
|
-
onToggleRequired: (n: string) => handleToggleRequired(n, rerender),
|
|
548
|
-
};
|
|
549
|
-
|
|
550
|
-
const targets = contentTypeNames();
|
|
551
|
-
const fieldCards = Object.entries(properties).map(([name, fieldDef]) =>
|
|
552
|
-
fieldCardTpl(name, fieldDef as SchemaProperty, required.includes(name), handlers, targets),
|
|
553
|
-
);
|
|
554
|
-
|
|
555
|
-
editorTpl = html`
|
|
556
|
-
<div class="settings-editor-panel">
|
|
557
|
-
<div class="settings-editor-header">
|
|
558
|
-
<h3>${selectedDef}</h3>
|
|
559
|
-
<sp-action-button
|
|
560
|
-
size="xs"
|
|
561
|
-
quiet
|
|
562
|
-
title="Delete data shape"
|
|
563
|
-
@click=${() => handleDeleteDef(rerender)}
|
|
564
|
-
>
|
|
565
|
-
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
566
|
-
</sp-action-button>
|
|
567
|
-
</div>
|
|
568
|
-
<div class="schema-field-list">${fieldCards}</div>
|
|
569
|
-
${
|
|
570
|
-
showAddField
|
|
571
|
-
? addFieldFormTpl(newFieldState, {
|
|
572
|
-
onCancel: () => {
|
|
573
|
-
showAddField = false;
|
|
574
|
-
newFieldState = {
|
|
575
|
-
format: "",
|
|
576
|
-
name: "",
|
|
577
|
-
required: false,
|
|
578
|
-
type: "string",
|
|
579
|
-
};
|
|
580
|
-
rerender();
|
|
581
|
-
},
|
|
582
|
-
onConfirm: () => handleAddField(rerender),
|
|
583
|
-
onInput: (field, value) => {
|
|
584
|
-
newFieldState = { ...newFieldState, [field]: value };
|
|
585
|
-
rerender();
|
|
586
|
-
},
|
|
587
|
-
})
|
|
588
|
-
: html`
|
|
589
|
-
<sp-action-button
|
|
590
|
-
size="s"
|
|
591
|
-
quiet
|
|
592
|
-
@click=${() => {
|
|
593
|
-
showAddField = true;
|
|
594
|
-
rerender();
|
|
595
|
-
}}
|
|
596
|
-
>
|
|
597
|
-
<sp-icon-add slot="icon"></sp-icon-add> Add Field
|
|
598
|
-
</sp-action-button>
|
|
599
|
-
`
|
|
600
|
-
}
|
|
601
|
-
</div>
|
|
602
|
-
`;
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
// Every section names itself in an <h3> matching its nav entry (Overview, Contexts, Head,
|
|
606
|
-
// Packages, Extensions, Deploy, Raw JSON). This one did not, so it was the only section whose
|
|
607
|
-
// Body never said what the reader had clicked.
|
|
608
|
-
const tpl = html`
|
|
609
|
-
<div class="settings-section">
|
|
610
|
-
<h3 class="settings-section-title">Data Shapes</h3>
|
|
611
|
-
<div class="settings-two-col">${listTpl} ${editorTpl}</div>
|
|
612
|
-
</div>
|
|
613
|
-
`;
|
|
614
|
-
|
|
615
|
-
litRender(tpl, container);
|
|
645
|
+
export function renderDefsEditor(container: HTMLElement): void {
|
|
646
|
+
let handle = mounts.get(container);
|
|
647
|
+
if (!handle?.attached()) {
|
|
648
|
+
handle?.dispose();
|
|
649
|
+
handle = mountDefsSurface(container, actionsFor(container));
|
|
650
|
+
mounts.set(container, handle);
|
|
651
|
+
}
|
|
652
|
+
handle.update(project(container));
|
|
616
653
|
}
|