@motion-proto/live-tokens 0.61.0 → 0.63.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 (83) 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 +114 -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-OIOXU7FR.js → chunk-ZHPX7ZYQ.js} +83 -25
  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 +1 -1
  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 +1 -1
  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 +461 -58
  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 +83 -25
  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 +4 -2
  43. package/src/app/site.css +32 -0
  44. package/src/editor/component-editor/CardEditor.svelte +7 -1
  45. package/src/editor/component-editor/scaffolding/VariantGroup.svelte +25 -2
  46. package/src/editor/core/preview/lookPreview.ts +46 -5
  47. package/src/editor/core/productionPulse.ts +6 -2
  48. package/src/editor/core/sketch/maskField.ts +75 -39
  49. package/src/editor/core/sketch/sketchLayer.ts +32 -11
  50. package/src/editor/core/sketch/sketchStore.ts +311 -86
  51. package/src/editor/core/sketch/{sketchPresetService.ts → sketchStyleService.ts} +14 -14
  52. package/src/editor/core/sketch/{sketchPresets.ts → sketchStyles.ts} +27 -28
  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 +34 -18
  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 +8 -0
  63. package/src/editor/ui/EditorViewSwitcher.svelte +3 -3
  64. package/src/editor/ui/ThemePanel.svelte +47 -1
  65. package/src/editor/ui/sections/textStyles.ts +29 -1
  66. package/src/editor/ui/sketch/SketchPreview.svelte +3 -3
  67. package/src/editor/ui/sketch/SketchTab.svelte +120 -134
  68. package/src/live-tokens/data/colors-and-type/midnight-study.json +40 -30
  69. package/src/live-tokens/data/themes/autumn.json +3 -3
  70. package/src/live-tokens/data/themes/halloween.json +3 -3
  71. package/src/live-tokens/data/themes/midnight-study.json +61 -49
  72. package/src/live-tokens/data/themes/ocean.json +3 -3
  73. package/src/live-tokens/data/themes/royal-velvet.json +3 -3
  74. package/src/live-tokens/data/themes/sketchy.json +3 -3
  75. package/src/live-tokens/data/themes/spring-meadow.json +3 -3
  76. package/src/live-tokens/data/themes/sunset.json +3 -3
  77. package/src/system/components/Card.svelte +27 -9
  78. package/src/system/components/FloatingTokenTags.css +10 -8
  79. package/src/system/components/ImageLightbox.svelte +5 -2
  80. package/src/system/components/SectionDivider.svelte +3 -3
  81. package/src/system/components/SegmentedControl.svelte +11 -9
  82. package/src/system/styles/tokens.css +28 -5
  83. package/template/src/pages/Home.svelte +1 -11
