@motion-proto/live-tokens 0.55.0 → 0.56.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.
- package/.claude/skills/{live-tokens-adjust-shape-space → live-tokens-adjust-geometry}/SKILL.md +5 -5
- package/.claude/skills/live-tokens-build-page/SKILL.md +2 -0
- package/.claude/skills/live-tokens-create-component/SKILL.md +20 -357
- package/.claude/skills/live-tokens-create-component/references/fixed-overlays.md +3 -0
- package/.claude/skills/live-tokens-create-component/references/intrinsics.md +58 -0
- package/.claude/skills/live-tokens-create-component/references/linked-siblings.md +70 -0
- package/.claude/skills/live-tokens-generate-theme/SKILL.md +79 -98
- package/.claude/skills/live-tokens-generate-theme/references/named-themes.md +18 -0
- package/.claude/skills/live-tokens-pair-fonts/SKILL.md +88 -0
- package/.claude/skills/live-tokens-pick-component/SKILL.md +4 -1
- package/CHANGELOG.md +108 -0
- package/README.md +20 -7
- package/bin/cli.mjs +41 -3
- package/bin/set-fonts.mjs +280 -0
- package/dist-plugin/adjust/index.d.cts +2 -2
- package/dist-plugin/adjust/index.d.ts +2 -2
- package/dist-plugin/fontPairing/index.cjs +411 -0
- package/dist-plugin/fontPairing/index.d.cts +109 -0
- package/dist-plugin/fontPairing/index.d.ts +109 -0
- package/dist-plugin/fontPairing/index.js +320 -0
- package/dist-plugin/generateColorsAndType/index.d.cts +2 -2
- package/dist-plugin/generateColorsAndType/index.d.ts +2 -2
- package/dist-plugin/index-4N-Orzzi.d.cts +3 -0
- package/dist-plugin/index-4N-Orzzi.d.ts +3 -0
- package/dist-plugin/{index-DpTIRZ2H.d.cts → themeTypes-BAqtv4XO.d.cts} +1 -3
- package/dist-plugin/{index-DpTIRZ2H.d.ts → themeTypes-BAqtv4XO.d.ts} +1 -3
- package/package.json +3 -2
- package/src/editor/core/fonts/applyFontPairing.ts +157 -0
- package/src/editor/core/fonts/fontPairing.ts +13 -6
- package/src/editor/core/fonts/googleFontsUrl.ts +123 -0
- package/src/editor/core/fonts/weightCoverage.ts +92 -0
- package/src/editor/docs/content/themes-workflow.md +39 -0
- package/src/editor/docs/content.generated.ts +1 -1
- package/src/editor/ui/ColorEditPanel.svelte +16 -1
- package/src/editor/ui/PaletteEditor.svelte +124 -31
- package/src/editor/ui/ProjectFontsSection.svelte +24 -36
- package/src/editor/ui/palette/OverridesPanel.svelte +5 -2
- package/src/editor/ui/palette/PaletteBase.svelte +13 -0
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
parseFontFaceText,
|
|
9
9
|
type ParsedFamily,
|
|
10
10
|
} from '../core/fonts/fontParse';
|
|
11
|
+
import { resolveGoogleFont, type ResolvedGoogleFont } from '../core/fonts/googleFontsUrl';
|
|
11
12
|
import UIPillButton from './UIPillButton.svelte';
|
|
12
13
|
import UISegmentedControl from './UISegmentedControl.svelte';
|
|
13
14
|
import UIInfoPopover from './UIInfoPopover.svelte';
|
|
@@ -19,7 +20,7 @@
|
|
|
19
20
|
let nameInput = $state('');
|
|
20
21
|
let nameError = $state('');
|
|
21
22
|
let nameDiscovering = $state(false);
|
|
22
|
-
let
|
|
23
|
+
let nameResolved: ResolvedGoogleFont | null = $state(null);
|
|
23
24
|
|
|
24
25
|
// Unified paste — accepts a bare URL, `<link>` tag, `@import url(...)`,
|
|
25
26
|
// or one or more `@font-face { ... }` rules. We sniff which on Detect.
|
|
@@ -37,7 +38,7 @@
|
|
|
37
38
|
nameInput = '';
|
|
38
39
|
nameError = '';
|
|
39
40
|
nameDiscovering = false;
|
|
40
|
-
|
|
41
|
+
nameResolved = null;
|
|
41
42
|
pasteInput = '';
|
|
42
43
|
pasteError = '';
|
|
43
44
|
pasteDiscovering = false;
|
|
@@ -110,46 +111,34 @@
|
|
|
110
111
|
pasteDiscovering = false;
|
|
111
112
|
}
|
|
112
113
|
|
|
113
|
-
/** Build a Google Fonts CSS2 URL for a family name, requesting a wide
|
|
114
|
-
* weight range with italics. Works for variable fonts and most static
|
|
115
|
-
* multi-weight families. Single-weight static fonts (e.g. GFS Didot) will
|
|
116
|
-
* reject the range axis with 400 Bad Request — Chrome then CORBs the
|
|
117
|
-
* response. Such fonts must be persisted as `?family=Name&display=swap`. */
|
|
118
|
-
function googleUrlForName(name: string): string {
|
|
119
|
-
const family = name.trim().replace(/\s+/g, '+');
|
|
120
|
-
return `https://fonts.googleapis.com/css2?family=${family}:ital,wght@0,100..900;1,100..900&display=swap`;
|
|
121
|
-
}
|
|
122
|
-
|
|
123
114
|
async function discoverByName() {
|
|
124
115
|
nameError = '';
|
|
125
|
-
|
|
116
|
+
nameResolved = null;
|
|
126
117
|
if (!nameInput.trim()) {
|
|
127
118
|
nameError = 'Enter a family name';
|
|
128
119
|
return;
|
|
129
120
|
}
|
|
130
121
|
nameDiscovering = true;
|
|
131
122
|
try {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
} else {
|
|
136
|
-
nameError = `Couldn't find "${nameInput.trim()}" on Google Fonts`;
|
|
137
|
-
}
|
|
138
|
-
} catch {
|
|
139
|
-
nameError = `Couldn't reach Google Fonts for "${nameInput.trim()}"`;
|
|
123
|
+
nameResolved = await resolveGoogleFont(nameInput, (url) => fetch(url));
|
|
124
|
+
} catch (e) {
|
|
125
|
+
nameError = e instanceof Error ? e.message : `Couldn't reach Google Fonts for "${nameInput.trim()}"`;
|
|
140
126
|
}
|
|
141
127
|
nameDiscovering = false;
|
|
142
128
|
}
|
|
143
129
|
|
|
144
130
|
function addNameSource() {
|
|
145
|
-
if (!
|
|
146
|
-
if (nameDuplicate) return;
|
|
131
|
+
if (!nameResolved || nameDuplicate) return;
|
|
147
132
|
// $state.snapshot() unwraps the reactive proxy. Without it the FontSource
|
|
148
133
|
// we hand to the store carries proxy arrays (weights, etc.) and the next
|
|
149
134
|
// `mutate()` call fails with DataCloneError inside structuredClone.
|
|
150
|
-
const
|
|
151
|
-
const
|
|
152
|
-
|
|
135
|
+
const resolved = $state.snapshot(nameResolved) as ResolvedGoogleFont;
|
|
136
|
+
const family: ParsedFamily = {
|
|
137
|
+
name: resolved.name,
|
|
138
|
+
...(resolved.weights.length > 0 ? { weights: resolved.weights } : {}),
|
|
139
|
+
...(resolved.italics ? { italics: true } : {}),
|
|
140
|
+
};
|
|
141
|
+
commitSources([...fontSourcesList, buildSourceFromUrl(resolved.url, [family])]);
|
|
153
142
|
reset();
|
|
154
143
|
}
|
|
155
144
|
|
|
@@ -166,10 +155,9 @@
|
|
|
166
155
|
return null;
|
|
167
156
|
}
|
|
168
157
|
|
|
169
|
-
let nameDuplicate = $derived.by(() =>
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
});
|
|
158
|
+
let nameDuplicate = $derived.by(() =>
|
|
159
|
+
nameResolved ? findExistingFamilyByName(nameResolved.name) : null,
|
|
160
|
+
);
|
|
173
161
|
|
|
174
162
|
function addUrlSource() {
|
|
175
163
|
const url = extractFontsUrl(pasteInput);
|
|
@@ -339,9 +327,9 @@
|
|
|
339
327
|
class="ui-form-input pf-name-input"
|
|
340
328
|
placeholder="e.g. Inter, Fraunces, Space Mono"
|
|
341
329
|
bind:value={nameInput}
|
|
342
|
-
onkeydown={(e) => { if (e.key === 'Enter' && !
|
|
330
|
+
onkeydown={(e) => { if (e.key === 'Enter' && !nameResolved) discoverByName(); }}
|
|
343
331
|
/>
|
|
344
|
-
{#if
|
|
332
|
+
{#if nameResolved}
|
|
345
333
|
<UIPillButton variant="primary" onclick={addNameSource} disabled={!!nameDuplicate}>Add</UIPillButton>
|
|
346
334
|
{:else}
|
|
347
335
|
<UIPillButton variant="secondary" onclick={discoverByName} disabled={!nameInput.trim() || nameDiscovering}>
|
|
@@ -350,16 +338,16 @@
|
|
|
350
338
|
{/if}
|
|
351
339
|
</div>
|
|
352
340
|
{#if nameError}<div class="pf-error">{nameError}</div>{/if}
|
|
353
|
-
{#if
|
|
341
|
+
{#if nameResolved}
|
|
354
342
|
{#if nameDuplicate}
|
|
355
343
|
<div class="pf-notice">
|
|
356
344
|
<strong>{nameDuplicate}</strong> is already in your project fonts.
|
|
357
345
|
</div>
|
|
358
346
|
{:else}
|
|
359
347
|
<div class="pf-detected">
|
|
360
|
-
Found <strong>{
|
|
361
|
-
{#if
|
|
362
|
-
<span class="pf-check-meta">({
|
|
348
|
+
Found <strong>{nameResolved.name}</strong>
|
|
349
|
+
{#if nameResolved.weights.length > 0}
|
|
350
|
+
<span class="pf-check-meta">({nameResolved.weights.length} weights)</span>
|
|
363
351
|
{/if}
|
|
364
352
|
</div>
|
|
365
353
|
{/if}
|
|
@@ -424,9 +424,12 @@
|
|
|
424
424
|
border-color: var(--ui-border-high);
|
|
425
425
|
}
|
|
426
426
|
|
|
427
|
+
/* Same weight-only selection language as the palette ramp, at the smaller
|
|
428
|
+
swatch's scale. */
|
|
427
429
|
.override-slot.active {
|
|
428
|
-
border-
|
|
429
|
-
|
|
430
|
+
border-style: solid;
|
|
431
|
+
border-color: var(--ui-surface-lowest);
|
|
432
|
+
outline: 2px solid var(--ui-text-primary);
|
|
430
433
|
outline-offset: 1px;
|
|
431
434
|
}
|
|
432
435
|
|
|
@@ -14,6 +14,9 @@
|
|
|
14
14
|
isEditingBase: boolean;
|
|
15
15
|
/** Keep the swatch reading as active while the curve editors are visible. */
|
|
16
16
|
pinnedOpen?: boolean;
|
|
17
|
+
/** The same ring the ramp's anchored step wears, so the two swatches read
|
|
18
|
+
* as the one value they are. */
|
|
19
|
+
selected?: boolean;
|
|
17
20
|
copiedKey: string | null;
|
|
18
21
|
onStartEdit: () => void;
|
|
19
22
|
onCopyBaseHex: (key: string, hex: string, event?: MouseEvent) => void;
|
|
@@ -29,6 +32,7 @@
|
|
|
29
32
|
baseColor,
|
|
30
33
|
isEditingBase,
|
|
31
34
|
pinnedOpen = false,
|
|
35
|
+
selected = false,
|
|
32
36
|
copiedKey,
|
|
33
37
|
onStartEdit,
|
|
34
38
|
onCopyBaseHex,
|
|
@@ -49,6 +53,7 @@
|
|
|
49
53
|
<div
|
|
50
54
|
class="header-swatch"
|
|
51
55
|
class:active={isEditingBase || pinnedOpen}
|
|
56
|
+
class:selected
|
|
52
57
|
style="background: {baseHex}"
|
|
53
58
|
onclick={onStartEdit}
|
|
54
59
|
role="button"
|
|
@@ -150,6 +155,14 @@
|
|
|
150
155
|
outline-offset: 1px;
|
|
151
156
|
}
|
|
152
157
|
|
|
158
|
+
.header-swatch.selected {
|
|
159
|
+
border-color: var(--ui-surface-lowest);
|
|
160
|
+
outline: none;
|
|
161
|
+
box-shadow:
|
|
162
|
+
inset 0 0 0 3px var(--ui-text-primary),
|
|
163
|
+
inset 0 0 0 4px var(--ui-surface-lowest);
|
|
164
|
+
}
|
|
165
|
+
|
|
153
166
|
.editor-label {
|
|
154
167
|
font-size: var(--ui-font-size-xl);
|
|
155
168
|
font-weight: var(--ui-font-weight-semibold);
|