@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/tabs/transact.ts
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
import { toRaw } from "../reactivity";
|
|
3
3
|
import { jsonClone } from "../utils/studio-utils";
|
|
4
|
-
import { childIndex, getNodeAtPath,
|
|
4
|
+
import { childIndex, getNodeAtPath, parentElementPath, pathsEqual } from "../state";
|
|
5
|
+
import {
|
|
6
|
+
cloneSelection,
|
|
7
|
+
isSpliceablePath,
|
|
8
|
+
primarySelection,
|
|
9
|
+
pruneSelection,
|
|
10
|
+
structuralBatch,
|
|
11
|
+
} from "./selection";
|
|
5
12
|
import { applyDocOpToDoc, childArray, cloneValue } from "./doc-op-apply";
|
|
6
13
|
import {
|
|
7
14
|
beginRecording,
|
|
@@ -125,13 +132,68 @@ export function setTransactGate(fn: TransactGate | null) {
|
|
|
125
132
|
_transactGate = fn;
|
|
126
133
|
}
|
|
127
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Undo a transaction that threw part-way through, using the pairs it managed to record.
|
|
137
|
+
*
|
|
138
|
+
* **Why the inverses are enough.** Every mutator records its op AFTER its splice or assignment
|
|
139
|
+
* succeeds, so `record.docOps` describes exactly the changes that landed — no more, no less.
|
|
140
|
+
* Replaying their inverses in reverse is the same replay `undo` performs on a committed entry, run
|
|
141
|
+
* against a document that never got a history entry. A mutator that changed the document WITHOUT
|
|
142
|
+
* recording is already broken for undo; this is no weaker a guarantee than the one history gives.
|
|
143
|
+
*
|
|
144
|
+
* The document root reference is replaced so every effect re-reads, and nothing is marked consumed
|
|
145
|
+
* — the canvas takes the full-render path, because the patch ops recorded for the half-applied
|
|
146
|
+
* state describe changes that no longer exist.
|
|
147
|
+
*/
|
|
148
|
+
function rollbackFailedTransaction(
|
|
149
|
+
tab: Tab,
|
|
150
|
+
record: TransactionRecord,
|
|
151
|
+
selectionBefore: JxPath[],
|
|
152
|
+
dirtyBefore: boolean,
|
|
153
|
+
) {
|
|
154
|
+
for (let i = record.docOps.length - 1; i >= 0; i--) {
|
|
155
|
+
applyDocOpToDoc(tab.doc.document, toRaw(record.docOps[i]!.inverse) as JxDocOp);
|
|
156
|
+
}
|
|
157
|
+
for (let i = record.fmOps.length - 1; i >= 0; i--) {
|
|
158
|
+
const op = record.fmOps[i]!;
|
|
159
|
+
applyFmState(tab, op.field, op.before);
|
|
160
|
+
}
|
|
161
|
+
tab.doc.document = { ...toRaw(tab.doc.document) };
|
|
162
|
+
// Mutators repair the selection as they go (`pruneSelection`, "select the clone"); rolling their
|
|
163
|
+
// Edits back and leaving the selection pointing at coordinates that no longer exist would be its
|
|
164
|
+
// Own corruption.
|
|
165
|
+
tab.session.selection = selectionBefore;
|
|
166
|
+
// `mutateUpdateFrontmatter` marks the tab dirty itself, mid-mutation. Rolling its write back and
|
|
167
|
+
// Leaving the mark standing would report unsaved changes that no longer exist; restoring the
|
|
168
|
+
// Captured value is right in both directions, since nothing but this transaction could have
|
|
169
|
+
// Moved it.
|
|
170
|
+
tab.doc.dirty = dirtyBefore;
|
|
171
|
+
}
|
|
172
|
+
|
|
128
173
|
/**
|
|
129
174
|
* Apply a document mutation transactionally: push to history and mark dirty. The mutationFn
|
|
130
175
|
* receives the tab and should mutate tab.doc.document in place.
|
|
131
176
|
*
|
|
177
|
+
* **All of it or none of it.** A mutationFn that throws part-way — a batch whose third splice hits
|
|
178
|
+
* a coordinate that is not one — used to leave the earlier splices standing in the live reactive
|
|
179
|
+
* tree while the throw skipped the new document reference, the history entry, the dirty mark and
|
|
180
|
+
* the collab observer: the document was changed on screen, unreachable by undo, and invisible to
|
|
181
|
+
* Save. The exception now rolls the applied ops back ({@link rollbackFailedTransaction}) and only
|
|
182
|
+
* then rethrows, so a failed transaction is indistinguishable from one that never ran.
|
|
183
|
+
*
|
|
184
|
+
* **AND IT SAYS WHETHER IT WROTE**, because {@link setTransactGate} made "returned normally" and
|
|
185
|
+
* "changed the document" two different facts and nothing downstream could tell them apart. Under
|
|
186
|
+
* the source-canonical freeze the dock's body commit is refused here — and its caller went on to
|
|
187
|
+
* mark the buffer settled, so `tabBufferUnsaved`, `shouldWarnOnClose` and `hasUnsavedTabs` all
|
|
188
|
+
* reported "nothing to lose" about text that only existed in the buffer. Every close then took it
|
|
189
|
+
* with no prompt. A refusal is not an exception (it is an ordinary, expected state of the room), so
|
|
190
|
+
* it is a return value: `false` means the document is exactly as it was.
|
|
191
|
+
*
|
|
132
192
|
* @param {Tab | null} tab
|
|
133
193
|
* @param {(tab: Tab) => void} mutationFn
|
|
134
194
|
* @param {{ skipHistory?: boolean; origin?: TransactOrigin }} [opts]
|
|
195
|
+
* @returns {boolean} True when the mutation was applied; false when there was no tab, or the gate
|
|
196
|
+
* refused it.
|
|
135
197
|
*/
|
|
136
198
|
export function transactDoc(
|
|
137
199
|
tab: Tab | null,
|
|
@@ -141,21 +203,31 @@ export function transactDoc(
|
|
|
141
203
|
origin = "user",
|
|
142
204
|
coalesceKey = null,
|
|
143
205
|
}: { skipHistory?: boolean; origin?: TransactOrigin; coalesceKey?: string | null } = {},
|
|
144
|
-
) {
|
|
206
|
+
): boolean {
|
|
145
207
|
if (!tab) {
|
|
146
|
-
return;
|
|
208
|
+
return false;
|
|
147
209
|
}
|
|
148
210
|
if (origin !== "remote" && _transactGate?.(tab, origin)) {
|
|
149
|
-
return;
|
|
211
|
+
return false;
|
|
150
212
|
}
|
|
151
|
-
const selectionBefore = tab.session.selection
|
|
213
|
+
const selectionBefore = cloneSelection(tab.session.selection);
|
|
214
|
+
const dirtyBefore = tab.doc.dirty;
|
|
152
215
|
beginRecording();
|
|
153
216
|
let record: TransactionRecord;
|
|
217
|
+
let failure: { error: unknown } | null = null;
|
|
154
218
|
try {
|
|
155
219
|
mutationFn(tab);
|
|
220
|
+
} catch (error) {
|
|
221
|
+
// Boxed rather than tested for truthiness: a mutation is free to throw a falsy value, and
|
|
222
|
+
// "did it throw" must not become "was what it threw interesting".
|
|
223
|
+
failure = { error };
|
|
156
224
|
} finally {
|
|
157
225
|
record = endRecording();
|
|
158
226
|
}
|
|
227
|
+
if (failure) {
|
|
228
|
+
rollbackFailedTransaction(tab, record, selectionBefore, dirtyBefore);
|
|
229
|
+
throw failure.error;
|
|
230
|
+
}
|
|
159
231
|
|
|
160
232
|
// Replace the document root reference so effects tracking tab.doc.document re-trigger.
|
|
161
233
|
// Nested objects are shared — the mutation already modified them in place.
|
|
@@ -174,7 +246,7 @@ export function transactDoc(
|
|
|
174
246
|
? { patchable: false, reason: "unrecorded-mutation" }
|
|
175
247
|
: consumer.classify(tab, record.ops);
|
|
176
248
|
if (verdict.patchable) {
|
|
177
|
-
consumer!.markConsumed(newRef);
|
|
249
|
+
consumer!.markConsumed(newRef, tab);
|
|
178
250
|
}
|
|
179
251
|
|
|
180
252
|
tab.doc.document = newRef;
|
|
@@ -185,6 +257,7 @@ export function transactDoc(
|
|
|
185
257
|
} catch (error) {
|
|
186
258
|
consumer!.escalate(
|
|
187
259
|
`patch-apply-failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
260
|
+
tab,
|
|
188
261
|
);
|
|
189
262
|
}
|
|
190
263
|
}
|
|
@@ -196,6 +269,7 @@ export function transactDoc(
|
|
|
196
269
|
tab.doc.dirty = true;
|
|
197
270
|
|
|
198
271
|
_transactObserver?.(tab, record, origin);
|
|
272
|
+
return true;
|
|
199
273
|
}
|
|
200
274
|
|
|
201
275
|
/**
|
|
@@ -207,13 +281,13 @@ export function transactDoc(
|
|
|
207
281
|
* @param {Tab} tab
|
|
208
282
|
* @param {JxMutableNode} raw Post-mutation raw document
|
|
209
283
|
* @param {TransactionRecord} record
|
|
210
|
-
* @param {JxPath
|
|
284
|
+
* @param {JxPath[]} selectionBefore
|
|
211
285
|
*/
|
|
212
286
|
function pushHistoryEntry(
|
|
213
287
|
tab: Tab,
|
|
214
288
|
raw: JxMutableNode,
|
|
215
289
|
record: TransactionRecord,
|
|
216
|
-
selectionBefore: JxPath
|
|
290
|
+
selectionBefore: JxPath[],
|
|
217
291
|
coalesceKey: string | null = null,
|
|
218
292
|
) {
|
|
219
293
|
const truncated = tab.history.snapshots.slice(0, tab.history.index + 1);
|
|
@@ -234,7 +308,7 @@ function pushHistoryEntry(
|
|
|
234
308
|
// Leaving the node holding both keys at once. Undo replays inverses in reverse order, so
|
|
235
309
|
// Appending yields "undo the last commit, then the one before it, …".
|
|
236
310
|
prev.inverseOps = [...prev.inverseOps, ...record.docOps.map((pair) => pair.inverse)];
|
|
237
|
-
prev.selection = tab.session.selection
|
|
311
|
+
prev.selection = cloneSelection(tab.session.selection);
|
|
238
312
|
if (prev.document) {
|
|
239
313
|
prev.document = jsonClone(raw);
|
|
240
314
|
}
|
|
@@ -250,7 +324,7 @@ function pushHistoryEntry(
|
|
|
250
324
|
forwardOps: useOps ? record.docOps.map((p) => p.forward) : null,
|
|
251
325
|
inverseOps: useOps ? record.docOps.map((p) => p.inverse) : null,
|
|
252
326
|
coalesceKey,
|
|
253
|
-
selection: tab.session.selection
|
|
327
|
+
selection: cloneSelection(tab.session.selection),
|
|
254
328
|
selectionBefore,
|
|
255
329
|
});
|
|
256
330
|
if (truncated.length > HISTORY_LIMIT) {
|
|
@@ -299,13 +373,14 @@ function materializeState(
|
|
|
299
373
|
* @param {Tab | null} tab
|
|
300
374
|
* @param {(doc: JxMutableNode) => void} fn
|
|
301
375
|
* @param {{ skipHistory?: boolean; origin?: TransactOrigin }} [opts]
|
|
376
|
+
* @returns {boolean} Whatever {@link transactDoc} answered — whether the mutation was applied.
|
|
302
377
|
*/
|
|
303
378
|
export function transact(
|
|
304
379
|
tab: Tab | null,
|
|
305
380
|
fn: (doc: JxMutableNode) => void,
|
|
306
381
|
opts?: { skipHistory?: boolean; origin?: TransactOrigin },
|
|
307
|
-
) {
|
|
308
|
-
transactDoc(tab, (t) => fn(t.doc.document), opts);
|
|
382
|
+
): boolean {
|
|
383
|
+
return transactDoc(tab, (t) => fn(t.doc.document), opts);
|
|
309
384
|
}
|
|
310
385
|
|
|
311
386
|
/**
|
|
@@ -424,7 +499,7 @@ export function endBatch() {
|
|
|
424
499
|
const raw = toRaw(tab.doc.document);
|
|
425
500
|
const snapshot = {
|
|
426
501
|
document: jsonClone(raw),
|
|
427
|
-
selection: tab.session.selection
|
|
502
|
+
selection: cloneSelection(tab.session.selection),
|
|
428
503
|
};
|
|
429
504
|
const truncated = tab.history.snapshots.slice(0, tab.history.index + 1);
|
|
430
505
|
truncated.push(snapshot);
|
|
@@ -444,6 +519,19 @@ export function isBatching(): boolean {
|
|
|
444
519
|
return _batchTab !== null;
|
|
445
520
|
}
|
|
446
521
|
|
|
522
|
+
/**
|
|
523
|
+
* Which tab the open batch belongs to, or `null` when none is open.
|
|
524
|
+
*
|
|
525
|
+
* The agent loop needs this to keep a batch honest across a tab change. A batch is opened against
|
|
526
|
+
* one tab and closed against the same one — {@link endBatch} pushes ITS history snapshot and fires
|
|
527
|
+
* the collab notifier for IT — so a loop whose tools moved to a second document has to close the
|
|
528
|
+
* first batch and open a second, or the second document's edits get neither a history entry nor a
|
|
529
|
+
* collab publish. Reading the batched tab is how the loop can tell.
|
|
530
|
+
*/
|
|
531
|
+
export function batchTab(): Tab | null {
|
|
532
|
+
return _batchTab;
|
|
533
|
+
}
|
|
534
|
+
|
|
447
535
|
// ─── Undo / Redo ─────────────────────────────────────────────────────────────
|
|
448
536
|
|
|
449
537
|
/**
|
|
@@ -487,7 +575,7 @@ function restoreState(tab: Tab, index: number) {
|
|
|
487
575
|
const { snapshots } = tab.history;
|
|
488
576
|
tab.doc.document = materializeState(snapshots, index);
|
|
489
577
|
const snap = snapshots[index]!;
|
|
490
|
-
tab.session.selection =
|
|
578
|
+
tab.session.selection = cloneSelection(toRaw(snap.selection));
|
|
491
579
|
}
|
|
492
580
|
|
|
493
581
|
/** Debug-flag assertion: ops-based undo/redo must land on the same state replay produces. */
|
|
@@ -524,7 +612,7 @@ export function undo(tab: Tab) {
|
|
|
524
612
|
if ((inverseOps && inverseOps.length > 0) || (fmOps && fmOps.length > 0)) {
|
|
525
613
|
// Surgical path: apply inverse ops through the normal transaction pipeline (the canvas
|
|
526
614
|
// Patches in place when it can), in reverse recording order.
|
|
527
|
-
transactDoc(
|
|
615
|
+
const applied = transactDoc(
|
|
528
616
|
tab,
|
|
529
617
|
(t) => {
|
|
530
618
|
if (inverseOps) {
|
|
@@ -541,7 +629,15 @@ export function undo(tab: Tab) {
|
|
|
541
629
|
},
|
|
542
630
|
{ origin: "history", skipHistory: true },
|
|
543
631
|
);
|
|
544
|
-
|
|
632
|
+
/* A REFUSED REPLAY IS NOT A STEP TAKEN. `"history"` is not `"remote"`, so the gate refuses this
|
|
633
|
+
exactly as it refuses a direct edit — and everything below assumed the ops landed. Moving the
|
|
634
|
+
index over a document that did not change desynchronises the log from the tree: the next redo
|
|
635
|
+
replays a forward op onto a state it was never the inverse of, and `doc.dirty` claims an edit
|
|
636
|
+
nobody made. Undo that cannot run is undo that does nothing, which is the honest answer. */
|
|
637
|
+
if (!applied) {
|
|
638
|
+
return;
|
|
639
|
+
}
|
|
640
|
+
tab.session.selection = cloneSelection(toRaw(entry.selectionBefore ?? []));
|
|
545
641
|
tab.history.index -= 1;
|
|
546
642
|
assertHistoryConsistency(tab, tab.history.index);
|
|
547
643
|
} else {
|
|
@@ -565,7 +661,7 @@ export function redo(tab: Tab) {
|
|
|
565
661
|
const forwardOps = patchHistoryEnabled() ? entry.forwardOps : null;
|
|
566
662
|
const fmOps = patchHistoryEnabled() ? entry.fmOps : null;
|
|
567
663
|
if ((forwardOps && forwardOps.length > 0) || (fmOps && fmOps.length > 0)) {
|
|
568
|
-
transactDoc(
|
|
664
|
+
const applied = transactDoc(
|
|
569
665
|
tab,
|
|
570
666
|
(t) => {
|
|
571
667
|
if (forwardOps) {
|
|
@@ -582,7 +678,12 @@ export function redo(tab: Tab) {
|
|
|
582
678
|
},
|
|
583
679
|
{ origin: "history", skipHistory: true },
|
|
584
680
|
);
|
|
585
|
-
|
|
681
|
+
// Same as {@link undo}: a refused replay leaves the document alone, so the index must stay
|
|
682
|
+
// Where it is or the log stops describing the tree.
|
|
683
|
+
if (!applied) {
|
|
684
|
+
return;
|
|
685
|
+
}
|
|
686
|
+
tab.session.selection = cloneSelection(toRaw(entry.selection));
|
|
586
687
|
tab.history.index += 1;
|
|
587
688
|
assertHistoryConsistency(tab, tab.history.index);
|
|
588
689
|
} else {
|
|
@@ -619,11 +720,18 @@ export function mutateInsertNode(
|
|
|
619
720
|
}
|
|
620
721
|
|
|
621
722
|
/**
|
|
723
|
+
* Remove the node at `path` from its parent's children array.
|
|
724
|
+
*
|
|
725
|
+
* The guard is {@link isSpliceablePath}, not `path.length < 2`: the shorter test admits the
|
|
726
|
+
* Outline's map-template and `$switch`-case rows, whose `parentElementPath` is a children ARRAY
|
|
727
|
+
* rather than a node — `.children.splice` on it throws, and the throw is what left a batch's
|
|
728
|
+
* earlier removals applied and unrecorded.
|
|
729
|
+
*
|
|
622
730
|
* @param {Tab} tab
|
|
623
731
|
* @param {JxPath} path
|
|
624
732
|
*/
|
|
625
733
|
export function mutateRemoveNode(tab: Tab, path: JxPath) {
|
|
626
|
-
if (!path || path
|
|
734
|
+
if (!path || !isSpliceablePath(path)) {
|
|
627
735
|
return;
|
|
628
736
|
}
|
|
629
737
|
const elemPath = parentElementPath(path) as JxPath;
|
|
@@ -637,9 +745,7 @@ export function mutateRemoveNode(tab: Tab, path: JxPath) {
|
|
|
637
745
|
forward: { index: idx, op: "remove-child", parentPath: elemPath },
|
|
638
746
|
inverse: { index: idx, node: cloneValue(removed), op: "insert-child", parentPath: elemPath },
|
|
639
747
|
});
|
|
640
|
-
|
|
641
|
-
tab.session.selection = null;
|
|
642
|
-
}
|
|
748
|
+
tab.session.selection = pruneSelection(tab.session.selection, path);
|
|
643
749
|
}
|
|
644
750
|
|
|
645
751
|
/**
|
|
@@ -647,7 +753,7 @@ export function mutateRemoveNode(tab: Tab, path: JxPath) {
|
|
|
647
753
|
* @param {JxPath} path
|
|
648
754
|
*/
|
|
649
755
|
export function mutateDuplicateNode(tab: Tab, path: JxPath) {
|
|
650
|
-
if (!path || path
|
|
756
|
+
if (!path || !isSpliceablePath(path)) {
|
|
651
757
|
return;
|
|
652
758
|
}
|
|
653
759
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
@@ -663,7 +769,66 @@ export function mutateDuplicateNode(tab: Tab, path: JxPath) {
|
|
|
663
769
|
forward: { index: idx + 1, node: cloneValue(clone), op: "insert-child", parentPath: elemPath },
|
|
664
770
|
inverse: { index: idx + 1, op: "remove-child", parentPath: elemPath },
|
|
665
771
|
});
|
|
666
|
-
tab.session.selection = [...elemPath, "children", idx + 1];
|
|
772
|
+
tab.session.selection = [[...elemPath, "children", idx + 1]];
|
|
773
|
+
}
|
|
774
|
+
|
|
775
|
+
/**
|
|
776
|
+
* Remove every selected node, in ONE transaction and therefore ONE undo step (§6.5).
|
|
777
|
+
*
|
|
778
|
+
* Three things make a batch different from a loop. `structuralBatch` drops paths contained by
|
|
779
|
+
* another selected path — deleting a `<section>` and a paragraph inside it is one deletion — drops
|
|
780
|
+
* paths that name no splice coordinate at all (the document element, a repeater's map template, a
|
|
781
|
+
* `$switch` case, each of which the Outline offers as a ctrl-clickable row), and orders what
|
|
782
|
+
* survives LAST-node-first, so each splice happens above coordinates no later step depends on. A
|
|
783
|
+
* forward loop would delete `children/0` and then `children/1`, which by then is the node that used
|
|
784
|
+
* to be `children/2`.
|
|
785
|
+
*
|
|
786
|
+
* Selection repair is `mutateRemoveNode`'s, applied once per removal: a path is dropped when the
|
|
787
|
+
* node it addresses has gone. With a single path in, that is exactly the old behaviour — the
|
|
788
|
+
* selection ends up empty.
|
|
789
|
+
*
|
|
790
|
+
* @param {Tab} tab
|
|
791
|
+
* @param {readonly JxPath[]} paths
|
|
792
|
+
*/
|
|
793
|
+
export function mutateRemoveNodes(tab: Tab, paths: readonly JxPath[]) {
|
|
794
|
+
for (const path of structuralBatch(paths)) {
|
|
795
|
+
mutateRemoveNode(tab, path);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
/**
|
|
800
|
+
* Duplicate every selected node in one transaction, selecting the clones.
|
|
801
|
+
*
|
|
802
|
+
* Same descending order, for the same reason: inserting a clone at `idx + 1` renumbers every later
|
|
803
|
+
* sibling, so the batch starts at the last. The resulting selection is the clones in document
|
|
804
|
+
* order, which means a duplicate-then-duplicate-again walks down the page the way repeating the
|
|
805
|
+
* single-node command always did.
|
|
806
|
+
*
|
|
807
|
+
* @param {Tab} tab
|
|
808
|
+
* @param {readonly JxPath[]} paths
|
|
809
|
+
*/
|
|
810
|
+
export function mutateDuplicateNodes(tab: Tab, paths: readonly JxPath[]) {
|
|
811
|
+
let clones: JxPath[] = [];
|
|
812
|
+
for (const path of structuralBatch(paths)) {
|
|
813
|
+
const before = tab.session.selection;
|
|
814
|
+
mutateDuplicateNode(tab, path);
|
|
815
|
+
const made = primarySelection(tab.session.selection);
|
|
816
|
+
// `mutateDuplicateNode` refuses a path that addresses nothing and leaves the selection alone;
|
|
817
|
+
// Only a real insertion contributes a clone.
|
|
818
|
+
if (!made || tab.session.selection === before) {
|
|
819
|
+
continue;
|
|
820
|
+
}
|
|
821
|
+
// Descending order keeps the ORIGINALS valid, but not the clones already made: this insertion
|
|
822
|
+
// Sits before them, so every one of their coordinates moved up by one. Translating them here
|
|
823
|
+
// Is the difference between "the copies are selected" and "two of the originals are".
|
|
824
|
+
const parentPath = parentElementPath(made) as JxPath;
|
|
825
|
+
const spliceIndex = childIndex(made) as number;
|
|
826
|
+
clones = clones.map((clone) => shiftPrefixedIndex(parentPath, clone, spliceIndex, 1, true));
|
|
827
|
+
clones.unshift(made);
|
|
828
|
+
}
|
|
829
|
+
if (clones.length > 0) {
|
|
830
|
+
tab.session.selection = clones;
|
|
831
|
+
}
|
|
667
832
|
}
|
|
668
833
|
|
|
669
834
|
/**
|
|
@@ -672,7 +837,7 @@ export function mutateDuplicateNode(tab: Tab, path: JxPath) {
|
|
|
672
837
|
* @param {string} wrapperTag
|
|
673
838
|
*/
|
|
674
839
|
export function mutateWrapNode(tab: Tab, path: JxPath, wrapperTag = "div") {
|
|
675
|
-
if (!path || path
|
|
840
|
+
if (!path || !isSpliceablePath(path)) {
|
|
676
841
|
return;
|
|
677
842
|
}
|
|
678
843
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
@@ -692,7 +857,7 @@ export function mutateWrapNode(tab: Tab, path: JxPath, wrapperTag = "div") {
|
|
|
692
857
|
forward: { index: idx, node: cloneValue(wrapper), op: "set-child", parentPath: elemPath },
|
|
693
858
|
inverse: { index: idx, node: original, op: "set-child", parentPath: elemPath },
|
|
694
859
|
});
|
|
695
|
-
tab.session.selection = [...elemPath, "children", idx];
|
|
860
|
+
tab.session.selection = [[...elemPath, "children", idx]];
|
|
696
861
|
}
|
|
697
862
|
|
|
698
863
|
/**
|
|
@@ -743,7 +908,7 @@ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath,
|
|
|
743
908
|
},
|
|
744
909
|
});
|
|
745
910
|
|
|
746
|
-
if (pathsEqual(tab.session.selection, fromPath)) {
|
|
911
|
+
if (pathsEqual(primarySelection(tab.session.selection), fromPath)) {
|
|
747
912
|
let idx = toIndex;
|
|
748
913
|
if (
|
|
749
914
|
fromParentPath.length === toParentPath.length &&
|
|
@@ -752,11 +917,24 @@ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath,
|
|
|
752
917
|
) {
|
|
753
918
|
idx = toIndex - 1;
|
|
754
919
|
}
|
|
755
|
-
tab.session.selection = [...toParentPath, "children", idx];
|
|
920
|
+
tab.session.selection = [[...toParentPath, "children", idx]];
|
|
756
921
|
}
|
|
757
922
|
}
|
|
758
923
|
|
|
759
924
|
/**
|
|
925
|
+
* Set (or, for an empty value, delete) one property on the node `path` names.
|
|
926
|
+
*
|
|
927
|
+
* **A path that no longer resolves is a no-op, not a throw.** `getNodeAtPath` answers `undefined`
|
|
928
|
+
* for a coordinate that has been deleted, and `node[key]` on that is `undefined is not an object` —
|
|
929
|
+
* an exception thrown from inside a mutation, which {@link transactDoc} correctly rolls back and
|
|
930
|
+
* rethrows into whatever asked for the edit. For a panel that is one stale click; for the dock's
|
|
931
|
+
* debounced body commit it escaped through `commitBufferWrites` and out of the dock's
|
|
932
|
+
* `afterRender`, aborting the repaint with the editor undisposed and its 500ms timer still armed.
|
|
933
|
+
* Deletions arrive from collaborators and from the author's own undo while an editor is open, so a
|
|
934
|
+
* stale path is an ordinary state of the document rather than a programming error, and the ordinary
|
|
935
|
+
* answer is to change nothing. Callers that must distinguish "wrote" from "nothing to write to"
|
|
936
|
+
* resolve the node themselves first — `panels/editors.ts`'s `bodyWriter` does exactly that.
|
|
937
|
+
*
|
|
760
938
|
* @param {Tab} tab
|
|
761
939
|
* @param {JxPath} path
|
|
762
940
|
* @param {string} key
|
|
@@ -764,6 +942,9 @@ export function mutateMoveNode(tab: Tab, fromPath: JxPath, toParentPath: JxPath,
|
|
|
764
942
|
*/
|
|
765
943
|
export function mutateUpdateProperty(tab: Tab, path: JxPath, key: string, value?: JxNodeValue) {
|
|
766
944
|
const node = getNodeAtPath(tab.doc.document, path);
|
|
945
|
+
if (!node) {
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
767
948
|
const prev = node[key];
|
|
768
949
|
const before = cloneValue(prev);
|
|
769
950
|
if (value === undefined || value === null || value === "") {
|
package/src/types.ts
CHANGED
|
@@ -38,6 +38,7 @@ import type {
|
|
|
38
38
|
ProjectListEntry,
|
|
39
39
|
ProjectSchemasResponse,
|
|
40
40
|
RecentProjectEntry,
|
|
41
|
+
ReferencesResult,
|
|
41
42
|
RenameResult,
|
|
42
43
|
SecretsSetRequest,
|
|
43
44
|
SecretsSetResponse,
|
|
@@ -85,6 +86,9 @@ export type {
|
|
|
85
86
|
ProjectSchemasResponse,
|
|
86
87
|
PullRequestInfo,
|
|
87
88
|
RecentProjectEntry,
|
|
89
|
+
ReferenceFile,
|
|
90
|
+
ReferenceHit,
|
|
91
|
+
ReferencesResult,
|
|
88
92
|
RenameResult,
|
|
89
93
|
SecretsListResponse,
|
|
90
94
|
SecretsSetRequest,
|
|
@@ -130,6 +134,28 @@ export type CreateProjectDestination =
|
|
|
130
134
|
| { kind: "path"; parent: string }
|
|
131
135
|
| { kind: "repo"; owner: string; repo: string; private: boolean };
|
|
132
136
|
|
|
137
|
+
/**
|
|
138
|
+
* What a site build reports back.
|
|
139
|
+
*
|
|
140
|
+
* `errors` is a list rather than a thrown exception because a partial build still produced pages:
|
|
141
|
+
* the author is better served by opening the page they asked for with the failures named beside it
|
|
142
|
+
* than by a refusal that says only that something went wrong.
|
|
143
|
+
*/
|
|
144
|
+
export interface SiteBuildResult {
|
|
145
|
+
routes: number;
|
|
146
|
+
files: number;
|
|
147
|
+
errors: string[];
|
|
148
|
+
/**
|
|
149
|
+
* Origin the built site is browsable at, e.g. `http://127.0.0.1:41234`.
|
|
150
|
+
*
|
|
151
|
+
* The backend names it because only the backend knows it: the built site is served on a port of
|
|
152
|
+
* its own, not on the editor's, since the editor's paths mean the project's SOURCES and a built
|
|
153
|
+
* page means its own output by the very same paths. Absent when the backend serves no preview,
|
|
154
|
+
* and `View: Open in Browser` then says so rather than guessing an origin.
|
|
155
|
+
*/
|
|
156
|
+
url?: string;
|
|
157
|
+
}
|
|
158
|
+
|
|
133
159
|
export interface StudioPlatform {
|
|
134
160
|
id: string;
|
|
135
161
|
projectRoot: string;
|
|
@@ -167,6 +193,20 @@ export interface StudioPlatform {
|
|
|
167
193
|
uploadFile: (path: string, data: string | File | Blob | ArrayBuffer) => Promise<unknown>;
|
|
168
194
|
deleteFile: (path: string) => Promise<void>;
|
|
169
195
|
renameFile: (from: string, to: string) => Promise<RenameResult>;
|
|
196
|
+
/**
|
|
197
|
+
* Where a file or a component tag is used across the project — the read side of the same walker
|
|
198
|
+
* `renameFile` writes through. Backs `capability.findReferences`, and with it the inspector's
|
|
199
|
+
* "Used on N pages", `Selection: Find Usages`, and the reference count inside every delete and
|
|
200
|
+
* rename confirmation.
|
|
201
|
+
*
|
|
202
|
+
* Optional so a backend without the route hides those three renderings rather than reporting a
|
|
203
|
+
* confident zero — the one answer a destructive dialog must never invent. Every shipped host
|
|
204
|
+
* implements it: the walker lives in `@jxsuite/server`, which desktop and cloud both run.
|
|
205
|
+
*
|
|
206
|
+
* At least one of `path` / `tagName` must be given. A `path` naming a component document
|
|
207
|
+
* contributes its own root tag, so one call answers "as a file AND as an element" together.
|
|
208
|
+
*/
|
|
209
|
+
findReferences?: (target: { path?: string; tagName?: string }) => Promise<ReferencesResult>;
|
|
170
210
|
createDirectory: (path: string) => Promise<void>;
|
|
171
211
|
/**
|
|
172
212
|
* Subscribe to backend filesystem change events for the active project. Returns an unsubscribe
|
|
@@ -266,6 +306,15 @@ export interface StudioPlatform {
|
|
|
266
306
|
gitDiscard: (files: string[]) => Promise<void>;
|
|
267
307
|
gitClone?: (url: string) => Promise<{ ok: boolean; root: string }>;
|
|
268
308
|
gitInit: () => Promise<void>;
|
|
309
|
+
/**
|
|
310
|
+
* Build the site to its output directory, so a reader opens what the author is looking at.
|
|
311
|
+
*
|
|
312
|
+
* `View: Open in Browser` runs this first and reports what it says. Optional because it is a
|
|
313
|
+
* capability, not an assumption: a backend that cannot build (a read-only cloud viewer) simply
|
|
314
|
+
* does not declare it, and the command says so rather than opening whatever stale output the last
|
|
315
|
+
* build happened to leave — which for most projects is nothing at all.
|
|
316
|
+
*/
|
|
317
|
+
buildSite?: () => Promise<SiteBuildResult>;
|
|
269
318
|
gitAddRemote: (name: string, url: string) => Promise<void>;
|
|
270
319
|
/**
|
|
271
320
|
* How the New Project modal collects a destination, and which `CreateProjectDestination` variant
|
|
@@ -321,8 +370,23 @@ export interface StudioPlatform {
|
|
|
321
370
|
/** Stack B AI assistant: URL of the OpenAI-compatible SSE chat proxy (`/__studio/ai/chat`). */
|
|
322
371
|
aiChatUrl: () => string | Promise<string>;
|
|
323
372
|
// ─── Multi-window (desktop only; undefined on dev-server) ───────────────────
|
|
324
|
-
/**
|
|
325
|
-
|
|
373
|
+
/**
|
|
374
|
+
* Open a project in a new window, focusing an existing window if it is already open.
|
|
375
|
+
*
|
|
376
|
+
* `focused` says which of the two happened, so the caller reports the outcome instead of
|
|
377
|
+
* announcing the intent — "opened in a new window" is a lie when the project was already open
|
|
378
|
+
* somewhere and that window merely came to the front.
|
|
379
|
+
*/
|
|
380
|
+
openProjectInNewWindow?: (root: string) => Promise<{ focused: boolean }>;
|
|
381
|
+
/**
|
|
382
|
+
* Pick a project WITHOUT binding this window to it — the answer to "which project", separated
|
|
383
|
+
* from the act of opening it here. Backs the New Window branch of Open Project: `openProject()`
|
|
384
|
+
* re-roots this window's backend as a side effect of picking, so it cannot ask a question whose
|
|
385
|
+
* answer is "not in this window". Resolves null when the user cancels the picker.
|
|
386
|
+
*
|
|
387
|
+
* Desktop only. Without it Studio does not offer the choice, because it could not honour it.
|
|
388
|
+
*/
|
|
389
|
+
pickProject?: () => Promise<{ root: string; name: string } | null>;
|
|
326
390
|
/** Open a fresh welcome window. */
|
|
327
391
|
newWindow?: () => Promise<void>;
|
|
328
392
|
/**
|
|
@@ -444,15 +508,6 @@ export interface CanvasPanel {
|
|
|
444
508
|
renderScope: { stop: () => void; run: <T>(fn: () => T) => T | undefined } | null;
|
|
445
509
|
}
|
|
446
510
|
|
|
447
|
-
export interface DocumentStackEntry {
|
|
448
|
-
document: JxMutableNode;
|
|
449
|
-
documentPath: string | null;
|
|
450
|
-
selection: JxPath | null;
|
|
451
|
-
dirty?: boolean;
|
|
452
|
-
mode?: string;
|
|
453
|
-
sourceFormat?: string | null;
|
|
454
|
-
}
|
|
455
|
-
|
|
456
511
|
export interface FunctionEditDef {
|
|
457
512
|
type: string;
|
|
458
513
|
defName?: string;
|
|
@@ -464,8 +519,8 @@ export interface FunctionEditDef {
|
|
|
464
519
|
}
|
|
465
520
|
|
|
466
521
|
/**
|
|
467
|
-
* Identifies which document position's `$expression` the
|
|
468
|
-
*
|
|
522
|
+
* Identifies which document position's `$expression` the Bottom dock's Logic tab is editing: a
|
|
523
|
+
* state entry (`type: "def"` + defName) or an element event binding (`type: "event"` + path +
|
|
469
524
|
* eventKey). Mirrors FunctionEditDef, the Monaco function editor's target shape.
|
|
470
525
|
*/
|
|
471
526
|
export interface FormulaEditDef {
|