@motion-proto/live-tokens 0.62.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 (67) 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 +41 -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 +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 +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 +4 -2
  43. package/src/app/site.css +16 -0
  44. package/src/editor/core/preview/lookPreview.ts +46 -5
  45. package/src/editor/core/productionPulse.ts +6 -2
  46. package/src/editor/core/sketch/maskField.ts +5 -5
  47. package/src/editor/core/sketch/sketchLayer.ts +8 -8
  48. package/src/editor/core/sketch/sketchStore.ts +233 -79
  49. package/src/editor/core/sketch/{sketchPresetService.ts → sketchStyleService.ts} +14 -14
  50. package/src/editor/core/sketch/{sketchPresets.ts → sketchStyles.ts} +17 -17
  51. package/src/editor/core/themes/themeDocumentSync.ts +2 -0
  52. package/src/editor/core/themes/themeInit.ts +19 -1
  53. package/src/editor/core/themes/themeService.ts +7 -2
  54. package/src/editor/core/themes/themeTypes.ts +5 -0
  55. package/src/editor/docs/content/editing-tokens.md +1 -1
  56. package/src/editor/docs/content/sketch-mode.md +27 -16
  57. package/src/editor/docs/content/themes-workflow.md +30 -21
  58. package/src/editor/docs/content/where-themes-live.md +9 -5
  59. package/src/editor/docs/content.generated.ts +4 -4
  60. package/src/editor/overlay/LiveTokensRouter.svelte +1 -1
  61. package/src/editor/ui/EditorViewSwitcher.svelte +3 -3
  62. package/src/editor/ui/ThemePanel.svelte +47 -1
  63. package/src/editor/ui/sections/textStyles.ts +15 -1
  64. package/src/editor/ui/sketch/SketchPreview.svelte +3 -3
  65. package/src/editor/ui/sketch/SketchTab.svelte +87 -53
  66. package/src/system/components/FloatingTokenTags.css +10 -8
  67. package/src/system/styles/tokens.css +17 -2
@@ -13,11 +13,11 @@ import {
13
13
  runTokensCssMigrations,
14
14
  semverBumpType,
15
15
  validateTokensCss
16
- } from "../chunk-OPYOK2CA.js";
16
+ } from "../chunk-ZHPX7ZYQ.js";
17
17
  import "../chunk-7TQQTHI6.js";
