@motion-proto/live-tokens 0.94.3 → 0.95.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.
- package/.claude/skills/live-tokens-create-page/SKILL.md +7 -3
- package/CHANGELOG.md +97 -0
- package/README.md +6 -6
- package/dist-plugin/{chunk-7XSUHJUH.js → chunk-4J6JPSVS.js} +53 -2
- package/dist-plugin/{chunk-W5VQV32K.js → chunk-GYRULB2X.js} +1 -1
- package/dist-plugin/{chunk-ONYAIXCZ.js → chunk-QQASYPW7.js} +32 -9
- package/dist-plugin/index.cjs +598 -17
- package/dist-plugin/index.d.cts +9 -1
- package/dist-plugin/index.d.ts +9 -1
- package/dist-plugin/index.js +513 -6
- package/dist-plugin/migrateData/index.cjs +32 -9
- package/dist-plugin/migrateData/index.js +2 -2
- package/dist-plugin/setColors/index.cjs +32 -22
- package/dist-plugin/setColors/index.js +2 -15
- package/dist-plugin/setGeometry/index.cjs +32 -9
- package/dist-plugin/setGeometry/index.js +1 -1
- package/dist-plugin/tokensCssMigrations/index.cjs +53 -2
- package/dist-plugin/tokensCssMigrations/index.d.cts +9 -2
- package/dist-plugin/tokensCssMigrations/index.d.ts +9 -2
- package/dist-plugin/tokensCssMigrations/index.js +1 -1
- package/package.json +8 -3
- package/src/demo/Demo.svelte +39 -5
- package/src/demo/DemoBar.svelte +23 -5
- package/src/demo/FloatingTokenTags.css +33 -4
- package/src/demo/FloatingTokenTags.svelte +81 -68
- package/src/demo/Section.svelte +1 -7
- package/src/demo/SourceLinks.svelte +5 -4
- package/src/demo/TestingLoops.svelte +128 -19
- package/src/demo/evalResults.ts +7 -7
- package/src/demo/sections/SectionClaude.svelte +20 -13
- package/src/demo/sections/SectionFooter.svelte +4 -5
- package/src/demo/sections/SectionGetStarted.svelte +5 -6
- package/src/demo/sections/SectionHero.svelte +43 -23
- package/src/demo/sections/SectionKit.svelte +141 -45
- package/src/demo/sections/SectionLayers.svelte +3 -3
- package/src/demo/sections/SectionOffering.svelte +8 -23
- package/src/editor/bootstrap.ts +12 -6
- package/src/editor/component-editor/ImageEditor.svelte +1 -1
- package/src/editor/component-editor/ImageLightboxEditor.svelte +3 -4
- package/src/editor/component-editor/registration.ts +36 -0
- package/src/editor/component-editor/registry.ts +5 -12
- package/src/editor/component-editor/scaffolding/ShadowBackdrop.svelte +11 -2
- package/src/editor/component-editor/scaffolding/StateBlock.svelte +7 -0
- package/src/editor/component-editor/scaffolding/TokenLayout.svelte +1 -2
- package/src/editor/component-editor/scaffolding/VariantGroup.svelte +17 -2
- package/src/editor/core/store/editorPersistence.ts +6 -1
- package/src/editor/core/store/editorRenderer.ts +1 -4
- package/src/editor/core/store/editorStore.ts +5 -9
- package/src/editor/core/store/editorTypes.ts +2 -2
- package/src/editor/core/themes/migrations/2026-10-15-responsive-columns.ts +40 -0
- package/src/editor/core/themes/migrations/index.ts +2 -0
- package/src/editor/core/themes/slices/columns.ts +112 -42
- package/src/editor/docs/Docs.svelte +25 -22
- package/src/editor/docs/chapters.ts +1 -0
- package/src/editor/docs/content/editing-tokens.md +5 -3
- package/src/editor/docs/content/production-css.md +116 -0
- package/src/editor/docs/content/themes-workflow.md +2 -0
- package/src/editor/docs/content.generated.ts +3 -2
- package/src/editor/index.ts +2 -1
- package/src/editor/overlay/LiveEditorOverlay.svelte +67 -19
- package/src/editor/overlay/LiveTokensRouter.svelte +8 -4
- package/src/editor/pages/Editor.svelte +15 -1
- package/src/editor/pages/EditorShell.svelte +312 -99
- package/src/editor/skill-atlas/SkillAtlas.svelte +111 -24
- package/src/editor/skill-atlas/TreeCanvas.svelte +14 -0
- package/src/editor/skill-atlas/skillSources.generated.ts +1 -1
- package/src/editor/skill-atlas/trees/create-page.ts +24 -24
- package/src/editor/skill-atlas/wireLayout.ts +62 -0
- package/src/editor/ui/BezierCurveEditor.svelte +3 -0
- package/src/editor/ui/FontStackEditor.svelte +2 -2
- package/src/editor/ui/PaletteEditor.svelte +64 -6
- package/src/editor/ui/UIPaletteSelector.svelte +2 -0
- package/src/editor/ui/colors/ColorsTab.svelte +5 -2
- package/src/editor/ui/palette/OverridesPanel.svelte +1 -0
- package/src/editor/ui/palette/ScaleCurveEditor.svelte +127 -18
- package/src/editor/ui/sections/ColumnsSection.svelte +79 -49
- package/src/editor/ui/sections/ShadowsSection.svelte +14 -0
- package/src/editor/ui/sections/WashesSection.svelte +11 -0
- package/src/editor/ui/sketch/SketchTab.svelte +1 -1
- package/src/live-tokens/data/colors-and-type/autumn.json +1 -4
- package/src/live-tokens/data/colors-and-type/default.json +1 -4
- package/src/live-tokens/data/colors-and-type/halloween.json +1 -4
- package/src/live-tokens/data/colors-and-type/midnight-study.json +1 -4
- package/src/live-tokens/data/colors-and-type/ocean.json +1 -4
- package/src/live-tokens/data/colors-and-type/royal-velvet.json +1 -4
- package/src/live-tokens/data/colors-and-type/sketchy.json +1 -4
- package/src/live-tokens/data/colors-and-type/spring-meadow.json +1 -4
- package/src/live-tokens/data/colors-and-type/sunset.json +1 -4
- package/src/live-tokens/data/themes/autumn.json +1 -4
- package/src/live-tokens/data/themes/halloween.json +1 -4
- package/src/live-tokens/data/themes/midnight-study.json +1 -4
- package/src/live-tokens/data/themes/ocean.json +1 -4
- package/src/live-tokens/data/themes/royal-velvet.json +1 -4
- package/src/live-tokens/data/themes/sketchy.json +1 -4
- package/src/live-tokens/data/themes/spring-meadow.json +1 -4
- package/src/live-tokens/data/themes/sunset.json +1 -4
- package/src/live-tokens/data/tokens.generated.css +1 -4
- package/src/system/components/SectionDivider.svelte +4 -0
- package/src/system/styles/tokens.css +30 -4
- package/template/src/main.ts +2 -2
- package/template/src/pages/Home.svelte +11 -8
- package/template/vite.config.ts +7 -3
- package/src/system/assets/offering.webp +0 -0
- /package/dist-plugin/{chunk-O6GQ6GBH.js → chunk-A4TBP4MM.js} +0 -0
package/src/demo/DemoBar.svelte
CHANGED
|
@@ -40,11 +40,13 @@
|
|
|
40
40
|
sections beneath it. The right side stays empty for the editor's collapsed
|
|
41
41
|
toggle, which pins top-right. */
|
|
42
42
|
.bar {
|
|
43
|
+
box-sizing: content-box;
|
|
43
44
|
max-width: var(--columns-max-width);
|
|
44
45
|
margin-inline: auto;
|
|
45
|
-
padding: var(--space-12)
|
|
46
|
+
padding-block: var(--space-12);
|
|
47
|
+
padding-inline: var(--columns-margin);
|
|
46
48
|
display: grid;
|
|
47
|
-
grid-template-columns: repeat(var(--columns-count), 1fr);
|
|
49
|
+
grid-template-columns: repeat(var(--columns-count), minmax(0, 1fr));
|
|
48
50
|
column-gap: var(--columns-gutter);
|
|
49
51
|
}
|
|
50
52
|
|
|
@@ -57,20 +59,36 @@
|
|
|
57
59
|
row-gap: var(--space-8);
|
|
58
60
|
}
|
|
59
61
|
|
|
60
|
-
@media (max-width:
|
|
62
|
+
@media (max-width: 1200px) {
|
|
61
63
|
.pickers {
|
|
62
64
|
grid-column: 1 / -1;
|
|
63
65
|
}
|
|
64
66
|
}
|
|
65
67
|
|
|
66
68
|
/* No room beside the editor's toggle on a phone, so the pickers sit in the
|
|
67
|
-
page below it and scroll away
|
|
69
|
+
page below it and scroll away, clearing the toggle only when it rests in
|
|
70
|
+
the top band. */
|
|
68
71
|
@media (max-width: 600px) {
|
|
69
72
|
.demo-bar {
|
|
70
73
|
position: static;
|
|
71
74
|
}
|
|
72
75
|
.bar {
|
|
73
|
-
padding: var(--space-
|
|
76
|
+
padding-block: max(var(--space-16), calc(var(--lt-pill-clearance, 0px) + var(--space-8))) var(--space-16);
|
|
77
|
+
}
|
|
78
|
+
/* Side by side, each label over a trigger that fills its half. */
|
|
79
|
+
.pickers {
|
|
80
|
+
display: grid;
|
|
81
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
82
|
+
gap: var(--space-12);
|
|
83
|
+
}
|
|
84
|
+
.pickers :global(.chrome-select) {
|
|
85
|
+
flex-direction: column;
|
|
86
|
+
align-items: stretch;
|
|
87
|
+
gap: var(--ui-space-4);
|
|
88
|
+
}
|
|
89
|
+
.pickers :global(.chrome-select-trigger) {
|
|
90
|
+
width: 100%;
|
|
91
|
+
min-width: 0;
|
|
74
92
|
}
|
|
75
93
|
}
|
|
76
94
|
</style>
|
|
@@ -79,8 +79,10 @@
|
|
|
79
79
|
|
|
80
80
|
.ftt-string {
|
|
81
81
|
stroke: var(--ftt-contrast-color);
|
|
82
|
+
fill: none;
|
|
82
83
|
stroke-width: 2;
|
|
83
84
|
stroke-linecap: round;
|
|
85
|
+
stroke-linejoin: round;
|
|
84
86
|
vector-effect: non-scaling-stroke;
|
|
85
87
|
opacity: var(--ftt-string-opacity);
|
|
86
88
|
transition: opacity 150ms ease, stroke 150ms ease;
|
|
@@ -124,8 +126,9 @@
|
|
|
124
126
|
|
|
125
127
|
/* --- Floating tag wrapper ------------------------------------------------- */
|
|
126
128
|
.ftt-float {
|
|
129
|
+
--ftt-shift-x: -50%;
|
|
127
130
|
position: absolute;
|
|
128
|
-
translate: -
|
|
131
|
+
translate: var(--ftt-shift-x) -50%;
|
|
129
132
|
rotate: var(--ftt-rot, 0deg);
|
|
130
133
|
animation: ftt-bob var(--ftt-bob-duration, 7s) ease-in-out infinite;
|
|
131
134
|
z-index: 4;
|
|
@@ -134,7 +137,7 @@
|
|
|
134
137
|
flex-direction: column;
|
|
135
138
|
align-items: center;
|
|
136
139
|
gap: 0.25rem;
|
|
137
|
-
width:
|
|
140
|
+
width: max-content;
|
|
138
141
|
}
|
|
139
142
|
|
|
140
143
|
/* Lift open tag so its dropdown paints over sibling tags. */
|
|
@@ -159,8 +162,8 @@
|
|
|
159
162
|
}
|
|
160
163
|
|
|
161
164
|
@keyframes ftt-bob {
|
|
162
|
-
0%, 100% { translate: -
|
|
163
|
-
50% { translate: -
|
|
165
|
+
0%, 100% { translate: var(--ftt-shift-x) -50%; }
|
|
166
|
+
50% { translate: var(--ftt-shift-x) calc(-50% + var(--ftt-bob-distance, -8px)); }
|
|
164
167
|
}
|
|
165
168
|
|
|
166
169
|
/* --- Property label paired with each tag ---------------------------------- */
|
|
@@ -272,6 +275,32 @@
|
|
|
272
275
|
z-index: 10;
|
|
273
276
|
}
|
|
274
277
|
|
|
278
|
+
/* --- Stacked layout ------------------------------------------------------ */
|
|
279
|
+
/* Narrow stage: tags hang in a column left of the box and the strings elbow
|
|
280
|
+
into its top and bottom edges. `left` positions each tag's left edge. */
|
|
281
|
+
.ftt-stacked .ftt-box {
|
|
282
|
+
left: auto;
|
|
283
|
+
right: 0;
|
|
284
|
+
translate: 0 -50%;
|
|
285
|
+
width: 10rem;
|
|
286
|
+
padding: 0.75rem 1rem;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
.ftt-stacked .ftt-float {
|
|
290
|
+
--ftt-shift-x: 0%;
|
|
291
|
+
rotate: 0deg;
|
|
292
|
+
align-items: flex-start;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
.ftt-stacked .ftt-float-property {
|
|
296
|
+
padding-left: 0.75rem;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
.ftt-stacked .ftt-dropdown-wrap {
|
|
300
|
+
left: 0;
|
|
301
|
+
translate: none;
|
|
302
|
+
}
|
|
303
|
+
|
|
275
304
|
/* --- Reduced motion ------------------------------------------------------- */
|
|
276
305
|
@media (prefers-reduced-motion: reduce) {
|
|
277
306
|
.ftt-float {
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
/** Tag center, in % of the stage. */
|
|
19
19
|
top: number;
|
|
20
20
|
left: number;
|
|
21
|
+
/** Stacked layout for a stage narrower than `NARROW_STAGE_PX`: tags form a
|
|
22
|
+
* column left of the box, `left` is the tag's left edge, and the string
|
|
23
|
+
* elbows into `anchor`. Nest anchors so higher tags land further right. */
|
|
24
|
+
narrow?: { top: number; left: number; anchor: Anchor };
|
|
21
25
|
/** Bob delay, seconds. Negative values offset the start. */
|
|
22
26
|
delay?: number;
|
|
23
27
|
/** Static tilt, degrees. */
|
|
@@ -34,6 +38,7 @@
|
|
|
34
38
|
|
|
35
39
|
<script lang="ts">
|
|
36
40
|
import MenuSelect from '../system/components/MenuSelect.svelte';
|
|
41
|
+
import { untrack } from 'svelte';
|
|
37
42
|
import { SvelteMap } from 'svelte/reactivity';
|
|
38
43
|
import { contrastTokenForBackground } from '../system/backdrop';
|
|
39
44
|
// `.ftt-tag` is hand-rolled (not Badge) so editing badge-* tokens doesn't
|
|
@@ -72,6 +77,7 @@
|
|
|
72
77
|
icon: 'fas fa-fill-drip',
|
|
73
78
|
label: '--surface-brand-high',
|
|
74
79
|
top: 15, left: 20, delay: 0, rotate: 3,
|
|
80
|
+
narrow: { top: 6, left: 0, anchor: { side: 'top', pos: 90 } },
|
|
75
81
|
anchor: { side: 'inside', x: 10, y: 40 },
|
|
76
82
|
controls: 'surface',
|
|
77
83
|
placement: 'top',
|
|
@@ -81,6 +87,7 @@
|
|
|
81
87
|
icon: 'fas fa-font',
|
|
82
88
|
label: '--font-display',
|
|
83
89
|
top: 5, left: 45, delay: -0.9, rotate: 2,
|
|
90
|
+
narrow: { top: 20, left: 5, anchor: { side: 'top', pos: 64 } },
|
|
84
91
|
anchor: { side: 'inside', x: 78, y: 28 },
|
|
85
92
|
controls: 'font-family',
|
|
86
93
|
placement: 'top',
|
|
@@ -89,6 +96,7 @@
|
|
|
89
96
|
icon: 'fa-solid fa-bezier-curve',
|
|
90
97
|
label: '--radius-2xl',
|
|
91
98
|
top: 23, left: 75, delay: -1.8, rotate: 2,
|
|
99
|
+
narrow: { top: 34, left: 10, anchor: { side: 'top', pos: 38 } },
|
|
92
100
|
anchor: { side: 'top', pos: 100 },
|
|
93
101
|
controls: 'radius',
|
|
94
102
|
placement: 'top',
|
|
@@ -97,6 +105,7 @@
|
|
|
97
105
|
icon: 'fas fa-paint-roller',
|
|
98
106
|
label: '--border-brand-strong',
|
|
99
107
|
top: 79, left: 72, delay: -3.6, rotate: -2,
|
|
108
|
+
narrow: { top: 82, left: 8, anchor: { side: 'bottom', pos: 84 } },
|
|
100
109
|
anchor: { side: 'bottom', pos: 75 },
|
|
101
110
|
controls: 'border-color',
|
|
102
111
|
placement: 'top',
|
|
@@ -105,6 +114,7 @@
|
|
|
105
114
|
icon: 'fas fa-grip-lines',
|
|
106
115
|
label: '--border-width-3',
|
|
107
116
|
top: 79, left: 28, delay: -5.2, rotate: -4,
|
|
117
|
+
narrow: { top: 66, left: 3, anchor: { side: 'bottom', pos: 44 } },
|
|
108
118
|
anchor: { side: 'bottom', pos: 25 },
|
|
109
119
|
controls: 'border-width',
|
|
110
120
|
placement: 'top',
|
|
@@ -138,8 +148,17 @@
|
|
|
138
148
|
const DRAG_THRESHOLD_PX = 4;
|
|
139
149
|
const dragStart = { px: 0, py: 0, tagIdx: -1, moved: false };
|
|
140
150
|
|
|
141
|
-
|
|
142
|
-
|
|
151
|
+
const NARROW_STAGE_PX = 480;
|
|
152
|
+
let stageWidth = $state(0);
|
|
153
|
+
const stacked = $derived(
|
|
154
|
+
stageWidth > 0 && stageWidth < NARROW_STAGE_PX && tags.every(t => t.narrow),
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
function home(i: number): { top: number; left: number; anchor: Anchor } {
|
|
158
|
+
return (stacked && tags[i].narrow) || tags[i];
|
|
159
|
+
}
|
|
160
|
+
function tagTop(i: number): number { return dragOverrides[i]?.top ?? home(i).top; }
|
|
161
|
+
function tagLeft(i: number): number { return dragOverrides[i]?.left ?? home(i).left; }
|
|
143
162
|
|
|
144
163
|
type BallState = { startedAt: number; duration: number; pendingValue: string };
|
|
145
164
|
const ballStates = new SvelteMap<number, BallState>();
|
|
@@ -147,34 +166,38 @@
|
|
|
147
166
|
const ballEls: HTMLSpanElement[] = $state([]);
|
|
148
167
|
|
|
149
168
|
let stageEl: HTMLDivElement | undefined = $state();
|
|
169
|
+
let svgEl: SVGSVGElement | undefined = $state();
|
|
150
170
|
let boxEl: HTMLDivElement | undefined = $state();
|
|
151
171
|
const tagEls: HTMLSpanElement[] = $state([]);
|
|
152
|
-
const
|
|
172
|
+
const pathEls: SVGPathElement[] = $state([]);
|
|
153
173
|
const knotEls: HTMLSpanElement[] = $state([]);
|
|
154
174
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
cy = 50,
|
|
159
|
-
w = boxSize.w,
|
|
160
|
-
h = boxSize.h,
|
|
161
|
-
): { x: number; y: number } {
|
|
162
|
-
const halfW = w / 2, halfH = h / 2;
|
|
175
|
+
type Rect = { x: number; y: number; w: number; h: number };
|
|
176
|
+
|
|
177
|
+
function anchorPoint(anchor: Anchor, box: Rect): { x: number; y: number } {
|
|
163
178
|
if (anchor.side === 'inside') {
|
|
164
|
-
return {
|
|
165
|
-
x: cx - halfW + (anchor.x / 100) * w,
|
|
166
|
-
y: cy - halfH + (anchor.y / 100) * h,
|
|
167
|
-
};
|
|
179
|
+
return { x: box.x + (anchor.x / 100) * box.w, y: box.y + (anchor.y / 100) * box.h };
|
|
168
180
|
}
|
|
169
181
|
const t = anchor.pos / 100;
|
|
170
182
|
switch (anchor.side) {
|
|
171
|
-
case 'top': return { x:
|
|
172
|
-
case 'bottom': return { x:
|
|
173
|
-
case 'left': return { x:
|
|
174
|
-
case 'right': return { x:
|
|
183
|
+
case 'top': return { x: box.x + t * box.w, y: box.y };
|
|
184
|
+
case 'bottom': return { x: box.x + t * box.w, y: box.y + box.h };
|
|
185
|
+
case 'left': return { x: box.x, y: box.y + t * box.h };
|
|
186
|
+
case 'right': return { x: box.x + box.w, y: box.y + t * box.h };
|
|
175
187
|
}
|
|
176
188
|
}
|
|
177
189
|
|
|
190
|
+
const ELBOW_RADIUS_PX = 14;
|
|
191
|
+
const px = (n: number) => n.toFixed(2);
|
|
192
|
+
|
|
193
|
+
// Runs level out of the tag, then turns through a rounded corner into the box.
|
|
194
|
+
function elbowPath(x1: number, y1: number, x2: number, y2: number): string {
|
|
195
|
+
const sx = Math.sign(x2 - x1), sy = Math.sign(y2 - y1);
|
|
196
|
+
const r = Math.min(ELBOW_RADIUS_PX, Math.abs(x2 - x1), Math.abs(y2 - y1));
|
|
197
|
+
return `M${px(x1)} ${px(y1)} H${px(x2 - sx * r)} `
|
|
198
|
+
+ `Q${px(x2)} ${px(y1)} ${px(x2)} ${px(y1 + sy * r)} V${px(y2)}`;
|
|
199
|
+
}
|
|
200
|
+
|
|
178
201
|
// Reads `boxValues` (not `currentValues`) so the box repaints at impact.
|
|
179
202
|
function resolveControl(name: TagControl): string | undefined {
|
|
180
203
|
const idx = tags.findIndex(t => t.controls === name);
|
|
@@ -216,31 +239,35 @@
|
|
|
216
239
|
|
|
217
240
|
// Kite strings and energy balls are recomputed each frame from the box's
|
|
218
241
|
// measured rect so intrinsic sizing drives anchor placement.
|
|
242
|
+
let lastViewBox = '';
|
|
219
243
|
function syncFrame() {
|
|
220
|
-
if (!stageEl) return;
|
|
244
|
+
if (!stageEl || !svgEl || !boxEl) return;
|
|
221
245
|
syncConnectorContrast();
|
|
222
246
|
syncBoxLabelContrast();
|
|
223
247
|
const stageRect = stageEl.getBoundingClientRect();
|
|
224
248
|
if (stageRect.width === 0 || stageRect.height === 0) return;
|
|
225
249
|
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
boxCx = ((br.left + br.width / 2) - stageRect.left) / stageRect.width * 100;
|
|
232
|
-
boxCy = ((br.top + br.height / 2) - stageRect.top) / stageRect.height * 100;
|
|
233
|
-
boxW = (br.width / stageRect.width) * 100;
|
|
234
|
-
boxH = (br.height / stageRect.height) * 100;
|
|
235
|
-
}
|
|
250
|
+
// A pixel viewBox keeps the elbow corners round on any stage aspect.
|
|
251
|
+
const viewBox = `0 0 ${px(stageRect.width)} ${px(stageRect.height)}`;
|
|
252
|
+
if (viewBox !== lastViewBox) {
|
|
253
|
+
svgEl.setAttribute('viewBox', viewBox);
|
|
254
|
+
lastViewBox = viewBox;
|
|
236
255
|
}
|
|
237
256
|
|
|
257
|
+
const br = boxEl.getBoundingClientRect();
|
|
258
|
+
const box: Rect = {
|
|
259
|
+
x: br.left - stageRect.left,
|
|
260
|
+
y: br.top - stageRect.top,
|
|
261
|
+
w: br.width,
|
|
262
|
+
h: br.height,
|
|
263
|
+
};
|
|
264
|
+
|
|
238
265
|
const now = performance.now();
|
|
239
266
|
|
|
240
267
|
for (let i = 0; i < tags.length; i++) {
|
|
241
268
|
const tagEl = tagEls[i];
|
|
242
|
-
const
|
|
243
|
-
if (!tagEl || !
|
|
269
|
+
const pathEl = pathEls[i];
|
|
270
|
+
if (!tagEl || !pathEl) continue;
|
|
244
271
|
|
|
245
272
|
// Tag endpoint: center of the pill's rounded cap on the box-facing side.
|
|
246
273
|
const pill = tagEl.querySelector('.ftt-tag') as HTMLElement | null;
|
|
@@ -254,23 +281,19 @@
|
|
|
254
281
|
radius = Math.min(raw, r.height / 2);
|
|
255
282
|
}
|
|
256
283
|
|
|
257
|
-
const
|
|
258
|
-
const
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
const x1 = (px - stageRect.left) / stageRect.width * 100;
|
|
262
|
-
const y1 = (py - stageRect.top) / stageRect.height * 100;
|
|
263
|
-
|
|
264
|
-
lineEl.setAttribute('x1', x1.toFixed(3));
|
|
265
|
-
lineEl.setAttribute('y1', y1.toFixed(3));
|
|
284
|
+
const capOnRight = stacked || tagLeft(i) <= 50;
|
|
285
|
+
const x1 = (capOnRight ? r.right - radius : r.left + radius) - stageRect.left;
|
|
286
|
+
const y1 = r.top + r.height / 2 - stageRect.top;
|
|
266
287
|
|
|
267
|
-
const a = anchorPoint(
|
|
268
|
-
|
|
269
|
-
|
|
288
|
+
const a = anchorPoint(home(i).anchor, box);
|
|
289
|
+
pathEl.setAttribute(
|
|
290
|
+
'd',
|
|
291
|
+
stacked ? elbowPath(x1, y1, a.x, a.y) : `M${px(x1)} ${px(y1)} L${px(a.x)} ${px(a.y)}`,
|
|
292
|
+
);
|
|
270
293
|
const knotEl = knotEls[i];
|
|
271
294
|
if (knotEl) {
|
|
272
|
-
knotEl.style.left = `${a.x
|
|
273
|
-
knotEl.style.top = `${a.y
|
|
295
|
+
knotEl.style.left = `${px(a.x)}px`;
|
|
296
|
+
knotEl.style.top = `${px(a.y)}px`;
|
|
274
297
|
}
|
|
275
298
|
|
|
276
299
|
const ballEl = ballEls[i];
|
|
@@ -290,13 +313,10 @@
|
|
|
290
313
|
triggerBloop();
|
|
291
314
|
continue;
|
|
292
315
|
}
|
|
293
|
-
const x2 = parseFloat(lineEl.getAttribute('x2') || '0');
|
|
294
|
-
const y2 = parseFloat(lineEl.getAttribute('y2') || '0');
|
|
295
316
|
const eased = 1 - Math.pow(1 - t, 3);
|
|
296
|
-
const
|
|
297
|
-
|
|
298
|
-
ballEl.style.
|
|
299
|
-
ballEl.style.top = `${by.toFixed(3)}%`;
|
|
317
|
+
const p = pathEl.getPointAtLength(pathEl.getTotalLength() * eased);
|
|
318
|
+
ballEl.style.left = `${px(p.x)}px`;
|
|
319
|
+
ballEl.style.top = `${px(p.y)}px`;
|
|
300
320
|
ballEl.style.opacity = '1';
|
|
301
321
|
}
|
|
302
322
|
}
|
|
@@ -307,6 +327,7 @@
|
|
|
307
327
|
syncFrame();
|
|
308
328
|
rafId = requestAnimationFrame(loop);
|
|
309
329
|
};
|
|
330
|
+
untrack(syncFrame);
|
|
310
331
|
rafId = requestAnimationFrame(loop);
|
|
311
332
|
return () => cancelAnimationFrame(rafId);
|
|
312
333
|
});
|
|
@@ -469,17 +490,16 @@
|
|
|
469
490
|
|
|
470
491
|
<div
|
|
471
492
|
class="ftt-stage"
|
|
493
|
+
class:ftt-stacked={stacked}
|
|
472
494
|
bind:this={stageEl}
|
|
495
|
+
bind:clientWidth={stageWidth}
|
|
473
496
|
style:--ftt-bob-duration="{duration}s"
|
|
474
497
|
style:--ftt-bob-distance="{-distance}px"
|
|
475
498
|
>
|
|
476
|
-
<svg class="ftt-strings"
|
|
477
|
-
{#each tags as
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
bind:this={lineEls[i]}
|
|
481
|
-
x1={tag.left} y1={tag.top}
|
|
482
|
-
x2={a.x} y2={a.y}
|
|
499
|
+
<svg class="ftt-strings" bind:this={svgEl} aria-hidden="true">
|
|
500
|
+
{#each tags as _t, i (i)}
|
|
501
|
+
<path
|
|
502
|
+
bind:this={pathEls[i]}
|
|
483
503
|
class="ftt-string"
|
|
484
504
|
class:ftt-live={ballStates.has(i)}
|
|
485
505
|
/>
|
|
@@ -504,15 +524,8 @@
|
|
|
504
524
|
>I'm a button</span>
|
|
505
525
|
</div>
|
|
506
526
|
|
|
507
|
-
{#each tags as
|
|
508
|
-
{
|
|
509
|
-
<span
|
|
510
|
-
bind:this={knotEls[i]}
|
|
511
|
-
class="ftt-knot"
|
|
512
|
-
style:left="{a.x}%"
|
|
513
|
-
style:top="{a.y}%"
|
|
514
|
-
aria-hidden="true"
|
|
515
|
-
></span>
|
|
527
|
+
{#each tags as _t, i (i)}
|
|
528
|
+
<span bind:this={knotEls[i]} class="ftt-knot" aria-hidden="true"></span>
|
|
516
529
|
{/each}
|
|
517
530
|
|
|
518
531
|
{#each tags as _t, i (i)}
|
package/src/demo/Section.svelte
CHANGED
|
@@ -33,14 +33,8 @@
|
|
|
33
33
|
|
|
34
34
|
<style>
|
|
35
35
|
.demo-section {
|
|
36
|
-
grid-column:
|
|
36
|
+
grid-column: var(--kit-content);
|
|
37
37
|
display: flex;
|
|
38
38
|
flex-direction: column;
|
|
39
39
|
}
|
|
40
|
-
|
|
41
|
-
@media (max-width: 960px) {
|
|
42
|
-
.demo-section {
|
|
43
|
-
grid-column: 1 / -1;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
40
|
</style>
|
|
@@ -48,9 +48,10 @@
|
|
|
48
48
|
<style>
|
|
49
49
|
.source-links {
|
|
50
50
|
display: flex;
|
|
51
|
+
flex-wrap: wrap;
|
|
51
52
|
align-items: center;
|
|
52
|
-
gap: var(--space-48);
|
|
53
|
-
|
|
53
|
+
column-gap: var(--space-48);
|
|
54
|
+
row-gap: var(--space-16);
|
|
54
55
|
}
|
|
55
56
|
|
|
56
57
|
.brand-link {
|
|
@@ -148,9 +149,9 @@
|
|
|
148
149
|
font-size: var(--font-size-xl);
|
|
149
150
|
}
|
|
150
151
|
|
|
151
|
-
@media (max-width:
|
|
152
|
+
@media (max-width: 1200px) {
|
|
152
153
|
.source-links:not(.compact) {
|
|
153
|
-
gap: var(--space-24);
|
|
154
|
+
column-gap: var(--space-24);
|
|
154
155
|
}
|
|
155
156
|
}
|
|
156
157
|
</style>
|
|
@@ -30,6 +30,13 @@
|
|
|
30
30
|
if (id) document.getElementById(id)?.scrollIntoView({ block: 'start' });
|
|
31
31
|
});
|
|
32
32
|
|
|
33
|
+
function armName(arm: { skills: boolean; lookup: boolean }) {
|
|
34
|
+
if (arm.skills && arm.lookup) return 'Skills and lookup';
|
|
35
|
+
if (arm.skills) return 'Skills only';
|
|
36
|
+
if (arm.lookup) return 'Lookup only';
|
|
37
|
+
return 'Neither';
|
|
38
|
+
}
|
|
39
|
+
|
|
33
40
|
function jump(event: MouseEvent, id: string) {
|
|
34
41
|
event.preventDefault();
|
|
35
42
|
contentsOpen = false;
|
|
@@ -68,7 +75,7 @@
|
|
|
68
75
|
<h2 id="skills-title">LiveTokens skills</h2>
|
|
69
76
|
<div class="text-columns skills-overview">
|
|
70
77
|
<div>
|
|
71
|
-
<p>The CLI checks verify that skill output uses the design system and works correctly in the browser. They cover token use, component behavior, and page layout. Skills make design decisions and the
|
|
78
|
+
<p>The CLI checks verify that skill output uses the design system and works correctly in the browser. They cover token use, component behavior, and page layout. Skills make design decisions and the checker verifies the code.</p>
|
|
72
79
|
<p>The package includes eight skills, and one setup command copies them into your project.</p>
|
|
73
80
|
</div>
|
|
74
81
|
<ul class="skill-stack">
|
|
@@ -356,29 +363,27 @@
|
|
|
356
363
|
<section class="chapter" id="measured-value" aria-labelledby="measured-value-title">
|
|
357
364
|
<div class="chapter-body">
|
|
358
365
|
<h2 id="measured-value-title">Measured value</h2>
|
|
359
|
-
<p>An eval asked an agent to choose a component for {result.requirements} requirements. It ran in four arms of {result.runsPerArm} runs each: with and without the skills, and with and without the
|
|
366
|
+
<p>An eval asked an agent to choose a component for {result.requirements} requirements. It ran in four arms of {result.runsPerArm} runs each: with and without the skills, and with and without the lookup, the <code>live-tokens components</code> command that lists each component with its entry and props. The arms chose the same components, with one miss among 144 answers, and differ in turns, time, and cost.</p>
|
|
360
367
|
<div class="eval-results">
|
|
361
368
|
<Table>
|
|
362
369
|
<table aria-label="Eval arms">
|
|
363
370
|
<thead>
|
|
364
371
|
<tr>
|
|
365
|
-
<th scope="col">
|
|
366
|
-
<th scope="col">
|
|
367
|
-
<th scope="col">Requirements right</th>
|
|
372
|
+
<th scope="col">Arm</th>
|
|
373
|
+
<th scope="col">Accuracy</th>
|
|
368
374
|
<th scope="col">Turns</th>
|
|
369
375
|
<th scope="col">Seconds</th>
|
|
370
376
|
<th scope="col">Cost</th>
|
|
371
377
|
</tr>
|
|
372
378
|
</thead>
|
|
373
379
|
<tbody>
|
|
374
|
-
{#each result.arms as arm (`${arm.skills}-${arm.
|
|
380
|
+
{#each result.arms as arm (`${arm.skills}-${arm.lookup}`)}
|
|
375
381
|
<tr>
|
|
376
|
-
<td>{arm
|
|
377
|
-
<td>{arm.
|
|
378
|
-
<td>{arm.
|
|
379
|
-
<td>{arm.
|
|
380
|
-
<td>{arm.
|
|
381
|
-
<td>{arm.cost}</td>
|
|
382
|
+
<td>{armName(arm)}</td>
|
|
383
|
+
<td data-label="Accuracy">{arm.rowsRight}</td>
|
|
384
|
+
<td data-label="Turns">{arm.turns}</td>
|
|
385
|
+
<td data-label="Seconds">{arm.seconds}</td>
|
|
386
|
+
<td data-label="Cost">{arm.cost}</td>
|
|
382
387
|
</tr>
|
|
383
388
|
{/each}
|
|
384
389
|
</tbody>
|
|
@@ -386,10 +391,10 @@
|
|
|
386
391
|
</Table>
|
|
387
392
|
</div>
|
|
388
393
|
<ul class="eval-summary">
|
|
389
|
-
<li><strong>Turns:</strong> the skill saves turns only through the
|
|
390
|
-
<li><strong>Time and cost:</strong> the skills and the
|
|
394
|
+
<li><strong>Turns:</strong> the skill saves turns only through the lookup. Without the lookup, the skill arm takes as many turns as the agent with neither.</li>
|
|
395
|
+
<li><strong>Time and cost:</strong> the skills and the lookup each save time on their own, and together the run costs less than half as much as with neither.</li>
|
|
391
396
|
<li><strong>Accuracy:</strong> every arm scores about the same. The choices come from the entries in the component files.</li>
|
|
392
|
-
<li><strong>Change since 2026-09-20:</strong> in that run, no agent without the skills found the
|
|
397
|
+
<li><strong>Change since 2026-09-20:</strong> in that run, no agent without the skills found the lookup; this time 2 of 3 did, so that arm varies from run to run.</li>
|
|
393
398
|
</ul>
|
|
394
399
|
<div class="text-columns">
|
|
395
400
|
<div>
|
|
@@ -456,9 +461,11 @@
|
|
|
456
461
|
display: grid;
|
|
457
462
|
grid-template-columns: repeat(var(--columns-count), minmax(var(--space-0), 1fr));
|
|
458
463
|
column-gap: var(--columns-gutter);
|
|
464
|
+
box-sizing: content-box;
|
|
459
465
|
max-width: var(--columns-max-width);
|
|
460
466
|
margin-inline: auto;
|
|
461
|
-
padding: var(--space-48) var(--space-
|
|
467
|
+
padding-block: var(--space-48) var(--space-64);
|
|
468
|
+
padding-inline: var(--columns-margin);
|
|
462
469
|
color: var(--text-primary);
|
|
463
470
|
}
|
|
464
471
|
|
|
@@ -664,6 +671,16 @@
|
|
|
664
671
|
width: auto;
|
|
665
672
|
}
|
|
666
673
|
|
|
674
|
+
/* The panel's stage padding sized for a figure makes a one-line toggle a tall slab. */
|
|
675
|
+
.contents {
|
|
676
|
+
--panel-stage-padding: var(--space-4);
|
|
677
|
+
--panel-stage-inline-padding: var(--space-8);
|
|
678
|
+
}
|
|
679
|
+
|
|
680
|
+
.contents :global(.panel) {
|
|
681
|
+
justify-content: flex-start;
|
|
682
|
+
}
|
|
683
|
+
|
|
667
684
|
.contents-body {
|
|
668
685
|
width: 100%;
|
|
669
686
|
min-width: var(--space-0);
|
|
@@ -775,6 +792,24 @@
|
|
|
775
792
|
overflow-x: auto;
|
|
776
793
|
}
|
|
777
794
|
|
|
795
|
+
/* A phone frame scrolls the diagram, and the default overlay bar is too thin
|
|
796
|
+
to signal it. The standard scrollbar-width has no length value, and setting
|
|
797
|
+
it would disable these pseudo-elements in Chromium. */
|
|
798
|
+
.figure-scroll::-webkit-scrollbar {
|
|
799
|
+
height: var(--space-32);
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
.figure-scroll::-webkit-scrollbar-track {
|
|
803
|
+
background: var(--surface-neutral-low);
|
|
804
|
+
border-radius: var(--radius-full);
|
|
805
|
+
}
|
|
806
|
+
|
|
807
|
+
.figure-scroll::-webkit-scrollbar-thumb {
|
|
808
|
+
background: var(--surface-neutral-higher);
|
|
809
|
+
border: var(--border-width-1) solid var(--border-neutral);
|
|
810
|
+
border-radius: var(--radius-full);
|
|
811
|
+
}
|
|
812
|
+
|
|
778
813
|
/* Inside the figure's subgrid, so 1 / 7 lines up with the text column. */
|
|
779
814
|
figcaption {
|
|
780
815
|
grid-column: 1 / 7;
|
|
@@ -950,10 +985,16 @@
|
|
|
950
985
|
display: block;
|
|
951
986
|
}
|
|
952
987
|
|
|
988
|
+
/* The top leaves room for the fixed editor toolbar above the back link. */
|
|
953
989
|
.loops {
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
990
|
+
padding-block: var(--space-64) var(--space-48);
|
|
991
|
+
}
|
|
992
|
+
|
|
993
|
+
/* Both outrank the 1023px block's `.chapter-body > *`, so without this
|
|
994
|
+
they keep the 2 / -2 inset the rest of the chapter drops. */
|
|
995
|
+
.chapter-body > h1,
|
|
996
|
+
.chapter-body > .eval-results {
|
|
997
|
+
grid-column: 1 / -1;
|
|
957
998
|
}
|
|
958
999
|
|
|
959
1000
|
h1 {
|
|
@@ -972,5 +1013,73 @@
|
|
|
972
1013
|
padding-block: var(--space-32);
|
|
973
1014
|
}
|
|
974
1015
|
|
|
1016
|
+
/* A phone leaves too little width for side-by-side columns, so each row stacks into a card. */
|
|
1017
|
+
:is(.rule-group, .eval-results) :global(.table-wrapper :is(table, tbody, tr, td)) {
|
|
1018
|
+
display: block;
|
|
1019
|
+
min-width: var(--space-0);
|
|
1020
|
+
}
|
|
1021
|
+
|
|
1022
|
+
:is(.rule-group, .eval-results) :global(.table-wrapper thead) {
|
|
1023
|
+
display: none;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
/* The row's first cell takes the table's header style and captions the card. */
|
|
1027
|
+
:is(.rule-group, .eval-results) :global(.table-wrapper td:first-child) {
|
|
1028
|
+
border-right: none;
|
|
1029
|
+
border-bottom: var(--table-default-header-hairline-width) solid var(--table-default-header-hairline-color);
|
|
1030
|
+
background: var(--table-default-header-surface);
|
|
1031
|
+
color: var(--table-default-header-text);
|
|
1032
|
+
font-family: var(--table-default-header-font-family);
|
|
1033
|
+
font-size: var(--table-default-header-font-size);
|
|
1034
|
+
font-weight: var(--table-default-header-font-weight);
|
|
1035
|
+
line-height: var(--table-default-header-line-height);
|
|
1036
|
+
/* Half again as tall as a body row, so each card's caption marks a break between rules. */
|
|
1037
|
+
padding-top: calc(var(--table-default-cell-padding-top, var(--table-default-cell-padding)) * 1.5 + 0.25lh);
|
|
1038
|
+
padding-bottom: calc(var(--table-default-cell-padding-bottom, var(--table-default-cell-padding)) * 1.5 + 0.25lh);
|
|
1039
|
+
}
|
|
1040
|
+
|
|
1041
|
+
.rule-group :global(.table-wrapper td:last-child) {
|
|
1042
|
+
padding-bottom: calc(var(--table-default-cell-padding-bottom, var(--table-default-cell-padding)) + 0.5lh);
|
|
1043
|
+
}
|
|
1044
|
+
|
|
1045
|
+
.eval-results :global(.table-wrapper tr:not(:last-child) td:last-child) {
|
|
1046
|
+
border-bottom: var(--table-default-header-hairline-width) solid var(--table-default-header-hairline-color);
|
|
1047
|
+
}
|
|
1048
|
+
|
|
1049
|
+
/* Each rule stands as its own box: the wrapper hands its chrome to every row.
|
|
1050
|
+
Visible overflow keeps the wrapper's scroll box from clipping each row's shadow. */
|
|
1051
|
+
.rule-group :global(.table-wrapper) {
|
|
1052
|
+
overflow: visible;
|
|
1053
|
+
background: none;
|
|
1054
|
+
border: none;
|
|
1055
|
+
border-radius: var(--radius-none);
|
|
1056
|
+
box-shadow: none;
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
.rule-group :global(.table-wrapper tbody) {
|
|
1060
|
+
display: grid;
|
|
1061
|
+
gap: var(--space-12);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
.rule-group :global(.table-wrapper tr) {
|
|
1065
|
+
overflow: hidden;
|
|
1066
|
+
background: var(--table-default-surface);
|
|
1067
|
+
border: var(--table-default-border-width) solid var(--table-default-border);
|
|
1068
|
+
border-radius: var(--table-default-radius);
|
|
1069
|
+
box-shadow: var(--table-default-shadow);
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/* The hidden header row named these columns, so each value carries its own label. */
|
|
1073
|
+
.eval-results :global(.table-wrapper td[data-label]) {
|
|
1074
|
+
display: grid;
|
|
1075
|
+
grid-template-columns: minmax(var(--space-0), 2fr) minmax(var(--space-0), 3fr);
|
|
1076
|
+
column-gap: var(--space-12);
|
|
1077
|
+
border-right: none;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
.eval-results :global(.table-wrapper td[data-label]::before) {
|
|
1081
|
+
content: attr(data-label);
|
|
1082
|
+
font-weight: var(--table-default-header-font-weight);
|
|
1083
|
+
}
|
|
975
1084
|
}
|
|
976
1085
|
</style>
|