@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
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Deploy-checklist.ts — what is MISSING before this project can ship, as an ordered list.
|
|
4
|
+
*
|
|
5
|
+
* What this replaces: a Cloudflare form that opened asking for a GitHub owner and repository on a
|
|
6
|
+
* project that had never been `git init`-ed, failed with a Cloudflare API error naming neither
|
|
7
|
+
* fact, and offered no way forward. Publishing is a CHAIN — the repository exists, it has a remote
|
|
8
|
+
* and the remote is current, a provider is connected, a deployment happened — and every link is a
|
|
9
|
+
* different command owned by a different module. Nothing in Studio held the chain, so no surface
|
|
10
|
+
* could say which link was missing.
|
|
11
|
+
*
|
|
12
|
+
* This module is the chain (plan §9.5):
|
|
13
|
+
*
|
|
14
|
+
* - {@link deployChecklist} is the ordered prerequisite list, each step carrying **the command that
|
|
15
|
+
* satisfies it** rather than a sentence telling the reader to go and find one;
|
|
16
|
+
* - {@link renderDeployChecklist} is its rendering, and it lives in the Bottom dock's **Activity**
|
|
17
|
+
* tab because a deploy is a long operation with a log and the dock cap is four (§2 principle 9).
|
|
18
|
+
* It draws in the Activity vocabulary — `activity-row`, `activity-steps`, `activity-step--done` —
|
|
19
|
+
* for the same reason: a fifth visual idiom for "an ordered list of stages that finish" would be
|
|
20
|
+
* a second design of a thing the tab already has;
|
|
21
|
+
* - {@link deployStatusItem} is the status bar's project-field item, whose **label is the next
|
|
22
|
+
* blocking prerequisite** — the shortcut and the explanation in one 24px item, and ambient state
|
|
23
|
+
* rather than a transient message, which is the only thing that bar carries (§3.2 ⑫).
|
|
24
|
+
*
|
|
25
|
+
* **A step it cannot answer says `unknown`.** `services/references.ts` established the rule and it
|
|
26
|
+
* matters more here: "no deployment" and "we have not asked Cloudflare" look identical from inside
|
|
27
|
+
* the app, and a checklist that renders the second as the first tells the user to redo a deploy
|
|
28
|
+
* that already succeeded. The observation cache ({@link noteDeployment}) is written by the code
|
|
29
|
+
* that actually asked — the Publish panel's load and {@link import("./publish-commands").runDeploy}
|
|
30
|
+
* — and by nothing else.
|
|
31
|
+
*
|
|
32
|
+
* **No secret reaches this module.** A provider is connected or it is not; the token that proves it
|
|
33
|
+
* is read by `platforms/devserver.ts` on its way to the same-origin proxy and by nothing that
|
|
34
|
+
* renders (`studio.md` §15 rule 1).
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
import { html, nothing } from "lit-html";
|
|
38
|
+
import { activeRegistry } from "../commands/active-registry";
|
|
39
|
+
import { platformSupportsPublish } from "./pages-service";
|
|
40
|
+
import { projectState } from "../store";
|
|
41
|
+
import { shell } from "../shell";
|
|
42
|
+
import type { DeployConfig, ProjectConfig } from "@jxsuite/schema/types";
|
|
43
|
+
import type { PagesDeploymentInfo } from "./pages-service";
|
|
44
|
+
import type { TemplateResult } from "lit-html";
|
|
45
|
+
|
|
46
|
+
/** The chain, in the order the links must be forged. */
|
|
47
|
+
export const DEPLOY_STEP_IDS = ["repo", "remote", "provider", "deployed"] as const;
|
|
48
|
+
|
|
49
|
+
export type DeployStepId = (typeof DEPLOY_STEP_IDS)[number];
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Whether a link is forged.
|
|
53
|
+
*
|
|
54
|
+
* `unknown` is not a third kind of "no": it is the honest answer when the fact lives on a server
|
|
55
|
+
* nobody has asked. It blocks like a `todo` and it READS differently, which is the whole point.
|
|
56
|
+
*/
|
|
57
|
+
export type DeployStepState = "done" | "todo" | "unknown";
|
|
58
|
+
|
|
59
|
+
/** One prerequisite. */
|
|
60
|
+
export interface DeployStep {
|
|
61
|
+
readonly id: DeployStepId;
|
|
62
|
+
/** Imperative, and short enough to be a 24px status-bar label. */
|
|
63
|
+
readonly label: string;
|
|
64
|
+
readonly state: DeployStepState;
|
|
65
|
+
/** One sentence: what is true now, or what satisfying it buys. Never a secret. */
|
|
66
|
+
readonly detail: string;
|
|
67
|
+
/** The command that satisfies it — an id, so the row inherits title, chord and refusal. */
|
|
68
|
+
readonly command: string;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** The active project's configuration, or null when no project is open. */
|
|
72
|
+
function currentConfig(): ProjectConfig | null {
|
|
73
|
+
return (projectState?.projectConfig as ProjectConfig | undefined) ?? null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* The connected provider block, or `undefined`.
|
|
78
|
+
*
|
|
79
|
+
* Exported because the Publish panel asks the same question and P6's lesson was that two copies of
|
|
80
|
+
* one derivation drift apart in silence — there is one reader of `build.deploy` in the app.
|
|
81
|
+
*/
|
|
82
|
+
export function currentDeploy(): DeployConfig | undefined {
|
|
83
|
+
return currentConfig()?.build?.deploy;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* The last deployment anybody actually observed, or null when nobody has asked.
|
|
88
|
+
*
|
|
89
|
+
* Module state rather than a field on the config: a deployment is a fact about Cloudflare, not
|
|
90
|
+
* about this repository, and writing it into `project.json` would commit a timestamp that is stale
|
|
91
|
+
* the moment it is pushed.
|
|
92
|
+
*/
|
|
93
|
+
let _observed: PagesDeploymentInfo | null = null;
|
|
94
|
+
|
|
95
|
+
/** Whether anybody has asked at all — the difference between `todo` and `unknown`. */
|
|
96
|
+
let _asked = false;
|
|
97
|
+
|
|
98
|
+
/** Record what Cloudflare answered. `null` means "asked, and there are none". */
|
|
99
|
+
export function noteDeployment(info: PagesDeploymentInfo | null): void {
|
|
100
|
+
_observed = info;
|
|
101
|
+
_asked = true;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** The observed deployment, or null. */
|
|
105
|
+
export function observedDeployment(): PagesDeploymentInfo | null {
|
|
106
|
+
return _observed;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Forget the observation — project close, and every test that asserts the `unknown` branch. */
|
|
110
|
+
export function forgetDeployment(): void {
|
|
111
|
+
_observed = null;
|
|
112
|
+
_asked = false;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function repoStep(): DeployStep {
|
|
116
|
+
const { status } = shell.git;
|
|
117
|
+
if (status === null) {
|
|
118
|
+
return {
|
|
119
|
+
command: "git.init",
|
|
120
|
+
detail: "Source control has not reported on this project yet.",
|
|
121
|
+
id: "repo",
|
|
122
|
+
label: "Track this project with git",
|
|
123
|
+
state: "unknown",
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
return status.isRepo
|
|
127
|
+
? {
|
|
128
|
+
command: "git.init",
|
|
129
|
+
detail: `Tracked${status.branch ? ` — on branch ${status.branch}` : ""}.`,
|
|
130
|
+
id: "repo",
|
|
131
|
+
label: "Track this project with git",
|
|
132
|
+
state: "done",
|
|
133
|
+
}
|
|
134
|
+
: {
|
|
135
|
+
command: "git.init",
|
|
136
|
+
detail: "A deploy ships what the repository holds, so the repository comes first.",
|
|
137
|
+
id: "repo",
|
|
138
|
+
label: "Track this project with git",
|
|
139
|
+
state: "todo",
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function remoteStep(): DeployStep {
|
|
144
|
+
const { status } = shell.git;
|
|
145
|
+
if (status === null || !status.isRepo) {
|
|
146
|
+
return {
|
|
147
|
+
command: "git.createGithubRepository",
|
|
148
|
+
detail: "A remote is where the provider builds from.",
|
|
149
|
+
id: "remote",
|
|
150
|
+
label: "Push to a remote",
|
|
151
|
+
state: status === null ? "unknown" : "todo",
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
// A GitStatus from a host that reports no remotes array is not a repo with remotes — it is a
|
|
155
|
+
// Host that did not say. Reading through it took the whole status bar down with a TypeError.
|
|
156
|
+
if ((status.remotes ?? []).length === 0) {
|
|
157
|
+
return {
|
|
158
|
+
command: "git.createGithubRepository",
|
|
159
|
+
detail: "This repository is local only — nothing outside this machine can build it.",
|
|
160
|
+
id: "remote",
|
|
161
|
+
label: "Push to a remote",
|
|
162
|
+
state: "todo",
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
return status.ahead > 0
|
|
166
|
+
? {
|
|
167
|
+
command: "git.push",
|
|
168
|
+
detail: `${status.ahead} commit(s) are not on ${status.remotes[0]} yet, so a build would ship the older tree.`,
|
|
169
|
+
id: "remote",
|
|
170
|
+
label: "Push to a remote",
|
|
171
|
+
state: "todo",
|
|
172
|
+
}
|
|
173
|
+
: {
|
|
174
|
+
command: "git.push",
|
|
175
|
+
detail: `Up to date with ${status.remotes[0]}.`,
|
|
176
|
+
id: "remote",
|
|
177
|
+
label: "Push to a remote",
|
|
178
|
+
state: "done",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
function providerStep(): DeployStep {
|
|
183
|
+
const deploy = currentDeploy();
|
|
184
|
+
if (deploy) {
|
|
185
|
+
return {
|
|
186
|
+
command: "publish.setUp",
|
|
187
|
+
detail: `Cloudflare Pages project ${deploy.projectName}.`,
|
|
188
|
+
id: "provider",
|
|
189
|
+
label: "Connect a deploy provider",
|
|
190
|
+
state: "done",
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
return platformSupportsPublish()
|
|
194
|
+
? {
|
|
195
|
+
command: "publish.setUp",
|
|
196
|
+
detail: "Connecting one makes every commit build and publish on its own.",
|
|
197
|
+
id: "provider",
|
|
198
|
+
label: "Connect a deploy provider",
|
|
199
|
+
state: "todo",
|
|
200
|
+
}
|
|
201
|
+
: {
|
|
202
|
+
command: "publish.setUp",
|
|
203
|
+
detail:
|
|
204
|
+
"This platform cannot reach the Cloudflare API. Commit and push instead — your host " +
|
|
205
|
+
"runs bunx jx build and serves dist/.",
|
|
206
|
+
id: "provider",
|
|
207
|
+
label: "Connect a deploy provider",
|
|
208
|
+
state: "unknown",
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function deployedStep(): DeployStep {
|
|
213
|
+
if (!currentDeploy()) {
|
|
214
|
+
return {
|
|
215
|
+
command: "publish.deploy",
|
|
216
|
+
detail: "Nothing has been deployed, because no provider is connected.",
|
|
217
|
+
id: "deployed",
|
|
218
|
+
label: "Deploy",
|
|
219
|
+
state: "todo",
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
if (!_asked) {
|
|
223
|
+
return {
|
|
224
|
+
command: "publish.deploy",
|
|
225
|
+
detail: "Cloudflare has not been asked yet, so this is not a claim that nothing shipped.",
|
|
226
|
+
id: "deployed",
|
|
227
|
+
label: "Deploy",
|
|
228
|
+
state: "unknown",
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
if (_observed === null) {
|
|
232
|
+
return {
|
|
233
|
+
command: "publish.deploy",
|
|
234
|
+
detail: "Cloudflare reports no deployments for this project.",
|
|
235
|
+
id: "deployed",
|
|
236
|
+
label: "Deploy",
|
|
237
|
+
state: "todo",
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
return _observed.status === "success"
|
|
241
|
+
? {
|
|
242
|
+
command: "publish.deploy",
|
|
243
|
+
detail: `${_observed.environment} — ${_observed.url}`,
|
|
244
|
+
id: "deployed",
|
|
245
|
+
label: "Deploy",
|
|
246
|
+
state: "done",
|
|
247
|
+
}
|
|
248
|
+
: {
|
|
249
|
+
command: "publish.deploy",
|
|
250
|
+
detail: `Last deployment ${_observed.stage}: ${_observed.status}.`,
|
|
251
|
+
id: "deployed",
|
|
252
|
+
label: "Deploy",
|
|
253
|
+
state: "todo",
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
/** The four prerequisites, in order. */
|
|
258
|
+
export function deployChecklist(): DeployStep[] {
|
|
259
|
+
return [repoStep(), remoteStep(), providerStep(), deployedStep()];
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* The first link that is not forged, or null when the chain is whole.
|
|
264
|
+
*
|
|
265
|
+
* `unknown` counts as not forged: the app may not claim a step is done on the strength of not
|
|
266
|
+
* having asked.
|
|
267
|
+
*/
|
|
268
|
+
export function nextDeployStep(): DeployStep | null {
|
|
269
|
+
return deployChecklist().find((step) => step.state !== "done") ?? null;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** What the status bar's project field shows. `null` when there is no project to deploy. */
|
|
273
|
+
export interface DeployStatusItem {
|
|
274
|
+
readonly command: string;
|
|
275
|
+
readonly label: string;
|
|
276
|
+
readonly title: string;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The status-bar item — ambient state, never a transient message.
|
|
281
|
+
*
|
|
282
|
+
* Its LABEL is the next blocking prerequisite, so the item explains itself: a reader who does not
|
|
283
|
+
* know what is missing learns it from the same 24px string that fixes it.
|
|
284
|
+
*/
|
|
285
|
+
export function deployStatusItem(): DeployStatusItem | null {
|
|
286
|
+
if (!projectState) {
|
|
287
|
+
return null;
|
|
288
|
+
}
|
|
289
|
+
const next = nextDeployStep();
|
|
290
|
+
if (next) {
|
|
291
|
+
return { command: next.command, label: next.label, title: next.detail };
|
|
292
|
+
}
|
|
293
|
+
const url = currentDeploy()?.productionUrl;
|
|
294
|
+
return {
|
|
295
|
+
command: "publish.openDashboard",
|
|
296
|
+
label: "Deployed",
|
|
297
|
+
title: url ?? "Everything this project needs to ship is in place.",
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// ─── Rendering ────────────────────────────────────────────────────────────────
|
|
302
|
+
|
|
303
|
+
/** Checklist state → the Activity tab's step classes, so one vocabulary draws both. */
|
|
304
|
+
const STEP_CLASS: Readonly<Record<DeployStepState, string>> = {
|
|
305
|
+
done: "done",
|
|
306
|
+
todo: "pending",
|
|
307
|
+
unknown: "pending",
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const STEP_ICON: Readonly<Record<DeployStepState, string>> = {
|
|
311
|
+
done: "✓",
|
|
312
|
+
todo: "·",
|
|
313
|
+
unknown: "?",
|
|
314
|
+
};
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* The next action, as a button — or nothing.
|
|
318
|
+
*
|
|
319
|
+
* A command the registry does not have, or whose `when` is false, renders NOTHING rather than a
|
|
320
|
+
* dead label. The status bar's `itemTpl` takes the same position for the same reason: a surface may
|
|
321
|
+
* choose whether to show a command, never whether it exists.
|
|
322
|
+
*/
|
|
323
|
+
function actionTpl(step: DeployStep | null): TemplateResult | typeof nothing {
|
|
324
|
+
if (!step) {
|
|
325
|
+
return nothing;
|
|
326
|
+
}
|
|
327
|
+
const registry = activeRegistry();
|
|
328
|
+
const command = registry?.get(step.command);
|
|
329
|
+
if (!registry || !command || !registry.isVisible(step.command)) {
|
|
330
|
+
return nothing;
|
|
331
|
+
}
|
|
332
|
+
const reason = registry.disabledReason(step.command);
|
|
333
|
+
return html`<sp-action-button
|
|
334
|
+
size="s"
|
|
335
|
+
?disabled=${reason !== undefined}
|
|
336
|
+
title=${reason ? `${command.title} — requires ${reason}` : command.title}
|
|
337
|
+
@click=${() => {
|
|
338
|
+
void registry.run(step.command);
|
|
339
|
+
}}
|
|
340
|
+
>${command.title}</sp-action-button
|
|
341
|
+
>`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* The checklist, as the Activity tab draws it — or nothing, when no project is open.
|
|
346
|
+
*
|
|
347
|
+
* Rendered ABOVE the operation list rather than as an entry in it: an activity is something that
|
|
348
|
+
* happened, and this is something that has not. It carries no `data-jx-region` stamp — the region
|
|
349
|
+
* is `dock.bottom/panel:activity`, which the dock derives, and a hand-stamped leaf is a committed
|
|
350
|
+
* budget (§13.2) that this does not need to spend.
|
|
351
|
+
*/
|
|
352
|
+
export function renderDeployChecklist(): TemplateResult | typeof nothing {
|
|
353
|
+
if (!projectState) {
|
|
354
|
+
return nothing;
|
|
355
|
+
}
|
|
356
|
+
const steps = deployChecklist();
|
|
357
|
+
const next = steps.find((step) => step.state !== "done") ?? null;
|
|
358
|
+
return html`
|
|
359
|
+
<ul class="activity-list">
|
|
360
|
+
<li class="activity-row ${next ? "activity-row--running" : "activity-row--done"}">
|
|
361
|
+
<span class="activity-icon" aria-hidden="true">${next ? "⋯" : "✓"}</span>
|
|
362
|
+
<div class="activity-body">
|
|
363
|
+
<div class="activity-head">
|
|
364
|
+
<span class="activity-title">Deploy checklist</span>
|
|
365
|
+
<span class="activity-source">Publish</span>
|
|
366
|
+
</div>
|
|
367
|
+
<div class="activity-status">
|
|
368
|
+
${next ? next.detail : "Everything this project needs to ship is in place."}
|
|
369
|
+
</div>
|
|
370
|
+
<ol class="activity-steps">
|
|
371
|
+
${steps.map(
|
|
372
|
+
(step) => html`<li class="activity-step activity-step--${STEP_CLASS[step.state]}">
|
|
373
|
+
<span class="activity-step-icon" aria-hidden="true">${STEP_ICON[step.state]}</span>
|
|
374
|
+
${step.label}${step.state === "done" ? "" : ` — ${step.detail}`}
|
|
375
|
+
</li>`,
|
|
376
|
+
)}
|
|
377
|
+
</ol>
|
|
378
|
+
${actionTpl(next)}
|
|
379
|
+
</div>
|
|
380
|
+
</li>
|
|
381
|
+
</ul>
|
|
382
|
+
`;
|
|
383
|
+
}
|
|
@@ -40,7 +40,16 @@ export interface ConnectOptions {
|
|
|
40
40
|
|
|
41
41
|
/** True when the active platform can reach the Cloudflare API. */
|
|
42
42
|
export function platformSupportsPublish(): boolean {
|
|
43
|
-
|
|
43
|
+
// Answers false before a platform is registered rather than throwing. This is read by ambient
|
|
44
|
+
// Surfaces — the status bar's deploy item, the Activity tab's checklist — which paint during boot
|
|
45
|
+
// And in tests that register no platform, and neither has any business crashing because
|
|
46
|
+
// Publishing is not available yet. "Can this host publish?" has a truthful answer before the host
|
|
47
|
+
// Exists, and it is no.
|
|
48
|
+
try {
|
|
49
|
+
return typeof getPlatform().cfApi === "function";
|
|
50
|
+
} catch {
|
|
51
|
+
return false;
|
|
52
|
+
}
|
|
44
53
|
}
|
|
45
54
|
|
|
46
55
|
async function cfApi<T>(path: string, init?: { method?: string; body?: unknown }): Promise<T> {
|
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Publish-commands.ts — the `Publish:` family (plan §9.5), defined beside the deploy it drives.
|
|
3
|
+
*
|
|
4
|
+
* Two unrelated operations were called "Publish" and neither was a command. `publishToGithub`
|
|
5
|
+
* created a git repository; `openPublishPanel` connected Cloudflare Pages — and it had **zero call
|
|
6
|
+
* sites in `src/`**, reachable only through the screenshot runner's seed, so the one flow that
|
|
7
|
+
* ships a site was, in the shipped app, unreachable. The toolbar's Publish button had already gone
|
|
8
|
+
* with the toolbar, and nothing replaced it.
|
|
9
|
+
*
|
|
10
|
+
* Three records, always reachable, each disabled with the one sentence that says why:
|
|
11
|
+
*
|
|
12
|
+
* | id | what it does |
|
|
13
|
+
* | ----------------------- | --------------------------------------------------------- |
|
|
14
|
+
* | `publish.setUp` | opens the provider connection flow |
|
|
15
|
+
* | `publish.deploy` | pushes the branch and asks Cloudflare what it did with it |
|
|
16
|
+
* | `publish.openDashboard` | opens the connected Pages project on Cloudflare |
|
|
17
|
+
*
|
|
18
|
+
* **`runDeploy` is an Activity, not a toast.** A deploy pushes a branch and then waits on a build
|
|
19
|
+
* that is happening somewhere else; that is the definition of the long operation §7.3 gave the
|
|
20
|
+
* Activity tab, and `fail()` raises the Problem so the failure outlives the run and carries a
|
|
21
|
+
* Retry. The caller therefore never also notifies (§16).
|
|
22
|
+
*
|
|
23
|
+
* **No credential is read here.** Whether a provider is reachable is a capability question, and
|
|
24
|
+
* whether one is connected is a `project.json` question; the token that proves it is read only by
|
|
25
|
+
* `platforms/devserver.ts` on its way to the same-origin proxy (`studio.md` §15 rule 1).
|
|
26
|
+
*
|
|
27
|
+
* **The panel is imported lazily** so this module carries no DOM at import time: `commands/
|
|
28
|
+
* app-commands.ts` is loaded by three CI checks in a bare Bun process, and a module that opens a
|
|
29
|
+
* modal at import would break all three.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { getPlatform } from "../platform";
|
|
33
|
+
import { beginActivity } from "../panels/activity-panel";
|
|
34
|
+
import { currentDeploy, noteDeployment } from "./deploy-checklist";
|
|
35
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
36
|
+
import { latestDeployment, platformSupportsPublish } from "./pages-service";
|
|
37
|
+
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* How long the deploy waits for Cloudflare to admit the push happened.
|
|
41
|
+
*
|
|
42
|
+
* Six attempts five seconds apart: a Pages build takes minutes, so this is not "wait for the build"
|
|
43
|
+
* — it is "wait for the deployment RECORD", which appears within seconds of the push. The activity
|
|
44
|
+
* says which of the two it is waiting for, because a progress line that implies the former and
|
|
45
|
+
* delivers the latter is the dishonesty §7.3 exists to end.
|
|
46
|
+
*/
|
|
47
|
+
export const DEPLOY_POLL = { attempts: 6, delayMs: 5000 } as const;
|
|
48
|
+
|
|
49
|
+
/** Cloudflare's dashboard, for a connected project. */
|
|
50
|
+
export function dashboardUrl(accountId: string, projectName: string): string {
|
|
51
|
+
return `https://dash.cloudflare.com/${accountId}/pages/view/${projectName}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async function sleep(ms: number): Promise<void> {
|
|
55
|
+
if (ms > 0) {
|
|
56
|
+
await new Promise((resolve) => {
|
|
57
|
+
setTimeout(resolve, ms);
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Push the current branch and report what Cloudflare made of it.
|
|
64
|
+
*
|
|
65
|
+
* @param options Poll shape, so a test does not wait 30 real seconds.
|
|
66
|
+
* @returns Whether the push succeeded — NOT whether the build did, which is Cloudflare's to say.
|
|
67
|
+
*/
|
|
68
|
+
export async function runDeploy(
|
|
69
|
+
options: { attempts?: number; delayMs?: number } = {},
|
|
70
|
+
): Promise<boolean> {
|
|
71
|
+
const deploy = currentDeploy();
|
|
72
|
+
let cancelled = false;
|
|
73
|
+
const activity = beginActivity({
|
|
74
|
+
cancel: () => {
|
|
75
|
+
cancelled = true;
|
|
76
|
+
},
|
|
77
|
+
source: "Publish",
|
|
78
|
+
status: "Starting…",
|
|
79
|
+
steps: ["Push to the remote", "Ask Cloudflare for the deployment"],
|
|
80
|
+
title: "Deploy",
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
activity.step("Push to the remote");
|
|
84
|
+
try {
|
|
85
|
+
await getPlatform().gitPush({ setUpstream: true });
|
|
86
|
+
activity.log("Pushed the current branch.");
|
|
87
|
+
} catch (error) {
|
|
88
|
+
// Logged BEFORE the failure: `fail()` snapshots the log into the Problem's `detail`, so a line
|
|
89
|
+
// Appended afterwards would be visible in the Activity row and absent from the report of it.
|
|
90
|
+
activity.log(errorMessage(error));
|
|
91
|
+
activity.fail("Could not push to the remote.", { action: "git.push" });
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (!deploy) {
|
|
96
|
+
// Reachable when `enablement` was true at click time and the config changed underneath — a
|
|
97
|
+
// Push is still a real outcome, so this ends as a success that says exactly what it did.
|
|
98
|
+
activity.done("Pushed. No provider is connected, so nothing was asked to build it.");
|
|
99
|
+
return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
activity.step("Ask Cloudflare for the deployment");
|
|
103
|
+
const attempts = options.attempts ?? DEPLOY_POLL.attempts;
|
|
104
|
+
const delayMs = options.delayMs ?? DEPLOY_POLL.delayMs;
|
|
105
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
106
|
+
if (cancelled) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
let info;
|
|
110
|
+
try {
|
|
111
|
+
info = await latestDeployment(deploy);
|
|
112
|
+
} catch (error) {
|
|
113
|
+
activity.log(errorMessage(error));
|
|
114
|
+
activity.fail("Cloudflare did not answer.", { action: "publish.deploy" });
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
if (info) {
|
|
118
|
+
noteDeployment(info);
|
|
119
|
+
activity.log(`${info.stage}: ${info.status} — ${info.url}`);
|
|
120
|
+
activity.done(`${info.stage}: ${info.status}`);
|
|
121
|
+
return true;
|
|
122
|
+
}
|
|
123
|
+
await sleep(delayMs);
|
|
124
|
+
}
|
|
125
|
+
if (cancelled) {
|
|
126
|
+
return false;
|
|
127
|
+
}
|
|
128
|
+
noteDeployment(null);
|
|
129
|
+
activity.done("Pushed. Cloudflare has not reported a deployment yet.");
|
|
130
|
+
return true;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* The three records.
|
|
135
|
+
*
|
|
136
|
+
* `enablement` reads `platformSupportsPublish()` rather than a `capability.*` key because there is
|
|
137
|
+
* no `publish` capability yet — adding one is a `commands/context.ts` + `commands/live-context.ts`
|
|
138
|
+
* change, and §5.2 is explicit that PAL differences belong there. Until it exists this is the one
|
|
139
|
+
* `if (platform.x)` the family carries, in the one place a reader looks for it.
|
|
140
|
+
*/
|
|
141
|
+
export function publishCommands(): AnyCommand[] {
|
|
142
|
+
return [
|
|
143
|
+
{
|
|
144
|
+
category: "Publish",
|
|
145
|
+
id: "publish.setUp",
|
|
146
|
+
level: "project",
|
|
147
|
+
menus: ["commandbar/overflow", "palette"],
|
|
148
|
+
group: "8_publish",
|
|
149
|
+
/* THE CAPABILITY TERM BELONGS TO THE WHOLE FAMILY, not to its first member. All three of
|
|
150
|
+
these reach Cloudflare, and only this one asked whether the host can: `currentDeploy()`
|
|
151
|
+
reads config stored IN THE PROJECT, so a repository cloned or scaffolded with a deploy
|
|
152
|
+
already configured carries it onto a host whose PAL has no `cfApi`. In that state Set Up
|
|
153
|
+
Publishing was correctly disabled while Deploy was enabled and ran — pushing the branch,
|
|
154
|
+
then failing at `latestDeployment` with "Cloudflare did not answer" after the push had
|
|
155
|
+
already gone out. The refusal that was meant to protect the family guarded one third. */
|
|
156
|
+
requires: "an open project on a platform that can reach the Cloudflare API",
|
|
157
|
+
when: (ctx) => ctx.project.open,
|
|
158
|
+
// `when` already asked about the project.
|
|
159
|
+
enablement: () => platformSupportsPublish(),
|
|
160
|
+
aiTool: {
|
|
161
|
+
description:
|
|
162
|
+
"Open the publishing set-up flow, which connects this project to a Cloudflare Pages " +
|
|
163
|
+
"project that rebuilds on every push.",
|
|
164
|
+
name: "set_up_publishing",
|
|
165
|
+
},
|
|
166
|
+
run: async () => {
|
|
167
|
+
const { openPublishPanel } = await import("./publish-panel");
|
|
168
|
+
openPublishPanel();
|
|
169
|
+
},
|
|
170
|
+
title: "Set Up Publishing",
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
category: "Publish",
|
|
174
|
+
id: "publish.deploy",
|
|
175
|
+
level: "project",
|
|
176
|
+
menus: ["commandbar/overflow", "palette"],
|
|
177
|
+
group: "8_publish",
|
|
178
|
+
requires:
|
|
179
|
+
"a repository with a remote and a connected deploy provider, on a platform that can " +
|
|
180
|
+
"reach the Cloudflare API",
|
|
181
|
+
when: (ctx) => ctx.project.open,
|
|
182
|
+
enablement: (ctx) =>
|
|
183
|
+
ctx.project.isRepo && currentDeploy() !== undefined && platformSupportsPublish(),
|
|
184
|
+
aiTool: {
|
|
185
|
+
description:
|
|
186
|
+
"Push the current branch so the connected provider builds it, then report the " +
|
|
187
|
+
"deployment. Does not commit — commit first if there are unsaved changes.",
|
|
188
|
+
name: "deploy_site",
|
|
189
|
+
},
|
|
190
|
+
run: async () => {
|
|
191
|
+
await runDeploy();
|
|
192
|
+
},
|
|
193
|
+
title: "Deploy",
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
category: "Publish",
|
|
197
|
+
id: "publish.openDashboard",
|
|
198
|
+
level: "project",
|
|
199
|
+
menus: ["commandbar/overflow", "palette"],
|
|
200
|
+
group: "8_publish",
|
|
201
|
+
requires: "a connected deploy provider, on a platform that can reach the Cloudflare API",
|
|
202
|
+
when: (ctx) => ctx.project.open,
|
|
203
|
+
enablement: () => currentDeploy() !== undefined && platformSupportsPublish(),
|
|
204
|
+
run: () => {
|
|
205
|
+
const deploy = currentDeploy();
|
|
206
|
+
if (deploy) {
|
|
207
|
+
window.open(dashboardUrl(deploy.accountId, deploy.projectName), "_blank", "noopener");
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
title: "Open Deployment Dashboard",
|
|
211
|
+
},
|
|
212
|
+
];
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Register the `Publish:` family. */
|
|
216
|
+
export function registerPublishCommands(registry: CommandRegistry): void {
|
|
217
|
+
registry.registerAll(publishCommands());
|
|
218
|
+
}
|