@motion-proto/live-tokens 0.67.0 → 0.68.1

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 (66) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/README.md +67 -9
  3. package/bin/generate-theme.mjs +5 -4
  4. package/dist-plugin/adjust/index.d.cts +1 -1
  5. package/dist-plugin/adjust/index.d.ts +1 -1
  6. package/dist-plugin/{chunk-T4PMCFJN.js → chunk-2UX6EVVA.js} +30 -30
  7. package/dist-plugin/{dataPaths-DZUzVv8H.d.cts → dataPaths-bJTCEO4H.d.cts} +1 -1
  8. package/dist-plugin/{dataPaths-DZUzVv8H.d.ts → dataPaths-bJTCEO4H.d.ts} +1 -1
  9. package/dist-plugin/fontPairing/index.d.cts +1 -1
  10. package/dist-plugin/fontPairing/index.d.ts +1 -1
  11. package/dist-plugin/generateColorsAndType/index.d.cts +1 -1
  12. package/dist-plugin/generateColorsAndType/index.d.ts +1 -1
  13. package/dist-plugin/index.cjs +32 -32
  14. package/dist-plugin/index.d.cts +1 -1
  15. package/dist-plugin/index.d.ts +1 -1
  16. package/dist-plugin/index.js +5 -5
  17. package/dist-plugin/migrateData/index.cjs +30 -30
  18. package/dist-plugin/migrateData/index.d.cts +1 -1
  19. package/dist-plugin/migrateData/index.d.ts +1 -1
  20. package/dist-plugin/migrateData/index.js +1 -1
  21. package/dist-plugin/tokensCssMigrations/index.d.cts +1 -1
  22. package/dist-plugin/tokensCssMigrations/index.d.ts +1 -1
  23. package/package.json +4 -4
  24. package/src/editor/bootstrap.ts +6 -6
  25. package/src/editor/component-editor/ImageLightboxEditor.svelte +1 -1
  26. package/src/editor/component-editor/scaffolding/ComponentFileManager.svelte +4 -4
  27. package/src/editor/core/fonts/fontLoader.ts +2 -2
  28. package/src/editor/core/preview/{lookPreview.ts → themePreview.ts} +27 -27
  29. package/src/editor/core/productionPulse.ts +3 -3
  30. package/src/editor/core/sketch/index.ts +52 -44
  31. package/src/editor/core/sketch/maskField.ts +9 -9
  32. package/src/editor/core/sketch/sketchLayer.ts +9 -9
  33. package/src/editor/core/sketch/sketchRegistry.ts +34 -34
  34. package/src/editor/core/sketch/sketchStore.ts +98 -83
  35. package/src/editor/core/sketch/sketchStyleService.ts +4 -4
  36. package/src/editor/core/sketch/sketchStyles.ts +28 -28
  37. package/src/editor/core/themes/colorsAndTypeService.ts +1 -1
  38. package/src/editor/core/themes/loadRows.ts +8 -8
  39. package/src/editor/core/themes/themeDocumentSync.ts +2 -2
  40. package/src/editor/core/themes/themeInit.ts +2 -2
  41. package/src/editor/core/themes/themeService.ts +17 -17
  42. package/src/editor/core/themes/{lookSummary.ts → themeSummary.ts} +9 -9
  43. package/src/editor/core/themes/themeTypes.ts +10 -6
  44. package/src/editor/docs/content/getting-started.md +1 -1
  45. package/src/editor/docs/content/sketch-mode.md +22 -17
  46. package/src/editor/docs/content/themes-workflow.md +11 -11
  47. package/src/editor/docs/content/where-themes-live.md +6 -6
  48. package/src/editor/docs/content.generated.ts +4 -4
  49. package/src/editor/index.ts +2 -2
  50. package/src/editor/ui/ThemePanel.svelte +52 -52
  51. package/src/editor/ui/sketch/SketchPreview.svelte +2 -2
  52. package/src/editor/ui/sketch/SketchTab.svelte +64 -38
  53. package/src/live-tokens/data/sketch-styles/dry.json +5 -5
  54. package/src/live-tokens/data/sketch-styles/hatched.json +9 -9
  55. package/src/live-tokens/data/sketch-styles/napkin.json +4 -4
  56. package/src/live-tokens/data/sketch-styles/pencil.json +2 -2
  57. package/src/live-tokens/data/themes/autumn.json +1 -1
  58. package/src/live-tokens/data/themes/halloween.json +1 -1
  59. package/src/live-tokens/data/themes/midnight-study.json +1 -1
  60. package/src/live-tokens/data/themes/ocean.json +1 -1
  61. package/src/live-tokens/data/themes/royal-velvet.json +1 -1
  62. package/src/live-tokens/data/themes/sketchy.json +1 -1
  63. package/src/live-tokens/data/themes/spring-meadow.json +1 -1
  64. package/src/live-tokens/data/themes/sunset.json +1 -1
  65. package/src/system/backdrop/backdrop.ts +1 -1
  66. package/src/system/components/SectionDivider.svelte +1 -1
@@ -1,12 +1,12 @@
1
1
  import { derived, get, writable } from 'svelte/store';
2
2
  import {
3
- SKETCH_STYLES,
3
+ SHIPPED_SKETCH_SETTINGS,
4
4
  DEFAULT_SKETCH_STYLE,
5
5
  THEME_SKETCH_ID,
6
- hydrateSketchStyle,
7
- type SketchStyle,
6
+ hydrateSketchSettings,
7
+ type SketchStyleSettings,
8
8
  } from './sketchStyles';
