@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
|
@@ -1,429 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
activeTab,
|
|
3
|
-
effect,
|
|
4
|
-
effectScope,
|
|
5
|
-
getNodeAtPath,
|
|
6
|
-
nodeLabel,
|
|
7
|
-
renderOnly,
|
|
8
|
-
statusbarEl,
|
|
9
|
-
updateSession
|
|
10
|
-
} from "./studio-sjh1wt1a.js";
|
|
11
|
-
import {
|
|
12
|
-
ref
|
|
13
|
-
} from "./studio-4k0m4fmp.js";
|
|
14
|
-
import {
|
|
15
|
-
html,
|
|
16
|
-
nothing,
|
|
17
|
-
render
|
|
18
|
-
} from "./studio-5x5774ak.js";
|
|
19
|
-
|
|
20
|
-
// ../schema/src/parse.ts
|
|
21
|
-
function errorMessage(e) {
|
|
22
|
-
return e instanceof Error ? e.message : String(e);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// src/view.ts
|
|
26
|
-
var view = {
|
|
27
|
-
panzoomWrap: null,
|
|
28
|
-
renderGeneration: 0,
|
|
29
|
-
centerObserver: null,
|
|
30
|
-
needsCenter: true,
|
|
31
|
-
panX: 0,
|
|
32
|
-
panY: 0,
|
|
33
|
-
prevCanvasMode: null,
|
|
34
|
-
monacoEditor: null,
|
|
35
|
-
functionEditor: null,
|
|
36
|
-
blockActionBarEl: null,
|
|
37
|
-
selDragCleanup: null,
|
|
38
|
-
dndCleanups: [],
|
|
39
|
-
canvasDndCleanups: [],
|
|
40
|
-
canvasEventCleanups: [],
|
|
41
|
-
forcedStyleTag: null,
|
|
42
|
-
forcedAttrEl: null,
|
|
43
|
-
elementsCollapsed: new Set,
|
|
44
|
-
elementsFilter: "",
|
|
45
|
-
_currentDropTargetRow: null,
|
|
46
|
-
layerDragSourceHeight: 0,
|
|
47
|
-
_completionRegistered: false,
|
|
48
|
-
showAddBreakpointForm: false,
|
|
49
|
-
addBreakpointPreview: "",
|
|
50
|
-
layoutSelection: null,
|
|
51
|
-
leftTab: "layers",
|
|
52
|
-
leftPanelCollapsed: false,
|
|
53
|
-
rightPanelCollapsed: false,
|
|
54
|
-
chatPanelCollapsed: false,
|
|
55
|
-
_layersCollapsed: null
|
|
56
|
-
};
|
|
57
|
-
var COLLAPSE_STORAGE_KEY = "jx-studio-panel-widths";
|
|
58
|
-
function applyPanelCollapse() {
|
|
59
|
-
const app = document.querySelector("#app");
|
|
60
|
-
if (!app) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
app.classList.toggle("left-collapsed", view.leftPanelCollapsed);
|
|
64
|
-
app.classList.toggle("right-collapsed", view.rightPanelCollapsed);
|
|
65
|
-
app.classList.toggle("chat-collapsed", view.chatPanelCollapsed);
|
|
66
|
-
try {
|
|
67
|
-
const saved = JSON.parse(localStorage.getItem(COLLAPSE_STORAGE_KEY) || "{}");
|
|
68
|
-
saved.leftCollapsed = view.leftPanelCollapsed;
|
|
69
|
-
saved.rightCollapsed = view.rightPanelCollapsed;
|
|
70
|
-
saved.chatCollapsed = view.chatPanelCollapsed;
|
|
71
|
-
localStorage.setItem(COLLAPSE_STORAGE_KEY, JSON.stringify(saved));
|
|
72
|
-
} catch {}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// src/ui/layers.ts
|
|
76
|
-
var _popoverLayer;
|
|
77
|
-
var _modalLayer;
|
|
78
|
-
var _dialogLayer;
|
|
79
|
-
function initLayers() {
|
|
80
|
-
_popoverLayer = document.querySelector("#layer-popover");
|
|
81
|
-
_modalLayer = document.querySelector("#layer-modal");
|
|
82
|
-
_dialogLayer = document.querySelector("#layer-dialog");
|
|
83
|
-
}
|
|
84
|
-
var UNDERLAID = "sp-dialog-wrapper[open], sp-underlay[open]";
|
|
85
|
-
function isModalOpen() {
|
|
86
|
-
return Boolean(_dialogLayer?.querySelector(UNDERLAID) || _modalLayer?.querySelector(UNDERLAID));
|
|
87
|
-
}
|
|
88
|
-
var BODY_FOCUSABLE = 'input, textarea, select, button, sp-textfield, sp-button, sp-picker, sp-checkbox, [tabindex]:not([tabindex="-1"])';
|
|
89
|
-
function focusDialog(slot) {
|
|
90
|
-
requestAnimationFrame(() => {
|
|
91
|
-
if (!slot.isConnected || slot.contains(document.activeElement)) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
const wrapper = slot.querySelector("sp-dialog-wrapper");
|
|
95
|
-
const target = slot.querySelector(BODY_FOCUSABLE) ?? wrapper?.shadowRoot?.querySelector("sp-button") ?? null;
|
|
96
|
-
target?.focus();
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
function showDialog(templateFn) {
|
|
100
|
-
return new Promise((resolve) => {
|
|
101
|
-
const slot = document.createElement("div");
|
|
102
|
-
slot.style.pointerEvents = "auto";
|
|
103
|
-
_dialogLayer.append(slot);
|
|
104
|
-
const restoreTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
105
|
-
let resolved = false;
|
|
106
|
-
const onKeydown = (e) => {
|
|
107
|
-
if (e.key !== "Escape") {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const wrapper = slot.querySelector("sp-dialog-wrapper");
|
|
111
|
-
if (!wrapper) {
|
|
112
|
-
return;
|
|
113
|
-
}
|
|
114
|
-
e.preventDefault();
|
|
115
|
-
e.stopPropagation();
|
|
116
|
-
wrapper.dispatchEvent(new Event("close", { bubbles: true }));
|
|
117
|
-
};
|
|
118
|
-
slot.addEventListener("keydown", onKeydown);
|
|
119
|
-
const done = (value) => {
|
|
120
|
-
if (resolved) {
|
|
121
|
-
return;
|
|
122
|
-
}
|
|
123
|
-
resolved = true;
|
|
124
|
-
slot.removeEventListener("keydown", onKeydown);
|
|
125
|
-
render(nothing, slot);
|
|
126
|
-
slot.remove();
|
|
127
|
-
if (restoreTo?.isConnected) {
|
|
128
|
-
restoreTo.focus();
|
|
129
|
-
}
|
|
130
|
-
resolve(value);
|
|
131
|
-
};
|
|
132
|
-
render(templateFn(done), slot);
|
|
133
|
-
focusDialog(slot);
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
function showConfirmDialog(headline, message, opts = {}) {
|
|
137
|
-
const { confirmLabel = "Confirm", cancelLabel = "Cancel", destructive = false } = opts;
|
|
138
|
-
return showDialog((done) => html`
|
|
139
|
-
<sp-dialog-wrapper
|
|
140
|
-
open
|
|
141
|
-
underlay
|
|
142
|
-
headline=${headline}
|
|
143
|
-
confirm-label=${confirmLabel}
|
|
144
|
-
cancel-label=${cancelLabel}
|
|
145
|
-
size="s"
|
|
146
|
-
@confirm=${() => done(true)}
|
|
147
|
-
@cancel=${() => done(false)}
|
|
148
|
-
@close=${() => done(false)}
|
|
149
|
-
class=${destructive ? "dialog-destructive" : ""}
|
|
150
|
-
>
|
|
151
|
-
<p>${message}</p>
|
|
152
|
-
</sp-dialog-wrapper>
|
|
153
|
-
`);
|
|
154
|
-
}
|
|
155
|
-
function showSaveDiscardDialog(headline, message, opts = {}) {
|
|
156
|
-
const { saveLabel = "Save", discardLabel = "Discard", cancelLabel = "Cancel" } = opts;
|
|
157
|
-
return showDialog((done) => html`
|
|
158
|
-
<sp-dialog-wrapper
|
|
159
|
-
open
|
|
160
|
-
underlay
|
|
161
|
-
headline=${headline}
|
|
162
|
-
confirm-label=${saveLabel}
|
|
163
|
-
secondary-label=${discardLabel}
|
|
164
|
-
cancel-label=${cancelLabel}
|
|
165
|
-
size="s"
|
|
166
|
-
@confirm=${() => done("save")}
|
|
167
|
-
@secondary=${() => done("discard")}
|
|
168
|
-
@cancel=${() => done("cancel")}
|
|
169
|
-
@close=${() => done("cancel")}
|
|
170
|
-
>
|
|
171
|
-
<p>${message}</p>
|
|
172
|
-
</sp-dialog-wrapper>
|
|
173
|
-
`);
|
|
174
|
-
}
|
|
175
|
-
function showPromptDialog(headline, opts = {}) {
|
|
176
|
-
const {
|
|
177
|
-
cancelLabel = "Cancel",
|
|
178
|
-
confirmLabel = "OK",
|
|
179
|
-
message,
|
|
180
|
-
placeholder = "",
|
|
181
|
-
select = "all",
|
|
182
|
-
validate,
|
|
183
|
-
value: initialValue = ""
|
|
184
|
-
} = opts;
|
|
185
|
-
const check = (candidate) => validate ? validate(candidate) : candidate.trim() ? "" : "Enter a value.";
|
|
186
|
-
let value = initialValue;
|
|
187
|
-
let error = "";
|
|
188
|
-
let wrapperEl = null;
|
|
189
|
-
let focusRequested = false;
|
|
190
|
-
return showDialog((done) => {
|
|
191
|
-
function rerender() {
|
|
192
|
-
const host = wrapperEl?.parentElement;
|
|
193
|
-
if (host) {
|
|
194
|
-
render(buildTpl(), host);
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
function confirm() {
|
|
198
|
-
error = check(value);
|
|
199
|
-
if (error) {
|
|
200
|
-
rerender();
|
|
201
|
-
return;
|
|
202
|
-
}
|
|
203
|
-
done(value.trim());
|
|
204
|
-
}
|
|
205
|
-
function onInput(e) {
|
|
206
|
-
value = e.target.value || "";
|
|
207
|
-
const next = check(value);
|
|
208
|
-
if (next !== error) {
|
|
209
|
-
error = next;
|
|
210
|
-
rerender();
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
function onKeydown(e) {
|
|
214
|
-
if (e.key === "Enter") {
|
|
215
|
-
confirm();
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
function onWrapperRef(el) {
|
|
219
|
-
if (el) {
|
|
220
|
-
wrapperEl = el;
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
function onFieldRef(el) {
|
|
224
|
-
if (!el || focusRequested) {
|
|
225
|
-
return;
|
|
226
|
-
}
|
|
227
|
-
focusRequested = true;
|
|
228
|
-
const field = el;
|
|
229
|
-
requestAnimationFrame(() => {
|
|
230
|
-
field.focus();
|
|
231
|
-
const input = field.shadowRoot?.querySelector("input");
|
|
232
|
-
if (!input || select === "none") {
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
if (select === "stem") {
|
|
236
|
-
const dot = input.value.lastIndexOf(".");
|
|
237
|
-
input.setSelectionRange(0, dot > 0 ? dot : input.value.length);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
input.select();
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
function buildTpl() {
|
|
244
|
-
return html`
|
|
245
|
-
<sp-dialog-wrapper
|
|
246
|
-
open
|
|
247
|
-
underlay
|
|
248
|
-
headline=${headline}
|
|
249
|
-
confirm-label=${confirmLabel}
|
|
250
|
-
cancel-label=${cancelLabel}
|
|
251
|
-
size="s"
|
|
252
|
-
@confirm=${confirm}
|
|
253
|
-
@cancel=${() => done(null)}
|
|
254
|
-
@close=${() => done(null)}
|
|
255
|
-
${ref(onWrapperRef)}
|
|
256
|
-
>
|
|
257
|
-
${message ? html`<p style="margin:0 0 8px">${message}</p>` : nothing}
|
|
258
|
-
<sp-textfield
|
|
259
|
-
style="width:100%"
|
|
260
|
-
placeholder=${placeholder}
|
|
261
|
-
value=${value}
|
|
262
|
-
?invalid=${Boolean(error)}
|
|
263
|
-
@input=${onInput}
|
|
264
|
-
@keydown=${onKeydown}
|
|
265
|
-
${ref(onFieldRef)}
|
|
266
|
-
>
|
|
267
|
-
${error ? html`<sp-help-text slot="negative-help-text">${error}</sp-help-text>` : nothing}
|
|
268
|
-
</sp-textfield>
|
|
269
|
-
</sp-dialog-wrapper>
|
|
270
|
-
`;
|
|
271
|
-
}
|
|
272
|
-
return buildTpl();
|
|
273
|
-
});
|
|
274
|
-
}
|
|
275
|
-
function openModal(template) {
|
|
276
|
-
const slot = document.createElement("div");
|
|
277
|
-
slot.style.pointerEvents = "auto";
|
|
278
|
-
_modalLayer.append(slot);
|
|
279
|
-
render(template, slot);
|
|
280
|
-
return {
|
|
281
|
-
close() {
|
|
282
|
-
render(nothing, slot);
|
|
283
|
-
slot.remove();
|
|
284
|
-
},
|
|
285
|
-
host: slot,
|
|
286
|
-
update(tpl) {
|
|
287
|
-
render(tpl, slot);
|
|
288
|
-
}
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
function renderPopover(template, opts = {}) {
|
|
292
|
-
const slot = document.createElement("div");
|
|
293
|
-
slot.style.pointerEvents = "auto";
|
|
294
|
-
const target = opts.layer === "modal" ? _modalLayer : opts.layer === "dialog" ? _dialogLayer : _popoverLayer;
|
|
295
|
-
target.append(slot);
|
|
296
|
-
render(template, slot);
|
|
297
|
-
let outsideClickHandler = null;
|
|
298
|
-
if (opts.dismissOnOutsideClick !== false) {
|
|
299
|
-
outsideClickHandler = (e) => {
|
|
300
|
-
if (!slot.contains(e.target)) {
|
|
301
|
-
handle.dismiss();
|
|
302
|
-
opts.onDismiss?.();
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
requestAnimationFrame(() => {
|
|
306
|
-
if (outsideClickHandler) {
|
|
307
|
-
document.addEventListener("mousedown", outsideClickHandler, true);
|
|
308
|
-
}
|
|
309
|
-
});
|
|
310
|
-
}
|
|
311
|
-
const handle = {
|
|
312
|
-
dismiss() {
|
|
313
|
-
if (outsideClickHandler) {
|
|
314
|
-
document.removeEventListener("mousedown", outsideClickHandler, true);
|
|
315
|
-
}
|
|
316
|
-
render(nothing, slot);
|
|
317
|
-
slot.remove();
|
|
318
|
-
},
|
|
319
|
-
host: slot,
|
|
320
|
-
update(tpl) {
|
|
321
|
-
render(tpl, slot);
|
|
322
|
-
}
|
|
323
|
-
};
|
|
324
|
-
return handle;
|
|
325
|
-
}
|
|
326
|
-
var _namedSlots = new Map;
|
|
327
|
-
function getLayerSlot(layer, id) {
|
|
328
|
-
const key = `${layer}:${id}`;
|
|
329
|
-
let slot = _namedSlots.get(key);
|
|
330
|
-
if (slot && slot.parentElement) {
|
|
331
|
-
return slot;
|
|
332
|
-
}
|
|
333
|
-
slot = document.createElement("div");
|
|
334
|
-
slot.style.pointerEvents = "auto";
|
|
335
|
-
const target = (layer === "popover" ? _popoverLayer : layer === "modal" ? _modalLayer : _dialogLayer) || document.body;
|
|
336
|
-
target.append(slot);
|
|
337
|
-
_namedSlots.set(key, slot);
|
|
338
|
-
return slot;
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
// src/panels/statusbar.ts
|
|
342
|
-
var statusMsg = "";
|
|
343
|
-
var statusTimeout;
|
|
344
|
-
var _rerender = null;
|
|
345
|
-
var _scope = null;
|
|
346
|
-
function setStatusbarRenderer(fn) {
|
|
347
|
-
_rerender = fn;
|
|
348
|
-
}
|
|
349
|
-
function mountStatusbar() {
|
|
350
|
-
_scope = effectScope();
|
|
351
|
-
_scope.run(() => {
|
|
352
|
-
effect(() => {
|
|
353
|
-
const tab = activeTab.value;
|
|
354
|
-
if (!tab) {
|
|
355
|
-
return;
|
|
356
|
-
}
|
|
357
|
-
tab.doc.document;
|
|
358
|
-
tab.doc.mode;
|
|
359
|
-
tab.session.selection;
|
|
360
|
-
tab.session.ui.stylebookSelection;
|
|
361
|
-
renderStatusbar();
|
|
362
|
-
});
|
|
363
|
-
});
|
|
364
|
-
statusbarEl?.addEventListener("click", _onStatusbarClick);
|
|
365
|
-
}
|
|
366
|
-
function esc(text) {
|
|
367
|
-
return String(text).replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
368
|
-
}
|
|
369
|
-
function renderStatusbar() {
|
|
370
|
-
const tab = activeTab.value;
|
|
371
|
-
const parts = [];
|
|
372
|
-
if (tab?.doc.mode === "content") {
|
|
373
|
-
parts.push("Content Mode");
|
|
374
|
-
}
|
|
375
|
-
if (tab?.session.selection?.length) {
|
|
376
|
-
const sel = tab.session.selection;
|
|
377
|
-
const node = getNodeAtPath(tab.doc.document, sel);
|
|
378
|
-
parts.push(`Selected: ${esc(nodeLabel(node))}`);
|
|
379
|
-
const pathSegments = [];
|
|
380
|
-
for (let i = 0;i < sel.length; ) {
|
|
381
|
-
const seg = sel[i];
|
|
382
|
-
const step = seg === "map" ? 1 : 2;
|
|
383
|
-
const subPath = sel.slice(0, i + step);
|
|
384
|
-
const childNode = getNodeAtPath(tab.doc.document, subPath);
|
|
385
|
-
const fallbackTag = childNode?.tag;
|
|
386
|
-
const label = childNode?.$prototype === "Array" ? "Repeater" : childNode?.tagName || (typeof fallbackTag === "string" ? fallbackTag : "") || (seg === "cases" ? String(sel[i + 1]) : `[${sel[i + 1]}]`);
|
|
387
|
-
const dataPath = JSON.stringify(subPath);
|
|
388
|
-
pathSegments.push(`<span class="sb-path-seg" data-path='${esc(dataPath)}'>${esc(label)}</span>`);
|
|
389
|
-
i += step;
|
|
390
|
-
}
|
|
391
|
-
parts.push(`Path: ${pathSegments.join(' <span class="sb-path-sep">></span> ')}`);
|
|
392
|
-
} else if (tab?.session.ui.stylebookSelection) {
|
|
393
|
-
const sel = tab.session.ui.stylebookSelection;
|
|
394
|
-
parts.push(`Style: ${esc(sel.replaceAll(" ", " > "))}`);
|
|
395
|
-
}
|
|
396
|
-
if (statusMsg) {
|
|
397
|
-
parts.push(esc(statusMsg));
|
|
398
|
-
}
|
|
399
|
-
statusbarEl.innerHTML = parts.join(" | ") || "Jx Studio";
|
|
400
|
-
}
|
|
401
|
-
function _onStatusbarClick(e) {
|
|
402
|
-
const target = e.target;
|
|
403
|
-
if (!target.classList.contains("sb-path-seg")) {
|
|
404
|
-
return;
|
|
405
|
-
}
|
|
406
|
-
const pathStr = target.dataset.path;
|
|
407
|
-
if (!pathStr) {
|
|
408
|
-
return;
|
|
409
|
-
}
|
|
410
|
-
try {
|
|
411
|
-
const path = JSON.parse(pathStr);
|
|
412
|
-
updateSession({ selection: path });
|
|
413
|
-
renderOnly("leftPanel", "rightPanel", "canvas");
|
|
414
|
-
} catch {}
|
|
415
|
-
}
|
|
416
|
-
function statusMessage(msg, duration = 3000) {
|
|
417
|
-
statusMsg = msg;
|
|
418
|
-
_rerender?.();
|
|
419
|
-
clearTimeout(statusTimeout);
|
|
420
|
-
statusTimeout = setTimeout(() => {
|
|
421
|
-
statusMsg = "";
|
|
422
|
-
_rerender?.();
|
|
423
|
-
}, duration);
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
export { errorMessage, view, applyPanelCollapse, initLayers, isModalOpen, showDialog, showConfirmDialog, showSaveDiscardDialog, showPromptDialog, openModal, renderPopover, getLayerSlot, setStatusbarRenderer, mountStatusbar, renderStatusbar, statusMessage };
|
|
427
|
-
|
|
428
|
-
//# debugId=FA6A26CABA076F0464756E2164756E21
|
|
429
|
-
//# sourceMappingURL=studio-0xgs7xrs.js.map
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../schema/src/parse.ts", "../src/view.ts", "../src/ui/layers.ts", "../src/panels/statusbar.ts"],
|
|
4
|
-
"sourcesContent": [
|
|
5
|
-
"/**\n * Typed parse boundary for Jx documents and configs.\n *\n * Raw `JSON.parse()` returns `any`, which silently disables checking for everything downstream.\n * These helpers are the single sanctioned crossing point: they parse, structurally verify, and\n * return the domain type — failures carry the source path.\n */\n\nimport type { JxClassDef, JxDocument, ProjectConfig } from \"../types\";\nimport { isJsonObject } from \"./guards\";\n\n// ─── Error helpers ──────────────────────────────────────────────────────────────\n\n/** Extract a human-readable message from a caught `unknown`. */\nexport function errorMessage(e: unknown): string {\n return e instanceof Error ? e.message : String(e);\n}\n\n/** Coerce a caught `unknown` to an Error without losing the original. */\nexport function toError(e: unknown): Error {\n return e instanceof Error ? e : new Error(String(e));\n}\n\n// ─── Parse helpers ──────────────────────────────────────────────────────────────\n\nfunction parseObject(text: string, sourcePath: string, what: string): Record<string, unknown> {\n let parsed: unknown;\n try {\n parsed = JSON.parse(text);\n } catch (error) {\n throw new Error(`Failed to parse ${what} at ${sourcePath}: ${errorMessage(error)}`, {\n cause: error,\n });\n }\n if (!isJsonObject(parsed)) {\n throw new Error(`Invalid ${what} at ${sourcePath}: expected a JSON object`);\n }\n return parsed;\n}\n\n/** Parse a Jx document (.json page/layout/component source). */\nexport function parseJxDocument(text: string, sourcePath: string): JxDocument {\n return parseObject(text, sourcePath, \"Jx document\") as JxDocument;\n}\n\n/** Parse a project.json site configuration. */\nexport function parseProjectConfig(text: string, sourcePath: string): ProjectConfig {\n return parseObject(text, sourcePath, \"project config\") as ProjectConfig;\n}\n\n/**\n * Parse a .class.json schema-defined class. Note: `$prototype: \"Class\"` is the canonical\n * discriminator but is not required — existing class files omit it.\n */\nexport function parseClassDef(text: string, sourcePath: string): JxClassDef {\n return parseObject(text, sourcePath, \"class definition\") as unknown as JxClassDef;\n}\n",
|
|
6
|
-
"/// <reference lib=\"dom\" />\n/**\n * View.js — Transient view state for Jx Studio\n *\n * Holds DOM references, editor instances, cleanup functions, and other mutable state that is the\n * OUTPUT of renderers (not the input). Separating this from persistent app state (in S via\n * store.js) makes renderer dependencies explicit.\n */\n\nimport type { editor } from \"monaco-editor\";\n\ninterface ViewState {\n panzoomWrap: HTMLElement | null;\n renderGeneration: number;\n centerObserver: ResizeObserver | null;\n needsCenter: boolean;\n panX: number;\n panY: number;\n prevCanvasMode: string | null;\n monacoEditor:\n | (editor.IStandaloneCodeEditor & {\n _ignoreNextChange?: boolean;\n })\n | null;\n functionEditor:\n | (editor.IStandaloneCodeEditor & {\n _ignoreNextChange?: boolean;\n _editingTarget?: string | null;\n })\n | null;\n blockActionBarEl: HTMLElement | null;\n selDragCleanup: (() => void) | null;\n dndCleanups: (() => void)[];\n canvasDndCleanups: (() => void)[];\n canvasEventCleanups: (() => void)[];\n forcedStyleTag: HTMLStyleElement | null;\n forcedAttrEl: HTMLElement | null;\n elementsCollapsed: Set<string>;\n elementsFilter: string;\n _currentDropTargetRow: HTMLElement | null;\n layerDragSourceHeight: number;\n _completionRegistered: boolean;\n showAddBreakpointForm: boolean;\n addBreakpointPreview: string;\n layoutSelection: unknown;\n leftTab: string;\n leftPanelCollapsed: boolean;\n rightPanelCollapsed: boolean;\n chatPanelCollapsed: boolean;\n _layersCollapsed: Set<string> | null;\n [key: string]: unknown;\n}\n\nexport const view: ViewState = {\n // Canvas infrastructure\n panzoomWrap: null,\n renderGeneration: 0,\n centerObserver: null,\n needsCenter: true,\n panX: 0,\n panY: 0,\n prevCanvasMode: null,\n\n // Editor instances\n monacoEditor: null,\n functionEditor: null,\n\n // Floating UI containers\n blockActionBarEl: null,\n\n // Selection & drag\n selDragCleanup: null,\n\n // Cleanup arrays (reset on each render cycle)\n dndCleanups: [],\n canvasDndCleanups: [],\n canvasEventCleanups: [],\n\n // Pseudo-state preview\n forcedStyleTag: null,\n forcedAttrEl: null,\n\n // Left panel / elements UI\n elementsCollapsed: new Set(),\n elementsFilter: \"\",\n\n // Drag interaction\n _currentDropTargetRow: null,\n layerDragSourceHeight: 0,\n\n // Editor state\n _completionRegistered: false,\n\n // Canvas / stylebook\n\n // Responsive breakpoints UI\n showAddBreakpointForm: false,\n addBreakpointPreview: \"\",\n\n // Layout selection (when user clicks a layout element)\n layoutSelection: null,\n\n // Global UI state (persists across tab switches)\n leftTab: \"layers\",\n leftPanelCollapsed: false,\n rightPanelCollapsed: false,\n chatPanelCollapsed: false,\n\n // Layers panel collapsed state\n _layersCollapsed: null,\n};\n\nconst COLLAPSE_STORAGE_KEY = \"jx-studio-panel-widths\";\n\nexport function applyPanelCollapse() {\n const app = document.querySelector(\"#app\");\n if (!app) {\n return;\n }\n app.classList.toggle(\"left-collapsed\", view.leftPanelCollapsed);\n app.classList.toggle(\"right-collapsed\", view.rightPanelCollapsed);\n app.classList.toggle(\"chat-collapsed\", view.chatPanelCollapsed);\n try {\n const saved = JSON.parse(localStorage.getItem(COLLAPSE_STORAGE_KEY) || \"{}\") as Record<\n string,\n unknown\n >;\n saved.leftCollapsed = view.leftPanelCollapsed;\n saved.rightCollapsed = view.rightPanelCollapsed;\n saved.chatCollapsed = view.chatPanelCollapsed;\n localStorage.setItem(COLLAPSE_STORAGE_KEY, JSON.stringify(saved));\n } catch {\n // Storage unavailable\n }\n}\n",
|
|
7
|
-
"/// <reference lib=\"dom\" />\n/**\n * The overlay layers — the only sanctioned way to open a popover, modal, or dialog in Studio.\n *\n * Everything renders into one of the three fixed hosts declared in index.html (#layer-popover,\n * #layer-modal, #layer-dialog), bound once at boot by initLayers(). Native browser dialogs\n * (`prompt`, `confirm`, `alert`) are not permitted anywhere in Studio — use `showPromptDialog`,\n * `showConfirmDialog`, or `showSaveDiscardDialog`. See studio-ui-guidelines.md §8.7.\n */\nimport { html, render as litRender, nothing } from \"lit-html\";\nimport { ref } from \"lit-html/directives/ref.js\";\nimport type { TemplateResult } from \"lit-html\";\n\nlet _popoverLayer: HTMLElement;\nlet _modalLayer: HTMLElement;\nlet _dialogLayer: HTMLElement;\n\nexport function initLayers() {\n _popoverLayer = document.querySelector(\"#layer-popover\") as HTMLElement;\n _modalLayer = document.querySelector(\"#layer-modal\") as HTMLElement;\n _dialogLayer = document.querySelector(\"#layer-dialog\") as HTMLElement;\n}\n\n/** Anything in the modal/dialog layers that paints a viewport-wide underlay over the app. */\nconst UNDERLAID = \"sp-dialog-wrapper[open], sp-underlay[open]\";\n\n/**\n * Whether a surface with an underlay is up — a dialog from {@link showDialog}, or an\n * {@link openModal} body that renders its own `sp-underlay`.\n *\n * Read by the app-level keyboard handlers, which must stand down while one is: an underlay swallows\n * every pointer event across the viewport, so leaving shortcuts live means <kbd>Delete</kbd>,\n * <kbd>Enter</kbd>, ⌘S and ⌘W keep hitting the document BEHIND a surface the author cannot even\n * click on. Derived from the live DOM rather than a registration counter, so the rule is simply\n * \"whatever blocks the mouse blocks the keyboard\" — no bookkeeping for a new modal to forget.\n */\nexport function isModalOpen(): boolean {\n return Boolean(_dialogLayer?.querySelector(UNDERLAID) || _modalLayer?.querySelector(UNDERLAID));\n}\n\n/** Focusable candidates in a dialog BODY, in the order a keyboard user would reach them. */\nconst BODY_FOCUSABLE =\n 'input, textarea, select, button, sp-textfield, sp-button, sp-picker, sp-checkbox, [tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Hand the keyboard to a freshly opened dialog.\n *\n * `sp-dialog-wrapper` only throws focus into itself when an `<sp-overlay>` drives it. Opened\n * directly through its `open` attribute — Studio's pattern, because this layer stack owns stacking\n * rather than Spectrum's overlay system — NOTHING does, so focus stays on whatever sits behind the\n * underlay: the dialog is unreachable by keyboard, <kbd>Escape</kbd> never reaches it, and\n * keystrokes keep landing in the app the underlay is blocking.\n *\n * Prefers the first focusable in the dialog BODY (a bespoke form's opening field), else the\n * wrapper's own cancel button — DialogWrapper renders cancel → secondary → confirm, so the first\n * shadow button is the least destructive landing spot. A body that already claimed focus\n * ({@link showPromptDialog}'s field) is left alone.\n */\nfunction focusDialog(slot: HTMLElement): void {\n // Deferred a frame: the wrapper's buttons live in a shadow root Spectrum renders asynchronously.\n requestAnimationFrame(() => {\n if (!slot.isConnected || slot.contains(document.activeElement)) {\n return;\n }\n const wrapper = slot.querySelector(\"sp-dialog-wrapper\");\n const target =\n slot.querySelector<HTMLElement>(BODY_FOCUSABLE) ??\n wrapper?.shadowRoot?.querySelector<HTMLElement>(\"sp-button\") ??\n null;\n target?.focus();\n });\n}\n\n/**\n * Show an ephemeral dialog. Returns a Promise that resolves when the dialog is dismissed.\n *\n * Takes the keyboard on open ({@link focusDialog}) and hands it back to the previously focused\n * element on close. <kbd>Escape</kbd> dismisses by firing the wrapper's `close` event, so each\n * helper's own `@close` binding decides what \"dismissed\" resolves to; a bespoke body with no\n * `sp-dialog-wrapper` owns its own keys.\n *\n * @template T\n * @param {(done: (value: T) => void) => import(\"lit-html\").TemplateResult} templateFn\n * @returns {Promise<T>}\n */\nexport function showDialog<T>(\n templateFn: (done: (value: T) => void) => TemplateResult,\n): Promise<T> {\n return new Promise((resolve) => {\n const slot = document.createElement(\"div\");\n slot.style.pointerEvents = \"auto\";\n _dialogLayer.append(slot);\n // Whoever held focus before the modal took it, so it can be handed back (a dialog opened from a\n // Toolbar button returns the caret to that button, not to <body>).\n const restoreTo = document.activeElement instanceof HTMLElement ? document.activeElement : null;\n let resolved = false;\n const onKeydown = (e: KeyboardEvent) => {\n if (e.key !== \"Escape\") {\n return;\n }\n const wrapper = slot.querySelector(\"sp-dialog-wrapper\");\n if (!wrapper) {\n return;\n }\n // Stop it ALSO reaching the app behind (which clears the canvas selection on Escape).\n e.preventDefault();\n e.stopPropagation();\n wrapper.dispatchEvent(new Event(\"close\", { bubbles: true }));\n };\n slot.addEventListener(\"keydown\", onKeydown);\n const done = (value: T) => {\n if (resolved) {\n return;\n }\n resolved = true;\n slot.removeEventListener(\"keydown\", onKeydown);\n litRender(nothing, slot);\n slot.remove();\n if (restoreTo?.isConnected) {\n restoreTo.focus();\n }\n resolve(value);\n };\n litRender(templateFn(done), slot);\n focusDialog(slot);\n });\n}\n\n/**\n * Show a confirm/cancel dialog. Returns true if confirmed, false otherwise.\n *\n * @param {string} headline\n * @param {string | import(\"lit-html\").TemplateResult} message\n * @param {{ confirmLabel?: string; cancelLabel?: string; destructive?: boolean }} [opts]\n * @returns {Promise<boolean>}\n */\nexport function showConfirmDialog(\n headline: string,\n message: string | TemplateResult,\n opts: {\n confirmLabel?: string;\n cancelLabel?: string;\n destructive?: boolean;\n } = {},\n) {\n const { confirmLabel = \"Confirm\", cancelLabel = \"Cancel\", destructive = false } = opts;\n return showDialog(\n (done) => html`\n <sp-dialog-wrapper\n open\n underlay\n headline=${headline}\n confirm-label=${confirmLabel}\n cancel-label=${cancelLabel}\n size=\"s\"\n @confirm=${() => done(true)}\n @cancel=${() => done(false)}\n @close=${() => done(false)}\n class=${destructive ? \"dialog-destructive\" : \"\"}\n >\n <p>${message}</p>\n </sp-dialog-wrapper>\n `,\n );\n}\n\n/**\n * Show a three-way Save / Discard / Cancel dialog. Resolves \"save\", \"discard\", or \"cancel\"\n * (dismiss/close counts as cancel).\n *\n * @param {string} headline\n * @param {string | import(\"lit-html\").TemplateResult} message\n * @param {{ saveLabel?: string; discardLabel?: string; cancelLabel?: string }} [opts]\n * @returns {Promise<\"save\" | \"discard\" | \"cancel\">}\n */\nexport function showSaveDiscardDialog(\n headline: string,\n message: string | TemplateResult,\n opts: {\n saveLabel?: string;\n discardLabel?: string;\n cancelLabel?: string;\n } = {},\n): Promise<\"save\" | \"discard\" | \"cancel\"> {\n const { saveLabel = \"Save\", discardLabel = \"Discard\", cancelLabel = \"Cancel\" } = opts;\n return showDialog<\"save\" | \"discard\" | \"cancel\">(\n (done) => html`\n <sp-dialog-wrapper\n open\n underlay\n headline=${headline}\n confirm-label=${saveLabel}\n secondary-label=${discardLabel}\n cancel-label=${cancelLabel}\n size=\"s\"\n @confirm=${() => done(\"save\")}\n @secondary=${() => done(\"discard\")}\n @cancel=${() => done(\"cancel\")}\n @close=${() => done(\"cancel\")}\n >\n <p>${message}</p>\n </sp-dialog-wrapper>\n `,\n );\n}\n\n/** Options accepted by {@link showPromptDialog}. */\nexport interface PromptDialogOptions {\n /** Label on the confirming button. */\n confirmLabel?: string;\n /** Label on the dismissing button. */\n cancelLabel?: string;\n /** Explanatory copy rendered above the text field. */\n message?: string | TemplateResult;\n /** Placeholder shown while the field is empty. */\n placeholder?: string;\n /** How much of the pre-filled value to select once the field takes focus. */\n select?: \"all\" | \"stem\" | \"none\";\n /**\n * Validate the raw field value. Return an empty string when valid, or a message to show as\n * negative help text (which also blocks confirmation). Defaults to \"must not be blank\".\n */\n validate?: (value: string) => string;\n /** Pre-filled value. */\n value?: string;\n}\n\n/**\n * Show a single-field text-entry dialog — the Spectrum replacement for `window.prompt()`.\n *\n * Resolves the trimmed value, or `null` when cancelled/dismissed. Confirming with an invalid value\n * keeps the dialog open and surfaces the validation message as negative help text.\n *\n * @param {string} headline\n * @param {PromptDialogOptions} [opts]\n * @returns {Promise<string | null>}\n */\nexport function showPromptDialog(\n headline: string,\n opts: PromptDialogOptions = {},\n): Promise<string | null> {\n const {\n cancelLabel = \"Cancel\",\n confirmLabel = \"OK\",\n message,\n placeholder = \"\",\n select = \"all\",\n validate,\n value: initialValue = \"\",\n } = opts;\n\n const check = (candidate: string) =>\n validate ? validate(candidate) : candidate.trim() ? \"\" : \"Enter a value.\";\n\n let value = initialValue;\n let error = \"\";\n let wrapperEl: HTMLElement | null = null;\n let focusRequested = false;\n\n return showDialog<string | null>((done) => {\n function rerender() {\n // Resolved lazily: lit commits element refs before inserting the fragment, so the host is\n // Only reachable once the first render has landed.\n const host = wrapperEl?.parentElement;\n if (host) {\n litRender(buildTpl(), host);\n }\n }\n\n function confirm() {\n error = check(value);\n if (error) {\n rerender();\n return;\n }\n done(value.trim());\n }\n\n function onInput(e: Event) {\n value = (e.target as HTMLInputElement).value || \"\";\n const next = check(value);\n if (next !== error) {\n error = next;\n rerender();\n }\n }\n\n function onKeydown(e: KeyboardEvent) {\n if (e.key === \"Enter\") {\n confirm();\n }\n }\n\n /** Capture the dialog element so validation errors can re-render in place. */\n function onWrapperRef(el?: Element) {\n if (el) {\n wrapperEl = el as HTMLElement;\n }\n }\n\n /** Focus (and optionally select) the field once, on first render only. */\n function onFieldRef(el?: Element) {\n if (!el || focusRequested) {\n return;\n }\n focusRequested = true;\n const field = el as HTMLElement;\n requestAnimationFrame(() => {\n field.focus();\n const input = field.shadowRoot?.querySelector(\"input\");\n if (!input || select === \"none\") {\n return;\n }\n if (select === \"stem\") {\n const dot = input.value.lastIndexOf(\".\");\n input.setSelectionRange(0, dot > 0 ? dot : input.value.length);\n return;\n }\n input.select();\n });\n }\n\n function buildTpl() {\n return html`\n <sp-dialog-wrapper\n open\n underlay\n headline=${headline}\n confirm-label=${confirmLabel}\n cancel-label=${cancelLabel}\n size=\"s\"\n @confirm=${confirm}\n @cancel=${() => done(null)}\n @close=${() => done(null)}\n ${ref(onWrapperRef)}\n >\n ${\n // Spectrum resets <p> margins to 0, so without this the copy sits flush on the field.\n message ? html`<p style=\"margin:0 0 8px\">${message}</p>` : nothing\n }\n <sp-textfield\n style=\"width:100%\"\n placeholder=${placeholder}\n value=${value}\n ?invalid=${Boolean(error)}\n @input=${onInput}\n @keydown=${onKeydown}\n ${ref(onFieldRef)}\n >\n ${\n error\n ? html`<sp-help-text slot=\"negative-help-text\">${error}</sp-help-text>`\n : nothing\n }\n </sp-textfield>\n </sp-dialog-wrapper>\n `;\n }\n\n return buildTpl();\n });\n}\n\n/**\n * Open a persistent modal. Returns a handle with update() and close() methods.\n *\n * @param {import(\"lit-html\").TemplateResult} template\n */\nexport function openModal(template: TemplateResult) {\n const slot = document.createElement(\"div\");\n slot.style.pointerEvents = \"auto\";\n _modalLayer.append(slot);\n litRender(template, slot);\n return {\n close() {\n litRender(nothing, slot);\n slot.remove();\n },\n host: slot,\n /** @param {import(\"lit-html\").TemplateResult} tpl */\n update(tpl: TemplateResult) {\n litRender(tpl, slot);\n },\n };\n}\n\n/**\n * Render a popover into a layer.\n *\n * @param {import(\"lit-html\").TemplateResult} template\n * @param {{\n * dismissOnOutsideClick?: boolean;\n * onDismiss?: () => void;\n * layer?: \"popover\" | \"modal\" | \"dialog\";\n * }} [opts]\n */\nexport function renderPopover(\n template: TemplateResult,\n opts: {\n dismissOnOutsideClick?: boolean;\n onDismiss?: () => void;\n layer?: \"popover\" | \"modal\" | \"dialog\";\n } = {},\n) {\n const slot = document.createElement(\"div\");\n slot.style.pointerEvents = \"auto\";\n const target =\n opts.layer === \"modal\" ? _modalLayer : opts.layer === \"dialog\" ? _dialogLayer : _popoverLayer;\n target.append(slot);\n litRender(template, slot);\n\n let outsideClickHandler: ((e: MouseEvent) => void) | null = null;\n if (opts.dismissOnOutsideClick !== false) {\n outsideClickHandler = (e: MouseEvent) => {\n if (!slot.contains(e.target as Node)) {\n handle.dismiss();\n opts.onDismiss?.();\n }\n };\n requestAnimationFrame(() => {\n if (outsideClickHandler) {\n document.addEventListener(\"mousedown\", outsideClickHandler, true);\n }\n });\n }\n\n const handle = {\n dismiss() {\n if (outsideClickHandler) {\n document.removeEventListener(\"mousedown\", outsideClickHandler, true);\n }\n litRender(nothing, slot);\n slot.remove();\n },\n host: slot,\n /** @param {import(\"lit-html\").TemplateResult} tpl */\n update(tpl: TemplateResult) {\n litRender(tpl, slot);\n },\n };\n return handle;\n}\n\nconst _namedSlots = new Map<string, HTMLElement>();\n\n/**\n * Get or create a named slot in a layer. Useful for persistent popovers like zoom indicator.\n *\n * @param {\"popover\" | \"modal\" | \"dialog\"} layer\n * @param {string} id\n * @returns {HTMLElement}\n */\nexport function getLayerSlot(layer: \"popover\" | \"modal\" | \"dialog\", id: string) {\n const key = `${layer}:${id}`;\n let slot = _namedSlots.get(key);\n if (slot && slot.parentElement) {\n return slot;\n }\n\n slot = document.createElement(\"div\");\n slot.style.pointerEvents = \"auto\";\n const target =\n (layer === \"popover\" ? _popoverLayer : layer === \"modal\" ? _modalLayer : _dialogLayer) ||\n document.body;\n target.append(slot);\n _namedSlots.set(key, slot);\n return slot;\n}\n\n/**\n * Clear a named layer slot (remove from DOM and map).\n *\n * @param {\"popover\" | \"modal\" | \"dialog\"} layer\n * @param {string} id\n */\nexport function clearLayerSlot(layer: \"popover\" | \"modal\" | \"dialog\", id: string) {\n const key = `${layer}:${id}`;\n const slot = _namedSlots.get(key);\n if (slot) {\n litRender(nothing, slot);\n slot.remove();\n _namedSlots.delete(key);\n }\n}\n",
|
|
8
|
-
"/// <reference lib=\"dom\" />\n/** Statusbar — status message display for Jx Studio */\n\nimport { getNodeAtPath, nodeLabel, renderOnly, statusbarEl, updateSession } from \"../store\";\nimport { effect, effectScope } from \"../reactivity\";\nimport { activeTab } from \"../workspace/workspace\";\nimport type { JxPath } from \"../state\";\nimport type { EffectScope } from \"@vue/reactivity\";\n\n// ─── Module state ────────────────────────────────────────────────────────────\n\nlet statusMsg = \"\";\n/** @type {ReturnType<typeof setTimeout> | undefined} */\nlet statusTimeout: ReturnType<typeof setTimeout> | undefined;\nlet _rerender: (() => void) | null = null;\nlet _scope: EffectScope | null = null;\n\n/**\n * Register the callback used to re-render the statusbar. Called once from studio.js during init.\n *\n * @param {() => void} fn\n */\nexport function setStatusbarRenderer(fn: () => void) {\n _rerender = fn;\n}\n\n/** Subscribe the statusbar to state changes via reactive effect. */\nexport function mountStatusbar() {\n _scope = effectScope();\n _scope.run(() => {\n effect(() => {\n const tab = activeTab.value;\n if (!tab) {\n return;\n }\n // Track relevant reactive properties\n void tab.doc.document;\n void tab.doc.mode;\n void tab.session.selection;\n void tab.session.ui.stylebookSelection;\n renderStatusbar();\n });\n });\n\n statusbarEl?.addEventListener(\"click\", _onStatusbarClick);\n}\n\nexport function unmountStatusbar() {\n _scope?.stop();\n _scope = null;\n statusbarEl?.removeEventListener(\"click\", _onStatusbarClick);\n}\n\n// ─── Statusbar ───────────────────────────────────────────────────────────────\n\n/** @param {string} text */\nfunction esc(text: string) {\n return String(text).replaceAll(\"&\", \"&\").replaceAll(\"<\", \"<\").replaceAll(\">\", \">\");\n}\n\n/** Render the statusbar content. */\nexport function renderStatusbar() {\n const tab = activeTab.value;\n const parts = [];\n if (tab?.doc.mode === \"content\") {\n parts.push(\"Content Mode\");\n }\n if (tab?.session.selection?.length) {\n const sel = tab.session.selection as JxPath;\n const node = getNodeAtPath(tab.doc.document, sel);\n parts.push(`Selected: ${esc(nodeLabel(node))}`);\n\n // Walk the path one structural step at a time. Most steps are `[\"children\", index]` or\n // `[\"cases\", name]` pairs, but a repeater template is reached by a lone `\"map\"` segment — so the\n // Step width varies. Emitting a crumb per node keeps the array pseudo-element (\"Repeater\") and\n // Its template both visible instead of collapsing the array into a bare `[index]`.\n const pathSegments = [];\n for (let i = 0; i < sel.length;) {\n const seg = sel[i];\n const step = seg === \"map\" ? 1 : 2;\n const subPath = sel.slice(0, i + step);\n const childNode = getNodeAtPath(tab.doc.document, subPath);\n const fallbackTag = childNode?.tag;\n const label =\n childNode?.$prototype === \"Array\"\n ? \"Repeater\"\n : childNode?.tagName ||\n (typeof fallbackTag === \"string\" ? fallbackTag : \"\") ||\n (seg === \"cases\" ? String(sel[i + 1]) : `[${sel[i + 1]}]`);\n const dataPath = JSON.stringify(subPath);\n pathSegments.push(\n `<span class=\"sb-path-seg\" data-path='${esc(dataPath)}'>${esc(label)}</span>`,\n );\n i += step;\n }\n parts.push(`Path: ${pathSegments.join(' <span class=\"sb-path-sep\">></span> ')}`);\n } else if (tab?.session.ui.stylebookSelection) {\n const sel = tab.session.ui.stylebookSelection;\n parts.push(`Style: ${esc(sel.replaceAll(\" \", \" > \"))}`);\n }\n if (statusMsg) {\n parts.push(esc(statusMsg));\n }\n statusbarEl.innerHTML = parts.join(\" | \") || \"Jx Studio\";\n}\n\n/** @param {Event} e */\nfunction _onStatusbarClick(e: Event) {\n const target = e.target as HTMLElement;\n if (!target.classList.contains(\"sb-path-seg\")) {\n return;\n }\n const pathStr = target.dataset.path;\n if (!pathStr) {\n return;\n }\n try {\n const path = JSON.parse(pathStr) as JxPath;\n updateSession({ selection: path });\n renderOnly(\"leftPanel\", \"rightPanel\", \"canvas\");\n } catch {\n // Ignore\n }\n}\n\n/**\n * Show a temporary status message.\n *\n * @param {string} msg\n * @param {number} [duration]\n */\nexport function statusMessage(msg: string, duration = 3000) {\n statusMsg = msg;\n _rerender?.();\n clearTimeout(statusTimeout);\n statusTimeout = setTimeout(() => {\n statusMsg = \"\";\n _rerender?.();\n }, duration);\n}\n"
|
|
9
|
-
],
|
|
10
|
-
"mappings": "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",
|
|
11
|
-
"debugId": "FA6A26CABA076F0464756E2164756E21",
|
|
12
|
-
"names": []
|
|
13
|
-
}
|