@jxsuite/studio 2.0.0 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/canvas.html +21 -0
- package/dist/chunks/Overlay.dev-qfjzkms8.js +23 -0
- package/dist/chunks/{Overlay.dev-cszx7yn5.js.map → Overlay.dev-qfjzkms8.js.map} +1 -1
- package/dist/chunks/{adapter-z8fkmbmv.js → adapter-qfnesn5v.js} +2 -2
- package/dist/chunks/{cf-settings-mmyv436d.js → cf-settings-73sh5z4p.js} +3 -3
- package/dist/chunks/{components-0hzentwq.js → components-2bpqp4mq.js} +5 -7
- package/dist/chunks/{components-0hzentwq.js.map → components-2bpqp4mq.js.map} +1 -1
- package/dist/chunks/content-source-34gnkbnn.js +38 -0
- package/dist/chunks/content-source-34gnkbnn.js.map +9 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js +258 -0
- package/dist/chunks/convert-to-repeater-t8kxre5s.js.map +10 -0
- package/dist/chunks/extension-sections-6mqydz35.js +301 -0
- package/dist/chunks/extension-sections-6mqydz35.js.map +11 -0
- package/dist/chunks/files-8kn321h1.js +70 -0
- package/dist/chunks/files-8kn321h1.js.map +9 -0
- package/dist/chunks/{format-host-2c188n41.js → format-host-mkwsmt8d.js} +3 -3
- package/dist/chunks/git-panel-tjbr191d.js +51 -0
- package/dist/chunks/git-panel-tjbr191d.js.map +9 -0
- package/dist/chunks/{index-baeg2x3y.js → index-7pxchcce.js} +4 -3
- package/dist/chunks/{index-baeg2x3y.js.map → index-7pxchcce.js.map} +3 -3
- package/dist/chunks/{jx-validate-qx8degj7.js → jx-validate-v6qgknrs.js} +3 -3
- package/dist/chunks/layers-panel-87pb25km.js +58 -0
- package/dist/chunks/layers-panel-87pb25km.js.map +9 -0
- package/dist/chunks/{monaco-setup-zjdp9zdw.js → monaco-setup-e0yh25j4.js} +4 -75
- package/dist/chunks/{monaco-setup-zjdp9zdw.js.map → monaco-setup-e0yh25j4.js.map} +3 -4
- package/dist/chunks/publish-panel-m0fcgdrt.js +21 -0
- package/dist/chunks/publish-panel-m0fcgdrt.js.map +9 -0
- package/dist/chunks/quick-search-rhs6cbsb.js +53 -0
- package/dist/chunks/quick-search-rhs6cbsb.js.map +9 -0
- package/dist/chunks/right-panel-zpdxd98t.js +37 -0
- package/dist/chunks/right-panel-zpdxd98t.js.map +9 -0
- package/dist/chunks/section-registry-zmckaxf4.js +30 -0
- package/dist/chunks/section-registry-zmckaxf4.js.map +9 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js +18 -0
- package/dist/chunks/sp-overlay.dev-9ran0sfq.js.map +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js +9 -0
- package/dist/chunks/sp-popover.dev-rebmdncr.js.map +9 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js +15 -0
- package/dist/chunks/sp-progress-circle.dev-5vkppfkt.js.map +9 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js +15 -0
- package/dist/chunks/sp-tooltip.dev-ykqrg8h5.js.map +9 -0
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js → sp-tray.dev-ey59vfh7.js} +10 -8
- package/dist/chunks/{sp-tray.dev-pb2fsd65.js.map → sp-tray.dev-ey59vfh7.js.map} +2 -2
- package/dist/chunks/{studio-9qr6fq7p.js → studio-0rqqhrdy.js} +10 -378
- package/dist/chunks/{studio-9qr6fq7p.js.map → studio-0rqqhrdy.js.map} +4 -14
- package/dist/chunks/{studio-5x6tqqec.js → studio-2sj1jj3v.js} +10 -8
- package/dist/chunks/{studio-5x6tqqec.js.map → studio-2sj1jj3v.js.map} +2 -2
- package/dist/chunks/{studio-h3fw0wn8.js → studio-3fk1ac9z.js} +6 -4
- package/dist/chunks/{studio-h3fw0wn8.js.map → studio-3fk1ac9z.js.map} +2 -2
- package/dist/chunks/studio-41exgzhq.js +6690 -0
- package/dist/chunks/studio-41exgzhq.js.map +38 -0
- package/dist/chunks/{studio-7v2bpweq.js → studio-4mqb9a0j.js} +6 -4
- package/dist/chunks/{studio-7v2bpweq.js.map → studio-4mqb9a0j.js.map} +2 -2
- package/dist/chunks/studio-50k77cca.js +1787 -0
- package/dist/chunks/studio-50k77cca.js.map +18 -0
- package/dist/chunks/{studio-56v3zwk6.js → studio-6a0h7sne.js} +10 -8
- package/dist/chunks/{studio-56v3zwk6.js.map → studio-6a0h7sne.js.map} +2 -2
- package/dist/chunks/studio-6f0am8f2.js +53 -0
- package/dist/chunks/studio-6f0am8f2.js.map +10 -0
- package/dist/chunks/studio-6nmjc5qk.js +7002 -0
- package/dist/chunks/studio-6nmjc5qk.js.map +32 -0
- package/dist/chunks/studio-7afd21nw.js +67 -0
- package/dist/chunks/studio-7afd21nw.js.map +10 -0
- package/dist/chunks/{studio-xze4zrs1.js → studio-7bmdvzne.js} +2 -2
- package/dist/chunks/studio-7bmdvzne.js.map +10 -0
- package/dist/chunks/studio-8jpzqk5s.js +128 -0
- package/dist/chunks/studio-8jpzqk5s.js.map +10 -0
- package/dist/chunks/studio-94m77tgk.js +1482 -0
- package/dist/chunks/studio-94m77tgk.js.map +13 -0
- package/dist/chunks/studio-9je6w5sq.js +1000 -0
- package/dist/chunks/studio-9je6w5sq.js.map +16 -0
- package/dist/chunks/{studio-w7c6kvk7.js → studio-a7pzveq1.js} +2 -2
- package/dist/chunks/{studio-97qcgvdn.js → studio-adn0z1k3.js} +14 -8
- package/dist/chunks/{studio-97qcgvdn.js.map → studio-adn0z1k3.js.map} +3 -3
- package/dist/chunks/{studio-kr88ryr7.js → studio-bsxxx3x2.js} +17 -15
- package/dist/chunks/{studio-kr88ryr7.js.map → studio-bsxxx3x2.js.map} +7 -6
- package/dist/chunks/studio-bw5dz6km.js +608 -0
- package/dist/chunks/studio-bw5dz6km.js.map +21 -0
- package/dist/chunks/studio-c6s5getc.js +4232 -0
- package/dist/chunks/studio-c6s5getc.js.map +39 -0
- package/dist/chunks/studio-cjm7fdjf.js +451 -0
- package/dist/chunks/studio-cjm7fdjf.js.map +14 -0
- package/dist/chunks/{studio-cx91mnh8.js → studio-ckwz4d3x.js} +53 -53
- package/dist/chunks/{studio-cx91mnh8.js.map → studio-ckwz4d3x.js.map} +5 -5
- package/dist/chunks/studio-dg7zyt5x.js +28 -0
- package/dist/chunks/studio-dg7zyt5x.js.map +10 -0
- package/dist/chunks/studio-dzcyvvvz.js +166 -0
- package/dist/chunks/studio-dzcyvvvz.js.map +10 -0
- package/dist/chunks/{studio-7kqe41sa.js → studio-evkr5d3b.js} +15 -7
- package/dist/chunks/{studio-7kqe41sa.js.map → studio-evkr5d3b.js.map} +5 -4
- package/dist/chunks/studio-f12h6177.js +411 -0
- package/dist/chunks/studio-f12h6177.js.map +13 -0
- package/dist/chunks/studio-hbp1s8m9.js +1817 -0
- package/dist/chunks/studio-hbp1s8m9.js.map +14 -0
- package/dist/chunks/studio-k2sxjph5.js +15295 -0
- package/dist/chunks/studio-k2sxjph5.js.map +235 -0
- package/dist/chunks/studio-myje51t3.js +385 -0
- package/dist/chunks/studio-myje51t3.js.map +12 -0
- package/dist/chunks/studio-na6rwzhw.js +353 -0
- package/dist/chunks/studio-na6rwzhw.js.map +11 -0
- package/dist/chunks/{studio-4k0m4fmp.js → studio-p471gg5n.js} +126 -3
- package/dist/chunks/studio-p471gg5n.js.map +14 -0
- package/dist/chunks/{studio-9bjwf0d4.js → studio-rgwcpast.js} +5 -46
- package/dist/chunks/{studio-9bjwf0d4.js.map → studio-rgwcpast.js.map} +4 -5
- package/dist/chunks/{studio-hhj92gcr.js → studio-s519zc99.js} +3 -3
- package/dist/chunks/studio-sas7mt3k.js +75 -0
- package/dist/chunks/studio-sas7mt3k.js.map +10 -0
- package/dist/chunks/studio-t5wqwzdh.js +74 -0
- package/dist/chunks/studio-t5wqwzdh.js.map +10 -0
- package/dist/chunks/{studio-9qpspwfn.js → studio-temv5bw4.js} +5 -5
- package/dist/chunks/{studio-9qpspwfn.js.map → studio-temv5bw4.js.map} +1 -1
- package/dist/chunks/{studio-qkvt37c0.js → studio-tr20m8g8.js} +669 -18
- package/dist/chunks/studio-tr20m8g8.js.map +9 -0
- package/dist/chunks/{studio-ygzav7z5.js → studio-v96qfnww.js} +5514 -4291
- package/dist/chunks/studio-v96qfnww.js.map +54 -0
- package/dist/chunks/studio-vcetc91k.js +659 -0
- package/dist/chunks/studio-vcetc91k.js.map +10 -0
- package/dist/chunks/{studio-n9zmpxjg.js → studio-vmymjefp.js} +2972 -4937
- package/dist/chunks/studio-vmymjefp.js.map +98 -0
- package/dist/chunks/{studio-bghqfmea.js → studio-wwseqyw0.js} +2 -2
- package/dist/chunks/studio-x9hmy5kr.js +390 -0
- package/dist/chunks/studio-x9hmy5kr.js.map +10 -0
- package/dist/chunks/studio-xa0ce0n7.js +335 -0
- package/dist/chunks/studio-xa0ce0n7.js.map +10 -0
- package/dist/chunks/studio-xm3dbxvn.js +378 -0
- package/dist/chunks/studio-xm3dbxvn.js.map +19 -0
- package/dist/chunks/{studio-nqw433kv.js → studio-xmvbhg31.js} +2 -2
- package/dist/chunks/studio-zv37yrfa.js +1054 -0
- package/dist/chunks/studio-zv37yrfa.js.map +13 -0
- package/dist/chunks/{ws-client-q7hkz3j9.js → ws-client-654ts66j.js} +6 -4
- package/dist/chunks/ws-client-654ts66j.js.map +11 -0
- package/dist/iframe-entry.js +668 -230
- package/dist/studio.js +23711 -35185
- package/index.html +35 -4685
- package/package.json +17 -15
- package/src/about/about-modal.ts +42 -11
- package/src/browse/library-commands.ts +240 -0
- package/src/browse/library-layouts.ts +369 -0
- package/src/browse/library-model.ts +396 -0
- package/src/browse/library-pane.ts +1049 -0
- package/src/browse/library-preview.ts +252 -0
- package/src/browse/library-source.ts +150 -0
- package/src/canvas/canvas-helpers.ts +33 -9
- package/src/canvas/canvas-live-render.ts +45 -29
- package/src/canvas/canvas-patcher.ts +151 -31
- package/src/canvas/canvas-perf.ts +46 -7
- package/src/canvas/canvas-render.ts +1092 -220
- package/src/canvas/canvas-surface.ts +240 -0
- package/src/canvas/canvas-utils.ts +1166 -124
- package/src/canvas/iframe-drop.ts +2 -1
- package/src/canvas/iframe-entry.ts +241 -4
- package/src/canvas/iframe-host.ts +1226 -143
- package/src/canvas/iframe-inline-edit.ts +31 -0
- package/src/canvas/iframe-interaction.ts +76 -16
- package/src/canvas/iframe-keys.ts +107 -45
- package/src/canvas/iframe-overlay.ts +38 -1
- package/src/canvas/iframe-protocol.ts +137 -2
- package/src/canvas/iframe-render.ts +70 -2
- package/src/canvas/iframe-slash.ts +6 -0
- package/src/canvas/path-mapping.ts +56 -6
- package/src/canvas/surface-registry.ts +292 -0
- package/src/collab/collab-commands.ts +244 -0
- package/src/collab/collab-session.ts +198 -32
- package/src/collab/collab-state.ts +28 -2
- package/src/collab/presence-chips.ts +100 -9
- package/src/commands/active-registry.ts +42 -0
- package/src/commands/app-commands.ts +176 -0
- package/src/commands/budget.ts +158 -0
- package/src/commands/command-args.ts +253 -0
- package/src/commands/context.ts +387 -0
- package/src/commands/defaults.ts +580 -0
- package/src/commands/keymap.ts +571 -0
- package/src/commands/levels.ts +345 -0
- package/src/commands/live-context.ts +193 -0
- package/src/commands/reference.ts +227 -0
- package/src/commands/registry.ts +293 -0
- package/src/component-props.ts +3 -2
- package/src/content/draft-state.ts +106 -0
- package/src/content/entry-commands.ts +282 -0
- package/src/content/entry-editor.ts +327 -0
- package/src/content/entry-fields.ts +116 -0
- package/src/content/entry-model.ts +175 -0
- package/src/editor/canvas-context-menu.ts +17 -11
- package/src/editor/canvas-slash-bridge.ts +11 -4
- package/src/editor/context-menu.ts +804 -204
- package/src/editor/convert-to-component.ts +31 -10
- package/src/editor/convert-to-repeater.ts +3 -2
- package/src/editor/inline-edit-apply.ts +5 -5
- package/src/editor/inline-edit.ts +86 -50
- package/src/editor/insert-zone-action.ts +1 -1
- package/src/editor/shortcuts.ts +1043 -329
- package/src/editor/slash-menu.ts +1 -0
- package/src/files/elements.ts +166 -0
- package/src/files/file-ops.ts +178 -31
- package/src/files/files.ts +898 -268
- package/src/files/fs-events.ts +21 -0
- package/src/files/media-meta.ts +203 -0
- package/src/files/media-paths.ts +141 -0
- package/src/files/media-upload.ts +7 -3
- package/src/files/media-usage.ts +189 -0
- package/src/github/github-auth.ts +235 -33
- package/src/github/github-publish.ts +80 -21
- package/src/grid/grid-controller.ts +152 -12
- package/src/grid/grid-layout.ts +225 -20
- package/src/grid/grid-open.ts +120 -0
- package/src/grid/grid-panel.ts +554 -39
- package/src/grid/grid-source.ts +27 -1
- package/src/grid/redirects-grid.ts +599 -0
- package/src/grid/redirects.ts +398 -0
- package/src/grid/sources/content-source.ts +108 -2
- package/src/i18n/i18n-commands.ts +322 -0
- package/src/new-project/add-repo-modal.ts +8 -10
- package/src/new-project/import-tab.ts +34 -9
- package/src/new-project/location-fields.ts +9 -1
- package/src/new-project/new-project-modal.ts +270 -262
- package/src/packages/jxsuite-update.ts +97 -48
- package/src/packages/pull-package-sync.ts +18 -9
- package/src/page-params.ts +2 -1
- package/src/panels/activity-bar.ts +152 -91
- package/src/panels/activity-panel.ts +481 -0
- package/src/panels/ai-chat/chat-markdown.ts +8 -3
- package/src/panels/ai-chat/chat-view.ts +245 -18
- package/src/panels/ai-chat/composer.ts +31 -5
- package/src/panels/ai-chat/sessions-view.ts +17 -10
- package/src/panels/ai-panel.ts +279 -120
- package/src/panels/block-action-bar.ts +1115 -224
- package/src/panels/bottom-dock.ts +337 -0
- package/src/panels/canvas-dnd-bridge.ts +37 -8
- package/src/panels/chat-panel.ts +29 -12
- package/src/panels/data-explorer.ts +254 -112
- package/src/panels/data-grid.ts +10 -4
- package/src/panels/dnd.ts +28 -51
- package/src/panels/editors.ts +434 -107
- package/src/panels/elements-panel.ts +66 -2
- package/src/panels/empty-state.ts +112 -0
- package/src/panels/events-panel.ts +596 -82
- package/src/panels/formula-workspace.ts +408 -218
- package/src/panels/frontmatter-fields.ts +51 -10
- package/src/panels/frontmatter-panel.ts +357 -79
- package/src/panels/git-panel.ts +375 -164
- package/src/panels/head-panel.ts +791 -85
- package/src/panels/i18n-panel.ts +592 -0
- package/src/panels/imports-panel.ts +95 -85
- package/src/panels/jump-bar.ts +584 -0
- package/src/panels/layers-panel.ts +1106 -328
- package/src/panels/left-panel.ts +109 -204
- package/src/panels/navigator-panels.ts +131 -0
- package/src/panels/overlays.ts +7 -2
- package/src/panels/pane-context.ts +1477 -0
- package/src/panels/pane-grid.ts +406 -0
- package/src/panels/panel-registry.ts +340 -0
- package/src/panels/panel-scheduler.ts +75 -0
- package/src/panels/problems-panel.ts +234 -0
- package/src/panels/properties-panel.ts +946 -1139
- package/src/panels/provenance.ts +258 -0
- package/src/panels/quick-search.ts +791 -122
- package/src/panels/right-panel.ts +228 -102
- package/src/panels/seo-modal.ts +395 -0
- package/src/panels/settings-pane.ts +203 -0
- package/src/panels/signals-panel.ts +354 -137
- package/src/panels/statement-editor.ts +41 -25
- package/src/panels/statusbar.ts +342 -106
- package/src/panels/style-panel.ts +974 -463
- package/src/panels/style-utils.ts +3 -3
- package/src/panels/stylebook-doc.ts +4 -2
- package/src/panels/stylebook-layers-panel.ts +3 -2
- package/src/panels/stylebook-panel.ts +51 -26
- package/src/panels/tab-strip.ts +1058 -78
- package/src/panels/target-line.ts +274 -0
- package/src/panels/toolbar.ts +631 -484
- package/src/panels/welcome-screen.ts +301 -198
- package/src/platform-errors.ts +26 -3
- package/src/platform.ts +96 -2
- package/src/platforms/cloud.ts +71 -8
- package/src/platforms/devserver.ts +92 -29
- package/src/publish/deploy-checklist.ts +383 -0
- package/src/publish/pages-service.ts +10 -1
- package/src/publish/publish-commands.ts +218 -0
- package/src/publish/publish-panel.ts +110 -16
- package/src/recent-projects.ts +3 -2
- package/src/services/a11y-report.ts +505 -0
- package/src/services/ai-models.ts +75 -2
- package/src/services/ai-project-tools.ts +29 -10
- package/src/services/ai-system-prompt.ts +53 -14
- package/src/services/ai-tools.ts +39 -3
- package/src/services/ai-writes.ts +154 -0
- package/src/services/announce.ts +97 -0
- package/src/services/automation.ts +424 -175
- package/src/services/cem-export.ts +9 -4
- package/src/services/clock.ts +61 -0
- package/src/services/document-assistant.ts +36 -8
- package/src/services/idle.ts +179 -0
- package/src/services/import-client.ts +26 -0
- package/src/services/monaco-buffer.ts +627 -0
- package/src/services/monaco-lazy.ts +36 -0
- package/src/services/notify.ts +271 -0
- package/src/services/profile.ts +155 -0
- package/src/services/references.ts +0 -0
- package/src/services/token-lint.ts +3 -2
- package/src/services/tool-executor.ts +53 -7
- package/src/services/trusted-types.ts +123 -0
- package/src/settings/contexts-section.ts +427 -0
- package/src/settings/contributed-section.ts +149 -11
- package/src/settings/css-vars-editor.ts +306 -349
- package/src/settings/defs-editor.ts +93 -34
- package/src/settings/dependencies-editor.ts +7 -7
- package/src/settings/extension-sections.ts +37 -5
- package/src/settings/general-settings.ts +204 -121
- package/src/settings/locales-section.ts +347 -0
- package/src/settings/preferences-accounts.ts +140 -0
- package/src/settings/preferences-dialog.ts +620 -0
- package/src/settings/preferences-keymap.ts +270 -0
- package/src/settings/project-sections.ts +260 -0
- package/src/settings/section-registry.ts +169 -0
- package/src/settings/settings-document.ts +385 -0
- package/src/shell.ts +1549 -0
- package/src/site-context.ts +45 -20
- package/src/state.ts +14 -275
- package/src/store.ts +88 -43
- package/src/studio.ts +790 -346
- package/src/style/live-preview.ts +36 -0
- package/src/style/project-styles.ts +349 -0
- package/src/style/token-ref.ts +123 -0
- package/src/tabs/patch-ops.ts +16 -4
- package/src/tabs/project-config.ts +416 -0
- package/src/tabs/selection.ts +323 -0
- package/src/tabs/tab.ts +182 -47
- package/src/tabs/transact.ts +209 -28
- package/src/types.ts +68 -13
- package/src/ui/ai-credentials-form.ts +39 -35
- package/src/ui/ai-managed-connect.ts +113 -0
- package/src/ui/dynamic-slot.ts +224 -81
- package/src/ui/expression-editor.ts +71 -63
- package/src/ui/field-input.ts +4 -6
- package/src/ui/field-row.ts +215 -38
- package/src/ui/form-controls.ts +194 -7
- package/src/ui/layers.ts +492 -69
- package/src/ui/media-picker.ts +115 -25
- package/src/ui/panel-resize.ts +109 -91
- package/src/ui/progress-modal.ts +131 -50
- package/src/ui/regions.ts +462 -0
- package/src/ui/schema-form.ts +229 -45
- package/src/ui/spectrum.ts +24 -8
- package/src/ui/timing.ts +49 -0
- package/src/ui/value-selector.ts +20 -0
- package/src/ui/value-source.ts +348 -0
- package/src/ui/virtual-window.ts +400 -0
- package/src/utils/edit-display.ts +3 -2
- package/src/utils/inherited-style.ts +67 -37
- package/src/view.ts +64 -78
- package/src/workspace/pane-derive.ts +1258 -0
- package/src/workspace/session.ts +317 -0
- package/src/workspace/workspace.ts +1345 -23
- package/styles/canvas.css +653 -0
- package/styles/forced-colors.css +106 -0
- package/styles/inspector.css +1454 -0
- package/styles/overlays.css +2704 -0
- package/styles/panels.css +1701 -0
- package/styles/shell.css +1423 -0
- package/styles/tokens.css +151 -0
- package/dist/chunks/Overlay.dev-cszx7yn5.js +0 -22
- package/dist/chunks/extension-sections-qct0gt8f.js +0 -531
- package/dist/chunks/extension-sections-qct0gt8f.js.map +0 -12
- package/dist/chunks/files-02zn5y24.js +0 -50
- package/dist/chunks/files-02zn5y24.js.map +0 -9
- package/dist/chunks/git-panel-mbb0y0hz.js +0 -28
- package/dist/chunks/git-panel-mbb0y0hz.js.map +0 -9
- package/dist/chunks/layers-panel-9qekmqk1.js +0 -26
- package/dist/chunks/layers-panel-9qekmqk1.js.map +0 -9
- package/dist/chunks/settings-modal-gj0hrah4.js +0 -29
- package/dist/chunks/settings-modal-gj0hrah4.js.map +0 -9
- package/dist/chunks/sp-overlay.dev-earn5as8.js +0 -17
- package/dist/chunks/sp-overlay.dev-earn5as8.js.map +0 -9
- package/dist/chunks/sp-popover.dev-frxtfejn.js +0 -8
- package/dist/chunks/sp-popover.dev-frxtfejn.js.map +0 -9
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js +0 -14
- package/dist/chunks/sp-progress-circle.dev-7tvw34h8.js.map +0 -9
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js +0 -21
- package/dist/chunks/sp-tooltip.dev-a9fywesm.js.map +0 -10
- package/dist/chunks/studio-4k0m4fmp.js.map +0 -13
- package/dist/chunks/studio-568nx4t5.js +0 -469
- package/dist/chunks/studio-568nx4t5.js.map +0 -14
- package/dist/chunks/studio-9smfzqhc.js +0 -19
- package/dist/chunks/studio-9smfzqhc.js.map +0 -10
- package/dist/chunks/studio-asvf0j9e.js +0 -429
- package/dist/chunks/studio-asvf0j9e.js.map +0 -13
- package/dist/chunks/studio-d77qm3pk.js +0 -3427
- package/dist/chunks/studio-d77qm3pk.js.map +0 -26
- package/dist/chunks/studio-j6gqyjf4.js +0 -1854
- package/dist/chunks/studio-j6gqyjf4.js.map +0 -18
- package/dist/chunks/studio-n9zmpxjg.js.map +0 -115
- package/dist/chunks/studio-qkvt37c0.js.map +0 -9
- package/dist/chunks/studio-rq0vxb19.js +0 -3744
- package/dist/chunks/studio-rq0vxb19.js.map +0 -22
- package/dist/chunks/studio-wd6ewdvd.js +0 -853
- package/dist/chunks/studio-wd6ewdvd.js.map +0 -12
- package/dist/chunks/studio-xq0t7n5t.js +0 -140
- package/dist/chunks/studio-xq0t7n5t.js.map +0 -10
- package/dist/chunks/studio-xze4zrs1.js.map +0 -10
- package/dist/chunks/studio-ygzav7z5.js.map +0 -46
- package/dist/chunks/studio-zmy7241e.js +0 -399
- package/dist/chunks/studio-zmy7241e.js.map +0 -12
- package/dist/chunks/ws-client-q7hkz3j9.js.map +0 -11
- package/src/browse/browse-modal.ts +0 -69
- package/src/browse/browse.ts +0 -904
- package/src/canvas/canvas-diff.ts +0 -216
- package/src/canvas/nested-site-style.ts +0 -58
- package/src/new-project/design-fields.ts +0 -264
- package/src/new-project/templates.ts +0 -61
- package/src/panels/tab-bar.ts +0 -548
- package/src/settings/settings-modal.ts +0 -203
- /package/dist/chunks/{adapter-z8fkmbmv.js.map → adapter-qfnesn5v.js.map} +0 -0
- /package/dist/chunks/{cf-settings-mmyv436d.js.map → cf-settings-73sh5z4p.js.map} +0 -0
- /package/dist/chunks/{format-host-2c188n41.js.map → format-host-mkwsmt8d.js.map} +0 -0
- /package/dist/chunks/{jx-validate-qx8degj7.js.map → jx-validate-v6qgknrs.js.map} +0 -0
- /package/dist/chunks/{studio-w7c6kvk7.js.map → studio-a7pzveq1.js.map} +0 -0
- /package/dist/chunks/{studio-hhj92gcr.js.map → studio-s519zc99.js.map} +0 -0
- /package/dist/chunks/{studio-bghqfmea.js.map → studio-wwseqyw0.js.map} +0 -0
- /package/dist/chunks/{studio-nqw433kv.js.map → studio-xmvbhg31.js.map} +0 -0
|
@@ -0,0 +1,347 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Locales — the project's languages, its default, and how a locale appears in a URL.
|
|
4
|
+
*
|
|
5
|
+
* `project.json`'s `i18n` block was authorable only by hand: every other i18n surface in Studio
|
|
6
|
+
* (the parity panel, the pane's locale companion, the translation verbs) reads a locale list
|
|
7
|
+
* nothing in the app could write, so a project became multilingual in a text editor and only then
|
|
8
|
+
* grew any of the affordances that make it worth being.
|
|
9
|
+
*
|
|
10
|
+
* **One write, two doors.** {@link addProjectLocale} is the whole of "declare a language" and the
|
|
11
|
+
* `i18n.addLocale` command calls this function rather than repeating the patch — a second spelling
|
|
12
|
+
* of the same merge is how the command and the form would come to disagree about what happens to
|
|
13
|
+
* `defaultLocale` when the first locale is added.
|
|
14
|
+
*
|
|
15
|
+
* @docs studio/projects/settings
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
import { html, render as litRender, nothing } from "lit-html";
|
|
19
|
+
import { canonicalizeLocale, isWellFormedLocale, localeLabel } from "@jxsuite/schema/locale";
|
|
20
|
+
import { errorMessage } from "@jxsuite/schema/parse";
|
|
21
|
+
import { notify } from "../services/notify";
|
|
22
|
+
import { projectState } from "../store";
|
|
23
|
+
import { updateSiteConfig } from "../site-context";
|
|
24
|
+
import type { LocaleRouting } from "@jxsuite/schema/locale";
|
|
25
|
+
import type { ProjectConfig } from "@jxsuite/schema/types";
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* The last failed write per rendered section container.
|
|
29
|
+
*
|
|
30
|
+
* Keyed by container so two mounted copies (and two tests) never share a message — the same shape
|
|
31
|
+
* `project-sections.ts` and `general-settings.ts` use, and for the same reason: these sections save
|
|
32
|
+
* on change, so a silent rejection would read as "my edit just vanished".
|
|
33
|
+
*/
|
|
34
|
+
const errors = new WeakMap<HTMLElement, string>();
|
|
35
|
+
|
|
36
|
+
/** Drop a parked error and re-render. */
|
|
37
|
+
function clearError(container: HTMLElement): void {
|
|
38
|
+
errors.delete(container);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** The error line for a section, if its last write failed. */
|
|
42
|
+
function errorLine(container: HTMLElement) {
|
|
43
|
+
const message = errors.get(container);
|
|
44
|
+
return message === undefined
|
|
45
|
+
? nothing
|
|
46
|
+
: html`<p class="settings-field-error" role="alert">${message}</p>`;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Persist a patch, surfacing the rejection instead of swallowing it.
|
|
51
|
+
*
|
|
52
|
+
* `updateSiteConfig` rejects on a failed write and the chokepoint has already filed the Problem, so
|
|
53
|
+
* all that is left here is to keep the form honest about what is on disk. A bare `void
|
|
54
|
+
* updateSiteConfig(...)` would drop the rejection and the field would silently snap back to the
|
|
55
|
+
* value the file still holds.
|
|
56
|
+
*
|
|
57
|
+
* @param {HTMLElement} container
|
|
58
|
+
* @param {Partial<ProjectConfig>} patch
|
|
59
|
+
* @param {() => void} rerender
|
|
60
|
+
*/
|
|
61
|
+
async function persist(
|
|
62
|
+
container: HTMLElement,
|
|
63
|
+
patch: Partial<ProjectConfig>,
|
|
64
|
+
rerender: () => void,
|
|
65
|
+
): Promise<void> {
|
|
66
|
+
try {
|
|
67
|
+
await updateSiteConfig(patch);
|
|
68
|
+
clearError(container);
|
|
69
|
+
} catch (error) {
|
|
70
|
+
errors.set(container, `Could not save project.json — ${errorMessage(error)}`);
|
|
71
|
+
}
|
|
72
|
+
rerender();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** The live project configuration, or an empty one before a project is open. */
|
|
76
|
+
function config(): ProjectConfig {
|
|
77
|
+
return (projectState?.projectConfig ?? {}) as ProjectConfig;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The patch that removes the whole `i18n` block.
|
|
82
|
+
*
|
|
83
|
+
* `undefined` survives the object spread inside `updateSiteConfig` and `JSON.stringify` then drops
|
|
84
|
+
* the key, so removing the last locale leaves a project with no `i18n` at all rather than one
|
|
85
|
+
* declaring an empty list — which `resolveI18n` reports as "declared with no usable locale", a
|
|
86
|
+
* build error for a state the author reached by deleting things. The cast is the price of
|
|
87
|
+
* `exactOptionalPropertyTypes`, the same one `general-settings.ts`'s `CLEAR_URL` pays.
|
|
88
|
+
*/
|
|
89
|
+
const CLEAR_I18N = { i18n: undefined } as unknown as Partial<ProjectConfig>;
|
|
90
|
+
|
|
91
|
+
/** The routing modes, with the sentence that says what each does to a URL. */
|
|
92
|
+
export const LOCALE_ROUTINGS: readonly { value: LocaleRouting; label: string }[] = [
|
|
93
|
+
{ label: "Prefix every locale but the default", value: "prefix-except-default" },
|
|
94
|
+
{ label: "Prefix every locale", value: "prefix-always" },
|
|
95
|
+
];
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The tags as the author wrote them, not as `resolveI18n` reports them.
|
|
99
|
+
*
|
|
100
|
+
* Reading the resolved list back would rewrite the file on the next save: `resolveI18n` unshifts a
|
|
101
|
+
* `defaultLocale` that is missing from `locales` and canonicalizes the case of every entry, so a
|
|
102
|
+
* patch built from it edits lines the author did not touch.
|
|
103
|
+
*/
|
|
104
|
+
function declaredLocales(): string[] {
|
|
105
|
+
const raw = config().i18n?.locales;
|
|
106
|
+
return Array.isArray(raw) ? raw.filter((tag): tag is string => typeof tag === "string") : [];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Whether `tag` names a language the project already declares, whatever case it was written in. */
|
|
110
|
+
function alreadyDeclared(tag: string): boolean {
|
|
111
|
+
const canonical = canonicalizeLocale(tag);
|
|
112
|
+
return declaredLocales().some((declared) => canonicalizeLocale(declared) === canonical);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Add one tag to `i18n.locales` — the one write both this section and `i18n.addLocale` make.
|
|
117
|
+
*
|
|
118
|
+
* The patch spreads `config().i18n` because `commitProjectConfig` merges at the TOP LEVEL only: `{
|
|
119
|
+
* i18n: { locales } }` replaces the block wholesale and takes `defaultLocale` and `routing` with
|
|
120
|
+
* it, so a project that declared a default would silently lose it the first time a language was
|
|
121
|
+
* added.
|
|
122
|
+
*
|
|
123
|
+
* Refusals are notified rather than thrown: the palette reports a rejected `run` to the console and
|
|
124
|
+
* nowhere else, so a thrown refusal is one the author never sees. The form disables Add for both of
|
|
125
|
+
* them before it gets here, with the reason under the field.
|
|
126
|
+
*
|
|
127
|
+
* @param {string} tag - A BCP 47 language tag, in any case.
|
|
128
|
+
* @throws {Error} Whatever `updateSiteConfig` rejects with — the caller decides where a failed
|
|
129
|
+
* write shows.
|
|
130
|
+
*/
|
|
131
|
+
export async function addProjectLocale(tag: string): Promise<void> {
|
|
132
|
+
const canonical = canonicalizeLocale(tag);
|
|
133
|
+
if (canonical === null) {
|
|
134
|
+
notify.error(`"${tag}" is not a well-formed language tag.`, {
|
|
135
|
+
detail:
|
|
136
|
+
"Languages are BCP 47 tags — a language, optionally a script and a region: `fr`, " +
|
|
137
|
+
"`pt-BR`, `zh-Hant`. Underscores and spaces are not part of the grammar.",
|
|
138
|
+
source: "Languages",
|
|
139
|
+
});
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
if (alreadyDeclared(canonical)) {
|
|
143
|
+
notify.info(`${localeLabel(canonical)} is already one of this project's languages.`, {
|
|
144
|
+
source: "Languages",
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
await updateSiteConfig({
|
|
149
|
+
i18n: { ...config().i18n, locales: [...declaredLocales(), canonical] },
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Pending tag per container, so typing survives the section re-rendering. */
|
|
154
|
+
const pendingTag = new WeakMap<HTMLElement, string>();
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Why the pending tag cannot be added, or `""` when it can.
|
|
158
|
+
*
|
|
159
|
+
* Both refusals are stated while the author is typing rather than after the Add: a language tag is
|
|
160
|
+
* the one field in this section whose value the author cannot check by looking at it, and "nothing
|
|
161
|
+
* happened" is what a silently refused Add looks like.
|
|
162
|
+
*/
|
|
163
|
+
function pendingRefusal(container: HTMLElement): string {
|
|
164
|
+
const tag = (pendingTag.get(container) ?? "").trim();
|
|
165
|
+
if (tag === "") {
|
|
166
|
+
return "";
|
|
167
|
+
}
|
|
168
|
+
if (!isWellFormedLocale(tag)) {
|
|
169
|
+
return `"${tag}" is not a well-formed language tag. Try \`fr\`, \`pt-BR\` or \`zh-Hant\`.`;
|
|
170
|
+
}
|
|
171
|
+
return alreadyDeclared(tag) ? `${localeLabel(tag)} is already declared.` : "";
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The project's languages, its default, and how a locale is spelled in a URL.
|
|
176
|
+
*
|
|
177
|
+
* Every handler re-reads `config()` instead of closing over the render's values: `projectState` is
|
|
178
|
+
* a plain module binding replaced wholesale on a project switch, and an extension or the raw JSON
|
|
179
|
+
* editor can write `i18n` between this render and the click that follows it.
|
|
180
|
+
*
|
|
181
|
+
* @param {HTMLElement} container
|
|
182
|
+
*/
|
|
183
|
+
export function renderLocalesSection(container: HTMLElement): void {
|
|
184
|
+
const rerender = () => renderLocalesSection(container);
|
|
185
|
+
const locales = declaredLocales();
|
|
186
|
+
const { i18n } = config();
|
|
187
|
+
const pending = pendingTag.get(container) ?? "";
|
|
188
|
+
const refusal = pendingRefusal(container);
|
|
189
|
+
|
|
190
|
+
const add = () => {
|
|
191
|
+
const tag = (pendingTag.get(container) ?? "").trim();
|
|
192
|
+
if (tag === "" || pendingRefusal(container) !== "") {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
pendingTag.delete(container);
|
|
196
|
+
void persist(
|
|
197
|
+
container,
|
|
198
|
+
{ i18n: { ...config().i18n, locales: [...declaredLocales(), canonicalizeLocale(tag)!] } },
|
|
199
|
+
rerender,
|
|
200
|
+
);
|
|
201
|
+
};
|
|
202
|
+
|
|
203
|
+
const remove = (tag: string) => {
|
|
204
|
+
const kept = declaredLocales().filter((declared) => declared !== tag);
|
|
205
|
+
if (kept.length === 0) {
|
|
206
|
+
/* The last language leaves with the block. An `i18n` holding an empty `locales` is a build
|
|
207
|
+
error (`resolveI18n`: "declared with no usable locale"), so removing the last row would
|
|
208
|
+
otherwise break the build of a project that is once again ordinary and monolingual. */
|
|
209
|
+
void persist(container, CLEAR_I18N, rerender);
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
const current = config().i18n;
|
|
213
|
+
/* A `defaultLocale` naming a locale that is no longer declared does not go away — `resolveI18n`
|
|
214
|
+
puts it BACK at the head of the list, so deleting the default row without this would delete
|
|
215
|
+
nothing and reorder the rest. */
|
|
216
|
+
const orphaned =
|
|
217
|
+
current?.defaultLocale !== undefined &&
|
|
218
|
+
canonicalizeLocale(current.defaultLocale) === canonicalizeLocale(tag);
|
|
219
|
+
void persist(
|
|
220
|
+
container,
|
|
221
|
+
{ i18n: { ...current, ...(orphaned ? { defaultLocale: kept[0]! } : {}), locales: kept } },
|
|
222
|
+
rerender,
|
|
223
|
+
);
|
|
224
|
+
};
|
|
225
|
+
|
|
226
|
+
const onDefaultChange = (e: Event) => {
|
|
227
|
+
void persist(
|
|
228
|
+
container,
|
|
229
|
+
{ i18n: { ...config().i18n, defaultLocale: (e.target as HTMLInputElement).value } },
|
|
230
|
+
rerender,
|
|
231
|
+
);
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const onRoutingChange = (e: Event) => {
|
|
235
|
+
void persist(
|
|
236
|
+
container,
|
|
237
|
+
{
|
|
238
|
+
i18n: {
|
|
239
|
+
...config().i18n,
|
|
240
|
+
routing: (e.target as HTMLInputElement).value as LocaleRouting,
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
rerender,
|
|
244
|
+
);
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const tpl = html`
|
|
248
|
+
<div class="settings-section">
|
|
249
|
+
<h3 class="settings-section-title">Locales</h3>
|
|
250
|
+
<p class="settings-field-desc">
|
|
251
|
+
The languages this site is written in. A translation is a file under the locale's directory
|
|
252
|
+
— pages/fr/about.json beside pages/about.json — so declaring a language here is what makes
|
|
253
|
+
that directory mean something to the build and to Studio.
|
|
254
|
+
</p>
|
|
255
|
+
${errorLine(container)}
|
|
256
|
+
<div class="settings-field">
|
|
257
|
+
<label class="settings-field-label">Languages</label>
|
|
258
|
+
<div class="settings-list-panel">
|
|
259
|
+
${
|
|
260
|
+
locales.length === 0
|
|
261
|
+
? html`<div class="settings-empty-state">No languages declared.</div>`
|
|
262
|
+
: locales.map(
|
|
263
|
+
(tag) => html`
|
|
264
|
+
<div class="settings-row">
|
|
265
|
+
<span class="settings-row-name">${localeLabel(tag)}</span>
|
|
266
|
+
<span class="settings-locale-tag">${tag}</span>
|
|
267
|
+
<sp-action-button
|
|
268
|
+
size="xs"
|
|
269
|
+
quiet
|
|
270
|
+
title=${`Remove ${tag}`}
|
|
271
|
+
@click=${() => remove(tag)}
|
|
272
|
+
>
|
|
273
|
+
<sp-icon-delete slot="icon"></sp-icon-delete>
|
|
274
|
+
</sp-action-button>
|
|
275
|
+
</div>
|
|
276
|
+
`,
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
<div class="settings-inline-form">
|
|
280
|
+
<sp-textfield
|
|
281
|
+
size="s"
|
|
282
|
+
class="settings-locale-name"
|
|
283
|
+
placeholder="fr-CA"
|
|
284
|
+
.value=${pending}
|
|
285
|
+
@input=${(e: Event) => {
|
|
286
|
+
const before = pendingRefusal(container);
|
|
287
|
+
pendingTag.set(container, (e.target as HTMLInputElement).value);
|
|
288
|
+
/* Re-render only when the VERDICT changes, not per keystroke: redrawing on every
|
|
289
|
+
character would push `.value` back into a field the author is typing in, and
|
|
290
|
+
never redrawing would leave the refusal below it describing an older tag. */
|
|
291
|
+
if (pendingRefusal(container) !== before) {
|
|
292
|
+
rerender();
|
|
293
|
+
}
|
|
294
|
+
}}
|
|
295
|
+
@keydown=${(e: KeyboardEvent) => {
|
|
296
|
+
if (e.key === "Enter") {
|
|
297
|
+
add();
|
|
298
|
+
}
|
|
299
|
+
}}
|
|
300
|
+
></sp-textfield>
|
|
301
|
+
<sp-action-button size="s" @click=${add}>Add</sp-action-button>
|
|
302
|
+
</div>
|
|
303
|
+
</div>
|
|
304
|
+
${refusal === "" ? nothing : html`<p class="settings-field-error">${refusal}</p>`}
|
|
305
|
+
</div>
|
|
306
|
+
<div class="settings-field">
|
|
307
|
+
<label class="settings-field-label">Default Language</label>
|
|
308
|
+
<p class="settings-field-desc">
|
|
309
|
+
The language a page is written in when its file carries no locale directory.
|
|
310
|
+
</p>
|
|
311
|
+
<sp-picker
|
|
312
|
+
size="s"
|
|
313
|
+
label="Default Language"
|
|
314
|
+
class="settings-default-locale"
|
|
315
|
+
.value=${i18n?.defaultLocale ?? locales[0] ?? ""}
|
|
316
|
+
?disabled=${locales.length === 0}
|
|
317
|
+
@change=${onDefaultChange}
|
|
318
|
+
>
|
|
319
|
+
${locales.map(
|
|
320
|
+
(tag) => html`<sp-menu-item value=${tag}>${localeLabel(tag)}</sp-menu-item>`,
|
|
321
|
+
)}
|
|
322
|
+
</sp-picker>
|
|
323
|
+
</div>
|
|
324
|
+
<div class="settings-field">
|
|
325
|
+
<label class="settings-field-label">URL Routing</label>
|
|
326
|
+
<p class="settings-field-desc">
|
|
327
|
+
Whether the default language owns the unprefixed URLs, or every language is named in its
|
|
328
|
+
own.
|
|
329
|
+
</p>
|
|
330
|
+
<sp-picker
|
|
331
|
+
size="s"
|
|
332
|
+
label="URL Routing"
|
|
333
|
+
class="settings-locale-routing"
|
|
334
|
+
.value=${i18n?.routing ?? "prefix-except-default"}
|
|
335
|
+
?disabled=${locales.length === 0}
|
|
336
|
+
@change=${onRoutingChange}
|
|
337
|
+
>
|
|
338
|
+
${LOCALE_ROUTINGS.map(
|
|
339
|
+
(routing) => html`<sp-menu-item value=${routing.value}>${routing.label}</sp-menu-item>`,
|
|
340
|
+
)}
|
|
341
|
+
</sp-picker>
|
|
342
|
+
</div>
|
|
343
|
+
</div>
|
|
344
|
+
`;
|
|
345
|
+
|
|
346
|
+
litRender(tpl, container);
|
|
347
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Preferences-accounts.ts — every credential Studio holds, in one list that can also forget them.
|
|
3
|
+
*
|
|
4
|
+
* Studio stores three kinds of secret in three unrelated places and, until this file, could neither
|
|
5
|
+
* enumerate nor clear any of them:
|
|
6
|
+
*
|
|
7
|
+
* - The GitHub device-flow token (`github/github-auth.ts`) — `clearGithubToken()` had **zero
|
|
8
|
+
* callers**, so signing out was impossible from inside the app;
|
|
9
|
+
* - The AI provider key, endpoint and model (`services/ai-settings.ts`) — clearable only by saving a
|
|
10
|
+
* blank key into a dialog that did not say that is what a blank key means;
|
|
11
|
+
* - The Cloudflare publish token and account id (`services/cf-settings.ts`) — not clearable at all.
|
|
12
|
+
*
|
|
13
|
+
* Plan §9.3 puts them under Preferences › Accounts, "listed, revocable". This module is that list.
|
|
14
|
+
* It is deliberately UI-free — a record per account with a `revoke` — so the disconnect path is
|
|
15
|
+
* testable without a dialog, and so the same records can back a future Problems entry or an
|
|
16
|
+
* `accounts.revoke` command without being re-derived from a template.
|
|
17
|
+
*
|
|
18
|
+
* **No secret is ever returned.** A record says whether something is stored and what it is for;
|
|
19
|
+
* printing a token into a list is how a screenshot leaks one.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { clearGithubToken, githubTokenLocation, githubTokenStored } from "../github/github-auth";
|
|
23
|
+
import { getCfAccountId, getCfToken, setCfAccountId, setCfToken } from "../services/cf-settings";
|
|
24
|
+
import {
|
|
25
|
+
getBaseUrl,
|
|
26
|
+
getModel,
|
|
27
|
+
hasOpenAiKey,
|
|
28
|
+
setBaseUrl,
|
|
29
|
+
setModel,
|
|
30
|
+
setOpenAiKey,
|
|
31
|
+
} from "../services/ai-settings";
|
|
32
|
+
|
|
33
|
+
/** One stored credential, as the Accounts section renders it. */
|
|
34
|
+
export interface AccountRecord {
|
|
35
|
+
/** Stable key — the value `revokeAccount` takes and the row's `data-account`. */
|
|
36
|
+
id: string;
|
|
37
|
+
/** The service, in the words its own docs use. */
|
|
38
|
+
label: string;
|
|
39
|
+
/** What is held, or what connecting would buy. Never the secret. */
|
|
40
|
+
detail: string;
|
|
41
|
+
connected: boolean;
|
|
42
|
+
/** Forget it. Idempotent: revoking a disconnected account is a no-op, not an error. */
|
|
43
|
+
revoke: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* The three accounts, always all three.
|
|
48
|
+
*
|
|
49
|
+
* A disconnected account still gets a row: "you are not signed in to GitHub" is information, and a
|
|
50
|
+
* list that hides what is absent cannot answer the question the section exists to answer.
|
|
51
|
+
*/
|
|
52
|
+
export function listAccounts(): AccountRecord[] {
|
|
53
|
+
const githubToken = githubTokenStored();
|
|
54
|
+
const cfToken = getCfToken();
|
|
55
|
+
const cfAccount = getCfAccountId();
|
|
56
|
+
const model = getModel();
|
|
57
|
+
const endpoint = getBaseUrl();
|
|
58
|
+
return [
|
|
59
|
+
{
|
|
60
|
+
id: "github",
|
|
61
|
+
label: "GitHub",
|
|
62
|
+
detail: githubToken
|
|
63
|
+
? githubTokenLocation() === "desktop"
|
|
64
|
+
? "Signed in — the token is in this app's config folder, readable only by you."
|
|
65
|
+
: "Signed in — a device-flow token is stored in this browser."
|
|
66
|
+
: "Not signed in. Cloning and pushing will ask when they need it.",
|
|
67
|
+
connected: Boolean(githubToken),
|
|
68
|
+
revoke: clearGithubToken,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
id: "ai",
|
|
72
|
+
label: "AI provider",
|
|
73
|
+
detail: hasOpenAiKey()
|
|
74
|
+
? `Key stored. Model ${model}${endpoint ? ` via ${endpoint}` : ""}.`
|
|
75
|
+
: "No key stored. The Assistant section is where one is entered.",
|
|
76
|
+
connected: hasOpenAiKey(),
|
|
77
|
+
revoke: () => {
|
|
78
|
+
setOpenAiKey("");
|
|
79
|
+
setBaseUrl("");
|
|
80
|
+
setModel("");
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
id: "cloudflare",
|
|
85
|
+
label: "Cloudflare",
|
|
86
|
+
detail: cfToken
|
|
87
|
+
? `Connected${cfAccount ? ` — account ${cfAccount}` : ""}.`
|
|
88
|
+
: "Not connected. Publishing will ask when it needs it.",
|
|
89
|
+
connected: Boolean(cfToken),
|
|
90
|
+
revoke: () => {
|
|
91
|
+
setCfToken("");
|
|
92
|
+
setCfAccountId("");
|
|
93
|
+
},
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Revoke one account by id.
|
|
100
|
+
*
|
|
101
|
+
* @returns `true` when an account by that id exists, `false` when it does not — so a caller with a
|
|
102
|
+
* stale id finds out rather than silently doing nothing.
|
|
103
|
+
*/
|
|
104
|
+
export function revokeAccount(id: string): boolean {
|
|
105
|
+
const account = listAccounts().find((candidate) => candidate.id === id);
|
|
106
|
+
if (!account) {
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
109
|
+
account.revoke();
|
|
110
|
+
notifyCredentialsChanged();
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const _listeners = new Set<() => void>();
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Subscribe to credential changes. Returns the unsubscribe.
|
|
118
|
+
*
|
|
119
|
+
* This is the seam that keeps the dependency one-directional: the assistant panel shows a "no
|
|
120
|
+
* provider connected" notice, Preferences is where a key is now entered and cleared, and the panel
|
|
121
|
+
* would otherwise have to be imported BY the dialog it opens. Instead both depend on this leaf.
|
|
122
|
+
*/
|
|
123
|
+
export function onCredentialsChanged(listener: () => void): () => void {
|
|
124
|
+
_listeners.add(listener);
|
|
125
|
+
return () => {
|
|
126
|
+
_listeners.delete(listener);
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Announce that a credential was saved or revoked. */
|
|
131
|
+
export function notifyCredentialsChanged(): void {
|
|
132
|
+
for (const listener of _listeners) {
|
|
133
|
+
listener();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** Drop every subscription — tests only; the app subscribes once, at mount. */
|
|
138
|
+
export function resetCredentialListeners(): void {
|
|
139
|
+
_listeners.clear();
|
|
140
|
+
}
|