@jxsuite/studio 0.30.1 → 0.31.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jxsuite/studio",
3
- "version": "0.30.1",
3
+ "version": "0.31.0",
4
4
  "description": "Jx Studio — visual builder for Jx documents",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -23,19 +23,20 @@
23
23
  "provenance": true
24
24
  },
25
25
  "scripts": {
26
- "build": "bun build ./src/studio.ts --outdir dist --target browser --sourcemap=linked && bun run scripts/build-workers.ts",
26
+ "build": "bun run scripts/build.ts",
27
27
  "build:metafile": "bun build ./src/studio.ts --outdir dist --target browser --sourcemap=linked --metafile=metafile.json",
28
28
  "gen:webdata": "bun run scripts/gen-webdata.js",
29
- "test": "bun test --isolate",
29
+ "lint:styles": "bun run scripts/check-styles.ts",
30
+ "test": "bun run lint:styles && bun test --isolate",
30
31
  "test:coverage": "bun test --isolate --coverage",
31
32
  "upgrade": "bunx npm-check-updates -u && bun install"
32
33
  },
33
34
  "dependencies": {
34
35
  "@atlaskit/pragmatic-drag-and-drop": "^2.0.1",
35
36
  "@atlaskit/pragmatic-drag-and-drop-hitbox": "^2.0.0",
36
- "@jxsuite/parser": "^0.30.1",
37
- "@jxsuite/runtime": "^0.30.1",
38
- "@jxsuite/schema": "^0.30.1",
37
+ "@jxsuite/parser": "^0.31.0",
38
+ "@jxsuite/runtime": "^0.31.0",
39
+ "@jxsuite/schema": "^0.31.0",
39
40
  "@spectrum-web-components/accordion": "^1.12.1",
40
41
  "@spectrum-web-components/action-bar": "1.12.1",
41
42
  "@spectrum-web-components/action-button": "^1.12.1",
@@ -61,6 +62,7 @@
61
62
  "@spectrum-web-components/picker": "^1.12.1",
62
63
  "@spectrum-web-components/picker-button": "^1.12.1",
63
64
  "@spectrum-web-components/popover": "^1.12.1",
65
+ "@spectrum-web-components/progress-circle": "^1.12.1",
64
66
  "@spectrum-web-components/search": "^1.12.1",
65
67
  "@spectrum-web-components/swatch": "^1.12.1",
66
68
  "@spectrum-web-components/switch": "^1.12.1",
@@ -0,0 +1,156 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * About modal — shows app version, build metadata, external links, the resolved `@jxsuite/*`
4
+ * package versions, and (on desktop) the release channel / update status.
5
+ *
6
+ * Build metadata comes from src/version.ts (injected at bundle time). Package versions are fetched
7
+ * lazily via the platform; desktop update info is shown only when the active platform implements
8
+ * the optional `getAppInfo` method.
9
+ */
10
+
11
+ import { html } from "lit-html";
12
+ import type { TemplateResult } from "lit-html";
13
+ import { openModal } from "../ui/layers";
14
+ import { getPlatform } from "../platform";
15
+ import { APP_NAME, BUILD_DATE, GIT_COMMIT, LINKS, VERSION } from "../version";
16
+ import type { AppInfo, PackageInfo } from "../types";
17
+
18
+ let _handle: ReturnType<typeof openModal> | null = null;
19
+
20
+ let _packages: PackageInfo[] | null = null;
21
+ let _appInfo: AppInfo | null = null;
22
+
23
+ export function openAboutModal() {
24
+ if (_handle) {
25
+ return;
26
+ }
27
+ _packages = null;
28
+ _appInfo = null;
29
+ renderModal();
30
+ void loadDetails();
31
+ }
32
+
33
+ export function closeAboutModal() {
34
+ if (!_handle) {
35
+ return;
36
+ }
37
+ _handle.close();
38
+ _handle = null;
39
+ _packages = null;
40
+ _appInfo = null;
41
+ }
42
+
43
+ async function loadDetails() {
44
+ const platform = getPlatform();
45
+ try {
46
+ _packages = await platform.listPackages();
47
+ } catch {
48
+ _packages = [];
49
+ }
50
+ if (platform.getAppInfo) {
51
+ try {
52
+ _appInfo = await platform.getAppInfo();
53
+ } catch {
54
+ _appInfo = null;
55
+ }
56
+ }
57
+ if (_handle) {
58
+ renderModal();
59
+ }
60
+ }
61
+
62
+ function formatBuildDate(iso: string) {
63
+ if (!iso) {
64
+ return "—";
65
+ }
66
+ const date = new Date(iso);
67
+ return Number.isNaN(date.getTime()) ? iso : date.toLocaleString();
68
+ }
69
+
70
+ function metaRows() {
71
+ const rows: [string, string][] = [
72
+ ["Version", VERSION],
73
+ ["Build date", formatBuildDate(BUILD_DATE)],
74
+ ["Commit", GIT_COMMIT],
75
+ ];
76
+ if (_appInfo) {
77
+ rows.push(["Channel", _appInfo.channel]);
78
+ if (_appInfo.updateStatus) {
79
+ rows.push(["Updates", _appInfo.updateStatus]);
80
+ }
81
+ }
82
+ return rows;
83
+ }
84
+
85
+ function renderPackages(): TemplateResult {
86
+ if (_packages === null) {
87
+ return html`<p class="about-muted">Loading packages…</p>`;
88
+ }
89
+ if (_packages.length === 0) {
90
+ return html`<p class="about-muted">No packages reported.</p>`;
91
+ }
92
+ return html`
93
+ <ul class="about-packages">
94
+ ${_packages.map(
95
+ (p) => html`
96
+ <li class="about-package-row">
97
+ <span class="about-package-name">${p.name}</span>
98
+ <span class="about-package-version">${p.version}</span>
99
+ </li>
100
+ `,
101
+ )}
102
+ </ul>
103
+ `;
104
+ }
105
+
106
+ function renderModal() {
107
+ const tpl = html`
108
+ <sp-underlay open @close=${closeAboutModal}></sp-underlay>
109
+ <div
110
+ class="about-modal"
111
+ role="dialog"
112
+ aria-label="About ${APP_NAME}"
113
+ @keydown=${(e: KeyboardEvent) => {
114
+ if (e.key === "Escape") {
115
+ closeAboutModal();
116
+ }
117
+ }}
118
+ >
119
+ <div class="settings-modal-header">
120
+ <h2 class="settings-modal-title">About ${APP_NAME}</h2>
121
+ <sp-action-button quiet size="s" @click=${closeAboutModal} title="Close">
122
+ <sp-icon-close slot="icon"></sp-icon-close>
123
+ </sp-action-button>
124
+ </div>
125
+ <div class="about-modal-body">
126
+ <dl class="about-meta">
127
+ ${metaRows().map(
128
+ ([label, value]) => html`
129
+ <div class="about-meta-row">
130
+ <dt class="about-meta-label">${label}</dt>
131
+ <dd class="about-meta-value">${value}</dd>
132
+ </div>
133
+ `,
134
+ )}
135
+ </dl>
136
+
137
+ <div class="about-links">
138
+ <a href=${LINKS.github} target="_blank" rel="noreferrer noopener">GitHub</a>
139
+ <a href=${LINKS.docs} target="_blank" rel="noreferrer noopener">Documentation</a>
140
+ <a href=${LINKS.license} target="_blank" rel="noreferrer noopener">License</a>
141
+ </div>
142
+
143
+ <section class="about-section">
144
+ <h3 class="about-section-title">Packages</h3>
145
+ ${renderPackages()}
146
+ </section>
147
+ </div>
148
+ </div>
149
+ `;
150
+
151
+ if (_handle) {
152
+ _handle.update(tpl);
153
+ } else {
154
+ _handle = openModal(tpl);
155
+ }
156
+ }
@@ -16,6 +16,8 @@ import { createState, projectState, requireProjectState, setProjectState } from
16
16
  import { getPlatform } from "../platform";
17
17
  import { statusMessage } from "../panels/statusbar";
18
18
  import { loadComponentRegistry } from "./components";
19
+ import { ensureDependenciesInstalled } from "../packages/ensure-deps";
20
+ import { maybePromptJxsuiteUpdate } from "../packages/jxsuite-update";
19
21
  import { markLocalMutation } from "./fs-events";
20
22
  import {
21
23
  draggable,
@@ -23,14 +25,7 @@ import {
23
25
  monitorForElements,
24
26
  } from "@atlaskit/pragmatic-drag-and-drop/element/adapter";
25
27
  import { combine } from "@atlaskit/pragmatic-drag-and-drop/combine";
26
- import {
27
- activateTab,
28
- activeTab,
29
- openTab,
30
- renameTab,
31
- replaceAllTabs,
32
- workspace,
33
- } from "../workspace/workspace";
28
+ import { activateTab, openTab, renameTab, replaceAllTabs, workspace } from "../workspace/workspace";
34
29
  import { parseSourceForPath, serializeDocument } from "./file-ops";
35
30
  import {
36
31
  documentExtensions,
@@ -100,9 +95,11 @@ export async function loadProject() {
100
95
  });
101
96
 
102
97
  if (info.isSiteProject) {
98
+ await ensureDependenciesInstalled();
103
99
  await loadDirectory(".");
104
100
  await loadComponentRegistry();
105
101
  await openHomePage();
102
+ void maybePromptJxsuiteUpdate(meta.root);
106
103
  }
107
104
  // If not a site project (monorepo) — show welcome prompt, don't load tree
108
105
  } catch {
@@ -156,6 +153,7 @@ export async function openProject({
156
153
  selectedPath: null,
157
154
  });
158
155
 
156
+ await ensureDependenciesInstalled();
159
157
  await loadDirectory(".");
160
158
  await loadComponentRegistry();
161
159
 
@@ -187,6 +185,7 @@ export async function openProject({
187
185
  statusMessage(`Opened project: ${requireProjectState().name}`);
188
186
 
189
187
  await openHomePage();
188
+ void maybePromptJxsuiteUpdate(requireProjectState().projectRoot);
190
189
  } catch (error) {
191
190
  statusMessage(`Error: ${errorMessage(error)}`);
192
191
  }
@@ -1056,13 +1055,6 @@ export async function openFileInTab(path: string) {
1056
1055
  requireProjectState().selectedPath = path;
1057
1056
  trackRecentFile({ name: path.split("/").pop() || path, path });
1058
1057
 
1059
- if (path === "project.json") {
1060
- const tab = activeTab.value;
1061
- if (tab) {
1062
- tab.session.ui.canvasMode = "stylebook";
1063
- }
1064
- }
1065
-
1066
1058
  statusMessage(`Opened ${path.split("/").pop()}`);
1067
1059
  } catch (error) {
1068
1060
  statusMessage(`Error: ${errorMessage(error)}`);
@@ -0,0 +1,39 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * Ensure the active project's dependencies are installed before the editor loads. When the backend
4
+ * reports node_modules is missing, run `bun install` behind a blocking progress modal so component
5
+ * and format resolution see the real dependency tree. No-op on platforms without package support.
6
+ */
7
+
8
+ import { getPlatform } from "../platform";
9
+ import { showProgressModal } from "../ui/progress-modal";
10
+
11
+ export async function ensureDependenciesInstalled(): Promise<void> {
12
+ const platform = getPlatform();
13
+ if (!platform.dependenciesNeedInstall || !platform.installDependencies) {
14
+ return;
15
+ }
16
+ let needs = false;
17
+ try {
18
+ needs = await platform.dependenciesNeedInstall();
19
+ } catch {
20
+ return;
21
+ }
22
+ if (!needs) {
23
+ return;
24
+ }
25
+ const progress = showProgressModal({
26
+ status: "Running bun install…",
27
+ title: "Installing dependencies",
28
+ });
29
+ try {
30
+ const result = await platform.installDependencies();
31
+ if (result.ok) {
32
+ progress.done();
33
+ } else {
34
+ progress.fail(result.log ?? "bun install failed");
35
+ }
36
+ } catch (error) {
37
+ progress.fail(error instanceof Error ? error.message : String(error));
38
+ }
39
+ }
@@ -0,0 +1,131 @@
1
+ /// <reference lib="dom" />
2
+ /**
3
+ * On project open, compare the project's `@jxsuite/*` dependency ranges to the version this Studio
4
+ * build embeds (`VERSION`). If the project is behind, prompt the user to bump them to match and —
5
+ * on confirm — rewrite the ranges and reinstall. The target is the embedded version (not npm
6
+ * latest) so a project always lines up with the runtime/compiler the running app actually uses.
7
+ */
8
+
9
+ import { html } from "lit-html";
10
+ import { getPlatform } from "../platform";
11
+ import { VERSION } from "../version";
12
+ import { showConfirmDialog } from "../ui/layers";
13
+ import { showProgressModal } from "../ui/progress-modal";
14
+ import { statusMessage } from "../panels/statusbar";
15
+ import { isComparable, isUpgrade } from "./semver";
16
+ import type { PackageInfo } from "../types";
17
+
18
+ const JXSUITE_PREFIX = "@jxsuite/";
19
+
20
+ export interface JxsuiteUpdate {
21
+ name: string;
22
+ current: string;
23
+ dev: boolean;
24
+ }
25
+
26
+ /**
27
+ * Compare the project's @jxsuite/* deps to the embedded version. Returns the packages behind the
28
+ * target plus the target version, or null when there's nothing to do (dev build, no project, or all
29
+ * already current/ahead).
30
+ */
31
+ export async function checkJxsuiteUpdate(): Promise<{
32
+ target: string;
33
+ outdated: JxsuiteUpdate[];
34
+ } | null> {
35
+ const target = VERSION;
36
+ if (!isComparable(target)) {
37
+ return null; // "dev" / non-semver build — no reliable target
38
+ }
39
+ const platform = getPlatform();
40
+ let pkgs: PackageInfo[];
41
+ try {
42
+ pkgs = await platform.listPackages();
43
+ } catch {
44
+ return null;
45
+ }
46
+ const outdated: JxsuiteUpdate[] = [];
47
+ for (const p of pkgs) {
48
+ if (!p.name.startsWith(JXSUITE_PREFIX) || !isComparable(p.version)) {
49
+ continue;
50
+ }
51
+ if (isUpgrade(p.version, target)) {
52
+ outdated.push({ current: p.version, dev: Boolean(p.dev), name: p.name });
53
+ }
54
+ }
55
+ return outdated.length > 0 ? { outdated, target } : null;
56
+ }
57
+
58
+ function dismissKey(root: string, target: string): string {
59
+ return `jx:jxsuite-update-dismissed:${root}:${target}`;
60
+ }
61
+
62
+ function isDismissed(root: string, target: string): boolean {
63
+ try {
64
+ return localStorage.getItem(dismissKey(root, target)) === "1";
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ function setDismissed(root: string, target: string): void {
71
+ try {
72
+ localStorage.setItem(dismissKey(root, target), "1");
73
+ } catch {
74
+ /* Ignore storage errors */
75
+ }
76
+ }
77
+
78
+ /** Apply a set of @jxsuite bumps to `^target` and reinstall, behind a progress modal. */
79
+ export async function applyJxsuiteUpdate(outdated: JxsuiteUpdate[], target: string): Promise<void> {
80
+ const platform = getPlatform();
81
+ if (!platform.setPackageVersions) {
82
+ return;
83
+ }
84
+ const progress = showProgressModal({
85
+ status: "Updating @jxsuite packages…",
86
+ title: "Updating dependencies",
87
+ });
88
+ try {
89
+ const result = await platform.setPackageVersions(
90
+ outdated.map((p) => ({ dev: p.dev, name: p.name, version: `^${target}` })),
91
+ );
92
+ if (result.ok) {
93
+ progress.done();
94
+ statusMessage(`Updated ${outdated.length} @jxsuite package(s) to ${target}`);
95
+ } else {
96
+ progress.fail(result.log ?? "Update failed");
97
+ }
98
+ } catch (error) {
99
+ progress.fail(error instanceof Error ? error.message : String(error));
100
+ }
101
+ }
102
+
103
+ /**
104
+ * Prompt to update @jxsuite packages on open. Skips when there's nothing to do or the user already
105
+ * declined this exact target for this project (remembered in localStorage).
106
+ */
107
+ export async function maybePromptJxsuiteUpdate(projectRoot: string): Promise<void> {
108
+ const platform = getPlatform();
109
+ if (!platform.setPackageVersions) {
110
+ return;
111
+ }
112
+ const check = await checkJxsuiteUpdate();
113
+ if (!check || isDismissed(projectRoot, check.target)) {
114
+ return;
115
+ }
116
+ const list = check.outdated.map((p) => `${p.name} ${p.current} → ^${check.target}`).join("\n");
117
+ const confirmed = await showConfirmDialog(
118
+ "Update @jxsuite packages?",
119
+ html`This project uses older @jxsuite packages. Update them to match Studio ${check.target}?
120
+ <br /><br /><span
121
+ style="font-size:var(--spectrum-font-size-75, 12px);color:var(--fg-dim);white-space:pre-line"
122
+ >${list}</span
123
+ >`,
124
+ { cancelLabel: "Not now", confirmLabel: "Update" },
125
+ );
126
+ if (!confirmed) {
127
+ setDismissed(projectRoot, check.target);
128
+ return;
129
+ }
130
+ await applyJxsuiteUpdate(check.outdated, check.target);
131
+ }
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Minimal semver helpers for comparing dependency versions in the UI. Deliberately tiny — we only
3
+ * need range stripping and ordering of `x.y.z` triples, not full semver range satisfaction, so we
4
+ * avoid pulling in a dependency.
5
+ */
6
+
7
+ /** Strip a range prefix to its base version: "^0.19.0" → "0.19.0", "~1.2.3" → "1.2.3". */
8
+ export function stripRange(range: string): string {
9
+ const base = range.replace(/^[\^~>=<v\s]+/, "").trim();
10
+ return base.split(/\s+/)[0] ?? "";
11
+ }
12
+
13
+ /**
14
+ * Whether a spec is a comparable registry semver (a plain `x.y.z` range), as opposed to
15
+ * `workspace:`, `file:`, `link:`, a git/URL spec, or a wildcard like `*`/`latest`.
16
+ */
17
+ export function isComparable(spec: string): boolean {
18
+ if (spec.includes(":")) {
19
+ return false;
20
+ }
21
+ return /^\d+\.\d+/.test(stripRange(spec));
22
+ }
23
+
24
+ /** Parse a version into numeric [major, minor, patch], ignoring any pre-release/build suffix. */
25
+ function parts(version: string): [number, number, number] {
26
+ const core = stripRange(version).split(/[-+]/)[0] ?? "";
27
+ const [major = 0, minor = 0, patch = 0] = core.split(".").map((n) => Math.trunc(Number(n)) || 0);
28
+ return [major, minor, patch];
29
+ }
30
+
31
+ /**
32
+ * Compare two versions (range prefixes tolerated). Returns -1 if a < b, 1 if a > b, 0 if equal.
33
+ * Pre-release/build metadata is ignored.
34
+ */
35
+ export function compareSemver(a: string, b: string): number {
36
+ const pa = parts(a);
37
+ const pb = parts(b);
38
+ for (let i = 0; i < 3; i++) {
39
+ const da = pa[i] ?? 0;
40
+ const db = pb[i] ?? 0;
41
+ if (da !== db) {
42
+ return da < db ? -1 : 1;
43
+ }
44
+ }
45
+ return 0;
46
+ }
47
+
48
+ /** Whether `latest` is strictly newer than the base version of `current` (both comparable). */
49
+ export function isUpgrade(current: string, latest: string): boolean {
50
+ if (!isComparable(current) || !isComparable(latest)) {
51
+ return false;
52
+ }
53
+ return compareSemver(latest, current) > 0;
54
+ }
@@ -7,6 +7,7 @@ import { effect, effectScope } from "../reactivity";
7
7
  import { activeTab } from "../workspace/workspace";
8
8
  import { applyPanelCollapse, view } from "../view";
9
9
  import { openSettingsModal } from "../settings/settings-modal";
10
+ import { openAboutModal } from "../about/about-modal";
10
11
  import { refreshGitStatus } from "./git-panel";
11
12
  import type { EffectScope } from "@vue/reactivity";
12
13
  import type { TemplateResult } from "lit-html";
@@ -129,7 +130,18 @@ export function renderActivityBar() {
129
130
  `,
130
131
  )}
131
132
  </sp-tabs>
132
- <div style="margin-top:auto;padding:8px 0;display:flex;justify-content:center">
133
+ <div
134
+ style="margin-top:auto;padding:8px 0;display:flex;flex-direction:column;align-items:center;gap:4px"
135
+ >
136
+ <sp-action-button
137
+ quiet
138
+ size="m"
139
+ title="About"
140
+ aria-label="About"
141
+ @click=${() => openAboutModal()}
142
+ >
143
+ <sp-icon-info slot="icon"></sp-icon-info>
144
+ </sp-action-button>
133
145
  <sp-action-button
134
146
  quiet
135
147
  size="m"
@@ -457,7 +457,9 @@ export function renderAiPanelTemplate() {
457
457
  <div class="ai-status-center">
458
458
  <sp-icon-artboard style="font-size:32px"></sp-icon-artboard>
459
459
  <div>Claude authentication required</div>
460
- <div style="font-size:11px;color:var(--spectrum-global-color-gray-600)">
460
+ <div
461
+ style="font-size:var(--spectrum-font-size-50, 11px);color:var(--spectrum-gray-600, #808080)"
462
+ >
461
463
  Run the following in your terminal:
462
464
  </div>
463
465
  <code class="ai-code-snippet">npx @anthropic-ai/claude-code login</code>
@@ -141,7 +141,8 @@ export function renderElementsTemplate(ctx: {
141
141
  }}
142
142
  >
143
143
  <div class="element-card-preview">
144
- <span style="color:var(--fg-dim);font-size:11px;font-style:italic"
144
+ <span
145
+ style="color:var(--fg-dim);font-size:var(--spectrum-font-size-50, 11px);font-style:italic"
145
146
  >&lt;${comp.tagName}&gt;</span
146
147
  >
147
148
  </div>
@@ -151,7 +151,11 @@ export function renderLayersTemplate(ctx: {
151
151
  class="layer-row"
152
152
  style="padding-left:${depth * 16 + 8}px; opacity: 0.6; font-style: italic;"
153
153
  >
154
- <span class="layer-tag" style="background: #64748b; font-size: 0.65rem;">text</span>
154
+ <span
155
+ class="layer-tag"
156
+ style="background: var(--spectrum-gray-500, #64748b); font-size: 0.65rem;"
157
+ >text</span
158
+ >
155
159
  <span class="layer-label">${textPreview}</span>
156
160
  </div>
157
161
  `,
@@ -122,7 +122,7 @@ export function renderCanvasNode(
122
122
  const placeholder = document.createElement("div");
123
123
  placeholder.textContent = `[$switch: ${keys.join(" | ")}]`;
124
124
  placeholder.style.cssText =
125
- "font-family:monospace;font-size:11px;padding:6px 10px;background:color-mix(in srgb, var(--danger) 8%, transparent);border:1px dashed color-mix(in srgb, var(--danger) 40%, transparent);border-radius:4px;color:var(--danger);font-style:italic";
125
+ "font-family:var(--font-mono);font-size:var(--spectrum-font-size-50,11px);padding:6px 10px;background:color-mix(in srgb, var(--danger) 8%, transparent);border:1px dashed color-mix(in srgb, var(--danger) 40%, transparent);border-radius:var(--radius);color:var(--danger);font-style:italic";
126
126
  el.append(placeholder);
127
127
  }
128
128
 
@@ -331,7 +331,7 @@ function renderSwitchFieldsTemplate(
331
331
  mapSignals,
332
332
  )}
333
333
  <div
334
- style="font-size:11px;font-weight:600;color:var(--fg-dim);margin:8px 0 4px;text-transform:uppercase;letter-spacing:0.05em"
334
+ style="font-size:var(--spectrum-font-size-50, 11px);font-weight:600;color:var(--fg-dim);margin:8px 0 4px;text-transform:uppercase;letter-spacing:0.05em"
335
335
  >
336
336
  Cases
337
337
  </div>
@@ -369,7 +369,7 @@ function renderSwitchFieldsTemplate(
369
369
  >→</span
370
370
  >
371
371
  <span
372
- style="cursor:pointer;color:var(--danger);font-size:11px"
372
+ style="cursor:pointer;color:var(--danger);font-size:var(--spectrum-font-size-50, 11px)"
373
373
  @click=${(e: Event) => {
374
374
  e.stopPropagation();
375
375
  transactDoc(activeTab.value!, (t) => mutateRemoveSwitchCase(t, path, caseName));
@@ -657,7 +657,7 @@ function renderMediaFieldsTemplate(node: JxMutableNode) {
657
657
  }}
658
658
  />
659
659
  <span
660
- style="font-size:10px;color:var(--fg-dim);font-family:'SF Mono','Fira Code',monospace;white-space:nowrap"
660
+ style="font-size:10px;color:var(--fg-dim);font-family:var(--font-mono);white-space:nowrap"
661
661
  >${view.addBreakpointPreview}</span
662
662
  >
663
663
  </div>
@@ -714,7 +714,7 @@ function mediaBreakpointRowTemplate(name: string, query: string) {
714
714
  <input
715
715
  class="field-input"
716
716
  .value=${live(mediaDisplayName(name))}
717
- style="flex:1;font-weight:600;font-size:12px"
717
+ style="flex:1;font-weight:600;font-size:var(--spectrum-font-size-75, 12px)"
718
718
  @input=${(e: Event) => {
719
719
  const newKey = friendlyNameToMedia((e.target as HTMLInputElement).value);
720
720
  currentRawLabel = newKey || "";
@@ -738,7 +738,7 @@ function mediaBreakpointRowTemplate(name: string, query: string) {
738
738
  />
739
739
  <span
740
740
  class="bp-raw-label"
741
- style="font-size:10px;color:var(--fg-dim);font-family:'SF Mono','Fira Code',monospace;white-space:nowrap"
741
+ style="font-size:10px;color:var(--fg-dim);font-family:var(--font-mono);white-space:nowrap"
742
742
  >${name}</span
743
743
  >
744
744
  <span
@@ -892,14 +892,18 @@ function renderLayoutSelectionPanel(ctx: { navigateToComponent: (path: string) =
892
892
  style="font-size:9px;padding:2px 6px;background:var(--spectrum-purple-600);color:white;border-radius:3px;text-transform:uppercase;letter-spacing:0.5px"
893
893
  >Layout</span
894
894
  >
895
- <code style="font-size:12px;font-family:monospace">&lt;${tagName}&gt;</code>
895
+ <code
896
+ style="font-size:var(--spectrum-font-size-75, 12px);font-family:var(--font-mono)"
897
+ >&lt;${tagName}&gt;</code
898
+ >
896
899
  </div>
897
900
  ${className
898
901
  ? html`<div class="style-row">
899
902
  <div class="style-row-label">
900
903
  <sp-field-label size="s">Class</sp-field-label>
901
904
  </div>
902
- <span style="font-size:11px;color:var(--fg-dim);word-break:break-all"
905
+ <span
906
+ style="font-size:var(--spectrum-font-size-50, 11px);color:var(--fg-dim);word-break:break-all"
903
907
  >${className}</span
904
908
  >
905
909
  </div>`
@@ -1234,9 +1238,9 @@ export function renderPropertiesPanelTemplate(ctx: {
1234
1238
  const def = d as Record<string, unknown>;
1235
1239
  return html`
1236
1240
  <div
1237
- style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:11px"
1241
+ style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:var(--spectrum-font-size-50, 11px)"
1238
1242
  >
1239
- <code style="font-family:monospace;color:var(--accent)"
1243
+ <code style="font-family:var(--font-mono);color:var(--accent)"
1240
1244
  >${def.attribute}</code
1241
1245
  >
1242
1246
  <span style="color:var(--fg-dim)"> → </span>
@@ -1248,7 +1252,7 @@ export function renderPropertiesPanelTemplate(ctx: {
1248
1252
  : nothing}
1249
1253
  ${def.reflects
1250
1254
  ? html`<span
1251
- style="font-size:9px;background:var(--bg-hover);padding:1px 4px;border-radius:3px"
1255
+ style="font-size:9px;background:var(--hover-bg);padding:1px 4px;border-radius:var(--radius)"
1252
1256
  >reflects</span
1253
1257
  >`
1254
1258
  : nothing}
@@ -1344,9 +1348,9 @@ export function renderPropertiesPanelTemplate(ctx: {
1344
1348
  ${cssProps.map(
1345
1349
  ([prop, val]) => html`
1346
1350
  <div
1347
- style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:11px"
1351
+ style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:var(--spectrum-font-size-50, 11px)"
1348
1352
  >
1349
- <code style="font-family:monospace;color:var(--accent)">${prop}</code>
1353
+ <code style="font-family:var(--font-mono);color:var(--accent)">${prop}</code>
1350
1354
  <span style="margin-left:auto;color:var(--fg-dim)">${String(val)}</span>
1351
1355
  </div>
1352
1356
  `,
@@ -1374,9 +1378,11 @@ export function renderPropertiesPanelTemplate(ctx: {
1374
1378
  ${parts.map(
1375
1379
  (p) => html`
1376
1380
  <div
1377
- style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:11px"
1381
+ style="display:flex;gap:6px;align-items:center;padding:2px 0;font-size:var(--spectrum-font-size-50, 11px)"
1378
1382
  >
1379
- <code style="font-family:monospace;color:var(--accent)">${p.name}</code>
1383
+ <code style="font-family:var(--font-mono);color:var(--accent)"
1384
+ >${p.name}</code
1385
+ >
1380
1386
  <span style="color:var(--fg-dim)">&lt;${p.tag}&gt;</span>
1381
1387
  </div>
1382
1388
  `,