9
- import { lookById, replaceRegisteredLooks, sketchLooks } from './sketchRegistry';
9
+ import { sketchStyleById, replaceRegisteredSketchStyles, sketchStyles } from './sketchRegistry';
10
10
  import {
11
11
  applySketchLayer,
12
12
  hostRoot,
@@ -26,7 +26,9 @@ import {
26
26
 
27
27
  const ENABLED_KEY = 'lt.sketchEnabled';
28
28
  const SETTINGS_KEY = 'lt.sketchSettings';
29
- const STYLE_NAME_KEY = 'lt.sketchStyleName';
29
+ /** The stored key keeps its old spelling. It is a browser's saved selection,
30
+ so renaming the string would drop every visitor's pick on upgrade. */
31
+ const SELECTED_ID_KEY = 'lt.sketchStyleName';
30
32
  const BASELINE_KEY = 'lt.sketchBaseline';
31
33
  /** Set only by a genuine user decision (`markSketchTouched`), never by the
32
34
  `subscribe` write-back below. That write-back plants the four keys above
@@ -66,7 +68,7 @@ export function hasPersistedSketchState(): boolean {
66
68
  }
67
69
 
68
70
  /** Called from every control a user can act on: a dial, a sketchstyle pick, the
69
- on/off switch. Never from `openThemeSketchStyle` or the storage-sync `adopt`
71
+ on/off switch. Never from `openThemeSketchSettings` or the storage-sync `adopt`
70
72
  path, both of which write the same four keys as a side effect of state
71
73
  this browser did not decide on its own. */
72
74
  function markSketchTouched(): void {
@@ -83,20 +85,20 @@ function readEnabled(): boolean {
83
85
  }
84
86
  }
85
87
 
86
- function readSettings(): SketchStyle {
88
+ function readSettings(): SketchStyleSettings {
87
89
  try {
88
90
  const raw = localStorage.getItem(SETTINGS_KEY);
89
- if (raw) return hydrateSketchStyle(JSON.parse(raw));
91
+ if (raw) return hydrateSketchSettings(JSON.parse(raw));
90
92
  } catch {
91
93
  // fall through to the default sketchstyle
92
94
  }
93
- return { ...SKETCH_STYLES[DEFAULT_SKETCH_STYLE] };
95
+ return { ...SHIPPED_SKETCH_SETTINGS[DEFAULT_SKETCH_STYLE] };
94
96
  }
95
97
 
96
- function readBaseline(): SketchStyle | null {
98
+ function readBaseline(): SketchStyleSettings | null {
97
99
  try {
98
100
  const raw = localStorage.getItem(BASELINE_KEY);
99
- if (raw) return hydrateSketchStyle(JSON.parse(raw));
101
+ if (raw) return hydrateSketchSettings(JSON.parse(raw));
100
102
  } catch {
101
103
  // fall through
102
104
  }
@@ -108,14 +110,14 @@ function readBaseline(): SketchStyle | null {
108
110
  it. Stripped on read below; delete a release after that ships. */
109
111
  const RETIRED_USER_PREFIX = 'user:';
110
112
 
111
- /** Deliberately unvalidated. Looks are registered after this module is
113
+ /** Deliberately unvalidated. Sketchstyles are registered after this module is
112
114
  imported, so an id it has never heard of is the normal case rather than a
113
115
  fault: `selectSketchStyle` no-ops on one, and `sketchPick` already reports
114
- a look nothing names as `adjusted`. Only a browser that has stored nothing
116
+ a style nothing names as `adjusted`. Only a browser that has stored nothing
115
117
  falls back. */
116
- function readStyleName(): string {
118
+ function readSelectedId(): string {
117
119
  try {
118
- const name = localStorage.getItem(STYLE_NAME_KEY);
120
+ const name = localStorage.getItem(SELECTED_ID_KEY);
119
121
  if (name !== null) {
120
122
  return name.startsWith(RETIRED_USER_PREFIX) ? name.slice(RETIRED_USER_PREFIX.length) : name;
121
123
  }
@@ -128,39 +130,39 @@ function readStyleName(): string {
128
130
  /** Off by default: the effect paints nothing until the open theme's own
129
131
  sketchstyle, or a decision this browser already recorded, says otherwise. */
130
132
  export const sketchEnabled = writable<boolean>(readEnabled());
131
- export const sketchSettings = writable<SketchStyle>(readSettings());
132
- /** The sketchstyle the dials started from. It survives dial moves, so the grid
133
- keeps showing what the current look is closest to. Any id in the pool, or
134
- `THEME_SKETCH_ID` for the look the open theme carries; empty only when
135
- nothing was picked, or the picked file was deleted. */
136
- export const sketchStyleName = writable<string>(readStyleName());
133
+ export const sketchSettings = writable<SketchStyleSettings>(readSettings());
134
+ /** Id of the sketchstyle the dials started from. It survives dial moves, so the
135
+ grid keeps showing what the current settings are closest to. Any id in the
136
+ pool, or `THEME_SKETCH_ID` for settings the open theme carries that no file
137
+ holds; empty only when nothing was picked, or the picked file was deleted. */
138
+ export const selectedSketchStyleId = writable<string>(readSelectedId());
137
139
 
138
140
  /** The settings as the selected sketchstyle defined them. Kept beside the live
139
141
  settings so "modified" is derived by comparison rather than tracked as a
140
142
  flag: dial a value back to where it started and the tab stops claiming a
141
143
  change, with no bookkeeping to get wrong. */
142
- export const sketchBaseline = writable<SketchStyle | null>(readBaseline());
144
+ export const sketchBaseline = writable<SketchStyleSettings | null>(readBaseline());
143
145
 
144
146
  /** Dial-set fields only. `label` and `blurb` name the sketchstyle rather than
145
- describe the look, and no dial writes them. */
146
- export function sameLook(a: SketchStyle, b: SketchStyle): boolean {
147
- return (Object.keys(a) as (keyof SketchStyle)[])
147
+ describe the style, and no dial writes them. */
148
+ export function sameSketchStyle(a: SketchStyleSettings, b: SketchStyleSettings): boolean {
149
+ return (Object.keys(a) as (keyof SketchStyleSettings)[])
148
150
  .filter((k) => k !== 'label' && k !== 'blurb')
149
151
  .every((k) => a[k] === b[k]);
150
152
  }
151
153
 
152
154
  export const sketchDirty = derived(
153
155
  [sketchSettings, sketchBaseline],
154
- ([settings, baseline]) => baseline !== null && !sameLook(settings, baseline),
156
+ ([settings, baseline]) => baseline !== null && !sameSketchStyle(settings, baseline),
155
157
  );
156
158
 
157
159
  export const sketchBlurb = derived(sketchSettings, (s) => s.blurb);
158
160
 
159
- /** The live look as a theme would carry it: the dials when the effect is
161
+ /** The live style as a theme would carry it: the dials when the effect is
160
162
  on, nothing when it is off (RJC 1). A copy, not the store's own object,
161
163
  so a caller holding onto the result can never observe a later dial move
162
164
  through it. */
163
- export function liveSketchStyle(): SketchStyle | undefined {
165
+ export function liveSketchSettings(): SketchStyleSettings | undefined {
164
166
  return get(sketchEnabled) ? { ...get(sketchSettings) } : undefined;
165
167
  }
166
168
 
@@ -175,25 +177,25 @@ export function setSketchEnabled(enabled: boolean): void {
175
177
 
176
178
  /** What the open theme holds, so "unsaved" is a comparison rather than a
177
179
  flag (RJC 5). Set by every path that opens or saves a theme. */
178
- export const themeSketchStyle = writable<SketchStyle | undefined>(undefined);
180
+ export const themeSketchSettings = writable<SketchStyleSettings | undefined>(undefined);
179
181
 
180
182
  /** Open a theme's sketchstyle: the dials, the on/off state, and the name
181
183
  recovered by comparison (RJC 3). Overwrites the live buffer, which
182
184
  is what opening a theme means everywhere else (RJC 6). The name is recovered
183
- over the whole pool, so a theme carrying a look a saved file also holds is
185
+ over the whole pool, so a theme carrying a style a saved file also holds is
184
186
  named by that file rather than falling back to `THEME_SKETCH_ID`. */
185
- export function openThemeSketchStyle(sketchStyle: SketchStyle | undefined): void {
186
- themeSketchStyle.set(sketchStyle);
187
- if (!sketchStyle) {
187
+ export function openThemeSketchSettings(settings: SketchStyleSettings | undefined): void {
188
+ themeSketchSettings.set(settings);
189
+ if (!settings) {
188
190
  sketchEnabled.set(false);
189
191
  sketchBaseline.set(null);
190
- sketchStyleName.set('');
192
+ selectedSketchStyleId.set('');
191
193
  return;
192
194
  }
193
- const matched = get(sketchLooks).find((look) => sameLook(look.settings, sketchStyle))?.id;
194
- sketchSettings.set({ ...sketchStyle });
195
- sketchBaseline.set({ ...sketchStyle });
196
- sketchStyleName.set(matched ?? THEME_SKETCH_ID);
195
+ const matched = get(sketchStyles).find((style) => sameSketchStyle(style.settings, settings))?.id;
196
+ sketchSettings.set({ ...settings });
197
+ sketchBaseline.set({ ...settings });
198
+ selectedSketchStyleId.set(matched ?? THEME_SKETCH_ID);
197
199
  sketchEnabled.set(true);
198
200
  }
199
201
 
@@ -202,40 +204,40 @@ export function openThemeSketchStyle(sketchStyle: SketchStyle | undefined): void
202
204
  * browser has already decided for itself.
203
205
  *
204
206
  * The rule boot has always followed in dev, and the only one a built site has:
205
- * a visitor who picked a look, or picked None, keeps it, and `themeSketchStyle`
207
+ * a visitor who picked a style, or picked None, keeps it, and `themeSketchSettings`
206
208
  * still learns what the theme holds so the panel can call the difference
207
- * unsaved. Both branches set it, so a picker can offer the theme's look as a
209
+ * unsaved. Both branches set it, so a picker can offer the theme's style as a
208
210
  * row either way.
209
211
  *
210
- * Takes the raw field rather than a `SketchStyle`, and hydrates it here: a
212
+ * Takes the raw field rather than a `SketchStyleSettings`, and hydrates it here: a
211
213
  * built site reads its theme JSON straight off disk with no dev server to run
212
- * `normalizeTheme` over it first, so this is the only place a look stored under
214
+ * `normalizeTheme` over it first, so this is the only place a style stored under
213
215
  * a retired dial name gets carried forward. Anything that is not an object is
214
216
  * the absent case, which is off (invariant 3).
215
217
  */
216
- export function seedSketchFromTheme(sketchStyle: unknown): void {
218
+ export function seedSketchFromTheme(raw: unknown): void {
217
219
  const style =
218
- typeof sketchStyle === 'object' && sketchStyle !== null && !Array.isArray(sketchStyle)
219
- ? hydrateSketchStyle(sketchStyle)
220
+ typeof raw === 'object' && raw !== null && !Array.isArray(raw)
221
+ ? hydrateSketchSettings(raw)
220
222
  : undefined;
221
223
  if (hasPersistedSketchState()) {
222
- themeSketchStyle.set(style);
224
+ themeSketchSettings.set(style);
223
225
  return;
224
226
  }
225
- openThemeSketchStyle(style);
227
+ openThemeSketchSettings(style);
226
228
  }
227
229
 
228
- /** Go back to the look the theme carries, after picking something else. The
229
- theme's is the one look a picker can offer that this module did not ship, so
230
+ /** Go back to the style the theme carries, after picking something else. The
231
+ theme's is the one style a picker can offer that this module did not ship, so
230
232
  it needs a door of its own beside `selectSketchStyle`; `setSketch` gives the
231
233
  two the same face. Silent when the theme carries none, the way
232
234
  `selectSketchStyle` is for a name it does not know. */
233
- export function selectThemeSketchStyle(): void {
234
- const style = get(themeSketchStyle);
235
+ export function selectUnsavedSketchStyle(): void {
236
+ const style = get(themeSketchSettings);
235
237
  if (!style) return;
236
238
  markSketchTouched();
237
239
  if (get(sketchEnabled)) liveMovedSinceBake.set(true);
238
- sketchStyleName.set(THEME_SKETCH_ID);
240
+ selectedSketchStyleId.set(THEME_SKETCH_ID);
239
241
  sketchBaseline.set({ ...style });
240
242
  sketchSettings.set({ ...style });
241
243
  }
@@ -243,13 +245,13 @@ export function selectThemeSketchStyle(): void {
243
245
  /** The live sketch differs from what the open theme carries. Presence is
244
246
  half the comparison: on with dials the theme does not hold, or off while
245
247
  the theme holds a layer, are both off the theme. */
246
- export const sketchOffLook = derived(
247
- [sketchEnabled, sketchSettings, themeSketchStyle],
248
+ export const sketchOffTheme = derived(
249
+ [sketchEnabled, sketchSettings, themeSketchSettings],
248
250
  ([enabled, settings, saved]) => {
249
251
  const live = enabled ? settings : undefined;
250
252
  if (!live && !saved) return false;
251
253
  if (!live || !saved) return true;
252
- return !sameLook(live, saved);
254
+ return !sameSketchStyle(live, saved);
253
255
  },
254
256
  );
255
257
 
@@ -257,13 +259,13 @@ export const sketchOffLook = derived(
257
259
  Silent for an id nothing knows, the way it has always been for an unknown
258
260
  shipped name. */
259
261
  export function selectSketchStyle(id: string): void {
260
- const look = lookById(id);
261
- if (!look) return;
262
+ const style = sketchStyleById(id);
263
+ if (!style) return;
262
264
  markSketchTouched();
263
265
  if (get(sketchEnabled)) liveMovedSinceBake.set(true);
264
- sketchStyleName.set(id);
265
- sketchBaseline.set({ ...look.settings });
266
- sketchSettings.set({ ...look.settings });
266
+ selectedSketchStyleId.set(id);
267
+ sketchBaseline.set({ ...style.settings });
268
+ sketchSettings.set({ ...style.settings });
267
269
  }
268
270
 
269
271
  /** Saved sketchstyles, listed from the data tree. Empty until
@@ -279,7 +281,7 @@ export async function refreshSavedSketchStyles(): Promise<void> {
279
281
  const files = await listSketchStyles();
280
282
  const loaded = await Promise.all(files.map((f) => loadSketchStyle(f.fileName)));
281
283
  savedSketchStyles.set(files);
282
- replaceRegisteredLooks(
284
+ replaceRegisteredSketchStyles(
283
285
  files.map((file, i) => ({
284
286
  id: file.fileName,
285
287
  label: file.name || file.fileName,
@@ -303,23 +305,36 @@ export async function saveCurrentSketchStyle(name: string): Promise<string> {
303
305
  await refreshSavedSketchStyles();
304
306
  sketchSettings.set(settings);
305
307
  sketchBaseline.set({ ...settings });
306
- sketchStyleName.set(fileName);
308
+ selectedSketchStyleId.set(fileName);
307
309
  return fileName;
308
310
  }
309
311
 
310
- /** Overwrite the selected saved sketchstyle. The file name comes from the
311
- selection rather than from re-slugifying the label, because the two can
312
- disagree: a file hand-edited to a new display name would otherwise be saved
313
- beside itself under a fresh slug instead of over itself. The label comes
314
- from the look for the same reason, so the name the grid shows survives.
312
+ /** Overwrite the selected sketchstyle, so a style can be put down and picked up
313
+ again across sessions.
314
+
315
+ The file name comes from the selection rather than from re-slugifying the
316
+ label, because the two can disagree: `dry` is labelled "Dry marker", which
317
+ re-slugified writes `dry-marker.json` and leaves the selected file untouched.
318
+ The label comes from the style for the same reason, so the name the grid shows
319
+ survives.
320
+
321
+ A shipped style saves the same way. Its file lives in the package, so the
322
+ write lands in this project under the same id and shadows it, which is how a
323
+ project comes to own its Pencil; deleting that file uncovers the packaged one
324
+ again. A registered style owns no file at all, so it has none to write over.
315
325
 
316
326
  No `markSketchTouched`: the button only lights once a dial has moved, and
317
327
  every dial goes through `updateSketchSettings`, which marks it. */
318
328
  export async function saveSelectedSketchStyle(): Promise<void> {
319
- const look = lookById(get(sketchStyleName));
320
- if (look?.source !== 'file') throw new Error('No saved sketchstyle is selected');
329
+ const style = sketchStyleById(get(selectedSketchStyleId));
330
+ if (!style) throw new Error('No sketchstyle is selected');
331
+ if (style.source === 'registered') {
332
+ throw new Error(
333
+ `${style.label} was registered in code rather than saved as a file. Use Save As to keep these dials.`,
334
+ );
335
+ }
321
336
  const settings = { ...get(sketchSettings) };
322
- await saveSketchStyle(look.id, look.label, settings);
337
+ await saveSketchStyle(style.id, style.label, settings);
323
338
  await refreshSavedSketchStyles();
324
339
  // Re-baselining is what disables the button again and returns the readout
325
340
  // from "Modified from X" to the saved blurb.
@@ -330,16 +345,16 @@ export async function deleteSavedSketchStyle(fileName: string): Promise<void> {
330
345
  await deleteSketchStyle(fileName);
331
346
  await refreshSavedSketchStyles();
332
347
  // The dials keep their values; only the name stops naming a file that exists.
333
- if (get(sketchStyleName) === fileName) {
334
- sketchStyleName.set('');
348
+ if (get(selectedSketchStyleId) === fileName) {
349
+ selectedSketchStyleId.set('');
335
350
  sketchBaseline.set(null);
336
351
  }
337
352
  }
338
353
 
339
354
  /** Every dial goes through here. The selection deliberately survives: the grid
340
- keeps naming the sketchstyle this look came from, and `sketchDirty` reports
355
+ keeps naming the sketchstyle this style came from, and `sketchDirty` reports
341
356
  the drift. Save writes a new sketchstyle rather than overwriting the base. */
342
- export function updateSketchSettings(patch: Partial<SketchStyle>): void {
357
+ export function updateSketchSettings(patch: Partial<SketchStyleSettings>): void {
343
358
  markSketchTouched();
344
359
  if (get(sketchEnabled)) liveMovedSinceBake.set(true);
345
360
  sketchSettings.update((s) => ({ ...s, ...patch }));
@@ -361,7 +376,7 @@ function persist(key: string, value: string): void {
361
376
  itself between import and first render. */
362
377
  let pageRoot: HTMLElement | null = null;
363
378
 
364
- function render(enabled: boolean, settings: SketchStyle): void {
379
+ function render(enabled: boolean, settings: SketchStyleSettings): void {
365
380
  if (typeof document === 'undefined') return;
366
381
  if (!enabled) {
367
382
  if (sketchLayerInstalled()) removeSketchLayer();
@@ -375,14 +390,14 @@ function render(enabled: boolean, settings: SketchStyle): void {
375
390
  setSketchScope(pageRoot, settings);
376
391
  }
377
392
 
378
- /** Paint one look on the two roots `render` owns, and on nothing else.
393
+ /** Paint one style on the two roots `render` owns, and on nothing else.
379
394
 
380
395
  `render(false, ...)` reaches further: it clears every `[data-sketch]` in
381
396
  the document. The scopes it takes with it belong to `$effect`s that paint
382
397
  from the live stores, which a preview deliberately never ticks, so those
383
398
  never come back. Previewing a theme carrying no sketchstyle left the
384
399
  Sketchstyle view's own stage crisp for good, Cancel included. */
385
- function paintPreviewRoots(style: SketchStyle | undefined): void {
400
+ function paintPreviewRoots(style: SketchStyleSettings | undefined): void {
386
401
  if (typeof document === 'undefined') return;
387
402
  if (style) applySketchLayer(style);
388
403
  setSketchScope(hostRoot(), style ?? null);
@@ -397,7 +412,7 @@ function paintPreviewRoots(style: SketchStyle | undefined): void {
397
412
  host page across the iframe boundary the same way Apply's does; only the
398
413
  store write and the persistence are skipped. Pass `undefined` for a theme
399
414
  that carries no sketchstyle. */
400
- export function previewSketchStyle(style: SketchStyle | undefined): void {
415
+ export function previewSketchStyle(style: SketchStyleSettings | undefined): void {
401
416
  paintPreviewRoots(style);
402
417
  }
403
418
 
@@ -457,7 +472,7 @@ if (typeof document !== 'undefined') {
457
472
  render(get(sketchEnabled), settings);
458
473
  });
459
474
 
460
- sketchStyleName.subscribe((name) => share(STYLE_NAME_KEY, name));
475
+ selectedSketchStyleId.subscribe((name) => share(SELECTED_ID_KEY, name));
461
476
  sketchBaseline.subscribe((b) => share(BASELINE_KEY, b ? JSON.stringify(b) : ''));
462
477
 
463
478
  /* The overlay editor runs in an iframe, so the Sketchstyle view and a
@@ -477,10 +492,10 @@ if (typeof document !== 'undefined') {
477
492
  adopt(SETTINGS_KEY, JSON.stringify(next));
478
493
  sketchSettings.set(next);
479
494
  }
480
- } else if (event.key === STYLE_NAME_KEY) {
481
- const next = readStyleName();
482
- adopt(STYLE_NAME_KEY, next);
483
- if (next !== get(sketchStyleName)) sketchStyleName.set(next);
495
+ } else if (event.key === SELECTED_ID_KEY) {
496
+ const next = readSelectedId();
497
+ adopt(SELECTED_ID_KEY, next);
498
+ if (next !== get(selectedSketchStyleId)) selectedSketchStyleId.set(next);
484
499
  } else if (event.key === BASELINE_KEY) {
485
500
  const current = get(sketchBaseline);
486
501
  if ((event.newValue || '') !== (current ? JSON.stringify(current) : '')) {
@@ -1,11 +1,11 @@
1
1
  import { API_BASE } from '../storage/apiBase';
2
- import { hydrateSketchStyle, type SketchStyle } from './sketchStyles';
2
+ import { hydrateSketchSettings, type SketchStyleSettings } from './sketchStyles';
3
3
 
4
4
  export interface SketchStyleFile {
5
5
  name: string;
6
6
  createdAt?: string;
7
7
  updatedAt?: string;
8
- settings: SketchStyle;
8
+ settings: SketchStyleSettings;
9
9
  }
10
10
 
11
11
  export interface SketchStyleMeta {
@@ -40,13 +40,13 @@ export async function loadSketchStyle(fileName: string): Promise<SketchStyleFile
40
40
  const res = await fetch(`${BASE}/${encodeURIComponent(fileName)}`);
41
41
  if (!res.ok) throw new Error(`Failed to load sketchstyle: ${fileName}`);
42
42
  const body = await res.json();
43
- return { ...body, settings: hydrateSketchStyle(body.settings) };
43
+ return { ...body, settings: hydrateSketchSettings(body.settings) };
44
44
  }
45
45
 
46
46
  export async function saveSketchStyle(
47
47
  fileName: string,
48
48
  name: string,
49
- settings: SketchStyle,
49
+ settings: SketchStyleSettings,
50
50
  ): Promise<void> {
51
51
  const res = await fetch(`${BASE}/${encodeURIComponent(fileName)}`, {
52
52
  method: 'PUT',
@@ -6,7 +6,7 @@ import dashed from '../../../live-tokens/data/sketch-styles/dashed.json';
6
6
  import napkin from '../../../live-tokens/data/sketch-styles/napkin.json';
7
7
  import dry from '../../../live-tokens/data/sketch-styles/dry.json';
8
8
 
9
- export interface SketchStyle {
9
+ export interface SketchStyleSettings {
10
10
  label: string;
11
11
  blurb: string;
12
12
  /** How far the fill's edge travels at its furthest, in px. Every dial that
@@ -80,7 +80,7 @@ export interface SketchStyle {
80
80
  /** Whether the two move together. Unlinked they part, and the field comes out
81
81
  stretched: blobs wider than they are tall read as a wash dragged sideways,
82
82
  the way ink pulled across a page does. Stored rather than inferred from
83
- the pair matching, so a look that stretches to exactly square keeps its
83
+ the pair matching, so a sketchstyle that stretches to exactly square keeps its
84
84
  dials apart. */
85
85
  maskBlobLinked: boolean;
86
86
  /** Output levels on the coverage field, 0 to 1: the palest the fill gets and
@@ -144,40 +144,40 @@ export interface SketchStyle {
144
144
  /**
145
145
  * The shipped sketchstyles, read from the files the package distributes. The
146
146
  * files are the source: a project shadows one by saving a sketchstyle under the
147
- * same id, the editor restores a shipped look by deleting that file, and
147
+ * same id, the editor restores a shipped sketchstyle by deleting that file, and
148
148
  * `themeFileApi` serves these as the read-only fallback behind the project's own
149
- * directory. Editing a look here means editing its JSON, which is what the
149
+ * directory. Editing a sketchstyle here means editing its JSON, which is what the
150
150
  * Sketchstyle view already writes.
151
151
  *
152
152
  * Each file carries every dial, so there is nothing to merge a default into.
153
153
  * `sketchStyles.test.ts` pins that: the seven key sets have to match, and a dial
154
- * added to `SketchStyle` has to reach all seven before the suite goes green.
154
+ * added to `SketchStyleSettings` has to reach all seven before the suite goes green.
155
155
  *
156
156
  * Order is picker order.
157
157
  */
158
158
  const SHIPPED_FILES = { pencil, marker, whiteboard, hatched, dashed, napkin, dry };
159
159
 
160
- export const SKETCH_STYLES: Record<string, SketchStyle> = Object.fromEntries(
161
- Object.entries(SHIPPED_FILES).map(([id, file]) => [id, file.settings as unknown as SketchStyle]),
160
+ export const SHIPPED_SKETCH_SETTINGS: Record<string, SketchStyleSettings> = Object.fromEntries(
161
+ Object.entries(SHIPPED_FILES).map(([id, file]) => [id, file.settings as unknown as SketchStyleSettings]),
162
162
  );
163
163
 
164
164
  export const DEFAULT_SKETCH_STYLE = 'marker';
165
165
 
166
- /** The id of the look a theme carries, in the same id namespace as the shipped
166
+ /** The id for sketch settings a theme carries, in the same id namespace as the shipped
167
167
  sketchstyles so one picker row and one `setSketch` call cover both. Never a
168
- key of `SKETCH_STYLES`: a shipped style claiming it would shadow the theme's
169
- own look in every picker. `index.test.ts` pins that. */
168
+ key of `SHIPPED_SKETCH_SETTINGS`: a shipped style claiming it would shadow the theme's
169
+ own sketchstyle in every picker. `index.test.ts` pins that. */
170
170
  export const THEME_SKETCH_ID = 'theme';
171
171
 
172
172
  /** Reconciled against a full sketchstyle in both directions: a value stored before a
173
173
  control existed picks up the default, and a value stored for a control since
174
174
  retired is dropped. Without the drop, a stale key survives every spread and
175
175
  makes the settings compare unequal to any baseline forever. */
176
- export function hydrateSketchStyle(raw: unknown): SketchStyle {
177
- const base = SKETCH_STYLES[DEFAULT_SKETCH_STYLE];
178
- const stored = (raw ?? {}) as Partial<SketchStyle>;
176
+ export function hydrateSketchSettings(raw: unknown): SketchStyleSettings {
177
+ const base = SHIPPED_SKETCH_SETTINGS[DEFAULT_SKETCH_STYLE];
178
+ const stored = (raw ?? {}) as Partial<SketchStyleSettings>;
179
179
  const out = { ...base };
180
- for (const key of Object.keys(base) as (keyof SketchStyle)[]) {
180
+ for (const key of Object.keys(base) as (keyof SketchStyleSettings)[]) {
181
181
  if (stored[key] !== undefined) (out[key] as unknown) = stored[key];
182
182
  }
183
183
  // A retired option: the fill's presence belongs to the theme, not the effect.
@@ -194,9 +194,9 @@ export function hydrateSketchStyle(raw: unknown): SketchStyle {
194
194
  }
195
195
 
196
196
  /** The second pass used to sit at a distance derived from the stroke width,
197
- with no dial of its own. A look stored before the dial comes back at that
197
+ with no dial of its own. A sketchstyle stored before the dial comes back at that
198
198
  distance rather than at whatever the fallback sketchstyle happens to carry. */
199
- function restoreDerivedRetrace(stored: Record<string, unknown>, out: SketchStyle): void {
199
+ function restoreDerivedRetrace(stored: Record<string, unknown>, out: SketchStyleSettings): void {
200
200
  if (stored.retraceOffset === undefined) {
201
201
  out.retraceOffset = Number(Math.max(1.2, out.strokeWidth * 0.55).toFixed(2));
202
202
  }
@@ -204,7 +204,7 @@ function restoreDerivedRetrace(stored: Record<string, unknown>, out: SketchStyle
204
204
 
205
205
  /** The four displacement dials used to be stated as the map's own `scale`,
206
206
  which is the full swing: the number on the dial was twice the furthest
207
- anything actually moved. They are peak travel in px now, so a look stored
207
+ anything actually moved. They are peak travel in px now, so a sketchstyle stored
208
208
  under the old names comes back halved and renders identically. */
209
209
  const SWING_DIALS = {
210
210
  fillScale: 'fillTravel',
@@ -216,11 +216,11 @@ const SWING_DIALS = {
216
216
  /** The pen wobble used to be stated as `frequency`, in cycles per px, which is
217
217
  the number the filter wants and not one anybody can picture. It is a
218
218
  wavelength in px now, the way the mask states its blobs. */
219
- function convertCyclesToWavelength(stored: Record<string, unknown>, out: SketchStyle): void {
219
+ function convertCyclesToWavelength(stored: Record<string, unknown>, out: SketchStyleSettings): void {
220
220
  const cycles = stored.frequency;
221
221
  if (typeof cycles === 'number' && cycles > 0) out.wobble = Math.round(1 / cycles);
222
222
  // The layer count was `octaves`, and its dial ran to 5. The top two moved
223
- // nothing, so a look stored there comes back at the roughest that reads.
223
+ // nothing, so a sketchstyle stored there comes back at the roughest that reads.
224
224
  // Both were multipliers on the frequency, so they ran the other way.
225
225
  for (const [legacy, key] of [
226
226
  ['borderFrequency', 'borderWavelength'], ['iconFrequency', 'iconWavelength'],
@@ -234,9 +234,9 @@ function convertCyclesToWavelength(stored: Record<string, unknown>, out: SketchS
234
234
  if (typeof layers === 'number') out.roughness = Math.min(3, Math.max(1, layers));
235
235
  }
236
236
 
237
- /** The blob size was one number for both axes. A look stored before the split
238
- comes back square, with the two dials linked, which is the look it had. */
239
- function splitBlobAxes(stored: Record<string, unknown>, out: SketchStyle): void {
237
+ /** The blob size was one number for both axes. A sketchstyle stored before the split
238
+ comes back square, with the two dials linked, which is the shape it had. */
239
+ function splitBlobAxes(stored: Record<string, unknown>, out: SketchStyleSettings): void {
240
240
  const blob = stored.maskBlob;
241
241
  if (typeof blob !== 'number') return;
242
242
  out.maskBlobX = blob;
@@ -249,13 +249,13 @@ function splitBlobAxes(stored: Record<string, unknown>, out: SketchStyle): void
249
249
  of the field, so it came out either untouched or gone. It is a share of the
250
250
  glyph now, and the old default reads as one period across the glyph, which
251
251
  is what that size was aiming at. */
252
- function convertIconTileToScale(stored: Record<string, unknown>, out: SketchStyle): void {
252
+ function convertIconTileToScale(stored: Record<string, unknown>, out: SketchStyleSettings): void {
253
253
  const tile = stored.iconMaskTile;
254
254
  if (typeof tile !== 'number') return;
255
255
  out.iconMaskScale = Math.min(5, Math.max(0.5, Number((tile / 90).toFixed(2))));
256
256
  }
257
257
 
258
- function halveSwingDials(stored: Record<string, unknown>, out: SketchStyle): void {
258
+ function halveSwingDials(stored: Record<string, unknown>, out: SketchStyleSettings): void {
259
259
  for (const [legacy, key] of Object.entries(SWING_DIALS)) {
260
260
  const value = stored[legacy];
261
261
  if (typeof value === 'number') out[key as 'fillTravel'] = value / 2;
@@ -265,7 +265,7 @@ function halveSwingDials(stored: Record<string, unknown>, out: SketchStyle): voi
265
265
  /** The mask used to be a 600-unit tile painted at `maskScale` px, with the
266
266
  coverage point buried in the hardness slope. Recover page-px blobs and
267
267
  softness, and the levels the old slope and floor put the edge at. */
268
- function convertTiledMask(stored: Record<string, unknown>, out: SketchStyle): void {
268
+ function convertTiledMask(stored: Record<string, unknown>, out: SketchStyleSettings): void {
269
269
  const scale = stored.maskScale;
270
270
  if (typeof scale !== 'number') return;
271
271
  const freq = stored.maskFrequency;
@@ -283,7 +283,7 @@ function convertTiledMask(stored: Record<string, unknown>, out: SketchStyle): vo
283
283
  * levels the pair put the edge between, and rescale them onto the field as it
284
284
  * is now: stretched onto its full range before the levels see it.
285
285
  */
286
- function convertCutToLevels(stored: Record<string, unknown>, out: SketchStyle): void {
286
+ function convertCutToLevels(stored: Record<string, unknown>, out: SketchStyleSettings): void {
287
287
  const contrast = stored.maskContrast;
288
288
  const coverage = stored.maskCoverage;
289
289
  if (typeof contrast !== 'number' || typeof coverage !== 'number') return;
@@ -297,9 +297,9 @@ function convertCutToLevels(stored: Record<string, unknown>, out: SketchStyle):
297
297
 
298
298
  /** The pair were input levels, a cut through the field, and are output levels
299
299
  now, the palest and densest the fill gets. The same two numbers carry over:
300
- a look cut between 20% and 50% comes back as a wash between 20% and 50% ink,
300
+ a sketchstyle cut between 20% and 50% comes back as a wash between 20% and 50% ink,
301
301
  which keeps its spread and loses only the hole. */
302
- function carryLevelsToOutput(stored: Record<string, unknown>, out: SketchStyle): void {
302
+ function carryLevelsToOutput(stored: Record<string, unknown>, out: SketchStyleSettings): void {
303
303
  if (typeof stored.maskLevelMin === 'number') out.maskOutputMin = stored.maskLevelMin;
304
304
  if (typeof stored.maskLevelMax === 'number') out.maskOutputMax = stored.maskLevelMax;
305
305
  }
@@ -65,7 +65,7 @@ export const sanitizeFileName = sanitizeFileNameImpl;
65
65
 
66
66
  /** Flush the editor state to the buffer under `displayName`, the name of the
67
67
  * theme the buffer belongs to, and clear the dirty flag. A capture reads the
68
- * buffer back, so this is what makes Save and Adopt mean the look on screen. */
68
+ * buffer back, so this is what makes Save and Adopt mean the theme on screen. */
69
69
  export async function persistColorsAndType(
70
70
  state: EditorState,
71
71
  displayName: string,
@@ -1,7 +1,7 @@
1
1
  import type { ThemeMeta, ColorsAndTypeMeta } from './themeTypes';
2
2
 
3
3
  /**
4
- * The one Load list. A theme is the whole look; a colors and type file is a
4
+ * The one Load list. A theme is the whole theme; a colors and type file is a
5
5
  * preset holding that half of one. Both belong in the same window, told apart
6
6
  * by a badge rather than by living in separate managers.
7
7
  *
@@ -10,7 +10,7 @@ import type { ThemeMeta, ColorsAndTypeMeta } from './themeTypes';
10
10
  * that copy is exactly the file the list must keep reachable.
11
11
  */
12
12
 
13
- export type LoadRowKind = 'look' | 'layer';
13
+ export type LoadRowKind = 'theme' | 'layer';
14
14
 
15
15
  export interface LoadRow {
16
16
  /** `<kind>:<slug>`. The two kinds are separate resources with separate name
@@ -25,12 +25,12 @@ export interface LoadRow {
25
25
 
26
26
  export const loadRowId = (kind: LoadRowKind, slug: string): string => `${kind}:${slug}`;
27
27
 
28
- export function buildLoadRows(looks: ThemeMeta[], layers: ColorsAndTypeMeta[]): LoadRow[] {
29
- const lookRows: LoadRow[] = looks
28
+ export function buildLoadRows(themes: ThemeMeta[], layers: ColorsAndTypeMeta[]): LoadRow[] {
29
+ const themeRows: LoadRow[] = themes
30
30
  .map((f): LoadRow => ({
31
- fileName: loadRowId('look', f.fileName),
31
+ fileName: loadRowId('theme', f.fileName),
32
32
  slug: f.fileName,
33
- kind: 'look',
33
+ kind: 'theme',
34
34
  name: f.name,
35
35
  updatedAt: f.updatedAt,
36
36
  isProtected: f.isProtected,
@@ -46,12 +46,12 @@ export function buildLoadRows(looks: ThemeMeta[], layers: ColorsAndTypeMeta[]):
46
46
  updatedAt: f.updatedAt,
47
47
  isProtected: false,
48
48
  }));
49
- return [...lookRows, ...layerRows];
49
+ return [...themeRows, ...layerRows];
50
50
  }
51
51
 
52
52
  /**
53
53
  * Whether picking this row loads colors and type alone. A layer file holds
54
- * nothing else, so it ignores the toggle; a look honors it.
54
+ * nothing else, so it ignores the toggle; a theme honors it.
55
55
  */
56
56
  export function isColorsOnly(row: LoadRow | null, colorsOnly: boolean): boolean {
57
57
  if (row?.kind === 'layer') return true;