@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.
Files changed (91) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +7 -1
  2. package/CHANGELOG.md +108 -0
  3. package/bin/adjust.mjs +16 -39
  4. package/bin/generate-theme.mjs +20 -6
  5. package/dist-plugin/adjust/index.cjs +781 -21
  6. package/dist-plugin/adjust/index.d.cts +2 -1
  7. package/dist-plugin/adjust/index.d.ts +2 -1
  8. package/dist-plugin/adjust/index.js +43 -21
  9. package/dist-plugin/{chunk-6OZFXIQI.js → chunk-232GZGQU.js} +207 -12
  10. package/dist-plugin/chunk-NE6N66EE.js +753 -0
  11. package/dist-plugin/{chunk-76TFDTJO.js → chunk-PB2JTK2H.js} +179 -128
  12. package/dist-plugin/{chunk-44RSTAII.js → chunk-TKZBVIW5.js} +33 -5
  13. package/dist-plugin/chunk-YR6GPXW2.js +129 -0
  14. package/dist-plugin/generateColorsAndType/index.cjs +135 -63
  15. package/dist-plugin/generateColorsAndType/index.d.cts +2 -1
  16. package/dist-plugin/generateColorsAndType/index.d.ts +2 -1
  17. package/dist-plugin/generateColorsAndType/index.js +28 -784
  18. package/dist-plugin/{themeTypes-DSwKq-bj.d.ts → index-DpTIRZ2H.d.cts} +67 -26
  19. package/dist-plugin/{themeTypes-DSwKq-bj.d.cts → index-DpTIRZ2H.d.ts} +67 -26
  20. package/dist-plugin/index.cjs +1254 -140
  21. package/dist-plugin/index.js +106 -82
  22. package/dist-plugin/migrateData/index.cjs +956 -14
  23. package/dist-plugin/migrateData/index.js +10 -3
  24. package/dist-plugin/tokensCssMigrations/index.cjs +71 -5
  25. package/dist-plugin/tokensCssMigrations/index.d.cts +20 -9
  26. package/dist-plugin/tokensCssMigrations/index.d.ts +20 -9
  27. package/dist-plugin/tokensCssMigrations/index.js +4 -1
  28. package/package.json +8 -4
  29. package/src/editor/component-editor/NotificationEditor.svelte +6 -0
  30. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
  31. package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
  32. package/src/editor/core/components/adjustAliases.ts +49 -22
  33. package/src/editor/core/components/aliasKinds.ts +7 -2
  34. package/src/editor/core/components/componentConfigService.ts +1 -1
  35. package/src/editor/core/palettes/oklch.ts +3 -137
  36. package/src/editor/core/palettes/paletteDerivation.ts +79 -37
  37. package/src/editor/core/store/editorStore.ts +7 -197
  38. package/src/editor/core/store/editorTypes.ts +9 -30
  39. package/src/editor/core/themes/generateColorsAndType.ts +18 -8
  40. package/src/editor/core/themes/lookSummary.ts +3 -25
  41. package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
  42. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
  43. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
  44. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
  45. package/src/editor/core/themes/parsers/gradient.ts +211 -0
  46. package/src/editor/core/themes/slices/gradients.ts +5 -58
  47. package/src/editor/core/themes/themeService.ts +22 -9
  48. package/src/editor/core/themes/themeTypes.ts +89 -27
  49. package/src/editor/docs/content/editing-tokens.md +6 -2
  50. package/src/editor/docs/content/themes-workflow.md +9 -11
  51. package/src/editor/docs/content/where-themes-live.md +2 -2
  52. package/src/editor/docs/content.generated.ts +3 -3
  53. package/src/editor/index.ts +1 -0
  54. package/src/editor/styles/ui-editor.css +5 -2
  55. package/src/editor/ui/BezierCurveEditor.svelte +90 -67
  56. package/src/editor/ui/PaletteEditor.svelte +180 -59
  57. package/src/editor/ui/ThemePanel.svelte +24 -15
  58. package/src/editor/ui/Toggle.svelte +36 -24
  59. package/src/editor/ui/UIPillButton.svelte +1 -1
  60. package/src/editor/ui/UIReveal.svelte +47 -0
  61. package/src/editor/ui/UISquareButton.svelte +1 -1
  62. package/src/editor/ui/colors/ColorStory.svelte +12 -8
  63. package/src/editor/ui/colors/ColorsTab.svelte +4 -5
  64. package/src/editor/ui/curveEngine.ts +101 -21
  65. package/src/editor/ui/palette/OverridesPanel.svelte +23 -8
  66. package/src/editor/ui/palette/PaletteBase.svelte +17 -78
  67. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
  68. package/src/editor/ui/palette/ScaleCurveEditor.svelte +110 -20
  69. package/src/editor/ui/palette/curveSummary.ts +20 -0
  70. package/src/editor/ui/palette/dockMagnify.ts +5 -13
  71. package/src/editor/ui/palette/paletteMath.ts +7 -4
  72. package/src/live-tokens/data/colors-and-type/autumn.json +528 -528
  73. package/src/live-tokens/data/colors-and-type/default.json +247 -247
  74. package/src/live-tokens/data/colors-and-type/halloween.json +532 -532
  75. package/src/live-tokens/data/colors-and-type/midnight-study.json +538 -538
  76. package/src/live-tokens/data/colors-and-type/ocean.json +521 -521
  77. package/src/live-tokens/data/colors-and-type/royal-velvet.json +532 -532
  78. package/src/live-tokens/data/colors-and-type/spring-meadow.json +504 -504
  79. package/src/live-tokens/data/colors-and-type/sunset.json +531 -531
  80. package/src/live-tokens/data/themes/autumn.json +604 -568
  81. package/src/live-tokens/data/themes/halloween.json +584 -583
  82. package/src/live-tokens/data/themes/midnight-study.json +673 -605
  83. package/src/live-tokens/data/themes/ocean.json +597 -561
  84. package/src/live-tokens/data/themes/royal-velvet.json +574 -573
  85. package/src/live-tokens/data/themes/spring-meadow.json +590 -556
  86. package/src/live-tokens/data/themes/sunset.json +632 -571
  87. package/src/live-tokens/data/tokens.generated.css +282 -282
  88. package/src/system/components/FloatingTokenTags.css +0 -1
  89. package/src/system/internal/backgroundContrast.ts +7 -0
  90. package/src/system/internal/oklch.ts +189 -0
  91. package/src/system/styles/tokens.css +283 -283
