@jxsuite/studio 2.0.1 → 2.1.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/canvas.html +21 -0
- package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
- package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
- package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
- package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
- package/dist/chunks/{components-799jpvhm.js → components-2bpqp4mq.js} +5 -7
- package/dist/chunks/{components-799jpvhm.js.map → components-2bpqp4mq.js.map} +1 -1
- package/dist/chunks/content-source-34gnkbnn.js +38 -0
- package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
- package/dist/chunks/extension-sections-6mqydz35.js +301 -0
- package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
- package/dist/chunks/files-8kn321h1.js +70 -0
- package/dist/chunks/files-8kn321h1.js.map +9 -0
- package/dist/chunks/{format-host-8jxwts2p.js → format-host-mkwsmt8d.js} +3 -3
- package/dist/chunks/git-panel-tjbr191d.js +51 -0
- package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
- package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
- package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
- package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
- package/dist/chunks/layers-panel-87pb25km.js +58 -0
- package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
- package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
- package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
- package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
- package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
- package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
- package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
- package/dist/chunks/right-panel-zpdxd98t.js +37 -0
- package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
- package/dist/chunks/section-registry-zmckaxf4.js +30 -0
- package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
- package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
- package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
- package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
- package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
- package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
- package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
- package/dist/chunks/studio-41exgzhq.js +6690 -0
- package/dist/chunks/studio-41exgzhq.js.map +38 -0
- package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
- package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
- package/dist/chunks/studio-50k77cca.js +1787 -0
- package/dist/chunks/studio-50k77cca.js.map +18 -0
- package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
- package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
- package/dist/chunks/studio-6f0am8f2.js +53 -0
- package/dist/chunks/studio-6f0am8f2.js.map +10 -0
- package/dist/chunks/studio-6nmjc5qk.js +7002 -0
- package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
- package/dist/chunks/studio-7afd21nw.js +67 -0
- package/dist/chunks/studio-7afd21nw.js.map +10 -0
- package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
- package/dist/chunks/studio-7bmdvzne.js.map +10 -0
- package/dist/chunks/studio-8jpzqk5s.js +128 -0
- package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
- package/dist/chunks/studio-94m77tgk.js +1482 -0
- package/dist/chunks/studio-94m77tgk.js.map +13 -0
- package/dist/chunks/studio-9je6w5sq.js +1000 -0
- package/dist/chunks/studio-9je6w5sq.js.map +16 -0
- package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
- package/dist/chunks/{studio-gs6kqdt1.js → studio-adn0z1k3.js} +14 -8
- package/dist/chunks/{studio-gs6kqdt1.js.map → studio-adn0z1k3.js.map} +3 -3
- package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
- package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
- package/dist/chunks/studio-bw5dz6km.js +608 -0
- package/dist/chunks/studio-bw5dz6km.js.map +21 -0
- package/dist/chunks/studio-c6s5getc.js +4232 -0
- package/dist/chunks/studio-c6s5getc.js.map +39 -0
- package/dist/chunks/studio-cjm7fdjf.js +451 -0
- package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
- package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
- package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
- package/dist/chunks/studio-dg7zyt5x.js +28 -0
- package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
- package/dist/chunks/studio-dzcyvvvz.js +166 -0
- package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
- package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
- package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
- package/dist/chunks/studio-f12h6177.js +411 -0
- package/dist/chunks/studio-f12h6177.js.map +13 -0
- package/dist/chunks/studio-hbp1s8m9.js +1817 -0
- package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
- package/dist/chunks/studio-k2sxjph5.js +15295 -0
- package/dist/chunks/studio-k2sxjph5.js.map +235 -0
- package/dist/chunks/studio-myje51t3.js +385 -0
- package/dist/chunks/studio-myje51t3.js.map +12 -0
- package/dist/chunks/studio-na6rwzhw.js +353 -0
- package/dist/chunks/studio-na6rwzhw.js.map +11 -0
- package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
- package/dist/chunks/studio-p471gg5n.js.map +14 -0
- package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
- package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
- package/dist/chunks/{studio-esrewxap.js → studio-s519zc99.js} +3 -3
- package/dist/chunks/studio-sas7mt3k.js +75 -0
- package/dist/chunks/studio-sas7mt3k.js.map +10 -0
- package/dist/chunks/studio-t5wqwzdh.js +74 -0
- package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
- package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
- package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
- package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
- package/dist/chunks/studio-tr20m8g8.js.map +9 -0
- package/dist/chunks/{studio-v7jkjqng.js → studio-v96qfnww.js} +5514 -4291
- package/dist/chunks/studio-v96qfnww.js.map +54 -0
- package/dist/chunks/studio-vcetc91k.js +659 -0
- package/dist/chunks/studio-vcetc91k.js.map +10 -0
- package/dist/chunks/{studio-jm0z592w.js → studio-vmymjefp.js} +2972 -4937
- package/dist/chunks/studio-vmymjefp.js.map +98 -0
- package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
- package/dist/chunks/studio-x9hmy5kr.js +390 -0
- package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
- package/dist/chunks/studio-xa0ce0n7.js +335 -0
- package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
- package/dist/chunks/studio-xm3dbxvn.js +378 -0
- package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
- package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
- package/dist/chunks/studio-zv37yrfa.js +1054 -0
- package/dist/chunks/studio-zv37yrfa.js.map +13 -0
- package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
- package/dist/chunks/ws-client-654ts66j.js.map +11 -0
- package/dist/iframe-entry.js +668 -230
- package/dist/studio.js +21080 -32585
- package/index.html +35 -4685
- package/package.json +17 -15
- package/src/about/about-modal.ts +42 -11
- package/src/browse/library-commands.ts +240 -0
- package/src/browse/library-layouts.ts +369 -0
- package/src/browse/library-model.ts +396 -0
- package/src/browse/library-pane.ts +1049 -0
- package/src/browse/library-preview.ts +252 -0
- package/src/browse/library-source.ts +150 -0
- package/src/canvas/canvas-helpers.ts +33 -9
- package/src/canvas/canvas-live-render.ts +45 -29
- package/src/canvas/canvas-patcher.ts +151 -31
- package/src/canvas/canvas-perf.ts +46 -7
- package/src/canvas/canvas-render.ts +1092 -220
- package/src/canvas/canvas-surface.ts +240 -0
- package/src/canvas/canvas-utils.ts +1166 -124
- package/src/canvas/iframe-drop.ts +2 -1
- package/src/canvas/iframe-entry.ts +241 -4
- package/src/canvas/iframe-host.ts +1226 -143
- package/src/canvas/iframe-inline-edit.ts +31 -0
- package/src/canvas/iframe-interaction.ts +76 -16
- package/src/canvas/iframe-keys.ts +107 -45
- package/src/canvas/iframe-overlay.ts +38 -1
- package/src/canvas/iframe-protocol.ts +137 -2
- package/src/canvas/iframe-render.ts +70 -2
- package/src/canvas/iframe-slash.ts +6 -0
- package/src/canvas/path-mapping.ts +56 -6
- package/src/canvas/surface-registry.ts +292 -0
- package/src/collab/collab-commands.ts +244 -0
- package/src/collab/collab-session.ts +198 -32
- package/src/collab/collab-state.ts +28 -2
- package/src/collab/presence-chips.ts +100 -9
- package/src/commands/active-registry.ts +42 -0
- package/src/commands/app-commands.ts +176 -0
- package/src/commands/budget.ts +158 -0
- package/src/commands/command-args.ts +253 -0
- package/src/commands/context.ts +387 -0
- package/src/commands/defaults.ts +580 -0
- package/src/commands/keymap.ts +571 -0
- package/src/commands/levels.ts +345 -0
- package/src/commands/live-context.ts +193 -0
- package/src/commands/reference.ts +227 -0
- package/src/commands/registry.ts +293 -0
- package/src/component-props.ts +3 -2
- package/src/content/draft-state.ts +106 -0
- package/src/content/entry-commands.ts +282 -0
- package/src/content/entry-editor.ts +327 -0
- package/src/content/entry-fields.ts +116 -0
- package/src/content/entry-model.ts +175 -0
- package/src/editor/canvas-context-menu.ts +17 -11
- package/src/editor/canvas-slash-bridge.ts +11 -4
- package/src/editor/context-menu.ts +804 -204
- package/src/editor/convert-to-component.ts +31 -10
- package/src/editor/convert-to-repeater.ts +3 -2
- package/src/editor/inline-edit-apply.ts +5 -5
- package/src/editor/inline-edit.ts +86 -50
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/shortcuts.ts +1043 -329
- package/src/editor/slash-menu.ts +1 -0
- package/src/files/elements.ts +166 -0
- package/src/files/file-ops.ts +178 -31
- package/src/files/files.ts +898 -268
- package/src/files/fs-events.ts +21 -0
- package/src/files/media-meta.ts +203 -0
- package/src/files/media-paths.ts +141 -0
- package/src/files/media-upload.ts +7 -3
- package/src/files/media-usage.ts +189 -0
- package/src/github/github-auth.ts +235 -33
- package/src/github/github-publish.ts +80 -21
- package/src/grid/grid-controller.ts +152 -12
- package/src/grid/grid-layout.ts +225 -20
- package/src/grid/grid-open.ts +120 -0
- package/src/grid/grid-panel.ts +554 -39
- package/src/grid/grid-source.ts +27 -1
- package/src/grid/redirects-grid.ts +599 -0
- package/src/grid/redirects.ts +398 -0
- package/src/grid/sources/content-source.ts +108 -2
- package/src/i18n/i18n-commands.ts +322 -0
- package/src/new-project/add-repo-modal.ts +8 -10
- package/src/new-project/import-tab.ts +7 -0
- package/src/new-project/location-fields.ts +9 -1
- package/src/new-project/new-project-modal.ts +221 -245
- package/src/packages/jxsuite-update.ts +97 -48
- package/src/packages/pull-package-sync.ts +18 -9
- package/src/page-params.ts +2 -1
- package/src/panels/activity-bar.ts +152 -91
- package/src/panels/activity-panel.ts +481 -0
- package/src/panels/ai-chat/chat-markdown.ts +8 -3
- package/src/panels/ai-chat/chat-view.ts +245 -18
- package/src/panels/ai-chat/composer.ts +31 -5
- package/src/panels/ai-chat/sessions-view.ts +17 -10
- package/src/panels/ai-panel.ts +279 -53
- package/src/panels/block-action-bar.ts +1115 -224
- package/src/panels/bottom-dock.ts +337 -0
- package/src/panels/canvas-dnd-bridge.ts +37 -8
- package/src/panels/chat-panel.ts +29 -12
- package/src/panels/data-explorer.ts +254 -112
- package/src/panels/data-grid.ts +10 -4
- package/src/panels/dnd.ts +28 -51
- package/src/panels/editors.ts +434 -107
- package/src/panels/elements-panel.ts +66 -2
- package/src/panels/empty-state.ts +112 -0
- package/src/panels/events-panel.ts +596 -82
- package/src/panels/formula-workspace.ts +408 -218
- package/src/panels/frontmatter-fields.ts +51 -10
- package/src/panels/frontmatter-panel.ts +357 -79
- package/src/panels/git-panel.ts +375 -164
- package/src/panels/head-panel.ts +791 -85
- package/src/panels/i18n-panel.ts +592 -0
- package/src/panels/imports-panel.ts +95 -85
- package/src/panels/jump-bar.ts +584 -0
- package/src/panels/layers-panel.ts +1106 -328
- package/src/panels/left-panel.ts +109 -204
- package/src/panels/navigator-panels.ts +131 -0
- package/src/panels/overlays.ts +7 -2
- package/src/panels/pane-context.ts +1477 -0
- package/src/panels/pane-grid.ts +406 -0
- package/src/panels/panel-registry.ts +340 -0
- package/src/panels/panel-scheduler.ts +75 -0
- package/src/panels/problems-panel.ts +234 -0
- package/src/panels/properties-panel.ts +946 -1139
- package/src/panels/provenance.ts +258 -0
- package/src/panels/quick-search.ts +791 -122
- package/src/panels/right-panel.ts +228 -102
- package/src/panels/seo-modal.ts +395 -0
- package/src/panels/settings-pane.ts +203 -0
- package/src/panels/signals-panel.ts +354 -137
- package/src/panels/statement-editor.ts +41 -25
- package/src/panels/statusbar.ts +342 -106
- package/src/panels/style-panel.ts +974 -463
- package/src/panels/style-utils.ts +3 -3
- package/src/panels/stylebook-doc.ts +4 -2
- package/src/panels/stylebook-layers-panel.ts +3 -2
- package/src/panels/stylebook-panel.ts +51 -26
- package/src/panels/tab-strip.ts +1058 -78
- package/src/panels/target-line.ts +274 -0
- package/src/panels/toolbar.ts +631 -484
- package/src/panels/welcome-screen.ts +301 -198
- package/src/platform-errors.ts +26 -3
- package/src/platform.ts +96 -2
- package/src/platforms/cloud.ts +71 -8
- package/src/platforms/devserver.ts +92 -29
- package/src/publish/deploy-checklist.ts +383 -0
- package/src/publish/pages-service.ts +10 -1
- package/src/publish/publish-commands.ts +218 -0
- package/src/publish/publish-panel.ts +110 -16
- package/src/recent-projects.ts +3 -2
- package/src/services/a11y-report.ts +505 -0
- package/src/services/ai-models.ts +30 -1
- package/src/services/ai-project-tools.ts +29 -10
- package/src/services/ai-system-prompt.ts +53 -14
- package/src/services/ai-tools.ts +39 -3
- package/src/services/ai-writes.ts +154 -0
- package/src/services/announce.ts +97 -0
- package/src/services/automation.ts +424 -175
- package/src/services/cem-export.ts +9 -4
- package/src/services/clock.ts +61 -0
- package/src/services/document-assistant.ts +36 -8
- package/src/services/idle.ts +179 -0
- package/src/services/import-client.ts +26 -0
- package/src/services/monaco-buffer.ts +627 -0
- package/src/services/monaco-lazy.ts +36 -0
- package/src/services/notify.ts +271 -0
- package/src/services/profile.ts +155 -0
- package/src/services/references.ts +0 -0
- package/src/services/token-lint.ts +3 -2
- package/src/services/tool-executor.ts +53 -7
- package/src/services/trusted-types.ts +123 -0
- package/src/settings/contexts-section.ts +427 -0
- package/src/settings/contributed-section.ts +149 -11
- package/src/settings/css-vars-editor.ts +306 -349
- package/src/settings/defs-editor.ts +93 -34
- package/src/settings/dependencies-editor.ts +7 -7
- package/src/settings/extension-sections.ts +37 -5
- package/src/settings/general-settings.ts +204 -121
- package/src/settings/locales-section.ts +347 -0
- package/src/settings/preferences-accounts.ts +140 -0
- package/src/settings/preferences-dialog.ts +620 -0
- package/src/settings/preferences-keymap.ts +270 -0
- package/src/settings/project-sections.ts +260 -0
- package/src/settings/section-registry.ts +169 -0
- package/src/settings/settings-document.ts +385 -0
- package/src/shell.ts +1549 -0
- package/src/site-context.ts +45 -20
- package/src/state.ts +14 -275
- package/src/store.ts +88 -43
- package/src/studio.ts +790 -346
- package/src/style/live-preview.ts +36 -0
- package/src/style/project-styles.ts +349 -0
- package/src/style/token-ref.ts +123 -0
- package/src/tabs/patch-ops.ts +16 -4
- package/src/tabs/project-config.ts +416 -0
- package/src/tabs/selection.ts +323 -0
- package/src/tabs/tab.ts +182 -47
- package/src/tabs/transact.ts +209 -28
- package/src/types.ts +68 -13
- package/src/ui/ai-credentials-form.ts +39 -35
- package/src/ui/ai-managed-connect.ts +7 -1
- package/src/ui/dynamic-slot.ts +224 -81
- package/src/ui/expression-editor.ts +71 -63
- package/src/ui/field-input.ts +4 -6
- package/src/ui/field-row.ts +215 -38
- package/src/ui/form-controls.ts +194 -7
- package/src/ui/layers.ts +492 -69
- package/src/ui/media-picker.ts +115 -25
- package/src/ui/panel-resize.ts +109 -91
- package/src/ui/progress-modal.ts +131 -50
- package/src/ui/regions.ts +462 -0
- package/src/ui/schema-form.ts +229 -45
- package/src/ui/spectrum.ts +24 -8
- package/src/ui/timing.ts +49 -0
- package/src/ui/value-selector.ts +20 -0
- package/src/ui/value-source.ts +348 -0
- package/src/ui/virtual-window.ts +400 -0
- package/src/utils/edit-display.ts +3 -2
- package/src/utils/inherited-style.ts +67 -37
- package/src/view.ts +64 -78
- package/src/workspace/pane-derive.ts +1258 -0
- package/src/workspace/session.ts +317 -0
- package/src/workspace/workspace.ts +1345 -23
- package/styles/canvas.css +653 -0
- package/styles/forced-colors.css +106 -0
- package/styles/inspector.css +1454 -0
- package/styles/overlays.css +2704 -0
- package/styles/panels.css +1701 -0
- package/styles/shell.css +1423 -0
- package/styles/tokens.css +151 -0
- package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
- package/dist/chunks/extension-sections-qr3drkkw.js +0 -531
- package/dist/chunks/extension-sections-qr3drkkw.js.map +0 -12
- package/dist/chunks/files-j8a1t06k.js +0 -50
- package/dist/chunks/files-j8a1t06k.js.map +0 -9
- package/dist/chunks/git-panel-xtq5z81x.js +0 -28
- package/dist/chunks/git-panel-xtq5z81x.js.map +0 -9
- package/dist/chunks/layers-panel-ancn2wpq.js +0 -26
- package/dist/chunks/layers-panel-ancn2wpq.js.map +0 -9
- package/dist/chunks/settings-modal-17ttr96g.js +0 -29
- package/dist/chunks/settings-modal-17ttr96g.js.map +0 -9
- package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
- package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
- package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
- package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
- package/dist/chunks/studio-0xgs7xrs.js +0 -429
- package/dist/chunks/studio-0xgs7xrs.js.map +0 -13
- package/dist/chunks/studio-4d8hzcz5.js +0 -3427
- package/dist/chunks/studio-4d8hzcz5.js.map +0 -26
- package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
- package/dist/chunks/studio-9smfzqhc.js +0 -19
- package/dist/chunks/studio-9smfzqhc.js.map +0 -10
- package/dist/chunks/studio-jm0z592w.js.map +0 -115
- package/dist/chunks/studio-m2t4mdg5.js +0 -1854
- package/dist/chunks/studio-m2t4mdg5.js.map +0 -18
- package/dist/chunks/studio-n4p11dfy.js +0 -853
- package/dist/chunks/studio-n4p11dfy.js.map +0 -12
- package/dist/chunks/studio-qkvt37c0.js.map +0 -9
- package/dist/chunks/studio-sjh1wt1a.js +0 -3744
- package/dist/chunks/studio-sjh1wt1a.js.map +0 -22
- package/dist/chunks/studio-v7jkjqng.js.map +0 -46
- package/dist/chunks/studio-xq0t7n5t.js +0 -140
- package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
- package/dist/chunks/studio-xze4zrs1.js.map +0 -10
- package/dist/chunks/studio-yfcstdzy.js +0 -399
- package/dist/chunks/studio-yfcstdzy.js.map +0 -12
- package/dist/chunks/studio-zp7jncs8.js +0 -469
- package/dist/chunks/studio-zp7jncs8.js.map +0 -14
- package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
- package/src/browse/browse-modal.ts +0 -69
- package/src/browse/browse.ts +0 -904
- package/src/canvas/canvas-diff.ts +0 -216
- package/src/canvas/nested-site-style.ts +0 -58
- package/src/new-project/design-fields.ts +0 -264
- package/src/new-project/templates.ts +0 -61
- package/src/panels/tab-bar.ts +0 -548
- package/src/settings/settings-modal.ts +0 -203
- /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
- /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
- /package/dist/chunks/{format-host-8jxwts2p.js.map → format-host-mkwsmt8d.js.map} +0 -0
- /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
- /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
- /package/dist/chunks/{studio-esrewxap.js.map → studio-s519zc99.js.map} +0 -0
- /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
- /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
package/src/grid/grid-panel.ts
CHANGED
|
@@ -7,48 +7,103 @@
|
|
|
7
7
|
* loading/error surfaces stay live without renderCanvas involvement. The Tabulator view is created
|
|
8
8
|
* once per tab entry, after the controller has loaded columns, and destroyed on detach — all grid
|
|
9
9
|
* data survives in the controller/buffer, so a rebuild on re-entry is cheap.
|
|
10
|
+
*
|
|
11
|
+
* **The panel owns the view state** (plan §12 P7.2). Saved views persist through `grid-layout.ts` —
|
|
12
|
+
* one store, the grid id as its key, so "per collection" needs no code — and this module is what
|
|
13
|
+
* drives them onto the four surfaces they touch: the engine (column order, width and visibility,
|
|
14
|
+
* applied by a REBUILD, which is cheap because the data never leaves the controller), the
|
|
15
|
+
* controller (sort and grouping, which are row order and belong to the data), and the toolbar's own
|
|
16
|
+
* filter box. Applying a view is therefore one function with one order of operations, not five
|
|
17
|
+
* controls that each half-remember what the others did.
|
|
10
18
|
*/
|
|
11
19
|
import { html, render as litRender, nothing } from "lit-html";
|
|
12
20
|
import { ref } from "lit-html/directives/ref.js";
|
|
13
|
-
import { effect, effectScope } from "../reactivity";
|
|
14
|
-
import { renderPopover } from "../ui/layers";
|
|
15
|
-
import {
|
|
21
|
+
import { effect, effectScope, reactive } from "../reactivity";
|
|
22
|
+
import { renderPopover, showConfirmDialog, showPromptDialog } from "../ui/layers";
|
|
23
|
+
import { notify } from "../services/notify";
|
|
16
24
|
import { rectOf } from "../utils/geometry";
|
|
25
|
+
import { activeTab, workspace } from "../workspace/workspace";
|
|
17
26
|
import { createGridController, getGridController } from "./grid-controller";
|
|
18
27
|
import { createCsvFileSource } from "./sources/csv-file-source";
|
|
19
28
|
import { createGridView } from "./grid-view";
|
|
20
29
|
import { parseGridTabId } from "./grid-source";
|
|
30
|
+
import {
|
|
31
|
+
activeViewModified,
|
|
32
|
+
activeViewName,
|
|
33
|
+
applySavedView,
|
|
34
|
+
deleteSavedView,
|
|
35
|
+
listSavedViews,
|
|
36
|
+
loadGridLayout,
|
|
37
|
+
resetGridLayout,
|
|
38
|
+
saveGridLayout,
|
|
39
|
+
saveViewAs,
|
|
40
|
+
} from "./grid-layout";
|
|
41
|
+
import { argsSchema, stringArg, stringProperty } from "../commands/command-args";
|
|
21
42
|
import type { EffectScope } from "@vue/reactivity";
|
|
22
43
|
import type { GridController } from "./grid-controller";
|
|
44
|
+
import type { GridLayout, GridSortSpec } from "./grid-layout";
|
|
23
45
|
import type { GridView } from "./grid-view";
|
|
24
46
|
import type { Tab } from "../tabs/tab";
|
|
47
|
+
import type { CanvasSurface } from "../canvas/canvas-surface";
|
|
48
|
+
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
25
49
|
|
|
26
50
|
interface ActiveGridPanel {
|
|
51
|
+
/** The pane whose stage this grid is drawn on. */
|
|
52
|
+
paneId: string;
|
|
27
53
|
tabId: string;
|
|
28
54
|
scope: EffectScope;
|
|
29
55
|
view: GridView | null;
|
|
30
56
|
wrap: HTMLElement;
|
|
57
|
+
/**
|
|
58
|
+
* Rebuild the engine over the same controller.
|
|
59
|
+
*
|
|
60
|
+
* Column order, width and visibility are read by `createGridView` from the saved layout, so this
|
|
61
|
+
* is how a view change reaches them. Nothing is re-fetched: rows, edits, undo history and the
|
|
62
|
+
* dirty flag all live in the controller, and the engine is a rendering of them.
|
|
63
|
+
*/
|
|
64
|
+
remount: () => void;
|
|
65
|
+
/** Apply a whole layout — sort, grouping, columns, filter — in one order of operations. */
|
|
66
|
+
applyLayout: (layout: GridLayout | null) => void;
|
|
67
|
+
/** Re-render the toolbar. `localStorage` is not reactive, so a view edit has to say so. */
|
|
68
|
+
bump: () => void;
|
|
31
69
|
}
|
|
32
70
|
|
|
33
|
-
|
|
71
|
+
/**
|
|
72
|
+
* The grid mounted in each pane, keyed by pane id.
|
|
73
|
+
*
|
|
74
|
+
* A module-level `let active` described a shell with one stage. With two, pane B mounting a grid
|
|
75
|
+
* destroyed pane A's Tabulator view and effect scope out from under it — and `resetCanvasView`
|
|
76
|
+
* calls `detachGridPanel` on every pane that empties, so a second grid was never required.
|
|
77
|
+
*/
|
|
78
|
+
const _active = new Map<string, ActiveGridPanel>();
|
|
34
79
|
|
|
35
|
-
/**
|
|
36
|
-
|
|
37
|
-
|
|
80
|
+
/** The grid mounted in a pane, or null. */
|
|
81
|
+
function activeIn(paneId: string): ActiveGridPanel | null {
|
|
82
|
+
return _active.get(paneId) ?? null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Destroy one pane's grid view/effects (canvas-render teardown + tab switches). */
|
|
86
|
+
export function detachGridPanel(paneId: string) {
|
|
87
|
+
const panel = _active.get(paneId);
|
|
88
|
+
if (!panel) {
|
|
38
89
|
return;
|
|
39
90
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
91
|
+
panel.view?.destroy();
|
|
92
|
+
panel.scope.stop();
|
|
93
|
+
_active.delete(paneId);
|
|
43
94
|
}
|
|
44
95
|
|
|
45
|
-
/** Whether the grid panel is live for this tab (canvas-render fast-path guard). */
|
|
46
|
-
export function gridPanelMounted(tab: Tab): boolean {
|
|
47
|
-
|
|
96
|
+
/** Whether the grid panel is live in this pane for this tab (canvas-render fast-path guard). */
|
|
97
|
+
export function gridPanelMounted(paneId: string, tab: Tab): boolean {
|
|
98
|
+
const panel = activeIn(paneId);
|
|
99
|
+
return panel !== null && panel.tabId === tab.id && panel.wrap.isConnected;
|
|
48
100
|
}
|
|
49
101
|
|
|
50
|
-
function toolbarTpl(controller: GridController,
|
|
102
|
+
function toolbarTpl(controller: GridController, panel: ActiveGridPanel) {
|
|
103
|
+
const getView = () => panel.view;
|
|
51
104
|
const { state } = controller;
|
|
105
|
+
const gridId = controller.source.id;
|
|
106
|
+
const layout = loadGridLayout(gridId);
|
|
52
107
|
const dirty = controller.buffer.dirtyCount();
|
|
53
108
|
const sourceRef = parseGridTabId(controller.source.id);
|
|
54
109
|
const lossyNote =
|
|
@@ -67,7 +122,9 @@ function toolbarTpl(controller: GridController, getView: () => GridView | null)
|
|
|
67
122
|
size="s"
|
|
68
123
|
variant="accent"
|
|
69
124
|
?disabled=${dirty === 0 || state.saving}
|
|
70
|
-
@click=${() =>
|
|
125
|
+
@click=${() => {
|
|
126
|
+
void controller.save();
|
|
127
|
+
}}
|
|
71
128
|
>
|
|
72
129
|
Save${dirty > 0 ? ` (${dirty})` : ""}
|
|
73
130
|
</sp-button>
|
|
@@ -76,7 +133,9 @@ function toolbarTpl(controller: GridController, getView: () => GridView | null)
|
|
|
76
133
|
quiet
|
|
77
134
|
title="Reload from source"
|
|
78
135
|
?disabled=${state.loading || state.saving}
|
|
79
|
-
@click=${() =>
|
|
136
|
+
@click=${() => {
|
|
137
|
+
void controller.refresh();
|
|
138
|
+
}}
|
|
80
139
|
>
|
|
81
140
|
Refresh
|
|
82
141
|
</sp-action-button>
|
|
@@ -121,15 +180,32 @@ function toolbarTpl(controller: GridController, getView: () => GridView | null)
|
|
|
121
180
|
>
|
|
122
181
|
Replace
|
|
123
182
|
</sp-action-button>
|
|
183
|
+
<sp-action-button
|
|
184
|
+
size="s"
|
|
185
|
+
quiet
|
|
186
|
+
class="jx-grid-view-button"
|
|
187
|
+
title="Saved views, columns, sort and grouping"
|
|
188
|
+
@click=${(e: MouseEvent) => openViewPopover(controller, panel, e.currentTarget as HTMLElement)}
|
|
189
|
+
>
|
|
190
|
+
${viewButtonLabel(gridId)}
|
|
191
|
+
</sp-action-button>
|
|
124
192
|
<sp-search
|
|
125
193
|
size="s"
|
|
126
194
|
quiet
|
|
127
195
|
placeholder="Filter rows"
|
|
128
|
-
|
|
196
|
+
.value=${layout?.filter ?? ""}
|
|
197
|
+
@input=${(e: Event) => {
|
|
198
|
+
const term = (e.target as HTMLInputElement).value;
|
|
199
|
+
// The filter is part of the view, so it persists with the rest of it rather than
|
|
200
|
+
// Evaporating on a tab switch and taking a saved view's meaning with it.
|
|
201
|
+
saveGridLayout(gridId, { filter: term });
|
|
202
|
+
getView()?.setSearch(term);
|
|
203
|
+
}}
|
|
129
204
|
@submit=${(e: Event) => e.preventDefault()}
|
|
130
205
|
></sp-search>
|
|
131
206
|
<span class="jx-grid-spacer"></span>
|
|
132
|
-
${
|
|
207
|
+
${groupNoteTpl(controller)} ${lossyNote}
|
|
208
|
+
${controller.source.capabilities.remotePaging ? pagerTpl(controller) : nothing}
|
|
133
209
|
<span class="jx-grid-count">
|
|
134
210
|
${
|
|
135
211
|
state.loading
|
|
@@ -143,6 +219,262 @@ function toolbarTpl(controller: GridController, getView: () => GridView | null)
|
|
|
143
219
|
`;
|
|
144
220
|
}
|
|
145
221
|
|
|
222
|
+
// ─── Saved views ──────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* The View button's label: the applied view's name, with a dot when the layout has since drifted.
|
|
226
|
+
*
|
|
227
|
+
* "View" alone when nothing is applied — the grid still remembers its columns, and claiming a name
|
|
228
|
+
* for a layout the author never named would make Save-as look like a no-op.
|
|
229
|
+
*/
|
|
230
|
+
function viewButtonLabel(gridId: string): string {
|
|
231
|
+
const name = activeViewName(gridId);
|
|
232
|
+
if (!name) {
|
|
233
|
+
return "View";
|
|
234
|
+
}
|
|
235
|
+
return activeViewModified(gridId) ? `${name} •` : name;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/** "Grouped by Status · 3 groups" — the grouping is invisible in the rows, so the toolbar says it. */
|
|
239
|
+
function groupNoteTpl(controller: GridController) {
|
|
240
|
+
const field = controller.state.grouping;
|
|
241
|
+
if (!field) {
|
|
242
|
+
return nothing;
|
|
243
|
+
}
|
|
244
|
+
const title = controller.state.columns.find((column) => column.field === field)?.title ?? field;
|
|
245
|
+
const count = controller.groups().length;
|
|
246
|
+
return html`<span class="jx-grid-note" title="Rows are ordered so each group is contiguous.">
|
|
247
|
+
Grouped by ${title} · ${count} group${count === 1 ? "" : "s"}
|
|
248
|
+
</span>`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/**
|
|
252
|
+
* The View popover: saved views, then the four facets one of them is made of.
|
|
253
|
+
*
|
|
254
|
+
* One control rather than four, because the chrome budget is a cap on named things in the toolbar
|
|
255
|
+
* (plan §2, principle 9) and these four are only ever adjusted together. Each edit writes the
|
|
256
|
+
* working layout and applies immediately — there is no Apply button, so there is no state in which
|
|
257
|
+
* the popover shows something the grid is not already doing.
|
|
258
|
+
*/
|
|
259
|
+
function openViewPopover(controller: GridController, panel: ActiveGridPanel, anchor: HTMLElement) {
|
|
260
|
+
const rect = rectOf(anchor);
|
|
261
|
+
const gridId = controller.source.id;
|
|
262
|
+
// Opened empty and filled by `rerender()` below, because the body's handlers close over `handle`
|
|
263
|
+
// And every one of them re-renders the popover in place rather than closing it.
|
|
264
|
+
const handle = renderPopover(nothing as never, {
|
|
265
|
+
dismissOnOutsideClick: true,
|
|
266
|
+
region: "grid/views",
|
|
267
|
+
});
|
|
268
|
+
const rerender = () => handle.update(body());
|
|
269
|
+
const close = () => handle.dismiss();
|
|
270
|
+
|
|
271
|
+
/** Persist one facet, put it on screen, and refresh both the popover and the toolbar. */
|
|
272
|
+
const change = (patch: GridLayout, apply: () => void) => {
|
|
273
|
+
saveGridLayout(gridId, patch);
|
|
274
|
+
apply();
|
|
275
|
+
panel.bump();
|
|
276
|
+
rerender();
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
function body() {
|
|
280
|
+
const layout = loadGridLayout(gridId) ?? {};
|
|
281
|
+
const views = listSavedViews(gridId);
|
|
282
|
+
const activeName = activeViewName(gridId);
|
|
283
|
+
const hidden = new Set(layout.hidden);
|
|
284
|
+
const sort = layout.sort ?? null;
|
|
285
|
+
const { columns } = controller.state;
|
|
286
|
+
|
|
287
|
+
const setSort = (spec: GridSortSpec | null) =>
|
|
288
|
+
change({ sort: spec }, () => {
|
|
289
|
+
void controller.setSort(spec);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
return html`<sp-popover
|
|
293
|
+
open
|
|
294
|
+
class="jx-grid-view-popover"
|
|
295
|
+
style="position:fixed;z-index:10000;left:${Math.max(4, rect.left)}px;top:${rect.bottom + 4}px"
|
|
296
|
+
>
|
|
297
|
+
<div class="jx-grid-cell-popover-body">
|
|
298
|
+
<div class="jx-grid-cell-popover-title">Saved views</div>
|
|
299
|
+
${
|
|
300
|
+
views.length === 0
|
|
301
|
+
? html`<div class="jx-grid-view-empty">
|
|
302
|
+
No saved views yet. Arrange the grid, then save it under a name.
|
|
303
|
+
</div>`
|
|
304
|
+
: views.map(
|
|
305
|
+
(view) => html`<div class="jx-grid-view-row">
|
|
306
|
+
<button
|
|
307
|
+
class="jx-grid-view-name ${view.name === activeName ? "is-active" : ""}"
|
|
308
|
+
@click=${() => {
|
|
309
|
+
panel.applyLayout(applySavedView(gridId, view.name));
|
|
310
|
+
close();
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
${view.name}
|
|
314
|
+
</button>
|
|
315
|
+
<sp-action-button
|
|
316
|
+
size="s"
|
|
317
|
+
quiet
|
|
318
|
+
title="Delete this view"
|
|
319
|
+
@click=${async () => {
|
|
320
|
+
const confirmed = await showConfirmDialog(
|
|
321
|
+
"Delete View",
|
|
322
|
+
`Delete the saved view "${view.name}"? The grid keeps its current layout.`,
|
|
323
|
+
{ confirmLabel: "Delete", destructive: true },
|
|
324
|
+
);
|
|
325
|
+
if (confirmed && deleteSavedView(gridId, view.name)) {
|
|
326
|
+
panel.bump();
|
|
327
|
+
rerender();
|
|
328
|
+
}
|
|
329
|
+
}}
|
|
330
|
+
>✕</sp-action-button
|
|
331
|
+
>
|
|
332
|
+
</div>`,
|
|
333
|
+
)
|
|
334
|
+
}
|
|
335
|
+
<div class="jx-grid-cell-popover-actions">
|
|
336
|
+
<sp-button
|
|
337
|
+
size="s"
|
|
338
|
+
variant="accent"
|
|
339
|
+
@click=${() => {
|
|
340
|
+
void promptSaveView(controller, panel, rerender);
|
|
341
|
+
}}
|
|
342
|
+
>
|
|
343
|
+
${activeName ? "Save as…" : "Save view…"}
|
|
344
|
+
</sp-button>
|
|
345
|
+
<sp-button
|
|
346
|
+
size="s"
|
|
347
|
+
variant="secondary"
|
|
348
|
+
title="Forget this grid's saved columns, sort, grouping and filter. Named views are kept."
|
|
349
|
+
@click=${() => {
|
|
350
|
+
resetGridLayout(gridId);
|
|
351
|
+
panel.applyLayout(null);
|
|
352
|
+
close();
|
|
353
|
+
}}
|
|
354
|
+
>
|
|
355
|
+
Reset
|
|
356
|
+
</sp-button>
|
|
357
|
+
</div>
|
|
358
|
+
|
|
359
|
+
<div class="jx-grid-cell-popover-title">Columns</div>
|
|
360
|
+
${columns.map(
|
|
361
|
+
(column) => html`<label class="jx-grid-view-check">
|
|
362
|
+
<input
|
|
363
|
+
type="checkbox"
|
|
364
|
+
.checked=${!hidden.has(column.field)}
|
|
365
|
+
data-field=${column.field}
|
|
366
|
+
@change=${(e: Event) => {
|
|
367
|
+
const next = new Set(hidden);
|
|
368
|
+
if ((e.target as HTMLInputElement).checked) {
|
|
369
|
+
next.delete(column.field);
|
|
370
|
+
} else {
|
|
371
|
+
next.add(column.field);
|
|
372
|
+
}
|
|
373
|
+
change({ hidden: [...next] }, () => panel.remount());
|
|
374
|
+
}}
|
|
375
|
+
/>
|
|
376
|
+
${column.title}
|
|
377
|
+
</label>`,
|
|
378
|
+
)}
|
|
379
|
+
|
|
380
|
+
<div class="jx-grid-cell-popover-title">Sort</div>
|
|
381
|
+
<select
|
|
382
|
+
class="jx-grid-input jx-grid-sort-field"
|
|
383
|
+
@change=${(e: Event) => {
|
|
384
|
+
const field = (e.target as HTMLSelectElement).value;
|
|
385
|
+
setSort(field === "" ? null : { dir: sort?.dir ?? "asc", field });
|
|
386
|
+
}}
|
|
387
|
+
>
|
|
388
|
+
<option value="" ?selected=${!sort}>Source order</option>
|
|
389
|
+
${columns.map(
|
|
390
|
+
(column) =>
|
|
391
|
+
html`<option value=${column.field} ?selected=${sort?.field === column.field}>
|
|
392
|
+
${column.title}
|
|
393
|
+
</option>`,
|
|
394
|
+
)}
|
|
395
|
+
</select>
|
|
396
|
+
<select
|
|
397
|
+
class="jx-grid-input jx-grid-sort-dir"
|
|
398
|
+
?disabled=${!sort}
|
|
399
|
+
@change=${(e: Event) => {
|
|
400
|
+
const dir = (e.target as HTMLSelectElement).value === "desc" ? "desc" : "asc";
|
|
401
|
+
if (sort) {
|
|
402
|
+
setSort({ dir, field: sort.field });
|
|
403
|
+
}
|
|
404
|
+
}}
|
|
405
|
+
>
|
|
406
|
+
<option value="asc" ?selected=${sort?.dir !== "desc"}>Ascending</option>
|
|
407
|
+
<option value="desc" ?selected=${sort?.dir === "desc"}>Descending</option>
|
|
408
|
+
</select>
|
|
409
|
+
|
|
410
|
+
<div class="jx-grid-cell-popover-title">Group by</div>
|
|
411
|
+
<select
|
|
412
|
+
class="jx-grid-input jx-grid-group-field"
|
|
413
|
+
@change=${(e: Event) => {
|
|
414
|
+
const field = (e.target as HTMLSelectElement).value || null;
|
|
415
|
+
change({ groupBy: field }, () => controller.setGrouping(field));
|
|
416
|
+
}}
|
|
417
|
+
>
|
|
418
|
+
<option value="" ?selected=${!controller.state.grouping}>Ungrouped</option>
|
|
419
|
+
${columns.map(
|
|
420
|
+
(column) =>
|
|
421
|
+
html`<option
|
|
422
|
+
value=${column.field}
|
|
423
|
+
?selected=${controller.state.grouping === column.field}
|
|
424
|
+
>
|
|
425
|
+
${column.title}
|
|
426
|
+
</option>`,
|
|
427
|
+
)}
|
|
428
|
+
</select>
|
|
429
|
+
</div>
|
|
430
|
+
</sp-popover>`;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
rerender();
|
|
434
|
+
// The button's label is derived from storage, and storage is ALSO written by the engine — a
|
|
435
|
+
// Column drag or resize goes straight to `saveGridLayout` from `grid-view.ts`. Refreshing the
|
|
436
|
+
// Toolbar as the control opens is what stops the drift dot lagging a resize by one interaction.
|
|
437
|
+
panel.bump();
|
|
438
|
+
return handle;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
/**
|
|
442
|
+
* Name the current layout.
|
|
443
|
+
*
|
|
444
|
+
* An existing name overwrites that view — the author who types it means "update this one", and a
|
|
445
|
+
* confirmation for replacing something they can re-save in two clicks is chrome for its own sake. A
|
|
446
|
+
* blank name is refused in the field, so the dialog never returns one.
|
|
447
|
+
*/
|
|
448
|
+
async function promptSaveView(
|
|
449
|
+
controller: GridController,
|
|
450
|
+
panel: ActiveGridPanel,
|
|
451
|
+
rerender: () => void,
|
|
452
|
+
): Promise<string | null> {
|
|
453
|
+
const gridId = controller.source.id;
|
|
454
|
+
const name = await showPromptDialog("Save Grid View", {
|
|
455
|
+
confirmLabel: "Save View",
|
|
456
|
+
message: `Columns, sort, grouping and filter, saved for ${controller.source.label}.`,
|
|
457
|
+
placeholder: "Recent drafts",
|
|
458
|
+
validate: (candidate) => (candidate.trim() === "" ? "Name the view." : ""),
|
|
459
|
+
value: activeViewName(gridId) ?? "",
|
|
460
|
+
});
|
|
461
|
+
if (name === null) {
|
|
462
|
+
return null;
|
|
463
|
+
}
|
|
464
|
+
const saved = saveViewAs(gridId, name);
|
|
465
|
+
if (!saved) {
|
|
466
|
+
// Storage is off (privacy mode): the layout still works, it just cannot be remembered.
|
|
467
|
+
notify.warn("Views cannot be saved — this browser has local storage disabled.", {
|
|
468
|
+
key: "grid.saveView",
|
|
469
|
+
source: "Data",
|
|
470
|
+
});
|
|
471
|
+
return null;
|
|
472
|
+
}
|
|
473
|
+
panel.bump();
|
|
474
|
+
rerender();
|
|
475
|
+
return saved.name;
|
|
476
|
+
}
|
|
477
|
+
|
|
146
478
|
/** Find & replace popover — buffers all replacements as one undo group. */
|
|
147
479
|
function openReplacePopover(controller: GridController, anchor: HTMLElement) {
|
|
148
480
|
const rect = rectOf(anchor);
|
|
@@ -172,11 +504,14 @@ function openReplacePopover(controller: GridController, anchor: HTMLElement) {
|
|
|
172
504
|
variant="accent"
|
|
173
505
|
@click=${() => {
|
|
174
506
|
const changed = controller.replaceAll(find, replace);
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
507
|
+
if (changed === 0) {
|
|
508
|
+
notify.info("No matches.", { key: "grid.replaceAll" });
|
|
509
|
+
} else {
|
|
510
|
+
notify.success(
|
|
511
|
+
`Replaced in ${changed} cell${changed === 1 ? "" : "s"} — save to apply.`,
|
|
512
|
+
{ action: "file.save", key: "grid.replaceAll" },
|
|
513
|
+
);
|
|
514
|
+
}
|
|
180
515
|
if (changed > 0) {
|
|
181
516
|
handle.dismiss();
|
|
182
517
|
}
|
|
@@ -207,12 +542,13 @@ function pagerTpl(controller: GridController) {
|
|
|
207
542
|
quiet
|
|
208
543
|
title="Previous page"
|
|
209
544
|
?disabled=${offset === 0 || state.loading}
|
|
210
|
-
@click=${() =>
|
|
545
|
+
@click=${() => {
|
|
211
546
|
void controller.setQuery({
|
|
212
547
|
...state.query,
|
|
213
548
|
limit,
|
|
214
549
|
offset: Math.max(0, offset - limit),
|
|
215
|
-
})
|
|
550
|
+
});
|
|
551
|
+
}}
|
|
216
552
|
>
|
|
217
553
|
‹ Prev
|
|
218
554
|
</sp-action-button>
|
|
@@ -222,7 +558,9 @@ function pagerTpl(controller: GridController) {
|
|
|
222
558
|
quiet
|
|
223
559
|
title="Next page"
|
|
224
560
|
?disabled=${offset + limit >= state.total || state.loading}
|
|
225
|
-
@click=${() =>
|
|
561
|
+
@click=${() => {
|
|
562
|
+
void controller.setQuery({ ...state.query, limit, offset: offset + limit });
|
|
563
|
+
}}
|
|
226
564
|
>
|
|
227
565
|
Next ›
|
|
228
566
|
</sp-action-button>
|
|
@@ -231,12 +569,12 @@ function pagerTpl(controller: GridController) {
|
|
|
231
569
|
|
|
232
570
|
function shellTpl(
|
|
233
571
|
controller: GridController,
|
|
234
|
-
|
|
572
|
+
panel: ActiveGridPanel,
|
|
235
573
|
onHost: (el: HTMLElement | undefined) => void,
|
|
236
574
|
) {
|
|
237
575
|
return html`
|
|
238
576
|
<div class="jx-grid">
|
|
239
|
-
${toolbarTpl(controller,
|
|
577
|
+
${toolbarTpl(controller, panel)}
|
|
240
578
|
<div class="jx-grid-host" ${ref((el) => onHost(el as HTMLElement | undefined))}></div>
|
|
241
579
|
</div>
|
|
242
580
|
`;
|
|
@@ -249,11 +587,12 @@ function shellTpl(
|
|
|
249
587
|
* @param {HTMLElement} canvasWrap
|
|
250
588
|
* @param {Tab} tab
|
|
251
589
|
*/
|
|
252
|
-
export function renderGridMode(
|
|
253
|
-
|
|
590
|
+
export function renderGridMode(surface: CanvasSurface, tab: Tab) {
|
|
591
|
+
const { paneId, wrap: canvasWrap } = surface;
|
|
592
|
+
if (gridPanelMounted(paneId, tab)) {
|
|
254
593
|
return;
|
|
255
594
|
}
|
|
256
|
-
detachGridPanel();
|
|
595
|
+
detachGridPanel(paneId);
|
|
257
596
|
|
|
258
597
|
let controller = getGridController(tab);
|
|
259
598
|
// CSV file tabs can reach grid mode through any open path (deep link, quick search, recents) —
|
|
@@ -268,17 +607,47 @@ export function renderGridMode(canvasWrap: HTMLElement, tab: Tab) {
|
|
|
268
607
|
}
|
|
269
608
|
|
|
270
609
|
const scope = effectScope();
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
610
|
+
// `localStorage` is not reactive and neither is the engine, so the toolbar needs something that
|
|
611
|
+
// Is: a saved-view edit bumps this and the panel's one effect re-renders like any other change.
|
|
612
|
+
const local = reactive({ views: 0 });
|
|
613
|
+
const live = controller;
|
|
274
614
|
let hostEl: HTMLElement | null = null;
|
|
615
|
+
|
|
616
|
+
const panel: ActiveGridPanel = {
|
|
617
|
+
applyLayout(layout) {
|
|
618
|
+
void live.setSort(layout?.sort ?? null);
|
|
619
|
+
live.setGrouping(layout?.groupBy ?? null);
|
|
620
|
+
panel.remount();
|
|
621
|
+
panel.view?.setSearch(layout?.filter ?? "");
|
|
622
|
+
panel.bump();
|
|
623
|
+
},
|
|
624
|
+
bump() {
|
|
625
|
+
local.views += 1;
|
|
626
|
+
},
|
|
627
|
+
remount() {
|
|
628
|
+
panel.view?.destroy();
|
|
629
|
+
panel.view = hostEl ? createGridView(hostEl, live) : null;
|
|
630
|
+
},
|
|
631
|
+
paneId,
|
|
632
|
+
scope,
|
|
633
|
+
tabId: tab.id,
|
|
634
|
+
view: null,
|
|
635
|
+
wrap: canvasWrap,
|
|
636
|
+
};
|
|
637
|
+
_active.set(paneId, panel);
|
|
638
|
+
|
|
275
639
|
const onHost = (el: HTMLElement | undefined) => {
|
|
276
640
|
hostEl = el ?? null;
|
|
277
641
|
};
|
|
278
642
|
|
|
643
|
+
// The stored sort, grouping and filter are applied ONCE, when the engine first exists. Re-running
|
|
644
|
+
// Them on every render would fight the author: every ad-hoc header sort would be undone by the
|
|
645
|
+
// Next repaint, which is the behaviour a saved view is supposed to replace, not impose.
|
|
646
|
+
let restored = false;
|
|
647
|
+
|
|
279
648
|
scope.run(() => {
|
|
280
649
|
effect(() => {
|
|
281
|
-
if (
|
|
650
|
+
if (activeIn(paneId) !== panel) {
|
|
282
651
|
return;
|
|
283
652
|
}
|
|
284
653
|
// Track everything the toolbar shows.
|
|
@@ -287,12 +656,11 @@ export function renderGridMode(canvasWrap: HTMLElement, tab: Tab) {
|
|
|
287
656
|
void controller.state.error;
|
|
288
657
|
void controller.state.total;
|
|
289
658
|
void controller.state.query.offset;
|
|
659
|
+
void controller.state.grouping;
|
|
290
660
|
void controller.buffer.dirtyCount();
|
|
661
|
+
void local.views;
|
|
291
662
|
|
|
292
|
-
litRender(
|
|
293
|
-
shellTpl(controller, () => panel.view, onHost),
|
|
294
|
-
canvasWrap,
|
|
295
|
-
);
|
|
663
|
+
litRender(shellTpl(controller, panel, onHost), canvasWrap);
|
|
296
664
|
|
|
297
665
|
// Create the engine once the columns exist and the host div is in the DOM.
|
|
298
666
|
if (
|
|
@@ -302,7 +670,154 @@ export function renderGridMode(canvasWrap: HTMLElement, tab: Tab) {
|
|
|
302
670
|
hostEl
|
|
303
671
|
) {
|
|
304
672
|
panel.view = createGridView(hostEl, controller);
|
|
673
|
+
if (!restored) {
|
|
674
|
+
restored = true;
|
|
675
|
+
const layout = loadGridLayout(controller.source.id);
|
|
676
|
+
void controller.setSort(layout?.sort ?? null);
|
|
677
|
+
controller.setGrouping(layout?.groupBy ?? null);
|
|
678
|
+
panel.view.setSearch(layout?.filter ?? "");
|
|
679
|
+
}
|
|
305
680
|
}
|
|
306
681
|
});
|
|
307
682
|
});
|
|
308
683
|
}
|
|
684
|
+
|
|
685
|
+
// ─── Commands ─────────────────────────────────────────────────────────────────
|
|
686
|
+
|
|
687
|
+
/**
|
|
688
|
+
* The live grid surface, or null.
|
|
689
|
+
*
|
|
690
|
+
* Both halves are required: a controller says the active tab HAS a grid, and a mounted panel says
|
|
691
|
+
* the grid is what is on screen. A view command that ran against a grid tab currently showing its
|
|
692
|
+
* Monaco source mode would remount an engine into a host that is not there.
|
|
693
|
+
*/
|
|
694
|
+
function activeGridSurface(): { controller: GridController; panel: ActiveGridPanel } | null {
|
|
695
|
+
const controller = getGridController(activeTab.value);
|
|
696
|
+
const active = activeIn(workspace.activePaneId);
|
|
697
|
+
if (!controller || !active || active.tabId !== controller.tab.id) {
|
|
698
|
+
return null;
|
|
699
|
+
}
|
|
700
|
+
return { controller, panel: active };
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
/** The saved-view verbs. Every one of them is also a control in the View popover. */
|
|
704
|
+
function requireSurface(commandId: string) {
|
|
705
|
+
const surface = activeGridSurface();
|
|
706
|
+
if (!surface) {
|
|
707
|
+
throw new Error(`command "${commandId}" needs a grid on screen`);
|
|
708
|
+
}
|
|
709
|
+
return surface;
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
/**
|
|
713
|
+
* Saved views, as commands.
|
|
714
|
+
*
|
|
715
|
+
* A view is named, so it is addressable, so it belongs in the palette, on the `__jxAutomation`
|
|
716
|
+
* surface and in front of the assistant — not only behind a popover the author has to find. The
|
|
717
|
+
* refusals name what the grid actually holds, in the idiom of `collection.editInGrid`: an unknown
|
|
718
|
+
* view lists the views that exist rather than doing nothing.
|
|
719
|
+
*
|
|
720
|
+
* @returns {AnyCommand[]}
|
|
721
|
+
*/
|
|
722
|
+
export function gridViewCommands(): AnyCommand[] {
|
|
723
|
+
const viewNameArg = (commandId: string, args: unknown): { gridId: string; name: string } => {
|
|
724
|
+
const { controller } = requireSurface(commandId);
|
|
725
|
+
const gridId = controller.source.id;
|
|
726
|
+
const name = stringArg(commandId, args as Record<string, unknown>, "name");
|
|
727
|
+
const known = listSavedViews(gridId).map((view) => view.name);
|
|
728
|
+
if (!known.includes(name)) {
|
|
729
|
+
throw new RangeError(
|
|
730
|
+
`command "${commandId}" argument "name": "${name}" is not a saved view of ` +
|
|
731
|
+
`${controller.source.label} — it has: ${known.length > 0 ? known.join(", ") : "none"}`,
|
|
732
|
+
);
|
|
733
|
+
}
|
|
734
|
+
return { gridId, name };
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
return [
|
|
738
|
+
{
|
|
739
|
+
category: "View",
|
|
740
|
+
id: "grid.saveView",
|
|
741
|
+
level: "document",
|
|
742
|
+
menus: ["palette"],
|
|
743
|
+
group: "5_data",
|
|
744
|
+
requires: "a grid on screen",
|
|
745
|
+
when: (ctx) => ctx.project.open,
|
|
746
|
+
enablement: () => activeGridSurface() !== null,
|
|
747
|
+
aiTool: {
|
|
748
|
+
description:
|
|
749
|
+
"Save the open grid's columns, sort, grouping and filter as a named view. Prompts for the name.",
|
|
750
|
+
name: "save_grid_view",
|
|
751
|
+
},
|
|
752
|
+
run: async () => {
|
|
753
|
+
const { controller, panel } = requireSurface("grid.saveView");
|
|
754
|
+
await promptSaveView(controller, panel, () => {});
|
|
755
|
+
},
|
|
756
|
+
title: "Save Grid View…",
|
|
757
|
+
},
|
|
758
|
+
{
|
|
759
|
+
args: argsSchema({ name: stringProperty("The saved view's name.") }),
|
|
760
|
+
category: "View",
|
|
761
|
+
id: "grid.applyView",
|
|
762
|
+
level: "document",
|
|
763
|
+
menus: ["palette"],
|
|
764
|
+
group: "5_data",
|
|
765
|
+
requires: "a grid on screen",
|
|
766
|
+
when: (ctx) => ctx.project.open,
|
|
767
|
+
enablement: () => activeGridSurface() !== null,
|
|
768
|
+
aiTool: {
|
|
769
|
+
description: "Apply a saved view to the open grid by name.",
|
|
770
|
+
name: "apply_grid_view",
|
|
771
|
+
},
|
|
772
|
+
run: (_ctx, args) => {
|
|
773
|
+
const { gridId, name } = viewNameArg("grid.applyView", args);
|
|
774
|
+
const { panel } = requireSurface("grid.applyView");
|
|
775
|
+
panel.applyLayout(applySavedView(gridId, name));
|
|
776
|
+
},
|
|
777
|
+
title: "Apply Grid View",
|
|
778
|
+
},
|
|
779
|
+
{
|
|
780
|
+
args: argsSchema({ name: stringProperty("The saved view's name.") }),
|
|
781
|
+
category: "View",
|
|
782
|
+
id: "grid.deleteView",
|
|
783
|
+
level: "document",
|
|
784
|
+
menus: ["palette"],
|
|
785
|
+
group: "5_data",
|
|
786
|
+
requires: "a grid on screen",
|
|
787
|
+
destructive: true,
|
|
788
|
+
when: (ctx) => ctx.project.open,
|
|
789
|
+
enablement: () => activeGridSurface() !== null,
|
|
790
|
+
run: (_ctx, args) => {
|
|
791
|
+
const { gridId, name } = viewNameArg("grid.deleteView", args);
|
|
792
|
+
deleteSavedView(gridId, name);
|
|
793
|
+
requireSurface("grid.deleteView").panel.bump();
|
|
794
|
+
},
|
|
795
|
+
title: "Delete Grid View",
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
category: "View",
|
|
799
|
+
id: "grid.resetView",
|
|
800
|
+
level: "document",
|
|
801
|
+
menus: ["palette"],
|
|
802
|
+
group: "5_data",
|
|
803
|
+
requires: "a grid on screen",
|
|
804
|
+
when: (ctx) => ctx.project.open,
|
|
805
|
+
enablement: () => activeGridSurface() !== null,
|
|
806
|
+
run: () => {
|
|
807
|
+
const { controller, panel } = requireSurface("grid.resetView");
|
|
808
|
+
resetGridLayout(controller.source.id);
|
|
809
|
+
panel.applyLayout(null);
|
|
810
|
+
},
|
|
811
|
+
title: "Reset Grid View",
|
|
812
|
+
},
|
|
813
|
+
];
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
/**
|
|
817
|
+
* Register the saved-view commands.
|
|
818
|
+
*
|
|
819
|
+
* @param {CommandRegistry} registry
|
|
820
|
+
*/
|
|
821
|
+
export function registerGridViewCommands(registry: CommandRegistry): void {
|
|
822
|
+
registry.registerAll(gridViewCommands());
|
|
823
|
+
}
|