@motion-proto/live-tokens 0.52.1 → 0.54.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 (69) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +28 -3
  2. package/.claude/skills/live-tokens-generate-theme/SKILL.md +16 -1
  3. package/CHANGELOG.md +89 -0
  4. package/bin/adjust.mjs +16 -39
  5. package/bin/generate-theme.mjs +21 -6
  6. package/dist-plugin/adjust/index.cjs +785 -21
  7. package/dist-plugin/adjust/index.d.cts +2 -1
  8. package/dist-plugin/adjust/index.d.ts +2 -1
  9. package/dist-plugin/adjust/index.js +47 -21
  10. package/dist-plugin/{chunk-6OZFXIQI.js → chunk-232GZGQU.js} +207 -12
  11. package/dist-plugin/chunk-NE6N66EE.js +753 -0
  12. package/dist-plugin/{chunk-JIGIIE5P.js → chunk-PB2JTK2H.js} +123 -0
  13. package/dist-plugin/generateColorsAndType/index.cjs +110 -31
  14. package/dist-plugin/generateColorsAndType/index.d.cts +6 -2
  15. package/dist-plugin/generateColorsAndType/index.d.ts +6 -2
  16. package/dist-plugin/generateColorsAndType/index.js +69 -772
  17. package/dist-plugin/{themeTypes-Dky03HL9.d.cts → index-DpTIRZ2H.d.cts} +49 -21
  18. package/dist-plugin/{themeTypes-Dky03HL9.d.ts → index-DpTIRZ2H.d.ts} +49 -21
  19. package/dist-plugin/index.cjs +1159 -85
  20. package/dist-plugin/index.js +102 -80
  21. package/dist-plugin/migrateData/index.cjs +956 -14
  22. package/dist-plugin/migrateData/index.js +10 -3
  23. package/package.json +6 -4
  24. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +9 -23
  25. package/src/editor/component-editor/scaffolding/LinkedBlock.svelte +8 -8
  26. package/src/editor/core/components/adjustAliases.ts +65 -22
  27. package/src/editor/core/components/aliasKinds.ts +7 -2
  28. package/src/editor/core/components/componentConfigService.ts +1 -1
  29. package/src/editor/core/store/editorStore.ts +7 -197
  30. package/src/editor/core/store/editorTypes.ts +9 -30
  31. package/src/editor/core/themes/generateColorsAndType.ts +50 -1
  32. package/src/editor/core/themes/lookSummary.ts +3 -25
  33. package/src/editor/core/themes/migrateComponentConfig.ts +205 -0
  34. package/src/editor/core/themes/migrations/2026-05-19-sectiondivider-rich-gradient.ts +1 -1
  35. package/src/editor/core/themes/migrations/2026-05-20-sectiondivider-slim-variants.ts +1 -1
  36. package/src/editor/core/themes/migrations/2026-05-22-sectiondivider-intrinsics-to-css.ts +1 -1
  37. package/src/editor/core/themes/parsers/gradient.ts +211 -0
  38. package/src/editor/core/themes/parsers/shadow.ts +52 -0
  39. package/src/editor/core/themes/slices/gradients.ts +5 -58
  40. package/src/editor/core/themes/slices/shadows.ts +2 -38
  41. package/src/editor/core/themes/themeService.ts +22 -9
  42. package/src/editor/core/themes/themeTypes.ts +71 -21
  43. package/src/editor/docs/content/themes-workflow.md +9 -11
  44. package/src/editor/docs/content/where-themes-live.md +2 -2
  45. package/src/editor/docs/content.generated.ts +2 -2
  46. package/src/editor/index.ts +1 -0
  47. package/src/editor/styles/ui-editor.css +5 -2
  48. package/src/editor/ui/PaletteEditor.svelte +30 -15
  49. package/src/editor/ui/ThemePanel.svelte +24 -15
  50. package/src/editor/ui/Toggle.svelte +22 -22
  51. package/src/editor/ui/UIPillButton.svelte +1 -1
  52. package/src/editor/ui/UIReveal.svelte +47 -0
  53. package/src/editor/ui/UISquareButton.svelte +1 -1
  54. package/src/editor/ui/colors/ColorsTab.svelte +4 -5
  55. package/src/editor/ui/palette/OverridesPanel.svelte +4 -3
  56. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -3
  57. package/src/editor/ui/palette/ScaleCurveEditor.svelte +3 -2
  58. package/src/live-tokens/data/colors-and-type/autumn.json +5 -5
  59. package/src/live-tokens/data/colors-and-type/ocean.json +5 -5
  60. package/src/live-tokens/data/colors-and-type/spring-meadow.json +5 -5
  61. package/src/live-tokens/data/themes/autumn.json +82 -46
  62. package/src/live-tokens/data/themes/halloween.json +53 -52
  63. package/src/live-tokens/data/themes/midnight-study.json +151 -83
  64. package/src/live-tokens/data/themes/ocean.json +82 -46
  65. package/src/live-tokens/data/themes/royal-velvet.json +43 -42
  66. package/src/live-tokens/data/themes/spring-meadow.json +91 -57
  67. package/src/live-tokens/data/themes/sunset.json +101 -40
  68. package/src/system/components/FloatingTokenTags.css +6 -4
  69. package/src/system/internal/backgroundContrast.ts +2 -1
