@jxsuite/studio 10.0.0 → 10.2.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/dist/chunks/{2020-j56903f1.js → 2020-bx702wqa.js} +3 -3
- package/dist/chunks/adapter-pp011m5x.js +4 -0
- package/dist/chunks/add-new-flow-ecqmzz76.js +4 -0
- package/dist/chunks/cf-account-picker-c07znkqt.js +4 -0
- package/dist/chunks/cf-settings-0v2bmqy0.js +4 -0
- package/dist/chunks/components-yn0dn2pq.js +4 -0
- package/dist/chunks/content-source-g0j41yy6.js +4 -0
- package/dist/chunks/{convert-to-repeater-fhdwyg14.js → convert-to-repeater-sbjk7tgn.js} +3 -3
- package/dist/chunks/editor-xz02jeh1.js +4 -0
- package/dist/chunks/entry-commands-kmt02crp.js +4 -0
- package/dist/chunks/extension-sections-0rprf12d.js +4 -0
- package/dist/chunks/file-ops-mexj5qed.js +4 -0
- package/dist/chunks/files-b2tcppdd.js +4 -0
- package/dist/chunks/format-host-btsevv0c.js +4 -0
- package/dist/chunks/fs-events-r0apejva.js +4 -0
- package/dist/chunks/grid-open-n2a8xkht.js +4 -0
- package/dist/chunks/{grid-view-ka55z38f.js → grid-view-tghcwrrt.js} +12 -12
- package/dist/chunks/iframe-host-gx0ypyh3.js +4 -0
- package/dist/chunks/index-bs4k9tr6.js +4 -0
- package/dist/chunks/{index-qb98xhse.js → index-vr006kbt.js} +3 -3
- package/dist/chunks/{javascript-ces13wny.js → javascript-z1g3s9fd.js} +2 -2
- package/dist/chunks/{jsonMode-6md16d4v.js → jsonMode-7szwg8c4.js} +5 -5
- package/dist/chunks/jx-validate-ymf10teh.js +4 -0
- package/dist/chunks/layers-p9nfya38.js +4 -0
- package/dist/chunks/layers-panel-tt1hzhhf.js +4 -0
- package/dist/chunks/lit-html-x18n2sfz.js +4 -0
- package/dist/chunks/media-library-0n45gv8r.js +4 -0
- package/dist/chunks/media-picker-s09at95z.js +4 -0
- package/dist/chunks/{monaco-binding-p8j80s4w.js → monaco-binding-v42w9c1v.js} +3 -3
- package/dist/chunks/monaco-buffer-een2ea0v.js +4 -0
- package/dist/chunks/{monaco-setup-wasktzfg.js → monaco-setup-7bffxd6v.js} +3 -3
- package/dist/chunks/notify-qwb8radt.js +4 -0
- package/dist/chunks/parse-v77xtwv7.js +4 -0
- package/dist/chunks/platform-8jnez0wr.js +4 -0
- package/dist/chunks/publish-panel-aysnj611.js +4 -0
- package/dist/chunks/{publish-quick-vr695j26.js → publish-quick-kpbqs9mz.js} +3 -3
- package/dist/chunks/publish-workspace-nd0a8q99.js +4 -0
- package/dist/chunks/quick-search-vg0jkhk2.js +4 -0
- package/dist/chunks/references-a7v6a0e2.js +4 -0
- package/dist/chunks/right-panel-cvk1vxx5.js +4 -0
- package/dist/chunks/source-control-fsqzc6he.js +4 -0
- package/dist/chunks/studio-05jfwbmf.js +5 -0
- package/dist/chunks/{studio-hy4ye0dx.js → studio-0q5jwddh.js} +7 -7
- package/dist/chunks/{studio-jttyqrmh.js → studio-0w2dr0q8.js} +4 -4
- package/dist/chunks/{studio-xkxetm65.js → studio-1tzpds97.js} +5 -5
- package/dist/chunks/{studio-m48v13s3.js → studio-216b27sv.js} +3 -3
- package/dist/chunks/studio-2ejhqns4.js +5 -0
- package/dist/chunks/studio-2r90cts3.js +16 -0
- package/dist/chunks/{studio-h310mbqm.js → studio-399a25za.js} +4 -4
- package/dist/chunks/studio-399xknkb.js +6 -0
- package/dist/chunks/{studio-wxhy1yh7.js → studio-43y04qck.js} +4 -4
- package/dist/chunks/{studio-06j4en5h.js → studio-4g8yb6jn.js} +2 -2
- package/dist/chunks/{studio-jw0q5j6k.js → studio-4jhw38b2.js} +4 -4
- package/dist/chunks/{studio-3v5cagy9.js → studio-4ncmwqw5.js} +4 -4
- package/dist/chunks/studio-5atwjgkj.js +7 -0
- package/dist/chunks/{studio-gr36vnte.js → studio-5frqk7c0.js} +4 -4
- package/dist/chunks/{studio-9dz76yqc.js → studio-646nn2ys.js} +5 -5
- package/dist/chunks/{studio-tj2s0tks.js → studio-6cwj6xrx.js} +4 -4
- package/dist/chunks/studio-6hzdjfv0.js +5 -0
- package/dist/chunks/{studio-hcsba201.js → studio-71m92v9s.js} +7 -7
- package/dist/chunks/{studio-3c8pp7pb.js → studio-75kcdbsk.js} +4 -4
- package/dist/chunks/{studio-gwrhcrss.js → studio-78pywnp2.js} +5 -5
- package/dist/chunks/{studio-9cxmp61c.js → studio-86r8hp7r.js} +5 -5
- package/dist/chunks/studio-886t6ztg.js +5 -0
- package/dist/chunks/studio-8wnvtr4z.js +5 -0
- package/dist/chunks/studio-8z66jk4z.js +5 -0
- package/dist/chunks/{studio-ajw4k8hp.js → studio-9rbe2xpw.js} +3 -3
- package/dist/chunks/{studio-y26hx9re.js → studio-awxwjawq.js} +4 -4
- package/dist/chunks/studio-b2qpt0xy.js +14 -0
- package/dist/chunks/{studio-dx73md4n.js → studio-bvs9kpst.js} +7 -7
- package/dist/chunks/studio-c71806xz.js +7 -0
- package/dist/chunks/{studio-7dzyz9n8.js → studio-cbwks69x.js} +144 -144
- package/dist/chunks/studio-cg1bfyhf.js +9 -0
- package/dist/chunks/studio-cg710sk9.js +5 -0
- package/dist/chunks/studio-cw0wb9qk.js +7 -0
- package/dist/chunks/studio-cyheys7k.js +5 -0
- package/dist/chunks/studio-ea5m9kg5.js +5 -0
- package/dist/chunks/{studio-ry81j4q1.js → studio-ea74s8c1.js} +4 -4
- package/dist/chunks/{studio-xdpz9rpe.js → studio-efdxjwyp.js} +34 -34
- package/dist/chunks/{studio-agyvzfm4.js → studio-egmw2837.js} +4 -4
- package/dist/chunks/{studio-5tzgq6fx.js → studio-er5f383a.js} +4 -4
- package/dist/chunks/studio-f746039f.js +5 -0
- package/dist/chunks/studio-fmdymd9v.js +5 -0
- package/dist/chunks/studio-gcm12z00.js +6 -0
- package/dist/chunks/{studio-vswcpzk7.js → studio-gj98m8gy.js} +5 -5
- package/dist/chunks/{studio-yg3djpnm.js → studio-hasbgjah.js} +4 -4
- package/dist/chunks/studio-hhp4x8xf.js +6 -0
- package/dist/chunks/{studio-3w35qvay.js → studio-hz5n7x27.js} +4 -4
- package/dist/chunks/{studio-7xag4na8.js → studio-j3d2fjqz.js} +4 -4
- package/dist/chunks/studio-jeraxdgv.js +7 -0
- package/dist/chunks/studio-jvgek9xt.js +5 -0
- package/dist/chunks/studio-jws1a87n.js +5 -0
- package/dist/chunks/studio-k2sz13zg.js +5 -0
- package/dist/chunks/studio-k3m4jkdk.js +5 -0
- package/dist/chunks/{studio-r17asgs0.js → studio-kg1abnz0.js} +4 -4
- package/dist/chunks/{studio-s0fg2wav.js → studio-kg5ya5r2.js} +4 -4
- package/dist/chunks/studio-khwbgrtg.js +5 -0
- package/dist/chunks/studio-mx6wzh25.js +5 -0
- package/dist/chunks/studio-mxs3ar9t.js +5 -0
- package/dist/chunks/studio-nm4szey4.js +5 -0
- package/dist/chunks/studio-nqxqf8p2.js +4 -0
- package/dist/chunks/{studio-h5tpz5ke.js → studio-pjtntksy.js} +4 -4
- package/dist/chunks/studio-qh8berdz.js +5 -0
- package/dist/chunks/studio-qn7z4krm.js +5 -0
- package/dist/chunks/studio-qyed0ah6.js +5 -0
- package/dist/chunks/{studio-x97ay0v4.js → studio-r4bcdwhq.js} +4 -4
- package/dist/chunks/{studio-wxdcd42g.js → studio-r7pf0rkv.js} +6 -6
- package/dist/chunks/studio-rtfnhrdg.js +5 -0
- package/dist/chunks/{studio-fy7jzj9j.js → studio-rzqaz21g.js} +4 -4
- package/dist/chunks/studio-s5tzw84t.js +5 -0
- package/dist/chunks/{studio-1e8dw2w2.js → studio-sevbc3xv.js} +4 -4
- package/dist/chunks/{studio-ewarnzm4.js → studio-sxdb9h3q.js} +6 -6
- package/dist/chunks/studio-t51y8v1d.js +5 -0
- package/dist/chunks/{studio-k3bcy02e.js → studio-t98vq4r6.js} +4 -4
- package/dist/chunks/studio-th7z3fg7.js +5 -0
- package/dist/chunks/studio-tsxpqvh0.js +6 -0
- package/dist/chunks/{studio-396srjbk.js → studio-v1yascv2.js} +4 -4
- package/dist/chunks/{studio-ekgtrndg.js → studio-wc6za3zs.js} +4 -4
- package/dist/chunks/{studio-ndskgqef.js → studio-wxq6v5sv.js} +4 -4
- package/dist/chunks/{studio-4kzbrr5t.js → studio-y60sts62.js} +3 -3
- package/dist/chunks/studio-ybnwymxw.js +5 -0
- package/dist/chunks/studio-zndrqawb.js +5 -0
- package/dist/chunks/studio-zwaxn5s2.js +5 -0
- package/dist/chunks/{tsMode-h6tkz0ea.js → tsMode-f54j66ym.js} +5 -5
- package/dist/chunks/workspace-home-x1787wpv.js +4 -0
- package/dist/chunks/workspace-zpe1np43.js +4 -0
- package/dist/chunks/{ws-client-e951my95.js → ws-client-e8kj684q.js} +3 -3
- package/dist/manifest.json +76 -75
- package/dist/studio.js +83 -81
- package/package.json +4 -4
- package/src/browse/library-model.ts +56 -23
- package/src/commands/context.ts +1 -0
- package/src/editor/shortcuts.ts +3 -0
- package/src/panels/data-grid.ts +0 -4
- package/src/panels/settings-menu.ts +3 -3
- package/src/panels/settings-pane.ts +42 -56
- package/src/platform-errors.ts +17 -1
- package/src/platforms/cloud.ts +14 -0
- package/src/publish/publish-changes.ts +136 -4
- package/src/services/ai-import-tools.ts +6 -3
- package/src/services/ai-models.ts +65 -11
- package/src/services/ai-settings.ts +45 -5
- package/src/services/document-assistant.ts +5 -2
- package/src/services/settings/definitions.ts +19 -0
- package/src/settings/contributed-section.ts +16 -7
- package/src/settings/css-vars-editor.ts +4 -4
- package/src/settings/general-settings.ts +1 -4
- package/src/settings/preferences-accounts.ts +2 -2
- package/src/settings/preferences-dialog.ts +4 -2
- package/src/settings/project-sections.ts +1 -1
- package/src/settings/section-registry.ts +5 -4
- package/src/settings/settings-document.ts +33 -24
- package/src/settings/settings-workspace.ts +205 -0
- package/src/shell.ts +31 -6
- package/src/surfaces/ai-credentials-form.json +59 -5
- package/src/surfaces/ai-credentials-form.ts +15 -2
- package/src/surfaces/commandbar.ts +7 -3
- package/src/surfaces/settings-contributed.json +4 -4
- package/src/surfaces/settings-contributed.ts +2 -2
- package/src/surfaces/settings-pane.json +262 -180
- package/src/surfaces/settings-pane.ts +63 -14
- package/src/types.ts +14 -0
- package/src/ui/ai-credentials-form.ts +128 -11
- package/src/ui/regions.ts +5 -5
- package/src/workspace/tab-workspace.ts +3 -2
- package/src/workspace/workspace-home.ts +18 -4
- package/src/workspace/workspace-ids.ts +40 -7
- package/dist/chunks/adapter-wc5zvg9a.js +0 -4
- package/dist/chunks/add-new-flow-snx1s4b0.js +0 -4
- package/dist/chunks/cf-account-picker-xyqw0jex.js +0 -4
- package/dist/chunks/cf-settings-hp1pjm0n.js +0 -4
- package/dist/chunks/components-nkwphrx3.js +0 -4
- package/dist/chunks/content-source-6a253wmk.js +0 -4
- package/dist/chunks/editor-wqrc68ca.js +0 -4
- package/dist/chunks/entry-commands-nef0wrmp.js +0 -4
- package/dist/chunks/extension-sections-8ez2rrk8.js +0 -4
- package/dist/chunks/file-ops-y3w71k1n.js +0 -4
- package/dist/chunks/files-de90apn9.js +0 -4
- package/dist/chunks/format-host-zfjz4rzm.js +0 -4
- package/dist/chunks/fs-events-77c0mr0x.js +0 -4
- package/dist/chunks/grid-open-4n4fjfpf.js +0 -4
- package/dist/chunks/iframe-host-na2mywz4.js +0 -4
- package/dist/chunks/index-ewmgdb7c.js +0 -4
- package/dist/chunks/jx-validate-b309c020.js +0 -4
- package/dist/chunks/layers-8rn8y6qv.js +0 -4
- package/dist/chunks/layers-panel-eyah7h9z.js +0 -4
- package/dist/chunks/lit-html-wyqw04y2.js +0 -4
- package/dist/chunks/media-library-gs3np47y.js +0 -4
- package/dist/chunks/media-picker-tt7dk4tb.js +0 -4
- package/dist/chunks/monaco-buffer-wsf6cmnq.js +0 -4
- package/dist/chunks/notify-qwst108r.js +0 -4
- package/dist/chunks/parse-n8dfxnqf.js +0 -4
- package/dist/chunks/platform-c16mqvmp.js +0 -4
- package/dist/chunks/publish-panel-d34bhvwj.js +0 -4
- package/dist/chunks/publish-workspace-2yr60mt4.js +0 -4
- package/dist/chunks/quick-search-e6xsdv0d.js +0 -4
- package/dist/chunks/references-gsj6trdv.js +0 -4
- package/dist/chunks/right-panel-33jv1knc.js +0 -4
- package/dist/chunks/source-control-bj9bejjv.js +0 -4
- package/dist/chunks/studio-03mqh8ax.js +0 -5
- package/dist/chunks/studio-0ydxv2mr.js +0 -7
- package/dist/chunks/studio-2hsqbq7b.js +0 -7
- package/dist/chunks/studio-2j86y34y.js +0 -6
- package/dist/chunks/studio-2ks6ka4g.js +0 -5
- package/dist/chunks/studio-34jrz12k.js +0 -5
- package/dist/chunks/studio-35vrtpdq.js +0 -5
- package/dist/chunks/studio-38g0jdz7.js +0 -5
- package/dist/chunks/studio-3zge48z4.js +0 -7
- package/dist/chunks/studio-45gyxpcq.js +0 -5
- package/dist/chunks/studio-5wzdy1jc.js +0 -16
- package/dist/chunks/studio-732mw0g8.js +0 -5
- package/dist/chunks/studio-771k2n12.js +0 -5
- package/dist/chunks/studio-88bmt9rh.js +0 -5
- package/dist/chunks/studio-c1pxszat.js +0 -5
- package/dist/chunks/studio-cpkamjdn.js +0 -14
- package/dist/chunks/studio-d038kqbs.js +0 -5
- package/dist/chunks/studio-d83m0wyq.js +0 -5
- package/dist/chunks/studio-dbxv7w4s.js +0 -5
- package/dist/chunks/studio-de1s2x9v.js +0 -5
- package/dist/chunks/studio-epenqmvk.js +0 -7
- package/dist/chunks/studio-et2q4s8n.js +0 -5
- package/dist/chunks/studio-h7weqf2q.js +0 -5
- package/dist/chunks/studio-hwejq03c.js +0 -5
- package/dist/chunks/studio-k3a8rp3d.js +0 -5
- package/dist/chunks/studio-p1n2xjda.js +0 -5
- package/dist/chunks/studio-pgm83a2f.js +0 -6
- package/dist/chunks/studio-pqqq6v85.js +0 -5
- package/dist/chunks/studio-psh8mwnw.js +0 -4
- package/dist/chunks/studio-rzg8hnkf.js +0 -5
- package/dist/chunks/studio-tyw7tk0x.js +0 -5
- package/dist/chunks/studio-v9ywz2se.js +0 -6
- package/dist/chunks/studio-vzgmyt0v.js +0 -5
- package/dist/chunks/studio-x08a3m0a.js +0 -5
- package/dist/chunks/studio-x3mjr5cc.js +0 -5
- package/dist/chunks/studio-xh683qzy.js +0 -5
- package/dist/chunks/studio-yx8hzqja.js +0 -5
- package/dist/chunks/studio-zg16eeq1.js +0 -5
- package/dist/chunks/studio-zgx7svjd.js +0 -9
- package/dist/chunks/studio-zkq3r91p.js +0 -5
- package/dist/chunks/workspace-home-zkkrxq3r.js +0 -4
- package/dist/chunks/workspace-tdqqjqaa.js +0 -4
- package/src/settings/settings-dialog.ts +0 -192
- package/src/surfaces/settings-dialog.json +0 -51
- package/src/surfaces/settings-dialog.ts +0 -169
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
|
|
28
28
|
import { clearGithubToken, githubTokenLocation, githubTokenStored } from "../github/github-auth";
|
|
29
29
|
import { clearCfConnection, getCfAccountId, getCfToken } from "../services/cf-settings";
|
|
30
|
-
import { clearAiProvider, getBaseUrl, hasOpenAiKey } from "../services/ai-settings";
|
|
30
|
+
import { clearAiProvider, getAiProvider, getBaseUrl, hasOpenAiKey } from "../services/ai-settings";
|
|
31
31
|
import { preferredModel, resetModelCache } from "../services/ai-models";
|
|
32
32
|
import { getPlatform, hasPlatform } from "../platform";
|
|
33
33
|
import { getAccountEntries } from "../account-status";
|
|
@@ -292,7 +292,7 @@ export function listAccounts(): AccountRecord[] {
|
|
|
292
292
|
id: "ai",
|
|
293
293
|
label: "AI provider",
|
|
294
294
|
detail: hasOpenAiKey()
|
|
295
|
-
?
|
|
295
|
+
? `${getAiProvider() === "anthropic" ? "Anthropic key" : "Key"} stored. Model ${model}${endpoint ? ` via ${endpoint}` : ""}.`
|
|
296
296
|
: "No key stored. The Assistant section is where one is entered.",
|
|
297
297
|
connected: hasOpenAiKey(),
|
|
298
298
|
revoke: clearAiProvider,
|
|
@@ -143,8 +143,10 @@ function sectionShown(): void {
|
|
|
143
143
|
const credsForm = createAiCredentialsForm({
|
|
144
144
|
/* A sentence rather than a template: the form is a document now, and a document renders text
|
|
145
145
|
rather than another module's markup. */
|
|
146
|
-
intro:
|
|
147
|
-
|
|
146
|
+
intro: (offersProviders) =>
|
|
147
|
+
offersProviders
|
|
148
|
+
? "An Anthropic key for Claude, or any OpenAI-compatible key. Stored locally on this machine; sent only to the Studio proxy (never to a third party except your chosen provider)."
|
|
149
|
+
: "Any OpenAI-compatible key works. Stored locally on this machine; sent only to the Studio proxy (never to a third party except your chosen endpoint).",
|
|
148
150
|
// Both buttons keep the sheet open: Preferences is a place, not a wizard step, and the previous
|
|
149
151
|
// Dialog's habit of vanishing on Save is what made "did that take?" unanswerable.
|
|
150
152
|
onCancel: repaint,
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*
|
|
14
14
|
* **Raw JSON does not open a second editor.** It shows what is on disk and hands the author to the
|
|
15
15
|
* Code editor over the `project.json` tab (§17.1): the host says how, through `editAsCode`, because
|
|
16
|
-
* the section sits in the
|
|
16
|
+
* the section sits in the Settings workspace and the tab it opens is the Styles workspace's.
|
|
17
17
|
*
|
|
18
18
|
* **Deploy is no longer a Project Settings section.** Hosting moved to the Publish workspace
|
|
19
19
|
* (§17.1's Moved footnote), which hosts `renderDeploySection`; the renderer is unchanged.
|
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
*
|
|
4
4
|
* `registerSettingsSection` is a **preserved public contract** (specs/extensions.md §9.1): an
|
|
5
5
|
* extension's `project` class declares a `$studio.settings` block and `settings/extension-sections`
|
|
6
|
-
* turns it into one of these records. Project Settings has moved host
|
|
7
|
-
* document in the pane, and now a
|
|
8
|
-
* because a section is a contribution to CONFIGURATION, not
|
|
9
|
-
* is what lets the host be replaced without the contribution
|
|
6
|
+
* turns it into one of these records. Project Settings has moved host three times — a modal, then a
|
|
7
|
+
* document in the pane, then a dialog again, and now a workspace of its own (§17.1, §22.9) — and
|
|
8
|
+
* the registry moved with none of them, because a section is a contribution to CONFIGURATION, not
|
|
9
|
+
* to whatever draws it. Splitting it out is what lets the host be replaced without the contribution
|
|
10
|
+
* point being touched.
|
|
10
11
|
*
|
|
11
12
|
* **This module holds no renderer and imports nothing.** That is load-bearing rather than tidy: the
|
|
12
13
|
* gear menu (`panels/settings-menu.ts`) enumerates these sections from the top bar, so anything
|
|
@@ -3,18 +3,18 @@
|
|
|
3
3
|
* Project Settings: the built-in sections, the `project.json` tab's editors, and the two commands
|
|
4
4
|
* that reach them.
|
|
5
5
|
*
|
|
6
|
-
* **Settings is a
|
|
7
|
-
* under the transaction log with one write chokepoint (`tabs/project-config.ts`, §17.1), and it
|
|
8
|
-
* three editors, the Styles workspace's three views: Elements (`stylebook`), Variables
|
|
9
|
-
* `style/variables-view.ts`) and Code (`source`). Project Settings is neither of them:
|
|
10
|
-
*
|
|
11
|
-
* every workspace, and every section in it writes through the same chokepoint the
|
|
12
|
-
* so the file has one history whichever door changed it.
|
|
6
|
+
* **Settings is a workspace; the configuration DOCUMENT is still a tab.** `project.json` is a real
|
|
7
|
+
* Tab under the transaction log with one write chokepoint (`tabs/project-config.ts`, §17.1), and it
|
|
8
|
+
* has three editors, the Styles workspace's three views: Elements (`stylebook`), Variables
|
|
9
|
+
* (`tokens`, `style/variables-view.ts`) and Code (`source`). Project Settings is neither of them:
|
|
10
|
+
* it is the Settings workspace, whose home hosts the section list (`./settings-workspace.ts`),
|
|
11
|
+
* reached from every workspace, and every section in it writes through the same chokepoint the
|
|
12
|
+
* tab's editors do, so the file has one history whichever door changed it.
|
|
13
13
|
*
|
|
14
14
|
* **`settings.open` did not change what it means.** It still means "reach project settings,
|
|
15
15
|
* optionally at a named section and entry", its args schema still declares `section` and `entry`,
|
|
16
|
-
* and a key nobody registered still fails the call. What changed is the `run`: a
|
|
17
|
-
* a
|
|
16
|
+
* and a key nobody registered still fails the call. What changed is the `run`: a workspace switch
|
|
17
|
+
* instead of a dialog.
|
|
18
18
|
*
|
|
19
19
|
* @docs studio/projects/settings
|
|
20
20
|
*/
|
|
@@ -31,7 +31,8 @@ import { notify } from "../services/notify";
|
|
|
31
31
|
import { projectState } from "../state";
|
|
32
32
|
import { PROJECT_CONFIG_PATH } from "../tabs/tab";
|
|
33
33
|
import { lendProjectConfigLayout } from "../tabs/project-config";
|
|
34
|
-
import { focusPane, openTab, workspace } from "../workspace/workspace";
|
|
34
|
+
import { focusPane, openTab, paneOfTab, workspace } from "../workspace/workspace";
|
|
35
|
+
import { workspaceHomeTabId } from "../workspace/workspace-ids";
|
|
35
36
|
import {
|
|
36
37
|
notifySettingsDocument,
|
|
37
38
|
registerSettingsSection,
|
|
@@ -47,7 +48,7 @@ import { renderGeneralSettings } from "./general-settings";
|
|
|
47
48
|
import { renderHeadEditor } from "./head-editor";
|
|
48
49
|
import { renderLocalesSection } from "./locales-section";
|
|
49
50
|
import { selectContributedEntry } from "./contributed-section";
|
|
50
|
-
import {
|
|
51
|
+
import { focusSettingsHome } from "./settings-workspace";
|
|
51
52
|
// The NAME and the wire value, kept apart by that module on purpose — see its docstring.
|
|
52
53
|
import {
|
|
53
54
|
PROJECT_STYLES_TITLE,
|
|
@@ -79,7 +80,7 @@ const PROJECT_TAB_MODES = STYLES_TAB_MODES;
|
|
|
79
80
|
|
|
80
81
|
Two sections that used to be here are not, and their renderers are not deleted: CSS Variables
|
|
81
82
|
(`./css-vars-editor.ts`) belongs to the Styles workspace, and Deploy (`renderDeploySection`) to
|
|
82
|
-
the Publish workspace.
|
|
83
|
+
the Publish workspace. Settings' Moved footnote says where each went.
|
|
83
84
|
|
|
84
85
|
`icon` is a KEY into the kit's glyph manifest (`@jxsuite/ui/icons`), not a tag: `studio.md`
|
|
85
86
|
§13.5 is about exactly this distinction, and a key with no row draws nothing and says nothing.
|
|
@@ -156,9 +157,9 @@ registerSettingsSection({
|
|
|
156
157
|
render: renderDependenciesEditor,
|
|
157
158
|
});
|
|
158
159
|
/*
|
|
159
|
-
* Raw JSON's "Edit as code" leaves
|
|
160
|
-
* the Styles workspace's, so the
|
|
161
|
-
*
|
|
160
|
+
* Raw JSON's "Edit as code" leaves Settings: the Code editor is the `project.json` tab's, which is
|
|
161
|
+
* the Styles workspace's, so the tab opens in its source editor, and activating it is what switches
|
|
162
|
+
* the workspace (`workspace/tab-workspace.ts`).
|
|
162
163
|
*/
|
|
163
164
|
registerSettingsSection({
|
|
164
165
|
group: "advanced",
|
|
@@ -169,7 +170,6 @@ registerSettingsSection({
|
|
|
169
170
|
render: (container) => {
|
|
170
171
|
renderRawJsonSection(container, {
|
|
171
172
|
editAsCode: () => {
|
|
172
|
-
closeSettingsDialog();
|
|
173
173
|
showSettingsDocument(SOURCE_MODE);
|
|
174
174
|
},
|
|
175
175
|
});
|
|
@@ -271,12 +271,13 @@ function revealTab(tab: Tab): Tab {
|
|
|
271
271
|
/**
|
|
272
272
|
* Reach Project Settings, optionally at a named section.
|
|
273
273
|
*
|
|
274
|
-
*
|
|
275
|
-
* remembers for the session),
|
|
276
|
-
* caller that then asks whether a section exists is
|
|
277
|
-
* COALESCED (`extension-sections.ts`):
|
|
278
|
-
* activation and after every `project.json`
|
|
279
|
-
* take the caller's section down between the
|
|
274
|
+
* Switches to the Settings workspace on the requested section (or on the one it last showed, which
|
|
275
|
+
* the registry remembers for the session), puts the keyboard on that section's row, then awaits the
|
|
276
|
+
* contributed-section sync before returning, so a caller that then asks whether a section exists is
|
|
277
|
+
* asking the complete registry. The sync is COALESCED (`extension-sections.ts`):
|
|
278
|
+
* `refreshExtensionUi` fires the same function on project activation and after every `project.json`
|
|
279
|
+
* write, and a second, interleaved run could otherwise take the caller's section down between the
|
|
280
|
+
* promise resolving and the frame being drawn.
|
|
280
281
|
*
|
|
281
282
|
* @param {string} [section]
|
|
282
283
|
*/
|
|
@@ -284,7 +285,15 @@ export async function openProjectSettings(section?: string): Promise<void> {
|
|
|
284
285
|
if (section !== undefined) {
|
|
285
286
|
setSettingsSection(section);
|
|
286
287
|
}
|
|
287
|
-
|
|
288
|
+
const registry = activeRegistry();
|
|
289
|
+
if (!registry) {
|
|
290
|
+
throw new Error('command "settings.open" needs the window\'s command registry');
|
|
291
|
+
}
|
|
292
|
+
await registry.run("view.setWorkspace", { workspace: "settings" });
|
|
293
|
+
const paneId = paneOfTab(workspaceHomeTabId("settings"))?.id;
|
|
294
|
+
if (paneId !== undefined) {
|
|
295
|
+
void focusSettingsHome(paneId);
|
|
296
|
+
}
|
|
288
297
|
try {
|
|
289
298
|
const { syncExtensionSettingsSections } = await import("./extension-sections");
|
|
290
299
|
await syncExtensionSettingsSections();
|
|
@@ -318,7 +327,7 @@ export async function settingsSectionsReady(): Promise<void> {
|
|
|
318
327
|
* step are the same call.
|
|
319
328
|
*
|
|
320
329
|
* The validation is deliberately asynchronous. Extension-contributed sections (`connections`,
|
|
321
|
-
* `data`, `content`) register a tick after
|
|
330
|
+
* `data`, `content`) register a tick after Settings opens, so refusing synchronously would reject
|
|
322
331
|
* three legitimate section ids; the command opens, awaits the sync, and only then decides whether
|
|
323
332
|
* the caller named something real. A wrong key therefore fails the call instead of rendering an
|
|
324
333
|
* empty body.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
/// <reference lib="dom" />
|
|
2
|
+
/**
|
|
3
|
+
* Project Settings as a workspace (§17.1, §22.9): the home that hosts the settings editor, mounted
|
|
4
|
+
* into a pane's stage, and the two ways out of it.
|
|
5
|
+
*
|
|
6
|
+
* Project Settings was a dialog, and before that the `project.json` tab under a `settings` editor
|
|
7
|
+
* mode. The dialog was reachable from every workspace, but a dialog is a box of fixed size over a
|
|
8
|
+
* window that has a whole stage to spare, and the map-layout sections (Content types, Data tables,
|
|
9
|
+
* Connections) are two columns of their own inside the editor's two columns: four columns in 900px.
|
|
10
|
+
* A workspace gives the editor the stage, and keeps what the dialog was for: Settings is reached
|
|
11
|
+
* from every workspace through its links (the gear menu, ⌘⇧, and every `settings.open` deep link),
|
|
12
|
+
* and **Back** returns to the workspace it was opened from, which the top bar cannot say because it
|
|
13
|
+
* draws no tab for Settings.
|
|
14
|
+
*
|
|
15
|
+
* **Per pane**, for the reason `publish/publish-workspace.ts` gives: the home is a tab, so two
|
|
16
|
+
* panes can each show it. Each mount is the editor, built when the home is drawn and taken down
|
|
17
|
+
* when the pane's stage moves on (a switch to another workspace, a project closing). A visit is
|
|
18
|
+
* therefore a fresh editor: a half-typed entry or a failed save's error from the last visit, or
|
|
19
|
+
* from another project's, does not come back. **Which section shows is the registry's**, not this
|
|
20
|
+
* module's (`settings/section-registry.ts`), and the registry outlives every mount, which is what
|
|
21
|
+
* makes Settings reopen where the author left it for the rest of the session.
|
|
22
|
+
*
|
|
23
|
+
* @docs studio/projects/settings
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { activeRegistry } from "../commands/active-registry";
|
|
27
|
+
import { runActiveReported } from "../commands/run-reported";
|
|
28
|
+
import { mountSettingsPane } from "../panels/settings-pane";
|
|
29
|
+
import { previousWorkspace } from "../shell";
|
|
30
|
+
import { paneRegion } from "../ui/regions";
|
|
31
|
+
import { DEFAULT_WORKSPACE, workspaceInfo } from "../workspace/workspace-ids";
|
|
32
|
+
import type { CanvasSurface } from "../canvas/canvas-surface";
|
|
33
|
+
import type { SettingsPaneMount, SettingsPaneOptions } from "../panels/settings-pane";
|
|
34
|
+
import type { PublishTab } from "../publish/publish-ids";
|
|
35
|
+
import type { InspectorTabId } from "../shell";
|
|
36
|
+
import type { StylesView } from "../style/project-styles";
|
|
37
|
+
import type { Tab } from "../tabs/tab";
|
|
38
|
+
import type { WorkspaceId } from "../workspace/workspace-ids";
|
|
39
|
+
|
|
40
|
+
/** The region part the home is stamped with, under its pane: `pane.<id>/settings`. */
|
|
41
|
+
export const SETTINGS_REGION_PART = "settings";
|
|
42
|
+
|
|
43
|
+
/** The region of the Settings home in `paneId`. */
|
|
44
|
+
export function settingsRegion(paneId: string): string {
|
|
45
|
+
return paneRegion(paneId, SETTINGS_REGION_PART);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/** One pane's mount: the editor, and which tab and stage it was drawn for. */
|
|
49
|
+
interface PaneMount {
|
|
50
|
+
pane: SettingsPaneMount;
|
|
51
|
+
tabId: string;
|
|
52
|
+
wrap: HTMLElement;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const _panes = new Map<string, PaneMount>();
|
|
56
|
+
|
|
57
|
+
/** Panes whose next mount takes the keyboard: Settings was asked for before its home was drawn. */
|
|
58
|
+
const _focusOnMount = new Set<string>();
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Where Back goes: the workspace entered before Settings, or Design when there was none (a session
|
|
62
|
+
* that was restored into Settings).
|
|
63
|
+
*/
|
|
64
|
+
export function returnWorkspace(): WorkspaceId {
|
|
65
|
+
const previous = previousWorkspace();
|
|
66
|
+
return previous === null || previous === "settings" ? DEFAULT_WORKSPACE : previous;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Leave Settings for the workspace it was opened from.
|
|
71
|
+
*
|
|
72
|
+
* A refusal (the switch is gated on an open project) is filed in Problems under this surface's name
|
|
73
|
+
* instead of being dropped.
|
|
74
|
+
*/
|
|
75
|
+
export async function leaveSettings(): Promise<void> {
|
|
76
|
+
await runActiveReported("view.setWorkspace", { workspace: returnWorkspace() }, "Settings");
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Leave Settings for the workspace something moved to: the Moved footnote's buttons.
|
|
81
|
+
*
|
|
82
|
+
* A row that names an Inspector tab ("Head and SEO defaults → Page tab") also shows the Inspector
|
|
83
|
+
* on that tab, AFTER the switch, because entering a workspace applies its frame preset and would
|
|
84
|
+
* otherwise put the dock back as the preset has it. The tab is a document-level view, so with no
|
|
85
|
+
* document open there is no tab to show and asking would only file a refusal; the dock is still
|
|
86
|
+
* opened, which is where the tab appears once one is.
|
|
87
|
+
*
|
|
88
|
+
* A row that names a Styles view ("Colors and fonts → Styles › Variables") runs `styles.open` with
|
|
89
|
+
* that view instead, which is the same workspace switch followed by the view, and a row that names
|
|
90
|
+
* a Publish sub-tab runs `publish.open` on it.
|
|
91
|
+
*
|
|
92
|
+
* @param {WorkspaceId} workspace
|
|
93
|
+
* @param {InspectorTabId} [inspector]
|
|
94
|
+
* @param {StylesView} [view]
|
|
95
|
+
* @param {PublishTab} [publishTab]
|
|
96
|
+
* @returns {Promise<void>} Settles once every command the row runs has settled
|
|
97
|
+
*/
|
|
98
|
+
export async function openMoved(
|
|
99
|
+
workspace: WorkspaceId,
|
|
100
|
+
inspector?: InspectorTabId,
|
|
101
|
+
view?: StylesView,
|
|
102
|
+
publishTab?: PublishTab,
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
if (view !== undefined) {
|
|
105
|
+
await runActiveReported("styles.open", { view }, "Settings");
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
// Hosting is a Publish sub-tab, not the workspace's first: the button says where it lands.
|
|
109
|
+
if (publishTab !== undefined) {
|
|
110
|
+
await runActiveReported("publish.open", { tab: publishTab }, "Settings");
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
await runActiveReported("view.setWorkspace", { workspace }, "Settings");
|
|
114
|
+
if (inspector === undefined) {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
await runActiveReported("view.setRightPanel", { open: true }, "Settings");
|
|
118
|
+
if (activeRegistry()?.isEnabled("view.setRightTab") === true) {
|
|
119
|
+
await runActiveReported("view.setRightTab", { tab: inspector }, "Settings");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Whether this tab's home is already mounted in this pane and still in the document. */
|
|
124
|
+
export function settingsWorkspaceMounted(paneId: string, tab: Tab): boolean {
|
|
125
|
+
const mount = _panes.get(paneId);
|
|
126
|
+
return (
|
|
127
|
+
mount !== undefined && mount.tabId === tab.id && mount.wrap.isConnected && mount.pane.attached()
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/** Tear one pane's home down (mode change, tab switch, project close). Idempotent. */
|
|
132
|
+
export function detachSettingsWorkspace(paneId: string): void {
|
|
133
|
+
const mount = _panes.get(paneId);
|
|
134
|
+
if (!mount) {
|
|
135
|
+
return;
|
|
136
|
+
}
|
|
137
|
+
_panes.delete(paneId);
|
|
138
|
+
mount.pane.dispose();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Put the keyboard on the current section's row once the editor has drawn it. */
|
|
142
|
+
async function focusWhenReady(paneId: string, mount: PaneMount): Promise<void> {
|
|
143
|
+
await mount.pane.ready;
|
|
144
|
+
if (_panes.get(paneId) === mount) {
|
|
145
|
+
await mount.pane.focusActive();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Give the keyboard to the Settings home in `paneId`, on the current section's row: what opening
|
|
151
|
+
* Settings does, so an author who asked for Advanced › Raw JSON is on Raw JSON rather than on the
|
|
152
|
+
* first group's tab stop. Served now when the home is standing in the pane, or by its next mount
|
|
153
|
+
* there.
|
|
154
|
+
*
|
|
155
|
+
* @param {string} paneId
|
|
156
|
+
* @returns {Promise<void>} Settles once the keyboard has moved, or once the request is queued
|
|
157
|
+
*/
|
|
158
|
+
export async function focusSettingsHome(paneId: string): Promise<void> {
|
|
159
|
+
const mount = _panes.get(paneId);
|
|
160
|
+
if (mount?.wrap.isConnected === true && mount.pane.attached()) {
|
|
161
|
+
await focusWhenReady(paneId, mount);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
_focusOnMount.add(paneId);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* What the home's controls do, the same in every pane: Back and the Moved buttons are workspace
|
|
169
|
+
* switches, window-level commands that act wherever the author is, and no fact about the pane the
|
|
170
|
+
* home is drawn in goes into them. Declared here rather than in {@link renderSettingsWorkspace} so
|
|
171
|
+
* nothing that consults the window's state is reached from a function holding a pane.
|
|
172
|
+
*/
|
|
173
|
+
const HOME_ACTIONS: Omit<SettingsPaneOptions, "region"> = {
|
|
174
|
+
back: () => {
|
|
175
|
+
void leaveSettings();
|
|
176
|
+
},
|
|
177
|
+
backLabel: () => `Back to ${workspaceInfo(returnWorkspace()).title}`,
|
|
178
|
+
openMoved: (workspace, inspector, view, publishTab) => {
|
|
179
|
+
void openMoved(workspace, inspector, view, publishTab);
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Draw the Settings home into a pane's stage. It reads the pane's id and its stage, and nothing
|
|
185
|
+
* else of the canvas surface.
|
|
186
|
+
*
|
|
187
|
+
* @param {Pick<CanvasSurface, "paneId" | "wrap">} surface
|
|
188
|
+
* @param {Tab} tab
|
|
189
|
+
*/
|
|
190
|
+
export function renderSettingsWorkspace(
|
|
191
|
+
surface: Pick<CanvasSurface, "paneId" | "wrap">,
|
|
192
|
+
tab: Tab,
|
|
193
|
+
): void {
|
|
194
|
+
const { paneId, wrap } = surface;
|
|
195
|
+
if (settingsWorkspaceMounted(paneId, tab)) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
detachSettingsWorkspace(paneId);
|
|
199
|
+
const pane = mountSettingsPane(wrap, { ...HOME_ACTIONS, region: settingsRegion(paneId) });
|
|
200
|
+
const mount: PaneMount = { pane, tabId: tab.id, wrap };
|
|
201
|
+
_panes.set(paneId, mount);
|
|
202
|
+
if (_focusOnMount.delete(paneId)) {
|
|
203
|
+
void focusWhenReady(paneId, mount);
|
|
204
|
+
}
|
|
205
|
+
}
|
package/src/shell.ts
CHANGED
|
@@ -45,8 +45,8 @@ import type { AnyCommand, CommandRegistry } from "./commands/registry";
|
|
|
45
45
|
import {
|
|
46
46
|
DEFAULT_WORKSPACE,
|
|
47
47
|
isWorkspaceId,
|
|
48
|
+
SWITCHER_WORKSPACES,
|
|
48
49
|
WORKSPACE_IDS,
|
|
49
|
-
WORKSPACES,
|
|
50
50
|
} from "./workspace/workspace-ids";
|
|
51
51
|
import type { WorkspaceId } from "./workspace/workspace-ids";
|
|
52
52
|
import { DEFAULT_PUBLISH_DESTINATION, DEFAULT_PUBLISH_TAB } from "./publish/publish-ids";
|
|
@@ -416,7 +416,7 @@ export interface WorkspacePreset {
|
|
|
416
416
|
}
|
|
417
417
|
|
|
418
418
|
/**
|
|
419
|
-
* The
|
|
419
|
+
* The six presets.
|
|
420
420
|
*
|
|
421
421
|
* - **Design** draws the rail and both side docks: the canvas, its outline and its inspector.
|
|
422
422
|
* - **Content** opens the Navigator on the Collections panel and collapses the Inspector, drawing it
|
|
@@ -427,8 +427,10 @@ export interface WorkspacePreset {
|
|
|
427
427
|
* schemes), and the Inspector on its Style tab only while the Elements view is on screen: that
|
|
428
428
|
* view selects an element default on a canvas, and the Inspector is where it is styled. Variables
|
|
429
429
|
* is edited in the view itself, and Code has no canvas, so both leave the pane its width.
|
|
430
|
+
* - **Settings** collapses both side docks, as Media and Publish do: its home is the settings editor,
|
|
431
|
+
* whose section list is its own navigator.
|
|
430
432
|
*
|
|
431
|
-
* Only Design and Content draw a tab strip; the other
|
|
433
|
+
* Only Design and Content draw a tab strip; the other four draw their home surface alone.
|
|
432
434
|
*/
|
|
433
435
|
export const WORKSPACE_PRESETS: Readonly<Record<WorkspaceId, WorkspacePreset>> = {
|
|
434
436
|
content: {
|
|
@@ -459,6 +461,13 @@ export const WORKSPACE_PRESETS: Readonly<Record<WorkspaceId, WorkspacePreset>> =
|
|
|
459
461
|
rail: false,
|
|
460
462
|
tabStrip: false,
|
|
461
463
|
},
|
|
464
|
+
settings: {
|
|
465
|
+
id: "settings",
|
|
466
|
+
inspector: { open: false },
|
|
467
|
+
navigator: { open: false },
|
|
468
|
+
rail: false,
|
|
469
|
+
tabStrip: false,
|
|
470
|
+
},
|
|
462
471
|
styles: {
|
|
463
472
|
id: "styles",
|
|
464
473
|
inspector: { open: false, tab: "style", whileWriting: true },
|
|
@@ -833,6 +842,19 @@ let _workspaceRoot: string | null | undefined;
|
|
|
833
842
|
*/
|
|
834
843
|
let _panels: Partial<Record<WorkspaceId, string>> = {};
|
|
835
844
|
|
|
845
|
+
/**
|
|
846
|
+
* The workspace on screen before the one that is, in this window, or `null` before the first
|
|
847
|
+
* switch. Not persisted, for the reason {@link _panels} is not. Project Settings' **Close** returns
|
|
848
|
+
* here (`settings/settings-workspace.ts`), because Settings is visited from somewhere rather than
|
|
849
|
+
* worked in, and the top bar has no tab to say where that was.
|
|
850
|
+
*/
|
|
851
|
+
let _previous: WorkspaceId | null = null;
|
|
852
|
+
|
|
853
|
+
/** The workspace entered before the one on screen, or `null` when there was none this session. */
|
|
854
|
+
export function previousWorkspace(): WorkspaceId | null {
|
|
855
|
+
return _previous;
|
|
856
|
+
}
|
|
857
|
+
|
|
836
858
|
/** The raw record for `root`, or `{}` for absent, corrupt or hand-edited storage. */
|
|
837
859
|
function rawPersistedProject(root: string | null): JsonObject {
|
|
838
860
|
if (!root) {
|
|
@@ -949,6 +971,7 @@ export function syncProjectWorkspace(root: string | null): void {
|
|
|
949
971
|
}
|
|
950
972
|
_workspaceRoot = root;
|
|
951
973
|
_panels = {};
|
|
974
|
+
_previous = null;
|
|
952
975
|
shell.workspace = persistedWorkspace(root);
|
|
953
976
|
if (root === null) {
|
|
954
977
|
/* No frame to apply, but the Navigator is still on screen beside the rail: a panel only the
|
|
@@ -1020,6 +1043,7 @@ export function enterWorkspace(id: WorkspaceId, deps?: WorkspaceDeps): boolean {
|
|
|
1020
1043
|
if (!WORKSPACE_PRESETS[shell.workspace].navigator.panel) {
|
|
1021
1044
|
_panels[shell.workspace] = shell.leftTab;
|
|
1022
1045
|
}
|
|
1046
|
+
_previous = shell.workspace;
|
|
1023
1047
|
shell.workspace = id;
|
|
1024
1048
|
applyFrame(id, deps);
|
|
1025
1049
|
persistDocks();
|
|
@@ -1580,8 +1604,9 @@ export function shellViewCommands(deps: ShellCommandDeps): AnyCommand[] {
|
|
|
1580
1604
|
title: "Show Assistant",
|
|
1581
1605
|
},
|
|
1582
1606
|
// ── Workspaces ───────────────────────────────────────────────────────────
|
|
1583
|
-
|
|
1584
|
-
|
|
1607
|
+
/* One SETTER, and one chord record per switcher workspace. A chord cannot carry an argument, so
|
|
1608
|
+
⌘1–5 are five records that each run the setter's own path rather than five implementations
|
|
1609
|
+
of it. Settings has none: `settings.open` is its record, with its own chord (⌘⇧,). */
|
|
1585
1610
|
{
|
|
1586
1611
|
args: argsSchema({
|
|
1587
1612
|
workspace: enumProperty(WORKSPACE_IDS, "Which workspace to show."),
|
|
@@ -1603,7 +1628,7 @@ export function shellViewCommands(deps: ShellCommandDeps): AnyCommand[] {
|
|
|
1603
1628
|
},
|
|
1604
1629
|
title: "Switch Workspace",
|
|
1605
1630
|
},
|
|
1606
|
-
...
|
|
1631
|
+
...SWITCHER_WORKSPACES.map((entry, index): AnyCommand => ({
|
|
1607
1632
|
category: "View",
|
|
1608
1633
|
id: `workspace.focus.${entry.id}`,
|
|
1609
1634
|
keybinding: `mod+${index + 1}`,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://jxsuite.com/schema/v1",
|
|
3
3
|
"$id": "StudioAiCredentialsForm",
|
|
4
|
-
"$description": "The AI provider credentials form every gate embeds below the keyless offer: a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether there is anything to Save or Cancel at all. This document never reads a setting and never calls the proxy.",
|
|
4
|
+
"$description": "The AI provider credentials form every gate embeds below the keyless offer: a provider family (only where the backend reaches more than one), a key, a model, an endpoint, and the two buttons that keep or abandon them. Everything here is a projection — `ui/ai-credentials-form.ts` keeps the drafts, decides what a fetch is sent with, what Save actually persists and whether there is anything to Save or Cancel at all. This document never reads a setting and never calls the proxy.",
|
|
5
5
|
"tagName": "div",
|
|
6
6
|
"attributes": {
|
|
7
7
|
"part": "ai-creds-form",
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"fontWeight": "500",
|
|
33
33
|
"fontSize": "var(--jx-text-sm)"
|
|
34
34
|
},
|
|
35
|
-
"& [part=\"key\"], & [part=\"model\"], & [part=\"endpoint\"]": {
|
|
35
|
+
"& [part=\"provider\"], & [part=\"key\"], & [part=\"model\"], & [part=\"endpoint\"]": {
|
|
36
36
|
"$description": "Every control takes the column's width; the kit's fields are inline-flex by default.",
|
|
37
37
|
"width": "100%"
|
|
38
38
|
},
|
|
@@ -73,6 +73,54 @@
|
|
|
73
73
|
},
|
|
74
74
|
"textContent": "${state.intro}"
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
"$description": "The family the key belongs to (specs/ai.md §2.6), drawn only where it is a choice: the backend's models route lists more than the OpenAI-compatible family, or the stored or drafted family is already another one, so a reader can always switch back. A backend that lists nothing reaches the OpenAI-compatible family alone and forwards every key there, so offering Claude to it would send a Claude key to OpenAI. Above the key because it decides what the key is.",
|
|
78
|
+
"tagName": "div",
|
|
79
|
+
"attributes": {
|
|
80
|
+
"part": "provider-slot",
|
|
81
|
+
"role": "none"
|
|
82
|
+
},
|
|
83
|
+
"style": {
|
|
84
|
+
"display": "contents"
|
|
85
|
+
},
|
|
86
|
+
"$switch": {
|
|
87
|
+
"$ref": "#/state/showProvider"
|
|
88
|
+
},
|
|
89
|
+
"cases": {
|
|
90
|
+
"true": {
|
|
91
|
+
"tagName": "jx-select",
|
|
92
|
+
"attributes": {
|
|
93
|
+
"part": "provider"
|
|
94
|
+
},
|
|
95
|
+
"$props": {
|
|
96
|
+
"label": "Provider",
|
|
97
|
+
"size": "sm",
|
|
98
|
+
"options": {
|
|
99
|
+
"$ref": "#/state/providerOptions"
|
|
100
|
+
},
|
|
101
|
+
"value": {
|
|
102
|
+
"$ref": "#/state/providerDraft"
|
|
103
|
+
}
|
|
104
|
+
},
|
|
105
|
+
"onchange": {
|
|
106
|
+
"$prototype": "Function",
|
|
107
|
+
"body": [
|
|
108
|
+
{
|
|
109
|
+
"operator": "call",
|
|
110
|
+
"target": {
|
|
111
|
+
"$ref": "#/state/setProvider"
|
|
112
|
+
},
|
|
113
|
+
"value": [
|
|
114
|
+
{
|
|
115
|
+
"$ref": "event#/target/value"
|
|
116
|
+
}
|
|
117
|
+
]
|
|
118
|
+
}
|
|
119
|
+
]
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
},
|
|
76
124
|
{
|
|
77
125
|
"tagName": "jx-textfield",
|
|
78
126
|
"attributes": {
|
|
@@ -82,7 +130,9 @@
|
|
|
82
130
|
"label": "AI provider key",
|
|
83
131
|
"type": "password",
|
|
84
132
|
"size": "sm",
|
|
85
|
-
"placeholder":
|
|
133
|
+
"placeholder": {
|
|
134
|
+
"$ref": "#/state/keyPlaceholder"
|
|
135
|
+
},
|
|
86
136
|
"value": {
|
|
87
137
|
"$ref": "#/state/keyDraft"
|
|
88
138
|
}
|
|
@@ -120,7 +170,9 @@
|
|
|
120
170
|
"$props": {
|
|
121
171
|
"label": "Model ID",
|
|
122
172
|
"size": "sm",
|
|
123
|
-
"placeholder":
|
|
173
|
+
"placeholder": {
|
|
174
|
+
"$ref": "#/state/modelPlaceholder"
|
|
175
|
+
},
|
|
124
176
|
"allowsCustomValue": true,
|
|
125
177
|
"value": {
|
|
126
178
|
"$ref": "#/state/modelDraft"
|
|
@@ -231,7 +283,9 @@
|
|
|
231
283
|
"$props": {
|
|
232
284
|
"label": "Endpoint",
|
|
233
285
|
"size": "sm",
|
|
234
|
-
"placeholder":
|
|
286
|
+
"placeholder": {
|
|
287
|
+
"$ref": "#/state/endpointPlaceholder"
|
|
288
|
+
},
|
|
235
289
|
"value": {
|
|
236
290
|
"$ref": "#/state/baseUrlDraft"
|
|
237
291
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
/**
|
|
3
3
|
* The AI provider credentials form as a mounted document.
|
|
4
4
|
*
|
|
5
|
-
* `ui/ai-credentials-form.ts` is the flow — it holds the
|
|
5
|
+
* `ui/ai-credentials-form.ts` is the flow — it holds the drafts, decides which credentials a
|
|
6
6
|
* listing is made with, what Save persists and re-reads, and whether there is anything to commit —
|
|
7
7
|
* and this is the surface it draws into.
|
|
8
8
|
*
|
|
@@ -37,10 +37,22 @@ registerSurface("ai-credentials-form", credentialsDoc);
|
|
|
37
37
|
export interface AiCredentialsView {
|
|
38
38
|
/** The line under the title: where the key goes, and where it does not. */
|
|
39
39
|
intro: string;
|
|
40
|
-
/** The
|
|
40
|
+
/** The drafts, exactly as the flow holds them. */
|
|
41
|
+
providerDraft: string;
|
|
41
42
|
keyDraft: string;
|
|
42
43
|
modelDraft: string;
|
|
43
44
|
baseUrlDraft: string;
|
|
45
|
+
/**
|
|
46
|
+
* Whether the provider picker is drawn: the backend reaches another family, or the drafted or
|
|
47
|
+
* stored family already is one (specs/ai.md §2.6).
|
|
48
|
+
*/
|
|
49
|
+
showProvider: boolean;
|
|
50
|
+
/** The families the picker offers. */
|
|
51
|
+
providerOptions: SurfaceOption[];
|
|
52
|
+
/** What each field says while it is empty; they follow the drafted family. */
|
|
53
|
+
keyPlaceholder: string;
|
|
54
|
+
modelPlaceholder: string;
|
|
55
|
+
endpointPlaceholder: string;
|
|
44
56
|
/** The catalogue the last successful listing returned; empty draws no chevron on the field. */
|
|
45
57
|
models: SurfaceOption[];
|
|
46
58
|
/** A listing is in flight: the button says so and refuses a second one. */
|
|
@@ -59,6 +71,7 @@ export interface AiCredentialsView {
|
|
|
59
71
|
|
|
60
72
|
/** Everything the reader can do here. */
|
|
61
73
|
export interface AiCredentialsActions {
|
|
74
|
+
setProvider: (value: string) => void;
|
|
62
75
|
setKey: (value: string) => void;
|
|
63
76
|
setModel: (value: string) => void;
|
|
64
77
|
setBaseUrl: (value: string) => void;
|
|
@@ -57,7 +57,7 @@ import {
|
|
|
57
57
|
openSettingsMenu,
|
|
58
58
|
SETTINGS_MENU_PLACEMENT,
|
|
59
59
|
} from "../panels/settings-menu";
|
|
60
|
-
import {
|
|
60
|
+
import { SWITCHER_WORKSPACES } from "../workspace/workspace-ids";
|
|
61
61
|
import type { WorkspaceId } from "../workspace/workspace-ids";
|
|
62
62
|
import commandbarDoc from "./commandbar.json";
|
|
63
63
|
import type { PresenceProjection } from "../collab/presence-chips";
|
|
@@ -584,7 +584,11 @@ function projectSegments(tab: Tab | null): SegmentProjection[] {
|
|
|
584
584
|
}
|
|
585
585
|
|
|
586
586
|
/**
|
|
587
|
-
* `Design · Content · Media · Styles · Publish`: the five workspaces, as a tablist.
|
|
587
|
+
* `Design · Content · Media · Styles · Publish`: the five switcher workspaces, as a tablist.
|
|
588
|
+
*
|
|
589
|
+
* Project Settings is a workspace too, and is not here: it is reached from its links (the gear
|
|
590
|
+
* menu's row, ⌘⇧, and every `settings.open` deep link), so while it is on screen no tab is selected
|
|
591
|
+
* and any of these five leaves it (studio.md §22.2).
|
|
588
592
|
*
|
|
589
593
|
* Each tab RUNS `view.setWorkspace`, so the click, ⌘1–5, the palette row and a script all take the
|
|
590
594
|
* same path, and the selected tab is `shell.workspace` rather than a second record of it. Publish
|
|
@@ -598,7 +602,7 @@ function projectWorkspaces(registry: CommandRegistry | null): WorkspaceTabProjec
|
|
|
598
602
|
return [];
|
|
599
603
|
}
|
|
600
604
|
const dirty = shell.git.status?.files.length ?? 0;
|
|
601
|
-
return
|
|
605
|
+
return SWITCHER_WORKSPACES.map((entry) => {
|
|
602
606
|
const badge = entry.id === "publish" && dirty > 0 ? String(dirty) : "";
|
|
603
607
|
const chord = registry.keymap.formatBinding(`workspace.focus.${entry.id}`);
|
|
604
608
|
const named = chord ? `${entry.title} (${chord})` : entry.title;
|