@jxsuite/studio 2.0.0 → 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-0hzentwq.js → components-2bpqp4mq.js} +5 -7
- package/dist/chunks/{components-0hzentwq.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-2c188n41.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-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
- package/dist/chunks/{studio-97qcgvdn.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-hhj92gcr.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-ygzav7z5.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-n9zmpxjg.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 +23711 -35185
- 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 +34 -9
- package/src/new-project/location-fields.ts +9 -1
- package/src/new-project/new-project-modal.ts +270 -262
- 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 -120
- 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 +75 -2
- 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 +113 -0
- 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-qct0gt8f.js +0 -531
- package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
- package/dist/chunks/files-02zn5y24.js +0 -50
- package/dist/chunks/files-02zn5y24.js.map +0 -9
- package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
- package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
- package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
- package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
- package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
- package/dist/chunks/settings-modal-gj0hrah4.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-4k0m4fmp.js.map +0 -13
- package/dist/chunks/studio-568nx4t5.js +0 -469
- package/dist/chunks/studio-568nx4t5.js.map +0 -14
- package/dist/chunks/studio-9smfzqhc.js +0 -19
- package/dist/chunks/studio-9smfzqhc.js.map +0 -10
- package/dist/chunks/studio-asvf0j9e.js +0 -429
- package/dist/chunks/studio-asvf0j9e.js.map +0 -13
- package/dist/chunks/studio-d77qm3pk.js +0 -3427
- package/dist/chunks/studio-d77qm3pk.js.map +0 -26
- package/dist/chunks/studio-j6gqyjf4.js +0 -1854
- package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
- package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
- package/dist/chunks/studio-qkvt37c0.js.map +0 -9
- package/dist/chunks/studio-rq0vxb19.js +0 -3744
- package/dist/chunks/studio-rq0vxb19.js.map +0 -22
- package/dist/chunks/studio-wd6ewdvd.js +0 -853
- package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
- 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-ygzav7z5.js.map +0 -46
- package/dist/chunks/studio-zmy7241e.js +0 -399
- package/dist/chunks/studio-zmy7241e.js.map +0 -12
- 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-2c188n41.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-hhj92gcr.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
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Library previews — a live document render is expensive, so it is capped, lazy, and evictable.
|
|
4
|
+
*
|
|
5
|
+
* Four limits, each answering a different way the old Manage view fell over on a large project:
|
|
6
|
+
*
|
|
7
|
+
* 1. **Lazy.** A preview is built when its card INTERSECTS the viewport, not when the card is created.
|
|
8
|
+
* Scrolling a 300-item Library therefore builds a preview per card actually looked at.
|
|
9
|
+
* 2. **Capped.** {@link createPreviewCache} is an LRU with a hard limit. The predecessor's
|
|
10
|
+
* `_previewCache` was an unbounded `Map<string, HTMLElement>` of live runtime subtrees, so a
|
|
11
|
+
* long browse leaked every document it had ever shown until the tab closed.
|
|
12
|
+
* 3. **Cancellable.** A preview whose card left the window before its read resolved is dropped on
|
|
13
|
+
* arrival rather than appended to a detached node.
|
|
14
|
+
* 4. **Released.** The visibility gate keeps a set too, and it is the same leak one level down: a card
|
|
15
|
+
* observed and then scrolled past before it ever intersected was retained for the life of the
|
|
16
|
+
* pane. {@link PreviewObserver.releaseDetached} is how the window gives those back.
|
|
17
|
+
*
|
|
18
|
+
* The renders themselves go through `platform.readFile`, which the PAL counts, so `probe.idle()`
|
|
19
|
+
* already knows a preview is in flight without this module declaring an idle source of its own.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { buildScope, renderNode, setSkipServerFunctions } from "@jxsuite/runtime";
|
|
23
|
+
import { componentRegistry } from "../files/components";
|
|
24
|
+
import { getPlatform } from "../platform";
|
|
25
|
+
import { loadFormats, formatForPath } from "../format/format-host";
|
|
26
|
+
import { parseSourceForPath } from "../files/file-ops";
|
|
27
|
+
import { renderComponentPreview } from "../panels/component-preview";
|
|
28
|
+
import type { ComponentEntry } from "../files/components";
|
|
29
|
+
import type { JxDocument } from "@jxsuite/schema/types";
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* How many rendered previews stay alive at once.
|
|
33
|
+
*
|
|
34
|
+
* **The cap must exceed one window, or it thrashes against itself.** The first value tried here was
|
|
35
|
+
* 48, and the 300-page measurement caught it immediately: a 1180×700 pane of cards renders 55 items
|
|
36
|
+
* (11 rows of 5, overscan included), so the cache evicted documents that were still on screen and
|
|
37
|
+
* re-rendered them on the next repaint — 560 reads for 300 documents. 150 clears a nine-column,
|
|
38
|
+
* twelve-row window on a wide display with room to spare, so eviction is only ever reached by
|
|
39
|
+
* SCROLLING, which is what an LRU is for.
|
|
40
|
+
*
|
|
41
|
+
* It remains a fixed cost rather than a function of project size, which is the whole point: the
|
|
42
|
+
* predecessor's cache was unbounded, so a long browse retained every document it had ever shown.
|
|
43
|
+
*/
|
|
44
|
+
export const PREVIEW_CACHE_LIMIT = 150;
|
|
45
|
+
|
|
46
|
+
export interface PreviewCache {
|
|
47
|
+
/** Fetch and mark as most-recently-used. */
|
|
48
|
+
get: (key: string) => HTMLElement | undefined;
|
|
49
|
+
/** Insert, evicting the least-recently-used entry when over the limit. */
|
|
50
|
+
set: (key: string, value: HTMLElement) => void;
|
|
51
|
+
has: (key: string) => boolean;
|
|
52
|
+
/** Live entry count — what the perf test asserts stays bounded. */
|
|
53
|
+
size: () => number;
|
|
54
|
+
/** Keys, least-recently-used first. */
|
|
55
|
+
keys: () => string[];
|
|
56
|
+
clear: () => void;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* A least-recently-used cache over rendered previews.
|
|
61
|
+
*
|
|
62
|
+
* `Map` iterates in insertion order, so re-inserting on read is the whole LRU: the oldest key is
|
|
63
|
+
* always the first one the iterator yields. Evicted subtrees are removed from the DOM as they go —
|
|
64
|
+
* an evicted preview that stayed attached would keep its runtime alive and defeat the cap.
|
|
65
|
+
*/
|
|
66
|
+
export function createPreviewCache(limit: number = PREVIEW_CACHE_LIMIT): PreviewCache {
|
|
67
|
+
const entries = new Map<string, HTMLElement>();
|
|
68
|
+
|
|
69
|
+
function evict() {
|
|
70
|
+
// `size > limit` guarantees a first key, so the iterator result is never `done` here.
|
|
71
|
+
while (entries.size > limit) {
|
|
72
|
+
const oldest = entries.keys().next().value as string;
|
|
73
|
+
const stale = entries.get(oldest);
|
|
74
|
+
entries.delete(oldest);
|
|
75
|
+
stale?.remove();
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return {
|
|
80
|
+
clear() {
|
|
81
|
+
for (const element of entries.values()) {
|
|
82
|
+
element.remove();
|
|
83
|
+
}
|
|
84
|
+
entries.clear();
|
|
85
|
+
},
|
|
86
|
+
get(key) {
|
|
87
|
+
const value = entries.get(key);
|
|
88
|
+
if (value !== undefined) {
|
|
89
|
+
entries.delete(key);
|
|
90
|
+
entries.set(key, value);
|
|
91
|
+
}
|
|
92
|
+
return value;
|
|
93
|
+
},
|
|
94
|
+
has: (key) => entries.has(key),
|
|
95
|
+
keys: () => [...entries.keys()],
|
|
96
|
+
set(key, value) {
|
|
97
|
+
entries.delete(key);
|
|
98
|
+
entries.set(key, value);
|
|
99
|
+
evict();
|
|
100
|
+
},
|
|
101
|
+
size: () => entries.size,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// ─── Rendering one preview ───────────────────────────────────────────────────
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Render a page/layout/content document to a detached element, or null when it cannot be rendered.
|
|
109
|
+
*
|
|
110
|
+
* Returning null rather than throwing is deliberate: a document that fails to parse is a real state
|
|
111
|
+
* of the project, and the card shows a document glyph for it. The Problems list is for failures the
|
|
112
|
+
* author must ACT on; an in-progress file that does not parse yet is not one.
|
|
113
|
+
*/
|
|
114
|
+
export async function renderDocPreview(filePath: string): Promise<HTMLElement | null> {
|
|
115
|
+
try {
|
|
116
|
+
const content = await getPlatform().readFile(filePath);
|
|
117
|
+
setSkipServerFunctions(true);
|
|
118
|
+
await loadFormats();
|
|
119
|
+
let document_: JxDocument;
|
|
120
|
+
if (formatForPath(filePath)) {
|
|
121
|
+
const parsed = await parseSourceForPath(filePath, content);
|
|
122
|
+
document_ = parsed.document as JxDocument;
|
|
123
|
+
} else {
|
|
124
|
+
document_ = JSON.parse(content) as JxDocument;
|
|
125
|
+
}
|
|
126
|
+
const scope = await buildScope(document_, {}, location.href);
|
|
127
|
+
const element = renderNode(document_, scope);
|
|
128
|
+
return element instanceof HTMLElement ? element : null;
|
|
129
|
+
} catch {
|
|
130
|
+
return null;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The preview for one file — a registered component's own preview when there is one, the document
|
|
136
|
+
* render otherwise. Cached; a cache hit does no work at all.
|
|
137
|
+
*/
|
|
138
|
+
export async function previewFor(
|
|
139
|
+
path: string,
|
|
140
|
+
cache: PreviewCache,
|
|
141
|
+
): Promise<HTMLElement | undefined> {
|
|
142
|
+
const cached = cache.get(path);
|
|
143
|
+
if (cached) {
|
|
144
|
+
return cached;
|
|
145
|
+
}
|
|
146
|
+
let rendered: HTMLElement | null | undefined;
|
|
147
|
+
try {
|
|
148
|
+
const component = componentRegistry.find((c: ComponentEntry) => c.path === path);
|
|
149
|
+
rendered = component
|
|
150
|
+
? ((await renderComponentPreview(component)) as HTMLElement | undefined)
|
|
151
|
+
: await renderDocPreview(path);
|
|
152
|
+
} catch {
|
|
153
|
+
return undefined;
|
|
154
|
+
}
|
|
155
|
+
if (!rendered) {
|
|
156
|
+
return undefined;
|
|
157
|
+
}
|
|
158
|
+
cache.set(path, rendered);
|
|
159
|
+
return rendered;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// ─── Visibility ──────────────────────────────────────────────────────────────
|
|
163
|
+
|
|
164
|
+
export interface PreviewObserver {
|
|
165
|
+
/** Start watching a card. Safe to call repeatedly for the same element. */
|
|
166
|
+
observe: (element: Element) => void;
|
|
167
|
+
/**
|
|
168
|
+
* Stop watching every card the document no longer contains; answers how many went.
|
|
169
|
+
*
|
|
170
|
+
* The caller is whoever re-renders the window: once lit has committed, a card outside the new
|
|
171
|
+
* window is detached, and an observation of a detached node can never fire again. It is pure cost
|
|
172
|
+
* — the browser still walks it on every scroll frame — and it is unreachable garbage that pins
|
|
173
|
+
* the card's whole subtree.
|
|
174
|
+
*
|
|
175
|
+
* **The one release path, deliberately.** There is no per-element `unobserve` here: the previous
|
|
176
|
+
* shape had one, nothing ever called it, and "a card left the window" is a fact the WINDOW knows
|
|
177
|
+
* and an individual card never does. One caller, at one moment, over the whole set.
|
|
178
|
+
*/
|
|
179
|
+
releaseDetached: () => number;
|
|
180
|
+
destroy: () => void;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Watch cards for viewport entry and report each one ONCE.
|
|
185
|
+
*
|
|
186
|
+
* **Every observation is owned here, not by the caller.** The first version of this module let the
|
|
187
|
+
* intersect callback be the only release path, which is only ever taken by a card that actually
|
|
188
|
+
* came into view: scroll fast enough that the callback never runs and nothing is ever released.
|
|
189
|
+
* Four sweeps of the same 300-page list retained 805 → 1310 → 1815 → 2320 cards with zero
|
|
190
|
+
* `unobserve` calls, every one of them a node no longer in the document — the very leak the LRU
|
|
191
|
+
* above was written to end, one level down. So the `watched` set is kept here, and
|
|
192
|
+
* {@link PreviewObserver.releaseDetached} sweeps it; `tests/library-observer-leak.test.ts` is the
|
|
193
|
+
* measurement, and it fails on the shape this replaced.
|
|
194
|
+
*
|
|
195
|
+
* Where `IntersectionObserver` is unavailable — happy-dom, and any host that has not implemented it
|
|
196
|
+
* — every observed element is reported immediately. That is the honest degradation: the window
|
|
197
|
+
* already bounds how many cards exist, so "all of them" is at most one screen plus the overscan,
|
|
198
|
+
* and a Library that silently showed no previews would look broken rather than slow. Nothing is
|
|
199
|
+
* retained on that path, so there is nothing for the sweep to find.
|
|
200
|
+
*/
|
|
201
|
+
export function createPreviewObserver(onVisible: (element: Element) => void): PreviewObserver {
|
|
202
|
+
if (typeof IntersectionObserver !== "function") {
|
|
203
|
+
return {
|
|
204
|
+
destroy() {
|
|
205
|
+
/* Nothing to disconnect. */
|
|
206
|
+
},
|
|
207
|
+
observe: (element) => onVisible(element),
|
|
208
|
+
releaseDetached: () => 0,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const watched = new Set<Element>();
|
|
212
|
+
const observer = new IntersectionObserver(
|
|
213
|
+
(records) => {
|
|
214
|
+
for (const record of records) {
|
|
215
|
+
if (record.isIntersecting) {
|
|
216
|
+
watched.delete(record.target);
|
|
217
|
+
observer.unobserve(record.target);
|
|
218
|
+
onVisible(record.target);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
},
|
|
222
|
+
// One card-height of lead-in, so a preview is usually ready by the time it is looked at.
|
|
223
|
+
{ rootMargin: "200px" },
|
|
224
|
+
);
|
|
225
|
+
return {
|
|
226
|
+
destroy() {
|
|
227
|
+
observer.disconnect();
|
|
228
|
+
watched.clear();
|
|
229
|
+
},
|
|
230
|
+
observe(element) {
|
|
231
|
+
// Re-observing is a no-op in the browser, but the repaint that follows every scroll frame
|
|
232
|
+
// Hands the same slot back, and a call per card per frame is work nobody needs.
|
|
233
|
+
if (watched.has(element)) {
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
236
|
+
watched.add(element);
|
|
237
|
+
observer.observe(element);
|
|
238
|
+
},
|
|
239
|
+
releaseDetached() {
|
|
240
|
+
let released = 0;
|
|
241
|
+
// Deleting the current key during a `Set` iteration is defined; later keys are still visited.
|
|
242
|
+
for (const element of watched) {
|
|
243
|
+
if (!element.isConnected) {
|
|
244
|
+
watched.delete(element);
|
|
245
|
+
observer.unobserve(element);
|
|
246
|
+
released += 1;
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
return released;
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Library as a {@link GridSource}.
|
|
3
|
+
*
|
|
4
|
+
* The Library is one more source over the same contract the CSV, collection, pages and connector
|
|
5
|
+
* grids already use, which is what makes Table a real grid rather than a fifth hand-written table:
|
|
6
|
+
* columns come from {@link libraryColumns}, rows are the scan, and every layout — Cards, Media,
|
|
7
|
+
* Calendar, Board — draws the SAME rows through a different arrangement. A layout switch never
|
|
8
|
+
* re-reads the project.
|
|
9
|
+
*
|
|
10
|
+
* It is a **read-only** source, and says so rather than pretending. Renaming, duplicating and
|
|
11
|
+
* deleting a file are real operations with real consequences (a rename rewrites references; a
|
|
12
|
+
* delete needs the reference count in its confirmation), and they belong to the commands that
|
|
13
|
+
* already do that work correctly. A grid cell that silently renamed a file on blur would bypass
|
|
14
|
+
* both. {@link LibrarySource.commit} therefore refuses, naming the surface that can.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { getPlatform } from "../platform";
|
|
18
|
+
import { projectState } from "../store";
|
|
19
|
+
import { makeGridTabId } from "../grid/grid-source";
|
|
20
|
+
import { scanLibrary } from "./library-model";
|
|
21
|
+
import type { LibraryFile, LibraryScan, ScanFailure } from "./library-model";
|
|
22
|
+
import type {
|
|
23
|
+
CommitResult,
|
|
24
|
+
GridColumn,
|
|
25
|
+
GridEditBatch,
|
|
26
|
+
GridRow,
|
|
27
|
+
GridRowsResult,
|
|
28
|
+
GridSource,
|
|
29
|
+
} from "../grid/grid-source";
|
|
30
|
+
|
|
31
|
+
/** Why a cell edit is refused — printed by the grid, so the author is told where to go instead. */
|
|
32
|
+
export const LIBRARY_READ_ONLY =
|
|
33
|
+
"The Library lists files; it does not edit them. Rename, duplicate or delete from the file's " +
|
|
34
|
+
"context menu, or open the file to edit it.";
|
|
35
|
+
|
|
36
|
+
/** The Table layout's columns. `path` is the row key and is never editable. */
|
|
37
|
+
export function libraryColumns(): GridColumn[] {
|
|
38
|
+
return [
|
|
39
|
+
{ editable: false, field: "name", kind: "string", pk: true, title: "Name", widthHint: 260 },
|
|
40
|
+
{ editable: false, field: "category", kind: "string", title: "Category", widthHint: 120 },
|
|
41
|
+
{ editable: false, field: "locale", kind: "string", title: "Locale", widthHint: 90 },
|
|
42
|
+
{ editable: false, field: "type", kind: "string", title: "Type", widthHint: 120 },
|
|
43
|
+
{ editable: false, field: "size", kind: "number", title: "Size", widthHint: 90 },
|
|
44
|
+
{ editable: false, field: "modified", kind: "date", title: "Modified", widthHint: 160 },
|
|
45
|
+
{ editable: false, field: "path", kind: "readonly", title: "Path", widthHint: 320 },
|
|
46
|
+
];
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** One scanned file as a grid row. */
|
|
50
|
+
export function libraryRow(file: LibraryFile): GridRow {
|
|
51
|
+
return {
|
|
52
|
+
cells: {
|
|
53
|
+
category: file.category,
|
|
54
|
+
// Null, not "": the platform reported no locale directory for this file, and a grid cell
|
|
55
|
+
// Distinguishes "no value" from "the empty string" everywhere else in this contract.
|
|
56
|
+
locale: file.locale ?? null,
|
|
57
|
+
modified: file.modified ?? null,
|
|
58
|
+
name: file.name,
|
|
59
|
+
path: file.path,
|
|
60
|
+
size: file.size ?? null,
|
|
61
|
+
type: file.type,
|
|
62
|
+
},
|
|
63
|
+
key: file.path,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* A Library source, plus the two questions a grid source cannot ask but the Library must answer:
|
|
69
|
+
* what the scan could not read, and the files behind the rows (the non-table layouts want the typed
|
|
70
|
+
* record, not a cell bag).
|
|
71
|
+
*/
|
|
72
|
+
export interface LibrarySource extends GridSource {
|
|
73
|
+
/** The scanned files, in path order. Empty before the first {@link GridSource.rows} call. */
|
|
74
|
+
files: () => readonly LibraryFile[];
|
|
75
|
+
/** Directories the last scan could not read. NON-EMPTY MEANS THE LISTING IS INCOMPLETE. */
|
|
76
|
+
failures: () => readonly ScanFailure[];
|
|
77
|
+
/** Whether a scan has completed at least once — "not loaded yet" is not "empty". */
|
|
78
|
+
scanned: () => boolean;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The Library's tab id. One Library per window; category and layout are view state, not identity. */
|
|
82
|
+
export function libraryTabId(): string {
|
|
83
|
+
return makeGridTabId({ kind: "library" });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Build the Library source. The scan is lazy: nothing is read until the first `rows()`.
|
|
88
|
+
*
|
|
89
|
+
* @param {() => readonly string[]} [dirs] Project directories to scan, injectable for tests and for
|
|
90
|
+
* the perf measurement; defaults to the open project's own list.
|
|
91
|
+
*/
|
|
92
|
+
export function createLibrarySource(dirs?: () => readonly string[]): LibrarySource {
|
|
93
|
+
let scan: LibraryScan | null = null;
|
|
94
|
+
let inFlight: Promise<LibraryScan> | null = null;
|
|
95
|
+
|
|
96
|
+
const projectDirs = dirs ?? (() => (projectState?.projectDirs ?? []) as readonly string[]);
|
|
97
|
+
|
|
98
|
+
async function ensureScan(): Promise<LibraryScan> {
|
|
99
|
+
if (scan) {
|
|
100
|
+
return scan;
|
|
101
|
+
}
|
|
102
|
+
// Coalesce: five layouts and a command can all ask at once on the first paint, and the answer
|
|
103
|
+
// Is one walk of the project, not six.
|
|
104
|
+
inFlight ??= scanLibrary(projectDirs(), getPlatform()).then((result) => {
|
|
105
|
+
scan = result;
|
|
106
|
+
inFlight = null;
|
|
107
|
+
return result;
|
|
108
|
+
});
|
|
109
|
+
return inFlight;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
capabilities: { delete: false, insert: false, remotePaging: false, remoteSort: false },
|
|
114
|
+
columns: () => Promise.resolve(libraryColumns()),
|
|
115
|
+
commit(batch: GridEditBatch): Promise<CommitResult> {
|
|
116
|
+
return Promise.resolve({
|
|
117
|
+
cells: batch.cells.map((cell) => ({
|
|
118
|
+
error: LIBRARY_READ_ONLY,
|
|
119
|
+
field: cell.field,
|
|
120
|
+
ok: false,
|
|
121
|
+
rowKey: cell.rowKey,
|
|
122
|
+
})),
|
|
123
|
+
deletes: batch.deletes.map((row) => ({
|
|
124
|
+
error: LIBRARY_READ_ONLY,
|
|
125
|
+
ok: false,
|
|
126
|
+
rowKey: row.rowKey,
|
|
127
|
+
})),
|
|
128
|
+
inserts: batch.inserts.map((row) => ({
|
|
129
|
+
error: LIBRARY_READ_ONLY,
|
|
130
|
+
ok: false,
|
|
131
|
+
tempKey: row.tempKey,
|
|
132
|
+
})),
|
|
133
|
+
});
|
|
134
|
+
},
|
|
135
|
+
failures: () => scan?.failures ?? [],
|
|
136
|
+
files: () => scan?.files ?? [],
|
|
137
|
+
id: libraryTabId(),
|
|
138
|
+
label: "Library",
|
|
139
|
+
async refresh() {
|
|
140
|
+
scan = null;
|
|
141
|
+
inFlight = null;
|
|
142
|
+
await ensureScan();
|
|
143
|
+
},
|
|
144
|
+
async rows(): Promise<GridRowsResult> {
|
|
145
|
+
const result = await ensureScan();
|
|
146
|
+
return { rows: result.files.map((file) => libraryRow(file)), total: result.files.length };
|
|
147
|
+
},
|
|
148
|
+
scanned: () => scan !== null,
|
|
149
|
+
};
|
|
150
|
+
}
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* multiple canvas-related modules: element lookup, zoom, panel resolution, inline bubbling.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
8
|
+
import { getNodeAtPath, parentElementPath } from "../store";
|
|
9
|
+
import { displayTagName } from "@jxsuite/schema/guards";
|
|
10
|
+
import { activeCanvasSurface, activeMediaOfPane } from "./canvas-surface";
|
|
10
11
|
import { isInlineInContext } from "../editor/inline-edit";
|
|
12
|
+
import type { CanvasSurface } from "./canvas-surface";
|
|
13
|
+
import type { CanvasPanel } from "../types";
|
|
11
14
|
import type { JxPath } from "../state";
|
|
12
15
|
import type { JxMutableNode } from "@jxsuite/schema/types";
|
|
13
16
|
|
|
@@ -22,15 +25,26 @@ export function panelMediaToActiveMedia(mediaName: string | null | undefined) {
|
|
|
22
25
|
return !mediaName || mediaName === "base" ? null : mediaName;
|
|
23
26
|
}
|
|
24
27
|
|
|
25
|
-
/**
|
|
26
|
-
|
|
28
|
+
/**
|
|
29
|
+
* The active canvas panel of ONE pane's stage, from that pane's own `activeMedia`.
|
|
30
|
+
*
|
|
31
|
+
* The breakpoint a stage is showing is a fact about the tab in it — `session.ui.activeMedia` — so
|
|
32
|
+
* this reads it from `surface.paneId`'s tab and never from the focus. {@link getActivePanel} is this
|
|
33
|
+
* function applied to the focused pane, which is what "the active panel" means when nobody says
|
|
34
|
+
* which one.
|
|
35
|
+
*
|
|
36
|
+
* @param {CanvasSurface} surface
|
|
37
|
+
* @returns {CanvasPanel | null}
|
|
38
|
+
*/
|
|
39
|
+
export function panelOfSurface(surface: CanvasSurface): CanvasPanel | null {
|
|
40
|
+
const canvasPanels = surface.panels;
|
|
27
41
|
if (canvasPanels.length === 0) {
|
|
28
42
|
return null;
|
|
29
43
|
}
|
|
30
44
|
if (canvasPanels.length === 1) {
|
|
31
|
-
return canvasPanels[0]
|
|
45
|
+
return canvasPanels[0]!;
|
|
32
46
|
}
|
|
33
|
-
const activeMedia =
|
|
47
|
+
const activeMedia = activeMediaOfPane(surface.paneId);
|
|
34
48
|
for (const p of canvasPanels) {
|
|
35
49
|
if (activeMedia === null && (p.mediaName === "base" || p.mediaName === null)) {
|
|
36
50
|
return p;
|
|
@@ -39,7 +53,17 @@ export function getActivePanel() {
|
|
|
39
53
|
return p;
|
|
40
54
|
}
|
|
41
55
|
}
|
|
42
|
-
return canvasPanels[0]
|
|
56
|
+
return canvasPanels[0]!;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Return the active canvas panel of the FOCUSED pane, based on its activeMedia setting.
|
|
61
|
+
*
|
|
62
|
+
* Panels belong to a pane's stage (`canvas-surface.ts`), so "the active panel" is only answerable
|
|
63
|
+
* relative to one — this is the pane whose artboards the person is looking at.
|
|
64
|
+
*/
|
|
65
|
+
export function getActivePanel() {
|
|
66
|
+
return panelOfSurface(activeCanvasSurface());
|
|
43
67
|
}
|
|
44
68
|
|
|
45
69
|
/**
|
|
@@ -61,8 +85,8 @@ export function bubbleInlinePath(doc: JxMutableNode | undefined, path: JxPath) {
|
|
|
61
85
|
if (!node || !parentNode) {
|
|
62
86
|
break;
|
|
63
87
|
}
|
|
64
|
-
const childTag = (node.tagName
|
|
65
|
-
const parentTag = (parentNode.tagName
|
|
88
|
+
const childTag = (displayTagName(node.tagName) || "div").toLowerCase();
|
|
89
|
+
const parentTag = (displayTagName(parentNode.tagName) || "div").toLowerCase();
|
|
66
90
|
if (!isInlineInContext(childTag, parentTag)) {
|
|
67
91
|
break;
|
|
68
92
|
}
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { projectState, stripEventHandlers } from "../store";
|
|
10
|
-
import {
|
|
10
|
+
import { displayTagName } from "@jxsuite/schema/guards";
|
|
11
|
+
import { canvasModeOfTab } from "./canvas-surface";
|
|
11
12
|
import { toRaw } from "../reactivity";
|
|
12
13
|
import {
|
|
13
14
|
distributePageIntoLayout,
|
|
@@ -29,13 +30,13 @@ import {
|
|
|
29
30
|
import { isComponentDoc, substitutePreviewProps } from "../component-props";
|
|
30
31
|
import { rewriteContentAssets } from "./content-assets";
|
|
31
32
|
|
|
32
|
-
import type { JxElement, JxMutableNode } from "@jxsuite/schema/types";
|
|
33
|
+
import type { JxElement, JxMutableNode, JxPath } from "@jxsuite/schema/types";
|
|
33
34
|
import type { ComponentEntry } from "../files/components.js";
|
|
34
35
|
import type { ContentSectionEntry } from "../types";
|
|
36
|
+
import type { LayoutMarker } from "./path-mapping";
|
|
37
|
+
import type { Tab } from "../tabs/tab";
|
|
35
38
|
import type { WireMapperCtx } from "./iframe-protocol";
|
|
36
39
|
|
|
37
|
-
let _ctx: { getCanvasMode: () => string } | null = null;
|
|
38
|
-
|
|
39
40
|
/**
|
|
40
41
|
* Walk the merged document tree to find where page children were distributed into the layout slot.
|
|
41
42
|
* Returns the `prefix` path to the container whose children hold the page content (first container
|
|
@@ -77,58 +78,71 @@ function findPageContentPrefix(
|
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/**
|
|
80
|
-
* Recursively mark
|
|
81
|
-
*
|
|
81
|
+
* Recursively mark every node of a layout doc tree with its ORIGIN — `$__layout: { file, path }` —
|
|
82
|
+
* so the renderer can both tell layout nodes apart from page content and say which node of which
|
|
83
|
+
* file each one is. The marker used to be a bare `true`, which made a layout node identifiable but
|
|
84
|
+
* unaddressable: the canvas could tell you had clicked something it could not edit, but not what
|
|
85
|
+
* you had clicked or where to go to edit it. `path` is the node's own path inside `file`, so the
|
|
86
|
+
* editor can open the layout with that node selected. See {@link LayoutMarker}.
|
|
82
87
|
*/
|
|
83
|
-
function markLayoutNodes(node: JxMutableNode) {
|
|
88
|
+
export function markLayoutNodes(node: JxMutableNode, file: string, path: JxPath = []) {
|
|
84
89
|
if (!node || typeof node !== "object") {
|
|
85
90
|
return;
|
|
86
91
|
}
|
|
87
|
-
node.$__layout =
|
|
92
|
+
node.$__layout = { file, path } satisfies LayoutMarker;
|
|
88
93
|
if (Array.isArray(node.children)) {
|
|
89
|
-
for (const child of node.children) {
|
|
94
|
+
for (const [i, child] of node.children.entries()) {
|
|
90
95
|
if (typeof child === "string") {
|
|
91
96
|
continue;
|
|
92
97
|
}
|
|
93
|
-
markLayoutNodes(child);
|
|
98
|
+
markLayoutNodes(child, file, [...path, "children", i]);
|
|
94
99
|
}
|
|
95
100
|
}
|
|
96
|
-
if (node.$elements) {
|
|
97
|
-
for (const el of node.$elements) {
|
|
101
|
+
if (Array.isArray(node.$elements)) {
|
|
102
|
+
for (const [i, el] of node.$elements.entries()) {
|
|
98
103
|
if (typeof el !== "string") {
|
|
99
|
-
markLayoutNodes(el as JxMutableNode);
|
|
104
|
+
markLayoutNodes(el as JxMutableNode, file, [...path, "$elements", i]);
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
}
|
|
104
109
|
|
|
105
|
-
/**
|
|
106
|
-
* Initialize the canvas live render module.
|
|
107
|
-
*
|
|
108
|
-
* @param {{
|
|
109
|
-
* getCanvasMode: () => string;
|
|
110
|
-
* }} ctx
|
|
111
|
-
*/
|
|
112
|
-
export function initCanvasLiveRender(ctx: { getCanvasMode: () => string }) {
|
|
113
|
-
_ctx = ctx;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
110
|
/**
|
|
117
111
|
* Resolve a document into the form the iframe canvas renders: layout-distributed, edit-transformed,
|
|
118
112
|
* with components/imports/$media/$head merged in, plus the path-mapper context and site style. This
|
|
119
113
|
* is the "parent resolves, iframe renders" split — the realm-specific work (defineElement,
|
|
120
114
|
* injecting $head/site-style into the DOM, buildScope/renderNode) happens inside the iframe from
|
|
121
115
|
* this result.
|
|
116
|
+
*
|
|
117
|
+
* **`tab` is the tab the render is FOR, and it is a parameter because it is not "the active tab".**
|
|
118
|
+
* This function opened with `const tab = activeTab.value` and took its mode from an injected
|
|
119
|
+
* `getCanvasMode()` — the focused pane's, one layer down — which threw away everything the caller
|
|
120
|
+
* had already resolved correctly: `canvas-render.ts` picks the document with
|
|
121
|
+
* `tabOfPane(surface.paneId)` and threads that tab's id through `mountIframeCanvas` →
|
|
122
|
+
* `preparePassRender`. Six values came from the wrong tab whenever the pane being drawn was not the
|
|
123
|
+
* focused one: `documentPath` (hence `docBase` and the `isPage` test), `showLayout`,
|
|
124
|
+
* `previewParams`, `previewProps`, and the composed `canvasMode`.
|
|
125
|
+
*
|
|
126
|
+
* The mode is the one with teeth. `mountIframeCanvas` ends with `setHostPreview(state, message.mode
|
|
127
|
+
* === "preview")`, so a side pane in Edit whose neighbour was in Preview got a PREVIEW frame — no
|
|
128
|
+
* overlay, no editing messages honoured — and a pane being previewed beside an editing one got an
|
|
129
|
+
* editable frame for a document nobody was editing. `canvasModeOfTab(tab)` is the same composition
|
|
130
|
+
* every other gate reads, applied to the tab that is actually being drawn.
|
|
131
|
+
*
|
|
132
|
+
* @param {JxMutableNode} doc
|
|
133
|
+
* @param {Tab | null} tab — the tab whose document and view settings this render is of.
|
|
122
134
|
*/
|
|
123
|
-
export async function resolveCanvasDocument(
|
|
135
|
+
export async function resolveCanvasDocument(
|
|
136
|
+
doc: JxMutableNode,
|
|
137
|
+
tab: Tab | null,
|
|
138
|
+
): Promise<{
|
|
124
139
|
renderDoc: JxMutableNode;
|
|
125
140
|
docBase: string | undefined;
|
|
126
141
|
mapperCtx: WireMapperCtx;
|
|
127
142
|
siteStyle: Record<string, unknown> | null;
|
|
128
143
|
}> {
|
|
129
|
-
const tab = activeTab.value;
|
|
130
144
|
const S = { documentPath: tab?.documentPath, mode: tab?.doc.mode };
|
|
131
|
-
const canvasMode =
|
|
145
|
+
const canvasMode = canvasModeOfTab(tab);
|
|
132
146
|
|
|
133
147
|
let renderDoc =
|
|
134
148
|
canvasMode === "preview"
|
|
@@ -150,7 +164,9 @@ export async function resolveCanvasDocument(doc: JxMutableNode): Promise<{
|
|
|
150
164
|
if (layoutPath) {
|
|
151
165
|
const layoutDoc = (await resolveLayoutDoc(layoutPath)) as JxMutableNode | null;
|
|
152
166
|
if (layoutDoc) {
|
|
153
|
-
|
|
167
|
+
// The SAME normalization resolveLayoutDoc reads the file with, so the marker's `file` is a
|
|
168
|
+
// Path the editor can hand straight to navigateToComponent when the author asks to open it.
|
|
169
|
+
markLayoutNodes(layoutDoc, layoutPath.replace(/^\.\//, ""));
|
|
154
170
|
const pageForSlots = canvasMode === "preview" ? structuredClone(toRaw(doc)) : renderDoc;
|
|
155
171
|
const merged = distributePageIntoLayout(layoutDoc, pageForSlots);
|
|
156
172
|
renderDoc =
|
|
@@ -262,7 +278,7 @@ export async function resolveCanvasDocument(doc: JxMutableNode): Promise<{
|
|
|
262
278
|
return tags;
|
|
263
279
|
}
|
|
264
280
|
if (node.tagName) {
|
|
265
|
-
tags.add(node.tagName);
|
|
281
|
+
tags.add(displayTagName(node.tagName));
|
|
266
282
|
}
|
|
267
283
|
if (Array.isArray(node.children)) {
|
|
268
284
|
for (const child of node.children) {
|