@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/panels/editors.ts
CHANGED
|
@@ -1,33 +1,80 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
3
|
+
* The Logic dock's CODE surface — the Monaco function-body editor and its state-scope completions.
|
|
4
|
+
*
|
|
5
|
+
* It used to take over the canvas: `renderFunctionEditor` cleared `canvasWrap`, dropped every
|
|
6
|
+
* canvas panel and mounted Monaco over the stage, so the page whose handler you were writing was
|
|
7
|
+
* the one thing you could not see while writing it. It is a surface of the Bottom dock's **Logic**
|
|
8
|
+
* tab now (plan §12 P8.5); `panels/formula-workspace.ts` owns that tab's record and calls the three
|
|
9
|
+
* exports below — {@link functionEditorTemplate} for the container, {@link syncFunctionEditor} from
|
|
10
|
+
* the panel's `afterRender`, and {@link closeFunctionEditor} for its Close. The canvas keeps
|
|
11
|
+
* rendering the page underneath the dock; `canvas/canvas-render.ts` no longer knows this surface
|
|
12
|
+
* exists.
|
|
13
|
+
*
|
|
14
|
+
* **The mount is driven by the DOM, not by a render call.** A dock tab's body is re-rendered by lit
|
|
15
|
+
* whenever anything it reads changes, and lit will happily replace the container element out from
|
|
16
|
+
* under a live Monaco instance (switching to the formula surface and back does exactly that). So
|
|
17
|
+
* `syncFunctionEditor` asks the one question that matters — is the editor I hold still inside the
|
|
18
|
+
* container I was just handed? — and rebuilds when the answer is no. Re-checking the target string
|
|
19
|
+
* alone, which is all the takeover ever did, would have left a detached editor holding the user's
|
|
20
|
+
* unsaved body.
|
|
21
|
+
*
|
|
22
|
+
* **The move changed the REPAINT rate, not just the teardown rate, and the repaint is the one that
|
|
23
|
+
* runs with the user's hands on the keyboard.** The dock's `afterRender` effect tracks every badge
|
|
24
|
+
* in the strip — Problems' count, Activity's running list, Source Control's file count, which git
|
|
25
|
+
* polls every 30 seconds — so this module's sync runs on events that have nothing to do with the
|
|
26
|
+
* code surface. Every write below therefore asks `services/monaco-buffer.ts` whether the buffer has
|
|
27
|
+
* moved on first. Identity ("is this still the same editor?") is the OTHER question, still asked
|
|
28
|
+
* where it applies, and it is passed by a repaint into a live editor mid-word.
|
|
5
29
|
*/
|
|
6
30
|
|
|
7
31
|
import type * as monaco from "monaco-editor";
|
|
8
|
-
import { loadMonaco } from "../services/monaco-lazy";
|
|
32
|
+
import { loadMonaco, mountStillWanted } from "../services/monaco-lazy";
|
|
9
33
|
import { isJsonObject } from "@jxsuite/schema/guards";
|
|
10
|
-
import { html
|
|
11
|
-
import { ref } from "lit-html/directives/ref.js";
|
|
34
|
+
import { html } from "lit-html";
|
|
12
35
|
|
|
13
|
-
import {
|
|
14
|
-
import { activeTab } from "../workspace/workspace";
|
|
36
|
+
import { getNodeAtPath, renderOnly, updateUi } from "../store";
|
|
37
|
+
import { activeTab, tabIsLive } from "../workspace/workspace";
|
|
15
38
|
import { mutateUpdateDef, mutateUpdateProperty, transactDoc } from "../tabs/transact";
|
|
16
39
|
import { view } from "../view";
|
|
17
40
|
import { codeService, getFunctionArgs, setLintMarkers } from "../services/code-services";
|
|
41
|
+
import {
|
|
42
|
+
BUFFER_COMMIT,
|
|
43
|
+
bufferIsLive,
|
|
44
|
+
bufferMovedOn,
|
|
45
|
+
bufferWrites,
|
|
46
|
+
cancelBufferWrites,
|
|
47
|
+
commitBufferWrites,
|
|
48
|
+
} from "../services/monaco-buffer";
|
|
18
49
|
import { globalEntries, namedFormulaEntries } from "../ui/formula-catalog";
|
|
50
|
+
import { notify } from "../services/notify";
|
|
19
51
|
|
|
52
|
+
import type { BufferWrites } from "../services/monaco-buffer";
|
|
20
53
|
import type { OxLintDiagnostic } from "../services/code-services";
|
|
21
|
-
import type {
|
|
54
|
+
import type { JxPrototypeDef } from "@jxsuite/schema/types";
|
|
22
55
|
import type { JxPath } from "../state";
|
|
56
|
+
import type { Tab } from "../tabs/tab";
|
|
57
|
+
import type { TemplateResult } from "lit-html";
|
|
23
58
|
|
|
24
59
|
type EditingTarget =
|
|
25
60
|
| { type: "def"; defName: string }
|
|
26
61
|
| { type: "event"; path: JxPath; eventKey: string };
|
|
27
62
|
|
|
28
|
-
/**
|
|
29
|
-
|
|
30
|
-
|
|
63
|
+
/** The Monaco instance plus the four fields this module hangs off it (declared in `../view`). */
|
|
64
|
+
type FunctionEditor = NonNullable<typeof view.functionEditor>;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* The body `editing` names, read out of `tab`'s document.
|
|
68
|
+
*
|
|
69
|
+
* The tab is a PARAMETER, not `activeTab.value`. Every read and every write in this module is about
|
|
70
|
+
* one document — the one whose editor is mounted — and the focused tab is only incidentally that
|
|
71
|
+
* document.
|
|
72
|
+
*
|
|
73
|
+
* @param {Tab | null | undefined} tab
|
|
74
|
+
* @param {EditingTarget | null | undefined} editing
|
|
75
|
+
*/
|
|
76
|
+
function getFunctionBody(tab: Tab | null | undefined, editing: EditingTarget | null | undefined) {
|
|
77
|
+
const document = tab?.doc.document;
|
|
31
78
|
// Read body off any object-shaped def (covers legacy entries without $prototype).
|
|
32
79
|
const bodyOf = (def: unknown) =>
|
|
33
80
|
isJsonObject(def) && typeof def.body === "string" ? def.body : "";
|
|
@@ -40,87 +87,335 @@ function getFunctionBody(editing: EditingTarget | null | undefined) {
|
|
|
40
87
|
return "";
|
|
41
88
|
}
|
|
42
89
|
|
|
43
|
-
|
|
44
|
-
|
|
90
|
+
/** Where Monaco goes inside the Logic tab. Empty on purpose — the editor fills it imperatively. */
|
|
91
|
+
export function functionEditorTemplate(): TemplateResult {
|
|
92
|
+
return html`<div class="fw-code"></div>`;
|
|
93
|
+
}
|
|
45
94
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
95
|
+
/**
|
|
96
|
+
* Create, re-target or tear down the Monaco instance for the Logic tab's current body.
|
|
97
|
+
*
|
|
98
|
+
* The panel's `afterRender` hook, so it runs against the DOM lit has just painted.
|
|
99
|
+
*
|
|
100
|
+
* @param {HTMLElement} host The panel body element the Logic tab was rendered into.
|
|
101
|
+
*/
|
|
102
|
+
export function syncFunctionEditor(host: HTMLElement): void {
|
|
103
|
+
const tab = activeTab.value;
|
|
104
|
+
const editing = tab?.session.ui.editingFunction as EditingTarget | null | undefined;
|
|
105
|
+
const container = editing && tab ? host.querySelector<HTMLElement>(".fw-code") : null;
|
|
106
|
+
if (!container || !tab || !editing) {
|
|
107
|
+
// Either nothing is open in the code surface, or the formula surface is showing: whichever
|
|
108
|
+
// Monaco instance we hold is detached DOM, and holding it leaks a model plus its listeners.
|
|
109
|
+
disposeFunctionEditor();
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const current = view.functionEditor;
|
|
114
|
+
const attached = Boolean(current && container.contains(current.getDomNode() as Node | null));
|
|
115
|
+
/* THE TAB IS HALF OF THE IDENTITY, and the half that used to be missing.
|
|
116
|
+
`_editingTarget` is a JSON string built from the target alone, and the ordinary target
|
|
117
|
+
`{"eventKey":"onclick","path":["children",0],"type":"event"}` is the SAME string for the first
|
|
118
|
+
button on any two pages. Two tabs whose strings match would take this branch across a tab
|
|
119
|
+
switch, keeping an editor whose buffer holds the OTHER document's handler — and, once the
|
|
120
|
+
buffer is ahead, keeping it without even re-syncing. Comparing the tab by identity turns that
|
|
121
|
+
into the rebuild it always was. */
|
|
122
|
+
if (
|
|
123
|
+
current &&
|
|
124
|
+
attached &&
|
|
125
|
+
current._editingTab === tab &&
|
|
126
|
+
current._editingTarget === JSON.stringify(editing)
|
|
127
|
+
) {
|
|
128
|
+
/* THE RE-SYNC, and the one thing it is not allowed to do.
|
|
129
|
+
This branch exists for the edit that arrives from somewhere else — an undo, a collaborator,
|
|
130
|
+
a rename that rewrote the body — and its job is to show it. The comparison it makes is
|
|
131
|
+
"buffer ≠ document", which is ALSO true of every keystroke between a keypress and the 500ms
|
|
132
|
+
commit; resolving that in the document's favour reverts what is being typed. And because the
|
|
133
|
+
dock repaints on Problems counts, Activity and a 30-second git poll, that window is hit by
|
|
134
|
+
events the author never connected to their editor: the word half-finished under the cursor
|
|
135
|
+
jumps back to what it was a moment ago, for no reason they can see.
|
|
136
|
+
`bufferMovedOn` is the difference between the two cases. An edit from elsewhere lands while
|
|
137
|
+
the buffer is settled; a keystroke does not. */
|
|
138
|
+
const body = getFunctionBody(tab, editing);
|
|
139
|
+
if (!bufferMovedOn(current) && current.getValue() !== body) {
|
|
140
|
+
current._ignoreNextChange = true;
|
|
141
|
+
current.setValue(body);
|
|
142
|
+
// The buffer just took the DOCUMENT's text, so the two agree again. Declared rather than
|
|
143
|
+
// Inferred: clause 3 is a fact the writer owns, and this write is the one that clears it.
|
|
144
|
+
current._writes?.markSettled();
|
|
53
145
|
}
|
|
54
146
|
return;
|
|
55
147
|
}
|
|
56
148
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
149
|
+
disposeFunctionEditor();
|
|
150
|
+
container.textContent = "";
|
|
151
|
+
const body = getFunctionBody(tab, editing);
|
|
152
|
+
const args = getFunctionArgs(editing, tab.doc.document);
|
|
153
|
+
void mountFunctionEditor(container, tab, editing, body, args);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Drop the function editor, its model, and the debounced work armed over it. Safe to call when
|
|
158
|
+
* there is nothing to drop.
|
|
159
|
+
*
|
|
160
|
+
* **What happens to the armed commit is the whole correctness of the teardown.** Monaco's
|
|
161
|
+
* `dispose()` runs `_detachModel()`, and `CodeEditorWidget.getValue()` opens with `if
|
|
162
|
+
* (!this._modelData) return "";`. A disposed editor therefore answers the EMPTY STRING, not the
|
|
163
|
+
* buffer it was holding. The mount arms a 500ms timer that reads `getValue()` and writes it into
|
|
164
|
+
* the document, so every surviving timer was a scheduled deletion of the user's handler:
|
|
165
|
+
*
|
|
166
|
+
* - Type, then click Problems / the dock's × / ⌘\ to another pane — `syncFunctionEditor` disposes
|
|
167
|
+
* synchronously, and half a second later the body is `""` and the tab is dirty.
|
|
168
|
+
* - Worse, click **Close** — `closeFunctionEditor` minifies, writes the correct body, disposes, and
|
|
169
|
+
* the orphan timer overwrites that correct write with `""`. The action that exists to save the
|
|
170
|
+
* work was the one that destroyed it.
|
|
171
|
+
*
|
|
172
|
+
* The takeover never saw this because it was torn down by exactly one thing (closing it, which
|
|
173
|
+
* happened to be slower than the debounce). A dock tab is torn down by five, four of them new in
|
|
174
|
+
* P8. Handling it here rather than guarding inside each callback is deliberate: a guard on "is this
|
|
175
|
+
* still the current editor?" is passed by a timer that fires after a REMOUNT.
|
|
176
|
+
*
|
|
177
|
+
* **But cancelling alone is the same loss with the alarm switched off.** The armed commit IS the
|
|
178
|
+
* last half-second of typing; dropping it means the pre-typing body survives instead of an empty
|
|
179
|
+
* one, and `doc.dirty` stays `false`, so nothing anywhere says an edit went missing. Five ways out
|
|
180
|
+
* of the surface measured as silent loss — switching the dock tab, opening another target, opening
|
|
181
|
+
* a formula through `openLogicTarget`, collapsing the dock, and a same-target switch to another
|
|
182
|
+
* TAB. {@link commitBufferWrites} reads the buffer while it is still alive, writes it to the tab it
|
|
183
|
+
* was mounted for, and only then drops what is left. The order is the fix, not the call.
|
|
184
|
+
*
|
|
185
|
+
* **Seven, and the last two are not this function's.** ⌘W / the tab × and quitting destroy the TAB:
|
|
186
|
+
* `closeTab` deletes it from `workspace.tabs` before any repaint reaches this disposer, so the
|
|
187
|
+
* commit flushed here finds `tabIsLive(tab) === false` and correctly writes nothing. Those two are
|
|
188
|
+
* fixed at the gate that decides — `services/monaco-buffer.ts`'s `commitTabBuffers` runs the commit
|
|
189
|
+
* while the tab is still open, and `tabBufferUnsaved` answers for typing no commit could land.
|
|
190
|
+
*
|
|
191
|
+
* **AND THE COMMIT IS ALLOWED TO FAIL, at which point this is a deletion.** The collab freeze
|
|
192
|
+
* refuses the body write outright; a collaborator deleting the element the handler hangs off leaves
|
|
193
|
+
* `editing.path` resolving to nothing. Either way `commitBufferWrites` returns false, and the very
|
|
194
|
+
* next line detaches the model — so `buffersForTab` stops finding the buffer and `tabBufferUnsaved`
|
|
195
|
+
* goes back to answering "nothing to lose" about text that no longer exists anywhere.
|
|
196
|
+
*
|
|
197
|
+
* Refusing the teardown is not on offer here, which is why the answer is a message rather than a
|
|
198
|
+
* bail-out: every caller is a repaint or a mode transition that has ALREADY replaced the container
|
|
199
|
+
* (the dock's `afterRender`, `closeFunctionEditor`'s tail), so keeping the instance would keep a
|
|
200
|
+
* live Monaco bound to detached DOM — the leak `syncFunctionEditor` exists to close, and an editor
|
|
201
|
+
* the author cannot see or reach is not a rescued edit. `closeFunctionEditor` is where a refusal
|
|
202
|
+
* DOES keep the surface, because it is a gesture and the surface is still standing.
|
|
203
|
+
*/
|
|
204
|
+
export function disposeFunctionEditor(): void {
|
|
205
|
+
const editor = view.functionEditor;
|
|
206
|
+
if (editor) {
|
|
207
|
+
commitBufferWrites(editor, "logic");
|
|
208
|
+
editor.dispose();
|
|
60
209
|
view.functionEditor = null;
|
|
61
210
|
}
|
|
62
|
-
|
|
63
|
-
view.monacoEditor.dispose();
|
|
64
|
-
view.monacoEditor = null;
|
|
65
|
-
}
|
|
211
|
+
}
|
|
66
212
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
213
|
+
/**
|
|
214
|
+
* The one way a body is written into a document, bound to the tab and target it is for.
|
|
215
|
+
*
|
|
216
|
+
* Two callers used to carry their own copy of this def/event branch — the 500ms commit and the
|
|
217
|
+
* Close — and each resolved the tab it wrote to at CALL time, through `activeTab.value`. That is
|
|
218
|
+
* "whatever is focused now", which is the tab the buffer came from only when nothing happened in
|
|
219
|
+
* between. Bound here instead, at mount, so the two cannot answer differently and neither can
|
|
220
|
+
* answer with a document the buffer was never filled from.
|
|
221
|
+
*
|
|
222
|
+
* `tabIsLive` rather than "is it active": the write belongs to its tab whether or not that tab is
|
|
223
|
+
* the one on screen, and a tab that has been CLOSED is the one case where the write must not happen
|
|
224
|
+
* at all — nothing would ever read it, and `transactDoc` would still push history and dirty a
|
|
225
|
+
* document the user has already dismissed.
|
|
226
|
+
*
|
|
227
|
+
* **A WRITE IS ALLOWED TO BE REFUSED, and the buffer has to hear about it.** `transactDoc` consults
|
|
228
|
+
* the collab gate, which under the source-canonical freeze blocks every structural edit including
|
|
229
|
+
* the lock holder's own — so this returns having changed nothing. Settling the buffer anyway told
|
|
230
|
+
* `tabBufferUnsaved` (and therefore `shouldWarnOnClose` and `hasUnsavedTabs`) that the document had
|
|
231
|
+
* been given the author's text when it had not, and every subsequent close discarded it without a
|
|
232
|
+
* word. So the settle is conditional on the write LANDING, and the answer is handed back to the
|
|
233
|
+
* caller: the debounce has nothing useful to do with it (the gate raises its own keyed toast on
|
|
234
|
+
* every refusal), but the Close does — see {@link closeFunctionEditor} — and so does every
|
|
235
|
+
* disposer, through `commitBufferWrites`.
|
|
236
|
+
*
|
|
237
|
+
* **Three ways it does not land, not one.** The tab is gone; the collab gate refuses; or the node
|
|
238
|
+
* `editing.path` names has been deleted out from under the open editor, in which case there is no
|
|
239
|
+
* coordinate to write to at all.
|
|
240
|
+
*
|
|
241
|
+
* @param {Tab} tab The document this buffer was filled from.
|
|
242
|
+
* @param {EditingTarget} editing The def or event binding inside it.
|
|
243
|
+
* @param {BufferWrites} [writes] The buffer's debounce holder, when there is a buffer: a completed
|
|
244
|
+
* write is precisely what settles clause 3.
|
|
245
|
+
* @returns {(body: string) => boolean} True when the body reached the document.
|
|
246
|
+
*/
|
|
247
|
+
function bodyWriter(tab: Tab, editing: EditingTarget, writes?: BufferWrites) {
|
|
248
|
+
return (newBody: string): boolean => {
|
|
249
|
+
if (!tabIsLive(tab)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
let wrote = false;
|
|
253
|
+
if (editing.type === "def") {
|
|
254
|
+
wrote = transactDoc(tab, (t) => mutateUpdateDef(t, editing.defName, { body: newBody }));
|
|
255
|
+
} else if (editing.type === "event") {
|
|
256
|
+
const node = getNodeAtPath(tab.doc.document, editing.path);
|
|
257
|
+
/* THE ELEMENT THE HANDLER HANGS OFF CAN GO WHILE THE EDITOR IS OPEN — a collaborator's
|
|
258
|
+
delete arriving over the wire, or the author's own ⌘Z. `editing.path` then resolves to
|
|
259
|
+
nothing, and `mutateUpdateProperty` read `getNodeAtPath(...)[key]` straight through it:
|
|
260
|
+
`undefined is not an object`, thrown out of the 500ms commit and, through
|
|
261
|
+
`commitBufferWrites`, out of the dock panel's `afterRender`.
|
|
262
|
+
There is no coordinate left to write the body to, so the honest answer is that the write
|
|
263
|
+
did not happen. Reporting it as a success would settle the buffer and tell every close
|
|
264
|
+
gate the document had been given text that has nowhere to be. */
|
|
265
|
+
if (!node) {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
const current = node[editing.eventKey] || {};
|
|
269
|
+
wrote = transactDoc(tab, (t) =>
|
|
270
|
+
mutateUpdateProperty(t, editing.path, editing.eventKey, {
|
|
271
|
+
...(current as object),
|
|
272
|
+
$prototype: "Function",
|
|
273
|
+
body: newBody,
|
|
274
|
+
}),
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
// The document now holds what the buffer holds — clause 3 is cleared by the write that made it
|
|
278
|
+
// True, not by a timer expiring, and never by a write that was refused.
|
|
279
|
+
if (wrote) {
|
|
280
|
+
writes?.markSettled();
|
|
281
|
+
}
|
|
282
|
+
return wrote;
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Close the code surface: minify what is in the buffer, write it back, and clear the target.
|
|
288
|
+
*
|
|
289
|
+
* The write is the reason the Close is a real one rather than a tab switch. It lived in `studio.ts`
|
|
290
|
+
* beside the pane context bar's crumb, which was its only caller; the Logic tab's Close is the
|
|
291
|
+
* second, and a body-saving routine that two surfaces call belongs beside the editor whose buffer
|
|
292
|
+
* it reads.
|
|
293
|
+
*
|
|
294
|
+
* **The minify is an await, and a retarget fits inside it.** The buffer and the target are both
|
|
295
|
+
* read BEFORE it; the teardown after it acts on whatever is mounted then. Open A, click Close, and
|
|
296
|
+
* while `minify` is in flight click "Open in code editor" on B: the write still belongs to A and
|
|
297
|
+
* still lands on A, but the dispose-and-clear would have killed B's live editor and closed a
|
|
298
|
+
* document position the user had just asked for. So the write is unconditional and the TEARDOWN is
|
|
299
|
+
* conditional — they answer to different moments in time and only one of them is still A's.
|
|
300
|
+
*
|
|
301
|
+
* **But that is TWO obligations, and only one of them is the editor's.** Guarding the whole tail on
|
|
302
|
+
* `view.functionEditor === editor` conflated them, and the case it got wrong is the ordinary one:
|
|
303
|
+
* an editor torn down inside the minify and NOT retargeted — collapse the dock, select Problems, ⌘\
|
|
304
|
+
* to another pane — leaves `view.functionEditor` null, so the guard bailed and `editingFunction`
|
|
305
|
+
* was never cleared. Nothing else clears it, so the Logic tab stayed on the strip pointing at a
|
|
306
|
+
* body the user had just dismissed, and re-opening the dock restored the editor they closed.
|
|
307
|
+
* Disposing belongs to the instance; clearing the target belongs to the TARGET, and the only thing
|
|
308
|
+
* with a claim on the target is a retarget that already replaced it.
|
|
309
|
+
*
|
|
310
|
+
* **The write goes through the editor's own `_commitBody`, not through the focused tab.** That
|
|
311
|
+
* writer was bound at mount to the tab and target this buffer belongs to, so the Close and the
|
|
312
|
+
* debounce cannot land in different places. An editor handle this module did not mount carries no
|
|
313
|
+
* writer, and then the only meaning the gesture has left is its own tab and its own target — the
|
|
314
|
+
* same {@link bodyWriter}, bound to those.
|
|
315
|
+
*
|
|
316
|
+
* **AND THE WRITE CAN BE REFUSED, in which case there is nothing left to close over.** Under the
|
|
317
|
+
* source-canonical freeze the collab gate blocks the body commit, and this used to carry on:
|
|
318
|
+
* minify, refused write, `cancelBufferWrites` (the armed commit gone too), dispose, target cleared.
|
|
319
|
+
* The Close button — the one action whose entire purpose is to save the body — destroyed it, and
|
|
320
|
+
* the standing "frozen" chip in the presence strip does not say that a button just did nothing. A
|
|
321
|
+
* refusal therefore keeps the surface exactly as it is (the text stays on screen, the target stays
|
|
322
|
+
* open) and says why, once, through the same Problems/toast channel every other failed write uses.
|
|
323
|
+
* Nothing is re-armed: the buffer is still marked typed, so the close and quit gates already know
|
|
324
|
+
* the work exists.
|
|
325
|
+
*/
|
|
326
|
+
export async function closeFunctionEditor(): Promise<void> {
|
|
327
|
+
const tab = activeTab.value;
|
|
328
|
+
const editing = tab?.session.ui.editingFunction as EditingTarget | null | undefined;
|
|
329
|
+
if (!tab || !editing) {
|
|
330
|
+
return;
|
|
70
331
|
}
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
332
|
+
const editor = view.functionEditor;
|
|
333
|
+
if (editor) {
|
|
334
|
+
const currentCode = editor.getValue();
|
|
335
|
+
const minResult = await codeService("minify", { code: currentCode });
|
|
336
|
+
/* Cancelled AFTER the round trip, not before it. Either position stops the armed commit from
|
|
337
|
+
replaying raw text over the minified body, but cancelling first means a minify that REJECTS
|
|
338
|
+
takes the user's last keystrokes with it — there would be no armed commit left to write them.
|
|
339
|
+
Left armed, the worst case is that the timer fires mid-await and writes the same text to the
|
|
340
|
+
same target a moment before the minified version lands on top of it. */
|
|
341
|
+
cancelBufferWrites(editor);
|
|
342
|
+
const write = editor._commitBody ?? bodyWriter(tab, editing);
|
|
343
|
+
if (!write(minResult?.code ?? currentCode)) {
|
|
344
|
+
notify.warn(
|
|
345
|
+
"The handler could not be saved — source editing holds this document. Your code is still in the editor.",
|
|
346
|
+
{
|
|
347
|
+
key: "logic.close-refused",
|
|
348
|
+
source: "Logic",
|
|
349
|
+
...(tab.documentPath ? { path: tab.documentPath } : {}),
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
354
|
+
if (view.functionEditor !== null && view.functionEditor !== editor) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
// A no-op when the teardown already happened, which is the whole point: the clear below still
|
|
358
|
+
// Runs, because a target with no editor is precisely the state nothing else resolves.
|
|
359
|
+
disposeFunctionEditor();
|
|
74
360
|
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
litRender(nothing, canvasWrap);
|
|
79
|
-
canvasWrap.style.padding = "0";
|
|
80
|
-
canvasWrap.style.flexDirection = "column";
|
|
81
|
-
canvasWrap.style.alignItems = "stretch";
|
|
82
|
-
|
|
83
|
-
// The tab bar renders the Back button + breadcrumb context for the function editor.
|
|
84
|
-
let editorContainer: HTMLDivElement | null = null;
|
|
85
|
-
litRender(
|
|
86
|
-
html`<div class="source-wrap">
|
|
87
|
-
<div
|
|
88
|
-
class="source-editor"
|
|
89
|
-
${ref((el) => {
|
|
90
|
-
if (el) {
|
|
91
|
-
editorContainer = el as HTMLDivElement;
|
|
92
|
-
}
|
|
93
|
-
})}
|
|
94
|
-
></div>
|
|
95
|
-
</div>`,
|
|
96
|
-
canvasWrap,
|
|
97
|
-
);
|
|
98
|
-
|
|
99
|
-
const body = getFunctionBody(editing);
|
|
100
|
-
const args = getFunctionArgs(
|
|
101
|
-
/** @type {EditingTarget} */ editing as EditingTarget,
|
|
102
|
-
activeTab.value?.doc.document,
|
|
103
|
-
);
|
|
361
|
+
updateUi(activeTab.value, "editingFunction", null);
|
|
362
|
+
}
|
|
104
363
|
|
|
105
|
-
|
|
364
|
+
/**
|
|
365
|
+
* Whether the mount started for `container` / `tab` / `editing` is still the one the app wants.
|
|
366
|
+
*
|
|
367
|
+
* The three clauses `services/monaco-lazy.ts` states, spelled for this surface: the container is
|
|
368
|
+
* still in the document, no other mount has already produced the editor, and the dock still wants
|
|
369
|
+
* exactly this tab and this target. Asked AFTER the Monaco load, because that load is a cold
|
|
370
|
+
* dynamic import of 12.6 MB and the user has hundreds of milliseconds to close the surface,
|
|
371
|
+
* retarget it or switch tabs inside it — each of which used to end with a live editor nobody could
|
|
372
|
+
* reach, assigned to `view.functionEditor` where the next sync would overwrite the handle rather
|
|
373
|
+
* than dispose it.
|
|
374
|
+
*
|
|
375
|
+
* `activeTab.value === tab` covers the tab having been closed as well as merely left: a closed tab
|
|
376
|
+
* is nobody's active tab, and the dock only ever shows the active one's target.
|
|
377
|
+
*/
|
|
378
|
+
function functionMountWanted(container: HTMLElement, tab: Tab, editing: EditingTarget): boolean {
|
|
379
|
+
return mountStillWanted(container, view.functionEditor, () => {
|
|
380
|
+
const current = activeTab.value;
|
|
381
|
+
return (
|
|
382
|
+
current === tab &&
|
|
383
|
+
JSON.stringify(current.session.ui.editingFunction ?? null) === JSON.stringify(editing)
|
|
384
|
+
);
|
|
385
|
+
});
|
|
106
386
|
}
|
|
107
387
|
|
|
108
388
|
/**
|
|
109
|
-
* Create the function-body editor once Monaco has loaded.
|
|
110
|
-
*
|
|
111
|
-
*
|
|
389
|
+
* Create the function-body editor once Monaco has loaded.
|
|
390
|
+
*
|
|
391
|
+
* The container is in the DOM when this is called and the target is current — but neither is still
|
|
392
|
+
* guaranteed on the far side of the `await`, which is why {@link functionMountWanted} is asked
|
|
393
|
+
* again there. Nothing else covers it: `syncFunctionEditor` runs from the dock's `afterRender`, so
|
|
394
|
+
* it cannot see a mount that has not produced an editor yet.
|
|
395
|
+
*
|
|
396
|
+
* **`tab` is captured here and never re-read.** Everything below — the commit, the format
|
|
397
|
+
* continuation, the lint continuations — names THIS tab, because a buffer belongs to the document
|
|
398
|
+
* it was filled from and to no other. Resolving through `activeTab.value` at callback time meant
|
|
399
|
+
* the 500ms commit addressed whatever tab was focused when the timer fired: open `a.json`, Edit
|
|
400
|
+
* Event Handler on `children[0].onclick`, open `b.json`, same handler, type one character in A and
|
|
401
|
+
* click B's tab inside the window, and B's handler was replaced by A's buffer while A never
|
|
402
|
+
* received the edit at all.
|
|
112
403
|
*/
|
|
113
404
|
async function mountFunctionEditor(
|
|
114
405
|
editorContainer: HTMLElement,
|
|
406
|
+
tab: Tab,
|
|
407
|
+
editing: EditingTarget,
|
|
115
408
|
body: string,
|
|
116
409
|
args: string[],
|
|
117
|
-
editing: unknown,
|
|
118
410
|
): Promise<void> {
|
|
119
411
|
const monacoNs = await loadMonaco();
|
|
412
|
+
if (!functionMountWanted(editorContainer, tab, editing)) {
|
|
413
|
+
return;
|
|
414
|
+
}
|
|
120
415
|
// Completions used to register at studio startup, which would now await the lazy load and undo it.
|
|
121
416
|
// The editor is the only surface they serve, and the registration is idempotent.
|
|
122
417
|
void registerFunctionCompletions();
|
|
123
|
-
|
|
418
|
+
const editor: FunctionEditor = monacoNs.editor.create(editorContainer, {
|
|
124
419
|
automaticLayout: true,
|
|
125
420
|
fontFamily: "'JetBrains Mono', 'SF Mono', 'Fira Code', 'Consolas', monospace",
|
|
126
421
|
fontSize: 12,
|
|
@@ -133,67 +428,99 @@ async function mountFunctionEditor(
|
|
|
133
428
|
value: body,
|
|
134
429
|
wordWrap: "on",
|
|
135
430
|
});
|
|
136
|
-
|
|
137
|
-
|
|
431
|
+
editor._editingTarget = JSON.stringify(editing);
|
|
432
|
+
editor._editingTab = tab;
|
|
433
|
+
const writes = bufferWrites(editor);
|
|
138
434
|
|
|
139
|
-
//
|
|
435
|
+
// The writer this buffer will use for the rest of its life, bound to the tab and target it was
|
|
436
|
+
// Mounted for. The debounce below and `closeFunctionEditor` both go through it.
|
|
437
|
+
editor._commitBody = bodyWriter(tab, editing, writes);
|
|
438
|
+
view.functionEditor = editor;
|
|
439
|
+
|
|
440
|
+
let lintGen = 0;
|
|
441
|
+
|
|
442
|
+
// Format on open — show pretty-printed code, then run initial lint.
|
|
443
|
+
//
|
|
444
|
+
// Both continuations address `editor`, not `view.functionEditor`. That handle is a variable, and
|
|
445
|
+
// A code service round-trip is long enough to retarget twice: it names whatever is mounted NOW,
|
|
446
|
+
// So answering a stale request through it wrote one def's formatted body into another def's
|
|
447
|
+
// Editor. `editor` is the instance the request was made FOR, and the identity check is how a
|
|
448
|
+
// Request that outlived it says nothing at all.
|
|
449
|
+
//
|
|
450
|
+
// Identity is where that reasoning STOPPED, and the common case is the one it misses: no
|
|
451
|
+
// Retarget happened, so the guard passes — and a cold format round trip is long enough to type
|
|
452
|
+
// Into. What lands is the formatting of the body as it was BEFORE those keystrokes, on top of
|
|
453
|
+
// Them. `body` is what the request was computed from, so passing it as `expected` is the whole
|
|
454
|
+
// Difference between "still the same editor" and "still the same text".
|
|
140
455
|
void codeService("format", { args, code: body }).then((result) => {
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
view.functionEditor
|
|
456
|
+
if (
|
|
457
|
+
result?.code != null &&
|
|
458
|
+
view.functionEditor === editor &&
|
|
459
|
+
tabIsLive(tab) &&
|
|
460
|
+
!bufferMovedOn(editor, body)
|
|
461
|
+
) {
|
|
462
|
+
editor._ignoreNextChange = true;
|
|
463
|
+
editor.setValue(result.code);
|
|
464
|
+
/* AND SAY SO, or the next repaint un-formats it in front of the author.
|
|
465
|
+
This text did not come from the document and is deliberately never committed to it, so the
|
|
466
|
+
buffer is now ahead. Inferred from the debounce it would read as "settled" — `setValue`
|
|
467
|
+
sets `_ignoreNextChange`, the change handler returns before it can arm anything — and the
|
|
468
|
+
re-sync branch would resolve the difference in the document's favour. With the dock
|
|
469
|
+
repainting on the Problems badge, on Activity and on a 30-second git poll, "Format on open"
|
|
470
|
+
visibly undid itself within seconds of every open. */
|
|
471
|
+
writes.markAhead();
|
|
144
472
|
}
|
|
145
473
|
});
|
|
146
474
|
void codeService("lint", { args, code: body }).then((result) => {
|
|
147
|
-
if (
|
|
148
|
-
|
|
475
|
+
if (
|
|
476
|
+
result?.diagnostics &&
|
|
477
|
+
view.functionEditor === editor &&
|
|
478
|
+
tabIsLive(tab) &&
|
|
479
|
+
bufferIsLive(editor)
|
|
480
|
+
) {
|
|
481
|
+
setLintMarkers(editor, result.diagnostics as OxLintDiagnostic[]);
|
|
149
482
|
}
|
|
150
483
|
});
|
|
151
484
|
|
|
152
|
-
// Debounced sync back to state + lint on edit
|
|
153
|
-
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
154
|
-
let syncDebounce: ReturnType<typeof setTimeout> | undefined;
|
|
155
|
-
/** @type {ReturnType<typeof setTimeout> | undefined} */
|
|
156
|
-
let lintDebounce: ReturnType<typeof setTimeout> | undefined;
|
|
157
|
-
let lintGen = 0;
|
|
158
485
|
editor.onDidChangeModelContent(() => {
|
|
159
486
|
if (editor._ignoreNextChange) {
|
|
160
487
|
editor._ignoreNextChange = false;
|
|
161
488
|
return;
|
|
162
489
|
}
|
|
163
490
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
transactDoc(activeTab.value, (t) =>
|
|
174
|
-
mutateUpdateProperty(t, target.path, target.eventKey, {
|
|
175
|
-
...current,
|
|
176
|
-
$prototype: "Function",
|
|
177
|
-
body: newBody,
|
|
178
|
-
}),
|
|
179
|
-
);
|
|
180
|
-
}
|
|
491
|
+
// A keystroke. The buffer is ahead of the document from this instant until the commit lands —
|
|
492
|
+
// Stated as a fact rather than left to be read off the timer, because the timer is only ONE of
|
|
493
|
+
// The ways a buffer gets ahead and the other one is invisible to it. `markTyped` rather than
|
|
494
|
+
// `markAhead`: this text is the AUTHOR'S, so it is also the text a close would destroy, and
|
|
495
|
+
// Format-on-open's (below) is not.
|
|
496
|
+
writes.markTyped();
|
|
497
|
+
|
|
498
|
+
writes.arm(BUFFER_COMMIT, 500, () => {
|
|
499
|
+
editor._commitBody?.(editor.getValue());
|
|
181
500
|
renderOnly("leftPanel");
|
|
182
|
-
}
|
|
501
|
+
});
|
|
183
502
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
const gen =
|
|
503
|
+
writes.arm("lint", 750, () => {
|
|
504
|
+
lintGen += 1;
|
|
505
|
+
const gen = lintGen;
|
|
187
506
|
const currentCode = editor.getValue();
|
|
188
507
|
void codeService("lint", { args, code: currentCode }).then((result) => {
|
|
189
|
-
|
|
508
|
+
// `lintGen` is per-MOUNT, so it can only order this editor's own requests; a remount
|
|
509
|
+
// Starts a fresh generation that a stale closure's `gen` still matches. The identity check
|
|
510
|
+
// Is the half that spans mounts, and the tab is the half that spans documents.
|
|
511
|
+
if (
|
|
512
|
+
gen !== lintGen ||
|
|
513
|
+
view.functionEditor !== editor ||
|
|
514
|
+
!tabIsLive(tab) ||
|
|
515
|
+
!bufferIsLive(editor)
|
|
516
|
+
) {
|
|
190
517
|
return;
|
|
191
518
|
}
|
|
192
|
-
if (result?.diagnostics
|
|
193
|
-
setLintMarkers(
|
|
519
|
+
if (result?.diagnostics) {
|
|
520
|
+
setLintMarkers(editor, result.diagnostics as OxLintDiagnostic[]);
|
|
194
521
|
}
|
|
195
522
|
});
|
|
196
|
-
}
|
|
523
|
+
});
|
|
197
524
|
});
|
|
198
525
|
}
|
|
199
526
|
|