@@ -1,9 +1,11 @@
1
1
  import {
2
- hexToOklch,
2
+ formatGradientValue,
3
3
  palettesToVars,
4
+ parseColorOpacity,
5
+ parseGradientValue,
4
6
  reconcilePalettesFromCssVars,
5
7
  sanitizeFileName
6
- } from "./chunk-76TFDTJO.js";
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-6OZFXIQI.js";
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-44RSTAII.js";
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
- if (fs.existsSync(defaultPath)) {
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
- if (existingAliases) {
457
- for (const [token, val] of Object.entries(existingAliases)) {
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) componentConfigs[comp] = cfg;
614
+ if (cfg) {
615
+ const { schemaVersion: _standaloneStamp, ...rest } = cfg;
616
+ componentConfigs[comp] = rest;
617
+ }
607
618
  }
608
- if (existing?.schemaVersion === THEME_SCHEMA_VERSION && JSON.stringify(existing.colorsAndType) === JSON.stringify(colorsAndType) && JSON.stringify(existing.componentConfigs) === JSON.stringify(componentConfigs)) {
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
- updatedAt: now,
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 embedded = theme?.componentConfigs[comp];
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: "default" };
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
- const embedded = theme?.componentConfigs[comp];
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 formatAliasGradient(v.value);
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
- // Presence, not a parse: one corrupt buffer must not take the listing
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, { error: "This theme carries no colors and type" });
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
- readComponentConfig: (comp, name) => bundle.componentConfigs?.[`${comp}/${name}`],
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 }) {