@motion-proto/live-tokens 0.62.0 → 0.64.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 (71) hide show
  1. package/.claude/skills/live-tokens-adjust-geometry/SKILL.md +4 -4
  2. package/.claude/skills/live-tokens-build-page/SKILL.md +21 -3
  3. package/.claude/skills/live-tokens-create-component/SKILL.md +16 -48
  4. package/.claude/skills/live-tokens-create-component/references/fixed-overlays.md +10 -1
  5. package/.claude/skills/live-tokens-create-component/references/intrinsics.md +7 -5
  6. package/.claude/skills/live-tokens-create-component/references/sketch-mode.md +1 -1
  7. package/.claude/skills/live-tokens-create-component/references/token-naming.md +50 -0
  8. package/.claude/skills/live-tokens-generate-theme/SKILL.md +16 -9
  9. package/.claude/skills/live-tokens-pair-fonts/SKILL.md +8 -6
  10. package/.claude/skills/live-tokens-pick-component/SKILL.md +19 -5
  11. package/CHANGELOG.md +100 -0
  12. package/bin/migrate.mjs +6 -2
  13. package/dist-plugin/adjust/index.cjs +1 -1
  14. package/dist-plugin/adjust/index.d.cts +1 -1
  15. package/dist-plugin/adjust/index.d.ts +1 -1
  16. package/dist-plugin/adjust/index.js +1 -1
  17. package/dist-plugin/{chunk-232GZGQU.js → chunk-NDJJORKJ.js} +342 -5
  18. package/dist-plugin/{chunk-Y5CNFSSV.js → chunk-RVE3MNKM.js} +1 -1
  19. package/dist-plugin/{chunk-OPYOK2CA.js → chunk-ZHPX7ZYQ.js} +39 -1
  20. package/dist-plugin/{dataPaths-CRfD1LdA.d.ts → dataPaths-DZUzVv8H.d.cts} +3 -3
  21. package/dist-plugin/{dataPaths-CRfD1LdA.d.cts → dataPaths-DZUzVv8H.d.ts} +3 -3
  22. package/dist-plugin/fontPairing/index.cjs +32 -8
  23. package/dist-plugin/fontPairing/index.d.cts +1 -1
  24. package/dist-plugin/fontPairing/index.d.ts +1 -1
  25. package/dist-plugin/fontPairing/index.js +32 -8
  26. package/dist-plugin/generateColorsAndType/index.cjs +1 -1
  27. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  28. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  29. package/dist-plugin/generateColorsAndType/index.js +1 -1
  30. package/dist-plugin/index.cjs +417 -34
  31. package/dist-plugin/index.d.cts +1 -1
  32. package/dist-plugin/index.d.ts +1 -1
  33. package/dist-plugin/index.js +31 -23
  34. package/dist-plugin/migrateData/index.cjs +350 -9
  35. package/dist-plugin/migrateData/index.d.cts +1 -1
  36. package/dist-plugin/migrateData/index.d.ts +1 -1
  37. package/dist-plugin/migrateData/index.js +9 -5
  38. package/dist-plugin/tokensCssMigrations/index.cjs +39 -1
  39. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  40. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  41. package/dist-plugin/tokensCssMigrations/index.js +2 -2
  42. package/package.json +9 -2
  43. package/src/app/site.css +16 -0
  44. package/src/editor/core/fonts/googleFontsUrl.ts +44 -8
  45. package/src/editor/core/preview/lookPreview.ts +46 -5
  46. package/src/editor/core/productionPulse.ts +6 -2
  47. package/src/editor/core/sketch/index.ts +60 -0
  48. package/src/editor/core/sketch/maskField.ts +5 -5
  49. package/src/editor/core/sketch/sketchLayer.ts +19 -8
  50. package/src/editor/core/sketch/sketchStore.ts +238 -85
  51. package/src/editor/core/sketch/{sketchPresetService.ts → sketchStyleService.ts} +14 -14
  52. package/src/editor/core/sketch/{sketchPresets.ts → sketchStyles.ts} +17 -17
  53. package/src/editor/core/themes/themeDocumentSync.ts +2 -0
  54. package/src/editor/core/themes/themeInit.ts +19 -1
  55. package/src/editor/core/themes/themeService.ts +7 -2
  56. package/src/editor/core/themes/themeTypes.ts +5 -0
  57. package/src/editor/docs/content/editing-tokens.md +1 -1
  58. package/src/editor/docs/content/sketch-mode.md +27 -16
  59. package/src/editor/docs/content/themes-workflow.md +30 -21
  60. package/src/editor/docs/content/where-themes-live.md +9 -5
  61. package/src/editor/docs/content.generated.ts +4 -4
  62. package/src/editor/overlay/LiveTokensRouter.svelte +1 -1
  63. package/src/editor/ui/EditorViewSwitcher.svelte +3 -3
  64. package/src/editor/ui/FontStackEditor.svelte +49 -11
  65. package/src/editor/ui/ProjectFontsSection.svelte +6 -0
  66. package/src/editor/ui/ThemePanel.svelte +47 -1
  67. package/src/editor/ui/sections/textStyles.ts +15 -1
  68. package/src/editor/ui/sketch/SketchPreview.svelte +3 -3
  69. package/src/editor/ui/sketch/SketchTab.svelte +87 -53
  70. package/src/system/components/FloatingTokenTags.css +10 -8
  71. package/src/system/styles/tokens.css +17 -2
