@motion-proto/live-tokens 0.56.1 → 0.57.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 (90) hide show
  1. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +2 -2
  2. package/CHANGELOG.md +124 -0
  3. package/README.md +1 -1
  4. package/bin/cli.mjs +2 -1
  5. package/dist-plugin/adjust/index.cjs +1 -0
  6. package/dist-plugin/adjust/index.d.cts +2 -2
  7. package/dist-plugin/adjust/index.d.ts +2 -2
  8. package/dist-plugin/adjust/index.js +1 -1
  9. package/dist-plugin/{chunk-TKZBVIW5.js → chunk-E5QYON4L.js} +59 -2
  10. package/dist-plugin/{chunk-PB2JTK2H.js → chunk-LW4SR7AZ.js} +39 -6
  11. package/dist-plugin/{chunk-YR6GPXW2.js → chunk-XWXIMTWZ.js} +0 -5
  12. package/dist-plugin/{chunk-D3ZVKOR4.js → chunk-Y5CNFSSV.js} +1 -0
  13. package/dist-plugin/{dataPaths-DBN0RPuT.d.cts → dataPaths-CRfD1LdA.d.cts} +3 -0
  14. package/dist-plugin/{dataPaths-DBN0RPuT.d.ts → dataPaths-CRfD1LdA.d.ts} +3 -0
  15. package/dist-plugin/fontPairing/index.cjs +4 -2
  16. package/dist-plugin/fontPairing/index.d.cts +3 -3
  17. package/dist-plugin/fontPairing/index.d.ts +3 -3
  18. package/dist-plugin/fontPairing/index.js +4 -3
  19. package/dist-plugin/generateColorsAndType/index.cjs +22 -9
  20. package/dist-plugin/generateColorsAndType/index.d.cts +2 -2
  21. package/dist-plugin/generateColorsAndType/index.d.ts +2 -2
  22. package/dist-plugin/generateColorsAndType/index.js +8 -5
  23. package/dist-plugin/index.cjs +158 -9
  24. package/dist-plugin/index.d.cts +1 -1
  25. package/dist-plugin/index.d.ts +1 -1
  26. package/dist-plugin/index.js +71 -4
  27. package/dist-plugin/migrateData/index.cjs +1 -0
  28. package/dist-plugin/migrateData/index.d.cts +1 -1
  29. package/dist-plugin/migrateData/index.d.ts +1 -1
  30. package/dist-plugin/migrateData/index.js +1 -1
  31. package/dist-plugin/{themeTypes-BAqtv4XO.d.cts → themeTypes-DSV3Zisf.d.cts} +12 -1
  32. package/dist-plugin/{themeTypes-BAqtv4XO.d.ts → themeTypes-DSV3Zisf.d.ts} +12 -1
  33. package/dist-plugin/tokensCssMigrations/index.cjs +58 -1
  34. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  35. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  36. package/dist-plugin/tokensCssMigrations/index.js +3 -3
  37. package/package.json +3 -1
  38. package/src/editor/core/fonts/applyFontPairing.ts +3 -2
  39. package/src/editor/core/fonts/fontLoader.ts +1 -0
  40. package/src/editor/core/fonts/fontMigration.ts +31 -1
  41. package/src/editor/core/palettes/paletteDerivation.ts +9 -2
  42. package/src/editor/core/sketch/maskField.ts +381 -0
  43. package/src/editor/core/sketch/sketchLayer.ts +1028 -0
  44. package/src/editor/core/sketch/sketchPresetService.ts +65 -0
  45. package/src/editor/core/sketch/sketchPresets.ts +328 -0
  46. package/src/editor/core/sketch/sketchStore.ts +190 -0
  47. package/src/editor/core/store/editorPersistence.ts +28 -12
  48. package/src/editor/core/store/editorTypes.ts +4 -1
  49. package/src/editor/core/store/editorViewStore.ts +2 -2
  50. package/src/editor/core/store/gradientSource.ts +15 -1
  51. package/src/editor/core/themes/parsers/gradient.ts +62 -4
  52. package/src/editor/core/themes/slices/gradients.ts +75 -14
  53. package/src/editor/core/themes/themeTypes.ts +6 -1
  54. package/src/editor/docs/Docs.svelte +4 -3
  55. package/src/editor/docs/chapters.ts +1 -0
  56. package/src/editor/docs/content/01-overview.md +2 -1
  57. package/src/editor/docs/content/editing-tokens.md +5 -1
  58. package/src/editor/docs/content/sketch-mode.md +89 -0
  59. package/src/editor/docs/content.generated.ts +3 -2
  60. package/src/editor/overlay/LiveEditorOverlay.svelte +15 -0
  61. package/src/editor/pages/EditorShell.svelte +43 -0
  62. package/src/editor/ui/EditorViewSwitcher.svelte +15 -2
  63. package/src/editor/ui/FontStackEditor.svelte +3 -0
  64. package/src/editor/ui/GradientEditor.svelte +38 -1
  65. package/src/editor/ui/UIReveal.svelte +7 -1
  66. package/src/editor/ui/UISegmentedControl.svelte +4 -8
  67. package/src/editor/ui/sections/GradientsSection.svelte +47 -1
  68. package/src/editor/ui/sketch/SketchDial.svelte +124 -0
  69. package/src/editor/ui/sketch/SketchPreview.svelte +119 -0
  70. package/src/editor/ui/sketch/SketchRange.svelte +185 -0
  71. package/src/editor/ui/sketch/SketchTab.svelte +1263 -0
  72. package/src/live-tokens/data/colors-and-type/autumn.json +17 -0
  73. package/src/live-tokens/data/colors-and-type/default.json +17 -0
  74. package/src/live-tokens/data/colors-and-type/halloween.json +17 -0
  75. package/src/live-tokens/data/colors-and-type/midnight-study.json +17 -0
  76. package/src/live-tokens/data/colors-and-type/ocean.json +17 -0
  77. package/src/live-tokens/data/colors-and-type/royal-velvet.json +17 -0
  78. package/src/live-tokens/data/colors-and-type/sketches.json +2520 -0
  79. package/src/live-tokens/data/colors-and-type/spring-meadow.json +17 -0
  80. package/src/live-tokens/data/colors-and-type/sunset.json +17 -0
  81. package/src/live-tokens/data/themes/autumn.json +17 -0
  82. package/src/live-tokens/data/themes/halloween.json +17 -0
  83. package/src/live-tokens/data/themes/midnight-study.json +17 -0
  84. package/src/live-tokens/data/themes/ocean.json +17 -0
  85. package/src/live-tokens/data/themes/royal-velvet.json +17 -0
  86. package/src/live-tokens/data/themes/sketches.json +3984 -0
  87. package/src/live-tokens/data/themes/spring-meadow.json +17 -0
  88. package/src/live-tokens/data/themes/sunset.json +17 -0
  89. package/src/live-tokens/data/tokens.generated.css +1 -0
  90. package/src/system/styles/tokens.css +15 -2
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  DEFAULT_PALETTE_LIGHTNESS,
3
3
  DEFAULT_PALETTE_SATURATION,