18
18
  import {
19
19
  readLiveTokensConfig
20
- } from "../chunk-Y5CNFSSV.js";
20
+ } from "../chunk-RVE3MNKM.js";
21
21
  export {
22
22
  TOKENS_CSS_MIGRATIONS,
23
23
  collectDefinedTokens,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@motion-proto/live-tokens",
3
- "version": "0.62.0",
3
+ "version": "0.63.0",
4
4
  "type": "module",
5
5
  "description": "Design token editor with live CSS variable editing. Svelte 5 + Vite 8.",
6
6
  "keywords": [
@@ -137,13 +137,15 @@
137
137
  "check:token-contract": "node scripts/check-token-contract.mjs",
138
138
  "check:preset-themes": "node scripts/check-preset-themes.mjs",
139
139
  "check:skills": "node scripts/check-skills.mjs",
140
+ "check:skill-atlas": "node scripts/sync-skill-atlas.mjs",
140
141
  "sync:component-defaults": "node scripts/sync-component-defaults.mjs --write",
141
142
  "sync:docs": "node scripts/sync-docs.mjs --write",
143
+ "sync:skill-atlas": "node scripts/sync-skill-atlas.mjs --write",
142
144
  "seed:preset-theme": "node scripts/seed-preset-theme.mjs",
143
145
  "collapse:theme": "node scripts/collapse-theme-to-default.mjs",
144
146
  "check:smoke-install": "bash scripts/smoke-install.sh",
145
147
  "check:smoke-create": "bash scripts/smoke-create.sh",
146
- "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:smoke-install && npm run check:smoke-create"
148
+ "prepublishOnly": "npm run check:no-style-imports && npm run check:no-tooling-imports && npm run check:slot-prose && npm run check:overlay-portal && npm run check:editor-font-isolation && npm run check:component-defaults && npm run check:production-is-default && npm run check:docs-content && npm run build:lib && npm run check:token-contract && npm run check:preset-themes && npm run check:skills && npm run check:skill-atlas && npm run check:smoke-install && npm run check:smoke-create"
147
149
  },
148
150
  "peerDependencies": {
149
151
  "@sveltejs/vite-plugin-svelte": "^7.0",
package/src/app/site.css CHANGED
@@ -205,6 +205,22 @@ blockquote {
205
205
  letter-spacing: var(--body-sm-letter-spacing);
206
206
  }
207
207
 
208
+ .editorial-xl {
209
+ font-family: var(--editorial-xl-font-family);
210
+ font-size: var(--editorial-xl-font-size);
211
+ font-weight: var(--editorial-xl-font-weight);
212
+ line-height: var(--editorial-xl-line-height);
213
+ letter-spacing: var(--editorial-xl-letter-spacing);
214
+ }
215
+
216
+ .editorial-lg {
217
+ font-family: var(--editorial-lg-font-family);
218
+ font-size: var(--editorial-lg-font-size);
219
+ font-weight: var(--editorial-lg-font-weight);
220
+ line-height: var(--editorial-lg-line-height);
221
+ letter-spacing: var(--editorial-lg-letter-spacing);
222
+ }
223
+
208
224
  .editorial-md {
209
225
  font-family: var(--editorial-md-font-family);
210
226
  font-size: var(--editorial-md-font-size);
@@ -6,6 +6,7 @@ import { batchCssVarChanges, setCssVar, removeCssVar } from '../cssVarSync';
6
6
  import { applyFontSources } from '../fonts/fontLoader';
7
7
  import { migrateColorsAndTypeFonts } from '../fonts/fontMigration';
8
8
  import { loadTheme } from '../themes/themeService';
9
+ import { previewSketchStyle, revertSketchStylePreview } from '../sketch/sketchStore';
9
10
 
10
11
  /**
11
12
  * Client-side rendering of a saved file, for browsing looks without committing
@@ -20,6 +21,11 @@ import { loadTheme } from '../themes/themeService';
20
21
  * of the same theme produces, and a revert re-derives from the live store
21
22
  * rather than replaying DOM values scraped before the preview.
22
23
  *
24
+ * A theme's sketchstyle rides along the same way: `previewTheme` paints it
25
+ * through `previewSketchStyle`, which reaches the page without touching the
26
+ * live sketch buffer, and a revert re-derives from that buffer exactly as the
27
+ * CSS vars do.
28
+ *
23
29
  * One preview is live at a time, whichever kind it is: painting reverts first,
24
30
  * so every look is a diff against the user's real state.
25
31
  */
@@ -76,6 +82,14 @@ export function liveLook(): RenderedLook {
76
82
 
77
83
  let livePreview: RenderedLook | null = null;
78
84
  let defaultsPromise: Promise<Theme> | null = null;
85
+ // Whether the current preview session has a sketchstyle painted over the live
86
+ // buffer, so a colors-only preview (which never previews sketch) and revert
87
+ // know whether there is anything to hand back.
88
+ let sketchPreviewActive = false;
89
+ // Bumped whenever a preview session ends or is superseded. `previewTheme` awaits
90
+ // the defaults theme, and a Cancel landing inside that await was overwritten when
91
+ // it resolved, stranding a preview the picker believed it had already taken down.
92
+ let generation = 0;
79
93
 
80
94
  function loadDefaults(): Promise<Theme> {
81
95
  // A rejected promise must not be memoized, or one failed fetch (a dev-server
@@ -109,30 +123,55 @@ function applyPreview(look: RenderedLook): void {
109
123
  livePreview = look;
110
124
  }
111
125
 
112
- /** Paint a whole look: the theme's colors and type and every component config
113
- * it carries. */
126
+ /** Paint a whole look: the theme's colors and type, every component config
127
+ * it carries, and its sketchstyle — present or not, since a theme with none
128
+ * paints crisp regardless of what is live (invariant 3). */
114
129
  export async function previewTheme(theme: Theme): Promise<void> {
115
- applyPreview(themeLook(theme, await loadDefaults()));
130
+ const gen = generation;
131
+ const defaults = await loadDefaults();
132
+ if (gen !== generation) return;
133
+ applyPreview(themeLook(theme, defaults));
134
+ previewSketchStyle(theme.sketchStyle);
135
+ sketchPreviewActive = true;
116
136
  }
117
137
 
118
- /** Paint colors and type over the components as they stand. */
138
+ /** Paint colors and type over the components as they stand. Not a whole
139
+ * look, so the sketchstyle stays live too — reverting a sketch preview a
140
+ * prior row left painted, if one is running. */
119
141
  export function previewColorsAndType(colorsAndType: ColorsAndType): void {
142
+ generation++;
120
143
  applyPreview(colorsAndTypeLook(colorsAndType));
144
+ if (sketchPreviewActive) {
145
+ revertSketchStylePreview();
146
+ sketchPreviewActive = false;
147
+ }
121
148
  }
122
149
 
123
150
  /** Restore the live editor state. No-op when no preview is running. */
124
151
  export function revertPreview(): void {
152
+ generation++;
125
153
  if (!livePreview) return;
126
154
  paint(liveLook(), livePreview);
127
155
  livePreview = null;
156
+ if (sketchPreviewActive) {
157
+ revertSketchStylePreview();
158
+ sketchPreviewActive = false;
159
+ }
128
160
  }
129
161
 
130
162
  /** Release the preview without repainting. The caller must immediately load
131
163
  * the exact look being previewed into the store. This is the Save handoff: the
132
164
  * selected theme is already on screen, so restoring the old live look before
133
- * applying it would add work and create a visible flash across the request. */
165
+ * applying it would add work and create a visible flash across the request.
166
+ * The sketchstyle stays painted for the same reason, though the handoff is not
167
+ * quite free: when the live state was crisp, `openThemeSketchStyle` writes the
168
+ * settings before the flag, so the sheet and the filter bank come down and go
169
+ * straight back up in between. That order is still the right one, since writing
170
+ * the flag first would paint the OLD dials for a frame. */
134
171
  export function commitPreview(): void {
172
+ generation++;
135
173
  livePreview = null;
174
+ sketchPreviewActive = false;
136
175
  }
137
176
 
138
177
  export function isPreviewing(): boolean {
@@ -141,6 +180,8 @@ export function isPreviewing(): boolean {
141
180
 
142
181
  /** Test-only: drop the live preview and the cached defaults theme. */
143
182
  export function __resetPreviewForTests(): void {
183
+ generation++;
144
184
  livePreview = null;
145
185
  defaultsPromise = null;
186
+ sketchPreviewActive = false;
146
187
  }
@@ -35,8 +35,12 @@ export function bumpComponentActiveRevision(): void {
35
35
  * component editor's Adopt has to clear it for the panel.
36
36
  *
37
37
  * Set by the client writes that move the live look (`writeWorkingColorsAndType`,
38
- * `writeWorkingComponentConfig`, `saveActiveTheme`, `saveAsTheme`); cleared by
39
- * `adoptLook`. Module-level, so it survives the remounts a view switch causes.
38
+ * `writeWorkingComponentConfig`, `saveActiveTheme`, `saveAsTheme`, and every
39
+ * sketch gesture that changes what the effect paints: `setSketchEnabled`,
40
+ * `updateSketchSettings`, `selectSketchStyle`, `selectSavedSketchStyle`);
41
+ * cleared by `adoptLook` and by `hydrateAppliedTheme`, on this document and on
42
+ * every peer an Apply broadcasts to. Module-level, so it survives the
43
+ * remounts a view switch causes.
40
44
  */
41
45
  export const liveMovedSinceBake = writable(false);
42
46
 
@@ -19,7 +19,7 @@
19
19
  * Everything is integer maths on a Float32Array with no DOM, so what a test
20
20
  * asserts is what the browser paints.
21
21
  */
22
- import type { SketchSettings } from './sketchPresets';
22
+ import type { SketchStyle } from './sketchStyles';
23
23
 
24
24
  /** What the tile spans in page px. Blobs are fitted a whole number to the
25
25
  tile, which is what lets the lattice wrap; the dial's px reading is the
@@ -111,7 +111,7 @@ function makePerlin(seed: number): (x: number, y: number, period: number) => num
111
111
  * wherever the octave crossed zero, which is the marbled look; plain sum is
112
112
  * cloud.
113
113
  */
114
- function rawField(s: SketchSettings, seed: number, raster: number, cells: number): Float32Array {
114
+ function rawField(s: SketchStyle, seed: number, raster: number, cells: number): Float32Array {
115
115
  const noise = makePerlin(seed);
116
116
  const veined = s.maskGrain === 'turbulence';
117
117
  const octaves = Math.max(1, Math.round(s.maskOctaves));
@@ -256,7 +256,7 @@ function boxPass(f: Float32Array, n: number, radius: number): Float32Array {
256
256
  all four; the levels, the posterising and the blur are one pass each. */
257
257
  let rawCache: { key: string; field: Float32Array } | null = null;
258
258
 
259
- function cachedRaw(s: SketchSettings, seed: number): Float32Array {
259
+ function cachedRaw(s: SketchStyle, seed: number): Float32Array {
260
260
  const key = [s.maskBlob, s.maskOctaves, s.maskGrain, seed].join('|');
261
261
  if (rawCache?.key !== key) {
262
262
  const cells = Math.max(1, Math.round(MASK_TILE / s.maskBlob));
@@ -274,7 +274,7 @@ function cachedRaw(s: SketchSettings, seed: number): Float32Array {
274
274
  * pass did.
275
275
  */
276
276
  export function buildMaskField(
277
- s: SketchSettings, seed = 9, through?: MaskStage,
277
+ s: SketchStyle, seed = 9, through?: MaskStage,
278
278
  ): { field: Float32Array; raster: number } {
279
279
  const raw = cachedRaw(s, seed);
280
280
  if (through === 'noise') return { field: raw, raster: RASTER };
@@ -406,7 +406,7 @@ const CACHE_MAX = 6;
406
406
  const cache = new Map<string, string>();
407
407
 
408
408
  /** The field as a `url(...)` for `mask-image`. */
409
- export function buildMaskUri(s: SketchSettings, seed = 9, through?: MaskStage): string {
409
+ export function buildMaskUri(s: SketchStyle, seed = 9, through?: MaskStage): string {
410
410
  const key = [...KEYS.map((k) => s[k]), seed, through ?? 'all'].join('|');
411
411
  const hit = cache.get(key);
412
412
  if (hit) return hit;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Sketch effect layer.
3
3
  *
4
- * Builds an SVG filter bank and a stylesheet from one SketchSettings and
4
+ * Builds an SVG filter bank and a stylesheet from one SketchStyle and
5
5
  * injects both into every document cssVarSync tracks, so the host page behind
6
6
  * the overlay iframe gets the same effect the editor's preview shows.
7
7
  *
@@ -16,7 +16,7 @@
16
16
  */
17
17
  import { getSyncedDocuments } from '../cssVarSync';
18
18
  import { buildMaskUri, MASK_TILE } from './maskField';
19
- import type { SketchSettings } from './sketchPresets';
19
+ import type { SketchStyle } from './sketchStyles';
20
20
 
21
21
  const DEFS_ATTR = 'data-sketch-defs';
22
22
  const STYLE_ATTR = 'data-sketch-style';
@@ -325,7 +325,7 @@ const UNCLIPPED = PART_SPECS.filter((p) => !p.clips).map((p) => p.sel).join(', '
325
325
  const CLIPPED = PART_SPECS.filter((p) => p.clips).map((p) => p.sel).join(', ');
326
326
  const UNMASKED = PART_SPECS.filter((p) => p.unmasked).map((p) => p.sel).join(', ');
327
327
 
328
- export function buildDefsMarkup(s: SketchSettings): string {
328
+ export function buildDefsMarkup(s: SketchStyle): string {
329
329
  /**
330
330
  * `warp` is the shape stage: one wave of noise whose wavelength spans a whole
331
331
  * component, so the four corners sample different parts of the field and the
@@ -584,7 +584,7 @@ const swing = (travel: number) => String(Number((travel * 2).toFixed(4)));
584
584
  /** Squares the displacement wave off around 0.5, its zero, so full amplitude is
585
585
  spent along the whole edge rather than only where the wave peaks. Both
586
586
  channels take it: the map reads x from R and y from G. */
587
- function squareOff(s: SketchSettings, from: string, to: string): string {
587
+ function squareOff(s: SketchStyle, from: string, to: string): string {
588
588
  if (s.waveform <= 1) return '';
589
589
  const slope = s.waveform.toFixed(2);
590
590
  const intercept = ((1 - s.waveform) / 2).toFixed(3);
@@ -594,7 +594,7 @@ function squareOff(s: SketchSettings, from: string, to: string): string {
594
594
  `</feComponentTransfer>`;
595
595
  }
596
596
 
597
- const squaredResult = (s: SketchSettings, from: string, to: string) =>
597
+ const squaredResult = (s: SketchStyle, from: string, to: string) =>
598
598
  (s.waveform > 1 ? to : from);
599
599
 
600
600
  /** Along-stroke pressure wavelength. Low, with a high floor in the transfer
@@ -650,7 +650,7 @@ const RETRACE_SEED = 53;
650
650
  const WARP_FREQUENCY = 0.08;
651
651
 
652
652
 
653
- export function buildStylesheet(s: SketchSettings): string {
653
+ export function buildStylesheet(s: SketchStyle): string {
654
654
  const on = '[data-sketch]';
655
655
  const parts = `:is(${PARTS})`;
656
656
  const el = `${on} ${parts}`;
@@ -1083,7 +1083,7 @@ function defsNode(doc: Document): SVGSVGElement {
1083
1083
  * This only makes the effect *available*. An element opts in by carrying
1084
1084
  * data-sketch, which is what `setSketchScope` writes.
1085
1085
  */
1086
- export function applySketchLayer(settings: SketchSettings): void {
1086
+ export function applySketchLayer(settings: SketchStyle): void {
1087
1087
  const defs = buildDefsMarkup(settings);
1088
1088
  const css = buildStylesheet(settings);
1089
1089
  for (const doc of getSyncedDocuments()) {
@@ -1115,7 +1115,7 @@ export function removeSketchLayer(): void {
1115
1115
  * The host page's root and the editor's own preview container are both scopes,
1116
1116
  * which is why this takes an element rather than assuming documentElement.
1117
1117
  */
1118
- export function setSketchScope(el: HTMLElement | null, settings: SketchSettings | null): void {
1118
+ export function setSketchScope(el: HTMLElement | null, settings: SketchStyle | null): void {
1119
1119
  if (!el) return;
1120
1120
  if (!settings) {
1121
1121
  el.removeAttribute('data-sketch');