@motion-proto/live-tokens 0.94.2 → 0.95.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 (108) hide show
  1. package/.claude/skills/live-tokens-create-page/SKILL.md +7 -3
  2. package/CHANGELOG.md +108 -0
  3. package/README.md +6 -6
  4. package/dist-plugin/{chunk-7XSUHJUH.js → chunk-4J6JPSVS.js} +53 -2
  5. package/dist-plugin/{chunk-W5VQV32K.js → chunk-GYRULB2X.js} +1 -1
  6. package/dist-plugin/{chunk-ONYAIXCZ.js → chunk-QQASYPW7.js} +32 -9
  7. package/dist-plugin/index.cjs +598 -17
  8. package/dist-plugin/index.d.cts +9 -1
  9. package/dist-plugin/index.d.ts +9 -1
  10. package/dist-plugin/index.js +513 -6
  11. package/dist-plugin/migrateData/index.cjs +32 -9
  12. package/dist-plugin/migrateData/index.js +2 -2
  13. package/dist-plugin/setColors/index.cjs +32 -22
  14. package/dist-plugin/setColors/index.js +2 -15
  15. package/dist-plugin/setGeometry/index.cjs +32 -9
  16. package/dist-plugin/setGeometry/index.js +1 -1
  17. package/dist-plugin/tokensCssMigrations/index.cjs +53 -2
  18. package/dist-plugin/tokensCssMigrations/index.d.cts +9 -2
  19. package/dist-plugin/tokensCssMigrations/index.d.ts +9 -2
  20. package/dist-plugin/tokensCssMigrations/index.js +1 -1
  21. package/package.json +8 -3
  22. package/src/demo/ChromeSelect.svelte +111 -0
  23. package/src/demo/Demo.svelte +39 -5
  24. package/src/demo/DemoBar.svelte +33 -14
  25. package/src/demo/FloatingTokenTags.css +33 -4
  26. package/src/demo/FloatingTokenTags.svelte +81 -68
  27. package/src/demo/Section.svelte +1 -7
  28. package/src/demo/SketchSelect.svelte +36 -12
  29. package/src/demo/SourceLinks.svelte +5 -4
  30. package/src/demo/TestingLoops.svelte +128 -19
  31. package/src/demo/ThemeSelect.svelte +34 -11
  32. package/src/demo/evalResults.ts +7 -7
  33. package/src/demo/sections/SectionClaude.svelte +20 -13
  34. package/src/demo/sections/SectionFooter.svelte +4 -5
  35. package/src/demo/sections/SectionGetStarted.svelte +5 -6
  36. package/src/demo/sections/SectionHero.svelte +43 -23
  37. package/src/demo/sections/SectionKit.svelte +141 -45
  38. package/src/demo/sections/SectionLayers.svelte +3 -3
  39. package/src/demo/sections/SectionOffering.svelte +8 -23
  40. package/src/editor/bootstrap.ts +12 -6
  41. package/src/editor/component-editor/ImageEditor.svelte +1 -1
  42. package/src/editor/component-editor/ImageLightboxEditor.svelte +3 -4
  43. package/src/editor/component-editor/registration.ts +36 -0
  44. package/src/editor/component-editor/registry.ts +5 -12
  45. package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +11 -2
  46. package/src/editor/component-editor/scaffolding/StateBlock.svelte +7 -0
  47. package/src/editor/component-editor/scaffolding/TokenLayout.svelte +1 -2
  48. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +17 -2
  49. package/src/editor/core/store/editorPersistence.ts +6 -1
  50. package/src/editor/core/store/editorRenderer.ts +1 -4
  51. package/src/editor/core/store/editorStore.ts +5 -9
  52. package/src/editor/core/store/editorTypes.ts +2 -2
  53. package/src/editor/core/themes/migrations/2026-10-15-responsive-columns.ts +40 -0
  54. package/src/editor/core/themes/migrations/index.ts +2 -0
  55. package/src/editor/core/themes/slices/columns.ts +112 -42
  56. package/src/editor/docs/Docs.svelte +25 -22
  57. package/src/editor/docs/chapters.ts +1 -0
  58. package/src/editor/docs/content/editing-tokens.md +5 -3
  59. package/src/editor/docs/content/production-css.md +116 -0
  60. package/src/editor/docs/content/themes-workflow.md +2 -0
  61. package/src/editor/docs/content.generated.ts +3 -2
  62. package/src/editor/index.ts +2 -1
  63. package/src/editor/overlay/LiveEditorOverlay.svelte +67 -19
  64. package/src/editor/overlay/LiveTokensRouter.svelte +8 -4
  65. package/src/editor/pages/Editor.svelte +15 -1
  66. package/src/editor/pages/EditorShell.svelte +312 -99
  67. package/src/editor/skill-atlas/SkillAtlas.svelte +111 -24
  68. package/src/editor/skill-atlas/TreeCanvas.svelte +14 -0
  69. package/src/editor/skill-atlas/skillSources.generated.ts +1 -1
  70. package/src/editor/skill-atlas/trees/create-page.ts +24 -24
  71. package/src/editor/skill-atlas/wireLayout.ts +62 -0
  72. package/src/editor/ui/BezierCurveEditor.svelte +3 -0
  73. package/src/editor/ui/FontStackEditor.svelte +2 -2
  74. package/src/editor/ui/PaletteEditor.svelte +64 -6
  75. package/src/editor/ui/UIPaletteSelector.svelte +2 -0
  76. package/src/editor/ui/colors/ColorsTab.svelte +5 -2
  77. package/src/editor/ui/palette/OverridesPanel.svelte +1 -0
  78. package/src/editor/ui/palette/ScaleCurveEditor.svelte +127 -18
  79. package/src/editor/ui/sections/ColumnsSection.svelte +79 -49
  80. package/src/editor/ui/sections/ShadowsSection.svelte +14 -0
  81. package/src/editor/ui/sections/WashesSection.svelte +11 -0
  82. package/src/editor/ui/sketch/SketchTab.svelte +1 -1
  83. package/src/live-tokens/data/colors-and-type/autumn.json +1 -4
  84. package/src/live-tokens/data/colors-and-type/default.json +1 -4
  85. package/src/live-tokens/data/colors-and-type/halloween.json +1 -4
  86. package/src/live-tokens/data/colors-and-type/midnight-study.json +1 -4
  87. package/src/live-tokens/data/colors-and-type/ocean.json +1 -4
  88. package/src/live-tokens/data/colors-and-type/royal-velvet.json +1 -4
  89. package/src/live-tokens/data/colors-and-type/sketchy.json +1 -4
  90. package/src/live-tokens/data/colors-and-type/spring-meadow.json +1 -4
  91. package/src/live-tokens/data/colors-and-type/sunset.json +1 -4
  92. package/src/live-tokens/data/themes/autumn.json +1 -4
  93. package/src/live-tokens/data/themes/halloween.json +1 -4
  94. package/src/live-tokens/data/themes/midnight-study.json +1 -4
  95. package/src/live-tokens/data/themes/ocean.json +1 -4
  96. package/src/live-tokens/data/themes/royal-velvet.json +1 -4
  97. package/src/live-tokens/data/themes/sketchy.json +1 -4
  98. package/src/live-tokens/data/themes/spring-meadow.json +1 -4
  99. package/src/live-tokens/data/themes/sunset.json +1 -4
  100. package/src/live-tokens/data/tokens.generated.css +1 -4
  101. package/src/system/components/SectionDivider.svelte +4 -0
  102. package/src/system/styles/CONVENTIONS.md +1 -1
  103. package/src/system/styles/tokens.css +30 -4
  104. package/template/src/main.ts +2 -2
  105. package/template/src/pages/Home.svelte +11 -8
  106. package/template/vite.config.ts +7 -3
  107. package/src/system/assets/offering.webp +0 -0
  108. /package/dist-plugin/{chunk-O6GQ6GBH.js → chunk-A4TBP4MM.js} +0 -0