@@ -1,4 +1,5 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-Dky03HL9.cjs';
1
+ import { A as AliasDiskValue, b as ComponentConfig } from '../index-DpTIRZ2H.cjs';
2
+ export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-DpTIRZ2H.cjs';
2
3
  export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
3
4
 
4
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,4 +1,5 @@
1
- import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-Dky03HL9.js';
1
+ import { A as AliasDiskValue, b as ComponentConfig } from '../index-DpTIRZ2H.js';
2
+ export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-DpTIRZ2H.js';
2
3
  export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
3
4
 
4
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,3 +1,6 @@
1
+ import {
2
+ CURRENT_COMPONENT_SCHEMA_VERSION
3
+ } from "../chunk-NE6N66EE.js";
1
4
  import {
2
5
  readLiveTokensConfig,
3
6
  resolveDataDirs
@@ -31,9 +34,12 @@ var MATCHER_BY_KIND = Object.fromEntries(
31
34
  KIND_PATTERNS.map((p) => [p.kind, p.matches])
32
35
  );
33
36
  var SIDE_SUFFIXES = ["-top", "-right", "-bottom", "-left"];
34
- function matchesKind(variable, kind) {
37
+ function stripSide(variable) {
35
38
  const side = SIDE_SUFFIXES.find((s) => variable.endsWith(s));
36
- return MATCHER_BY_KIND[kind](side ? variable.slice(0, -side.length) : variable);
39
+ return side ? variable.slice(0, -side.length) : variable;
40
+ }
41
+ function matchesKind(variable, kind) {
42
+ return MATCHER_BY_KIND[kind](stripSide(variable));
37
43
  }
38
44
 
39
45
  // src/editor/core/components/adjustAliases.ts
@@ -48,7 +54,7 @@ var RADIUS_RUNGS = [
48
54
  "--radius-3xl",
49
55
  "--radius-4xl"
50
56
  ];
51
- var SPACE_RUNGS = [
57
+ var SPACE_SETTABLE = [
52
58
  "--space-0",
53
59
  "--space-2",
54
60
  "--space-4",
@@ -62,7 +68,10 @@ var SPACE_RUNGS = [
62
68
  "--space-32",
63
69
  "--space-48"
64
70
  ];
65
- var SPACE_FAMILY = [...SPACE_RUNGS, "--space-40", "--space-64", "--space-96", "--space-128"];
71
+ var SPACE_RUNGS = SPACE_SETTABLE;
72
+ var INSET_RUNGS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-4"));
73
+ var TEXT_INSET_RUNGS = SPACE_SETTABLE.slice(SPACE_SETTABLE.indexOf("--space-6"));
74
+ var SPACE_FAMILY = [...SPACE_SETTABLE, "--space-40", "--space-64", "--space-96", "--space-128"];
66
75
  var BORDER_WIDTH_RUNGS = [
67
76
  "--border-width-0",
68
77
  "--border-width-1",
@@ -79,37 +88,53 @@ var BORDER_WIDTH_RUNGS = [
79
88
  "--border-width-24"
80
89
  ];
81
90
  var LADDERS = {
82
- radius: { rungs: RADIUS_RUNGS, family: [...RADIUS_RUNGS, RADIUS_FULL] },
83
- padding: { rungs: SPACE_RUNGS, family: SPACE_FAMILY },
84
- gap: { rungs: SPACE_RUNGS, family: SPACE_FAMILY },
85
- "border-width": { rungs: BORDER_WIDTH_RUNGS, family: BORDER_WIDTH_RUNGS }
91
+ radius: {
92
+ rungs: RADIUS_RUNGS,
93
+ settable: [...RADIUS_RUNGS, RADIUS_FULL],
94
+ family: [...RADIUS_RUNGS, RADIUS_FULL]
95
+ },
96
+ padding: { rungs: SPACE_RUNGS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
97
+ gap: { rungs: SPACE_RUNGS, settable: SPACE_SETTABLE, family: SPACE_FAMILY },
98
+ "border-width": {
99
+ rungs: BORDER_WIDTH_RUNGS,
100
+ settable: BORDER_WIDTH_RUNGS,
101
+ family: BORDER_WIDTH_RUNGS
102
+ }
86
103
  };
87
104
  var TOKEN_NAME = /^--[a-z0-9-]+$/;
88
- function rungsFor(op) {
89
- return op.kind === "radius" && op.full ? [...RADIUS_RUNGS, RADIUS_FULL] : LADDERS[op.kind].rungs;
105
+ function rungsFor(op, variable, aliases) {
106
+ if (op.kind === "radius") return op.full ? [...RADIUS_RUNGS, RADIUS_FULL] : RADIUS_RUNGS;
107
+ if (op.kind !== "padding") return LADDERS[op.kind].rungs;
108
+ const base = stripSide(variable);
109
+ if (!base.endsWith("-padding")) return LADDERS.padding.rungs;
110
+ const variant = base.slice(0, -"-padding".length);
111
+ return `${variant}-text-font-size` in aliases ? TEXT_INSET_RUNGS : INSET_RUNGS;
90
112
  }
91
113
  function spaceStep(token) {
92
114
  return Number(token.slice("--space-".length));
93
115
  }
94
- function nearestSpaceRung(token, rungs) {
116
+ function snapRung(token, rungs, direction) {
95
117
  const step = spaceStep(token);
96
- let best = 0;
97
- for (let i = 1; i < rungs.length; i++) {
98
- if (Math.abs(spaceStep(rungs[i]) - step) <= Math.abs(spaceStep(rungs[best]) - step)) best = i;
118
+ if (direction > 0) return rungs.findIndex((rung) => spaceStep(rung) > step);
119
+ for (let index = rungs.length - 1; index >= 0; index--) {
120
+ if (spaceStep(rungs[index]) < step) return index;
99
121
  }
100
- return best;
122
+ return -1;
101
123
  }
102
- function resolve(op, value) {
124
+ function resolve(op, variable, value, aliases) {
103
125
  if (typeof value !== "string" || !TOKEN_NAME.test(value)) return { skip: "raw-value" };
104
126
  if (!LADDERS[op.kind].family.includes(value)) return { skip: "off-ladder" };
105
127
  if (op.set !== void 0) return { from: value, to: op.set };
106
- const rungs = rungsFor(op);
128
+ const rungs = rungsFor(op, variable, aliases);
107
129
  let index = rungs.indexOf(value);
130
+ let shift = op.shift;
108
131
  if (index < 0) {
109
132
  if (value === RADIUS_FULL) return { skip: "pill-preserved" };
110
- index = nearestSpaceRung(value, rungs);
133
+ index = snapRung(value, rungs, shift);
134
+ if (index < 0) return { skip: "clamped" };
135
+ shift -= Math.sign(shift);
111
136
  }
112
- const wanted = index + op.shift;
137
+ const wanted = index + shift;
113
138
  const landed = Math.min(Math.max(wanted, 0), rungs.length - 1);
114
139
  if (landed !== wanted && rungs[landed] === value) return { skip: "clamped" };
115
140
  return { from: value, to: rungs[landed] };
@@ -123,7 +148,7 @@ function validateOp(op, configs) {
123
148
  const hasSet = op.set !== void 0;
124
149
  const hasShift = op.shift !== void 0;
125
150
  if (hasSet === hasShift) throw new Error(`A ${op.kind} op needs exactly one of "set" or "shift"`);
126
- const settable = op.kind === "radius" ? ladder.family : ladder.rungs;
151
+ const settable = ladder.settable;
127
152
  if (hasSet && !settable.includes(op.set)) {
128
153
  throw new Error(`"${op.set}" is not on the ${op.kind} ladder`);
129
154
  }
@@ -153,7 +178,7 @@ function adjustAliases(configs, ops, now) {
153
178
  let changed = false;
154
179
  for (const [variable, value] of Object.entries(config.aliases)) {
155
180
  if (!matchesKind(variable, op.kind)) continue;
156
- const outcome = resolve(op, value);
181
+ const outcome = resolve(op, variable, value, config.aliases);
157
182
  if ("skip" in outcome) {
158
183
  reportFor(component).skips.push({ variable, value, reason: outcome.skip });
159
184
  continue;
@@ -169,6 +194,7 @@ function adjustAliases(configs, ops, now) {
169
194
  return { configs: next, report: { components: [...reports.values()] } };
170
195
  }
171
196
  export {
197
+ CURRENT_COMPONENT_SCHEMA_VERSION,
172
198
  adjustAliases,
173
199
  matchesKind,
174
200
  readLiveTokensConfig,
@@ -1,3 +1,8 @@
1
+ import {
2
+ CURRENT_COMPONENT_SCHEMA_VERSION,
3
+ runMigrations
4
+ } from "./chunk-NE6N66EE.js";
5
+
1
6
  // vite-plugin/files/versionedFileResourceServer.ts
2
7
  import fs from "fs";
3
8
  import path from "path";
@@ -129,8 +134,141 @@ function versionedFileResourceServer(opts) {
129
134
  };
130
135
  }
131
136
 
137
+ // src/editor/core/themes/themeTypes.ts
138
+ var THEME_SCHEMA_VERSION = 4;
139
+
140
+ // src/editor/core/themes/migrateComponentConfig.ts
141
+ function migrateComponentConfig(component, aliasesIn, rawConfig, fileVersion) {
142
+ const stringPart = {};
143
+ const objectPart = {};
144
+ for (const [k, v] of Object.entries(aliasesIn)) {
145
+ if (typeof v === "string") stringPart[k] = v;
146
+ else objectPart[k] = v;
147
+ }
148
+ const configOut = {};
149
+ for (const [k, v] of Object.entries(rawConfig ?? {})) {
150
+ if (typeof v === "string") stringPart[k] = v;
151
+ else configOut[k] = v;
152
+ }
153
+ if (component === "sectiondivider") {
154
+ synthesizeSectionDividerGradients(stringPart, objectPart);
155
+ }
156
+ const migratedString = runMigrations("component-config", fileVersion, stringPart, { component });
157
+ const migratedObject = component === "sectiondivider" ? renameSectionDividerObjectSlots(objectPart) : objectPart;
158
+ return {
159
+ aliases: { ...migratedString, ...migratedObject },
160
+ config: configOut
161
+ };
162
+ }
163
+ var FAMILIES = ["canvas", "neutral", "alternate", "primary", "accent", "special"];
164
+ var SIZE_VARIANTS = ["lg", "md", "sm"];
165
+ function renameSectionDividerObjectSlots(objectPart) {
166
+ const canvasSourceKeys = [
167
+ "--sectiondivider-canvas-background",
168
+ "--sectiondivider-canvas-gradient",
169
+ "--sectiondivider-color-canvas-background"
170
+ ];
171
+ let canvasGradient;
172
+ for (const key of canvasSourceKeys) {
173
+ if (objectPart[key] !== void 0) {
174
+ canvasGradient = objectPart[key];
175
+ break;
176
+ }
177
+ }
178
+ const out = {};
179
+ for (const [key, value] of Object.entries(objectPart)) {
180
+ let isFamilyGradient = false;
181
+ for (const f of FAMILIES) {
182
+ if (key === `--sectiondivider-${f}-background` || key === `--sectiondivider-${f}-gradient` || key === `--sectiondivider-color-${f}-background`) {
183
+ isFamilyGradient = true;
184
+ break;
185
+ }
186
+ }
187
+ if (isFamilyGradient) continue;
188
+ out[key] = value;
189
+ }
190
+ if (canvasGradient !== void 0) {
191
+ for (const v of SIZE_VARIANTS) {
192
+ out[`--sectiondivider-${v}-background`] = canvasGradient;
193
+ }
194
+ }
195
+ return out;
196
+ }
197
+ var SECTIONDIVIDER_VARIANTS = ["canvas", "neutral", "alternate", "primary", "accent", "special"];
198
+ function synthesizeSectionDividerGradients(stringPart, objectPart) {
199
+ const GRADIENT_ANGLE_TOKENS = {
200
+ "--gradient-angle-horizontal": 90,
201
+ "--gradient-angle-vertical": 180,
202
+ "--gradient-angle-diagonal": 135,
203
+ "--gradient-angle-counter-diagonal": 45
204
+ };
205
+ const GRADIENT_STOP_POSITIONS = {
206
+ "--gradient-stop-start": 0,
207
+ "--gradient-stop-mid": 50,
208
+ "--gradient-stop-end": 100
209
+ };
210
+ const parseAngle = (raw) => {
211
+ if (!raw) return 135;
212
+ if (raw.startsWith("--")) return GRADIENT_ANGLE_TOKENS[raw] ?? 135;
213
+ const m = raw.match(/^(-?\d+(?:\.\d+)?)\s*deg$/i);
214
+ return m ? parseFloat(m[1]) : 135;
215
+ };
216
+ const parsePosition = (raw, fallback) => {
217
+ if (!raw) return fallback;
218
+ if (raw.startsWith("--")) return GRADIENT_STOP_POSITIONS[raw] ?? fallback;
219
+ const m = raw.match(/^(-?\d+(?:\.\d+)?)\s*%$/);
220
+ return m ? parseFloat(m[1]) : fallback;
221
+ };
222
+ const parseStopColor = (raw) => {
223
+ if (!raw) return null;
224
+ if (raw.startsWith("--")) return { color: raw };
225
+ const mix = raw.match(/^color-mix\(\s*in\s+srgb\s*,\s*var\((--[a-z0-9-]+)\)\s+([\d.]+)%\s*,\s*transparent\s*\)$/i);
226
+ if (mix) return { color: mix[1], opacity: parseFloat(mix[2]) };
227
+ return null;
228
+ };
229
+ for (const v of SECTIONDIVIDER_VARIANTS) {
230
+ const slot = `--sectiondivider-${v}-gradient`;
231
+ if (slot in objectPart) continue;
232
+ const angleKey = `${slot}-angle`;
233
+ const has1 = stringPart[`${slot}-stop-1-color`] !== void 0;
234
+ const has2 = stringPart[`${slot}-stop-2-color`] !== void 0;
235
+ const has3 = stringPart[`${slot}-stop-3-color`] !== void 0;
236
+ if (!has1 && !has2 && !has3) continue;
237
+ const stops = [];
238
+ const positionDefaults = [0, 50, 100];
239
+ for (let i = 1; i <= 3; i++) {
240
+ const c = parseStopColor(stringPart[`${slot}-stop-${i}-color`]);
241
+ if (!c) continue;
242
+ const pos = parsePosition(stringPart[`${slot}-stop-${i}-position`], positionDefaults[i - 1]);
243
+ stops.push({ position: pos, color: c.color, ...c.opacity !== void 0 ? { opacity: c.opacity } : {} });
244
+ }
245
+ if (stops.length < 2) continue;
246
+ objectPart[slot] = {
247
+ kind: "gradient",
248
+ value: {
249
+ type: "linear",
250
+ angle: parseAngle(stringPart[angleKey]),
251
+ stops
252
+ }
253
+ };
254
+ }
255
+ }
256
+
257
+ // src/editor/core/components/componentConfigKeys.ts
258
+ var KNOWN_COMPONENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
259
+ "--dialog-confirm-variant",
260
+ "--dialog-cancel-variant",
261
+ // SectionDivider per-variant `color-family` is editor metadata that drives
262
+ // the family-swap rewrite on aliases. It is not a runtime CSS value, so it
263
+ // stays in the config bucket. The other intrinsics (align, hairline,
264
+ // eyebrow/description visibility, eyebrow text-transform) now flow through
265
+ // the alias bucket as cascading CSS vars — see the 2026-05-22 migration.
266
+ "--sectiondivider-lg-color-family",
267
+ "--sectiondivider-md-color-family",
268
+ "--sectiondivider-sm-color-family"
269
+ ]);
270
+
132
271
  // vite-plugin/themes/normalizeTheme.ts
133
- var THEME_SCHEMA_VERSION = 3;
134
272
  function asObject(value) {
135
273
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
136
274
  }
@@ -152,10 +290,41 @@ function migrateEmbeddedKey(src) {
152
290
  const { theme, ...rest } = src;
153
291
  return { ...rest, colorsAndType: theme };
154
292
  }
293
+ function routeKnownConfigKeys(aliases, config) {
294
+ const outAliases = { ...aliases };
295
+ const outConfig = { ...config };
296
+ for (const key of Object.keys(outAliases)) {
297
+ if (!KNOWN_COMPONENT_CONFIG_KEYS.has(key)) continue;
298
+ if (outConfig[key] === void 0) outConfig[key] = outAliases[key];
299
+ delete outAliases[key];
300
+ }
301
+ return { aliases: outAliases, config: outConfig };
302
+ }
303
+ function migrateEmbeddedComponentConfig(comp, config, fileVersion) {
304
+ const { schemaVersion: _entryStamp, aliases: rawAliases, config: rawConfig, ...rest } = config;
305
+ const aliasesIn = asObject(rawAliases);
306
+ if (!aliasesIn) {
307
+ return rawConfig !== void 0 ? { ...rest, config: rawConfig } : rest;
308
+ }
309
+ const migratedConfig = migrateComponentConfig(
310
+ comp,
311
+ aliasesIn,
312
+ asObject(rawConfig) ?? void 0,
313
+ fileVersion
314
+ );
315
+ const split = routeKnownConfigKeys(migratedConfig.aliases, migratedConfig.config);
316
+ const out = { ...rest, aliases: split.aliases };
317
+ if (rawConfig !== void 0 || Object.keys(split.config).length > 0) {
318
+ out.config = split.config;
319
+ }
320
+ return out;
321
+ }
155
322
  function normalizeTheme(raw, resolvers) {
156
323
  const input = asObject(raw) ?? {};
157
324
  const migrated = input.schemaVersion !== THEME_SCHEMA_VERSION;
158
- const src = migrated ? migrateEmbeddedKey(input) : input;
325
+ const inputVersion = typeof input.schemaVersion === "number" ? input.schemaVersion : 0;
326
+ const src = inputVersion < 3 ? migrateEmbeddedKey(input) : input;
327
+ const componentSchemaVersion = typeof input.componentSchemaVersion === "number" ? input.componentSchemaVersion : 0;
159
328
  const dropped = [];
160
329
  const now = (/* @__PURE__ */ new Date()).toISOString();
161
330
  const embeddedColorsAndType = asObject(src.colorsAndType);
@@ -174,18 +343,42 @@ function normalizeTheme(raw, resolvers) {
174
343
  const componentConfigs = {};
175
344
  for (const [comp, value] of Object.entries(asObject(src.componentConfigs) ?? {})) {
176
345
  const embedded = asObject(value);
346
+ let resolvedConfig;
177
347
  if (embedded) {
178
- componentConfigs[comp] = stripFileMarker(embedded);
179
- continue;
348
+ resolvedConfig = stripFileMarker(embedded);
349
+ } else {
350
+ const configName = asString(value, "default");
351
+ const resolved = asObject(resolvers.readComponentConfig(comp, configName));
352
+ if (!resolved) {
353
+ dropped.push(`${comp}/${configName}`);
354
+ continue;
355
+ }
356
+ resolvedConfig = stripFileMarker(resolved);
180
357
  }
181
- const configName = asString(value, "default");
182
- if (configName === "default") continue;
183
- const resolved = asObject(resolvers.readComponentConfig(comp, configName));
184
- if (!resolved) {
185
- dropped.push(`${comp}/${configName}`);
358
+ componentConfigs[comp] = migrateEmbeddedComponentConfig(comp, resolvedConfig, componentSchemaVersion);
359
+ }
360
+ const filled = { components: [], aliases: 0, orphans: 0 };
361
+ for (const comp of resolvers.listComponentNames()) {
362
+ const base = asObject(resolvers.readComponentConfig(comp, "default"));
363
+ const baseAliases = base ? asObject(base.aliases) : null;
364
+ if (!base || !baseAliases) continue;
365
+ const entry = componentConfigs[comp];
366
+ if (!entry) {
367
+ const { schemaVersion: _standaloneStamp, ...rest } = stripFileMarker(base);
368
+ componentConfigs[comp] = structuredClone(rest);
369
+ filled.components.push(comp);
186
370
  continue;
187
371
  }
188
- componentConfigs[comp] = stripFileMarker(resolved);
372
+ const entryAliases = asObject(entry.aliases) ?? {};
373
+ for (const key of Object.keys(baseAliases)) {
374
+ if (key in entryAliases) continue;
375
+ entryAliases[key] = structuredClone(baseAliases[key]);
376
+ filled.aliases++;
377
+ }
378
+ entry.aliases = entryAliases;
379
+ for (const key of Object.keys(entryAliases)) {
380
+ if (!(key in baseAliases)) filled.orphans++;
381
+ }
189
382
  }
190
383
  return {
191
384
  theme: {
@@ -194,10 +387,12 @@ function normalizeTheme(raw, resolvers) {
194
387
  updatedAt: asString(src.updatedAt, now),
195
388
  schemaVersion: THEME_SCHEMA_VERSION,
196
389
  colorsAndType,
197
- componentConfigs
390
+ componentConfigs,
391
+ componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
198
392
  },
199
393
  dropped,
200
- migrated
394
+ migrated,
395
+ filled
201
396
  };
202
397
  }
203
398