@motion-proto/live-tokens 0.52.1 → 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 +63 -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-JIGIIE5P.js → chunk-PB2JTK2H.js} +123 -0
- package/dist-plugin/generateColorsAndType/index.cjs +48 -32
- package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
- package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
- package/dist-plugin/generateColorsAndType/index.js +7 -773
- package/dist-plugin/{themeTypes-Dky03HL9.d.cts → index-DpTIRZ2H.d.cts} +49 -21
- package/dist-plugin/{themeTypes-Dky03HL9.d.ts → index-DpTIRZ2H.d.ts} +49 -21
- package/dist-plugin/index.cjs +1159 -85
- package/dist-plugin/index.js +102 -80
- package/dist-plugin/migrateData/index.cjs +956 -14
- package/dist-plugin/migrateData/index.js +10 -3
- package/package.json +6 -4
- 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/store/editorStore.ts +7 -197
- package/src/editor/core/store/editorTypes.ts +9 -30
- 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 +71 -21
- 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 +2 -2
- package/src/editor/index.ts +1 -0
- package/src/editor/styles/ui-editor.css +5 -2
- package/src/editor/ui/PaletteEditor.svelte +30 -15
- package/src/editor/ui/ThemePanel.svelte +24 -15
- package/src/editor/ui/Toggle.svelte +22 -22
- 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/ColorsTab.svelte +4 -5
- package/src/editor/ui/palette/OverridesPanel.svelte +4 -3
- package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
- package/src/editor/ui/palette/ScaleCurveEditor.svelte +3 -2
- package/src/live-tokens/data/themes/autumn.json +77 -41
- package/src/live-tokens/data/themes/halloween.json +53 -52
- package/src/live-tokens/data/themes/midnight-study.json +136 -68
- package/src/live-tokens/data/themes/ocean.json +77 -41
- package/src/live-tokens/data/themes/royal-velvet.json +43 -42
- package/src/live-tokens/data/themes/spring-meadow.json +86 -52
- package/src/live-tokens/data/themes/sunset.json +101 -40
package/dist-plugin/index.js
CHANGED
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
|
+
formatGradientValue,
|
|
2
3
|
palettesToVars,
|
|
4
|
+
parseColorOpacity,
|
|
5
|
+
parseGradientValue,
|
|
3
6
|
reconcilePalettesFromCssVars,
|
|
4
7
|
sanitizeFileName
|
|
5
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-PB2JTK2H.js";
|
|
6
9
|
import {
|
|
7
10
|
THEME_SCHEMA_VERSION,
|
|
8
11
|
detectLegacyLayout,
|
|
@@ -10,7 +13,10 @@ import {
|
|
|
10
13
|
nextAvailableName,
|
|
11
14
|
normalizeTheme,
|
|
12
15
|
versionedFileResourceServer
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-232GZGQU.js";
|
|
17
|
+
import {
|
|
18
|
+
CURRENT_COMPONENT_SCHEMA_VERSION
|
|
19
|
+
} from "./chunk-NE6N66EE.js";
|
|
14
20
|
import {
|
|
15
21
|
TOKENS_CSS_MIGRATIONS,
|
|
16
22
|
extractGlobalRootBody,
|
|
@@ -29,14 +35,6 @@ import {
|
|
|
29
35
|
import fs from "fs";
|
|
30
36
|
import path from "path";
|
|
31
37
|
|
|
32
|
-
// src/editor/core/themes/parsers/colorOpacity.ts
|
|
33
|
-
var COLOR_OPACITY_RE = /^color-mix\(in srgb,\s*var\((--[a-z0-9-]+)\)\s+(\d+)%,\s*transparent\)$/i;
|
|
34
|
-
function parseColorOpacity(value) {
|
|
35
|
-
const m = value.trim().match(COLOR_OPACITY_RE);
|
|
36
|
-
if (!m) return null;
|
|
37
|
-
return { name: m[1], opacity: parseInt(m[2], 10) };
|
|
38
|
-
}
|
|
39
|
-
|
|
40
38
|
// src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
|
|
41
39
|
var round = (v, dp) => {
|
|
42
40
|
const f = 10 ** dp;
|
|
@@ -106,8 +104,29 @@ var PKG_VERSION = (() => {
|
|
|
106
104
|
})();
|
|
107
105
|
var BUNDLE_KIND_BY_VERSION = {
|
|
108
106
|
1: "manifest-bundle",
|
|
107
|
+
3: "theme-bundle",
|
|
109
108
|
[THEME_SCHEMA_VERSION]: "theme-bundle"
|
|
110
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
|
+
}
|
|
111
130
|
function themeFileApi(opts) {
|
|
112
131
|
const dataDirs = resolveDataDirs({
|
|
113
132
|
dataDir: opts.dataDir,
|
|
@@ -284,6 +303,7 @@ function themeFileApi(opts) {
|
|
|
284
303
|
const overrides = [];
|
|
285
304
|
const defaultAliases = defaultCfg.aliases ?? {};
|
|
286
305
|
for (const [varName, semanticValue] of Object.entries(prodCfg.aliases ?? {})) {
|
|
306
|
+
if (!(varName in defaultAliases)) continue;
|
|
287
307
|
if (!aliasValuesEqual(defaultAliases[varName], semanticValue)) {
|
|
288
308
|
overrides.push([varName, semanticValue]);
|
|
289
309
|
}
|
|
@@ -346,18 +366,6 @@ function themeFileApi(opts) {
|
|
|
346
366
|
fs.writeFileSync(FONTS_CSS_PATH, content);
|
|
347
367
|
console.log(`[syncFontsToCss] Wrote ${sources.length} source(s) into ${path.basename(FONTS_CSS_PATH)}`);
|
|
348
368
|
}
|
|
349
|
-
function extractAliasDeclarations(body) {
|
|
350
|
-
const aliases = {};
|
|
351
|
-
const re = /(--[a-z0-9-]+)\s*:\s*([^;]+);/gi;
|
|
352
|
-
let m;
|
|
353
|
-
while ((m = re.exec(body)) !== null) {
|
|
354
|
-
const value = m[2].trim();
|
|
355
|
-
const plain = value.match(/^var\((--[a-z0-9-]+)\)$/i);
|
|
356
|
-
if (plain) aliases[m[1]] = plain[1];
|
|
357
|
-
else if (parseColorOpacity(value)) aliases[m[1]] = value;
|
|
358
|
-
}
|
|
359
|
-
return aliases;
|
|
360
|
-
}
|
|
361
369
|
function componentNameFromFile(filePath) {
|
|
362
370
|
return path.basename(filePath, ".svelte").toLowerCase();
|
|
363
371
|
}
|
|
@@ -433,17 +441,10 @@ ${lines.join("\n")}
|
|
|
433
441
|
r.ensureDir();
|
|
434
442
|
const defaultPath = r.filePath("default");
|
|
435
443
|
const sourceStat = fs.statSync(sourcePath);
|
|
436
|
-
|
|
437
|
-
const defaultStat = fs.statSync(defaultPath);
|
|
438
|
-
if (defaultStat.mtimeMs >= sourceStat.mtimeMs) return;
|
|
439
|
-
}
|
|
440
|
-
const source = fs.readFileSync(sourcePath, "utf-8");
|
|
441
|
-
const body = extractGlobalRootBody(source);
|
|
442
|
-
const aliases = extractAliasDeclarations(body);
|
|
443
|
-
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
444
|
-
let createdAt = now;
|
|
444
|
+
let createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
445
445
|
let existingUpdatedAt;
|
|
446
446
|
let existingAliases;
|
|
447
|
+
let existingSchemaVersion;
|
|
447
448
|
if (fs.existsSync(defaultPath)) {
|
|
448
449
|
try {
|
|
449
450
|
const existing = JSON.parse(fs.readFileSync(defaultPath, "utf-8"));
|
|
@@ -452,25 +453,29 @@ ${lines.join("\n")}
|
|
|
452
453
|
if (existing.aliases && typeof existing.aliases === "object") {
|
|
453
454
|
existingAliases = existing.aliases;
|
|
454
455
|
}
|
|
456
|
+
if (typeof existing.schemaVersion === "number") existingSchemaVersion = existing.schemaVersion;
|
|
455
457
|
} catch {
|
|
456
458
|
}
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
if (val !== null && typeof val === "object" && !(token in aliases)) {
|
|
461
|
-
aliases[token] = val;
|
|
462
|
-
}
|
|
459
|
+
const defaultStat = fs.statSync(defaultPath);
|
|
460
|
+
if (defaultStat.mtimeMs >= sourceStat.mtimeMs && existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION) {
|
|
461
|
+
return;
|
|
463
462
|
}
|
|
464
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);
|
|
465
468
|
const aliasesUnchanged = existingAliases !== void 0 && JSON.stringify(existingAliases) === JSON.stringify(aliases);
|
|
469
|
+
const schemaStampCurrent = existingSchemaVersion === CURRENT_COMPONENT_SCHEMA_VERSION;
|
|
466
470
|
const defaultConfig = {
|
|
467
471
|
name: "default",
|
|
468
472
|
component: comp,
|
|
469
473
|
createdAt,
|
|
470
474
|
updatedAt: aliasesUnchanged && existingUpdatedAt ? existingUpdatedAt : now,
|
|
471
|
-
aliases
|
|
475
|
+
aliases,
|
|
476
|
+
schemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
|
|
472
477
|
};
|
|
473
|
-
if (aliasesUnchanged && existingUpdatedAt) {
|
|
478
|
+
if (aliasesUnchanged && schemaStampCurrent && existingUpdatedAt) {
|
|
474
479
|
const t = /* @__PURE__ */ new Date();
|
|
475
480
|
fs.utimesSync(defaultPath, t, t);
|
|
476
481
|
return;
|
|
@@ -518,6 +523,7 @@ ${lines.join("\n")}
|
|
|
518
523
|
const diskThemeResolvers = {
|
|
519
524
|
readColorsAndType: (name) => colorsAndTypeResource.readJson(sanitizeFileName(name)),
|
|
520
525
|
readComponentConfig: (comp, name) => readComponentConfig(comp, sanitizeFileName(name)),
|
|
526
|
+
listComponentNames,
|
|
521
527
|
normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
|
|
522
528
|
};
|
|
523
529
|
function readTheme(fileName) {
|
|
@@ -543,7 +549,7 @@ ${lines.join("\n")}
|
|
|
543
549
|
return "corrupt";
|
|
544
550
|
}
|
|
545
551
|
function notAThemeError(fileName) {
|
|
546
|
-
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".`;
|
|
547
553
|
}
|
|
548
554
|
function respondUnreadableTheme(res, fileName, missingError) {
|
|
549
555
|
switch (unreadableThemeReason(fileName)) {
|
|
@@ -605,19 +611,26 @@ ${lines.join("\n")}
|
|
|
605
611
|
const componentConfigs = {};
|
|
606
612
|
for (const comp of listComponentNames()) {
|
|
607
613
|
const cfg = readComponentConfig(comp, "default");
|
|
608
|
-
if (cfg)
|
|
614
|
+
if (cfg) {
|
|
615
|
+
const { schemaVersion: _standaloneStamp, ...rest } = cfg;
|
|
616
|
+
componentConfigs[comp] = rest;
|
|
617
|
+
}
|
|
609
618
|
}
|
|
610
|
-
|
|
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) {
|
|
611
621
|
return;
|
|
612
622
|
}
|
|
613
623
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
614
624
|
writeTheme("default", {
|
|
615
625
|
name: "Motion Proto",
|
|
616
626
|
createdAt: typeof existing?.createdAt === "string" ? existing.createdAt : now,
|
|
617
|
-
|
|
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,
|
|
618
630
|
schemaVersion: THEME_SCHEMA_VERSION,
|
|
619
631
|
colorsAndType,
|
|
620
|
-
componentConfigs
|
|
632
|
+
componentConfigs,
|
|
633
|
+
componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
|
|
621
634
|
});
|
|
622
635
|
}
|
|
623
636
|
function ensureThemesDir(warn) {
|
|
@@ -649,14 +662,24 @@ ${lines.join("\n")}
|
|
|
649
662
|
if (!shipped) return null;
|
|
650
663
|
return { data: normalizeColorsAndType(shipped), source: "default" };
|
|
651
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
|
+
}
|
|
652
677
|
function resolveLiveComponentConfig(comp, theme) {
|
|
653
678
|
const working = componentResource(comp).readWorking();
|
|
654
679
|
if (working !== null) return { data: working, source: "working" };
|
|
655
|
-
const
|
|
656
|
-
if (embedded) return { data: { ...embedded, component: comp }, source: "theme" };
|
|
657
|
-
const cfg = readComponentConfig(comp, "default");
|
|
680
|
+
const cfg = resolveComponentConfigOrDefault(comp, theme);
|
|
658
681
|
if (!cfg) return null;
|
|
659
|
-
return { data: cfg, source: "
|
|
682
|
+
return { data: cfg, source: "theme" };
|
|
660
683
|
}
|
|
661
684
|
function resolveSavedColorsAndType(theme) {
|
|
662
685
|
if (theme?.colorsAndType) return theme.colorsAndType;
|
|
@@ -664,9 +687,7 @@ ${lines.join("\n")}
|
|
|
664
687
|
return shipped ? normalizeColorsAndType(shipped) : null;
|
|
665
688
|
}
|
|
666
689
|
function resolveSavedComponentConfig(comp, theme) {
|
|
667
|
-
|
|
668
|
-
if (embedded) return { ...embedded, component: comp };
|
|
669
|
-
return readComponentConfig(comp, "default");
|
|
690
|
+
return resolveComponentConfigOrDefault(comp, theme);
|
|
670
691
|
}
|
|
671
692
|
function sameJsonValue(a, b) {
|
|
672
693
|
if (a === null || b === null) return a === b;
|
|
@@ -702,26 +723,9 @@ ${lines.join("\n")}
|
|
|
702
723
|
if (entry.isDirectory()) componentResource(entry.name).clearWorking();
|
|
703
724
|
}
|
|
704
725
|
}
|
|
705
|
-
function formatAliasGradient(v) {
|
|
706
|
-
const stopColor = (s) => {
|
|
707
|
-
const base = s.color.startsWith("--") ? `var(${s.color})` : s.color;
|
|
708
|
-
const opacity = s.opacity ?? 100;
|
|
709
|
-
return opacity >= 100 ? base : `color-mix(in srgb, ${base} ${opacity}%, transparent)`;
|
|
710
|
-
};
|
|
711
|
-
if (v.type === "none") return "transparent";
|
|
712
|
-
if (v.type === "solid") {
|
|
713
|
-
const first = v.stops[0];
|
|
714
|
-
if (!first) return "transparent";
|
|
715
|
-
return stopColor(first);
|
|
716
|
-
}
|
|
717
|
-
const stops = v.stops.map((s) => `${stopColor(s)} ${s.position}%`).join(", ");
|
|
718
|
-
if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
|
|
719
|
-
const radial = v.radius && v.radius > 0 ? `circle ${v.radius}px at center` : "circle";
|
|
720
|
-
return `radial-gradient(${radial}, ${stops})`;
|
|
721
|
-
}
|
|
722
726
|
function aliasValueToCss(v) {
|
|
723
727
|
if (typeof v === "string") return v.startsWith("--") ? `var(${v})` : v;
|
|
724
|
-
return
|
|
728
|
+
return formatGradientValue(v.value);
|
|
725
729
|
}
|
|
726
730
|
function aliasValuesEqual(a, b) {
|
|
727
731
|
if (a === void 0 || b === void 0) return a === b;
|
|
@@ -871,13 +875,9 @@ ${lines.join("\n")}
|
|
|
871
875
|
}
|
|
872
876
|
}
|
|
873
877
|
async function handleListComponents({ res }) {
|
|
874
|
-
const theme = activeTheme();
|
|
875
878
|
const components = listComponentNames().map((comp) => ({
|
|
876
879
|
name: comp,
|
|
877
|
-
|
|
878
|
-
// down, and the door only has to say where each component's live config
|
|
879
|
-
// comes from.
|
|
880
|
-
source: componentResource(comp).hasWorking() ? "working" : theme?.componentConfigs[comp] ? "theme" : "default"
|
|
880
|
+
source: componentResource(comp).hasWorking() ? "working" : "theme"
|
|
881
881
|
}));
|
|
882
882
|
jsonResponse(res, 200, { components });
|
|
883
883
|
}
|
|
@@ -888,6 +888,7 @@ ${lines.join("\n")}
|
|
|
888
888
|
}
|
|
889
889
|
async function handleGetComponentActive({ params, res }) {
|
|
890
890
|
const [comp] = params;
|
|
891
|
+
if (rejectUnknownComponent(res, comp)) return;
|
|
891
892
|
const resolved = resolveLiveComponentConfig(comp, activeTheme());
|
|
892
893
|
if (!resolved) {
|
|
893
894
|
jsonResponse(res, 404, { error: "Active config not found" });
|
|
@@ -1134,9 +1135,11 @@ ${lines.join("\n")}
|
|
|
1134
1135
|
respondUnreadableTheme(res, fileName, "Theme not found");
|
|
1135
1136
|
return;
|
|
1136
1137
|
}
|
|
1137
|
-
const { theme } = read;
|
|
1138
|
+
const { theme, filled } = read;
|
|
1138
1139
|
if (!theme.colorsAndType) {
|
|
1139
|
-
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
|
+
});
|
|
1140
1143
|
return;
|
|
1141
1144
|
}
|
|
1142
1145
|
clearAllWorking();
|
|
@@ -1160,7 +1163,8 @@ ${lines.join("\n")}
|
|
|
1160
1163
|
_source: resolvedColorsAndType.source
|
|
1161
1164
|
},
|
|
1162
1165
|
componentConfigs: resolvedConfigs,
|
|
1163
|
-
skippedComponents
|
|
1166
|
+
skippedComponents,
|
|
1167
|
+
filled
|
|
1164
1168
|
});
|
|
1165
1169
|
}
|
|
1166
1170
|
async function handleExportTheme({ params, res }) {
|
|
@@ -1215,10 +1219,27 @@ ${lines.join("\n")}
|
|
|
1215
1219
|
}
|
|
1216
1220
|
const bundleResolvers = {
|
|
1217
1221
|
readColorsAndType: () => bundle.theme,
|
|
1218
|
-
|
|
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,
|
|
1219
1240
|
normalizeColorsAndType: (colorsAndType) => normalizeColorsAndType(colorsAndType)
|
|
1220
1241
|
};
|
|
1221
|
-
const { theme, dropped } = normalizeTheme(bundle.manifest, bundleResolvers);
|
|
1242
|
+
const { theme, dropped, filled } = normalizeTheme(bundle.manifest, bundleResolvers);
|
|
1222
1243
|
if (theme.colorsAndType) theme.colorsAndType = normalizeColorsAndType(theme.colorsAndType);
|
|
1223
1244
|
if (!theme.colorsAndType) {
|
|
1224
1245
|
jsonResponse(res, 400, { error: "Bundle carries no theme" });
|
|
@@ -1239,7 +1260,8 @@ ${lines.join("\n")}
|
|
|
1239
1260
|
ok: true,
|
|
1240
1261
|
theme: finalName,
|
|
1241
1262
|
renames,
|
|
1242
|
-
dropped
|
|
1263
|
+
dropped,
|
|
1264
|
+
filled
|
|
1243
1265
|
});
|
|
1244
1266
|
}
|
|
1245
1267
|
async function handleAdoptLook({ res }) {
|