@motion-proto/live-tokens 0.47.0 → 0.48.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 (124) hide show
  1. package/.claude/skills/live-tokens-adjust-shape-space/SKILL.md +68 -0
  2. package/.claude/skills/live-tokens-create-component/SKILL.md +2 -2
  3. package/.claude/skills/live-tokens-generate-theme/SKILL.md +152 -0
  4. package/CHANGELOG.md +292 -0
  5. package/README.md +37 -26
  6. package/bin/adjust.mjs +254 -0
  7. package/bin/cli.mjs +94 -7
  8. package/bin/generate-theme.mjs +251 -0
  9. package/bin/migrate.mjs +95 -6
  10. package/dist-plugin/adjust/index.cjs +259 -0
  11. package/dist-plugin/adjust/index.d.cts +45 -0
  12. package/dist-plugin/adjust/index.d.ts +45 -0
  13. package/dist-plugin/adjust/index.js +176 -0
  14. package/dist-plugin/{chunk-H4TRUINI.js → chunk-44RSTAII.js} +0 -48
  15. package/dist-plugin/chunk-6OZFXIQI.js +316 -0
  16. package/dist-plugin/chunk-76TFDTJO.js +556 -0
  17. package/dist-plugin/chunk-D3ZVKOR4.js +52 -0
  18. package/dist-plugin/dataPaths-DBN0RPuT.d.cts +54 -0
  19. package/dist-plugin/dataPaths-DBN0RPuT.d.ts +54 -0
  20. package/dist-plugin/generateColorsAndType/index.cjs +1781 -0
  21. package/dist-plugin/generateColorsAndType/index.d.cts +81 -0
  22. package/dist-plugin/generateColorsAndType/index.d.ts +81 -0
  23. package/dist-plugin/generateColorsAndType/index.js +1210 -0
  24. package/dist-plugin/index.cjs +901 -533
  25. package/dist-plugin/index.d.cts +3 -2
  26. package/dist-plugin/index.d.ts +3 -2
  27. package/dist-plugin/index.js +658 -1082
  28. package/dist-plugin/migrateData/index.cjs +728 -0
  29. package/dist-plugin/migrateData/index.d.cts +60 -0
  30. package/dist-plugin/migrateData/index.d.ts +60 -0
  31. package/dist-plugin/migrateData/index.js +348 -0
  32. package/dist-plugin/themeTypes-DMHZOnUn.d.cts +211 -0
  33. package/dist-plugin/themeTypes-DMHZOnUn.d.ts +211 -0
  34. package/dist-plugin/tokensCssMigrations/index.cjs +2 -1
  35. package/dist-plugin/tokensCssMigrations/index.d.cts +3 -15
  36. package/dist-plugin/tokensCssMigrations/index.d.ts +3 -15
  37. package/dist-plugin/tokensCssMigrations/index.js +4 -2
  38. package/package.json +18 -4
  39. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +183 -158
  40. package/src/editor/component-editor/scaffolding/ComponentFileMenu.svelte +0 -4
  41. package/src/editor/component-editor/scaffolding/SaveAsDialog.svelte +4 -4
  42. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +2 -59
  43. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +1 -1
  44. package/src/editor/core/components/adjustAliases.ts +180 -0
  45. package/src/editor/core/components/aliasKinds.ts +73 -0
  46. package/src/editor/core/components/componentConfigService.ts +26 -38
  47. package/src/editor/core/flashStatus.ts +1 -1
  48. package/src/editor/core/fonts/fontMigration.ts +13 -13
  49. package/src/editor/core/fonts/fontPairing.ts +35 -0
  50. package/src/editor/core/palettes/paletteDerivation.ts +128 -16
  51. package/src/editor/core/preview/lookPreview.ts +141 -0
  52. package/src/editor/core/productionPulse.ts +34 -21
  53. package/src/editor/core/storage/files/versionedFileResourceClient.ts +18 -45
  54. package/src/editor/core/store/editorConfigStore.ts +2 -2
  55. package/src/editor/core/store/editorPersistence.ts +0 -1
  56. package/src/editor/core/store/editorStore.ts +114 -51
  57. package/src/editor/core/store/editorTypes.ts +0 -1
  58. package/src/editor/core/store/gradientSource.ts +2 -2
  59. package/src/editor/core/themes/colorsAndTypeService.ts +95 -0
  60. package/src/editor/core/themes/generateColorsAndType.ts +433 -0
  61. package/src/editor/core/themes/loadRows.ts +64 -0
  62. package/src/editor/core/themes/lookSummary.ts +75 -0
  63. package/src/editor/core/themes/migrations/2026-04-24-legacy-keys-and-bg-to-canvas.ts +3 -3
  64. package/src/editor/core/themes/migrations/2026-05-13-primary-to-brand.ts +2 -2
  65. package/src/editor/core/themes/migrations/2026-05-26-drop-overlay-extra-stops.ts +3 -3
  66. package/src/editor/core/themes/migrations/2026-08-13-drop-legacy-shape-space-keys.ts +38 -0
  67. package/src/editor/core/themes/migrations/2026-08-15-line-height-scale-rename.ts +57 -0
  68. package/src/editor/core/themes/migrations/index.ts +19 -11
  69. package/src/editor/core/themes/slices/components.ts +4 -4
  70. package/src/editor/core/themes/slices/fonts.ts +1 -1
  71. package/src/editor/core/themes/slices/gradients.ts +22 -0
  72. package/src/editor/core/themes/slices/palettes.ts +2 -2
  73. package/src/editor/core/themes/themeInit.ts +21 -29
  74. package/src/editor/core/themes/themeService.ts +218 -78
  75. package/src/editor/core/themes/themeTypes.ts +94 -51
  76. package/src/editor/docs/Docs.svelte +1 -0
  77. package/src/editor/docs/chapters.ts +1 -0
  78. package/src/editor/docs/content/01-overview.md +1 -1
  79. package/src/editor/docs/content/editing-tokens.md +6 -6
  80. package/src/editor/docs/content/getting-started.md +9 -7
  81. package/src/editor/docs/content/themes-workflow.md +74 -34
  82. package/src/editor/docs/content/where-themes-live.md +61 -0
  83. package/src/editor/docs/content.generated.ts +5 -4
  84. package/src/editor/index.ts +27 -27
  85. package/src/editor/pages/ComponentEditorPage.svelte +2 -2
  86. package/src/editor/pages/EditorShell.svelte +4 -30
  87. package/src/editor/ui/BezierCurveEditor.svelte +2 -2
  88. package/src/editor/ui/ColorEditPanel.svelte +1 -1
  89. package/src/editor/ui/FileLoadList.svelte +67 -7
  90. package/src/editor/ui/GradientEditor.svelte +3 -3
  91. package/src/editor/ui/PaletteEditor.svelte +26 -12
  92. package/src/editor/ui/ThemePanel.svelte +1067 -0
  93. package/src/editor/ui/UIDialog.svelte +11 -9
  94. package/src/editor/ui/UIPillButton.svelte +7 -2
  95. package/src/editor/ui/colors/ColorWheel.svelte +34 -1
  96. package/src/editor/ui/curveEngine.ts +48 -0
  97. package/src/editor/ui/index.ts +4 -2
  98. package/src/editor/ui/palette/PaletteBase.svelte +75 -51
  99. package/src/editor/ui/palette/PaletteJumpButton.svelte +3 -8
  100. package/src/live-tokens/data/colors-and-type/autumn.json +2490 -0
  101. package/src/live-tokens/data/{themes → colors-and-type}/default.json +816 -293
  102. package/src/live-tokens/data/colors-and-type/halloween.json +2529 -0
  103. package/src/live-tokens/data/colors-and-type/midnight-study.json +2536 -0
  104. package/src/live-tokens/data/colors-and-type/ocean.json +2489 -0
  105. package/src/live-tokens/data/colors-and-type/royal-velvet.json +2520 -0
  106. package/src/live-tokens/data/colors-and-type/spring-meadow.json +2476 -0
  107. package/src/live-tokens/data/colors-and-type/sunset.json +2528 -0
  108. package/src/live-tokens/data/themes/autumn.json +3918 -0
  109. package/src/live-tokens/data/themes/halloween.json +3992 -0
  110. package/src/live-tokens/data/themes/midnight-study.json +3932 -0
  111. package/src/live-tokens/data/themes/ocean.json +3917 -0
  112. package/src/live-tokens/data/themes/royal-velvet.json +3983 -0
  113. package/src/live-tokens/data/themes/spring-meadow.json +3904 -0
  114. package/src/live-tokens/data/themes/sunset.json +3931 -0
  115. package/src/live-tokens/data/tokens.generated.css +106 -117
  116. package/src/system/styles/CONVENTIONS.md +1 -1
  117. package/src/system/styles/fonts.css +1 -1
  118. package/template/README.md +7 -5
  119. package/template/_gitignore +0 -6
  120. package/src/editor/core/components/componentPersist.ts +0 -62
  121. package/src/editor/core/manifests/manifestService.ts +0 -172
  122. package/src/editor/ui/ManifestFileManager.svelte +0 -446
  123. package/src/editor/ui/ThemeFileManager.svelte +0 -785
  124. package/src/live-tokens/data/manifests/default.json +0 -35
