@motion-proto/live-tokens 0.50.0 → 0.53.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/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +7 -1
- package/CHANGELOG.md +108 -0
- package/bin/adjust.mjs +16 -39
- package/bin/generate-theme.mjs +20 -6
- package/dist-plugin/adjust/index.cjs +781 -21
- package/dist-plugin/adjust/index.d.cts +2 -1
- package/dist-plugin/adjust/index.d.ts +2 -1
- package/dist-plugin/adjust/index.js +43 -21
- package/dist-plugin/{chunk-6OZFXIQI.js → chunk-232GZGQU.js} +207 -12
- package/dist-plugin/chunk-NE6N66EE.js +753 -0
- package/dist-plugin/{chunk-76TFDTJO.js → chunk-PB2JTK2H.js} +179 -128
- package/dist-plugin/{chunk-44RSTAII.js → chunk-TKZBVIW5.js} +33 -5
- package/dist-plugin/chunk-YR6GPXW2.js +129 -0
- package/dist-plugin/generateColorsAndType/index.cjs +135 -63
- package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
- package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
- package/dist-plugin/generateColorsAndType/index.js +28 -784
- package/dist-plugin/{themeTypes-DSwKq-bj.d.ts → index-DpTIRZ2H.d.cts} +67 -26
- package/dist-plugin/{themeTypes-DSwKq-bj.d.cts → index-DpTIRZ2H.d.ts} +67 -26
- package/dist-plugin/index.cjs +1254 -140
- package/dist-plugin/index.js +106 -82
- package/dist-plugin/migrateData/index.cjs +956 -14
- package/dist-plugin/migrateData/index.js +10 -3
- package/dist-plugin/tokensCssMigrations/index.cjs +71 -5
- package/dist-plugin/tokensCssMigrations/index.d.cts +20 -9
- package/dist-plugin/tokensCssMigrations/index.d.ts +20 -9
- package/dist-plugin/tokensCssMigrations/index.js +4 -1
- package/package.json +8 -4
- package/src/editor/component-editor/NotificationEditor.svelte +6 -0
- package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
- package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
- package/src/editor/core/components/adjustAliases.ts +49 -22
- package/src/editor/core/components/aliasKinds.ts +7 -2
- package/src/editor/core/components/componentConfigService.ts +1 -1
- package/src/editor/core/palettes/oklch.ts +3 -137
- package/src/editor/core/palettes/paletteDerivation.ts +79 -37
- package/src/editor/core/store/editorStore.ts +7 -197
- package/src/editor/core/store/editorTypes.ts +9 -30
- package/src/editor/core/themes/generateColorsAndType.ts +18 -8
- package/src/editor/core/themes/lookSummary.ts +3 -25
- package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
- package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
- package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
- package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
- package/src/editor/core/themes/parsers/gradient.ts +211 -0
- package/src/editor/core/themes/slices/gradients.ts +5 -58
- package/src/editor/core/themes/themeService.ts +22 -9
- package/src/editor/core/themes/themeTypes.ts +89 -27
- package/src/editor/docs/content/editing-tokens.md +6 -2
- package/src/editor/docs/content/themes-workflow.md +9 -11
- package/src/editor/docs/content/where-themes-live.md +2 -2
- package/src/editor/docs/content.generated.ts +3 -3
- package/src/editor/index.ts +1 -0
- package/src/editor/styles/ui-editor.css +5 -2
- package/src/editor/ui/BezierCurveEditor.svelte +90 -67
- package/src/editor/ui/PaletteEditor.svelte +180 -59
- package/src/editor/ui/ThemePanel.svelte +24 -15
- package/src/editor/ui/Toggle.svelte +36 -24
- package/src/editor/ui/UIPillButton.svelte +1 -1
- package/src/editor/ui/UIReveal.svelte +47 -0
- package/src/editor/ui/UISquareButton.svelte +1 -1
- package/src/editor/ui/colors/ColorStory.svelte +12 -8
- package/src/editor/ui/colors/ColorsTab.svelte +4 -5
- package/src/editor/ui/curveEngine.ts +101 -21
- package/src/editor/ui/palette/OverridesPanel.svelte +23 -8
- package/src/editor/ui/palette/PaletteBase.svelte +17 -78
- package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
- package/src/editor/ui/palette/ScaleCurveEditor.svelte +110 -20
- package/src/editor/ui/palette/curveSummary.ts +20 -0
- package/src/editor/ui/palette/dockMagnify.ts +5 -13
- package/src/editor/ui/palette/paletteMath.ts +7 -4
- package/src/live-tokens/data/colors-and-type/autumn.json +528 -528
- package/src/live-tokens/data/colors-and-type/default.json +247 -247
- package/src/live-tokens/data/colors-and-type/halloween.json +532 -532
- package/src/live-tokens/data/colors-and-type/midnight-study.json +538 -538
- package/src/live-tokens/data/colors-and-type/ocean.json +521 -521
- package/src/live-tokens/data/colors-and-type/royal-velvet.json +532 -532
- package/src/live-tokens/data/colors-and-type/spring-meadow.json +504 -504
- package/src/live-tokens/data/colors-and-type/sunset.json +531 -531
- package/src/live-tokens/data/themes/autumn.json +604 -568
- package/src/live-tokens/data/themes/halloween.json +584 -583
- package/src/live-tokens/data/themes/midnight-study.json +673 -605
- package/src/live-tokens/data/themes/ocean.json +597 -561
- package/src/live-tokens/data/themes/royal-velvet.json +574 -573
- package/src/live-tokens/data/themes/spring-meadow.json +590 -556
- package/src/live-tokens/data/themes/sunset.json +632 -571
- package/src/live-tokens/data/tokens.generated.css +282 -282
- package/src/system/components/FloatingTokenTags.css +0 -1
- package/src/system/internal/backgroundContrast.ts +7 -0
- package/src/system/internal/oklch.ts +189 -0
- package/src/system/styles/tokens.css +283 -283
package/dist-plugin/index.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
formatGradientValue,
|
|
3
3
|
palettesToVars,
|
|
4
|
+
parseColorOpacity,
|
|
5
|
+
parseGradientValue,
|
|
4
6
|
reconcilePalettesFromCssVars,
|
|
5
7
|
sanitizeFileName
|
|
6
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PB2JTK2H.js";
|
|
7
9
|
import {
|
|
8
10
|
THEME_SCHEMA_VERSION,
|
|
9
11
|
detectLegacyLayout,
|
|
@@ -11,14 +13,20 @@ import {
|
|
|
11
13
|
nextAvailableName,
|
|
12
14
|
normalizeTheme,
|
|
13
15
|
versionedFileResourceServer
|
|
14
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-232GZGQU.js";
|
|
17
|
+
import {
|
|
18
|
+
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
19
|
+
} from "./chunk-NE6N66EE.js";
|
|
15
20
|
import {
|
|
16
21
|
TOKENS_CSS_MIGRATIONS,
|
|
17
22
|
extractGlobalRootBody,
|
|
18
23
|
runAdditiveTokensCssMigrations,
|
|
19
24
|
runTokensCssMigrations,
|
|
20
25
|
validateTokensCss
|
|
21
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-TKZBVIW5.js";
|
|
27
|
+
import {
|
|
28
|
+
hexToOklch
|
|
29
|
+
} from "./chunk-YR6GPXW2.js";
|
|
22
30
|
import {
|
|
23
31
|
resolveDataDirs
|
|
24
32
|
} from "./chunk-D3ZVKOR4.js";
|
|
@@ -27,14 +35,6 @@ import {
|
|
|
27
35
|
import fs from "fs";
|
|
28
36
|
import path from "path";
|
|
29
37
|
|
|
30
|
-
// src/editor/core/themes/parsers/colorOpacity.ts
|
|
31
|
-
var COLOR_OPACITY_RE = /^color-mix\(in srgb,\s*var\((--[a-z0-9-]+)\)\s+(\d+)%,\s*transparent\)$/i;
|
|
32
|
-
function parseColorOpacity(value) {
|
|
33
|
-
const m = value.trim().match(COLOR_OPACITY_RE);
|
|
34
|
-
if (!m) return null;
|
|
35
|
-
return { name: m[1], opacity: parseInt(m[2], 10) };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
38
|
// src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
|
|
39
39
|
var round = (v, dp) => {
|
|
40
40
|
const f = 10 ** dp;
|
|
@@ -104,8 +104,29 @@ var PKG_VERSION = (() => {
|
|
|
104
104
|
})();
|
|
105
105
|
var BUNDLE_KIND_BY_VERSION = {
|
|
106
106
|
1: "manifest-bundle",
|
|
107
|
+
3: "theme-bundle",
|
|
107
108
|
[THEME_SCHEMA_VERSION]: "theme-bundle"
|
|
108
109
|
};
|
|
110
|
+
function extractAliasDeclarations(body) {
|
|
111
|
+
const aliases = {};
|
|
112
|
+
const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
|
|
113
|
+
let m;
|
|
114
|
+
while ((m = re.exec(body)) !== null) {
|
|
115
|
+
const value = m[2].trim();
|
|
116
|
+
const plain = value.match(/^var\((--[a-z0-9-]+)\)$/i);
|
|
117
|
+
if (plain) {
|
|
118
|
+
aliases[m[1]] = plain[1];
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (parseColorOpacity(value)) {
|
|
122
|
+
aliases[m[1]] = value;
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
const gradient = parseGradientValue(value);
|
|
126
|
+
if (gradient) aliases[m[1]] = { kind: "gradient", value: gradient };
|
|
127
|
+
}
|
|
128
|
+
return aliases;
|
|
129
|
+
}
|
|
109
130
|
function themeFileApi(opts) {
|
|
110
131
|
const dataDirs = resolveDataDirs({
|
|
111
132
|
dataDir: opts.dataDir,
|
|
@@ -282,6 +303,7 @@ function themeFileApi(opts) {
|
|
|
282
303
|
const overrides = [];
|
|
283
304
|
const defaultAliases = defaultCfg.aliases ?? {};
|
|
284
305
|
for (const [varName, semanticValue] of Object.entries(prodCfg.aliases ?? {})) {
|
|
306
|
+
if (!(varName in defaultAliases)) continue;
|
|
285
307
|
if (!aliasValuesEqual(defaultAliases[varName], semanticValue)) {
|
|
286
308
|
overrides.push([varName, semanticValue]);
|
|
287
309
|
}
|
|
@@ -344,18 +366,6 @@ function themeFileApi(opts) {
|
|
|
344
366
|
fs.writeFileSync(FONTS_CSS_PATH, content);
|
|
345
367
|
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path.basename(FONTS_CSS_PATH)}`);
|
|
346
368
|
}
|
|
347
|
-
function extractAliasDeclarations(body) {
|
|
348
|
-
const aliases = {};
|
|
349
|
-
const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
|
|
350
|
-
let m;
|
|
351
|
-
while ((m = re.exec(body)) !== null) {
|
|
352
|
-
const value = m[2].trim();
|
|
353
|
-
const plain = value.match(/^var\((--[a-z0-9-]+)\)$/i);
|
|
354
|
-
if (plain) aliases[m[1]] = plain[1];
|
|
355
|
-
else if (parseColorOpacity(value)) aliases[m[1]] = value;
|
|
356
|
-
}
|
|
357
|
-
return aliases;
|
|
358
|
-
}
|
|
359
369
|
function componentNameFromFile(filePath) {
|
|
360
370
|
return path.basename(filePath, ".svelte").toLowerCase();
|
|
361
371
|
}
|
|
@@ -431,17 +441,10 @@ ${lines.join("\n")}
|
|
|
431
441
|
r.ensureDir();
|
|
432
442
|
const defaultPath = r.filePath("default");
|
|
433
443
|
const sourceStat = fs.statSync(sourcePath);
|
|
434
|
-
|
|
435
|
-
const defaultStat = fs.statSync(defaultPath);
|
|
436
|
-
if (defaultStat.mtimeMs >= sourceStat.mtimeMs) return;
|
|
437
|
-
}
|
|
438
|
-
const source = fs.readFileSync(sourcePath, "utf-8");
|
|
439
|
-
const body = extractGlobalRootBody(source);
|
|
440
|
-
const aliases = extractAliasDeclarations(body);
|
|
441
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
442
|
-
let createdAt = now;
|
|
444
|
+
let createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
443
445
|
let existingUpdatedAt;
|
|
444
446
|
let existingAliases;
|
|
447
|
+
let existingSchemaVersion;
|
|
445
448
|
if (fs.existsSync(defaultPath)) {
|
|
446
449
|
try {
|
|
447
450
|
const existing = JSON.parse(fs.readFileSync(defaultPath, "utf-8"));
|
|
@@ -450,25 +453,29 @@ ${lines.join("\n")}
|
|
|
450
453
|
if (existing.aliases && typeof existing.aliases === "object") {
|
|
451
454
|
existingAliases = existing.aliases;
|
|
452
455
|
}
|
|
456
|
+
if (typeof existing.schemaVersion === "number") existingSchemaVersion = existing.schemaVersion;
|
|
453
457
|
} catch {
|
|
454
458
|
}
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
if (val !== null && typeof val === "object" && !(token in aliases)) {
|
|
459
|
-
aliases[token] = val;
|
|
460
|
-
}
|
|
459
|
+
const defaultStat = fs.statSync(defaultPath);
|
|
460
|
+
if (defaultStat.mtimeMs >= sourceStat.mtimeMs && existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION) {
|
|
461
|
+
return;
|
|
461
462
|
}
|
|
462
463
|
}
|
|
464
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
465
|
+
const source = fs.readFileSync(sourcePath, "utf-8");
|
|
466
|
+
const body = extractGlobalRootBody(source);
|
|
467
|
+
const aliases = extractAliasDeclarations(body);
|
|
463
468
|
const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
|
|
469
|
+
const schemaStampCurrent = existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION;
|
|
464
470
|
const defaultConfig = {
|
|
465
471
|
name: "default",
|
|
466
472
|
component: comp,
|
|
467
473
|
createdAt,
|
|
468
474
|
updatedAt: aliasesUnchanged && existingUpdatedAt ? existingUpdatedAt : now,
|
|
469
|
-
aliases
|
|
475
|
+
aliases,
|
|
476
|
+
schemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
|
|
470
477
|
};
|
|
471
|
-
if (aliasesUnchanged && existingUpdatedAt) {
|
|
478
|
+
if (aliasesUnchanged && schemaStampCurrent && existingUpdatedAt) {
|
|
472
479
|
const t = /* @__PURE__ */ new Date();
|
|
473
480
|
fs.utimesSync(defaultPath, t, t);
|
|
474
481
|
return;
|
|
@@ -516,6 +523,7 @@ ${lines.join("\n")}
|
|
|
516
523
|
const diskThemeResolvers = {
|
|
517
524
|
readColorsAndType: (name) => colorsAndTypeResource.readJson(sanitizeFileName(name)),
|
|
518
525
|
readComponentConfig: (comp, name) => readComponentConfig(comp, sanitizeFileName(name)),
|
|
526
|
+
listComponentNames,
|
|
519
527
|
normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
|
|
520
528
|
};
|
|
521
529
|
function readTheme(fileName) {
|
|
@@ -541,7 +549,7 @@ ${lines.join("\n")}
|
|
|
541
549
|
return "corrupt";
|
|
542
550
|
}
|
|
543
551
|
function notAThemeError(fileName) {
|
|
544
|
-
return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${path.basename(COLORS_AND_TYPE_DIR)}/ or delete it.`;
|
|
552
|
+
return `"${fileName}" is a colors-and-type file, not a theme. Before 0.48 those lived in themes/; move it to ${path.basename(COLORS_AND_TYPE_DIR)}/ or delete it. To build a theme, use the theme generator skill "generate-theme".`;
|
|
545
553
|
}
|
|
546
554
|
function respondUnreadableTheme(res, fileName, missingError) {
|
|
547
555
|
switch (unreadableThemeReason(fileName)) {
|
|
@@ -603,19 +611,26 @@ ${lines.join("\n")}
|
|
|
603
611
|
const componentConfigs = {};
|
|
604
612
|
for (const comp of listComponentNames()) {
|
|
605
613
|
const cfg = readComponentConfig(comp, "default");
|
|
606
|
-
if (cfg)
|
|
614
|
+
if (cfg) {
|
|
615
|
+
const { schemaVersion: _standaloneStamp, ...rest } = cfg;
|
|
616
|
+
componentConfigs[comp] = rest;
|
|
617
|
+
}
|
|
607
618
|
}
|
|
608
|
-
|
|
619
|
+
const contentUnchanged = JSON.stringify(existing?.colorsAndType) === JSON.stringify(colorsAndType) && JSON.stringify(existing?.componentConfigs) === JSON.stringify(componentConfigs);
|
|
620
|
+
if (existing?.schemaVersion === THEME_SCHEMA_VERSION && existing?.componentSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION && contentUnchanged) {
|
|
609
621
|
return;
|
|
610
622
|
}
|
|
611
623
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
612
624
|
writeTheme("default", {
|
|
613
625
|
name: "Motion Proto",
|
|
614
626
|
createdAt: typeof existing?.createdAt === "string" ? existing.createdAt : now,
|
|
615
|
-
|
|
627
|
+
// A stamp-only rewrite (componentSchemaVersion catching up) is not a
|
|
628
|
+
// content change, so it doesn't bump this the way a real edit would.
|
|
629
|
+
updatedAt: contentUnchanged && typeof existing?.updatedAt === "string" ? existing.updatedAt : now,
|
|
616
630
|
schemaVersion: THEME_SCHEMA_VERSION,
|
|
617
631
|
colorsAndType,
|
|
618
|
-
componentConfigs
|
|
632
|
+
componentConfigs,
|
|
633
|
+
componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
|
|
619
634
|
});
|
|
620
635
|
}
|
|
621
636
|
function ensureThemesDir(warn) {
|
|
@@ -647,14 +662,24 @@ ${lines.join("\n")}
|
|
|
647
662
|
if (!shipped) return null;
|
|
648
663
|
return { data: normalizeColorsAndType(shipped), source: "default" };
|
|
649
664
|
}
|
|
665
|
+
const loggedMissingThemeEntries = /* @__PURE__ */ new Set();
|
|
666
|
+
function resolveComponentConfigOrDefault(comp, theme) {
|
|
667
|
+
const embedded = theme?.componentConfigs[comp];
|
|
668
|
+
if (embedded) return { ...embedded, component: comp };
|
|
669
|
+
if (!loggedMissingThemeEntries.has(comp)) {
|
|
670
|
+
loggedMissingThemeEntries.add(comp);
|
|
671
|
+
console.warn(
|
|
672
|
+
`[live-tokens] "${comp}" has no entry in the open theme; resolving from its shipped default.`
|
|
673
|
+
);
|
|
674
|
+
}
|
|
675
|
+
return readComponentConfig(comp, "default");
|
|
676
|
+
}
|
|
650
677
|
function resolveLiveComponentConfig(comp, theme) {
|
|
651
678
|
const working = componentResource(comp).readWorking();
|
|
652
679
|
if (working !== null) return { data: working, source: "working" };
|
|
653
|
-
const
|
|
654
|
-
if (embedded) return { data: { ...embedded, component: comp }, source: "theme" };
|
|
655
|
-
const cfg = readComponentConfig(comp, "default");
|
|
680
|
+
const cfg = resolveComponentConfigOrDefault(comp, theme);
|
|
656
681
|
if (!cfg) return null;
|
|
657
|
-
return { data: cfg, source: "
|
|
682
|
+
return { data: cfg, source: "theme" };
|
|
658
683
|
}
|
|
659
684
|
function resolveSavedColorsAndType(theme) {
|
|
660
685
|
if (theme?.colorsAndType) return theme.colorsAndType;
|
|
@@ -662,9 +687,7 @@ ${lines.join("\n")}
|
|
|
662
687
|
return shipped ? normalizeColorsAndType(shipped) : null;
|
|
663
688
|
}
|
|
664
689
|
function resolveSavedComponentConfig(comp, theme) {
|
|
665
|
-
|
|
666
|
-
if (embedded) return { ...embedded, component: comp };
|
|
667
|
-
return readComponentConfig(comp, "default");
|
|
690
|
+
return resolveComponentConfigOrDefault(comp, theme);
|
|
668
691
|
}
|
|
669
692
|
function sameJsonValue(a, b) {
|
|
670
693
|
if (a === null || b === null) return a === b;
|
|
@@ -700,26 +723,9 @@ ${lines.join("\n")}
|
|
|
700
723
|
if (entry.isDirectory()) componentResource(entry.name).clearWorking();
|
|
701
724
|
}
|
|
702
725
|
}
|
|
703
|
-
function formatAliasGradient(v) {
|
|
704
|
-
const stopColor = (s) => {
|
|
705
|
-
const base = s.color.startsWith("--") ? `var(${s.color})` : s.color;
|
|
706
|
-
const opacity = s.opacity ?? 100;
|
|
707
|
-
return opacity >= 100 ? base : `color-mix(in srgb, ${base} ${opacity}%, transparent)`;
|
|
708
|
-
};
|
|
709
|
-
if (v.type === "none") return "transparent";
|
|
710
|
-
if (v.type === "solid") {
|
|
711
|
-
const first = v.stops[0];
|
|
712
|
-
if (!first) return "transparent";
|
|
713
|
-
return stopColor(first);
|
|
714
|
-
}
|
|
715
|
-
const stops = v.stops.map((s) => `${stopColor(s)} ${s.position}%`).join(", ");
|
|
716
|
-
if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
|
|
717
|
-
const radial = v.radius && v.radius > 0 ? `circle ${v.radius}px at center` : "circle";
|
|
718
|
-
return `radial-gradient(${radial}, ${stops})`;
|
|
719
|
-
}
|
|
720
726
|
function aliasValueToCss(v) {
|
|
721
727
|
if (typeof v === "string") return v.startsWith("--") ? `var(${v})` : v;
|
|
722
|
-
return
|
|
728
|
+
return formatGradientValue(v.value);
|
|
723
729
|
}
|
|
724
730
|
function aliasValuesEqual(a, b) {
|
|
725
731
|
if (a === void 0 || b === void 0) return a === b;
|
|
@@ -869,13 +875,9 @@ ${lines.join("\n")}
|
|
|
869
875
|
}
|
|
870
876
|
}
|
|
871
877
|
async function handleListComponents({ res }) {
|
|
872
|
-
const theme = activeTheme();
|
|
873
878
|
const components = listComponentNames().map((comp) => ({
|
|
874
879
|
name: comp,
|
|
875
|
-
|
|
876
|
-
// down, and the door only has to say where each component's live config
|
|
877
|
-
// comes from.
|
|
878
|
-
source: componentResource(comp).hasWorking() ? "working" : theme?.componentConfigs[comp] ? "theme" : "default"
|
|
880
|
+
source: componentResource(comp).hasWorking() ? "working" : "theme"
|
|
879
881
|
}));
|
|
880
882
|
jsonResponse(res, 200, { components });
|
|
881
883
|
}
|
|
@@ -886,6 +888,7 @@ ${lines.join("\n")}
|
|
|
886
888
|
}
|
|
887
889
|
async function handleGetComponentActive({ params, res }) {
|
|
888
890
|
const [comp] = params;
|
|
891
|
+
if (rejectUnknownComponent(res, comp)) return;
|
|
889
892
|
const resolved = resolveLiveComponentConfig(comp, activeTheme());
|
|
890
893
|
if (!resolved) {
|
|
891
894
|
jsonResponse(res, 404, { error: "Active config not found" });
|
|
@@ -1132,9 +1135,11 @@ ${lines.join("\n")}
|
|
|
1132
1135
|
respondUnreadableTheme(res, fileName, "Theme not found");
|
|
1133
1136
|
return;
|
|
1134
1137
|
}
|
|
1135
|
-
const { theme } = read;
|
|
1138
|
+
const { theme, filled } = read;
|
|
1136
1139
|
if (!theme.colorsAndType) {
|
|
1137
|
-
jsonResponse(res, 422, {
|
|
1140
|
+
jsonResponse(res, 422, {
|
|
1141
|
+
error: 'This theme carries no colors and type. Build one with the theme generator skill "generate-theme".'
|
|
1142
|
+
});
|
|
1138
1143
|
return;
|
|
1139
1144
|
}
|
|
1140
1145
|
clearAllWorking();
|
|
@@ -1158,7 +1163,8 @@ ${lines.join("\n")}
|
|
|
1158
1163
|
_source: resolvedColorsAndType.source
|
|
1159
1164
|
},
|
|
1160
1165
|
componentConfigs: resolvedConfigs,
|
|
1161
|
-
skippedComponents
|
|
1166
|
+
skippedComponents,
|
|
1167
|
+
filled
|
|
1162
1168
|
});
|
|
1163
1169
|
}
|
|
1164
1170
|
async function handleExportTheme({ params, res }) {
|
|
@@ -1213,10 +1219,27 @@ ${lines.join("\n")}
|
|
|
1213
1219
|
}
|
|
1214
1220
|
const bundleResolvers = {
|
|
1215
1221
|
readColorsAndType: () => bundle.theme,
|
|
1216
|
-
|
|
1222
|
+
// A named ref resolves inside the bundle only — a v1 bundle's
|
|
1223
|
+
// `${comp}/${name}` map is itself the sender's data, and a stale name
|
|
1224
|
+
// must still land in `dropped` rather than quietly picking up this
|
|
1225
|
+
// install's copy. But `'default'` never named a bundled entry; v1
|
|
1226
|
+
// spelled "no override" that way (normalizeTheme.ts's pointer-
|
|
1227
|
+
// resolution loop treats it identically), so it always meant *this
|
|
1228
|
+
// install's* default, not something the bundle carries. The
|
|
1229
|
+
// completeness fill (Wave 2 of docs/plans/theme-completeness.md) relies
|
|
1230
|
+
// on that fall-through: without it, `readComponentConfig(comp,
|
|
1231
|
+
// 'default')` answers undefined for every component (no bundle ever
|
|
1232
|
+
// carries a `<comp>/default` entry), the fill loop no-ops 25 times, and
|
|
1233
|
+
// an incomplete bundle stays incomplete forever — exactly the drift RJC
|
|
1234
|
+
// 10 exists to prevent.
|
|
1235
|
+
readComponentConfig: (comp, name) => bundle.componentConfigs?.[`${comp}/${name}`] ?? (name === "default" ? readComponentConfig(comp, "default") : void 0),
|
|
1236
|
+
// Installed components are a property of this install, not of the
|
|
1237
|
+
// bundle being imported, so the completeness fill (Wave 2) uses the
|
|
1238
|
+
// same list the disk resolver does.
|
|
1239
|
+
listComponentNames,
|
|
1217
1240
|
normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
|
|
1218
1241
|
};
|
|
1219
|
-
const { theme, dropped } = normalizeTheme(bundle.manifest, bundleResolvers);
|
|
1242
|
+
const { theme, dropped, filled } = normalizeTheme(bundle.manifest, bundleResolvers);
|
|
1220
1243
|
if (theme.colorsAndType) theme.colorsAndType = normalizeColorsAndType(theme.colorsAndType);
|
|
1221
1244
|
if (!theme.colorsAndType) {
|
|
1222
1245
|
jsonResponse(res, 400, { error: "Bundle carries no theme" });
|
|
@@ -1237,7 +1260,8 @@ ${lines.join("\n")}
|
|
|
1237
1260
|
ok: true,
|
|
1238
1261
|
theme: finalName,
|
|
1239
1262
|
renames,
|
|
1240
|
-
dropped
|
|
1263
|
+
dropped,
|
|
1264
|
+
filled
|
|
1241
1265
|
});
|
|
1242
1266
|
}
|
|
1243
1267
|
async function handleAdoptLook({ res }) {
|