4
+ DIRECTION_ANGLES,
4
5
  PALETTE_SPECS,
5
6
  PALETTE_STEPS,
6
7
  SCALES,
@@ -14,7 +15,7 @@ import {
14
15
  scaleToCssVar,
15
16
  setCurveAnchor,
16
17
  syncBaseAnchor
17
- } from "../chunk-PB2JTK2H.js";
18
+ } from "../chunk-LW4SR7AZ.js";
18
19
  import {
19
20
  CURRENT_COLORS_AND_TYPE_SCHEMA_VERSION,
20
21
  CURRENT_COMPONENT_SCHEMA_VERSION
@@ -25,11 +26,11 @@ import {
25
26
  hexToOklch,
26
27
  oklchToHex,
27
28
  oklchToHexClamped
28
- } from "../chunk-YR6GPXW2.js";
29
+ } from "../chunk-XWXIMTWZ.js";
29
30
  import {
30
31
  readLiveTokensConfig,
31
32
  resolveDataDirs
32
- } from "../chunk-D3ZVKOR4.js";
33
+ } from "../chunk-Y5CNFSSV.js";
33
34
 
34
35
  // src/editor/core/store/editorCore.ts
35
36
  import { writable, derived, get } from "svelte/store";
@@ -50,7 +51,8 @@ function makeDefaultGradients() {
50
51
  {
51
52
  variable: "--gradient-1",
52
53
  type: "linear",
53
- angle: 90,
54
+ angle: DIRECTION_ANGLES["to right"],
55
+ direction: "to right",
54
56
  stops: [
55
57
  { position: 0, color: "--color-brand-500" },
56
58
  { position: 100, color: "--color-accent-500" }
@@ -68,7 +70,8 @@ function makeDefaultGradients() {
68
70
  {
69
71
  variable: "--gradient-3",
70
72
  type: "linear",
71
- angle: 90,
73
+ angle: DIRECTION_ANGLES["to right"],
74
+ direction: "to right",
72
75
  stops: [
73
76
  { position: 0, color: "--color-success-500" },
74
77
  { position: 100, color: "--color-info-500" }
@@ -64,6 +64,20 @@ function formatColorOpacity(name, opacity) {
64
64
  }
65
65
 
66
66
  // src/editor/core/themes/parsers/gradient.ts
67
+ var LINEAR_DIRECTIONS = [
68
+ "to top",
69
+ "to top right",
70
+ "to right",
71
+ "to bottom right",
72
+ "to bottom",
73
+ "to bottom left",
74
+ "to left",
75
+ "to top left"
76
+ ];
77
+ function parseDirection(raw) {
78
+ const norm = raw.trim().toLowerCase().replace(/\s+/g, " ");
79
+ return LINEAR_DIRECTIONS.includes(norm) ? norm : null;
80
+ }
67
81
  var RADIAL_BASE_PX = 100;
68
82
  function formatStopColor(s) {
69
83
  const opacity = s.opacity ?? 100;
@@ -91,11 +105,15 @@ function formatGradientValue(v) {
91
105
  return first ? formatStopColor(first) : "transparent";
92
106
  }
93
107
  const stops = formatGradientStops(v.stops);
94
- if (v.type === "linear") return `linear-gradient(${v.angle}deg, ${stops})`;
108
+ if (v.type === "linear") {
109
+ return `linear-gradient(${v.direction ?? `${v.angle}deg`}, ${stops})`;
110
+ }
95
111
  return `radial-gradient(${formatRadialShape(v)} at ${v.centerX ?? 50}% 50%, ${stops})`;
96
112
  }
97
113
  var NUM = String.raw`-?\d+(?:\.\d+)?`;
98
- var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${NUM})deg\s*,\s*(.+)\)$`, "i");
114
+ var HEADING = String.raw`${NUM}deg|to\s+\w+(?:\s+\w+)?`;
115
+ var LINEAR_RE = new RegExp(String.raw`^linear-gradient\(\s*(${HEADING})\s*,\s*(.+)\)$`, "i");
116
+ var DEG_RE = new RegExp(String.raw`^(${NUM})deg$`, "i");
99
117
  var RADIAL_RE = new RegExp(
100
118
  String.raw`^radial-gradient\(\s*(circle|circle\s+${NUM}px|ellipse\s+${NUM}px\s+${NUM}px)\s+at\s+(${NUM})%\s+50%\s*,\s*(.+)\)$`,
101
119
  "i"
@@ -154,8 +172,12 @@ function parseGradientValue(value) {
154
172
  const css = value.trim();
155
173
  const linear = css.match(LINEAR_RE);
156
174
  if (linear) {
175
+ const deg = linear[1].match(DEG_RE);
176
+ const direction = deg ? null : parseDirection(linear[1]);
177
+ if (!deg && !direction) return null;
157
178
  const stops2 = parseStops(linear[2]);
158
- return stops2 && { type: "linear", angle: parseFloat(linear[1]), stops: stops2 };
179
+ if (!stops2) return null;
180
+ return deg ? { type: "linear", angle: parseFloat(deg[1]), stops: stops2 } : { type: "linear", angle: 0, direction, stops: stops2 };
159
181
  }
160
182
  const radial = css.match(RADIAL_RE);
161
183
  if (!radial) return null;
@@ -259,10 +281,6 @@ function oklchToCss(l, c, h) {
259
281
  const cs = trim(c, 4);
260
282
  return `oklch(${trim(l, 4)} ${cs} ${cs === "0" ? "0" : trim(h, 2)})`;
261
283
  }
262
- function oklchToCssClamped(l, c, h) {
263
- const g = gamutClamp(l, c, h);
264
- return oklchToCss(g.l, g.c, g.h);
265
- }
266
284
  var OKLCH_RE = /^oklch\(\s*([\d.]+)(%?)\s+([\d.]+)(%?)\s+([\d.]+)(?:deg)?\s*\)$/i;
267
285
  var HEX6_RE = /^#[0-9a-f]{6}$/i;
268
286
  function parseOklchCss(value) {
@@ -456,7 +474,7 @@ function scaleStepToX(step, scale) {
456
474
  return scale.steps.length > 1 ? idx / (scale.steps.length - 1) * 100 : 50;
457
475
  }
458
476
  function serializeDerivedValue(value) {
459
- return value.kind === "raw" ? value.css : oklchToCssClamped(value.l, value.c, value.h);
477
+ return value.kind === "raw" ? value.css : oklchToCss(value.l, value.c, value.h);
460
478
  }
461
479
  function computePaletteOklch(index, base, lightnessCurve, saturationCurve, curveOffset, hueCurve) {
462
480
  const { c: baseC } = base;
@@ -1977,6 +1995,7 @@ function resolveDataDirs(opts = {}) {
1977
1995
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path2.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path2.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
1978
1996
  componentConfigsDir: opts.componentConfigsDir ? import_path2.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path2.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
1979
1997
  themesDir: opts.themesDir ? import_path2.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path2.default.resolve(fileConfig.themesDir) : sub("themes"),
1998
+ sketchPresetsDir: sub("sketch-presets"),
1980
1999
  legacyManifestsDir: fileConfig.manifestsDir ? import_path2.default.resolve(fileConfig.manifestsDir) : void 0
1981
2000
  };
1982
2001
  }
@@ -2040,11 +2059,17 @@ function detectLegacyLayout(input) {
2040
2059
  // vite-plugin/tokensCssMigrations/cssTokenOps.ts
2041
2060
  var DECL_RE = /(^|[\s;{])(--[a-z0-9-]+)\s*:/gi;
2042
2061
  var REF_RE = /var\(\s*(--[a-z0-9-]+)/gi;
2062
+ var DECL_VALUE_RE = /(^|[\s;{])(--[a-z0-9-]+)\s*:([^;}]*)/gi;
2043
2063
  function collectDefinedTokens(css) {
2044
2064
  const out = /* @__PURE__ */ new Set();
2045
2065
  for (const m of css.matchAll(DECL_RE)) out.add(m[2]);
2046
2066
  return out;
2047
2067
  }
2068
+ function collectTokenValues(css) {
2069
+ const out = /* @__PURE__ */ new Map();
2070
+ for (const m of css.matchAll(DECL_VALUE_RE)) out.set(m[2], m[3].trim());
2071
+ return out;
2072
+ }
2048
2073
  function collectReferencedTokens(css) {
2049
2074
  const out = /* @__PURE__ */ new Set();
2050
2075
  for (const m of css.matchAll(REF_RE)) out.add(m[1]);
@@ -2392,6 +2417,61 @@ var tokensCssMigration_2026_08_19_oklchColorValues = {
2392
2417
  }
2393
2418
  };
2394
2419
 
2420
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-25-editorial-type-role.ts
2421
+ var STACK = [{ name: "--font-editorial", value: "var(--font-sans)" }];
2422
+ var BUNDLE2 = [
2423
+ { name: "--editorial-font-family", value: "var(--font-editorial)" },
2424
+ { name: "--editorial-font-size", value: "var(--font-size-md)" },
2425
+ { name: "--editorial-font-weight", value: "var(--font-weight-normal)" },
2426
+ { name: "--editorial-line-height", value: "var(--line-height-normal)" },
2427
+ { name: "--editorial-letter-spacing", value: "var(--letter-spacing-normal)" }
2428
+ ];
2429
+ var tokensCssMigration_2026_08_25_editorialTypeRole = {
2430
+ id: "2026-08-25-editorial-type-role",
2431
+ kind: "additive",
2432
+ description: "Add the editorial type role (--font-editorial and the --editorial-* bundle)",
2433
+ apply(css) {
2434
+ const withStack = ensureScale(css, {
2435
+ anchorPrefixes: ["--font-mono", "--font-serif", "--font-sans", "--font-display"],
2436
+ entries: STACK
2437
+ });
2438
+ return ensureScale(withStack, {
2439
+ sectionComment: "Editorial \u2014 the long-reading role, body face until repointed",
2440
+ anchorPrefixes: [
2441
+ "--eyebrow-",
2442
+ "--code-",
2443
+ "--body-",
2444
+ "--heading-",
2445
+ "--letter-spacing-",
2446
+ "--line-height-",
2447
+ "--font-weight-",
2448
+ "--font-size-",
2449
+ "--font-"
2450
+ ],
2451
+ entries: BUNDLE2
2452
+ });
2453
+ }
2454
+ };
2455
+
2456
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-26-gradient-stops.ts
2457
+ var STOPS_RE = /^(?:linear|radial)-gradient\(\s*[^,]+,\s*(.+)\)$/i;
2458
+ var tokensCssMigration_2026_08_26_gradientStops = {
2459
+ id: "2026-08-26-gradient-stops",
2460
+ kind: "additive",
2461
+ description: "Add --gradient-N-stops companions to the gradient slots",
2462
+ apply(css) {
2463
+ const values = collectTokenValues(css);
2464
+ const entries = [];
2465
+ for (const [name, value] of values) {
2466
+ if (!/^--gradient-\d+$/.test(name)) continue;
2467
+ const stops = value.match(STOPS_RE);
2468
+ if (stops) entries.push({ name: `${name}-stops`, value: stops[1].trim() });
2469
+ }
2470
+ if (entries.length === 0) return css;
2471
+ return ensureScale(css, { anchorPrefixes: ["--gradient-"], entries });
2472
+ }
2473
+ };
2474
+
2395
2475
  // vite-plugin/tokensCssMigrations/index.ts
2396
2476
  var TOKENS_CSS_MIGRATIONS = [
2397
2477
  tokensCssMigration_2026_05_29_typographyScaleAdditions,
@@ -2401,7 +2481,9 @@ var TOKENS_CSS_MIGRATIONS = [
2401
2481
  tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
2402
2482
  tokensCssMigration_2026_07_20_lineHeightRename,
2403
2483
  tokensCssMigration_2026_07_20_semanticTextStyles,
2404
- tokensCssMigration_2026_08_19_oklchColorValues
2484
+ tokensCssMigration_2026_08_19_oklchColorValues,
2485
+ tokensCssMigration_2026_08_25_editorialTypeRole,
2486
+ tokensCssMigration_2026_08_26_gradientStops
2405
2487
  ];
2406
2488
  function runTokensCssMigrations(css) {
2407
2489
  return foldMigrations(css, () => true);
@@ -2502,6 +2584,7 @@ function themeFileApi(opts) {
2502
2584
  const COLORS_AND_TYPE_DIR = dataDirs.colorsAndTypeDir;
2503
2585
  const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
2504
2586
  const THEMES_DIR = dataDirs.themesDir;
2587
+ const SKETCH_PRESETS_DIR = dataDirs.sketchPresetsDir;
2505
2588
  const CSS_PATH = import_path4.default.resolve(opts.tokensCssPath);
2506
2589
  const GENERATED_CSS_PATH = opts.tokensGeneratedCssPath ? import_path4.default.resolve(opts.tokensGeneratedCssPath) : import_path4.default.join(dataDirs.dataDir, "tokens.generated.css");
2507
2590
  const FONTS_CSS_PATH = opts.fontsCssPath ? import_path4.default.resolve(opts.fontsCssPath) : import_path4.default.join(import_path4.default.dirname(CSS_PATH), "fonts.css");
@@ -2559,6 +2642,7 @@ function themeFileApi(opts) {
2559
2642
  dir: THEMES_DIR,
2560
2643
  packageDir: packageThemesDir
2561
2644
  });
2645
+ const sketchPresetsResource = versionedFileResourceServer({ dir: SKETCH_PRESETS_DIR });
2562
2646
  function ensureColorsAndTypeDir() {
2563
2647
  colorsAndTypeResource.ensureDir();
2564
2648
  }
@@ -3124,6 +3208,8 @@ ${lines.join("\n")}
3124
3208
  const THEME_EXPORT_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)/export$`);
3125
3209
  const THEME_IMPORT_ROUTE = `${API_BASE}/themes/import`;
3126
3210
  const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
3211
+ const SKETCH_PRESETS_ROUTE = `${API_BASE}/sketch-presets`;
3212
+ const SKETCH_PRESET_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-presets/([a-z0-9\\-_]+)$`);
3127
3213
  const PRODUCTION_ROUTE = `${API_BASE}/production`;
3128
3214
  async function handleListColorsAndType(_ctx) {
3129
3215
  const files = colorsAndTypeResource.listNames().map((fileName) => {
@@ -3655,6 +3741,61 @@ ${lines.join("\n")}
3655
3741
  }
3656
3742
  });
3657
3743
  }
3744
+ async function handleListSketchPresets({ res }) {
3745
+ const files = sketchPresetsResource.listNames().map((fileName) => {
3746
+ let data = null;
3747
+ try {
3748
+ data = sketchPresetsResource.readJson(fileName);
3749
+ } catch {
3750
+ }
3751
+ return {
3752
+ name: data?.name || fileName,
3753
+ fileName,
3754
+ updatedAt: data?.updatedAt || ""
3755
+ };
3756
+ });
3757
+ jsonResponse(res, 200, { files });
3758
+ }
3759
+ async function handleSketchPresetByName({ params, req, res }) {
3760
+ const [fileName] = params;
3761
+ if (rejectReservedName(res, fileName)) return;
3762
+ const filePath = sketchPresetsResource.filePath(fileName);
3763
+ if (req.method === "GET") {
3764
+ const raw = sketchPresetsResource.readJson(fileName);
3765
+ if (!raw) {
3766
+ jsonResponse(res, 404, { error: "Not found" });
3767
+ return;
3768
+ }
3769
+ jsonResponse(res, 200, { ...raw, _fileName: fileName });
3770
+ return;
3771
+ }
3772
+ if (req.method === "PUT") {
3773
+ const body = JSON.parse(await readBody(req));
3774
+ if (!body || typeof body !== "object" || !body.settings || typeof body.settings !== "object") {
3775
+ jsonResponse(res, 400, { error: "A sketch preset needs a settings object" });
3776
+ return;
3777
+ }
3778
+ body.name = String(body.name || fileName);
3779
+ body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
3780
+ if (import_fs4.default.existsSync(filePath)) {
3781
+ try {
3782
+ const existing = JSON.parse(import_fs4.default.readFileSync(filePath, "utf-8"));
3783
+ if (existing.createdAt) body.createdAt = existing.createdAt;
3784
+ } catch {
3785
+ }
3786
+ }
3787
+ if (!body.createdAt) body.createdAt = body.updatedAt;
3788
+ sketchPresetsResource.ensureDir();
3789
+ import_fs4.default.writeFileSync(filePath, JSON.stringify(body, null, 2));
3790
+ jsonResponse(res, 200, { ok: true, fileName });
3791
+ return;
3792
+ }
3793
+ if (req.method === "DELETE") {
3794
+ if (import_fs4.default.existsSync(filePath)) import_fs4.default.unlinkSync(filePath);
3795
+ jsonResponse(res, 200, { ok: true });
3796
+ return;
3797
+ }
3798
+ }
3658
3799
  function methodNotAllowed({ res }) {
3659
3800
  jsonResponse(res, 405, { error: "Method not allowed" });
3660
3801
  }
@@ -3731,6 +3872,14 @@ ${lines.join("\n")}
3731
3872
  { method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
3732
3873
  { method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
3733
3874
  // Production — the whole-look adopt door
3875
+ { method: "GET", pattern: SKETCH_PRESETS_ROUTE, handler: handleListSketchPresets },
3876
+ { method: "PUT", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
3877
+ { method: "POST", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
3878
+ { method: "DELETE", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
3879
+ { method: "GET", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
3880
+ { method: "PUT", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
3881
+ { method: "DELETE", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
3882
+ { method: "POST", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: methodNotAllowed },
3734
3883
  { method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptLook },
3735
3884
  { method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
3736
3885
  { method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from 'vite';
2
2
  export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.cjs';
3
- import './dataPaths-DBN0RPuT.cjs';
3
+ import './dataPaths-CRfD1LdA.cjs';
4
4
 
5
5
  interface ThemeFileApiOptions {
6
6
  tokensCssPath: string;
@@ -1,6 +1,6 @@
1
1
  import { Plugin } from 'vite';
2
2
  export { ComponentSource, MissingToken, RunResult, TOKENS_CSS_MIGRATIONS, TokensCssMigration, ValidateInput, runTokensCssMigrations, validateTokensCss } from './tokensCssMigrations/index.js';
3
- import './dataPaths-DBN0RPuT.js';
3
+ import './dataPaths-CRfD1LdA.js';
4
4
 
5
5
  interface ThemeFileApiOptions {
6
6
  tokensCssPath: string;
@@ -5,7 +5,7 @@ import {
5
5
  parseGradientValue,
6
6
  reconcilePalettesFromCssVars,
7
7
  sanitizeFileName
8
- } from "./chunk-PB2JTK2H.js";
8
+ } from "./chunk-LW4SR7AZ.js";
9
9
  import {
10
10
  THEME_SCHEMA_VERSION,
11
11
  detectLegacyLayout,
@@ -23,13 +23,13 @@ import {
23
23
  runAdditiveTokensCssMigrations,
24
24
  runTokensCssMigrations,
25
25
  validateTokensCss
26
- } from "./chunk-TKZBVIW5.js";
26
+ } from "./chunk-E5QYON4L.js";
27
27
  import {
28
28
  hexToOklch
29
- } from "./chunk-YR6GPXW2.js";
29
+ } from "./chunk-XWXIMTWZ.js";
30
30
  import {
31
31
  resolveDataDirs
32
- } from "./chunk-D3ZVKOR4.js";
32
+ } from "./chunk-Y5CNFSSV.js";
33
33
 
34
34
  // vite-plugin/themeFileApi.ts
35
35
  import fs from "fs";
@@ -137,6 +137,7 @@ function themeFileApi(opts) {
137
137
  const COLORS_AND_TYPE_DIR = dataDirs.colorsAndTypeDir;
138
138
  const COMPONENT_CONFIGS_DIR = dataDirs.componentConfigsDir;
139
139
  const THEMES_DIR = dataDirs.themesDir;
140
+ const SKETCH_PRESETS_DIR = dataDirs.sketchPresetsDir;
140
141
  const CSS_PATH = path.resolve(opts.tokensCssPath);
141
142
  const GENERATED_CSS_PATH = opts.tokensGeneratedCssPath ? path.resolve(opts.tokensGeneratedCssPath) : path.join(dataDirs.dataDir, "tokens.generated.css");
142
143
  const FONTS_CSS_PATH = opts.fontsCssPath ? path.resolve(opts.fontsCssPath) : path.join(path.dirname(CSS_PATH), "fonts.css");
@@ -194,6 +195,7 @@ function themeFileApi(opts) {
194
195
  dir: THEMES_DIR,
195
196
  packageDir: packageThemesDir
196
197
  });
198
+ const sketchPresetsResource = versionedFileResourceServer({ dir: SKETCH_PRESETS_DIR });
197
199
  function ensureColorsAndTypeDir() {
198
200
  colorsAndTypeResource.ensureDir();
199
201
  }
@@ -759,6 +761,8 @@ ${lines.join("\n")}
759
761
  const THEME_EXPORT_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)/export$`);
760
762
  const THEME_IMPORT_ROUTE = `${API_BASE}/themes/import`;
761
763
  const THEME_BY_NAME_REGEX = new RegExp(`^${escapedBase}/themes/([a-z0-9\\-_]+)$`);
764
+ const SKETCH_PRESETS_ROUTE = `${API_BASE}/sketch-presets`;
765
+ const SKETCH_PRESET_BY_NAME_REGEX = new RegExp(`^${escapedBase}/sketch-presets/([a-z0-9\\-_]+)$`);
762
766
  const PRODUCTION_ROUTE = `${API_BASE}/production`;
763
767
  async function handleListColorsAndType(_ctx) {
764
768
  const files = colorsAndTypeResource.listNames().map((fileName) => {
@@ -1290,6 +1294,61 @@ ${lines.join("\n")}
1290
1294
  }
1291
1295
  });
1292
1296
  }
1297
+ async function handleListSketchPresets({ res }) {
1298
+ const files = sketchPresetsResource.listNames().map((fileName) => {
1299
+ let data = null;
1300
+ try {
1301
+ data = sketchPresetsResource.readJson(fileName);
1302
+ } catch {
1303
+ }
1304
+ return {
1305
+ name: data?.name || fileName,
1306
+ fileName,
1307
+ updatedAt: data?.updatedAt || ""
1308
+ };
1309
+ });
1310
+ jsonResponse(res, 200, { files });
1311
+ }
1312
+ async function handleSketchPresetByName({ params, req, res }) {
1313
+ const [fileName] = params;
1314
+ if (rejectReservedName(res, fileName)) return;
1315
+ const filePath = sketchPresetsResource.filePath(fileName);
1316
+ if (req.method === "GET") {
1317
+ const raw = sketchPresetsResource.readJson(fileName);
1318
+ if (!raw) {
1319
+ jsonResponse(res, 404, { error: "Not found" });
1320
+ return;
1321
+ }
1322
+ jsonResponse(res, 200, { ...raw, _fileName: fileName });
1323
+ return;
1324
+ }
1325
+ if (req.method === "PUT") {
1326
+ const body = JSON.parse(await readBody(req));
1327
+ if (!body || typeof body !== "object" || !body.settings || typeof body.settings !== "object") {
1328
+ jsonResponse(res, 400, { error: "A sketch preset needs a settings object" });
1329
+ return;
1330
+ }
1331
+ body.name = String(body.name || fileName);
1332
+ body.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
1333
+ if (fs.existsSync(filePath)) {
1334
+ try {
1335
+ const existing = JSON.parse(fs.readFileSync(filePath, "utf-8"));
1336
+ if (existing.createdAt) body.createdAt = existing.createdAt;
1337
+ } catch {
1338
+ }
1339
+ }
1340
+ if (!body.createdAt) body.createdAt = body.updatedAt;
1341
+ sketchPresetsResource.ensureDir();
1342
+ fs.writeFileSync(filePath, JSON.stringify(body, null, 2));
1343
+ jsonResponse(res, 200, { ok: true, fileName });
1344
+ return;
1345
+ }
1346
+ if (req.method === "DELETE") {
1347
+ if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
1348
+ jsonResponse(res, 200, { ok: true });
1349
+ return;
1350
+ }
1351
+ }
1293
1352
  function methodNotAllowed({ res }) {
1294
1353
  jsonResponse(res, 405, { error: "Method not allowed" });
1295
1354
  }
@@ -1366,6 +1425,14 @@ ${lines.join("\n")}
1366
1425
  { method: "PUT", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
1367
1426
  { method: "DELETE", pattern: THEME_BY_NAME_REGEX, handler: handleThemeByName },
1368
1427
  // Production — the whole-look adopt door
1428
+ { method: "GET", pattern: SKETCH_PRESETS_ROUTE, handler: handleListSketchPresets },
1429
+ { method: "PUT", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1430
+ { method: "POST", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1431
+ { method: "DELETE", pattern: SKETCH_PRESETS_ROUTE, handler: methodNotAllowed },
1432
+ { method: "GET", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1433
+ { method: "PUT", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1434
+ { method: "DELETE", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: handleSketchPresetByName },
1435
+ { method: "POST", pattern: SKETCH_PRESET_BY_NAME_REGEX, handler: methodNotAllowed },
1369
1436
  { method: "PUT", pattern: PRODUCTION_ROUTE, handler: handleAdoptLook },
1370
1437
  { method: "GET", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
1371
1438
  { method: "POST", pattern: PRODUCTION_ROUTE, handler: methodNotAllowed },
@@ -1659,6 +1659,7 @@ function resolveDataDirs(opts = {}) {
1659
1659
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path4.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path4.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
1660
1660
  componentConfigsDir: opts.componentConfigsDir ? import_path4.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path4.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
1661
1661
  themesDir: opts.themesDir ? import_path4.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path4.default.resolve(fileConfig.themesDir) : sub("themes"),
1662
+ sketchPresetsDir: sub("sketch-presets"),
1662
1663
  legacyManifestsDir: fileConfig.manifestsDir ? import_path4.default.resolve(fileConfig.manifestsDir) : void 0
1663
1664
  };
1664
1665
  }
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.cjs';
1
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
2
2
 
3
3
  interface LegacyRename {
4
4
  from: string;
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DBN0RPuT.js';
1
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
2
2
 
3
3
  interface LegacyRename {
4
4
  from: string;
@@ -14,7 +14,7 @@ import {
14
14
  import {
15
15
  readLiveTokensConfig,
16
16
  resolveDataDirs
17
- } from "../chunk-D3ZVKOR4.js";
17
+ } from "../chunk-Y5CNFSSV.js";
18
18
 
19
19
  // vite-plugin/migrateData/migrateData.ts
20
20
  import fs from "fs";
@@ -7,6 +7,13 @@ interface CurveAnchor {
7
7
  outDy: number;
8
8
  }
9
9
 
10
+ /** The `to <side-or-corner>` keywords CSS accepts in place of an angle. A
11
+ * keyword angles the gradient line off the box's own diagonal, so it tracks
12
+ * the element's aspect ratio where a fixed angle cannot: `to bottom right`
13
+ * reads as ~95deg across a wide heading and ~111deg once that heading wraps.
14
+ * That is the whole reason to carry them — a degree cannot express it. */
15
+ declare const LINEAR_DIRECTIONS: readonly ["to top", "to top right", "to right", "to bottom right", "to bottom", "to bottom left", "to left", "to top left"];
16
+ type LinearDirection = (typeof LINEAR_DIRECTIONS)[number];
10
17
  interface GradientStop$1 {
11
18
  /** 0–100 percentage along the gradient axis. */
12
19
  position: number;
@@ -25,6 +32,10 @@ interface GradientStop$1 {
25
32
  interface GradientValue {
26
33
  type: 'linear' | 'radial' | 'solid' | 'none';
27
34
  angle: number;
35
+ /** Emitted in place of `angle` on a `linear` gradient. `angle` is retained
36
+ * underneath so clearing the direction restores the user's degrees, the
37
+ * same way a radial keeps its angle. */
38
+ direction?: LinearDirection;
28
39
  radius?: number;
29
40
  centerX?: number;
30
41
  aspectX?: number;
@@ -136,7 +147,7 @@ interface FontSource {
136
147
  }
137
148
  type SystemCascadePreset = 'system-ui-sans' | 'system-ui-serif' | 'system-ui-mono';
138
149
  type GenericFamily = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy';
139
- type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono';
150
+ type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono' | '--font-editorial';
140
151
  type FontStackSlot = {
141
152
  kind: 'project';
142
153
  familyId: string;
@@ -7,6 +7,13 @@ interface CurveAnchor {
7
7
  outDy: number;
8
8
  }
9
9
 
10
+ /** The `to <side-or-corner>` keywords CSS accepts in place of an angle. A
11
+ * keyword angles the gradient line off the box's own diagonal, so it tracks
12
+ * the element's aspect ratio where a fixed angle cannot: `to bottom right`
13
+ * reads as ~95deg across a wide heading and ~111deg once that heading wraps.
14
+ * That is the whole reason to carry them — a degree cannot express it. */
15
+ declare const LINEAR_DIRECTIONS: readonly ["to top", "to top right", "to right", "to bottom right", "to bottom", "to bottom left", "to left", "to top left"];
16
+ type LinearDirection = (typeof LINEAR_DIRECTIONS)[number];
10
17
  interface GradientStop$1 {
11
18
  /** 0–100 percentage along the gradient axis. */
12
19
  position: number;
@@ -25,6 +32,10 @@ interface GradientStop$1 {
25
32
  interface GradientValue {
26
33
  type: 'linear' | 'radial' | 'solid' | 'none';
27
34
  angle: number;
35
+ /** Emitted in place of `angle` on a `linear` gradient. `angle` is retained
36
+ * underneath so clearing the direction restores the user's degrees, the
37
+ * same way a radial keeps its angle. */
38
+ direction?: LinearDirection;
28
39
  radius?: number;
29
40
  centerX?: number;
30
41
  aspectX?: number;
@@ -136,7 +147,7 @@ interface FontSource {
136
147
  }
137
148
  type SystemCascadePreset = 'system-ui-sans' | 'system-ui-serif' | 'system-ui-mono';
138
149
  type GenericFamily = 'sans-serif' | 'serif' | 'monospace' | 'cursive' | 'fantasy';
139
- type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono';
150
+ type FontStackVariable = '--font-display' | '--font-sans' | '--font-serif' | '--font-mono' | '--font-editorial';
140
151
  type FontStackSlot = {
141
152
  kind: 'project';
142
153
  familyId: string;
@@ -462,6 +462,61 @@ var tokensCssMigration_2026_08_19_oklchColorValues = {
462
462
  }
463
463
  };
464
464
 
465
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-25-editorial-type-role.ts
466
+ var STACK = [{ name: "--font-editorial", value: "var(--font-sans)" }];
467
+ var BUNDLE2 = [
468
+ { name: "--editorial-font-family", value: "var(--font-editorial)" },
469
+ { name: "--editorial-font-size", value: "var(--font-size-md)" },
470
+ { name: "--editorial-font-weight", value: "var(--font-weight-normal)" },
471
+ { name: "--editorial-line-height", value: "var(--line-height-normal)" },
472
+ { name: "--editorial-letter-spacing", value: "var(--letter-spacing-normal)" }
473
+ ];
474
+ var tokensCssMigration_2026_08_25_editorialTypeRole = {
475
+ id: "2026-08-25-editorial-type-role",
476
+ kind: "additive",
477
+ description: "Add the editorial type role (--font-editorial and the --editorial-* bundle)",
478
+ apply(css) {
479
+ const withStack = ensureScale(css, {
480
+ anchorPrefixes: ["--font-mono", "--font-serif", "--font-sans", "--font-display"],
481
+ entries: STACK
482
+ });
483
+ return ensureScale(withStack, {
484
+ sectionComment: "Editorial \u2014 the long-reading role, body face until repointed",
485
+ anchorPrefixes: [
486
+ "--eyebrow-",
487
+ "--code-",
488
+ "--body-",
489
+ "--heading-",
490
+ "--letter-spacing-",
491
+ "--line-height-",
492
+ "--font-weight-",
493
+ "--font-size-",
494
+ "--font-"
495
+ ],
496
+ entries: BUNDLE2
497
+ });
498
+ }
499
+ };
500
+
501
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-26-gradient-stops.ts
502
+ var STOPS_RE = /^(?:linear|radial)-gradient\(\s*[^,]+,\s*(.+)\)$/i;
503
+ var tokensCssMigration_2026_08_26_gradientStops = {
504
+ id: "2026-08-26-gradient-stops",
505
+ kind: "additive",
506
+ description: "Add --gradient-N-stops companions to the gradient slots",
507
+ apply(css) {
508
+ const values = collectTokenValues(css);
509
+ const entries = [];
510
+ for (const [name, value] of values) {
511
+ if (!/^--gradient-\d+$/.test(name)) continue;
512
+ const stops = value.match(STOPS_RE);
513
+ if (stops) entries.push({ name: `${name}-stops`, value: stops[1].trim() });
514
+ }
515
+ if (entries.length === 0) return css;
516
+ return ensureScale(css, { anchorPrefixes: ["--gradient-"], entries });
517
+ }
518
+ };
519
+
465
520
  // vite-plugin/files/dataPaths.ts
466
521
  var import_fs = __toESM(require("fs"), 1);
467
522
  var import_path = __toESM(require("path"), 1);
@@ -505,7 +560,9 @@ var TOKENS_CSS_MIGRATIONS = [
505
560
  tokensCssMigration_2026_06_04_easingColorAndTypescaleAdditions,
506
561
  tokensCssMigration_2026_07_20_lineHeightRename,
507
562
  tokensCssMigration_2026_07_20_semanticTextStyles,
508
- tokensCssMigration_2026_08_19_oklchColorValues
563
+ tokensCssMigration_2026_08_19_oklchColorValues,
564
+ tokensCssMigration_2026_08_25_editorialTypeRole,
565
+ tokensCssMigration_2026_08_26_gradientStops
509
566
  ];
510
567
  function runTokensCssMigrations(css) {
511
568
  return foldMigrations(css, () => true);
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-DBN0RPuT.cjs';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-CRfD1LdA.cjs';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -1,4 +1,4 @@
1
- export { r as readLiveTokensConfig } from '../dataPaths-DBN0RPuT.js';
1
+ export { r as readLiveTokensConfig } from '../dataPaths-CRfD1LdA.js';
2
2
 
3
3
  /**
4
4
  * A `tokens-css` migration: a pure, idempotent transform on the text of a
@@ -13,11 +13,11 @@ import {
13
13
  runTokensCssMigrations,
14
14
  semverBumpType,
15
15
  validateTokensCss
16
- } from "../chunk-TKZBVIW5.js";
17
- import "../chunk-YR6GPXW2.js";
16
+ } from "../chunk-E5QYON4L.js";
17
+ import "../chunk-XWXIMTWZ.js";
18
18
  import {
19
19
  readLiveTokensConfig
20
- } from "../chunk-D3ZVKOR4.js";
20
+ } from "../chunk-Y5CNFSSV.js";
21
21
  export {
22
22
  TOKENS_CSS_MIGRATIONS,
23
23
  collectDefinedTokens,