@@ -0,0 +1,251 @@
1
+ // `live-tokens generate-theme` worker.
2
+ //
3
+ // Reads a seed brief (JSON), builds a full validated colors-and-type layer via
4
+ // the compiled engine (dist-plugin/generateColorsAndType — the CLI never imports
5
+ // TS sources), enforces the AA contrast gate, and saves the result as a theme:
6
+ // <themesDir>/<slug>.json, the document that carries the whole look by value.
7
+ // Unless --no-activate it then opens that theme the way the dev server's apply
8
+ // door does — the embedded copies land in the reserved `_working` buffers and
9
+ // `themes/_active.json` names it. Nothing else moves, so generating a theme
10
+ // cannot change what the site ships.
11
+ //
12
+ // Non-color content (shadows, component aliases, fonts) carries forward from
13
+ // the live state so generation only replaces the color identity. Swatch
14
+ // gradients ride along too when user-tuned; stock ones are rebuilt from the new
15
+ // theme's families by the engine.
16
+
17
+ import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'node:fs';
18
+ import { dirname, join, relative, resolve } from 'node:path';
19
+ import { fileURLToPath } from 'node:url';
20
+
21
+ const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
22
+ const ENGINE = resolve(pkgRoot, 'dist-plugin/generateColorsAndType/index.js');
23
+ const packageDataDir = join(pkgRoot, 'src/live-tokens/data');
24
+ const THEME_SCHEMA_VERSION = 3;
25
+
26
+ async function loadEngine() {
27
+ if (!existsSync(ENGINE)) {
28
+ throw new Error(
29
+ `theme engine not found at ${relative(process.cwd(), ENGINE)}. ` +
30
+ `Build the plugin first (npm run build:plugin).`,
31
+ );
32
+ }
33
+ return import(ENGINE);
34
+ }
35
+
36
+ function readJson(path) {
37
+ return JSON.parse(readFileSync(path, 'utf8'));
38
+ }
39
+
40
+ function readJsonIfExists(path) {
41
+ try {
42
+ return existsSync(path) ? readJson(path) : null;
43
+ } catch {
44
+ return null;
45
+ }
46
+ }
47
+
48
+ /** Local dir first, then the installed package's shipped copy — the same
49
+ * fallback order the dev server's reads use. */
50
+ function readData(localDir, packageSubdir, fileName) {
51
+ return (
52
+ readJsonIfExists(join(localDir, `${fileName}.json`)) ??
53
+ readJsonIfExists(join(packageDataDir, packageSubdir, `${fileName}.json`))
54
+ );
55
+ }
56
+
57
+ function componentNames(componentConfigsDir) {
58
+ if (!existsSync(componentConfigsDir)) return [];
59
+ return readdirSync(componentConfigsDir, { withFileTypes: true })
60
+ .filter((e) => e.isDirectory())
61
+ .map((e) => e.name)
62
+ .sort();
63
+ }
64
+
65
+ /** `_fileName` and `_source` are read-door markers, never part of a document. */
66
+ function stripMarkers(value) {
67
+ if (!value || typeof value !== 'object') return value;
68
+ const { _fileName, _source, ...rest } = value;
69
+ return rest;
70
+ }
71
+
72
+ /**
73
+ * The look the new theme carries forward. Without --carry-from that is the LIVE
74
+ * state — buffer first, then the open theme, then the shipped default — because
75
+ * what the user sees is what they expect to keep. With it, the named theme,
76
+ * read by value.
77
+ */
78
+ function resolveCarrySource({ carryFrom, colorsAndTypeDir, componentConfigsDir, themesDir }) {
79
+ const comps = componentNames(componentConfigsDir);
80
+
81
+ if (carryFrom) {
82
+ const theme = readData(themesDir, 'themes', carryFrom);
83
+ if (!theme) throw new Error(`--carry-from theme "${carryFrom}" not found`);
84
+ const componentConfigs = {};
85
+ for (const comp of comps) {
86
+ const cfg = theme.componentConfigs?.[comp];
87
+ if (cfg) componentConfigs[comp] = stripMarkers(cfg);
88
+ }
89
+ return { colorsAndType: stripMarkers(theme.colorsAndType), componentConfigs };
90
+ }
91
+
92
+ const activeSlug = readJsonIfExists(join(themesDir, '_active.json'))?.activeFile ?? 'default';
93
+ const activeTheme = readData(themesDir, 'themes', activeSlug);
94
+ const colorsAndType = stripMarkers(
95
+ readJsonIfExists(join(colorsAndTypeDir, '_working.json')) ??
96
+ activeTheme?.colorsAndType ??
97
+ readData(colorsAndTypeDir, 'colors-and-type', 'default'),
98
+ );
99
+
100
+ // Presence is the model's own rule for "off the shipped default": a buffer
101
+ // exists only where the layer sits off it, and a theme carries only the
102
+ // components it changes.
103
+ const componentConfigs = {};
104
+ for (const comp of comps) {
105
+ const live =
106
+ readJsonIfExists(join(componentConfigsDir, comp, '_working.json')) ??
107
+ activeTheme?.componentConfigs?.[comp];
108
+ if (live) componentConfigs[comp] = stripMarkers(live);
109
+ }
110
+ return { colorsAndType, componentConfigs };
111
+ }
112
+
113
+ /** `engine` is a test seam; the CLI always runs the compiled bundle. */
114
+ export async function runGenerateTheme({
115
+ briefPath,
116
+ activate = true,
117
+ dryRun = false,
118
+ carryFrom,
119
+ root = process.cwd(),
120
+ colorsAndTypeDir,
121
+ componentConfigsDir,
122
+ themesDir,
123
+ engine,
124
+ } = {}) {
125
+ const { buildColorsAndTypeFromSeeds, resolveDataDirs } = engine ?? (await loadEngine());
126
+
127
+ const briefFull = resolve(root, briefPath);
128
+ if (!existsSync(briefFull)) {
129
+ throw new Error(`brief not found at ${relative(root, briefFull)}`);
130
+ }
131
+ let brief;
132
+ try {
133
+ brief = readJson(briefFull);
134
+ } catch (err) {
135
+ throw new Error(`brief is not valid JSON: ${err instanceof Error ? err.message : String(err)}`);
136
+ }
137
+
138
+ const dirs =
139
+ colorsAndTypeDir && componentConfigsDir && themesDir
140
+ ? { colorsAndTypeDir, componentConfigsDir, themesDir }
141
+ : resolveDataDirs();
142
+
143
+ const previousActive = readJsonIfExists(join(dirs.themesDir, '_active.json'))?.activeFile ?? 'default';
144
+ const carry = resolveCarrySource({ carryFrom, ...dirs });
145
+
146
+ const { colorsAndType, slug, report } = buildColorsAndTypeFromSeeds(
147
+ brief,
148
+ {
149
+ cssVariables: carry.colorsAndType?.cssVariables,
150
+ fontSources: carry.colorsAndType?.fontSources,
151
+ fontStacks: carry.colorsAndType?.fontStacks,
152
+ gradients: carry.colorsAndType?.gradients,
153
+ },
154
+ new Date().toISOString(),
155
+ );
156
+
157
+ const themePath = join(dirs.themesDir, `${slug}.json`);
158
+ const existing = readJsonIfExists(themePath);
159
+ const theme = {
160
+ name: colorsAndType.name,
161
+ createdAt: existing?.createdAt ?? colorsAndType.createdAt,
162
+ updatedAt: colorsAndType.updatedAt,
163
+ schemaVersion: THEME_SCHEMA_VERSION,
164
+ colorsAndType,
165
+ componentConfigs: carry.componentConfigs,
166
+ };
167
+
168
+ if (!dryRun) {
169
+ mkdirSync(dirs.themesDir, { recursive: true });
170
+ writeFileSync(themePath, JSON.stringify(theme, null, 2) + '\n');
171
+ if (activate) applyTheme(theme, slug, dirs);
172
+ }
173
+
174
+ return {
175
+ name: theme.name,
176
+ slug,
177
+ themePath,
178
+ existed: existing !== null,
179
+ activated: activate && !dryRun,
180
+ previousActive,
181
+ carriedFrom: carryFrom ?? null,
182
+ componentsCarried: Object.keys(carry.componentConfigs).length,
183
+ dryRun,
184
+ report,
185
+ };
186
+ }
187
+
188
+ /** The apply door's write set, reproduced: the theme's copies fill the reserved
189
+ * buffers, every component it does not carry is cleared back to the default,
190
+ * and `themes/_active.json` names the open document. Production is untouched. */
191
+ function applyTheme(theme, slug, dirs) {
192
+ mkdirSync(dirs.colorsAndTypeDir, { recursive: true });
193
+ writeFileSync(
194
+ join(dirs.colorsAndTypeDir, '_working.json'),
195
+ JSON.stringify(theme.colorsAndType, null, 2),
196
+ );
197
+
198
+ for (const comp of componentNames(dirs.componentConfigsDir)) {
199
+ const workingPath = join(dirs.componentConfigsDir, comp, '_working.json');
200
+ const embedded = theme.componentConfigs[comp];
201
+ if (embedded) writeFileSync(workingPath, JSON.stringify({ ...embedded, component: comp }, null, 2));
202
+ else if (existsSync(workingPath)) rmSync(workingPath);
203
+ }
204
+
205
+ writeFileSync(join(dirs.themesDir, '_active.json'), JSON.stringify({ activeFile: slug }));
206
+ }
207
+
208
+ export function formatGenerateThemeResult(result) {
209
+ const root = process.cwd();
210
+ const lines = [];
211
+ const wrote = result.dryRun ? 'Would write' : result.existed ? 'Updated' : 'Created';
212
+ lines.push(`${wrote} theme "${result.name}" → ${relative(root, result.themePath)}`);
213
+ lines.push(
214
+ `Carried forward from ${result.carriedFrom ? `theme "${result.carriedFrom}"` : 'the live state'}: ` +
215
+ `fonts, gradients and ${result.componentsCarried} component config(s).`,
216
+ );
217
+
218
+ lines.push(`\nContrast report (${result.report.scheme} scheme):`);
219
+ const width = Math.max(...result.report.checks.map((c) => c.textVar.length));
220
+ for (const c of result.report.checks) {
221
+ const mark = c.pass ? '✓' : '✗';
222
+ const fixed = c.corrected ? ' (auto-corrected)' : '';
223
+ lines.push(
224
+ ` ${mark} ${c.textVar.padEnd(width)} ${c.ratio.toFixed(2).padStart(6)}:1 vs ${c.against} floor ${c.floor}:1${fixed}`,
225
+ );
226
+ }
227
+ if (result.report.failures.length > 0) {
228
+ lines.push(`\nUnmet floors — adjust the brief's seeds and re-run:`);
229
+ for (const f of result.report.failures) lines.push(` ! ${f}`);
230
+ }
231
+
232
+ lines.push(
233
+ result.report.gradients === 'carried'
234
+ ? '\nGradients: kept your tuned swatch gradients.'
235
+ : '\nGradients: swatch tokens rebuilt from the theme families.',
236
+ );
237
+ if (result.report.canvasGradient) {
238
+ lines.push(`Canvas sky: ${result.report.canvasGradient}.`);
239
+ }
240
+
241
+ if (result.activated) {
242
+ lines.push(
243
+ `\nOpened "${result.slug}" (previously open: "${result.previousActive}"). ` +
244
+ `Reload the app to see it; switch back any time from Load in the editor's Theme panel. ` +
245
+ `Adopt it there to publish it to tokens.generated.css.`,
246
+ );
247
+ } else if (!result.dryRun) {
248
+ lines.push(`\nNot opened (--no-activate). Load "${result.slug}" from the editor's Theme panel to see it.`);
249
+ }
250
+ return lines.join('\n');
251
+ }
package/bin/migrate.mjs CHANGED
@@ -1,17 +1,23 @@
1
1
  // `live-tokens migrate` worker.