package/bin/migrate.mjs CHANGED
@@ -12,7 +12,7 @@
12
12
  // resolution, file IO, and reporting.
13
13
 
14
14
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
15
- import { dirname, join, relative, resolve } from 'node:path';
15
+ import { basename, dirname, join, relative, resolve } from 'node:path';
16
16
  import { fileURLToPath } from 'node:url';
17
17
 
18
18
  const pkgRoot = resolve(dirname(fileURLToPath(import.meta.url)), '..');
@@ -123,7 +123,11 @@ export function formatMigrateDataResult(result) {
123
123
  const lines = [planned ? 'Data tree heal (planned):' : 'Data tree healed:'];
124
124
 
125
125
  for (const { from, to } of result.renames) {
126
- lines.push(` ${planned ? 'would move' : 'moved'} ${rel(from)} → ${rel(to)} (0.48 layout)`);
126
+ // Two unrelated renames share this list: the pre-0.48 layout swap, and
127
+ // `sketch-presets/` → `sketch-styles/`. Told apart by the destination
128
+ // name, since `LegacyRename` carries no reason of its own.
129
+ const reason = basename(to) === 'sketch-styles' ? 'sketchstyle rename' : '0.48 layout';
130
+ lines.push(` ${planned ? 'would move' : 'moved'} ${rel(from)} → ${rel(to)} (${reason})`);
127
131
  }
128
132
  for (const p of result.upgradedThemes) {
129
133
  lines.push(` ${planned ? 'would carry' : 'carried'} ${rel(p)} by value (was a pre-v3 theme naming files)`);
@@ -270,7 +270,7 @@ function resolveDataDirs(opts = {}) {
270
270
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
271
271
  componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
272
272
  themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
273
- sketchPresetsDir: sub("sketch-presets"),
273
+ sketchStylesDir: sub("sketch-styles"),
274
274
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
275
275
  };
276
276
  }
@@ -1,5 +1,5 @@
1
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.cjs';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.cjs';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -1,5 +1,5 @@
1
1
  import { A as AliasDiskValue, a as ComponentConfig } from '../themeTypes-DSV3Zisf.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.js';
3
3
  export { C as CURRENT_COMPONENT_SCHEMA_VERSION } from '../index-4N-Orzzi.js';
4
4
 
5
5
  type AdjustKind = 'radius' | 'padding' | 'gap' | 'border-width';
@@ -4,7 +4,7 @@ import {
4
4
  import {
5
5
  readLiveTokensConfig,
6
6
  resolveDataDirs
7
- } from "../chunk-Y5CNFSSV.js";
7
+ } from "../chunk-RVE3MNKM.js";
8
8
 
9
9
  // src/editor/core/components/aliasKinds.ts
10
10
  var KIND_PATTERNS = [
@@ -268,6 +268,340 @@ var KNOWN_COMPONENT_CONFIG_KEYS = /* @__PURE__ */ new Set([
268
268
  "--sectiondivider-sm-color-family"
269
269
  ]);
270
270
 
271
+ // src/editor/core/sketch/sketchStyles.ts
272
+ var base = {
273
+ label: "",
274
+ blurb: "",
275
+ fillTravel: 1.5,
276
+ strokeTravel: 1.5,
277
+ wobble: 45,
278
+ roughness: 3,
279
+ borderWavelength: 1,
280
+ waveform: 1,
281
+ strokeWidth: 1.5,
282
+ doubleStroke: false,
283
+ retraceOffset: 1.2,
284
+ retracePass: "copy",
285
+ fillStyle: "solid",
286
+ hatchInk: 0.4,
287
+ strokeStyle: "solid",
288
+ pressure: 0.25,
289
+ pressureMod: 0.35,
290
+ pooling: 1.2,
291
+ strokeInk: 1,
292
+ maskOn: true,
293
+ maskBlob: 100,
294
+ maskOutputMin: 0.45,
295
+ maskOutputMax: 1,
296
+ maskOctaves: 2,
297
+ maskGrain: "fractal",
298
+ maskPosterize: 1,
299
+ maskSoftness: 1.5,
300
+ jitterX: 2.5,
301
+ jitterY: 2.5,
302
+ jitterRot: 0.6,
303
+ jitterScale: 0.035,
304
+ cornerSpread: 10,
305
+ cornerTravel: 8,
306
+ iconTravel: 1.25,
307
+ iconWavelength: 0.625,
308
+ iconMaskOn: true,
309
+ iconMaskScale: 1
310
+ };
311
+ var SKETCH_STYLES = {
312
+ pencil: {
313
+ ...base,
314
+ label: "Pencil",
315
+ blurb: "Two graphite passes on their own seeds, so the outline disagrees with itself the way a hand coming back round does. Tight grain, little else.",
316
+ fillTravel: 0.75,
317
+ strokeTravel: 1.25,
318
+ wobble: 30,
319
+ roughness: 3,
320
+ waveform: 1,
321
+ strokeWidth: 1.25,
322
+ doubleStroke: true,
323
+ retracePass: "reseeded",
324
+ retraceOffset: 1.5,
325
+ strokeInk: 0.85,
326
+ maskBlob: 40,
327
+ maskOutputMin: 0.62,
328
+ maskOutputMax: 1,
329
+ maskOctaves: 3,
330
+ maskPosterize: 1,
331
+ maskSoftness: 0,
332
+ jitterX: 1.5,
333
+ jitterY: 1.5,
334
+ jitterRot: 0.35,
335
+ jitterScale: 0.022,
336
+ cornerSpread: 6,
337
+ cornerTravel: 4.5,
338
+ pressure: 0.15,
339
+ pressureMod: 0.3,
340
+ pooling: 0,
341
+ iconTravel: 0.75
342
+ },
343
+ marker: {
344
+ ...base,
345
+ label: "Marker",
346
+ blurb: "Broad translucent nib gone round twice on the same line, so the overlap darkens and the ink pools where it slows.",
347
+ fillTravel: 2,
348
+ strokeTravel: 1.5,
349
+ wobble: 56,
350
+ waveform: 1.4,
351
+ borderWavelength: 1.3,
352
+ strokeWidth: 4,
353
+ doubleStroke: true,
354
+ retracePass: "copy",
355
+ strokeInk: 0.52,
356
+ retraceOffset: 2.2,
357
+ maskBlob: 115,
358
+ maskOutputMin: 0.42,
359
+ maskOutputMax: 1,
360
+ maskOctaves: 3,
361
+ maskPosterize: 4,
362
+ maskSoftness: 4,
363
+ jitterX: 3,
364
+ jitterY: 3,
365
+ jitterRot: 0.8,
366
+ jitterScale: 0.045,
367
+ cornerSpread: 10,
368
+ cornerTravel: 8,
369
+ pressure: 0.2,
370
+ pressureMod: 0.25,
371
+ pooling: 2,
372
+ iconTravel: 1.25,
373
+ iconMaskScale: 4
374
+ },
375
+ whiteboard: {
376
+ ...base,
377
+ label: "Whiteboard",
378
+ blurb: "The fattest nib on glass. One long smooth undulation, and a veined mask that streaks the fill like a half-wiped board.",
379
+ fillTravel: 2.5,
380
+ strokeTravel: 2.5,
381
+ wobble: 90,
382
+ roughness: 1,
383
+ waveform: 1,
384
+ borderWavelength: 1.5,
385
+ strokeWidth: 5.5,
386
+ doubleStroke: true,
387
+ retracePass: "copy",
388
+ strokeInk: 0.66,
389
+ retraceOffset: 3,
390
+ maskGrain: "turbulence",
391
+ maskBlob: 180,
392
+ maskOutputMin: 0.42,
393
+ maskOutputMax: 1,
394
+ maskOctaves: 1,
395
+ maskPosterize: 3,
396
+ maskSoftness: 8,
397
+ jitterX: 4.5,
398
+ jitterY: 4.5,
399
+ jitterRot: 1.2,
400
+ jitterScale: 0.07,
401
+ cornerSpread: 14,
402
+ cornerTravel: 11,
403
+ pressure: 0.15,
404
+ pressureMod: 0.15,
405
+ pooling: 3.5,
406
+ iconTravel: 1.75,
407
+ iconMaskScale: 1.5
408
+ },
409
+ hatched: {
410
+ ...base,
411
+ label: "Hatched",
412
+ blurb: "An etching. The fill is angled shading, the outline a single hard-edged scratch that chatters along its length. No mask: the hatch is the texture.",
413
+ fillTravel: 1.25,
414
+ strokeTravel: 1.5,
415
+ wobble: 24,
416
+ roughness: 3,
417
+ waveform: 3,
418
+ strokeWidth: 1.5,
419
+ fillStyle: "hatched",
420
+ hatchInk: 0.5,
421
+ doubleStroke: false,
422
+ maskOn: false,
423
+ iconMaskOn: false,
424
+ jitterX: 1.5,
425
+ jitterY: 1.5,
426
+ jitterRot: 0.4,
427
+ jitterScale: 0.03,
428
+ cornerSpread: 6,
429
+ cornerTravel: 6,
430
+ pressure: 0.3,
431
+ pressureMod: 0.45,
432
+ pooling: 0.8,
433
+ iconTravel: 1.25,
434
+ iconWavelength: 0.5
435
+ },
436
+ dashed: {
437
+ ...base,
438
+ label: "Dashed",
439
+ blurb: "A drafting outline. One slow drift along the ruler, broken into strokes, with jitter, mask and pressure all off. The clean pole.",
440
+ strokeStyle: "dashed",
441
+ strokeTravel: 1,
442
+ fillTravel: 0.5,
443
+ wobble: 120,
444
+ roughness: 1,
445
+ waveform: 1,
446
+ strokeWidth: 1.5,
447
+ doubleStroke: false,
448
+ maskOn: false,
449
+ iconMaskOn: false,
450
+ jitterX: 0,
451
+ jitterY: 0,
452
+ jitterRot: 0,
453
+ jitterScale: 0,
454
+ cornerSpread: 4,
455
+ cornerTravel: 3,
456
+ pressure: 0,
457
+ pressureMod: 0,
458
+ pooling: 0,
459
+ iconTravel: 0
460
+ },
461
+ napkin: {
462
+ ...base,
463
+ label: "Napkin",
464
+ blurb: "Ballpoint in a hurry. Everything loose at once: a square wave sends every edge to full travel, and the second pass lands wherever it lands.",
465
+ fillTravel: 3,
466
+ strokeTravel: 2.25,
467
+ wobble: 50,
468
+ roughness: 3,
469
+ waveform: 2.5,
470
+ strokeWidth: 2.25,
471
+ doubleStroke: true,
472
+ retracePass: "reseeded",
473
+ retraceOffset: 4,
474
+ strokeInk: 1,
475
+ maskBlob: 150,
476
+ maskOutputMin: 0.43,
477
+ maskOutputMax: 0.95,
478
+ maskOctaves: 2,
479
+ maskPosterize: 2,
480
+ maskSoftness: 10,
481
+ jitterX: 6,
482
+ jitterY: 6,
483
+ jitterRot: 1.8,
484
+ jitterScale: 0.1,
485
+ cornerSpread: 20,
486
+ cornerTravel: 17,
487
+ pressure: 0.45,
488
+ pressureMod: 0.6,
489
+ pooling: 2.5,
490
+ iconTravel: 2.25,
491
+ iconMaskScale: 3.6
492
+ },
493
+ dry: {
494
+ ...base,
495
+ label: "Dry marker",
496
+ blurb: "Ink that ran out. One scratchy pass that breaks up along its length, over a fill the mask has worn nearly through in patches.",
497
+ fillTravel: 2.25,
498
+ strokeTravel: 1.75,
499
+ wobble: 50,
500
+ waveform: 2,
501
+ borderWavelength: 0.5,
502
+ strokeWidth: 3.5,
503
+ doubleStroke: false,
504
+ strokeInk: 0.4,
505
+ maskBlob: 150,
506
+ maskOutputMin: 0.24,
507
+ maskOutputMax: 0.91,
508
+ maskOctaves: 2,
509
+ maskPosterize: 4,
510
+ maskSoftness: 1.75,
511
+ jitterX: 5,
512
+ jitterY: 5,
513
+ jitterRot: 1.4,
514
+ jitterScale: 0.08,
515
+ cornerSpread: 16,
516
+ cornerTravel: 13,
517
+ pressure: 0.4,
518
+ pressureMod: 0.8,
519
+ pooling: 1.5,
520
+ iconTravel: 1.75,
521
+ iconMaskScale: 3.8
522
+ }
523
+ };
524
+ var DEFAULT_SKETCH_STYLE = "marker";
525
+ function hydrateSketchStyle(raw) {
526
+ const base2 = SKETCH_STYLES[DEFAULT_SKETCH_STYLE];
527
+ const stored = raw ?? {};
528
+ const out = { ...base2 };
529
+ for (const key of Object.keys(base2)) {
530
+ if (stored[key] !== void 0) out[key] = stored[key];
531
+ }
532
+ if (out.fillStyle === "none") out.fillStyle = base2.fillStyle;
533
+ convertTiledMask(stored, out);
534
+ convertCutToLevels(stored, out);
535
+ carryLevelsToOutput(stored, out);
536
+ halveSwingDials(stored, out);
537
+ convertCyclesToWavelength(stored, out);
538
+ convertIconTileToScale(stored, out);
539
+ restoreDerivedRetrace(stored, out);
540
+ return out;
541
+ }
542
+ function restoreDerivedRetrace(stored, out) {
543
+ if (stored.retraceOffset === void 0) {
544
+ out.retraceOffset = Number(Math.max(1.2, out.strokeWidth * 0.55).toFixed(2));
545
+ }
546
+ }
547
+ var SWING_DIALS = {
548
+ fillScale: "fillTravel",
549
+ strokeScale: "strokeTravel",
550
+ iconScale: "iconTravel",
551
+ cornerShift: "cornerTravel"
552
+ };
553
+ function convertCyclesToWavelength(stored, out) {
554
+ const cycles = stored.frequency;
555
+ if (typeof cycles === "number" && cycles > 0) out.wobble = Math.round(1 / cycles);
556
+ for (const [legacy, key] of [
557
+ ["borderFrequency", "borderWavelength"],
558
+ ["iconFrequency", "iconWavelength"]
559
+ ]) {
560
+ const multiple = stored[legacy];
561
+ if (typeof multiple === "number" && multiple > 0) {
562
+ out[key] = Number((1 / multiple).toFixed(3));
563
+ }
564
+ }
565
+ const layers = stored.octaves;
566
+ if (typeof layers === "number") out.roughness = Math.min(3, Math.max(1, layers));
567
+ }
568
+ function convertIconTileToScale(stored, out) {
569
+ const tile = stored.iconMaskTile;
570
+ if (typeof tile !== "number") return;
571
+ out.iconMaskScale = Math.min(5, Math.max(0.5, Number((tile / 90).toFixed(2))));
572
+ }
573
+ function halveSwingDials(stored, out) {
574
+ for (const [legacy, key] of Object.entries(SWING_DIALS)) {
575
+ const value = stored[legacy];
576
+ if (typeof value === "number") out[key] = value / 2;
577
+ }
578
+ }
579
+ function convertTiledMask(stored, out) {
580
+ const scale = stored.maskScale;
581
+ if (typeof scale !== "number") return;
582
+ const freq = stored.maskFrequency;
583
+ if (typeof freq === "number") out.maskBlob = Math.round(scale / (freq * LEGACY_TILE));
584
+ const soft = stored.maskSoftness;
585
+ if (typeof soft === "number") out.maskSoftness = Number((soft * scale / LEGACY_TILE).toFixed(1));
586
+ }
587
+ function convertCutToLevels(stored, out) {
588
+ const contrast = stored.maskContrast;
589
+ const coverage = stored.maskCoverage;
590
+ if (typeof contrast !== "number" || typeof coverage !== "number") return;
591
+ const veined = out.maskGrain === "turbulence";
592
+ const [lo, hi] = veined ? LEGACY_RANGE.turbulence : LEGACY_RANGE.fractal;
593
+ const pivot = veined ? 0.55 - 0.5 * coverage : 0.8 - 0.6 * coverage;
594
+ const level = (raw) => Math.min(1, Math.max(0, (raw - lo) / (hi - lo)));
595
+ out.maskOutputMin = Number(level(pivot - 0.5 / contrast).toFixed(2));
596
+ out.maskOutputMax = Number(Math.max(out.maskOutputMin + 0.02, level(pivot + 0.5 / contrast)).toFixed(2));
597
+ }
598
+ function carryLevelsToOutput(stored, out) {
599
+ if (typeof stored.maskLevelMin === "number") out.maskOutputMin = stored.maskLevelMin;
600
+ if (typeof stored.maskLevelMax === "number") out.maskOutputMax = stored.maskLevelMax;
601
+ }
602
+ var LEGACY_RANGE = { fractal: [0.24, 0.77], turbulence: [0.02, 0.6] };
603
+ var LEGACY_TILE = 600;
604
+
271
605
  // vite-plugin/themes/normalizeTheme.ts
272
606
  function asObject(value) {
273
607
  return value && typeof value === "object" && !Array.isArray(value) ? value : null;
@@ -359,12 +693,12 @@ function normalizeTheme(raw, resolvers) {
359
693
  }
360
694
  const filled = { components: [], aliases: 0, orphans: 0 };
361
695
  for (const comp of resolvers.listComponentNames()) {
362
- const base = asObject(resolvers.readComponentConfig(comp, "default"));
363
- const baseAliases = base ? asObject(base.aliases) : null;
364
- if (!base || !baseAliases) continue;
696
+ const base2 = asObject(resolvers.readComponentConfig(comp, "default"));
697
+ const baseAliases = base2 ? asObject(base2.aliases) : null;
698
+ if (!base2 || !baseAliases) continue;
365
699
  const entry = componentConfigs[comp];
366
700
  if (!entry) {
367
- const { schemaVersion: _standaloneStamp, ...rest } = stripFileMarker(base);
701
+ const { schemaVersion: _standaloneStamp, ...rest } = stripFileMarker(base2);
368
702
  componentConfigs[comp] = structuredClone(rest);
369
703
  filled.components.push(comp);
370
704
  continue;
@@ -380,6 +714,8 @@ function normalizeTheme(raw, resolvers) {
380
714
  if (!(key in baseAliases)) filled.orphans++;
381
715
  }
382
716
  }
717
+ const embeddedSketchStyle = asObject(src.sketchStyle);
718
+ const sketchStyle = embeddedSketchStyle ? hydrateSketchStyle(embeddedSketchStyle) : void 0;
383
719
  return {
384
720
  theme: {
385
721
  name: asString(src.name, "Untitled"),
@@ -388,7 +724,8 @@ function normalizeTheme(raw, resolvers) {
388
724
  schemaVersion: THEME_SCHEMA_VERSION,
389
725
  colorsAndType,
390
726
  componentConfigs,
391
- componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION
727
+ componentSchemaVersion: CURRENT_COMPONENT_SCHEMA_VERSION,
728
+ ...sketchStyle ? { sketchStyle } : {}
392
729
  },
393
730
  dropped,
394
731
  migrated,
@@ -42,7 +42,7 @@ function resolveDataDirs(opts = {}) {
42
42
  colorsAndTypeDir: opts.colorsAndTypeDir ? path.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? path.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
43
43
  componentConfigsDir: opts.componentConfigsDir ? path.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? path.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
44
44
  themesDir: opts.themesDir ? path.resolve(opts.themesDir) : fileConfig.themesDir ? path.resolve(fileConfig.themesDir) : sub("themes"),
45
- sketchPresetsDir: sub("sketch-presets"),
45
+ sketchStylesDir: sub("sketch-styles"),
46
46
  legacyManifestsDir: fileConfig.manifestsDir ? path.resolve(fileConfig.manifestsDir) : void 0
47
47
  };
48
48
  }
@@ -479,6 +479,43 @@ var tokensCssMigration_2026_08_27_editorialSizeSteps = {
479
479
  }
480
480
  };
481
481
 
482
+ // vite-plugin/tokensCssMigrations/migrations/2026-08-27-editorial-large-steps.ts
483
+ var BUNDLE3 = [
484
+ { name: "--editorial-xl-font-family", value: "var(--font-editorial)" },
485
+ { name: "--editorial-xl-font-size", value: "var(--font-size-xl)" },
486
+ { name: "--editorial-xl-font-weight", value: "var(--font-weight-normal)" },
487
+ { name: "--editorial-xl-line-height", value: "var(--line-height-tighter)" },
488
+ { name: "--editorial-xl-letter-spacing", value: "var(--letter-spacing-normal)" },
489
+ { name: "--editorial-lg-font-family", value: "var(--font-editorial)" },
490
+ { name: "--editorial-lg-font-size", value: "var(--font-size-lg)" },
491
+ { name: "--editorial-lg-font-weight", value: "var(--font-weight-normal)" },
492
+ { name: "--editorial-lg-line-height", value: "var(--line-height-tight)" },
493
+ { name: "--editorial-lg-letter-spacing", value: "var(--letter-spacing-normal)" }
494
+ ];
495
+ var tokensCssMigration_2026_08_27_editorialLargeSteps = {
496
+ id: "2026-08-27-editorial-large-steps",
497
+ kind: "additive",
498
+ description: "Add the --editorial-lg-* and --editorial-xl-* steps above the reading size",
499
+ apply(css) {
500
+ return ensureScale(css, {
501
+ sectionComment: "Editorial \u2014 the steps above the reading size",
502
+ anchorPrefixes: [
503
+ "--editorial-",
504
+ "--eyebrow-",
505
+ "--code-",
506
+ "--body-",
507
+ "--heading-",
508
+ "--letter-spacing-",
509
+ "--line-height-",
510
+ "--font-weight-",
511
+ "--font-size-",
512
+ "--font-"
513
+ ],
514
+ entries: BUNDLE3
515
+ });
516
+ }
517
+ };
518
+
482
519
  // vite-plugin/tokensCssMigrations/index.ts
483
520
  var TOKENS_CSS_MIGRATIONS = [
484
521
  tokensCssMigration_2026_05_29_typographyScaleAdditions,
@@ -492,7 +529,8 @@ var TOKENS_CSS_MIGRATIONS = [
492
529
  tokensCssMigration_2026_08_25_editorialTypeRole,
493
530
  tokensCssMigration_2026_08_26_gradientStops,
494
531
  tokensCssMigration_2026_08_27_backdropPolarity,
495
- tokensCssMigration_2026_08_27_editorialSizeSteps
532
+ tokensCssMigration_2026_08_27_editorialSizeSteps,
533
+ tokensCssMigration_2026_08_27_editorialLargeSteps
496
534
  ];
497
535
  function runTokensCssMigrations(css) {
498
536
  return foldMigrations(css, () => true);
@@ -28,9 +28,9 @@ interface ResolvedDataDirs {
28
28
  colorsAndTypeDir: string;
29
29
  componentConfigsDir: string;
30
30
  themesDir: string;
31
- /** No per-folder config override: sketch presets are a draft-look artifact
32
- * with no reason to live apart from the rest of the data tree. */
33
- sketchPresetsDir: string;
31
+ /** No per-folder config override: a sketchstyle has no reason to live apart
32
+ * from the rest of the data tree. */
33
+ sketchStylesDir: string;
34
34
  /** Absolute path of the retired `manifestsDir` key, when the config sets it.
35
35
  * Only the pre-0.48 layout detector and the migration read it. */
36
36
  legacyManifestsDir?: string;
@@ -28,9 +28,9 @@ interface ResolvedDataDirs {
28
28
  colorsAndTypeDir: string;
29
29
  componentConfigsDir: string;
30
30
  themesDir: string;
31
- /** No per-folder config override: sketch presets are a draft-look artifact
32
- * with no reason to live apart from the rest of the data tree. */
33
- sketchPresetsDir: string;
31
+ /** No per-folder config override: a sketchstyle has no reason to live apart
32
+ * from the rest of the data tree. */
33
+ sketchStylesDir: string;
34
34
  /** Absolute path of the retired `manifestsDir` key, when the config sets it.
35
35
  * Only the pre-0.48 layout detector and the migration read it. */
36
36
  legacyManifestsDir?: string;
@@ -260,15 +260,39 @@ function censusFrom(css, requested) {
260
260
  italics: family.italics === true
261
261
  };
262
262
  }
263
+ async function probeFor(fetcher, url2) {
264
+ try {
265
+ const res = await fetcher(url2);
266
+ return res.ok ? { res } : { status: res.status };
267
+ } catch {
268
+ return {};
269
+ }
270
+ }
271
+ function titleCase(name) {
272
+ return name.replace(/\S+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
273
+ }
263
274
  async function resolveGoogleFont(name, fetcher) {
264
- const probeUrl = discoveryUrl(name);
265
- const probe = await fetcher(probeUrl);
266
- if (!probe.ok) {
275
+ const requested = name.trim();
276
+ const spellings = [.../* @__PURE__ */ new Set([requested, titleCase(requested)])];
277
+ let probe;
278
+ let probeUrl = discoveryUrl(requested);
279
+ let status;
280
+ for (const spelling of spellings) {
281
+ const candidateUrl = discoveryUrl(spelling);
282
+ const attempt = await probeFor(fetcher, candidateUrl);
283
+ if (attempt.res) {
284
+ probe = attempt.res;
285
+ probeUrl = candidateUrl;
286
+ break;
287
+ }
288
+ status ??= attempt.status;
289
+ }
290
+ if (!probe) {
267
291
  throw new Error(
268
- `"${name}" is not on Google Fonts (the API answered ${probe.status}). Check the spelling against fonts.google.com.`
292
+ `"${requested}" is not on Google Fonts` + (status === void 0 ? "" : ` (the API answered ${status})`) + `. Check the spelling against fonts.google.com.`
269
293
  );
270
294
  }
271
- const census = censusFrom(await probe.text(), name);
295
+ const census = censusFrom(await probe.text(), requested);
272
296
  const candidates = [persistUrlFor(census.name, census.weights, census.italics)];
273
297
  const enumerated = census.weights.length > 0 ? url(
274
298
  census.name,
@@ -278,8 +302,8 @@ async function resolveGoogleFont(name, fetcher) {
278
302
  const bare = url(census.name);
279
303
  if (!candidates.includes(bare)) candidates.push(bare);
280
304
  for (const candidate of candidates) {
281
- const res = await fetcher(candidate);
282
- if (!res.ok) continue;
305
+ const { res } = await probeFor(fetcher, candidate);
306
+ if (!res) continue;
283
307
  const served = censusFrom(await res.text(), census.name);
284
308
  return {
285
309
  name: census.name,
@@ -392,7 +416,7 @@ function resolveDataDirs(opts = {}) {
392
416
  colorsAndTypeDir: opts.colorsAndTypeDir ? import_path.default.resolve(opts.colorsAndTypeDir) : fileConfig.colorsAndTypeDir ? import_path.default.resolve(fileConfig.colorsAndTypeDir) : sub("colors-and-type"),
393
417
  componentConfigsDir: opts.componentConfigsDir ? import_path.default.resolve(opts.componentConfigsDir) : fileConfig.componentConfigsDir ? import_path.default.resolve(fileConfig.componentConfigsDir) : sub("component-configs"),
394
418
  themesDir: opts.themesDir ? import_path.default.resolve(opts.themesDir) : fileConfig.themesDir ? import_path.default.resolve(fileConfig.themesDir) : sub("themes"),
395
- sketchPresetsDir: sub("sketch-presets"),
419
+ sketchStylesDir: sub("sketch-styles"),
396
420
  legacyManifestsDir: fileConfig.manifestsDir ? import_path.default.resolve(fileConfig.manifestsDir) : void 0
397
421
  };
398
422
  }
@@ -1,5 +1,5 @@
1
1
  import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.cjs';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.cjs';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.cjs';
3
3
 
4
4
  type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
@@ -1,5 +1,5 @@
1
1
  import { c as FontStackVariable, C as ColorsAndType, b as FontStack, F as FontSource } from '../themeTypes-DSV3Zisf.js';
2
- export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-CRfD1LdA.js';
2
+ export { r as readLiveTokensConfig, a as resolveDataDirs } from '../dataPaths-DZUzVv8H.js';
3
3
 
4
4
  type PairingSlot = 'display' | 'body' | 'serif' | 'mono' | 'editorial';
5
5
  interface PairingFace {
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  readLiveTokensConfig,
3
3
  resolveDataDirs
4
- } from "../chunk-Y5CNFSSV.js";
4
+ } from "../chunk-RVE3MNKM.js";
5
5
 
6
6
  // src/editor/core/fonts/fontParse.ts
7
7
  function parseGoogleFontsUrl(url2) {
@@ -218,15 +218,39 @@ function censusFrom(css, requested) {
218
218
  italics: family.italics === true
219
219
  };
220
220
  }
221
+ async function probeFor(fetcher, url2) {
222
+ try {
223
+ const res = await fetcher(url2);
224
+ return res.ok ? { res } : { status: res.status };
225
+ } catch {
226
+ return {};
227
+ }
228
+ }
229
+ function titleCase(name) {
230
+ return name.replace(/\S+/g, (w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase());
231
+ }
221
232
  async function resolveGoogleFont(name, fetcher) {
222
- const probeUrl = discoveryUrl(name);
223
- const probe = await fetcher(probeUrl);
224
- if (!probe.ok) {
233
+ const requested = name.trim();
234
+ const spellings = [.../* @__PURE__ */ new Set([requested, titleCase(requested)])];
235
+ let probe;
236
+ let probeUrl = discoveryUrl(requested);
237
+ let status;
238
+ for (const spelling of spellings) {
239
+ const candidateUrl = discoveryUrl(spelling);
240
+ const attempt = await probeFor(fetcher, candidateUrl);
241
+ if (attempt.res) {
242
+ probe = attempt.res;
243
+ probeUrl = candidateUrl;
244
+ break;
245
+ }
246
+ status ??= attempt.status;
247
+ }
248
+ if (!probe) {
225
249
  throw new Error(
226
- `"${name}" is not on Google Fonts (the API answered ${probe.status}). Check the spelling against fonts.google.com.`
250
+ `"${requested}" is not on Google Fonts` + (status === void 0 ? "" : ` (the API answered ${status})`) + `. Check the spelling against fonts.google.com.`
227
251
  );
228
252
  }
229
- const census = censusFrom(await probe.text(), name);
253
+ const census = censusFrom(await probe.text(), requested);
230
254
  const candidates = [persistUrlFor(census.name, census.weights, census.italics)];
231
255
  const enumerated = census.weights.length > 0 ? url(
232
256
  census.name,
@@ -236,8 +260,8 @@ async function resolveGoogleFont(name, fetcher) {
236
260
  const bare = url(census.name);
237
261
  if (!candidates.includes(bare)) candidates.push(bare);
238
262
  for (const candidate of candidates) {
239
- const res = await fetcher(candidate);
240
- if (!res.ok) continue;
263
+ const { res } = await probeFor(fetcher, candidate);
264
+ if (!res) continue;
241
265
  const served = censusFrom(await res.text(), census.name);
242
266
  return {
243
267
  name: census.name,