@jxsuite/studio 2.4.3 → 4.0.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/README.md +5 -5
- package/dist/chunks/Overlay.dev-723bv0df.js +23 -0
- package/dist/chunks/cf-settings-45fgyx8y.js +21 -0
- package/dist/chunks/{components-z6cnkyy1.js → components-zmx10t62.js} +6 -5
- package/dist/chunks/content-source-t88vb2dr.js +49 -0
- package/dist/chunks/{convert-to-repeater-k10dvcc7.js → convert-to-repeater-dhw1nrz9.js} +27 -18
- package/dist/chunks/entry-commands-6z6bhxg0.js +46 -0
- package/dist/chunks/{extension-sections-0ada0xcw.js → extension-sections-3ydgjbdy.js} +32 -23
- package/dist/chunks/file-ops-rskmhxyq.js +48 -0
- package/dist/chunks/files-0cratsr0.js +92 -0
- package/dist/chunks/{format-host-wewba4nq.js → format-host-tgcmfs4p.js} +3 -3
- package/dist/chunks/fs-events-hrrg328g.js +21 -0
- package/dist/chunks/{git-panel-2dh1aw50.js → git-panel-0yywyk76.js} +21 -15
- package/dist/chunks/{jx-validate-jj9rnt4n.js → jx-validate-8q2g05x1.js} +6 -4
- package/dist/chunks/layers-panel-z3tqe5cd.js +67 -0
- package/dist/chunks/layers-q75tj2mf.js +45 -0
- package/dist/chunks/lit-html-j055mkh8.js +22 -0
- package/dist/chunks/monaco-buffer-dqw0xxpx.js +25 -0
- package/dist/chunks/{monaco-setup-a5wkmbgh.js → monaco-setup-rws7k4br.js} +4 -4
- package/dist/chunks/notify-yjm79n4s.js +30 -0
- package/dist/chunks/parse-24d0gay3.js +19 -0
- package/dist/chunks/platform-ba1j1gbh.js +16 -0
- package/dist/chunks/publish-panel-54jvhskq.js +28 -0
- package/dist/chunks/quick-search-cgfmjwq5.js +65 -0
- package/dist/chunks/references-3qx8fkyp.js +29 -0
- package/dist/chunks/right-panel-cqp8bc4v.js +46 -0
- package/dist/chunks/sp-overlay.dev-yztmtrs1.js +18 -0
- package/dist/chunks/sp-popover.dev-n2etmqdp.js +9 -0
- package/dist/chunks/sp-progress-circle.dev-hryhj0es.js +15 -0
- package/dist/chunks/sp-tooltip.dev-xcvpnqr0.js +15 -0
- package/dist/chunks/{sp-tray.dev-64v4pxw8.js → sp-tray.dev-tkbscm06.js} +5 -5
- package/dist/chunks/{studio-f5yyvqxv.js → studio-1hbqnt19.js} +37 -31
- package/dist/chunks/studio-2zkz8evn.js +3163 -0
- package/dist/chunks/{studio-0b6fs05q.js → studio-4pqqxbrt.js} +23 -15
- package/dist/chunks/{studio-kvfx9z2c.js → studio-4rzyrtjz.js} +70 -31
- package/dist/chunks/{studio-ef8t53vc.js → studio-5q69gfag.js} +60 -56
- package/dist/chunks/{studio-v2c5pmjj.js → studio-5rywrkxz.js} +565 -133
- package/dist/chunks/{studio-6kv3apa9.js → studio-68n67094.js} +25 -9
- package/dist/chunks/{studio-0rqqhrdy.js → studio-7nte2hj4.js} +2 -2
- package/dist/chunks/{studio-hyn3vhwr.js → studio-8swrjbny.js} +31 -18
- package/dist/chunks/studio-92ab2jqe.js +282 -0
- package/dist/chunks/{studio-sas7mt3k.js → studio-9ab8gqsm.js} +2 -2
- package/dist/chunks/{studio-28xc8ws4.js → studio-9c4k4vhv.js} +14 -10
- package/dist/chunks/studio-9n4mrmnb.js +184 -0
- package/dist/chunks/{studio-tr20m8g8.js → studio-9r6j5ev1.js} +37 -2
- package/dist/chunks/{studio-aweyr4v1.js → studio-ahjfdy6e.js} +9 -7
- package/dist/chunks/{studio-s519zc99.js → studio-bgxt93ga.js} +3 -3
- package/dist/chunks/{studio-26sv44br.js → studio-cb12yz9w.js} +8543 -8784
- package/dist/chunks/studio-cft9r4r7.js +119 -0
- package/dist/chunks/{studio-rqemfhz6.js → studio-eenfkt4e.js} +6 -6
- package/dist/chunks/studio-eesa1myv.js +498 -0
- package/dist/chunks/{studio-ay8wcr2b.js → studio-eqq1w638.js} +7 -9
- package/dist/chunks/{studio-ke244pam.js → studio-ex8zyp98.js} +51 -25
- package/dist/chunks/studio-gv8vvshc.js +118 -0
- package/dist/chunks/studio-hbtp0eh1.js +1315 -0
- package/dist/chunks/{studio-cz2j1c0g.js → studio-htsf0w6m.js} +75 -123
- package/dist/chunks/{studio-pyrw8h0w.js → studio-kjjsjak4.js} +528 -212
- package/dist/chunks/{studio-5x5774ak.js → studio-kryvsw6j.js} +3 -3
- package/dist/chunks/studio-kyh9avvv.js +28 -0
- package/dist/chunks/{studio-jnakhg87.js → studio-kyk2v92a.js} +14 -14
- package/dist/chunks/{studio-twsafr1e.js → studio-m3hwn7y1.js} +3 -3
- package/dist/chunks/{studio-tzx3xmvx.js → studio-mmv72a27.js} +8 -8
- package/dist/chunks/{studio-vhk5fmfv.js → studio-n8b9wqwy.js} +5 -5
- package/dist/chunks/{studio-cjm7fdjf.js → studio-p4ngc046.js} +8 -104
- package/dist/chunks/{studio-sccge2wf.js → studio-p8j86wag.js} +2 -2
- package/dist/chunks/{studio-v1va0vhd.js → studio-pdfjntbk.js} +75 -139
- package/dist/chunks/studio-q43a5y9h.js +394 -0
- package/dist/chunks/studio-q7v10cg2.js +106 -0
- package/dist/chunks/{studio-2w11grav.js → studio-qq6jym04.js} +3 -3
- package/dist/chunks/studio-r1r7afxx.js +1066 -0
- package/dist/chunks/{studio-3fk1ac9z.js → studio-rdx8jcxy.js} +4 -4
- package/dist/chunks/{studio-4mqb9a0j.js → studio-rn9ta2mz.js} +4 -4
- package/dist/chunks/studio-sf7zjgv1.js +91 -0
- package/dist/chunks/studio-t7qb9db8.js +665 -0
- package/dist/chunks/studio-tmc5emrw.js +8 -0
- package/dist/chunks/{studio-wdt9y92a.js → studio-txwkm6p9.js} +2 -2
- package/dist/chunks/{studio-dgj7gq44.js → studio-v38hmvx1.js} +7 -7
- package/dist/chunks/{studio-7ejemhej.js → studio-vadzgkrz.js} +6 -6
- package/dist/chunks/studio-wcy8trfx.js +263 -0
- package/dist/chunks/{studio-hb7c46jy.js → studio-wx5609xd.js} +21 -15
- package/dist/chunks/studio-xrapxwwc.js +1539 -0
- package/dist/chunks/{studio-ws6ef6js.js → studio-yxrkyv8a.js} +41 -29
- package/dist/chunks/{studio-xm3dbxvn.js → studio-zczps70q.js} +2 -2
- package/dist/chunks/workspace-xpres40v.js +89 -0
- package/dist/iframe-entry.js +450 -48
- package/dist/studio.js +2455 -1761
- package/package.json +16 -15
- package/src/browse/library-layouts.ts +3 -3
- package/src/browse/library-pane.ts +59 -5
- package/src/browse/library-preview.ts +19 -3
- package/src/canvas/asset-refs.ts +119 -0
- package/src/canvas/asset-resolve.ts +256 -0
- package/src/canvas/canvas-live-render.ts +23 -15
- package/src/canvas/canvas-render.ts +23 -1
- package/src/canvas/iframe-entry.ts +1 -0
- package/src/canvas/iframe-host.ts +4 -0
- package/src/canvas/iframe-patch.ts +47 -21
- package/src/canvas/iframe-protocol.ts +9 -0
- package/src/canvas/iframe-render.ts +50 -16
- package/src/commands/app-commands.ts +4 -0
- package/src/commands/command-args.ts +29 -0
- package/src/commands/context.ts +15 -1
- package/src/commands/defaults.ts +29 -1
- package/src/commands/levels.ts +16 -0
- package/src/commands/live-context.ts +3 -0
- package/src/content/collection-match.ts +267 -0
- package/src/content/entry-commands.ts +38 -29
- package/src/content/entry-fields.ts +6 -0
- package/src/editor/file-drop-action.ts +2 -1
- package/src/editor/shortcuts.ts +5 -0
- package/src/files/file-ops.ts +10 -42
- package/src/files/files.ts +487 -61
- package/src/files/fs-events.ts +9 -0
- package/src/files/gitignore.ts +432 -0
- package/src/files/media-paths.ts +37 -14
- package/src/files/media-upload.ts +47 -6
- package/src/files/serialize-document.ts +60 -0
- package/src/format/convert-file.ts +511 -0
- package/src/format/format-choices.ts +199 -0
- package/src/grid/grid-idle.ts +45 -0
- package/src/grid/grid-view.ts +35 -0
- package/src/grid/schema-columns.ts +2 -1
- package/src/grid/sources/content-source.ts +24 -3
- package/src/media/media-open.ts +58 -0
- package/src/media/media-pane.ts +310 -0
- package/src/new-project/import-tab.ts +333 -160
- package/src/new-project/new-project-modal.ts +27 -34
- package/src/packages/ensure-deps.ts +14 -7
- package/src/packages/jxsuite-update.ts +11 -11
- package/src/page-params.ts +10 -46
- package/src/panels/activity-bar.ts +45 -16
- package/src/panels/ai-chat/attached-context.ts +6 -1
- package/src/panels/ai-chat/chat-view.ts +255 -13
- package/src/panels/ai-chat/composer.ts +21 -80
- package/src/panels/ai-panel.ts +116 -2
- package/src/panels/frontmatter-fields.ts +6 -2
- package/src/panels/navigator-panels.ts +7 -6
- package/src/panels/properties-panel.ts +8 -2
- package/src/panels/seo-modal.ts +5 -6
- package/src/panels/settings-menu.ts +799 -0
- package/src/panels/signals-panel.ts +2 -1
- package/src/panels/toolbar.ts +135 -15
- package/src/platforms/cloud.ts +34 -3
- package/src/platforms/devserver.ts +72 -34
- package/src/preview/preview-overlay.ts +226 -0
- package/src/services/ai-ask.ts +247 -0
- package/src/services/ai-import-tools.ts +494 -0
- package/src/services/ai-models.ts +101 -11
- package/src/services/ai-project-tools.ts +20 -26
- package/src/services/ai-settings.ts +69 -71
- package/src/services/ai-system-prompt.ts +88 -2
- package/src/services/ai-turn-signal.ts +70 -0
- package/src/services/cf-settings.ts +19 -37
- package/src/services/connection.ts +74 -0
- package/src/services/context-manager.ts +89 -0
- package/src/services/document-assistant.ts +49 -7
- package/src/services/idle.ts +6 -1
- package/src/services/import-client.ts +33 -6
- package/src/services/import-run.ts +158 -0
- package/src/services/import-seed.ts +120 -0
- package/src/services/jx-validate.ts +58 -6
- package/src/services/project-adoption.ts +71 -0
- package/src/services/references.ts +0 -0
- package/src/services/settings/definitions.ts +181 -0
- package/src/services/settings/kernel.ts +335 -0
- package/src/services/settings/write-queue.ts +116 -0
- package/src/services/tool-executor.ts +34 -2
- package/src/settings/contexts-section.ts +45 -3
- package/src/settings/dependencies-editor.ts +43 -23
- package/src/settings/general-settings.ts +30 -12
- package/src/settings/preferences-accounts.ts +6 -19
- package/src/settings/preferences-dialog.ts +18 -41
- package/src/settings/preferences-keymap.ts +8 -18
- package/src/settings/preferences-sections.ts +57 -0
- package/src/settings/schema-field-ui.ts +2 -2
- package/src/settings/settings-document.ts +65 -9
- package/src/shell.ts +6 -14
- package/src/studio.ts +65 -7
- package/src/tabs/transact.ts +42 -19
- package/src/types.ts +164 -8
- package/src/ui/ai-credentials-form.ts +52 -27
- package/src/ui/ai-model-picker.ts +162 -0
- package/src/ui/choice-field.ts +72 -0
- package/src/ui/layers.ts +83 -5
- package/src/ui/media-picker.ts +6 -11
- package/src/utils/studio-utils.ts +43 -31
- package/styles/forced-colors.css +7 -0
- package/styles/overlays.css +256 -0
- package/styles/panels.css +13 -1
- package/styles/shell.css +111 -0
- package/dist/chunks/Overlay.dev-d2ajy6sj.js +0 -23
- package/dist/chunks/cf-settings-g5fbf2zp.js +0 -17
- package/dist/chunks/content-source-k9724vcv.js +0 -39
- package/dist/chunks/files-yew27sn9.js +0 -69
- package/dist/chunks/layers-panel-qnsghwwd.js +0 -59
- package/dist/chunks/publish-panel-a0pdft5w.js +0 -21
- package/dist/chunks/quick-search-9avyc4pc.js +0 -54
- package/dist/chunks/right-panel-kcnhtc5j.js +0 -38
- package/dist/chunks/sp-overlay.dev-s18hymn9.js +0 -18
- package/dist/chunks/sp-popover.dev-dj9x7e7a.js +0 -9
- package/dist/chunks/sp-progress-circle.dev-0zxcyz3r.js +0 -15
- package/dist/chunks/sp-tooltip.dev-5c6d1kjz.js +0 -15
- package/dist/chunks/studio-a7pzveq1.js +0 -110
- package/dist/chunks/studio-c31yb4t6.js +0 -55
- package/dist/chunks/studio-ra7ar50f.js +0 -6682
- package/src/canvas/content-assets.ts +0 -263
- package/src/canvas/site-style-css.ts +0 -121
- package/src/services/settings-store.ts +0 -99
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
/// <reference lib="dom" />
|
|
2
2
|
/**
|
|
3
|
-
* Dependencies settings section —
|
|
3
|
+
* Dependencies settings section — a registry-aware table of the project's npm dependencies. Shows
|
|
4
4
|
* current vs latest version, with per-row update/remove, an add-package field, "update all", and
|
|
5
|
-
* reinstall.
|
|
6
|
-
*
|
|
5
|
+
* reinstall.
|
|
6
|
+
*
|
|
7
|
+
* **Every row's Latest is that package's own newest version on npm.** Two assumptions used to sit
|
|
8
|
+
* between this table and the registry, and both are gone:
|
|
9
|
+
*
|
|
10
|
+
* 1. `@jxsuite/*` rows targeted `VERSION`, the version this Studio build embeds — right only while the
|
|
11
|
+
* whole suite released as one number. The packages release on their own cadences now, so that
|
|
12
|
+
* proposed a version that may never have been published, for a package whose real latest the
|
|
13
|
+
* table had not looked at. The same correction `packages/jxsuite-update.ts` already made.
|
|
14
|
+
* 2. The backend was asked for _outdated_ packages, so a package already at its latest arrived as an
|
|
15
|
+
* absence and the column read `—` — the registry's answer was known and thrown away. It now asks
|
|
16
|
+
* `platform.packageVersions()`, which reports every dependency's latest either way, and the
|
|
17
|
+
* comparison that decides whether an update button appears happens here.
|
|
18
|
+
*
|
|
19
|
+
* A row shows `—` only where there is genuinely nothing to show: a `workspace:`/`file:`/git spec, a
|
|
20
|
+
* package the registry does not answer for, or a host with no registry lookup at all.
|
|
7
21
|
*/
|
|
8
22
|
|
|
9
23
|
import { html, render as litRender } from "lit-html";
|
|
10
24
|
import { getPlatform } from "../platform";
|
|
11
|
-
import { VERSION } from "../version";
|
|
12
25
|
import { notify } from "../services/notify";
|
|
13
26
|
import { showProgressModal } from "../ui/progress-modal";
|
|
14
|
-
import {
|
|
27
|
+
import { isUpgrade, stripRange } from "../packages/semver";
|
|
15
28
|
import type { PackageInfo } from "../types";
|
|
16
29
|
|
|
17
|
-
const JXSUITE_PREFIX = "@jxsuite/";
|
|
18
|
-
|
|
19
30
|
interface Update {
|
|
20
31
|
name: string;
|
|
21
32
|
version: string;
|
|
@@ -28,13 +39,21 @@ let _latest = new Map<string, string>();
|
|
|
28
39
|
let _busy = false;
|
|
29
40
|
let _addName = "";
|
|
30
41
|
|
|
31
|
-
/**
|
|
42
|
+
/** This package's newest published version, or null when the registry did not answer for it. */
|
|
32
43
|
function latestFor(p: PackageInfo): string | null {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
44
|
+
return _latest.get(p.name) ?? null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* The version a package can be updated TO, or null when it is current or ahead.
|
|
49
|
+
*
|
|
50
|
+
* `isUpgrade`, not "differs from latest": a project deliberately pinned ahead of the registry — a
|
|
51
|
+
* prerelease, or a range bumped before the publish landed — would otherwise be offered a downgrade
|
|
52
|
+
* with an update button.
|
|
53
|
+
*/
|
|
54
|
+
function upgradeFor(p: PackageInfo): string | null {
|
|
55
|
+
const latest = latestFor(p);
|
|
56
|
+
return latest && isUpgrade(p.version, latest) ? latest : null;
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
async function load() {
|
|
@@ -45,10 +64,10 @@ async function load() {
|
|
|
45
64
|
_packages = [];
|
|
46
65
|
}
|
|
47
66
|
_latest = new Map();
|
|
48
|
-
if (platform.
|
|
67
|
+
if (platform.packageVersions) {
|
|
49
68
|
try {
|
|
50
|
-
for (const
|
|
51
|
-
_latest.set(
|
|
69
|
+
for (const info of await platform.packageVersions()) {
|
|
70
|
+
_latest.set(info.name, info.latest);
|
|
52
71
|
}
|
|
53
72
|
} catch {
|
|
54
73
|
/* Registry lookups are best-effort */
|
|
@@ -118,9 +137,9 @@ async function onUpdateAll() {
|
|
|
118
137
|
}
|
|
119
138
|
const updates = (_packages ?? [])
|
|
120
139
|
.map((p): Update | null => {
|
|
121
|
-
const
|
|
122
|
-
return
|
|
123
|
-
? { dev: Boolean(p.dev), name: p.name, version: `^${stripRange(
|
|
140
|
+
const target = upgradeFor(p);
|
|
141
|
+
return target
|
|
142
|
+
? { dev: Boolean(p.dev), name: p.name, version: `^${stripRange(target)}` }
|
|
124
143
|
: null;
|
|
125
144
|
})
|
|
126
145
|
.filter((u): u is Update => u !== null);
|
|
@@ -152,6 +171,7 @@ async function onReinstall() {
|
|
|
152
171
|
|
|
153
172
|
function row(p: PackageInfo) {
|
|
154
173
|
const latest = latestFor(p);
|
|
174
|
+
const upgrade = upgradeFor(p);
|
|
155
175
|
return html`
|
|
156
176
|
<sp-table-row>
|
|
157
177
|
<sp-table-cell>
|
|
@@ -163,13 +183,13 @@ function row(p: PackageInfo) {
|
|
|
163
183
|
<sp-table-cell>${latest ?? "—"}</sp-table-cell>
|
|
164
184
|
<sp-table-cell>
|
|
165
185
|
${
|
|
166
|
-
|
|
186
|
+
upgrade
|
|
167
187
|
? html`<sp-action-button
|
|
168
188
|
size="s"
|
|
169
189
|
quiet
|
|
170
190
|
?disabled=${_busy}
|
|
171
|
-
title="Update to ${
|
|
172
|
-
@click=${() => onUpdate(p,
|
|
191
|
+
title="Update to ${upgrade}"
|
|
192
|
+
@click=${() => onUpdate(p, upgrade)}
|
|
173
193
|
>
|
|
174
194
|
<sp-icon-refresh slot="icon"></sp-icon-refresh>
|
|
175
195
|
</sp-action-button>`
|
|
@@ -194,7 +214,7 @@ function render() {
|
|
|
194
214
|
return;
|
|
195
215
|
}
|
|
196
216
|
const pkgs = _packages ?? [];
|
|
197
|
-
const hasUpdates = pkgs.some((p) =>
|
|
217
|
+
const hasUpdates = pkgs.some((p) => upgradeFor(p) !== null);
|
|
198
218
|
|
|
199
219
|
const tpl = html`
|
|
200
220
|
<div class="settings-section">
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
import { html, render as litRender, nothing } from "lit-html";
|
|
13
13
|
import { live } from "lit-html/directives/live.js";
|
|
14
14
|
import { errorMessage } from "@jxsuite/schema/parse";
|
|
15
|
-
import { projectState
|
|
15
|
+
import { projectState } from "../store";
|
|
16
16
|
import { updateSiteConfig } from "../site-context";
|
|
17
|
-
import {
|
|
17
|
+
import { activeRegistry } from "../commands/active-registry";
|
|
18
18
|
import { getPlatform } from "../platform";
|
|
19
19
|
|
|
20
20
|
import type { JxHeadEntry, ProjectConfig } from "@jxsuite/schema/types";
|
|
@@ -165,16 +165,27 @@ export function renderGeneralSettings(container: HTMLElement) {
|
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
/*
|
|
168
|
-
* Project Styles is the SAME document in a different editor,
|
|
169
|
-
*
|
|
170
|
-
*
|
|
171
|
-
*
|
|
168
|
+
* Project Styles is the SAME document in a different editor, and `styles.open` is the one place
|
|
169
|
+
* that says so. This used to write `session.ui.canvasMode` here directly — a second
|
|
170
|
+
* implementation of a capability, §12.5's defect in miniature: the button and the command could
|
|
171
|
+
* disagree about what "open Project Styles" means, and only one of them was reachable by name.
|
|
172
|
+
* The mode is still a fact about the TAB, and there is exactly one `project.json` tab, so
|
|
173
|
+
* `revealTab`'s `focusPane` puts the keyboard on whichever pane holds it.
|
|
174
|
+
*
|
|
175
|
+
* Rendered FROM the record, so the button is disabled with its `requires` sentence rather than
|
|
176
|
+
* absent when it cannot act (§12.3), and it cannot drift from the palette row or the gear menu.
|
|
172
177
|
*/
|
|
173
|
-
const
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
+
const registry = activeRegistry();
|
|
179
|
+
const stylesCommand = registry?.get("styles.open");
|
|
180
|
+
/* The record's TITLE, and nothing else off the registry.
|
|
181
|
+
`disabledReason` would be the §12.3 shape for a control that can be inapplicable — but this one
|
|
182
|
+
cannot be. `styles.open` is gated on `ctx.project.open`, and Overview is a section of the
|
|
183
|
+
project's own configuration document, so by the time this renders the answer is always yes.
|
|
184
|
+
Asking anyway would also be a FOCUS read inside a function that was handed a container, which
|
|
185
|
+
is what `scripts/check-pane-singletons.ts` rule 4 forbids: a pane-scoped surface must not report
|
|
186
|
+
the focused pane's state. A project-level verb happens to read the same in every pane, but the
|
|
187
|
+
way to be right about that is not to ask. The one thing that can genuinely be missing is the
|
|
188
|
+
registry itself (bootstrap order, a test), and that is an existence check, not a state read. */
|
|
178
189
|
|
|
179
190
|
const currentFavicon = config.favicon;
|
|
180
191
|
|
|
@@ -266,7 +277,14 @@ export function renderGeneralSettings(container: HTMLElement) {
|
|
|
266
277
|
Design tokens and the default element styles that apply across every page. Opens Project
|
|
267
278
|
Styles over this same document.
|
|
268
279
|
</p>
|
|
269
|
-
<sp-action-button
|
|
280
|
+
<sp-action-button
|
|
281
|
+
size="s"
|
|
282
|
+
?disabled=${!stylesCommand}
|
|
283
|
+
title=${stylesCommand?.title ?? ""}
|
|
284
|
+
@click=${() => {
|
|
285
|
+
void registry?.run("styles.open");
|
|
286
|
+
}}
|
|
287
|
+
>
|
|
270
288
|
Edit Global Styles
|
|
271
289
|
</sp-action-button>
|
|
272
290
|
</div>
|
|
@@ -20,15 +20,9 @@
|
|
|
20
20
|
*/
|
|
21
21
|
|
|
22
22
|
import { clearGithubToken, githubTokenLocation, githubTokenStored } from "../github/github-auth";
|
|
23
|
-
import { getCfAccountId, getCfToken
|
|
24
|
-
import {
|
|
25
|
-
|
|
26
|
-
getModel,
|
|
27
|
-
hasOpenAiKey,
|
|
28
|
-
setBaseUrl,
|
|
29
|
-
setModel,
|
|
30
|
-
setOpenAiKey,
|
|
31
|
-
} from "../services/ai-settings";
|
|
23
|
+
import { clearCfConnection, getCfAccountId, getCfToken } from "../services/cf-settings";
|
|
24
|
+
import { clearAiProvider, getBaseUrl, hasOpenAiKey } from "../services/ai-settings";
|
|
25
|
+
import { preferredModel } from "../services/ai-models";
|
|
32
26
|
|
|
33
27
|
/** One stored credential, as the Accounts section renders it. */
|
|
34
28
|
export interface AccountRecord {
|
|
@@ -53,7 +47,7 @@ export function listAccounts(): AccountRecord[] {
|
|
|
53
47
|
const githubToken = githubTokenStored();
|
|
54
48
|
const cfToken = getCfToken();
|
|
55
49
|
const cfAccount = getCfAccountId();
|
|
56
|
-
const model =
|
|
50
|
+
const model = preferredModel();
|
|
57
51
|
const endpoint = getBaseUrl();
|
|
58
52
|
return [
|
|
59
53
|
{
|
|
@@ -74,11 +68,7 @@ export function listAccounts(): AccountRecord[] {
|
|
|
74
68
|
? `Key stored. Model ${model}${endpoint ? ` via ${endpoint}` : ""}.`
|
|
75
69
|
: "No key stored. The Assistant section is where one is entered.",
|
|
76
70
|
connected: hasOpenAiKey(),
|
|
77
|
-
revoke:
|
|
78
|
-
setOpenAiKey("");
|
|
79
|
-
setBaseUrl("");
|
|
80
|
-
setModel("");
|
|
81
|
-
},
|
|
71
|
+
revoke: clearAiProvider,
|
|
82
72
|
},
|
|
83
73
|
{
|
|
84
74
|
id: "cloudflare",
|
|
@@ -87,10 +77,7 @@ export function listAccounts(): AccountRecord[] {
|
|
|
87
77
|
? `Connected${cfAccount ? ` — account ${cfAccount}` : ""}.`
|
|
88
78
|
: "Not connected. Publishing will ask when it needs it.",
|
|
89
79
|
connected: Boolean(cfToken),
|
|
90
|
-
revoke:
|
|
91
|
-
setCfToken("");
|
|
92
|
-
setCfAccountId("");
|
|
93
|
-
},
|
|
80
|
+
revoke: clearCfConnection,
|
|
94
81
|
},
|
|
95
82
|
];
|
|
96
83
|
}
|
|
@@ -48,47 +48,24 @@ import type { ShortcutRow } from "../commands/reference";
|
|
|
48
48
|
import type { RebindResult } from "./preferences-keymap";
|
|
49
49
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
blurb: string;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
* The sections, in sheet order.
|
|
61
|
-
*
|
|
62
|
-
* §9.3 lists six for the finished surface (Editor behaviour and Updates/About are the other two).
|
|
63
|
-
* Four are built; a section is added here when it has something to configure, never before — an
|
|
64
|
-
* empty pane is the "declared but unbuilt" state the rail already refuses to render.
|
|
51
|
+
/*
|
|
52
|
+
* The sections moved to `./preferences-sections`, a leaf with no imports, so the rail's gear menu
|
|
53
|
+
* can enumerate `app.preferences`'s own `section` argument without pulling this module's graph
|
|
54
|
+
* (ai-models, github-auth, cf-settings, the settings kernel) into the rail. Re-exported here so
|
|
55
|
+
* every existing import site is unchanged.
|
|
65
56
|
*/
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
{
|
|
79
|
-
id: "keyboard",
|
|
80
|
-
title: "Keyboard",
|
|
81
|
-
blurb: "Generated from the command registry — the app's own record of every chord.",
|
|
82
|
-
},
|
|
83
|
-
];
|
|
84
|
-
|
|
85
|
-
/** The section the sheet opens on when none is named. */
|
|
86
|
-
export const DEFAULT_PREFERENCES_SECTION = PREFERENCES_SECTIONS[0]!.id;
|
|
87
|
-
|
|
88
|
-
/** Whether a string names a section. */
|
|
89
|
-
export function isPreferencesSection(id: unknown): boolean {
|
|
90
|
-
return PREFERENCES_SECTIONS.some((section) => section.id === id);
|
|
91
|
-
}
|
|
57
|
+
import {
|
|
58
|
+
DEFAULT_PREFERENCES_SECTION,
|
|
59
|
+
isPreferencesSection,
|
|
60
|
+
PREFERENCES_SECTIONS,
|
|
61
|
+
} from "./preferences-sections";
|
|
62
|
+
|
|
63
|
+
export {
|
|
64
|
+
DEFAULT_PREFERENCES_SECTION,
|
|
65
|
+
isPreferencesSection,
|
|
66
|
+
PREFERENCES_SECTIONS,
|
|
67
|
+
} from "./preferences-sections";
|
|
68
|
+
export type { PreferencesSection } from "./preferences-sections";
|
|
92
69
|
|
|
93
70
|
// ─── Open state ───────────────────────────────────────────────────────────────
|
|
94
71
|
|
|
@@ -588,7 +565,7 @@ export function preferencesCommands(): AnyCommand[] {
|
|
|
588
565
|
id: "app.preferences",
|
|
589
566
|
level: "application",
|
|
590
567
|
keybinding: "mod+,",
|
|
591
|
-
menus: ["commandbar/overflow", "palette"],
|
|
568
|
+
menus: ["commandbar/overflow", "settings/menu", "palette"],
|
|
592
569
|
group: "7_settings",
|
|
593
570
|
run: (_ctx, args) => {
|
|
594
571
|
const section = (args as { section?: unknown } | undefined)?.section;
|
|
@@ -34,12 +34,14 @@
|
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
36
|
import { normalizeChord, overlappingScopes, parseChord } from "../commands/keymap";
|
|
37
|
+
import { SETTINGS } from "../services/settings/definitions";
|
|
38
|
+
import { clearSettings, readStoredSetting, setSetting } from "../services/settings/kernel";
|
|
37
39
|
import type { KeybindingOverrides } from "../commands/keymap";
|
|
38
40
|
import type { KeyScope } from "../commands/levels";
|
|
39
41
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
40
42
|
|
|
41
43
|
/** Where the user's layer is kept. One key, one JSON object of `id → chords`. */
|
|
42
|
-
export const KEYBINDINGS_STORAGE_KEY =
|
|
44
|
+
export const KEYBINDINGS_STORAGE_KEY = SETTINGS.keybindings.key;
|
|
43
45
|
|
|
44
46
|
/** Keys that type nothing, and so may be bound without a modifier. */
|
|
45
47
|
const UNMODIFIED_KEYS = new Set([
|
|
@@ -114,12 +116,7 @@ export function isBindableChord(chord: string): boolean {
|
|
|
114
116
|
}
|
|
115
117
|
|
|
116
118
|
function readRaw(): string {
|
|
117
|
-
|
|
118
|
-
return globalThis.localStorage?.getItem(KEYBINDINGS_STORAGE_KEY) ?? "";
|
|
119
|
-
} catch {
|
|
120
|
-
// Storage unavailable (private mode): every command keeps the chord it declared.
|
|
121
|
-
return "";
|
|
122
|
-
}
|
|
119
|
+
return readStoredSetting(SETTINGS.keybindings);
|
|
123
120
|
}
|
|
124
121
|
|
|
125
122
|
/** Every chord in one stored entry, canonicalised — or `null` if any of them is not a chord. */
|
|
@@ -175,18 +172,11 @@ export function loadKeybindingOverrides(): Map<string, string[]> {
|
|
|
175
172
|
}
|
|
176
173
|
|
|
177
174
|
function store(layer: KeybindingOverrides): void {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
globalThis.localStorage?.setItem(
|
|
184
|
-
KEYBINDINGS_STORAGE_KEY,
|
|
185
|
-
JSON.stringify(Object.fromEntries(layer)),
|
|
186
|
-
);
|
|
187
|
-
} catch {
|
|
188
|
-
// Storage unavailable — the rebinding is live in this window and is not remembered.
|
|
175
|
+
if (layer.size === 0) {
|
|
176
|
+
clearSettings([SETTINGS.keybindings]);
|
|
177
|
+
return;
|
|
189
178
|
}
|
|
179
|
+
setSetting(SETTINGS.keybindings, JSON.stringify(Object.fromEntries(layer)));
|
|
190
180
|
}
|
|
191
181
|
|
|
192
182
|
/**
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Preferences sections — the values `app.preferences { section }` accepts.
|
|
3
|
+
*
|
|
4
|
+
* A LEAF: it imports nothing, and that is the whole reason it is a file. `preferences-dialog.ts`
|
|
5
|
+
* reaches `ui/layers`, `ui/ai-credentials-form`, `ui/ai-managed-connect`, `preferences-accounts` (→
|
|
6
|
+
* github-auth, cf-settings, ai-settings, ai-models) and `preferences-keymap` (→
|
|
7
|
+
* services/settings/kernel) — a graph so wide that `tests/preferences-dialog.test.ts` mocks
|
|
8
|
+
* `ai-models` merely to import the module at all. The rail's gear menu needs four labels from it
|
|
9
|
+
* and nothing else, so it takes the labels rather than the graph.
|
|
10
|
+
*
|
|
11
|
+
* Same split, and the same stated reason, as `settings/section-registry.ts`: what is contributed to
|
|
12
|
+
* is not the surface that renders it, and keeping the two apart is what lets either be replaced.
|
|
13
|
+
*
|
|
14
|
+
* `preferences-dialog.ts` re-exports every symbol here, so no existing import site moved.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
/** One section of the sheet. The `id` is what `app.preferences { section }` accepts. */
|
|
18
|
+
export interface PreferencesSection {
|
|
19
|
+
id: string;
|
|
20
|
+
title: string;
|
|
21
|
+
/** The one-line answer to "what is in here", shown under the heading. */
|
|
22
|
+
blurb: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The sections, in sheet order.
|
|
27
|
+
*
|
|
28
|
+
* §9.3 lists six for the finished surface (Editor behaviour and Updates/About are the other two).
|
|
29
|
+
* Four are built; a section is added here when it has something to configure, never before — an
|
|
30
|
+
* empty pane is the "declared but unbuilt" state the rail already refuses to render.
|
|
31
|
+
*/
|
|
32
|
+
export const PREFERENCES_SECTIONS: readonly PreferencesSection[] = [
|
|
33
|
+
{ id: "appearance", title: "Appearance", blurb: "How the Studio chrome looks." },
|
|
34
|
+
{
|
|
35
|
+
id: "assistant",
|
|
36
|
+
title: "Assistant",
|
|
37
|
+
blurb: "The AI provider the assistant talks to. Stored on this machine.",
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: "accounts",
|
|
41
|
+
title: "Accounts",
|
|
42
|
+
blurb: "Every credential Studio holds, and how to make it forget one.",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
id: "keyboard",
|
|
46
|
+
title: "Keyboard",
|
|
47
|
+
blurb: "Generated from the command registry — the app's own record of every chord.",
|
|
48
|
+
},
|
|
49
|
+
];
|
|
50
|
+
|
|
51
|
+
/** The section the sheet opens on when none is named. */
|
|
52
|
+
export const DEFAULT_PREFERENCES_SECTION = PREFERENCES_SECTIONS[0]!.id;
|
|
53
|
+
|
|
54
|
+
/** Whether a string names a section. */
|
|
55
|
+
export function isPreferencesSection(id: unknown): boolean {
|
|
56
|
+
return PREFERENCES_SECTIONS.some((section) => section.id === id);
|
|
57
|
+
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { html, nothing } from "lit-html";
|
|
8
|
-
import { camelToLabel } from "../utils/studio-utils";
|
|
8
|
+
import { camelToLabel, isMediaFormat } from "../utils/studio-utils";
|
|
9
9
|
|
|
10
10
|
export interface SchemaProperty {
|
|
11
11
|
type?: string;
|
|
@@ -483,7 +483,7 @@ export function yamlDefault(type: string, format?: string) {
|
|
|
483
483
|
if (format === "date") {
|
|
484
484
|
return new Date().toISOString().split("T")[0];
|
|
485
485
|
}
|
|
486
|
-
if (format
|
|
486
|
+
if (isMediaFormat(format)) {
|
|
487
487
|
return '""';
|
|
488
488
|
}
|
|
489
489
|
switch (type) {
|
|
@@ -52,6 +52,8 @@ import { renderGeneralSettings } from "./general-settings";
|
|
|
52
52
|
import { renderHeadEditor } from "./head-editor";
|
|
53
53
|
import { renderLocalesSection } from "./locales-section";
|
|
54
54
|
import { selectContributedEntry } from "./contributed-section";
|
|
55
|
+
// The NAME and the wire value, kept apart by that module on purpose — see its docstring.
|
|
56
|
+
import { PROJECT_STYLES_TITLE, PROJECT_STYLES_VIEW } from "../style/project-styles";
|
|
55
57
|
import type { Tab } from "../tabs/tab";
|
|
56
58
|
import type { AnyCommand, CommandRegistry } from "../commands/registry";
|
|
57
59
|
|
|
@@ -192,28 +194,37 @@ export function settingsDocumentOpen(): boolean {
|
|
|
192
194
|
*
|
|
193
195
|
* @returns {Tab | null} The tab, or null when no project is open
|
|
194
196
|
*/
|
|
195
|
-
export function showSettingsDocument(): Tab | null {
|
|
197
|
+
export function showSettingsDocument(mode: string = SETTINGS_MODE): Tab | null {
|
|
196
198
|
const state = requireProjectState();
|
|
197
199
|
if (!state) {
|
|
198
200
|
return null;
|
|
199
201
|
}
|
|
200
202
|
const existing = workspace.tabs.get(PROJECT_CONFIG_PATH);
|
|
201
203
|
if (existing) {
|
|
202
|
-
for (const
|
|
203
|
-
if (!existing.capabilities.modes.includes(
|
|
204
|
-
existing.capabilities.modes.unshift(
|
|
204
|
+
for (const candidate of SETTINGS_TAB_MODES) {
|
|
205
|
+
if (!existing.capabilities.modes.includes(candidate)) {
|
|
206
|
+
existing.capabilities.modes.unshift(candidate);
|
|
205
207
|
}
|
|
206
208
|
}
|
|
207
|
-
existing.session.ui.canvasMode =
|
|
209
|
+
existing.session.ui.canvasMode = mode;
|
|
208
210
|
existing.session.ui.preview = false;
|
|
209
211
|
return revealTab(existing as unknown as Tab);
|
|
210
212
|
}
|
|
211
|
-
|
|
213
|
+
const tab = openTab({
|
|
212
214
|
capabilities: { modes: [...SETTINGS_TAB_MODES] },
|
|
213
215
|
document: (state.projectConfig ?? {}) as unknown as Record<string, unknown>,
|
|
214
216
|
documentPath: PROJECT_CONFIG_PATH,
|
|
215
217
|
id: PROJECT_CONFIG_PATH,
|
|
216
218
|
});
|
|
219
|
+
/* The mode is written here rather than left to the capability list, because `createTab` seeds
|
|
220
|
+
`session.ui.canvasMode` from `resolvedModes.find(m => m !== "preview")` (`tabs/tab.ts`) — i.e.
|
|
221
|
+
from `SETTINGS_TAB_MODES[0]`, which is `settings`. A second door over the same document has to
|
|
222
|
+
SAY which editor it is, or `styles.open` from a cold workspace opens the settings form. The
|
|
223
|
+
capability list stays in its declared order for the same reason it always had one: the tab's
|
|
224
|
+
Editor control offers the modes in that order. */
|
|
225
|
+
tab.session.ui.canvasMode = mode;
|
|
226
|
+
tab.session.ui.preview = false;
|
|
227
|
+
return tab;
|
|
217
228
|
}
|
|
218
229
|
|
|
219
230
|
/**
|
|
@@ -328,10 +339,17 @@ export function settingsCommands(): AnyCommand[] {
|
|
|
328
339
|
// ⌘⇧, — the other half of §5.3's `⌘, / ⌘⇧,` pair. `app.preferences` shipped with its chord
|
|
329
340
|
// And this one did not, so the two halves of "settings" were a keystroke and a palette search.
|
|
330
341
|
keybinding: "mod+shift+,",
|
|
331
|
-
menus: ["commandbar/overflow", "palette"],
|
|
342
|
+
menus: ["commandbar/overflow", "settings/menu", "palette"],
|
|
332
343
|
group: "7_settings",
|
|
333
344
|
requires: "an open project",
|
|
334
|
-
when
|
|
345
|
+
/* `enablement`, not `when`: with no project open this row is DRAWN, greyed, carrying the
|
|
346
|
+
sentence above — §12.3's rule that a control which cannot act explains itself rather than
|
|
347
|
+
vanishing, and the reason the palette greys unavailable commands instead of hiding them.
|
|
348
|
+
It used to be `when`, so the rail's Settings menu held one row on the welcome screen and
|
|
349
|
+
said nothing about the other two; "why can't I" had no answer anywhere. The gate itself is
|
|
350
|
+
unchanged — `enabledWith` still refuses, so `registry.run` and the assistant's tool still
|
|
351
|
+
throw `CommandUnavailableError` exactly as before. */
|
|
352
|
+
enablement: (ctx) => ctx.project.open,
|
|
335
353
|
aiTool: {
|
|
336
354
|
description:
|
|
337
355
|
"Open the project's Settings, optionally on a named section (overview, contexts, head, " +
|
|
@@ -370,7 +388,45 @@ export function settingsCommands(): AnyCommand[] {
|
|
|
370
388
|
}
|
|
371
389
|
notifySettingsDocument();
|
|
372
390
|
},
|
|
373
|
-
title: "Open Settings",
|
|
391
|
+
title: "Open Project Settings",
|
|
392
|
+
},
|
|
393
|
+
/*
|
|
394
|
+
* The SECOND door over the same document. `project.json` has three editors — the settings form,
|
|
395
|
+
* Project Styles and the raw source — and until now only one of them had a name: Project Styles
|
|
396
|
+
* was reached by the pane's Editor control (which can only re-mode a tab that is ALREADY open)
|
|
397
|
+
* and by a hand-written `canvasMode` assignment inside Overview. `canvas.setMode` cannot stand
|
|
398
|
+
* in for this: it is level `document` and requires an open one, so from a cold workspace there
|
|
399
|
+
* is nothing to re-mode.
|
|
400
|
+
*
|
|
401
|
+
* It is a peer of `settings.open` and declares the same gate, byte for byte (§12.4: two verbs
|
|
402
|
+
* over one document declare ONE availability rule). `styles.*` is the project-level plural of
|
|
403
|
+
* the selection-level `style.*` verbs in `panels/style-panel.ts`; the ids are one letter apart
|
|
404
|
+
* and the levels are the reason.
|
|
405
|
+
*/
|
|
406
|
+
{
|
|
407
|
+
category: "Project",
|
|
408
|
+
id: "styles.open",
|
|
409
|
+
level: "project",
|
|
410
|
+
menus: ["settings/menu", "palette"],
|
|
411
|
+
/* A distinct ordinal inside the settings family, so the gear reads Settings → Styles.
|
|
412
|
+
§12.3's lever is `group`, never a per-menu sort; `styles.open` is not in the ⬢ menu, so
|
|
413
|
+
nothing else sees this string. */
|
|
414
|
+
group: "7_settings_styles",
|
|
415
|
+
requires: "an open project",
|
|
416
|
+
// §12.4: two verbs over ONE document declare ONE availability rule, byte-identical.
|
|
417
|
+
enablement: (ctx) => ctx.project.open,
|
|
418
|
+
aiTool: {
|
|
419
|
+
description:
|
|
420
|
+
"Open the project's configuration document in its Project Styles editor — the design " +
|
|
421
|
+
"tokens and the default element styles that apply across every page.",
|
|
422
|
+
name: "open_project_styles",
|
|
423
|
+
},
|
|
424
|
+
run: () => {
|
|
425
|
+
showSettingsDocument(PROJECT_STYLES_VIEW);
|
|
426
|
+
},
|
|
427
|
+
// Named, never spelled from the wire value: `stylebook` is shared with `dist/iframe-entry.js`
|
|
428
|
+
// And may never be renamed, so nothing a reader sees is derived from it (`style/project-styles`).
|
|
429
|
+
title: `Open ${PROJECT_STYLES_TITLE}`,
|
|
374
430
|
},
|
|
375
431
|
];
|
|
376
432
|
}
|
package/src/shell.ts
CHANGED
|
@@ -22,6 +22,8 @@
|
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
24
|
import { effect, effectScope, reactive } from "./reactivity";
|
|
25
|
+
import { SETTINGS } from "./services/settings/definitions";
|
|
26
|
+
import { readStoredSetting, setSetting } from "./services/settings/kernel";
|
|
25
27
|
import { loadedMonaco } from "./services/monaco-lazy";
|
|
26
28
|
import { applyStartupProfile } from "./services/profile";
|
|
27
29
|
import { workspace } from "./workspace/workspace";
|
|
@@ -349,8 +351,6 @@ export interface ShellState {
|
|
|
349
351
|
|
|
350
352
|
const DOCK_STORAGE_KEY = "jx-studio-panel-widths";
|
|
351
353
|
|
|
352
|
-
const THEME_STORAGE_KEY = "jx-studio-theme";
|
|
353
|
-
|
|
354
354
|
/**
|
|
355
355
|
* The per-project record's key prefix — one namespaced record per project root (§4.4).
|
|
356
356
|
*
|
|
@@ -378,14 +378,10 @@ function isChromeTheme(value: unknown): value is ChromeTheme {
|
|
|
378
378
|
return typeof value === "string" && (CHROME_THEMES as readonly string[]).includes(value);
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
-
/** Read the persisted theme, falling back to {@link DEFAULT_THEME} on absent/
|
|
381
|
+
/** Read the persisted theme, falling back to {@link DEFAULT_THEME} on an absent/unknown value. */
|
|
382
382
|
function readPersistedTheme(): ChromeTheme {
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
return isChromeTheme(stored) ? stored : DEFAULT_THEME;
|
|
386
|
-
} catch {
|
|
387
|
-
return DEFAULT_THEME;
|
|
388
|
-
}
|
|
383
|
+
const stored = readStoredSetting(SETTINGS.theme);
|
|
384
|
+
return isChromeTheme(stored) ? stored : DEFAULT_THEME;
|
|
389
385
|
}
|
|
390
386
|
|
|
391
387
|
export const DOCK_IDS: DockId[] = ["left", "right", "bottom"];
|
|
@@ -1168,11 +1164,7 @@ export function setChromeTheme(theme: ChromeTheme): void {
|
|
|
1168
1164
|
return;
|
|
1169
1165
|
}
|
|
1170
1166
|
shell.theme = theme;
|
|
1171
|
-
|
|
1172
|
-
localStorage.setItem(THEME_STORAGE_KEY, theme);
|
|
1173
|
-
} catch {
|
|
1174
|
-
// Storage full or unavailable — the theme is still applied, just not remembered.
|
|
1175
|
-
}
|
|
1167
|
+
setSetting(SETTINGS.theme, theme);
|
|
1176
1168
|
}
|
|
1177
1169
|
|
|
1178
1170
|
/**
|