2
2
  //
3
- // Applies the registered tokens-css migrations to a consumer's developer-authored
4
- // tokens.css, reconciling Layer-1 drift after a package upgrade. The migration
5
- // engine itself lives in the built plugin (dist-plugin/tokensCssMigrations) so
6
- // the CLI and the dev-plugin guardrail share one source of truth; this module
7
- // only handles path resolution, file IO, and reporting.
3
+ // Two passes, both against a consumer's project. The first applies the
4
+ // registered tokens-css migrations to their developer-authored tokens.css,
5
+ // reconciling Layer-1 drift after a package upgrade. The second heals the data
6
+ // tree: the pre-working-set model left a materialised copy of every applied
7
+ // theme behind and a pointer pair per layer pointing at them, and this is what
8
+ // turns that into one production theme plus one buffer per layer.
9
+ //
10
+ // Both engines live in the built plugin (dist-plugin/) so the CLI and the
11
+ // dev-plugin guardrail share one source of truth; this module handles path
12
+ // resolution, file IO, and reporting.
8
13
 
9
14
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
10
- import { dirname, relative, resolve } from 'node:path';
15
+ import { dirname, join, relative, resolve } from 'node:path';
11
16
  import { fileURLToPath } from 'node:url';
12
17
 
13
18
  const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