package/CHANGELOG.md CHANGED
@@ -1,5 +1,119 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.63.0 — A theme carries its sketchstyle
4
+
5
+ ### Added
6
+
7
+ - **The editorial role reaches above the reading size.** It shipped as a pair,
8
+ so a lede, a standfirst or a pull quote had nowhere to go but a heading style:
9
+ the display face, at heading leading. `--editorial-lg-*` and `--editorial-xl-*`
10
+ are the two steps above `md`, set in the same editorial face, editable in the
11
+ editor's Text Styles section and carried by an `.editorial-lg` /
12
+ `.editorial-xl` class in `site.css`. Leading follows the role's own rule: `md`
13
+ is the reading step and takes the most open leading, and every step away from
14
+ it tightens by one. Sizes stay inside the band the responsive scale leaves
15
+ constant, so an article does not resize itself between breakpoints. An
16
+ `additive` tokens.css migration (`2026-08-27-editorial-large-steps`) adds both
17
+ steps to a vendored `tokens.css`.
18
+
19
+ ### Changed
20
+
21
+ - **A theme carries its sketchstyle.** Save folds the live dials into the open
22
+ theme's `sketchStyle` field; Load applies whatever the theme carries, or
23
+ turns the effect off for a theme with none. A sketchstyle keeps its old job:
24
+ a reusable look you pick from, saved and loaded independently of the open
25
+ theme. Production is unchanged: Adopt still does not bake the layer, and a
26
+ built site still ships no sketch. No migration runs for the theme field
27
+ itself: a browser that already had sketch dials set reads them as off the
28
+ theme on its first boot after upgrading, and Save folds them into the open
29
+ theme like any other unsaved change.
30
+ - **The Theme Picker previews the sketch layer.** Picking a theme showed its
31
+ colors and type over whatever drawing the previously applied theme had left
32
+ painted, so two themes' looks were on screen at once, which is the one thing a
33
+ preview exists to prevent. A previewed theme now paints its own sketchstyle,
34
+ and one carrying none previews crisp whatever is live. Leaving the picker puts
35
+ back exactly what was there before, an unsaved dial included: the preview
36
+ paints without writing the live buffer, so browsing costs nothing and Cancel
37
+ reverts to the buffer rather than to a snapshot taken before it.
38
+ - **Sketchstyle, named as one thing.** "Sketch preset" is retired: the code,
39
+ the Theme panel row, the editor's fourth view and the on-disk directory all
40
+ say "sketchstyle" now. The saved-look directory moves from
41
+ `data/sketch-presets/` to `data/sketch-styles/`; `npx live-tokens migrate`
42
+ renames it (boot only warns on the old name, it does not refuse the tree).
43
+
44
+ ## 0.62.0 — Editorial type reads as a pair
45
+
46
+ ### Added
47
+
48
+ - **The editorial role has its own sizes.** The role shipped with a single
49
+ unsized bundle, so an editorial surface could only take the body size. It now
50
+ reads as a pair, matching body: `--editorial-md-*` and `--editorial-sm-*`,
51
+ both editable in the editor's Text Styles section beside the other styles, and
52
+ both carried by an `.editorial-md` / `.editorial-sm` class in `site.css`. Each
53
+ step mirrors its body counterpart apart from the family, so a project that
54
+ never repoints `--font-editorial` renders exactly as it did.
55
+ - **`Card` takes `variant="bare"`.** A card with no icon and no title dropped
56
+ its header silently, which read the same as a card whose title had gone
57
+ missing. `bare` says the headerless card is the intent; the frame and body
58
+ keep every token the default variant uses, so padding, stroke and fill stay
59
+ editable. A default-variant card with nothing to put in its header now warns
60
+ in dev.
61
+ - **Sketch mode reaches the component editor.** A component demo draws itself
62
+ the way the page will, sketch included, while the tab strips and property rows
63
+ around it stay chrome: they run on `--ui-*` tokens the layer knows nothing
64
+ about. The router hands the layer the root to paint, so a sketch style never
65
+ bleeds onto the editor's own routes.
66
+
67
+ ### Changed (breaking)
68
+
69
+ - **`--editorial-*` is now `--editorial-md-*`.** The unsized bundle became the
70
+ medium step. A `breaking` tokens.css migration
71
+ (`2026-08-27-editorial-size-steps`) renames it and backfills both steps,
72
+ carrying any value already tuned. It never auto-applies: run
73
+ `npx live-tokens migrate`.
74
+
75
+ ### Changed
76
+
77
+ - **Ink coverage is a levels control.** The dials read as a pile of unrelated
78
+ knobs because each one meant something different depending on the grain and
79
+ octave count under it. The field is now normalized to run black to white
80
+ whatever the noise beneath, so **Steps** flattens it into tones and **Output**
81
+ squeezes the whole of it into the range the ink covers, from palest to
82
+ densest. The field is squeezed into that gap, never clipped at it, so both
83
+ ends mean the same thing at every setting. The seven shipped presets are
84
+ retuned against the new response.
85
+ - **Menus and tooltips are drawn solid whatever the coverage dials say.** They
86
+ float over arbitrary page content, so a fill worn through in patches let the
87
+ page show through them and they stopped reading as a surface the pointer can
88
+ land on.
89
+ - **`SectionDivider` titles take no outline by default.** The three sizes each
90
+ carried a themed outline color, which drew a halo around every subsection
91
+ title on a page that never asked for one. All three default to transparent;
92
+ a theme that wants the halo sets the color.
93
+
94
+ ### Fixed
95
+
96
+ - **Per-side padding no longer strands the base token.** `themed-padding`
97
+ emits `var(--x-side, <base>)` for each side, so a per-side token is an
98
+ override slot the editor fills when the user splits the padding. `Card` and
99
+ `SegmentedControl` declared sides that named a literal step instead, which
100
+ always beat that fallback and left the base inert: merging split padding back
101
+ to one value cleared the side aliases, the fallback never took over, and the
102
+ padding control moved nothing. Both now track their own base, and
103
+ `paddingBaseStaysLive` holds every component that uses `themed-padding` to
104
+ the same rule. `Card`'s header and body padding are symmetric as a result;
105
+ a theme that wants the asymmetry back sets the per-side aliases.
106
+ - **`ImageLightbox`'s tile fill stays on the closed tile.** The fill also
107
+ painted the opened stage, where it had nothing to do: the stage rides a
108
+ near-opaque overlay that already backs transparent art, so the fill only
109
+ showed as a slab of theme color in the gap the image's aspect left inside the
110
+ stage box. The open stage now takes no fill of its own, matching the tile
111
+ shadow, which already stopped at the same edge.
112
+ - **Sketch settings no longer accumulate a trace on `globalThis`.** Debug
113
+ instrumentation left in `sketchStore` pushed an entry onto an unbounded
114
+ global array on every settings write, every share and every cross-frame
115
+ adopt. It is gone.
116
+
3
117
  ## 0.61.0 — A surface says which way it leans
4
118
 
5
119
  ### Added
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
  }