@jxsuite/studio 6.1.0 → 6.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunks/2020-f2gkpy8d.js +4 -0
- package/dist/chunks/{action-group-2mqby73c.js → action-group-4aan7g6g.js} +3 -3
- package/dist/chunks/adapter-4912g339.js +4 -0
- package/dist/chunks/cf-account-picker-wccaenh6.js +4 -0
- package/dist/chunks/cf-settings-br6105tr.js +4 -0
- package/dist/chunks/color-28ksk2qm.js +4 -0
- package/dist/chunks/color-area-0fj8a4ga.js +4 -0
- package/dist/chunks/color-field-xch8kx2g.js +4 -0
- package/dist/chunks/color-slider-jxd8v350.js +4 -0
- package/dist/chunks/combobox-p0efwjke.js +4 -0
- package/dist/chunks/components-db68vsn7.js +4 -0
- package/dist/chunks/content-source-1449qesq.js +4 -0
- package/dist/chunks/convert-to-repeater-5h457egj.js +4 -0
- package/dist/chunks/dialog-c4x05vyx.js +4 -0
- package/dist/chunks/editor-x9jn86a7.js +4 -0
- package/dist/chunks/entry-commands-may24cpt.js +4 -0
- package/dist/chunks/extension-sections-pvr14vxr.js +4 -0
- package/dist/chunks/file-ops-x9v54pcj.js +4 -0
- package/dist/chunks/files-j3knqf0s.js +4 -0
- package/dist/chunks/format-host-tyre2trb.js +4 -0
- package/dist/chunks/fs-events-64ma9htc.js +4 -0
- package/dist/chunks/git-panel-84ps5dex.js +4 -0
- package/dist/chunks/grid-view-d2wq9ezg.js +51 -0
- package/dist/chunks/icons-pnfz478q.js +4 -0
- package/dist/chunks/iframe-entry-fvbk89gm.js +5 -0
- package/dist/chunks/iframe-entry-jdcktez8.js +5 -0
- package/dist/chunks/iframe-entry-x0ygf2yr.js +5 -0
- package/dist/chunks/iframe-entry-zap785cw.js +5 -0
- package/dist/chunks/iframe-host-ck6vejt5.js +4 -0
- package/dist/chunks/index-b8xw3vy9.js +4 -0
- package/dist/chunks/{index-gybvv7a6.js → index-zpjmzqfk.js} +3 -3
- package/dist/chunks/{javascript-rnvdp3k4.js → javascript-m54mnwp6.js} +3 -3
- package/dist/chunks/jsonMode-w15cmrcm.js +13 -0
- package/dist/chunks/jx-validate-at3zn8wa.js +4 -0
- package/dist/chunks/layers-h4xp1pss.js +4 -0
- package/dist/chunks/layers-panel-m5hw1d7g.js +4 -0
- package/dist/chunks/listbox-e74qjyq6.js +4 -0
- package/dist/chunks/lit-html-8t8t3dnr.js +4 -0
- package/dist/chunks/media-picker-2cweexc5.js +4 -0
- package/dist/chunks/menu-n6yzswmy.js +4 -0
- package/dist/chunks/menu-ynw13y0s.js +4 -0
- package/dist/chunks/monaco-binding-7gx83x3r.js +4 -0
- package/dist/chunks/monaco-buffer-qzxn567r.js +4 -0
- package/dist/chunks/monaco-setup-gxf6656g.js +4 -0
- package/dist/chunks/notify-cj5zbrs2.js +4 -0
- package/dist/chunks/number-field-y62xq0ty.js +4 -0
- package/dist/chunks/parse-b7ddhr8m.js +4 -0
- package/dist/chunks/platform-3dfws9js.js +4 -0
- package/dist/chunks/popover-jt2gxmzs.js +4 -0
- package/dist/chunks/publish-panel-1gvxbj21.js +4 -0
- package/dist/chunks/quick-search-0dajrwqg.js +4 -0
- package/dist/chunks/references-49qgt404.js +4 -0
- package/dist/chunks/right-panel-jm1bwe53.js +4 -0
- package/dist/chunks/section-registry-6kwc4fky.js +4 -0
- package/dist/chunks/select-rzds841e.js +4 -0
- package/dist/chunks/split-n2gfpm5m.js +4 -0
- package/dist/chunks/{studio-5g0ag7c0.js → studio-0q988f9w.js} +3 -3
- package/dist/chunks/studio-0rt52vpq.js +5 -0
- package/dist/chunks/studio-18vgbhs5.js +5 -0
- package/dist/chunks/{studio-pxg4ap1e.js → studio-1fxkaaea.js} +4 -4
- package/dist/chunks/studio-24mccdrj.js +5 -0
- package/dist/chunks/studio-2eh507zk.js +9 -0
- package/dist/chunks/studio-2hcbccfv.js +5 -0
- package/dist/chunks/studio-2j1cb95x.js +5 -0
- package/dist/chunks/studio-2jbs3rxv.js +5 -0
- package/dist/chunks/{studio-qbj760tx.js → studio-485046te.js} +7 -7
- package/dist/chunks/studio-4h8k4dw9.js +5 -0
- package/dist/chunks/studio-5f6n18kx.js +5 -0
- package/dist/chunks/studio-5gthpky6.js +5 -0
- package/dist/chunks/studio-5ve6bbep.js +5 -0
- package/dist/chunks/studio-6ep3js6b.js +9 -0
- package/dist/chunks/studio-6wkmf3wz.js +9 -0
- package/dist/chunks/studio-841rf3cg.js +5 -0
- package/dist/chunks/studio-8gmwgheg.js +5 -0
- package/dist/chunks/studio-8qm0ep50.js +5 -0
- package/dist/chunks/{studio-anyrgzvb.js → studio-90pq9km1.js} +4 -4
- package/dist/chunks/studio-924p1tm1.js +6 -0
- package/dist/chunks/{studio-v2bvmbjj.js → studio-a3g98wjr.js} +4 -4
- package/dist/chunks/studio-a3shpz9y.js +5 -0
- package/dist/chunks/studio-amsbaaar.js +6 -0
- package/dist/chunks/studio-at5spjbc.js +7 -0
- package/dist/chunks/studio-b2xv945e.js +5 -0
- package/dist/chunks/studio-bn5nkafw.js +5 -0
- package/dist/chunks/studio-bw6q5xjf.js +9 -0
- package/dist/chunks/studio-c7q0t9x1.js +5 -0
- package/dist/chunks/studio-camnhc96.js +6 -0
- package/dist/chunks/studio-cffqvnzd.js +5 -0
- package/dist/chunks/studio-d81xfh1g.js +5 -0
- package/dist/chunks/studio-dgz8d0c1.js +5 -0
- package/dist/chunks/studio-efvfrsf8.js +5 -0
- package/dist/chunks/studio-er30bpcx.js +5 -0
- package/dist/chunks/studio-ezscgr64.js +5 -0
- package/dist/chunks/studio-ffrrncvz.js +5 -0
- package/dist/chunks/studio-fpphgn8y.js +10 -0
- package/dist/chunks/studio-j1kvhdjk.js +4 -0
- package/dist/chunks/{studio-txjjzrsm.js → studio-j430n16g.js} +5 -5
- package/dist/chunks/studio-jjkg84nj.js +5 -0
- package/dist/chunks/studio-m0q32ars.js +4 -0
- package/dist/chunks/studio-m6bdst11.js +5 -0
- package/dist/chunks/studio-nv2cptd4.js +64 -0
- package/dist/chunks/studio-nwfrc98n.js +5 -0
- package/dist/chunks/studio-p1wgvp7r.js +5 -0
- package/dist/chunks/studio-pag2wncm.js +5 -0
- package/dist/chunks/studio-qvanvzfd.js +5 -0
- package/dist/chunks/studio-qwa64kt7.js +5 -0
- package/dist/chunks/studio-rk1zc4x3.js +928 -0
- package/dist/chunks/studio-se2r3ptc.js +5 -0
- package/dist/chunks/studio-sg2a0dh0.js +5 -0
- package/dist/chunks/studio-tjzjhe68.js +5 -0
- package/dist/chunks/{studio-0x3jyb8r.js → studio-tq0rtfwj.js} +4 -4
- package/dist/chunks/{studio-cpxkgjrj.js → studio-v0jzqdjm.js} +4 -4
- package/dist/chunks/studio-v2w7q8r1.js +7 -0
- package/dist/chunks/studio-v5vjq1h0.js +4 -0
- package/dist/chunks/studio-v8aga3vp.js +14 -0
- package/dist/chunks/studio-vfcdrfmv.js +5 -0
- package/dist/chunks/studio-vmfkgdjf.js +5 -0
- package/dist/chunks/studio-wt1816jj.js +5 -0
- package/dist/chunks/studio-wz73q5kq.js +4 -0
- package/dist/chunks/studio-y04qtxvp.js +7 -0
- package/dist/chunks/studio-y21g8zzg.js +5 -0
- package/dist/chunks/studio-y64hrt8p.js +12 -0
- package/dist/chunks/studio-ynn90f58.js +16 -0
- package/dist/chunks/studio-yxz682sx.js +5 -0
- package/dist/chunks/studio-zp7snvkq.js +5 -0
- package/dist/chunks/studio-zq4z6tm6.js +5 -0
- package/dist/chunks/swatch-group-ewh24n1f.js +4 -0
- package/dist/chunks/tabs-f5cw3tgp.js +4 -0
- package/dist/chunks/textfield-wewc8rq2.js +4 -0
- package/dist/chunks/toast-c9fn075p.js +4 -0
- package/dist/chunks/toast-host-ke5z4ng7.js +4 -0
- package/dist/chunks/{toolbar-2nnzqhrj.js → toolbar-44cm5cta.js} +3 -3
- package/dist/chunks/tooltip-5v8ngwj2.js +4 -0
- package/dist/chunks/tree-mdq3v5cw.js +4 -0
- package/dist/chunks/tsMode-sf49ee9x.js +14 -0
- package/dist/chunks/workspace-jtfahpee.js +4 -0
- package/dist/chunks/ws-client-nze0k6mv.js +4 -0
- package/dist/iframe-entry.js +16 -16
- package/dist/manifest.json +63 -63
- package/dist/studio.js +98 -98
- package/dist/workers/ts.worker.js +3 -3
- package/package.json +11 -11
- package/src/about/about-modal.ts +4 -3
- package/src/account/action-flow.ts +45 -38
- package/src/account-status.ts +7 -2
- package/src/browse/library-layouts.ts +8 -5
- package/src/browse/library-model.ts +18 -6
- package/src/browse/library-pane.ts +37 -21
- package/src/browse/library-preview.ts +14 -9
- package/src/browse/library-source.ts +1 -1
- package/src/canvas/asset-refs.ts +24 -9
- package/src/canvas/asset-resolve.ts +19 -23
- package/src/canvas/canvas-helpers.ts +7 -6
- package/src/canvas/canvas-live-render.ts +44 -53
- package/src/canvas/canvas-patcher.ts +30 -30
- package/src/canvas/canvas-perf.ts +11 -5
- package/src/canvas/canvas-render.ts +66 -42
- package/src/canvas/canvas-surface.ts +5 -3
- package/src/canvas/canvas-utils.ts +163 -64
- package/src/canvas/dialog-path.ts +9 -4
- package/src/canvas/dialog-state.ts +1 -2
- package/src/canvas/diff-marks.ts +9 -16
- package/src/canvas/diff-toolbar.ts +8 -7
- package/src/canvas/edit-width-drag.ts +4 -1
- package/src/canvas/edit-width.ts +1 -3
- package/src/canvas/iframe-channel.ts +27 -13
- package/src/canvas/iframe-drop.ts +13 -12
- package/src/canvas/iframe-editable-root.ts +29 -16
- package/src/canvas/iframe-entry.ts +80 -63
- package/src/canvas/iframe-eval.ts +73 -49
- package/src/canvas/iframe-host.ts +203 -118
- package/src/canvas/iframe-inline-edit.ts +69 -32
- package/src/canvas/iframe-insert.ts +5 -4
- package/src/canvas/iframe-interaction.ts +37 -26
- package/src/canvas/iframe-overlay.ts +1 -1
- package/src/canvas/iframe-patch.ts +18 -15
- package/src/canvas/iframe-position.ts +12 -6
- package/src/canvas/iframe-protocol.ts +15 -16
- package/src/canvas/iframe-render.ts +41 -36
- package/src/canvas/iframe-slash.ts +24 -11
- package/src/canvas/iframe-subtree.ts +6 -2
- package/src/canvas/path-mapping.ts +19 -12
- package/src/canvas/popover-path.ts +7 -3
- package/src/canvas/popover-state.ts +1 -2
- package/src/canvas/serialize-scope.ts +13 -5
- package/src/canvas/stage-scrollbar.ts +2 -1
- package/src/canvas/surface-registry.ts +21 -3
- package/src/collab/collab-commands.ts +7 -2
- package/src/collab/collab-session.ts +84 -45
- package/src/collab/collab-state.ts +4 -3
- package/src/collab/monaco-binding.ts +16 -13
- package/src/collab/monaco-cursors.ts +5 -2
- package/src/commands/budget.ts +3 -3
- package/src/commands/command-args.ts +103 -71
- package/src/commands/context.ts +22 -20
- package/src/commands/defaults.ts +4 -1
- package/src/commands/keymap.ts +47 -43
- package/src/commands/levels.ts +3 -3
- package/src/commands/live-context.ts +10 -8
- package/src/commands/reference.ts +2 -1
- package/src/commands/registry.ts +12 -10
- package/src/commands/run-reported.ts +15 -10
- package/src/component-props.ts +46 -30
- package/src/content/collection-match.ts +5 -4
- package/src/content/draft-state.ts +4 -3
- package/src/content/entry-commands.ts +3 -2
- package/src/content/entry-editor.ts +29 -17
- package/src/content/entry-fields.ts +12 -7
- package/src/content/entry-model.ts +21 -12
- package/src/editor/canvas-context-menu.ts +1 -2
- package/src/editor/canvas-slash-bridge.ts +3 -6
- package/src/editor/content-slice.ts +16 -14
- package/src/editor/context-menu.ts +35 -22
- package/src/editor/convert-targets.ts +30 -35
- package/src/editor/convert-to-component.ts +11 -5
- package/src/editor/convert-to-repeater.ts +34 -22
- package/src/editor/editable-tags.ts +17 -2
- package/src/editor/file-drop-action.ts +21 -22
- package/src/editor/inline-edit-apply.ts +35 -17
- package/src/editor/inline-edit.ts +66 -50
- package/src/editor/inline-format.ts +59 -56
- package/src/editor/inline-link.ts +40 -15
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/merge-tags.ts +58 -19
- package/src/editor/repeater-scope.ts +39 -28
- package/src/editor/shortcuts.ts +87 -47
- package/src/editor/slash-menu.ts +1 -6
- package/src/files/asset-versions.ts +4 -4
- package/src/files/components.ts +15 -10
- package/src/files/elements.ts +5 -6
- package/src/files/file-ops.ts +77 -36
- package/src/files/files.ts +90 -95
- package/src/files/media-meta.ts +2 -1
- package/src/files/media-paths.ts +6 -1
- package/src/files/serialize-document.ts +2 -2
- package/src/format/constraints.ts +1 -1
- package/src/format/convert-file.ts +27 -13
- package/src/format/format-choices.ts +2 -1
- package/src/format/format-host.ts +76 -50
- package/src/github/github-auth-status.ts +27 -3
- package/src/github/github-auth.ts +19 -9
- package/src/github/github-publish.ts +24 -20
- package/src/grid/cell-editors.ts +46 -31
- package/src/grid/cell-popovers.ts +5 -3
- package/src/grid/edit-buffer.ts +10 -9
- package/src/grid/grid-controller.ts +20 -22
- package/src/grid/grid-layout.ts +21 -5
- package/src/grid/grid-open.ts +3 -6
- package/src/grid/grid-panel.ts +11 -4
- package/src/grid/grid-source.ts +8 -8
- package/src/grid/grid-view.ts +14 -17
- package/src/grid/redirects-grid.ts +14 -5
- package/src/grid/redirects.ts +16 -14
- package/src/grid/schema-columns.ts +25 -19
- package/src/grid/sources/connector-source.ts +47 -20
- package/src/grid/sources/content-source.ts +25 -24
- package/src/grid/sources/csv-file-source.ts +2 -2
- package/src/grid/tabulator-tables.d.ts +55 -34
- package/src/hosting/stage.ts +7 -7
- package/src/i18n/i18n-commands.ts +1 -2
- package/src/media/media-pane.ts +4 -1
- package/src/new-project/add-repo-modal.ts +20 -19
- package/src/new-project/import-tab.ts +10 -14
- package/src/new-project/location-fields.ts +2 -1
- package/src/new-project/new-project-modal.ts +12 -9
- package/src/packages/jxsuite-update.ts +4 -3
- package/src/packages/pull-package-sync.ts +26 -25
- package/src/page-params.ts +97 -68
- package/src/panels/activity-panel.ts +17 -9
- package/src/panels/ai-chat/attached-context.ts +13 -7
- package/src/panels/ai-chat/chat-view.ts +80 -73
- package/src/panels/ai-chat/composer.ts +30 -33
- package/src/panels/ai-chat/sessions-view.ts +1 -2
- package/src/panels/ai-panel.ts +23 -20
- package/src/panels/block-action-bar.ts +46 -35
- package/src/panels/bottom-dock.ts +5 -3
- package/src/panels/canvas-dnd-bridge.ts +31 -24
- package/src/panels/data-explorer.ts +62 -25
- package/src/panels/data-grid.ts +14 -11
- package/src/panels/dnd.ts +108 -118
- package/src/panels/editors.ts +55 -22
- package/src/panels/elements-panel.ts +10 -3
- package/src/panels/events-panel.ts +84 -69
- package/src/panels/formula-workspace.ts +76 -51
- package/src/panels/frontmatter-fields.ts +24 -19
- package/src/panels/frontmatter-panel.ts +44 -34
- package/src/panels/git-diff-open.ts +4 -4
- package/src/panels/git-panel.ts +49 -61
- package/src/panels/head-panel.ts +131 -111
- package/src/panels/i18n-panel.ts +10 -3
- package/src/panels/imports-panel.ts +23 -47
- package/src/panels/jump-bar.ts +48 -29
- package/src/panels/layers-panel.ts +97 -74
- package/src/panels/left-panel.ts +3 -3
- package/src/panels/pane-context.ts +31 -22
- package/src/panels/pane-grid.ts +49 -20
- package/src/panels/panel-registry.ts +22 -37
- package/src/panels/properties-panel.ts +175 -121
- package/src/panels/quick-search.ts +53 -31
- package/src/panels/right-panel.ts +3 -3
- package/src/panels/seo-modal.ts +13 -17
- package/src/panels/settings-menu.ts +15 -15
- package/src/panels/signals-panel.ts +153 -131
- package/src/panels/statement-editor.ts +230 -140
- package/src/panels/style-panel.ts +254 -216
- package/src/panels/style-utils.ts +114 -51
- package/src/panels/stylebook-doc.ts +57 -54
- package/src/panels/stylebook-layers-panel.ts +9 -9
- package/src/panels/stylebook-panel.ts +13 -17
- package/src/panels/tab-drop.ts +27 -11
- package/src/panels/tab-strip.ts +36 -26
- package/src/platform-errors.ts +57 -43
- package/src/platform.ts +58 -30
- package/src/platforms/cloud.ts +345 -175
- package/src/platforms/default-platform.ts +10 -4
- package/src/platforms/devserver.ts +174 -127
- package/src/platforms/read-batcher.ts +16 -8
- package/src/project-list.ts +2 -1
- package/src/publish/deploy-checklist.ts +8 -2
- package/src/publish/pages-service.ts +28 -13
- package/src/publish/publish-commands.ts +5 -4
- package/src/publish/publish-panel.ts +10 -6
- package/src/recent-projects.ts +15 -8
- package/src/resize-edges.ts +26 -36
- package/src/services/a11y-report.ts +26 -19
- package/src/services/agent-seed.ts +23 -11
- package/src/services/ai-ask.ts +9 -12
- package/src/services/ai-command-tools.ts +68 -62
- package/src/services/ai-import-tools.ts +80 -67
- package/src/services/ai-models.ts +17 -13
- package/src/services/ai-project-tools.ts +68 -32
- package/src/services/ai-session-store.ts +19 -10
- package/src/services/ai-system-prompt.ts +46 -20
- package/src/services/ai-tools.ts +71 -27
- package/src/services/ai-write-report.ts +20 -12
- package/src/services/ai-writes.ts +4 -4
- package/src/services/automation.ts +58 -31
- package/src/services/cem-export.ts +149 -58
- package/src/services/clock.ts +6 -1
- package/src/services/code-services.ts +37 -31
- package/src/services/context-manager.ts +14 -10
- package/src/services/context-resolver.ts +20 -8
- package/src/services/data-service.ts +19 -15
- package/src/services/directory-picker.ts +18 -12
- package/src/services/document-assistant.ts +39 -19
- package/src/services/gated-registry.ts +10 -6
- package/src/services/harness/chat-reducer.ts +23 -1
- package/src/services/import-client.ts +91 -38
- package/src/services/import-seed.ts +8 -2
- package/src/services/jx-validate.ts +28 -31
- package/src/services/live-preview.ts +0 -0
- package/src/services/live-surfaces.ts +6 -3
- package/src/services/model-uri.ts +10 -2
- package/src/services/module-preloads.ts +12 -8
- package/src/services/monaco-buffer.ts +43 -24
- package/src/services/monaco-lazy.ts +7 -8
- package/src/services/monaco-setup.ts +23 -16
- package/src/services/notify.ts +44 -17
- package/src/services/popover-report.ts +25 -10
- package/src/services/preview-eval.ts +20 -8
- package/src/services/profile.ts +13 -6
- package/src/services/project-path.ts +13 -10
- package/src/services/references.ts +0 -0
- package/src/services/render-critic.ts +8 -7
- package/src/services/settings/kernel.ts +13 -4
- package/src/services/settings/write-queue.ts +9 -9
- package/src/services/token-lint.ts +12 -10
- package/src/services/tool-executor.ts +17 -5
- package/src/services/tool-outcomes.ts +44 -24
- package/src/services/trusted-types.ts +24 -7
- package/src/settings/contexts-section.ts +11 -8
- package/src/settings/contributed-section.ts +79 -60
- package/src/settings/css-vars-editor.ts +37 -16
- package/src/settings/defs-editor.ts +38 -35
- package/src/settings/extension-rows.ts +16 -8
- package/src/settings/extension-sections.ts +23 -13
- package/src/settings/extensions-section.ts +4 -1
- package/src/settings/general-settings.ts +11 -8
- package/src/settings/locales-section.ts +20 -12
- package/src/settings/preferences-dialog.ts +9 -6
- package/src/settings/preferences-keymap.ts +7 -5
- package/src/settings/preferences-sections.ts +11 -5
- package/src/settings/project-sections.ts +1 -1
- package/src/settings/schema-field-ui.ts +9 -0
- package/src/settings/settings-document.ts +5 -5
- package/src/shell/tree.ts +6 -5
- package/src/shell.ts +104 -81
- package/src/site-context.ts +37 -22
- package/src/state.ts +41 -49
- package/src/store.ts +54 -34
- package/src/studio.ts +39 -77
- package/src/style/live-preview.ts +1 -1
- package/src/style/project-styles.ts +6 -7
- package/src/style/token-ref.ts +5 -4
- package/src/surfaces/about.ts +18 -19
- package/src/surfaces/add-repo.ts +17 -19
- package/src/surfaces/ai-chat.ts +31 -36
- package/src/surfaces/ai-credentials-form.ts +6 -16
- package/src/surfaces/ai-managed-connect.ts +3 -4
- package/src/surfaces/ai-model-picker.ts +3 -5
- package/src/surfaces/block-action-bar.ts +22 -11
- package/src/surfaces/boot-failure.ts +4 -5
- package/src/surfaces/bottom-dock.ts +9 -18
- package/src/surfaces/canvas-stage.ts +12 -11
- package/src/surfaces/cf-account-picker.ts +17 -18
- package/src/surfaces/commandbar.ts +14 -9
- package/src/surfaces/convert-repeater.ts +17 -19
- package/src/surfaces/data-actions.ts +9 -7
- package/src/surfaces/dialog.ts +26 -28
- package/src/surfaces/diff-toolbar.ts +9 -7
- package/src/surfaces/doc-header.ts +10 -19
- package/src/surfaces/empty-state.ts +7 -10
- package/src/surfaces/entry-editor.ts +14 -11
- package/src/surfaces/expression-editor.ts +11 -25
- package/src/surfaces/files-panel.ts +27 -14
- package/src/surfaces/formula-palette.ts +4 -5
- package/src/surfaces/git-panel.ts +7 -15
- package/src/surfaces/github-auth.ts +16 -20
- package/src/surfaces/github-publish.ts +17 -22
- package/src/surfaces/grid-cell.ts +30 -31
- package/src/surfaces/grid-open.ts +10 -14
- package/src/surfaces/grid-panel.ts +13 -9
- package/src/surfaces/grid-replace.ts +13 -13
- package/src/surfaces/grid-views.ts +15 -18
- package/src/surfaces/inspector-dock.ts +13 -12
- package/src/surfaces/jump-bar.ts +5 -6
- package/src/surfaces/jxsuite-update.ts +16 -22
- package/src/surfaces/library-pane.ts +25 -20
- package/src/surfaces/logic-panel.ts +17 -30
- package/src/surfaces/logic-workspace.ts +13 -15
- package/src/surfaces/media-browser.ts +11 -10
- package/src/surfaces/media-field.ts +4 -5
- package/src/surfaces/media-pane.ts +8 -7
- package/src/surfaces/menu.ts +29 -23
- package/src/surfaces/navigator-dock.ts +11 -12
- package/src/surfaces/new-project.ts +27 -26
- package/src/surfaces/pane-context.ts +20 -25
- package/src/surfaces/pane-grid.ts +15 -10
- package/src/surfaces/panel-activity.ts +5 -6
- package/src/surfaces/panel-data.ts +3 -4
- package/src/surfaces/panel-deploy-checklist.ts +5 -6
- package/src/surfaces/panel-elements.ts +5 -7
- package/src/surfaces/panel-i18n.ts +5 -7
- package/src/surfaces/panel-imports.ts +6 -13
- package/src/surfaces/panel-outline.ts +25 -19
- package/src/surfaces/panel-page.ts +11 -18
- package/src/surfaces/panel-problems.ts +5 -7
- package/src/surfaces/panel-signals.ts +20 -34
- package/src/surfaces/panel-stylebook-layers.ts +5 -8
- package/src/surfaces/preferences.ts +21 -11
- package/src/surfaces/progress-modal.ts +15 -9
- package/src/surfaces/properties-panel.json +1 -1
- package/src/surfaces/properties-panel.ts +20 -42
- package/src/surfaces/publish.ts +18 -18
- package/src/surfaces/push-plan.ts +7 -8
- package/src/surfaces/rail.json +126 -73
- package/src/surfaces/rail.ts +38 -12
- package/src/surfaces/reference-field.ts +11 -15
- package/src/surfaces/schema-builder.ts +16 -22
- package/src/surfaces/schema-form.ts +15 -25
- package/src/surfaces/secret-field.ts +9 -6
- package/src/surfaces/seo.ts +21 -15
- package/src/surfaces/settings-contexts.ts +9 -7
- package/src/surfaces/settings-contributed.ts +14 -10
- package/src/surfaces/settings-css-vars.ts +5 -12
- package/src/surfaces/settings-defs.json +2 -2
- package/src/surfaces/settings-defs.ts +17 -21
- package/src/surfaces/settings-deploy.ts +8 -15
- package/src/surfaces/settings-extensions.ts +5 -6
- package/src/surfaces/settings-head.ts +33 -39
- package/src/surfaces/settings-locales.ts +8 -20
- package/src/surfaces/settings-overview.ts +5 -6
- package/src/surfaces/settings-packages.ts +8 -7
- package/src/surfaces/settings-pane.ts +9 -18
- package/src/surfaces/settings-rawjson.ts +5 -6
- package/src/surfaces/shell.ts +1 -2
- package/src/surfaces/slash-menu.ts +19 -9
- package/src/surfaces/statements.ts +13 -18
- package/src/surfaces/statusbar.ts +17 -14
- package/src/surfaces/style-panel.ts +18 -31
- package/src/surfaces/stylebook-chrome.ts +3 -5
- package/src/surfaces/tab-strip.ts +14 -13
- package/src/surfaces/target-line.ts +6 -7
- package/src/surfaces/welcome.ts +5 -7
- package/src/tabs/patch-ops.ts +4 -3
- package/src/tabs/project-config.ts +32 -21
- package/src/tabs/selection.ts +36 -8
- package/src/tabs/tab.ts +52 -41
- package/src/tabs/transact.ts +98 -53
- package/src/tabs/typing.ts +31 -0
- package/src/types.ts +32 -17
- package/src/ui/ai-credentials-form.ts +10 -13
- package/src/ui/ai-model-picker.ts +3 -3
- package/src/ui/cf-account-picker.ts +7 -9
- package/src/ui/color-selector.ts +9 -5
- package/src/ui/dynamic-slot.ts +7 -8
- package/src/ui/expression-editor.ts +112 -105
- package/src/ui/form-controls.ts +57 -48
- package/src/ui/formula-catalog.ts +239 -230
- package/src/ui/formula-chips.ts +33 -18
- package/src/ui/layers.ts +44 -31
- package/src/ui/media-picker.ts +18 -9
- package/src/ui/popover-surface.ts +20 -12
- package/src/ui/progress-modal.ts +13 -8
- package/src/ui/regions.ts +7 -6
- package/src/ui/schema-form.ts +113 -70
- package/src/ui/surface.ts +71 -8
- package/src/ui/timing.ts +9 -0
- package/src/ui/value-source.ts +44 -31
- package/src/ui/virtual-window.ts +3 -2
- package/src/utils/base64.ts +3 -1
- package/src/utils/edit-display.ts +103 -74
- package/src/utils/element-selectors.ts +3 -2
- package/src/utils/google-fonts.ts +15 -14
- package/src/utils/inherited-style.ts +18 -20
- package/src/utils/link-target.ts +1 -1
- package/src/utils/preview-format.ts +25 -3
- package/src/utils/strip-events.ts +49 -22
- package/src/utils/studio-utils.ts +29 -14
- package/src/view.ts +2 -3
- package/src/workspace/pane-derive.ts +10 -12
- package/src/workspace/session.ts +53 -43
- package/src/workspace/workspace.ts +47 -36
- package/dist/chunks/2020-nkxms0ay.js +0 -4
- package/dist/chunks/adapter-m948ab5f.js +0 -4
- package/dist/chunks/cf-account-picker-n8hr38v7.js +0 -4
- package/dist/chunks/cf-settings-356xdtne.js +0 -4
- package/dist/chunks/color-area-jqhna8zy.js +0 -4
- package/dist/chunks/color-field-embq5ctg.js +0 -4
- package/dist/chunks/color-fpezvnkt.js +0 -4
- package/dist/chunks/color-slider-1qmw7wqy.js +0 -4
- package/dist/chunks/combobox-773hdzyj.js +0 -4
- package/dist/chunks/components-yk3t844e.js +0 -4
- package/dist/chunks/content-source-bawmyc09.js +0 -4
- package/dist/chunks/convert-to-repeater-7wns8kf1.js +0 -4
- package/dist/chunks/dialog-mtm9fq5t.js +0 -4
- package/dist/chunks/editor-6sc8nacq.js +0 -4
- package/dist/chunks/entry-commands-8ay9fh81.js +0 -4
- package/dist/chunks/extension-sections-na7rj3nr.js +0 -4
- package/dist/chunks/file-ops-gye0qw56.js +0 -4
- package/dist/chunks/files-dmc2xvcq.js +0 -4
- package/dist/chunks/format-host-t76b53f8.js +0 -4
- package/dist/chunks/fs-events-cg4tpz06.js +0 -4
- package/dist/chunks/git-panel-yef7bshy.js +0 -4
- package/dist/chunks/grid-view-7tt4bxw1.js +0 -51
- package/dist/chunks/icons-qbh8akb1.js +0 -4
- package/dist/chunks/iframe-entry-f7mqjwpm.js +0 -5
- package/dist/chunks/iframe-entry-haw7jkvz.js +0 -5
- package/dist/chunks/iframe-entry-z6qrytv3.js +0 -5
- package/dist/chunks/iframe-host-tmz30gfp.js +0 -4
- package/dist/chunks/index-nag78a9h.js +0 -4
- package/dist/chunks/jsonMode-zhwsprqk.js +0 -13
- package/dist/chunks/jx-validate-3gbp5nkn.js +0 -4
- package/dist/chunks/layers-fvz99859.js +0 -4
- package/dist/chunks/layers-panel-gchmbf9p.js +0 -4
- package/dist/chunks/listbox-s8fkmnq8.js +0 -4
- package/dist/chunks/lit-html-9agdspkt.js +0 -4
- package/dist/chunks/media-picker-rhjvyy00.js +0 -4
- package/dist/chunks/menu-2e4y9j7b.js +0 -4
- package/dist/chunks/menu-37d07z9m.js +0 -4
- package/dist/chunks/monaco-binding-2jnhjjvr.js +0 -4
- package/dist/chunks/monaco-buffer-rp21359g.js +0 -4
- package/dist/chunks/monaco-setup-vhh7gayr.js +0 -4
- package/dist/chunks/notify-h3www560.js +0 -4
- package/dist/chunks/number-field-gw7sq8rn.js +0 -4
- package/dist/chunks/parse-897fvqah.js +0 -4
- package/dist/chunks/platform-hz34wcd7.js +0 -4
- package/dist/chunks/popover-8htra4er.js +0 -4
- package/dist/chunks/publish-panel-exsmjzyj.js +0 -4
- package/dist/chunks/quick-search-pgaqeh0m.js +0 -4
- package/dist/chunks/references-2cgdxnb8.js +0 -4
- package/dist/chunks/right-panel-jf856w5w.js +0 -4
- package/dist/chunks/section-registry-jnf8r5gy.js +0 -4
- package/dist/chunks/select-n3v7gwas.js +0 -4
- package/dist/chunks/split-pff8f3ze.js +0 -4
- package/dist/chunks/studio-0f55h9ye.js +0 -5
- package/dist/chunks/studio-0sqdhnew.js +0 -12
- package/dist/chunks/studio-19x1c75w.js +0 -5
- package/dist/chunks/studio-2sqcndak.js +0 -5
- package/dist/chunks/studio-3aey7dd3.js +0 -5
- package/dist/chunks/studio-3bpdkq2s.js +0 -5
- package/dist/chunks/studio-47y6fbpf.js +0 -5
- package/dist/chunks/studio-4m3826v2.js +0 -5
- package/dist/chunks/studio-4nx2dj1g.js +0 -4
- package/dist/chunks/studio-4xv5s47r.js +0 -9
- package/dist/chunks/studio-5g4t8v6w.js +0 -5
- package/dist/chunks/studio-5py436sf.js +0 -5
- package/dist/chunks/studio-77kt594t.js +0 -5
- package/dist/chunks/studio-8f87n763.js +0 -10
- package/dist/chunks/studio-8mb00b84.js +0 -5
- package/dist/chunks/studio-8p51gq2h.js +0 -6
- package/dist/chunks/studio-954ds834.js +0 -5
- package/dist/chunks/studio-9ft45c83.js +0 -9
- package/dist/chunks/studio-a2138x5k.js +0 -7
- package/dist/chunks/studio-a9yzstqj.js +0 -5
- package/dist/chunks/studio-acdksxx6.js +0 -5
- package/dist/chunks/studio-ar8jcf2y.js +0 -5
- package/dist/chunks/studio-bmn4mn9j.js +0 -7
- package/dist/chunks/studio-e7xxa0sj.js +0 -5
- package/dist/chunks/studio-ecaz25vk.js +0 -5
- package/dist/chunks/studio-ery42sqr.js +0 -4
- package/dist/chunks/studio-fzdywr6p.js +0 -9
- package/dist/chunks/studio-g5fmf0c1.js +0 -5
- package/dist/chunks/studio-g7nse7zb.js +0 -5
- package/dist/chunks/studio-geq1ea6k.js +0 -14
- package/dist/chunks/studio-ghkb32r8.js +0 -5
- package/dist/chunks/studio-ghrhy2y7.js +0 -5
- package/dist/chunks/studio-grawhey1.js +0 -5
- package/dist/chunks/studio-h0xgswew.js +0 -5
- package/dist/chunks/studio-h9c4p2tx.js +0 -5
- package/dist/chunks/studio-jyzj5cxs.js +0 -5
- package/dist/chunks/studio-mgx8yhnz.js +0 -64
- package/dist/chunks/studio-mpb04y1d.js +0 -6
- package/dist/chunks/studio-n17mty64.js +0 -5
- package/dist/chunks/studio-nwh2eqsk.js +0 -6
- package/dist/chunks/studio-q1h883bt.js +0 -16
- package/dist/chunks/studio-q4tv82h7.js +0 -9
- package/dist/chunks/studio-qpqa7vk3.js +0 -5
- package/dist/chunks/studio-qqz8e5gk.js +0 -5
- package/dist/chunks/studio-qvasen3f.js +0 -5
- package/dist/chunks/studio-rggdb3hm.js +0 -5
- package/dist/chunks/studio-sghy936h.js +0 -4
- package/dist/chunks/studio-skprc6ef.js +0 -5
- package/dist/chunks/studio-tt2nactf.js +0 -5
- package/dist/chunks/studio-tyasmc39.js +0 -5
- package/dist/chunks/studio-vk9ezads.js +0 -5
- package/dist/chunks/studio-vzacdfsc.js +0 -4
- package/dist/chunks/studio-w4c0xmrw.js +0 -5
- package/dist/chunks/studio-wfjqhhbk.js +0 -5
- package/dist/chunks/studio-wgh5kxpj.js +0 -5
- package/dist/chunks/studio-xasj0796.js +0 -5
- package/dist/chunks/studio-y78s8r2n.js +0 -5
- package/dist/chunks/studio-z8w183sh.js +0 -5
- package/dist/chunks/studio-zscd9ty4.js +0 -928
- package/dist/chunks/studio-zxp13sss.js +0 -7
- package/dist/chunks/studio-zygnmm01.js +0 -5
- package/dist/chunks/swatch-group-8qh9a3bm.js +0 -4
- package/dist/chunks/tabs-jwthg40w.js +0 -4
- package/dist/chunks/textfield-spy3hc77.js +0 -4
- package/dist/chunks/toast-4d35zd64.js +0 -4
- package/dist/chunks/toast-host-8sjmfed3.js +0 -4
- package/dist/chunks/tooltip-fd6g9xy3.js +0 -4
- package/dist/chunks/tree-h19v1jfz.js +0 -4
- package/dist/chunks/tsMode-gr0m4dzr.js +0 -14
- package/dist/chunks/workspace-m15qtcqp.js +0 -4
- package/dist/chunks/ws-client-nf61frr1.js +0 -4
package/src/grid/edit-buffer.ts
CHANGED
|
@@ -92,15 +92,16 @@ export function createEditBuffer(opts: EditBufferOptions): EditBuffer {
|
|
|
92
92
|
const coalesceMs = opts.coalesceMs ?? 800;
|
|
93
93
|
const now = opts.now ?? (() => Date.now());
|
|
94
94
|
|
|
95
|
-
const
|
|
96
|
-
deletes: new Set
|
|
97
|
-
errors: new Map
|
|
98
|
-
inserts: new Map
|
|
99
|
-
pending: new Map
|
|
100
|
-
redoStack: []
|
|
101
|
-
stale: new Set
|
|
102
|
-
undoStack: []
|
|
103
|
-
}
|
|
95
|
+
const initial: EditBufferState = {
|
|
96
|
+
deletes: new Set(),
|
|
97
|
+
errors: new Map(),
|
|
98
|
+
inserts: new Map(),
|
|
99
|
+
pending: new Map(),
|
|
100
|
+
redoStack: [],
|
|
101
|
+
stale: new Set(),
|
|
102
|
+
undoStack: [],
|
|
103
|
+
};
|
|
104
|
+
const state: EditBufferState = reactive(initial);
|
|
104
105
|
|
|
105
106
|
let groupOps: BufferOp[] | null = null;
|
|
106
107
|
|
|
@@ -19,6 +19,7 @@ import { isRecentLocal } from "../files/fs-events";
|
|
|
19
19
|
import { showConfirmDialog } from "../ui/layers";
|
|
20
20
|
import { showProgressModal } from "../ui/progress-modal";
|
|
21
21
|
import { errorMessage } from "@jxsuite/schema/parse";
|
|
22
|
+
import { isNonNullObject, isNumber, isString } from "@jxsuite/schema/guards";
|
|
22
23
|
import { createEditBuffer } from "./edit-buffer";
|
|
23
24
|
import { cellToText } from "./schema-columns";
|
|
24
25
|
import type { EditBuffer } from "./edit-buffer";
|
|
@@ -111,25 +112,26 @@ export interface GridController {
|
|
|
111
112
|
/** Row-object key carrying the grid row identity through the engine. */
|
|
112
113
|
export const ROW_KEY_FIELD = "__key";
|
|
113
114
|
|
|
114
|
-
const _controllers = new WeakMap<
|
|
115
|
+
const _controllers = new WeakMap<Tab, GridController>();
|
|
115
116
|
|
|
116
117
|
/** The grid controller for a tab, if it is a grid tab. */
|
|
117
118
|
export function getGridController(tab: Tab | null): GridController | null {
|
|
118
|
-
return tab ? (_controllers.get(toRaw(tab
|
|
119
|
+
return tab ? (_controllers.get(toRaw(tab)) ?? null) : null;
|
|
119
120
|
}
|
|
120
121
|
|
|
121
122
|
/** Create and register the controller for a grid tab. One per tab; lives in the tab's scope. */
|
|
122
123
|
export function createGridController(tab: Tab, source: GridSource): GridController {
|
|
123
|
-
const
|
|
124
|
-
columns: []
|
|
125
|
-
error: null
|
|
126
|
-
grouping: null
|
|
124
|
+
const initial: GridControllerState = {
|
|
125
|
+
columns: [],
|
|
126
|
+
error: null,
|
|
127
|
+
grouping: null,
|
|
127
128
|
loading: false,
|
|
128
|
-
query: {}
|
|
129
|
-
rows: []
|
|
129
|
+
query: {},
|
|
130
|
+
rows: [],
|
|
130
131
|
saving: false,
|
|
131
132
|
total: 0,
|
|
132
|
-
}
|
|
133
|
+
};
|
|
134
|
+
const state: GridControllerState = reactive(initial);
|
|
133
135
|
|
|
134
136
|
let rowByKey = new Map<string, GridRow>();
|
|
135
137
|
const indexRows = (rows: GridRow[]) => {
|
|
@@ -181,7 +183,7 @@ export function createGridController(tab: Tab, source: GridSource): GridControll
|
|
|
181
183
|
|
|
182
184
|
const compareCells = (a: GridCellValue, b: GridCellValue, kind: GridColumnKind | undefined) => {
|
|
183
185
|
if (kind === "number") {
|
|
184
|
-
return (
|
|
186
|
+
return (isNumber(a) ? a : 0) - (isNumber(b) ? b : 0);
|
|
185
187
|
}
|
|
186
188
|
if (kind === "boolean") {
|
|
187
189
|
return (a === true ? 1 : 0) - (b === true ? 1 : 0);
|
|
@@ -280,7 +282,7 @@ export function createGridController(tab: Tab, source: GridSource): GridControll
|
|
|
280
282
|
}
|
|
281
283
|
for (const column of textColumns) {
|
|
282
284
|
const value = buffer.effectiveValue(rowKey, column.field);
|
|
283
|
-
if (
|
|
285
|
+
if (isString(value) && value.includes(find)) {
|
|
284
286
|
buffer.setCell(rowKey, column.field, value.replaceAll(find, replace));
|
|
285
287
|
changed += 1;
|
|
286
288
|
}
|
|
@@ -296,17 +298,13 @@ export function createGridController(tab: Tab, source: GridSource): GridControll
|
|
|
296
298
|
effectiveRows() {
|
|
297
299
|
// Detach values from reactive proxies — the grid engine must never hold live proxies.
|
|
298
300
|
const plain = (value: GridCellValue): GridCellValue =>
|
|
299
|
-
Array.isArray(value)
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
? { $ref: value.$ref }
|
|
303
|
-
: value;
|
|
304
|
-
const rowObject = (key: string) => {
|
|
305
|
-
const cells: Record<string, GridCellValue> = { [ROW_KEY_FIELD]: key };
|
|
301
|
+
Array.isArray(value) ? [...value] : isNonNullObject(value) ? { $ref: value.$ref } : value;
|
|
302
|
+
const rowObject = (key: string): Record<string, GridCellValue> => {
|
|
303
|
+
const cells: [field: string, value: GridCellValue][] = [[ROW_KEY_FIELD, key]];
|
|
306
304
|
for (const column of state.columns) {
|
|
307
|
-
cells[column.field
|
|
305
|
+
cells.push([column.field, plain(buffer.effectiveValue(key, column.field))]);
|
|
308
306
|
}
|
|
309
|
-
return cells;
|
|
307
|
+
return Object.fromEntries(cells);
|
|
310
308
|
};
|
|
311
309
|
const rows = orderedKeys().map((key) => rowObject(key));
|
|
312
310
|
for (const [tempKey] of buffer.state.inserts) {
|
|
@@ -547,11 +545,11 @@ export function createGridController(tab: Tab, source: GridSource): GridControll
|
|
|
547
545
|
|
|
548
546
|
onScopeDispose(() => {
|
|
549
547
|
setHistoryDelegate(tab, null);
|
|
550
|
-
_controllers.delete(toRaw(tab
|
|
548
|
+
_controllers.delete(toRaw(tab));
|
|
551
549
|
source.dispose?.();
|
|
552
550
|
});
|
|
553
551
|
});
|
|
554
552
|
|
|
555
|
-
_controllers.set(toRaw(tab
|
|
553
|
+
_controllers.set(toRaw(tab), controller);
|
|
556
554
|
return controller;
|
|
557
555
|
}
|
package/src/grid/grid-layout.ts
CHANGED
|
@@ -23,6 +23,7 @@
|
|
|
23
23
|
* layout keep their relative position at the end; saved fields that no longer exist are ignored —
|
|
24
24
|
* schemas drift).
|
|
25
25
|
*/
|
|
26
|
+
import { isNonNullObject, isString } from "@jxsuite/schema/guards";
|
|
26
27
|
|
|
27
28
|
/** One sort, as a saved view records it. The grid is single-key sorted; a second key is not saved. */
|
|
28
29
|
export interface GridSortSpec {
|
|
@@ -70,7 +71,7 @@ const STORAGE_PREFIX = "jx-grid-layout:";
|
|
|
70
71
|
|
|
71
72
|
function storage(): Storage | null {
|
|
72
73
|
try {
|
|
73
|
-
return
|
|
74
|
+
return typeof localStorage === "undefined" ? null : localStorage;
|
|
74
75
|
} catch {
|
|
75
76
|
return null; // Storage disabled (privacy mode) — layouts just don't persist.
|
|
76
77
|
}
|
|
@@ -83,8 +84,11 @@ function readRecord(gridId: string): GridLayoutRecord | null {
|
|
|
83
84
|
return null;
|
|
84
85
|
}
|
|
85
86
|
try {
|
|
86
|
-
const parsed = JSON.parse(raw)
|
|
87
|
-
|
|
87
|
+
const parsed: unknown = JSON.parse(raw);
|
|
88
|
+
/* SAFETY: this key is written only by `writeRecord`, which stores a serialized
|
|
89
|
+
GridLayoutRecord, and every reader treats each field as possibly absent (`?.`, `??`, and
|
|
90
|
+
`listSavedViews` drops any entry that is not a named view). A non-object is refused here. */
|
|
91
|
+
return isNonNullObject(parsed) ? (parsed as GridLayoutRecord) : null;
|
|
88
92
|
} catch {
|
|
89
93
|
return null;
|
|
90
94
|
}
|
|
@@ -153,7 +157,11 @@ export function resetGridLayout(gridId: string): void {
|
|
|
153
157
|
if (!record) {
|
|
154
158
|
return;
|
|
155
159
|
}
|
|
156
|
-
|
|
160
|
+
const next: GridLayoutRecord = { active: null };
|
|
161
|
+
if (record.views) {
|
|
162
|
+
next.views = record.views;
|
|
163
|
+
}
|
|
164
|
+
writeRecord(gridId, next);
|
|
157
165
|
}
|
|
158
166
|
|
|
159
167
|
// ─── Named views ──────────────────────────────────────────────────────────────
|
|
@@ -161,7 +169,15 @@ export function resetGridLayout(gridId: string): void {
|
|
|
161
169
|
/** The named views for a grid id, in save order. */
|
|
162
170
|
export function listSavedViews(gridId: string): SavedGridView[] {
|
|
163
171
|
const views = readRecord(gridId)?.views;
|
|
164
|
-
return Array.isArray(views) ? views.filter((view) =>
|
|
172
|
+
return Array.isArray(views) ? views.filter((view) => isSavedGridView(view)) : [];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Whether a stored `views` entry is a named view. Storage outlives the code that wrote it, so an
|
|
177
|
+
* entry without a string `name` (null, a primitive, an older shape) is skipped rather than shown.
|
|
178
|
+
*/
|
|
179
|
+
function isSavedGridView(view: unknown): view is SavedGridView {
|
|
180
|
+
return isNonNullObject(view) && "name" in view && isString(view.name);
|
|
165
181
|
}
|
|
166
182
|
|
|
167
183
|
/** The name of the view last applied to this grid, or null when the layout is unnamed. */
|
package/src/grid/grid-open.ts
CHANGED
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
createPagesSource,
|
|
20
20
|
} from "./sources/content-source";
|
|
21
21
|
import { createConnectorSource } from "./sources/connector-source";
|
|
22
|
-
import { makeGridTabId, parseGridTabId } from "./grid-source";
|
|
22
|
+
import { GRID_STUB_DOCUMENT, makeGridTabId, parseGridTabId } from "./grid-source";
|
|
23
23
|
import { libraryTabId } from "../browse/library-source";
|
|
24
24
|
import { argsSchema, optionalStringArg, stringArg, stringProperty } from "../commands/command-args";
|
|
25
25
|
import type { GridSource } from "./grid-source";
|
|
@@ -27,9 +27,6 @@ import type { GridOpenSurfaceHandle, GridSourceGroup } from "../surfaces/grid-op
|
|
|
27
27
|
import type { Tab } from "../tabs/tab";
|
|
28
28
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
29
29
|
|
|
30
|
-
/** Placeholder document for grid tabs — the grid never reads it; save routes to the controller. */
|
|
31
|
-
const GRID_STUB_DOCUMENT = { children: [], tagName: "div" };
|
|
32
|
-
|
|
33
30
|
/**
|
|
34
31
|
* Open (or activate) a `.csv` file as a grid tab.
|
|
35
32
|
*
|
|
@@ -187,14 +184,14 @@ function sourceGroups(
|
|
|
187
184
|
state: "listed",
|
|
188
185
|
title: "Project",
|
|
189
186
|
},
|
|
190
|
-
...connections.map((conn) => ({
|
|
187
|
+
...connections.map((conn): GridSourceGroup => ({
|
|
191
188
|
emptyMessage: "No tables — push a schema first.",
|
|
192
189
|
key: `data:${conn.name}`,
|
|
193
190
|
rows: conn.tables.map((table) => ({
|
|
194
191
|
id: makeGridTabId({ connection: conn.name, kind: "data", table }),
|
|
195
192
|
label: table,
|
|
196
193
|
})),
|
|
197
|
-
state:
|
|
194
|
+
state: conn.tables.length === 0 ? "empty" : "listed",
|
|
198
195
|
title: `Data · ${conn.name}`,
|
|
199
196
|
})),
|
|
200
197
|
];
|
package/src/grid/grid-panel.ts
CHANGED
|
@@ -56,6 +56,7 @@ import {
|
|
|
56
56
|
saveViewAs,
|
|
57
57
|
} from "./grid-layout";
|
|
58
58
|
import { argsSchema, stringArg, stringProperty } from "../commands/command-args";
|
|
59
|
+
import type { CommandArgValues } from "../commands/command-args";
|
|
59
60
|
import type { EffectScope } from "@vue/reactivity";
|
|
60
61
|
import type { GridController } from "./grid-controller";
|
|
61
62
|
import type { GridLayout, GridSortSpec } from "./grid-layout";
|
|
@@ -69,7 +70,7 @@ import type {
|
|
|
69
70
|
GridPanelView,
|
|
70
71
|
} from "../surfaces/grid-panel";
|
|
71
72
|
import type { GridViewsSurfaceHandle, GridViewsView } from "../surfaces/grid-views";
|
|
72
|
-
import type {
|
|
73
|
+
import type { PopoverSurfaceHandle } from "../ui/popover-surface";
|
|
73
74
|
|
|
74
75
|
interface ActiveGridPanel {
|
|
75
76
|
/** The pane whose stage this grid is drawn on. */
|
|
@@ -85,7 +86,7 @@ interface ActiveGridPanel {
|
|
|
85
86
|
/** Where that panel was opened, so a re-projection cannot move it back to the origin. */
|
|
86
87
|
viewsAt: { x: number; y: number };
|
|
87
88
|
/** Find & replace while it is open. */
|
|
88
|
-
replace:
|
|
89
|
+
replace: PopoverSurfaceHandle | null;
|
|
89
90
|
/**
|
|
90
91
|
* Rebuild the engine over the same controller.
|
|
91
92
|
*
|
|
@@ -692,6 +693,12 @@ function requireSurface(commandId: string) {
|
|
|
692
693
|
return surface;
|
|
693
694
|
}
|
|
694
695
|
|
|
696
|
+
/** A saved view, addressed by the grid it was saved for. */
|
|
697
|
+
interface SavedViewRef {
|
|
698
|
+
gridId: string;
|
|
699
|
+
name: string;
|
|
700
|
+
}
|
|
701
|
+
|
|
695
702
|
/**
|
|
696
703
|
* Saved views, as commands.
|
|
697
704
|
*
|
|
@@ -703,10 +710,10 @@ function requireSurface(commandId: string) {
|
|
|
703
710
|
* @returns {AnyCommand[]}
|
|
704
711
|
*/
|
|
705
712
|
export function gridViewCommands(): AnyCommand[] {
|
|
706
|
-
const viewNameArg = (commandId: string, args:
|
|
713
|
+
const viewNameArg = (commandId: string, args: CommandArgValues): SavedViewRef => {
|
|
707
714
|
const { controller } = requireSurface(commandId);
|
|
708
715
|
const gridId = controller.source.id;
|
|
709
|
-
const name = stringArg(commandId, args
|
|
716
|
+
const name = stringArg(commandId, args, "name");
|
|
710
717
|
const known = listSavedViews(gridId).map((view) => view.name);
|
|
711
718
|
if (!known.includes(name)) {
|
|
712
719
|
throw new RangeError(
|
package/src/grid/grid-source.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* pending edits; they never see the grid engine. Grid tabs over non-file sources are "virtual" —
|
|
7
7
|
* their tab id is a `grid://` URI rather than a document path.
|
|
8
8
|
*/
|
|
9
|
+
import { isNonNullObject } from "@jxsuite/schema/guards";
|
|
9
10
|
import type { JsonSchema } from "../ui/schema-form";
|
|
10
11
|
|
|
11
12
|
/** A single cell value. JSON-shaped so batches serialize cleanly. */
|
|
@@ -126,14 +127,7 @@ export function cellValuesEqual(a: GridCellValue, b: GridCellValue): boolean {
|
|
|
126
127
|
if (Array.isArray(a) && Array.isArray(b)) {
|
|
127
128
|
return a.length === b.length && a.every((v, i) => v === b[i]);
|
|
128
129
|
}
|
|
129
|
-
if (
|
|
130
|
-
a !== null &&
|
|
131
|
-
b !== null &&
|
|
132
|
-
typeof a === "object" &&
|
|
133
|
-
typeof b === "object" &&
|
|
134
|
-
!Array.isArray(a) &&
|
|
135
|
-
!Array.isArray(b)
|
|
136
|
-
) {
|
|
130
|
+
if (isNonNullObject(a) && isNonNullObject(b) && !Array.isArray(a) && !Array.isArray(b)) {
|
|
137
131
|
return a.$ref === b.$ref;
|
|
138
132
|
}
|
|
139
133
|
return false;
|
|
@@ -143,6 +137,12 @@ export function cellValuesEqual(a: GridCellValue, b: GridCellValue): boolean {
|
|
|
143
137
|
|
|
144
138
|
const GRID_SCHEME = "grid://";
|
|
145
139
|
|
|
140
|
+
/**
|
|
141
|
+
* Placeholder document for grid tabs: the grid never reads it, and save routes to the controller.
|
|
142
|
+
* Every tab takes its own `structuredClone` of it.
|
|
143
|
+
*/
|
|
144
|
+
export const GRID_STUB_DOCUMENT = { children: [], tagName: "div" };
|
|
145
|
+
|
|
146
146
|
/**
|
|
147
147
|
* The virtual sources a tab id can name.
|
|
148
148
|
*
|
package/src/grid/grid-view.ts
CHANGED
|
@@ -37,6 +37,7 @@ import type {
|
|
|
37
37
|
ColumnComponent,
|
|
38
38
|
ColumnDefinition,
|
|
39
39
|
RowComponent,
|
|
40
|
+
RowData,
|
|
40
41
|
} from "tabulator-tables";
|
|
41
42
|
import type { GridCellValue, GridColumn } from "./grid-source";
|
|
42
43
|
import type { GridController } from "./grid-controller";
|
|
@@ -89,7 +90,7 @@ function makeHost(className: string): HTMLElement {
|
|
|
89
90
|
return host;
|
|
90
91
|
}
|
|
91
92
|
|
|
92
|
-
function sorterForColumn(column: GridColumn): ColumnDefinition["sorter"] {
|
|
93
|
+
function sorterForColumn(column: GridColumn): ColumnDefinition<GridCellValue>["sorter"] {
|
|
93
94
|
switch (column.kind) {
|
|
94
95
|
case "number": {
|
|
95
96
|
return "number";
|
|
@@ -98,8 +99,7 @@ function sorterForColumn(column: GridColumn): ColumnDefinition["sorter"] {
|
|
|
98
99
|
return "boolean";
|
|
99
100
|
}
|
|
100
101
|
case "array": {
|
|
101
|
-
return (a:
|
|
102
|
-
cellToText(a as GridCellValue).localeCompare(cellToText(b as GridCellValue));
|
|
102
|
+
return (a: GridCellValue, b: GridCellValue) => cellToText(a).localeCompare(cellToText(b));
|
|
103
103
|
}
|
|
104
104
|
default: {
|
|
105
105
|
return "string";
|
|
@@ -119,9 +119,9 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
119
119
|
let built = false;
|
|
120
120
|
let pendingRefresh = false;
|
|
121
121
|
|
|
122
|
-
const rowKeyOf = (row: RowComponent) => String(row.getData()[ROW_KEY_FIELD] ?? "");
|
|
122
|
+
const rowKeyOf = (row: RowComponent<GridCellValue>) => String(row.getData()[ROW_KEY_FIELD] ?? "");
|
|
123
123
|
|
|
124
|
-
const paintRow = (row: RowComponent) => {
|
|
124
|
+
const paintRow = (row: RowComponent<GridCellValue>) => {
|
|
125
125
|
const rowState = buffer.rowState(rowKeyOf(row));
|
|
126
126
|
const el = row.getElement();
|
|
127
127
|
el.classList.toggle("jx-grid-row--pending-delete", rowState === "pending-delete");
|
|
@@ -129,7 +129,7 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
129
129
|
el.classList.toggle("jx-grid-row--stale", rowState === "stale");
|
|
130
130
|
};
|
|
131
131
|
|
|
132
|
-
const paintCell = (cell: CellComponent) => {
|
|
132
|
+
const paintCell = (cell: CellComponent<GridCellValue>) => {
|
|
133
133
|
const cellState = buffer.cellState(rowKeyOf(cell.getRow()), cell.getField());
|
|
134
134
|
const el = cell.getElement();
|
|
135
135
|
el.classList.toggle("jx-grid-cell--dirty", cellState === "dirty");
|
|
@@ -147,10 +147,11 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
147
147
|
(column.editable || (column.insertOnly === true && buffer.isInsertKey(rowKey))) &&
|
|
148
148
|
buffer.rowState(rowKey) !== "pending-delete";
|
|
149
149
|
|
|
150
|
-
const columnDefs: ColumnDefinition[] = columns.map((column) => {
|
|
150
|
+
const columnDefs: ColumnDefinition<GridCellValue>[] = columns.map((column) => {
|
|
151
151
|
const baseFormatter = formatterForColumn(column, makeHost);
|
|
152
152
|
return {
|
|
153
|
-
editable: (cell: CellComponent) =>
|
|
153
|
+
editable: (cell: CellComponent<GridCellValue>) =>
|
|
154
|
+
cellEditable(column, rowKeyOf(cell.getRow())),
|
|
154
155
|
// Image/reference cells edit through an anchored popover (dblclick), not an editor session.
|
|
155
156
|
editor: hasPopoverEditor(column) ? undefined : editorForColumn(column, makeHost),
|
|
156
157
|
field: column.field,
|
|
@@ -254,7 +255,7 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
254
255
|
};
|
|
255
256
|
const unregisterProbe = registerGridProbe(probe);
|
|
256
257
|
|
|
257
|
-
table.on("cellEdited", (cell: CellComponent) => {
|
|
258
|
+
table.on("cellEdited", (cell: CellComponent<GridCellValue>) => {
|
|
258
259
|
if (suppress) {
|
|
259
260
|
return;
|
|
260
261
|
}
|
|
@@ -267,7 +268,7 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
267
268
|
const value = coerceCellInput(cell.getValue(), column);
|
|
268
269
|
buffer.setCell(rowKey, field, value);
|
|
269
270
|
// Normalize what the table shows to the typed value (e.g. pasted "42" in a number column).
|
|
270
|
-
if (!cellValuesEqual(value, cell.getValue()
|
|
271
|
+
if (!cellValuesEqual(value, cell.getValue())) {
|
|
271
272
|
suppress = true;
|
|
272
273
|
try {
|
|
273
274
|
cell.setValue(value, true);
|
|
@@ -287,7 +288,7 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
287
288
|
saveGridLayout(gridId, { order: ordered.map((c) => c.getField()) });
|
|
288
289
|
});
|
|
289
290
|
|
|
290
|
-
table.on("cellDblClick", (_event:
|
|
291
|
+
table.on("cellDblClick", (_event: MouseEvent, cell: CellComponent<GridCellValue>) => {
|
|
291
292
|
const column = columnByField.get(cell.getField());
|
|
292
293
|
const rowKey = rowKeyOf(cell.getRow());
|
|
293
294
|
if (!column || !hasPopoverEditor(column) || !cellEditable(column, rowKey)) {
|
|
@@ -390,12 +391,8 @@ export function createGridView(host: HTMLElement, controller: GridController): G
|
|
|
390
391
|
return;
|
|
391
392
|
}
|
|
392
393
|
const fields = columns.map((column) => column.field);
|
|
393
|
-
table.setFilter((data:
|
|
394
|
-
fields.some((field) =>
|
|
395
|
-
cellToText(data[field] as GridCellValue)
|
|
396
|
-
.toLowerCase()
|
|
397
|
-
.includes(query),
|
|
398
|
-
),
|
|
394
|
+
table.setFilter((data: RowData<GridCellValue>) =>
|
|
395
|
+
fields.some((field) => cellToText(data[field]).toLowerCase().includes(query)),
|
|
399
396
|
);
|
|
400
397
|
},
|
|
401
398
|
};
|
|
@@ -41,6 +41,7 @@ import {
|
|
|
41
41
|
validateRedirects,
|
|
42
42
|
} from "./redirects";
|
|
43
43
|
import { optionalStringArg, stringProperty } from "../commands/command-args";
|
|
44
|
+
import { GRID_STUB_DOCUMENT } from "./grid-source";
|
|
44
45
|
import type { RedirectConfig, RedirectRule, RedirectTarget } from "./redirects";
|
|
45
46
|
import type {
|
|
46
47
|
CommitResult,
|
|
@@ -67,8 +68,10 @@ export const REDIRECTS_TAB_ID = "grid://redirects";
|
|
|
67
68
|
/** Who the Problems list attributes a redirect finding to. Also the key it clears them by. */
|
|
68
69
|
export const REDIRECTS_PROBLEM_SOURCE = "Redirects";
|
|
69
70
|
|
|
70
|
-
/**
|
|
71
|
-
|
|
71
|
+
/** The one key a redirect commit patches; `undefined` removes `redirects` from project.json. */
|
|
72
|
+
interface RedirectsPatch {
|
|
73
|
+
redirects: RedirectConfig | undefined;
|
|
74
|
+
}
|
|
72
75
|
|
|
73
76
|
/** Extensions the router treats as pages. Anything else under `pages/` is an asset, not a route. */
|
|
74
77
|
const PAGE_EXTENSIONS = [".json", ".md", ".html"];
|
|
@@ -120,7 +123,7 @@ export function redirectRow(rule: RedirectRule, index: number): GridRow {
|
|
|
120
123
|
|
|
121
124
|
/** The live redirects map, straight off the configuration document. */
|
|
122
125
|
function currentConfig(): RedirectConfig | undefined {
|
|
123
|
-
return projectState?.projectConfig?.redirects
|
|
126
|
+
return projectState?.projectConfig?.redirects;
|
|
124
127
|
}
|
|
125
128
|
|
|
126
129
|
/**
|
|
@@ -133,7 +136,10 @@ function parseTarget(raw: string): RedirectTarget {
|
|
|
133
136
|
return REWRITE;
|
|
134
137
|
}
|
|
135
138
|
const value = Number(trimmed);
|
|
136
|
-
|
|
139
|
+
/* SAFETY: deliberately unchecked, per the doc above. Every rule built from cells reaches
|
|
140
|
+
`ruleErrors` in `commit()`, which tests `status` against REDIRECT_TARGETS and refuses the whole
|
|
141
|
+
batch before `configFromRules` can write an unrecognised target to project.json. */
|
|
142
|
+
return (Number.isInteger(value) ? value : trimmed) as RedirectTarget;
|
|
137
143
|
}
|
|
138
144
|
|
|
139
145
|
/** A cell bag back to a rule. An unrecognised target is refused by {@link ruleErrors}. */
|
|
@@ -327,9 +333,12 @@ export function createRedirectsSource(): GridSource {
|
|
|
327
333
|
// `undefined` CLEARS the key (see `commitProjectConfig`): an empty table should leave no
|
|
328
334
|
// `"redirects": {}` behind. The local type spells the undefined out, which
|
|
329
335
|
// `exactOptionalPropertyTypes` will not let `Partial<ProjectConfig>` do.
|
|
330
|
-
const patch:
|
|
336
|
+
const patch: RedirectsPatch = {
|
|
331
337
|
redirects: next.length > 0 ? configFromRules(next) : undefined,
|
|
332
338
|
};
|
|
339
|
+
/* SAFETY: `commitProjectConfig` spreads the patch onto the document and documents an own
|
|
340
|
+
`undefined` as "clear this key"; the only other value is a RedirectConfig, which is
|
|
341
|
+
structurally ProjectConfig["redirects"]. Only the spelling of "absent" differs. */
|
|
333
342
|
const commit = await commitProjectConfig(patch as Partial<ProjectConfig>);
|
|
334
343
|
if (!commit.ok) {
|
|
335
344
|
// `commitProjectConfig` already filed the Problem; repeating it would list one failure
|
package/src/grid/redirects.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
*/
|
|
27
27
|
|
|
28
28
|
import { REDIRECT_STATUSES } from "@jxsuite/schema/defs";
|
|
29
|
+
import { isString } from "@jxsuite/schema/guards";
|
|
29
30
|
import { parseCsv } from "./csv-codec";
|
|
30
31
|
|
|
31
32
|
/** One redirect rule, flattened out of `project.json`'s two accepted spellings. */
|
|
@@ -128,17 +129,17 @@ export function routePattern(route: string): string {
|
|
|
128
129
|
/** Flatten `project.json`'s redirects map into rules, in declaration order. */
|
|
129
130
|
export function rulesFromConfig(config: RedirectConfig | undefined): RedirectRule[] {
|
|
130
131
|
return Object.entries(config ?? {}).map(([source, target]) => {
|
|
131
|
-
if (
|
|
132
|
+
if (isString(target)) {
|
|
132
133
|
return { destination: target, source, status: DEFAULT_REDIRECT_STATUS };
|
|
133
134
|
}
|
|
134
135
|
if ("rewrite" in target) {
|
|
135
136
|
return { destination: target.destination, source, status: REWRITE };
|
|
136
137
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
};
|
|
138
|
+
/* SAFETY: project.json's schema declares `status` as `enum: [...REDIRECT_STATUSES]`, the same
|
|
139
|
+
constant RedirectTarget is built from. A hand-edited file that breaks it is still caught:
|
|
140
|
+
every rule passes the grid's `ruleErrors` status check before anything is written back. */
|
|
141
|
+
const status = (target.status ?? DEFAULT_REDIRECT_STATUS) as RedirectTarget;
|
|
142
|
+
return { destination: target.destination, source, status };
|
|
142
143
|
});
|
|
143
144
|
}
|
|
144
145
|
|
|
@@ -151,7 +152,7 @@ export function rulesFromConfig(config: RedirectConfig | undefined): RedirectRul
|
|
|
151
152
|
* level down. A later duplicate source wins, which is what a JSON object means anyway; the grid
|
|
152
153
|
* refuses duplicates before they get here.
|
|
153
154
|
*/
|
|
154
|
-
export function configFromRules(rules: readonly RedirectRule[])
|
|
155
|
+
export function configFromRules(rules: readonly RedirectRule[]) {
|
|
155
156
|
const config: RedirectConfig = {};
|
|
156
157
|
for (const rule of rules) {
|
|
157
158
|
if (rule.status === REWRITE) {
|
|
@@ -189,11 +190,14 @@ function ruleFor(rules: readonly RedirectRule[], path: string): RedirectRule | u
|
|
|
189
190
|
);
|
|
190
191
|
}
|
|
191
192
|
|
|
193
|
+
/** The path a chain of rules walks, and whether it came back to a source it already visited. */
|
|
194
|
+
interface RedirectWalk {
|
|
195
|
+
path: string[];
|
|
196
|
+
looped: boolean;
|
|
197
|
+
}
|
|
198
|
+
|
|
192
199
|
/** Follow destinations from a rule until they stop resolving. Returns the path walked. */
|
|
193
|
-
function walk(
|
|
194
|
-
rules: readonly RedirectRule[],
|
|
195
|
-
start: RedirectRule,
|
|
196
|
-
): { path: string[]; looped: boolean } {
|
|
200
|
+
function walk(rules: readonly RedirectRule[], start: RedirectRule): RedirectWalk {
|
|
197
201
|
const path = [start.source];
|
|
198
202
|
const seen = new Set([normalizePath(start.source)]);
|
|
199
203
|
let current = start;
|
|
@@ -309,9 +313,7 @@ function parseStatus(token: string | undefined): RedirectTarget | null {
|
|
|
309
313
|
return REWRITE;
|
|
310
314
|
}
|
|
311
315
|
const value = Number(raw);
|
|
312
|
-
return (
|
|
313
|
-
? (value as RedirectTarget)
|
|
314
|
-
: null;
|
|
316
|
+
return REDIRECT_STATUSES.find((status) => status === value) ?? null;
|
|
315
317
|
}
|
|
316
318
|
|
|
317
319
|
/** Netlify/Cloudflare `_redirects`: `source destination [status]`, `#` comments, blank lines. */
|
|
@@ -8,6 +8,14 @@
|
|
|
8
8
|
* parser extension's `coerceCSVRows` (currency-stripped numbers, `"true"` booleans, comma-split
|
|
9
9
|
* arrays) so grid edits round-trip identically to the runtime content loader.
|
|
10
10
|
*/
|
|
11
|
+
import {
|
|
12
|
+
isBoolean,
|
|
13
|
+
isJsonObject,
|
|
14
|
+
isNonNullObject,
|
|
15
|
+
isNumber,
|
|
16
|
+
isString,
|
|
17
|
+
} from "@jxsuite/schema/guards";
|
|
18
|
+
import type { ContentTypeSchema, JsonValue } from "@jxsuite/schema/types";
|
|
11
19
|
import type { GridCellValue, GridColumn, GridColumnKind } from "./grid-source";
|
|
12
20
|
import type { JsonSchema } from "../ui/schema-form";
|
|
13
21
|
import { isMediaFormat } from "../utils/studio-utils";
|
|
@@ -93,17 +101,17 @@ export interface ColumnsFromSchemaOptions {
|
|
|
93
101
|
* required properties in declaration order, then the rest in declaration order.
|
|
94
102
|
*/
|
|
95
103
|
export function columnsFromSchema(
|
|
96
|
-
schema:
|
|
104
|
+
schema: ContentTypeSchema | null | undefined,
|
|
97
105
|
opts: ColumnsFromSchemaOptions = {},
|
|
98
106
|
): GridColumn[] {
|
|
99
|
-
const props = Object.entries(schema?.properties ?? {})
|
|
107
|
+
const props: [string, PropSchema][] = Object.entries(schema?.properties ?? {});
|
|
100
108
|
const required = new Set(schema?.required);
|
|
101
109
|
const readonly = new Set(opts.readonlyFields);
|
|
102
110
|
|
|
103
111
|
const columns = props.map(([field, prop]): GridColumn => {
|
|
104
112
|
const kind = readonly.has(field) ? "readonly" : kindForProp(prop);
|
|
105
113
|
const enumLen = Array.isArray(prop.enum)
|
|
106
|
-
? Math.max(...
|
|
114
|
+
? Math.max(...prop.enum.map((v) => String(v).length), 4)
|
|
107
115
|
: 0;
|
|
108
116
|
return {
|
|
109
117
|
editable: kind !== "readonly",
|
|
@@ -144,9 +152,10 @@ export function inferColumnsFromRows(
|
|
|
144
152
|
}
|
|
145
153
|
|
|
146
154
|
return fields.map((field): GridColumn => {
|
|
147
|
-
const values = sample
|
|
148
|
-
|
|
149
|
-
|
|
155
|
+
const values = sample.flatMap((row) => {
|
|
156
|
+
const value = row[field];
|
|
157
|
+
return value === null || value === undefined || value === "" ? [] : [value];
|
|
158
|
+
});
|
|
150
159
|
const kind = inferKind(values);
|
|
151
160
|
const maxLen = Math.max(...values.map((v) => cellToText(v).length), field.length, 4);
|
|
152
161
|
return {
|
|
@@ -167,14 +176,11 @@ function inferKind(values: Exclude<GridCellValue, null>[]): GridColumnKind {
|
|
|
167
176
|
return "array";
|
|
168
177
|
}
|
|
169
178
|
const texts = values.map(String);
|
|
170
|
-
if (
|
|
171
|
-
values.every((v) => typeof v === "boolean") ||
|
|
172
|
-
texts.every((t) => /^(true|false)$/i.test(t))
|
|
173
|
-
) {
|
|
179
|
+
if (values.every((v) => isBoolean(v)) || texts.every((t) => /^(true|false)$/i.test(t))) {
|
|
174
180
|
return "boolean";
|
|
175
181
|
}
|
|
176
182
|
if (
|
|
177
|
-
values.every((v) =>
|
|
183
|
+
values.every((v) => isNumber(v)) ||
|
|
178
184
|
texts.every((t) => t.trim() !== "" && !Number.isNaN(Number(t.replaceAll(/[$€£¥,\s]/g, ""))))
|
|
179
185
|
) {
|
|
180
186
|
return "number";
|
|
@@ -194,13 +200,13 @@ function inferKind(values: Exclude<GridCellValue, null>[]): GridColumnKind {
|
|
|
194
200
|
* Coerce editor/clipboard input to the column's typed value. Empty input clears to null. Number and
|
|
195
201
|
* array semantics mirror the parser extension's coerceCSVRows.
|
|
196
202
|
*/
|
|
197
|
-
export function coerceCellInput(raw:
|
|
203
|
+
export function coerceCellInput(raw: JsonValue | undefined, col: GridColumn): GridCellValue {
|
|
198
204
|
if (raw === null || raw === undefined) {
|
|
199
205
|
return null;
|
|
200
206
|
}
|
|
201
207
|
switch (col.kind) {
|
|
202
208
|
case "number": {
|
|
203
|
-
if (
|
|
209
|
+
if (isNumber(raw)) {
|
|
204
210
|
return Number.isNaN(raw) ? null : raw;
|
|
205
211
|
}
|
|
206
212
|
const cleaned = String(raw)
|
|
@@ -213,7 +219,7 @@ export function coerceCellInput(raw: unknown, col: GridColumn): GridCellValue {
|
|
|
213
219
|
return Number.isNaN(n) ? null : n;
|
|
214
220
|
}
|
|
215
221
|
case "boolean": {
|
|
216
|
-
if (
|
|
222
|
+
if (isBoolean(raw)) {
|
|
217
223
|
return raw;
|
|
218
224
|
}
|
|
219
225
|
return String(raw).trim().toLowerCase() === "true";
|
|
@@ -233,9 +239,9 @@ export function coerceCellInput(raw: unknown, col: GridColumn): GridCellValue {
|
|
|
233
239
|
case "reference": {
|
|
234
240
|
// Relationship VALUES are plain target-entry ids ("jane-doe") — the $ref lives on the
|
|
235
241
|
// Schema property, not in the data (specs/relationships.md; parser resolveContentTypeRefs).
|
|
236
|
-
if (
|
|
237
|
-
const ref =
|
|
238
|
-
return
|
|
242
|
+
if (isJsonObject(raw) && "$ref" in raw) {
|
|
243
|
+
const ref = raw.$ref;
|
|
244
|
+
return isString(ref) && ref !== "" ? ref : null;
|
|
239
245
|
}
|
|
240
246
|
const text = String(raw).trim();
|
|
241
247
|
return text === "" ? null : text;
|
|
@@ -248,14 +254,14 @@ export function coerceCellInput(raw: unknown, col: GridColumn): GridCellValue {
|
|
|
248
254
|
}
|
|
249
255
|
|
|
250
256
|
/** Plain-text projection of a cell value — display, clipboard, and CSV share it. */
|
|
251
|
-
export function cellToText(value: GridCellValue): string {
|
|
257
|
+
export function cellToText(value: GridCellValue | undefined): string {
|
|
252
258
|
if (value === null || value === undefined) {
|
|
253
259
|
return "";
|
|
254
260
|
}
|
|
255
261
|
if (Array.isArray(value)) {
|
|
256
262
|
return value.join(", ");
|
|
257
263
|
}
|
|
258
|
-
if (
|
|
264
|
+
if (isNonNullObject(value)) {
|
|
259
265
|
return value.$ref;
|
|
260
266
|
}
|
|
261
267
|
return String(value);
|