@@ -2,6 +2,13 @@ import { Plugin } from 'vite';
2
2
  export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.cjs';
3
3
  import './dataPaths-BpIK_Obx.cjs';
4
4
 
5
+ interface ProductionCssOptions {
6
+ /** default: true. With false the build removes unreached properties only and keeps every `var()`. */
7
+ substitute?: boolean;
8
+ /** Names a script sets or reads under a name it assembles at runtime. */
9
+ keep?: string[];
10
+ }
11
+
5
12
  interface ThemeFileApiOptions {
6
13
  tokensCssPath: string;
7
14
  tokensGeneratedCssPath?: string;
@@ -14,7 +21,8 @@ interface ThemeFileApiOptions {
14
21
  componentsSrcDir?: string;
15
22
  autoMigrate?: boolean;
16
23
  checks?: boolean;
24
+ productionCss?: false | ProductionCssOptions;
17
25
  }
18
26
  declare function themeFileApi(opts: ThemeFileApiOptions): Plugin;
19
27
 
20
- export { type ThemeFileApiOptions, themeFileApi };
28
+ export { type ProductionCssOptions, type ThemeFileApiOptions, themeFileApi };
@@ -2,6 +2,13 @@ import { Plugin } from 'vite';
2
2
  export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.js';
3
3
  import './dataPaths-BpIK_Obx.js';
4
4
 
5
+ interface ProductionCssOptions {
6
+ /** default: true. With false the build removes unreached properties only and keeps every `var()`. */
7
+ substitute?: boolean;
8
+ /** Names a script sets or reads under a name it assembles at runtime. */
9
+ keep?: string[];
10
+ }
11
+
5
12
  interface ThemeFileApiOptions {
6
13
  tokensCssPath: string;
7
14
  tokensGeneratedCssPath?: string;
@@ -14,7 +21,8 @@ interface ThemeFileApiOptions {
14
21
  componentsSrcDir?: string;
15
22
  autoMigrate?: boolean;
16
23
  checks?: boolean;
24
+ productionCss?: false | ProductionCssOptions;
17
25
  }
18
26
  declare function themeFileApi(opts: ThemeFileApiOptions): Plugin;
19
27
 
20
- export { type ThemeFileApiOptions, themeFileApi };
28
+ export { type ProductionCssOptions, type ThemeFileApiOptions, themeFileApi };
@@ -5,7 +5,7 @@ import {
5
5
  nextAvailableName,
6
6
  normalizeTheme,
7
7
  versionedFileResourceServer
8
- } from "./chunk-W5VQV32K.js";
8
+ } from "./chunk-GYRULB2X.js";
9
9
  import {
10
10
  formatGradientValue,
11
11
  palettesToVars,
@@ -13,17 +13,17 @@ import {
13
13
  parseGradientValue,
14
14
  reconcilePalettesFromCssVars,
15
15
  sanitizeFileName
16
- } from "./chunk-O6GQ6GBH.js";
16
+ } from "./chunk-A4TBP4MM.js";
17
17
  import {
18
18
  CURRENT_COMPONENT_SCHEMA_VERSION
19
- } from "./chunk-ONYAIXCZ.js";
19
+ } from "./chunk-QQASYPW7.js";
20
20
  import {
21
21
  TOKENS_CSS_MIGRATIONS,
22
22
  extractGlobalRootBody,
23
23
  runAdditiveTokensCssMigrations,
24
24
  runTokensCssMigrations,
25
25
  validateTokensCss
26
- } from "./chunk-7XSUHJUH.js";
26
+ } from "./chunk-4J6JPSVS.js";
27
27
  import {
28
28
  hexToOklch,
29
29
  oklchToRgb255,
@@ -37,7 +37,7 @@ import {
37
37
 
38
38
  // vite-plugin/themeFileApi.ts
39
39
  import fs2 from "fs";
40
- import { createHash } from "crypto";
40
+ import { createHash as createHash2 } from "crypto";
41
41
  import path3 from "path";
42
42
 
43
43
  // src/editor/core/themes/migrations/2026-07-21-palette-oklch-basis.ts
@@ -252,6 +252,502 @@ async function checkBeforeBuild({
252
252
  return null;
253
253
  }
254
254
 
255
+ // vite-plugin/productionCss/index.ts
256
+ import { createHash } from "crypto";
257
+
258
+ // vite-plugin/productionCss/analyze.ts
259
+ import postcss from "postcss";
260
+ var ROOT_SPECIFICITY = /* @__PURE__ */ new Map([
261
+ [":root", [0, 1, 0]],
262
+ [":root:root", [0, 2, 0]],
263
+ [":where(:root)", [0, 0, 0]],
264
+ ["html", [0, 0, 1]]
265
+ ]);
266
+ var NAME = String.raw`--(?:[\w-]|[^\x00-\x7f])+`;
267
+ var VAR_REF = new RegExp(String.raw`var\(\s*(${NAME})`, "gi");
268
+ var PRELUDE_NAME = new RegExp(NAME, "g");
269
+ var SCRIPT_NAME = new RegExp(String.raw`(?<![\w-])${NAME}`, "g");
270
+ var CSS_WIDE_KEYWORD = /^\s*(?:initial|inherit|unset|revert|revert-layer)\s*$/i;
271
+ var varRefs = (value) => [...value.matchAll(VAR_REF)].map((m) => m[1]);
272
+ function compareSpecificity(a, b) {
273
+ return a[0] - b[0] || a[1] - b[1] || a[2] - b[2];
274
+ }
275
+ function rootSpecificity(selector) {
276
+ let best = null;
277
+ for (const part of selector.split(",")) {
278
+ const specificity = ROOT_SPECIFICITY.get(part.trim());
279
+ if (!specificity) return null;
280
+ if (!best || compareSpecificity(specificity, best) > 0) best = specificity;
281
+ }
282
+ return best;
283
+ }
284
+ function outranks(a, b) {
285
+ if (a.important !== b.important) return a.important;
286
+ const bySpecificity = compareSpecificity(a.specificity, b.specificity);
287
+ if (bySpecificity !== 0) return bySpecificity > 0;
288
+ return a.asset !== b.asset ? a.asset > b.asset : a.order > b.order;
289
+ }
290
+ function winnerOf(declarations) {
291
+ let winner = null;
292
+ for (const d of declarations) if (d.bare && (!winner || outranks(d, winner))) winner = d;
293
+ return winner;
294
+ }
295
+ function analyze({ css, js, html, keep, entries }) {
296
+ const declarations = /* @__PURE__ */ new Map();
297
+ const regularRefs = /* @__PURE__ */ new Set();
298
+ const preludeNames = /* @__PURE__ */ new Set();
299
+ const registered = /* @__PURE__ */ new Set();
300
+ const reads = css.map(() => /* @__PURE__ */ new Set());
301
+ css.forEach((source, asset) => {
302
+ let order = 0;
303
+ postcss.parse(source).walk((node) => {
304
+ if (node.type === "atrule") {
305
+ if (node.name.toLowerCase() === "property") registered.add(node.params.trim());
306
+ for (const [name] of node.params.matchAll(PRELUDE_NAME)) preludeNames.add(name);
307
+ return;
308
+ }
309
+ if (node.type !== "decl") return;
310
+ const refs = varRefs(node.value);
311
+ for (const name of refs) reads[asset].add(name);
312
+ if (!node.prop.startsWith("--")) {
313
+ for (const name of refs) regularRefs.add(name);
314
+ return;
315
+ }
316
+ let selector = null;
317
+ let outerRule = false;
318
+ const atRules = [];
319
+ for (let p = node.parent; p && p.type !== "root"; p = p.parent) {
320
+ if (p.type === "atrule") {
321
+ const at = p;
322
+ atRules.unshift(`@${at.name} ${at.params}`.trim());
323
+ } else if (p.type === "rule") {
324
+ if (selector === null) selector = p.selector;
325
+ else outerRule = true;
326
+ }
327
+ }
328
+ const specificity = selector === null ? null : rootSpecificity(selector);
329
+ const declaration = {
330
+ asset,
331
+ order: order++,
332
+ selector,
333
+ atRules,
334
+ value: node.value,
335
+ important: node.important,
336
+ root: specificity !== null,
337
+ bare: specificity !== null && atRules.length === 0 && !outerRule,
338
+ specificity
339
+ };
340
+ const list = declarations.get(node.prop);
341
+ if (list) list.push(declaration);
342
+ else declarations.set(node.prop, [declaration]);
343
+ });
344
+ });
345
+ const scriptNames = /* @__PURE__ */ new Set();
346
+ for (const text2 of [...js, ...html]) {
347
+ for (const [name] of text2.matchAll(SCRIPT_NAME)) scriptNames.add(name);
348
+ }
349
+ const keepNames = new Set(keep);
350
+ const references = /* @__PURE__ */ new Map();
351
+ for (const [name, decls] of declarations) {
352
+ references.set(name, new Set(decls.flatMap((d) => varRefs(d.value))));
353
+ }
354
+ const reached = /* @__PURE__ */ new Set();
355
+ const toReach = [...regularRefs, ...preludeNames, ...scriptNames, ...keepNames];
356
+ while (toReach.length) {
357
+ const name = toReach.pop();
358
+ if (reached.has(name)) continue;
359
+ reached.add(name);
360
+ for (const ref of references.get(name) ?? []) toReach.push(ref);
361
+ }
362
+ const loops = /* @__PURE__ */ new Map();
363
+ const reachesCycle = (name) => {
364
+ const state = loops.get(name);
365
+ if (state !== void 0) return state !== false;
366
+ loops.set(name, "visiting");
367
+ let result2 = false;
368
+ for (const ref of references.get(name) ?? []) {
369
+ if (declarations.has(ref) && reachesCycle(ref)) result2 = true;
370
+ }
371
+ loops.set(name, result2);
372
+ return result2;
373
+ };
374
+ const setByName = (name) => scriptNames.has(name) || keepNames.has(name) || registered.has(name);
375
+ const entryAssets = new Set(entries);
376
+ const winners = /* @__PURE__ */ new Map();
377
+ const reasons = /* @__PURE__ */ new Map();
378
+ for (const [name, decls] of declarations) {
379
+ const winner = winnerOf(decls);
380
+ winners.set(name, winner);
381
+ const bare = decls.filter((d) => d.bare);
382
+ const why = [];
383
+ if (decls.some((d) => !d.bare)) why.push("scoped");
384
+ if (scriptNames.has(name)) why.push("script");
385
+ if (preludeNames.has(name)) why.push("prelude");
386
+ if (registered.has(name)) why.push("registered");
387
+ if (keepNames.has(name)) why.push("keep");
388
+ if (winner && CSS_WIDE_KEYWORD.test(winner.value)) why.push("keyword");
389
+ if (reachesCycle(name)) why.push("cycle");
390
+ if (new Set(bare.map((d) => d.asset)).size > 1 && new Set(bare.map((d) => d.value)).size > 1) {
391
+ why.push("assets");
392
+ }
393
+ if ([...references.get(name)].some((ref) => !declarations.has(ref) && !setByName(ref))) why.push("undeclared");
394
+ const declaring = new Set(decls.map((d) => d.asset));
395
+ if (![...declaring].some((asset) => entryAssets.has(asset)) && reads.some((names, asset) => !declaring.has(asset) && names.has(name))) {
396
+ why.push("lazy");
397
+ }
398
+ reasons.set(name, why);
399
+ }
400
+ const dependents = /* @__PURE__ */ new Map();
401
+ for (const [name, refs] of references) {
402
+ for (const ref of refs) {
403
+ const list = dependents.get(ref);
404
+ if (list) list.push(name);
405
+ else dependents.set(ref, [name]);
406
+ }
407
+ }
408
+ const dynamic = /* @__PURE__ */ new Set();
409
+ const toSpread = [
410
+ ...[...reasons].filter(([, why]) => why.length > 0).map(([name]) => name),
411
+ ...scriptNames,
412
+ ...keepNames,
413
+ ...registered
414
+ ];
415
+ while (toSpread.length) {
416
+ const name = toSpread.pop();
417
+ if (dynamic.has(name)) continue;
418
+ dynamic.add(name);
419
+ for (const dependent of dependents.get(name) ?? []) toSpread.push(dependent);
420
+ }
421
+ const result = /* @__PURE__ */ new Map();
422
+ for (const [name, decls] of declarations) {
423
+ const refs = [...references.get(name)];
424
+ const why = reasons.get(name);
425
+ if (refs.some((ref) => dynamic.has(ref))) why.push("reference");
426
+ result.set(name, {
427
+ declarations: decls,
428
+ references: refs,
429
+ reached: reached.has(name),
430
+ dynamic: why.length > 0,
431
+ reasons: why,
432
+ winner: winners.get(name)
433
+ });
434
+ }
435
+ return result;
436
+ }
437
+
438
+ // vite-plugin/productionCss/rewrite.ts
439
+ import postcss2, { AtRule, Rule } from "postcss";
440
+ var TOKEN_BEFORE_FUNCTION = /[\w\-#@\\]|[^\x00-\x7f]/;
441
+ var VAR_NAME = /^\s*(--(?:[\w-]|[^\x00-\x7f])+)\s*(?:,|$)/;
442
+ var JOINS_LEFT = /[\w\-#@.+\\]|[^\x00-\x7f]/;
443
+ var JOINS_RIGHT = /[\w\-.+%(\\]|[^\x00-\x7f]/;
444
+ var EMPTIED_AT_RULES = /* @__PURE__ */ new Set(["media", "supports", "container"]);
445
+ var DESCRIPTOR_AT_RULES = /* @__PURE__ */ new Set(["font-face", "property", "counter-style", "font-palette-values"]);
446
+ function skipString(value, start) {
447
+ const quote = value[start];
448
+ let i = start + 1;
449
+ while (i < value.length && value[i] !== quote) i += value[i] === "\\" ? 2 : 1;
450
+ return i + 1;
451
+ }
452
+ function skipComment(value, start) {
453
+ const end = value.indexOf("*/", start + 2);
454
+ return end < 0 ? value.length : end + 2;
455
+ }
456
+ function closingParen(value, open) {
457
+ let depth = 0;
458
+ let i = open;
459
+ while (i < value.length) {
460
+ const c = value[i];
461
+ if (c === '"' || c === "'") i = skipString(value, i);
462
+ else if (c === "/" && value[i + 1] === "*") i = skipComment(value, i);
463
+ else {
464
+ if (c === "\\") i += 1;
465
+ else if (c === "(") depth += 1;
466
+ else if (c === ")" && --depth === 0) return i;
467
+ i += 1;
468
+ }
469
+ }
470
+ return -1;
471
+ }
472
+ function afterUrl(value, start) {
473
+ let i = start;
474
+ while (/\s/.test(value[i] ?? "")) i += 1;
475
+ if (value[i] === '"' || value[i] === "'") {
476
+ const close = closingParen(value, start - 1);
477
+ return close < 0 ? value.length : close + 1;
478
+ }
479
+ while (i < value.length && value[i] !== ")") i += value[i] === "\\" ? 2 : 1;
480
+ return i + 1;
481
+ }
482
+ function functionAt(value, i, name) {
483
+ return value.slice(i, i + name.length).toLowerCase() === name && (i === 0 || !TOKEN_BEFORE_FUNCTION.test(value[i - 1]));
484
+ }
485
+ function breaksBetween(left, right) {
486
+ if (!left || !right) return false;
487
+ return JOINS_LEFT.test(left) && JOINS_RIGHT.test(right) || left === "/" && right === "*";
488
+ }
489
+ function spliced(before, text2, after) {
490
+ if (text2 === "") return breaksBetween(before, after) ? "/**/" : "";
491
+ const head = breaksBetween(before, text2[0]) ? "/**/" : "";
492
+ const tail = breaksBetween(text2[text2.length - 1], after) ? "/**/" : "";
493
+ return head + text2 + tail;
494
+ }
495
+ function substituteValue(value, valueOf) {
496
+ let out = "";
497
+ let copied = 0;
498
+ let i = 0;
499
+ while (i < value.length) {
500
+ const c = value[i];
501
+ if (c === '"' || c === "'") {
502
+ i = skipString(value, i);
503
+ } else if (c === "/" && value[i + 1] === "*") {
504
+ i = skipComment(value, i);
505
+ } else if (c === "\\") {
506
+ i += 2;
507
+ } else if (functionAt(value, i, "url(")) {
508
+ i = afterUrl(value, i + 4);
509
+ } else if (functionAt(value, i, "var(")) {
510
+ const close = closingParen(value, i + 3);
511
+ if (close < 0) break;
512
+ const args = value.slice(i + 4, close);
513
+ const name = VAR_NAME.exec(args)?.[1];
514
+ const replacement = name === void 0 ? void 0 : valueOf(name);
515
+ out += value.slice(copied, i);
516
+ out += replacement === void 0 ? `${value.slice(i, i + 4)}${substituteValue(args, valueOf)})` : spliced(out[out.length - 1], replacement, value[close + 1]);
517
+ copied = i = close + 1;
518
+ } else {
519
+ i += 1;
520
+ }
521
+ }
522
+ return out + value.slice(copied);
523
+ }
524
+ function staticValues(analysis) {
525
+ const values = /* @__PURE__ */ new Map();
526
+ const valueOf = (name) => {
527
+ const property = analysis.get(name);
528
+ if (!property || property.dynamic) return void 0;
529
+ let value = values.get(name);
530
+ if (value === void 0) {
531
+ value = substituteValue(property.winner.value.trim(), valueOf);
532
+ values.set(name, value);
533
+ }
534
+ return value;
535
+ };
536
+ for (const [name, property] of analysis) if (property.reached) valueOf(name);
537
+ return values;
538
+ }
539
+ function droppedDeclarations(analysis, asset, { substitute }) {
540
+ const dropped = /* @__PURE__ */ new Set();
541
+ for (const property of analysis.values()) {
542
+ const own = property.declarations.filter((d) => d.asset === asset);
543
+ if (!property.reached || substitute && !property.dynamic) {
544
+ for (const d of own) dropped.add(d.order);
545
+ continue;
546
+ }
547
+ const winner = winnerOf(own);
548
+ for (const d of own) if (d.bare && d !== winner) dropped.add(d.order);
549
+ }
550
+ return dropped;
551
+ }
552
+ var isDescriptor = (decl) => decl.parent instanceof AtRule && DESCRIPTOR_AT_RULES.has(decl.parent.name.toLowerCase());
553
+ var removableWhenEmpty = (node) => node instanceof Rule || node instanceof AtRule && EMPTIED_AT_RULES.has(node.name.toLowerCase());
554
+ function removeEmptied(containers) {
555
+ for (const container of containers) {
556
+ let node = container;
557
+ while (removableWhenEmpty(node) && node.nodes?.length === 0) {
558
+ const parent = node.parent;
559
+ node.remove();
560
+ node = parent;
561
+ }
562
+ }
563
+ }
564
+ function rewrite(css, asset, analysis, options, values = options.substitute ? staticValues(analysis) : null) {
565
+ const dropped = droppedDeclarations(analysis, asset, options);
566
+ const root = postcss2.parse(css);
567
+ const emptied = /* @__PURE__ */ new Set();
568
+ let order = 0;
569
+ root.walkDecls((decl) => {
570
+ if (decl.prop.startsWith("--") && dropped.has(order++)) {
571
+ emptied.add(decl.parent);
572
+ decl.remove();
573
+ return;
574
+ }
575
+ if (!values || isDescriptor(decl)) return;
576
+ const value = substituteValue(decl.value, (name) => values.get(name));
577
+ if (value !== decl.value) decl.value = value;
578
+ });
579
+ removeEmptied(emptied);
580
+ return root.toString();
581
+ }
582
+
583
+ // vite-plugin/productionCss/index.ts
584
+ var text = (source) => typeof source === "string" ? source : new TextDecoder().decode(source);
585
+ var HASH = /\[hash(?::(\d+))?\]/;
586
+ var PLACEHOLDER = /\[(name|hash(?::\d+)?|ext|extname|format)\]/g;
587
+ var escape = (literal) => literal.replace(/[.*+?^${}()|[\]\\/]/g, "\\$&");
588
+ var TEXT_ASSET = /\.(css|html|json|map)$/;
589
+ function hashedFileName(fileName, pattern, content) {
590
+ const hash = HASH.exec(pattern);
591
+ if (!hash) return null;
592
+ const length = hash[1] ? Number(hash[1]) : 8;
593
+ let regex = "^";
594
+ let last = 0;
595
+ for (const m of pattern.matchAll(PLACEHOLDER)) {
596
+ regex += escape(pattern.slice(last, m.index));
597
+ if (m.index === hash.index) regex += `([A-Za-z0-9_-]{${length}})`;
598
+ else if (m[1] === "ext") regex += "[^./]*";
599
+ else if (m[1] === "extname") regex += "(?:\\.[^./]*)?";
600
+ else regex += ".*?";
601
+ last = m.index + m[0].length;
602
+ }
603
+ regex += escape(pattern.slice(last)) + "$";
604
+ const match = new RegExp(regex, "d").exec(fileName);
605
+ if (!match?.indices?.[1]) throw new Error(`live-tokens: ${fileName} does not follow the output pattern "${pattern}"`);
606
+ const [start, end] = match.indices[1];
607
+ const digest = createHash("sha256").update(content).digest("base64url").slice(0, length);
608
+ return fileName.slice(0, start) + digest + fileName.slice(end);
609
+ }
610
+ function tails(from, to) {
611
+ let i = 0;
612
+ while (from[i] === to[i]) i += 1;
613
+ const start = from.lastIndexOf("/", i) + 1;
614
+ return [from.slice(start), to.slice(start)];
615
+ }
616
+ function repoint(files, renames) {
617
+ const pairs = [...renames].map(([from, to]) => tails(from, to));
618
+ const replace = (s) => {
619
+ for (const [from, to] of pairs) s = s.replaceAll(from, () => to);
620
+ return s;
621
+ };
622
+ const renamed = (names) => names.map((name) => renames.get(name) ?? name);
623
+ const changed = /* @__PURE__ */ new Set();
624
+ for (const file of files) {
625
+ if (file.type === "chunk") {
626
+ const code = replace(file.code);
627
+ if (code !== file.code) {
628
+ file.code = code;
629
+ changed.add(file);
630
+ }
631
+ if (file.imports.some((name) => renames.has(name))) file.imports = renamed(file.imports);
632
+ if (file.dynamicImports.some((name) => renames.has(name))) file.dynamicImports = renamed(file.dynamicImports);
633
+ const map = file.sourcemapFileName && replace(file.sourcemapFileName);
634
+ if (map && map !== file.sourcemapFileName) file.sourcemapFileName = map;
635
+ const imported = file.viteMetadata?.importedCss;
636
+ if (imported && [...imported].some((name) => renames.has(name))) {
637
+ const next = renamed([...imported]);
638
+ imported.clear();
639
+ for (const name of next) imported.add(name);
640
+ changed.add(file);
641
+ }
642
+ } else if (TEXT_ASSET.test(file.fileName)) {
643
+ const before = text(file.source);
644
+ const after = replace(before);
645
+ if (after !== before) file.source = after;
646
+ }
647
+ const fileName = replace(file.fileName);
648
+ if (fileName !== file.fileName) file.fileName = fileName;
649
+ }
650
+ return changed;
651
+ }
652
+ function renameRewritten(files, sheetRenames, names) {
653
+ const affected = repoint(files, sheetRenames);
654
+ const chunks = files.filter((f) => f.type === "chunk");
655
+ const byName = new Map(chunks.map((c) => [c.fileName, c]));
656
+ const imported = (c) => [...c.imports, ...c.dynamicImports].flatMap((name) => byName.get(name) ?? []);
657
+ for (let grew = true; grew; ) {
658
+ grew = false;
659
+ for (const chunk of chunks) {
660
+ if (!affected.has(chunk) && imported(chunk).some((c) => affected.has(c))) {
661
+ affected.add(chunk);
662
+ grew = true;
663
+ }
664
+ }
665
+ }
666
+ const identity = (c) => [c.code, ...c.viteMetadata?.importedCss ?? []].join("\0");
667
+ const chunkRenames = /* @__PURE__ */ new Map();
668
+ for (const chunk of affected) {
669
+ const reached = /* @__PURE__ */ new Set([chunk]);
670
+ const toVisit = [chunk];
671
+ while (toVisit.length) {
672
+ for (const next of imported(toVisit.pop())) {
673
+ if (!reached.has(next)) {
674
+ reached.add(next);
675
+ toVisit.push(next);
676
+ }
677
+ }
678
+ }
679
+ reached.delete(chunk);
680
+ const others = [...reached].sort((a, b) => a.fileName < b.fileName ? -1 : 1);
681
+ const fileNames = chunk.isEntry ? names.entryFileNames : names.chunkFileNames;
682
+ const pattern = typeof fileNames === "string" ? fileNames : fileNames({
683
+ name: chunk.name,
684
+ isEntry: chunk.isEntry,
685
+ isDynamicEntry: chunk.isDynamicEntry,
686
+ facadeModuleId: chunk.facadeModuleId ?? void 0,
687
+ moduleIds: chunk.moduleIds,
688
+ exports: chunk.exports
689
+ });
690
+ const renamed = hashedFileName(chunk.fileName, pattern, [chunk, ...others].map(identity).join("\0\0"));
691
+ if (renamed !== null && renamed !== chunk.fileName) chunkRenames.set(chunk.fileName, renamed);
692
+ }
693
+ if (chunkRenames.size > 0) repoint(files, chunkRenames);
694
+ }
695
+ function entrySheets(files) {
696
+ const chunks = new Map(files.flatMap((f) => f.type === "chunk" ? [[f.fileName, f]] : []));
697
+ const sheets = /* @__PURE__ */ new Set();
698
+ const seen = /* @__PURE__ */ new Set();
699
+ const toVisit = [...chunks.values()].filter((c) => c.isEntry);
700
+ while (toVisit.length) {
701
+ const chunk = toVisit.pop();
702
+ if (seen.has(chunk.fileName)) continue;
703
+ seen.add(chunk.fileName);
704
+ for (const sheet of chunk.viteMetadata?.importedCss ?? []) sheets.add(sheet);
705
+ for (const name of chunk.imports) {
706
+ const imported = chunks.get(name);
707
+ if (imported) toVisit.push(imported);
708
+ }
709
+ }
710
+ return sheets;
711
+ }
712
+ function rewriteBundle(bundle, { substitute = true, keep = [] }, names) {
713
+ const files = Object.values(bundle);
714
+ const assets = files.filter((f) => f.type === "asset");
715
+ const sheets = assets.filter((f) => f.fileName.endsWith(".css"));
716
+ if (sheets.length === 0) return null;
717
+ const css = sheets.map((f) => text(f.source));
718
+ const js = [
719
+ ...files.flatMap((f) => f.type === "chunk" ? [f.code] : []),
720
+ ...assets.filter((f) => /\.[cm]?js$/.test(f.fileName)).map((f) => text(f.source))
721
+ ];
722
+ const html = assets.filter((f) => f.fileName.endsWith(".html")).map((f) => text(f.source));
723
+ const loadedWithPage = entrySheets(files);
724
+ const entries = sheets.flatMap((sheet, i) => loadedWithPage.has(sheet.fileName) ? [i] : []);
725
+ const analysis = analyze({ css, js, html, keep, entries });
726
+ const values = substitute ? staticValues(analysis) : null;
727
+ let bytesSaved = 0;
728
+ const renames = /* @__PURE__ */ new Map();
729
+ const { assetFileNames } = names;
730
+ sheets.forEach((sheet, i) => {
731
+ const rewritten = rewrite(css[i], i, analysis, { substitute }, values);
732
+ bytesSaved += Buffer.byteLength(css[i]) - Buffer.byteLength(rewritten);
733
+ if (rewritten === css[i]) return;
734
+ sheet.source = rewritten;
735
+ const pattern = typeof assetFileNames === "string" ? assetFileNames : assetFileNames({ type: "asset", names: sheet.names, originalFileNames: sheet.originalFileNames, source: rewritten });
736
+ const renamed = hashedFileName(sheet.fileName, pattern, rewritten);
737
+ if (renamed !== null && renamed !== sheet.fileName) renames.set(sheet.fileName, renamed);
738
+ });
739
+ if (renames.size > 0) renameRewritten(files, renames, names);
740
+ let propertiesAfter = 0;
741
+ for (const property of analysis.values()) {
742
+ if (property.reached && (property.dynamic || !substitute)) propertiesAfter += 1;
743
+ }
744
+ return { propertiesBefore: analysis.size, propertiesAfter, bytesSaved };
745
+ }
746
+ function formatReport({ propertiesBefore, propertiesAfter, bytesSaved }) {
747
+ const kB = (Math.abs(bytesSaved) / 1e3).toFixed(1);
748
+ return `live-tokens: the production CSS declares ${propertiesAfter.toLocaleString("en-US")} of ${propertiesBefore.toLocaleString("en-US")} custom properties and is ${kB} kB ${bytesSaved >= 0 ? "smaller" : "larger"}.`;
749
+ }
750
+
255
751
  // vite-plugin/themeFileApi.ts
256
752
  import { fileURLToPath as fileURLToPath2 } from "url";
257
753
  var PKG_VERSION = (() => {
@@ -524,7 +1020,7 @@ function themeFileApi(opts) {
524
1020
  }
525
1021
  return { body: lines.join("\n"), colorsAndTypeVarCount, componentOverrideCount };
526
1022
  }
527
- const bakeFingerprint = (body) => createHash("sha256").update(body).digest("hex").slice(0, 16);
1023
+ const bakeFingerprint = (body) => createHash2("sha256").update(body).digest("hex").slice(0, 16);
528
1024
  const BAKE_FINGERPRINT_LINE = /^\/\* Bake fingerprint: ([0-9a-f]+) \*\/$/m;
529
1025
  function regenerateTokensCss() {
530
1026
  const { body, colorsAndTypeVarCount, componentOverrideCount } = renderTokensCss();
@@ -1698,6 +2194,7 @@ data: ${JSON.stringify(state)}
1698
2194
  const isEditorOwnedJson = (file) => file.endsWith(".json") && path3.resolve(file).startsWith(dataDirs.dataDir + path3.sep);
1699
2195
  let buildLogger = null;
1700
2196
  let buildChecked = false;
2197
+ let productionCssLogger = null;
1701
2198
  return {
1702
2199
  name: "theme-file-api",
1703
2200
  config() {
@@ -1712,6 +2209,7 @@ data: ${JSON.stringify(state)}
1712
2209
  },
1713
2210
  configResolved(config) {
1714
2211
  buildLogger = config.command === "build" && opts.checks !== false ? config.logger : null;
2212
+ productionCssLogger = config.command === "build" && opts.productionCss !== false ? config.logger : null;
1715
2213
  },
1716
2214
  async buildStart() {
1717
2215
  if (!buildLogger || buildChecked) return;
@@ -1719,6 +2217,15 @@ data: ${JSON.stringify(state)}
1719
2217
  const failure = await checkBeforeBuild({ root: process.cwd(), logger: buildLogger });
1720
2218
  if (failure) this.error(failure);
1721
2219
  },
2220
+ generateBundle: {
2221
+ // After Vite's own hooks, so the bundle holds the minified CSS and index.html.
2222
+ order: "post",
2223
+ handler(output, bundle) {
2224
+ if (!productionCssLogger) return;
2225
+ const report = rewriteBundle(bundle, opts.productionCss || {}, output);
2226
+ if (report) productionCssLogger.info(formatReport(report));
2227
+ }
2228
+ },
1722
2229
  configureServer(server) {
1723
2230
  legacyLayout = detectLegacyLayout({
1724
2231
  dataDir: dataDirs.dataDir,