14
19
  const ENGINE = resolve(pkgRoot, 'dist-plugin/tokensCssMigrations/index.js');
20
+ const DATA_ENGINE = resolve(pkgRoot, 'dist-plugin/migrateData/index.js');
15
21
 
16
22
  // Locations to probe when neither --tokens nor config.tokensCssPath is given.
17
23
  const DEFAULT_CANDIDATES = [
@@ -77,6 +83,89 @@ export async function runMigrate({ tokensArg, check = false, root = process.cwd(
77
83
  return { status: 'changed', tokensPath, applied, addedLines };
78
84
  }
79
85
 
86
+ async function loadDataEngine() {
87
+ if (!existsSync(DATA_ENGINE)) {
88
+ throw new Error(
89
+ `data migration engine not found at ${relative(process.cwd(), DATA_ENGINE)}. ` +
90
+ `Build the plugin first (npm run build:plugin).`,
91
+ );
92
+ }
93
+ return import(DATA_ENGINE);
94
+ }
95
+
96
+ /** `engine` is a test seam; the CLI always runs the compiled bundle. */
97
+ export async function runMigrateData({ check = false, engine } = {}) {
98
+ const { migrateData, resolveDataDirs } = engine ?? (await loadDataEngine());
99
+ const dirs = resolveDataDirs();
100
+ return migrateData({
101
+ dataDir: dirs.dataDir,
102
+ colorsAndTypeDir: dirs.colorsAndTypeDir,
103
+ componentConfigsDir: dirs.componentConfigsDir,
104
+ themesDir: dirs.themesDir,
105
+ legacyManifestsDir: dirs.legacyManifestsDir,
106
+ packageDataDir: join(pkgRoot, 'src/live-tokens/data'),
107
+ check,
108
+ });
109
+ }
110
+
111
+ const HOW_SAID = {
112
+ 'all-default': 'every retired pointer named the default',
113
+ matched: 'it matches what the retired pointers resolved to',
114
+ recovered: 'no saved theme matched what the retired pointers resolved to, so it was captured',
115
+ };
116
+
117
+ export function formatMigrateDataResult(result) {
118
+ const root = process.cwd();
119
+ const rel = (p) => relative(root, p);
120
+ if (result.status === 'clean') return '';
121
+
122
+ const planned = result.status === 'planned';
123
+ const lines = [planned ? 'Data tree heal (planned):' : 'Data tree healed:'];
124
+
125
+ for (const { from, to } of result.renames) {
126
+ lines.push(` ${planned ? 'would move' : 'moved'} ${rel(from)} → ${rel(to)} (0.48 layout)`);
127
+ }
128
+ for (const p of result.upgradedThemes) {
129
+ lines.push(` ${planned ? 'would carry' : 'carried'} ${rel(p)} by value (was a pre-v3 theme naming files)`);
130
+ }
131
+ if (result.production) {
132
+ lines.push(` production theme → "${result.production.slug}" (${HOW_SAID[result.production.how]})`);
133
+ }
134
+ if (result.recoveredThemePath) {
135
+ lines.push(` ${planned ? 'would write' : 'wrote'} ${rel(result.recoveredThemePath)}`);
136
+ }
137
+ for (const p of result.workingWritten) {
138
+ lines.push(` ${planned ? 'would fill' : 'filled'} ${rel(p)} from the live state`);
139
+ }
140
+ for (const p of result.workingKept) {
141
+ lines.push(` kept ${rel(p)}: the editor already owns that buffer`);
142
+ }
143
+ for (const p of result.deletedFiles) {
144
+ lines.push(` ${planned ? 'would delete' : 'deleted'} ${rel(p)}: a copy of a saved theme`);
145
+ }
146
+ for (const p of result.keptUserFiles) {
147
+ lines.push(` kept ${rel(p)}: it matches no theme, so it is yours`);
148
+ }
149
+ for (const note of result.droppedRefs) {
150
+ lines.push(` ${note}`);
151
+ }
152
+ for (const p of result.notThemes) {
153
+ lines.push(` left ${rel(p)} alone: it is a colors-and-type file, not a theme`);
154
+ }
155
+ for (const p of result.shadowedDefaults) {
156
+ lines.push(` kept ${rel(p)}: it shadows the default the package ships, which has moved on`);
157
+ }
158
+ lines.push(
159
+ ` ${planned ? 'would retire' : 'retired'} ${result.deletedPointers.length} per-layer pointer file(s)`,
160
+ );
161
+ lines.push(
162
+ planned
163
+ ? '\nRun without --check to apply this, then review the diff in git.'
164
+ : '\nRestart the dev server: it rebuilds tokens.generated.css from the production theme.',
165
+ );
166
+ return lines.join('\n');
167
+ }
168
+
80
169
  function diffAddedLines(before, after) {
81
170
  const beforeSet = new Set(before.split('\n'));
82
171
  return after.split('\n').filter((l) => l.trim() && !beforeSet.has(l));
@@ -0,0 +1,259 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // vite-plugin/adjust/index.ts
31
+ var adjust_exports = {};
32
+ __export(adjust_exports, {
33
+ adjustAliases: () => adjustAliases,
34
+ matchesKind: () => matchesKind,
35
+ readLiveTokensConfig: () => readLiveTokensConfig,
36
+ resolveDataDirs: () => resolveDataDirs
37
+ });
38
+ module.exports = __toCommonJS(adjust_exports);
39
+
40
+ // src/editor/core/components/aliasKinds.ts
41
+ var KIND_PATTERNS = [
42
+ { kind: "font-family", matches: (v) => v.endsWith("-font-family") },
43
+ { kind: "font-weight", matches: (v) => v.endsWith("-font-weight") },
44
+ { kind: "font-size", matches: (v) => v.endsWith("-font-size") || v.endsWith("-icon-size") || v.endsWith("-thumb-size") },
45
+ { kind: "line-height", matches: (v) => v.endsWith("-line-height") },
46
+ { kind: "letter-spacing", matches: (v) => v.endsWith("-letter-spacing") },
47
+ { kind: "text-color", matches: (v) => v.endsWith("-text") || v.startsWith("--text-") },
48
+ { kind: "radius", matches: (v) => v.endsWith("-radius") || v.startsWith("--radius-") },
49
+ { kind: "divider-width", matches: (v) => v.endsWith("-divider-width") || v.endsWith("-divider-thickness") },
50
+ { kind: "divider-height", matches: (v) => v.endsWith("-divider-height") || v.endsWith("-track-height") },
51
+ { kind: "divider-inset", matches: (v) => v.endsWith("-divider-inset") },
52
+ { kind: "dot-size", matches: (v) => v.endsWith("-dot-size") },
53
+ { kind: "blur", matches: (v) => v.endsWith("-blur") || v.startsWith("--blur-") },
54
+ { kind: "scale", matches: (v) => v.endsWith("-scale") || v.startsWith("--scale-") },
55
+ { kind: "shadow", matches: (v) => v.endsWith("-shadow") || v.startsWith("--shadow-") },
56
+ { kind: "padding", matches: (v) => v.endsWith("-padding") || v.endsWith("-margin") },
57
+ { kind: "gap", matches: (v) => v.endsWith("-gap") },
58
+ { kind: "duration", matches: (v) => v.endsWith("-duration") || v.startsWith("--duration-") },
59
+ { kind: "easing", matches: (v) => v.endsWith("-easing") || v.startsWith("--ease-") },
60
+ { kind: "border-width", matches: (v) => v.endsWith("-border-width") || v.endsWith("-accent-width") || v.endsWith("-hairline-thickness") || v.startsWith("--border-width-") },
61
+ { kind: "border", matches: (v) => v.endsWith("-border") || v.startsWith("--border-") },
62
+ { kind: "surface", matches: (v) => v.endsWith("-surface") || v.startsWith("--surface-") }
63
+ ];
64
+ var MATCHER_BY_KIND = Object.fromEntries(
65
+ KIND_PATTERNS.map((p) => [p.kind, p.matches])
66
+ );
67
+ var SIDE_SUFFIXES = ["-top", "-right", "-bottom", "-left"];
68
+ function matchesKind(variable, kind) {
69
+ const side = SIDE_SUFFIXES.find((s) => variable.endsWith(s));
70
+ return MATCHER_BY_KIND[kind](side ? variable.slice(0, -side.length) : variable);
71
+ }
72
+
73
+ // src/editor/core/components/adjustAliases.ts
74
+ var RADIUS_FULL = "--radius-full";
75
+ var RADIUS_RUNGS = [
76
+ "--radius-none",
77
+ "--radius-sm",
78
+ "--radius-md",
79
+ "--radius-lg",
80
+ "--radius-xl",
81
+ "--radius-2xl",
82
+ "--radius-3xl",
83
+ "--radius-4xl"
84
+ ];
85
+ var SPACE_RUNGS = [
86
+ "--space-0",
87
+ "--space-2",
88
+ "--space-4",
89
+ "--space-6",
90
+ "--space-8",
91
+ "--space-10",
92
+ "--space-12",
93
+ "--space-16",
94
+ "--space-20",
95
+ "--space-24",
96
+ "--space-32",
97
+ "--space-48"
98
+ ];
99
+ var SPACE_FAMILY = [...SPACE_RUNGS, "--space-40", "--space-64", "--space-96", "--space-128"];
100
+ var BORDER_WIDTH_RUNGS = [
101
+ "--border-width-0",
102
+ "--border-width-1",
103
+ "--border-width-2",
104
+ "--border-width-3",
105
+ "--border-width-4",
106
+ "--border-width-5",
107
+ "--border-width-6",
108
+ "--border-width-8",
109
+ "--border-width-10",
110
+ "--border-width-12",
111
+ "--border-width-16",
112
+ "--border-width-20",
113
+ "--border-width-24"
114
+ ];
115
+ var LADDERS = {
116
+ radius: { rungs: RADIUS_RUNGS, family: [...RADIUS_RUNGS, RADIUS_FULL] },
117
+ padding: { rungs: SPACE_RUNGS, family: SPACE_FAMILY },
118
+ gap: { rungs: SPACE_RUNGS, family: SPACE_FAMILY },
119
+ "border-width": { rungs: BORDER_WIDTH_RUNGS, family: BORDER_WIDTH_RUNGS }
120
+ };
121
+ var TOKEN_NAME = /^--[a-z0-9-]+$/;
122
+ function rungsFor(op) {
123
+ return op.kind === "radius" && op.full ? [...RADIUS_RUNGS, RADIUS_FULL] : LADDERS[op.kind].rungs;
124
+ }
125
+ function spaceStep(token) {
126
+ return Number(token.slice("--space-".length));
127
+ }
128
+ function nearestSpaceRung(token, rungs) {
129
+ const step = spaceStep(token);
130
+ let best = 0;
131
+ for (let i = 1; i < rungs.length; i++) {
132
+ if (Math.abs(spaceStep(rungs[i]) - step) <= Math.abs(spaceStep(rungs[best]) - step)) best = i;
133
+ }
134
+ return best;
135
+ }
136
+ function resolve(op, value) {
137
+ if (typeof value !== "string" || !TOKEN_NAME.test(value)) return { skip: "raw-value" };
138
+ if (!LADDERS[op.kind].family.includes(value)) return { skip: "off-ladder" };
139
+ if (op.set !== void 0) return { from: value, to: op.set };
140
+ const rungs = rungsFor(op);
141
+ let index = rungs.indexOf(value);
142
+ if (index < 0) {
143
+ if (value === RADIUS_FULL) return { skip: "pill-preserved" };
144
+ index = nearestSpaceRung(value, rungs);
145
+ }
146
+ const wanted = index + op.shift;
147
+ const landed = Math.min(Math.max(wanted, 0), rungs.length - 1);
148
+ if (landed !== wanted && rungs[landed] === value) return { skip: "clamped" };
149
+ return { from: value, to: rungs[landed] };
150
+ }
151
+ function validateOp(op, configs) {
152
+ const ladder = LADDERS[op.kind];
153
+ if (!ladder) throw new Error(`Unknown kind "${op.kind}" (expected ${Object.keys(LADDERS).join(", ")})`);
154
+ if (op.target !== void 0 && !(op.target in configs)) {
155
+ throw new Error(`Unknown target component "${op.target}"`);
156
+ }
157
+ const hasSet = op.set !== void 0;
158
+ const hasShift = op.shift !== void 0;
159
+ if (hasSet === hasShift) throw new Error(`A ${op.kind} op needs exactly one of "set" or "shift"`);
160
+ const settable = op.kind === "radius" ? ladder.family : ladder.rungs;
161
+ if (hasSet && !settable.includes(op.set)) {
162
+ throw new Error(`"${op.set}" is not on the ${op.kind} ladder`);
163
+ }
164
+ if (hasShift && !Number.isInteger(op.shift)) {
165
+ throw new Error(`"shift" must be a whole number of steps, got ${op.shift}`);
166
+ }
167
+ if (op.full !== void 0 && !(op.kind === "radius" && hasShift)) {
168
+ throw new Error('"full" applies to radius shifts only');
169
+ }
170
+ }
171
+ function adjustAliases(configs, ops, now) {
172
+ for (const op of ops) validateOp(op, configs);
173
+ const next = { ...configs };
174
+ const reports = /* @__PURE__ */ new Map();
175
+ const reportFor = (component) => {
176
+ let report = reports.get(component);
177
+ if (!report) {
178
+ report = { component, changes: [], skips: [] };
179
+ reports.set(component, report);
180
+ }
181
+ return report;
182
+ };
183
+ for (const op of ops) {
184
+ for (const component of op.target ? [op.target] : Object.keys(next)) {
185
+ const config = next[component];
186
+ const aliases = { ...config.aliases };
187
+ let changed = false;
188
+ for (const [variable, value] of Object.entries(config.aliases)) {
189
+ if (!matchesKind(variable, op.kind)) continue;
190
+ const outcome = resolve(op, value);
191
+ if ("skip" in outcome) {
192
+ reportFor(component).skips.push({ variable, value, reason: outcome.skip });
193
+ continue;
194
+ }
195
+ if (outcome.to === outcome.from) continue;
196
+ aliases[variable] = outcome.to;
197
+ reportFor(component).changes.push({ variable, from: outcome.from, to: outcome.to });
198
+ changed = true;
199
+ }
200
+ if (changed) next[component] = { ...config, aliases, updatedAt: now };
201
+ }
202
+ }
203
+ return { configs: next, report: { components: [...reports.values()] } };
204
+ }
205
+
206
+ // vite-plugin/files/dataPaths.ts
207
+ var import_fs = __toESM(require("fs"), 1);
208
+ var import_path = __toESM(require("path"), 1);
209
+ var DEFAULT_DATA_DIR = "src/live-tokens/data";
210
+ var KNOWN_CONFIG_KEYS = /* @__PURE__ */ new Set([
211
+ "dataDir",
212
+ "colorsAndTypeDir",
213
+ "componentConfigsDir",
214
+ "themesDir",
215
+ "manifestsDir",
216
+ "tokensCssPath"
217
+ ]);
218
+ var cached = null;
219
+ function readLiveTokensConfig() {
220
+ if (cached) return cached;
221
+ try {
222
+ const configPath = import_path.default.resolve("live-tokens.config.json");
223
+ if (!import_fs.default.existsSync(configPath)) return cached = {};
224
+ const parsed = JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
225
+ if (!parsed || typeof parsed !== "object") return cached = {};
226
+ const unknown = Object.keys(parsed).filter(
227
+ (k) => k !== "$schema" && !KNOWN_CONFIG_KEYS.has(k)
228
+ );
229
+ if (unknown.length > 0) {
230
+ console.warn(
231
+ `[live-tokens] Unknown key(s) in live-tokens.config.json: ${unknown.join(", ")}. Known keys: ${Array.from(KNOWN_CONFIG_KEYS).join(", ")}.`
232
+ );
233
+ }
234
+ cached = parsed;
235
+ return cached;
236
+ } catch {
237
+ return cached = {};
238
+ }
239
+ }
240
+ function resolveDataDirs(opts = {}) {
241
+ const fileConfig = readLiveTokensConfig();
242
+ const dataDirRaw = opts.dataDir ?? fileConfig.dataDir ?? DEFAULT_DATA_DIR;
243
+ const dataDir = import_path.default.resolve(dataDirRaw);
244
+ const sub = (name) => import_path.default.resolve(dataDir, name);
245
+ return {
246
+ dataDir,
247
+ colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
248
+ componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
249
+ themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
250
+ legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
251
+ };
252
+ }
253
+ // Annotate the CommonJS export names for ESM import in node:
254
+ 0 && (module.exports = {
255
+ adjustAliases,
256
+ matchesKind,
257
+ readLiveTokensConfig,
258
+ resolveDataDirs
259
+ });
@@ -0,0 +1,45 @@
1
+ import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DMHZOnUn.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
3
+
4
+ type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
5
+ interface AdjustOp {
6
+ /** Component id; omitted applies the op to every config. */
7
+ target?: string;
8
+ kind: AdjustKind;
9
+ set?: string;
10
+ shift?: number;
11
+ /** Radius shifts only: admits `--radius-full` as the ladder's top rung. */
12
+ full?: boolean;
13
+ }
14
+ type SkipReason = 'raw-value' | 'off-ladder' | 'clamped' | 'pill-preserved';
15
+ interface AliasChange {
16
+ variable: string;
17
+ from: string;
18
+ to: string;
19
+ }
20
+ interface AliasSkip {
21
+ variable: string;
22
+ value: AliasDiskValue;
23
+ reason: SkipReason;
24
+ }
25
+ interface ComponentReport {
26
+ component: string;
27
+ changes: AliasChange[];
28
+ skips: AliasSkip[];
29
+ }
30
+ interface AdjustReport {
31
+ components: ComponentReport[];
32
+ }
33
+ interface AdjustResult {
34
+ configs: Record<string, ComponentConfig>;
35
+ report: AdjustReport;
36
+ }
37
+ declare function adjustAliases(configs: Record<string, ComponentConfig>, ops: readonly AdjustOp[], now: string): AdjustResult;
38
+
39
+ type TokenKind = 'surface' | 'border' | 'border-width' | 'radius' | 'divider-width' | 'divider-height' | 'divider-inset' | 'dot-size' | 'blur' | 'scale' | 'shadow' | 'font-family' | 'font-weight' | 'font-size' | 'line-height' | 'letter-spacing' | 'padding' | 'gap' | 'duration' | 'easing' | 'text-color';
40
+ /** Side-aware kind test. Per-side paddings (`--card-default-body-padding-top`)
41
+ are written by UIPaddingSelector and never declared as editor tokens, so
42
+ `rawKind` never meets one; the CLI does, and they belong with their parent. */
43
+ declare function matchesKind(variable: string, kind: TokenKind): boolean;
44
+
45
+ export { type AdjustKind, type AdjustOp, type AdjustReport, type AdjustResult, type AliasChange, type AliasSkip, type ComponentReport, type SkipReason, adjustAliases, matchesKind };