flowfig 0.2.0 → 0.4.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/README.md +80 -6
- package/dist/browser.d.ts +33 -0
- package/dist/browser.js +201 -0
- package/dist/check.js +82 -4
- package/dist/cli.js +180 -5
- package/dist/diff.js +2 -2
- package/dist/draw.d.ts +12 -0
- package/dist/draw.js +195 -0
- package/dist/geometry.d.ts +4 -1
- package/dist/geometry.js +39 -12
- package/dist/gif.d.ts +12 -0
- package/dist/gif.js +245 -0
- package/dist/guide.d.ts +3 -3
- package/dist/guide.js +35 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +280 -35
- package/dist/init.d.ts +5 -0
- package/dist/init.js +58 -22
- package/dist/model.d.ts +107 -3
- package/dist/model.js +185 -7
- package/dist/open.d.ts +15 -0
- package/dist/open.js +82 -0
- package/dist/page.d.ts +2 -0
- package/dist/page.js +16 -0
- package/dist/picker.d.ts +33 -0
- package/dist/picker.js +148 -0
- package/dist/png.d.ts +7 -0
- package/dist/png.js +70 -0
- package/dist/rail.d.ts +2 -1
- package/dist/rail.js +15 -1
- package/dist/scene.d.ts +2 -0
- package/dist/source.d.ts +2 -0
- package/dist/source.js +5 -0
- package/dist/svg.js +313 -40
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +10 -3
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import { useEffect, useMemo, useRef, useState } from 'react';
|
|
3
|
+
import { Fragment, useEffect, useMemo, useRef, useState } from 'react';
|
|
4
4
|
import { route } from './geometry.js';
|
|
5
5
|
import { foldedLabel, groupBox, layoutRail, railState, RAIL } from './rail.js';
|
|
6
|
+
import { textWidth } from './text.js';
|
|
6
7
|
import { checkScene, checkSpec, checkTheme } from './check.js';
|
|
7
|
-
import { ASYNC_TAG_W, BASE_RATE, CARD_LINE, CARD_PAD, CARD_WIDTH, EDGE_OFF, EDGE_ON, LIGHT, TONES, ON_ACCENT, decisions, edgeId, groupGap, isGroup, isRows, nodes, toBeat, } from './model.js';
|
|
8
|
+
import { ASYNC_TAG_W, BASE_RATE, CARD_LINE, CARD_PAD, CARD_WIDTH, EDGE_OFF, EDGE_ON, LIGHT, TONES, ON_ACCENT, toneFill, toneTint, decisions, edgeId, groupGap, isGroup, isRows, laneColumns, LANE_GAP, LANE_PAD, LANE_ROW_GAP, isLanesLayout, nodes, timelineBeats, timelineLayout, TL_AXIS_H, TL_AXIS_W, TL_BAR_H, TL_ROW_GAP, toBeat, beatMs, STEP_HOLD_MS, playheadItem, dateLabelRaised, } from './model.js';
|
|
8
9
|
export { LIGHT, DARK } from './model.js';
|
|
9
10
|
const DEFAULTS = { ...LIGHT, font: 'inherit' };
|
|
10
11
|
const v = (k) => `var(--fig-${k}, ${DEFAULTS[k]})`;
|
|
11
12
|
const MONO = 'var(--ifm-font-family-monospace, ui-monospace, SFMono-Regular, Menlo, monospace)';
|
|
12
|
-
const
|
|
13
|
+
const NONE = []; // a stable default, so a memo keyed on it does not run every render
|
|
14
|
+
const ACTIVE = 'flowfig-active'; // the box a packet has just reached
|
|
15
|
+
// The active box reads --ff-hop (the tone of the hop that arrived), then --ff-box (the tone of the box), then the accent.
|
|
16
|
+
const hue = `var(--ff-hop, var(--ff-box, ${v('accent')}))`;
|
|
17
|
+
const glow = `0 0 0 3px color-mix(in srgb, ${hue} 18%, transparent)`;
|
|
13
18
|
const cardBody = (c) => {
|
|
14
19
|
if (c == null)
|
|
15
20
|
return '—';
|
|
@@ -43,7 +48,14 @@ const cardBody = (c) => {
|
|
|
43
48
|
* The interactive player for one figure: tabs for the steps, play and pause, speed, hover and full screen.
|
|
44
49
|
* Give it a `FlowProps` spec. Use `toSvg` from `flowfig/svg` for a static animated SVG instead.
|
|
45
50
|
*/
|
|
46
|
-
export function Flow({ layout, edges, steps =
|
|
51
|
+
export function Flow({ layout, edges, steps: stepsIn = NONE, theme, speed = 900, autoplay = true, check = false, rail: withRail = false, lanes, timeline, today, }) {
|
|
52
|
+
const tl = timeline && isLanesLayout(layout);
|
|
53
|
+
// A timeline with no steps of its own walks its dated items in date order, with no tab row.
|
|
54
|
+
const synthetic = tl && !stepsIn.length;
|
|
55
|
+
const steps = useMemo(() => (synthetic ? timelineBeats({ layout, edges, timeline, today }) : stepsIn), [synthetic, layout, edges, timeline, today, stepsIn]);
|
|
56
|
+
// The width of the timeline area in px: the server draws TL_AXIS_W, and the browser measures the real width after mount.
|
|
57
|
+
const [axisW, setAxisW] = useState(TL_AXIS_W);
|
|
58
|
+
const area = useRef(null);
|
|
47
59
|
const root = useRef(null);
|
|
48
60
|
const outer = useRef(null);
|
|
49
61
|
const [fit, setFit] = useState({ scale: 1, height: 0 });
|
|
@@ -59,6 +71,7 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
59
71
|
const chips = useRef([]);
|
|
60
72
|
const bar = useRef(null); // the active tab's progress line
|
|
61
73
|
const [beat, setBeat] = useState(0);
|
|
74
|
+
const [holding, setHolding] = useState(false); // the step hold: a timeline's today line rests at today
|
|
62
75
|
const paths = useRef({});
|
|
63
76
|
const [routed, setRouted] = useState([]);
|
|
64
77
|
const [active, setActive] = useState(steps.length ? 0 : null);
|
|
@@ -72,7 +85,15 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
72
85
|
const clock = useRef({ beats: null, elapsed: 0 });
|
|
73
86
|
const [hover, setHover] = useState(null);
|
|
74
87
|
const ids = useMemo(() => edges.map(edgeId), [edges]);
|
|
75
|
-
|
|
88
|
+
// A milestone is a diamond too, so its edges meet the tips.
|
|
89
|
+
const tips = useMemo(() => new Set([
|
|
90
|
+
...decisions(layout),
|
|
91
|
+
...(tl
|
|
92
|
+
? timelineLayout({ layout, edges, timeline, today }, TL_AXIS_W)
|
|
93
|
+
.items.filter((i) => i.milestone)
|
|
94
|
+
.map((i) => i.id)
|
|
95
|
+
: []),
|
|
96
|
+
]), [layout, tl, edges, timeline, today]);
|
|
76
97
|
const step = active == null ? null : steps[active];
|
|
77
98
|
const beats = useMemo(() => (step?.flow ?? []).map(toBeat), [step]);
|
|
78
99
|
// Every content each box's card will ever show. The card is sized to the largest, so text never gets cut and nothing jumps.
|
|
@@ -129,6 +150,8 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
129
150
|
const scale = Math.max(0.5, Math.min(1, box.clientWidth / el.offsetWidth));
|
|
130
151
|
setFit({ scale, height: el.offsetHeight * scale });
|
|
131
152
|
setMapW(el.offsetWidth);
|
|
153
|
+
if (area.current)
|
|
154
|
+
setAxisW(area.current.offsetWidth);
|
|
132
155
|
const base = el.getBoundingClientRect();
|
|
133
156
|
const k = base.width / el.offsetWidth; // the scale currently on screen; rects are measured unscaled
|
|
134
157
|
const rects = {};
|
|
@@ -141,7 +164,18 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
141
164
|
h: r.height / k,
|
|
142
165
|
};
|
|
143
166
|
});
|
|
144
|
-
|
|
167
|
+
const avoid = [];
|
|
168
|
+
el.querySelectorAll('[data-fig-outside]').forEach((n) => {
|
|
169
|
+
const r = n.getBoundingClientRect();
|
|
170
|
+
avoid.push({ x: (r.left - base.left) / k, y: (r.top - base.top) / k, w: r.width / k, h: r.height / k });
|
|
171
|
+
});
|
|
172
|
+
setRouted(route(edges.map((e, i) => ({
|
|
173
|
+
id: ids[i],
|
|
174
|
+
from: e.from,
|
|
175
|
+
to: e.to,
|
|
176
|
+
around: e.around,
|
|
177
|
+
...(tl && { sides: ['r', 'l'], elbow: true }),
|
|
178
|
+
})), rects, tips, avoid));
|
|
145
179
|
};
|
|
146
180
|
measure();
|
|
147
181
|
const ro = new ResizeObserver(measure);
|
|
@@ -149,7 +183,7 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
149
183
|
ro.observe(box);
|
|
150
184
|
el.querySelectorAll('[data-fig]').forEach((n) => ro.observe(n));
|
|
151
185
|
return () => ro.disconnect();
|
|
152
|
-
}, [edges, ids, layout, tips, noMap]);
|
|
186
|
+
}, [edges, ids, layout, tips, noMap, lanes, tl, axisW]);
|
|
153
187
|
// The same rules as `flowfig check`, on what the browser actually drew: real fonts, real wrapping. Each fault prints once.
|
|
154
188
|
const reported = useRef(new Set());
|
|
155
189
|
useEffect(() => {
|
|
@@ -192,7 +226,7 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
192
226
|
edges: [
|
|
193
227
|
...routed.map((r) => {
|
|
194
228
|
const e = edges[ids.indexOf(r.id)];
|
|
195
|
-
return { id: r.id, from: e.from, to: e.to, curve: r.curve, label: labels[r.id] };
|
|
229
|
+
return { id: r.id, from: e.from, to: e.to, curve: r.curve, label: labels[r.id], ...(tl && { behind: true }) };
|
|
196
230
|
}),
|
|
197
231
|
// A rail pill is a label for label-overlap; its curve is a point at the pill center, so it can cross no box.
|
|
198
232
|
...(rail?.rows ?? []).flatMap((row) => {
|
|
@@ -228,23 +262,23 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
228
262
|
}, [check, routed, layout, edges, steps, ids, theme, rail, noMap]);
|
|
229
263
|
// Play the step's beats: each moves its packets (and their data cards) along its edges, then the next step starts.
|
|
230
264
|
useEffect(() => {
|
|
231
|
-
const gs = dots.current, cs = chips.current;
|
|
265
|
+
const gs = dots.current, cs = chips.current, edgeOf = Object.fromEntries(edges.map((e, i) => [ids[i], e]));
|
|
232
266
|
if (clock.current.beats !== beats) {
|
|
233
267
|
// A rail click can open another step at a later beat; start the clock at that beat, not at 0.
|
|
234
268
|
const j = jump.current ?? 0;
|
|
235
269
|
jump.current = null;
|
|
236
|
-
clock.current = { beats, elapsed: beats.slice(0, j).reduce((t, b) => t + (b
|
|
270
|
+
clock.current = { beats, elapsed: beats.slice(0, j).reduce((t, b) => t + beatMs(b, speed), 0) };
|
|
237
271
|
setBeat(j);
|
|
238
272
|
}
|
|
239
|
-
if (!beats.length || (!noMap && !routed.length))
|
|
273
|
+
if (!beats.length || (!noMap && !routed.length && !(tl && !edges.length)))
|
|
240
274
|
return;
|
|
241
275
|
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) {
|
|
242
276
|
if (bar.current)
|
|
243
277
|
bar.current.style.transform = 'none';
|
|
244
278
|
return void setBeat(beats.length - 1);
|
|
245
279
|
}
|
|
246
|
-
const ends = beats.reduce((acc, b) => [...acc, (acc.at(-1) ?? 0) + (b
|
|
247
|
-
const total = ends.at(-1) +
|
|
280
|
+
const ends = beats.reduce((acc, b) => [...acc, (acc.at(-1) ?? 0) + beatMs(b, speed)], []);
|
|
281
|
+
const total = ends.at(-1) + STEP_HOLD_MS; // The figure holds on the last beat before the next step.
|
|
248
282
|
let raf = 0, last = performance.now(), shownBeat = -1;
|
|
249
283
|
const tick = (now) => {
|
|
250
284
|
if (playingRef.current)
|
|
@@ -262,8 +296,9 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
262
296
|
const i = next === -1 ? beats.length - 1 : next;
|
|
263
297
|
if (i !== shownBeat)
|
|
264
298
|
setBeat((shownBeat = i));
|
|
299
|
+
setHolding(t >= ends.at(-1));
|
|
265
300
|
const start = i ? ends[i - 1] : 0;
|
|
266
|
-
const f = Math.min(1, (t - start) /
|
|
301
|
+
const f = Math.min(1, (t - start) / speed); // the packet crosses in `speed`; the hold gives the rest
|
|
267
302
|
const eased = f < 0.5 ? 2 * f * f : 1 - (-2 * f + 2) ** 2 / 2;
|
|
268
303
|
const hops = t < ends.at(-1) ? beats[i].hops : [];
|
|
269
304
|
gs.forEach((g, j) => {
|
|
@@ -277,20 +312,56 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
277
312
|
return;
|
|
278
313
|
}
|
|
279
314
|
const pt = p.getPointAtLength((hops[j].back ? 1 - eased : eased) * p.getTotalLength());
|
|
315
|
+
const tone = hops[j].tone && TONES[hops[j].tone];
|
|
280
316
|
if (g) {
|
|
281
317
|
g.setAttribute('transform', `translate(${pt.x} ${pt.y})`);
|
|
282
318
|
g.style.opacity = '1';
|
|
319
|
+
g.style.color = tone ?? v('accent');
|
|
283
320
|
}
|
|
284
321
|
if (c) {
|
|
322
|
+
c.style.background = tone ? toneFill(tone) : v('accent');
|
|
285
323
|
c.style.transform = `translate(${pt.x}px, ${pt.y}px) translate(-50%, calc(-100% - 12px))`;
|
|
286
324
|
c.style.opacity = hops[j].data == null ? '0' : '1';
|
|
287
325
|
}
|
|
288
326
|
});
|
|
327
|
+
// A box is active from the packet arrival to the end of the step hold. The tick toggles the class and skips React.
|
|
328
|
+
const arrived = new Map();
|
|
329
|
+
if (f >= 1)
|
|
330
|
+
for (const h of beats[i].hops) {
|
|
331
|
+
const to = h.back ? edgeOf[h.edge]?.from : edgeOf[h.edge]?.to;
|
|
332
|
+
arrived.set(to, arrived.get(to) ?? (h.tone && TONES[h.tone]));
|
|
333
|
+
}
|
|
334
|
+
// A focused box turns active at the beat start; in a timeline, when the today line arrives.
|
|
335
|
+
if (t - start >= (tl ? 400 * BASE_RATE : 0))
|
|
336
|
+
for (const id of beats[i].focus ?? [])
|
|
337
|
+
if (!arrived.has(id))
|
|
338
|
+
arrived.set(id, undefined);
|
|
339
|
+
root.current?.querySelectorAll('[data-fig]').forEach((n) => {
|
|
340
|
+
const on = arrived.has(n.dataset.fig ?? '');
|
|
341
|
+
if (on) {
|
|
342
|
+
const tone = arrived.get(n.dataset.fig);
|
|
343
|
+
if (tone)
|
|
344
|
+
n.style.setProperty('--ff-hop', tone);
|
|
345
|
+
else
|
|
346
|
+
n.style.removeProperty('--ff-hop');
|
|
347
|
+
}
|
|
348
|
+
if (on === n.classList.contains(ACTIVE))
|
|
349
|
+
return;
|
|
350
|
+
if (!on)
|
|
351
|
+
return void n.classList.remove(ACTIVE); // the transition fades the box
|
|
352
|
+
// The box turns active at once: no transition while the class arrives.
|
|
353
|
+
const keep = n.style.transition;
|
|
354
|
+
n.style.transition = 'none';
|
|
355
|
+
n.classList.add(ACTIVE);
|
|
356
|
+
void n.offsetWidth;
|
|
357
|
+
n.style.transition = keep;
|
|
358
|
+
});
|
|
289
359
|
raf = requestAnimationFrame(tick);
|
|
290
360
|
};
|
|
291
361
|
raf = requestAnimationFrame(tick);
|
|
292
362
|
return () => {
|
|
293
363
|
cancelAnimationFrame(raf);
|
|
364
|
+
root.current?.querySelectorAll(`.${ACTIVE}`).forEach((n) => n.classList.remove(ACTIVE));
|
|
294
365
|
gs.forEach((g) => g && (g.style.opacity = '0'));
|
|
295
366
|
cs.forEach((c) => c && (c.style.opacity = '0'));
|
|
296
367
|
};
|
|
@@ -304,15 +375,20 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
304
375
|
? [edges[hoverEdgeIdx].from, edges[hoverEdgeIdx].to]
|
|
305
376
|
: hover
|
|
306
377
|
? [hover]
|
|
307
|
-
: [...(step?.nodes ?? []), ...Object.keys(shown), ...(cur?.light ?? [])]);
|
|
378
|
+
: [...(step?.nodes ?? []), ...Object.keys(shown), ...(cur?.light ?? []), ...(cur?.focus ?? [])]);
|
|
308
379
|
edges.forEach((e, i) => {
|
|
309
380
|
if (litEdges.has(ids[i]))
|
|
310
381
|
litNodes.add(e.from).add(e.to);
|
|
311
382
|
});
|
|
383
|
+
// A toned hop colors its edge for its own beat only; the trail after it is the accent.
|
|
384
|
+
const hopTone = (edge) => {
|
|
385
|
+
const t = cur?.hops.find((h) => h.edge === edge && h.tone)?.tone;
|
|
386
|
+
return t && TONES[t];
|
|
387
|
+
};
|
|
312
388
|
const goTo = (step, b) => {
|
|
313
389
|
setPlaying(true);
|
|
314
390
|
if (step === active) {
|
|
315
|
-
clock.current.elapsed = beats.slice(0, b).reduce((t, x) => t + (x
|
|
391
|
+
clock.current.elapsed = beats.slice(0, b).reduce((t, x) => t + beatMs(x, speed), 0);
|
|
316
392
|
setBeat(b);
|
|
317
393
|
}
|
|
318
394
|
else {
|
|
@@ -322,7 +398,149 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
322
398
|
};
|
|
323
399
|
const focus = hover != null || hoverEdge != null || step != null;
|
|
324
400
|
const vars = Object.fromEntries(Object.entries(theme ?? {}).map(([k, val]) => [`--fig-${k}`, val]));
|
|
401
|
+
const timelineFig = useMemo(() => (tl ? timelineLayout({ layout, edges, timeline, today }, axisW) : null), [tl, layout, edges, timeline, today, axisW]);
|
|
402
|
+
// A bar or a milestone of a timeline: placed by the shared layout, with the look of a box.
|
|
403
|
+
const renderBar = (n, it) => {
|
|
404
|
+
const lit = litNodes.has(n.id);
|
|
405
|
+
const bt = n.tone && TONES[n.tone];
|
|
406
|
+
const label = String(n.label);
|
|
407
|
+
const inside = it.labelInside;
|
|
408
|
+
const outside = (_jsx("span", { "data-fig-outside": "", style: { position: 'absolute', left: '100%', marginLeft: 6, whiteSpace: 'nowrap', fontSize: 13, fontWeight: 500, color: v('fg') }, children: label }));
|
|
409
|
+
const top = LANE_PAD + it.row * (TL_BAR_H + TL_ROW_GAP);
|
|
410
|
+
return (_jsxs("div", { "data-fig": n.id, "data-diamond": it.milestone || undefined, onMouseEnter: () => setHover(n.id), onMouseLeave: () => setHover(null), style: {
|
|
411
|
+
position: 'absolute',
|
|
412
|
+
left: it.x,
|
|
413
|
+
top: it.milestone ? top + (TL_BAR_H - it.w) / 2 : top,
|
|
414
|
+
width: it.w,
|
|
415
|
+
height: it.milestone ? it.w : TL_BAR_H,
|
|
416
|
+
boxSizing: 'border-box',
|
|
417
|
+
display: 'flex',
|
|
418
|
+
alignItems: 'center',
|
|
419
|
+
padding: it.milestone ? 0 : '0 calc(8px - var(--ff-b, 0px))',
|
|
420
|
+
color: v('fg'),
|
|
421
|
+
background: it.milestone ? undefined : bt ? toneTint(bt, v('bg')) : v('bg'),
|
|
422
|
+
border: it.milestone ? undefined : `1px solid ${bt ?? (lit ? v('accent') : v('border'))}`,
|
|
423
|
+
...(bt && { '--ff-box': bt }),
|
|
424
|
+
borderRadius: 6,
|
|
425
|
+
fontSize: 13,
|
|
426
|
+
fontWeight: 500,
|
|
427
|
+
whiteSpace: 'nowrap',
|
|
428
|
+
opacity: focus && !lit ? 0.7 : 1,
|
|
429
|
+
transition: 'border-color .4s, background .4s, box-shadow .4s, border-width .4s, padding .4s, opacity .25s',
|
|
430
|
+
cursor: 'default',
|
|
431
|
+
}, children: [it.milestone && (_jsx("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", style: { position: 'absolute', inset: 0, width: '100%', height: '100%', overflow: 'visible' }, children: _jsx("polygon", { points: "50,0 100,50 50,100 0,50", fill: bt ? toneTint(bt, v('bg')) : v('bg'), stroke: bt ?? (lit ? v('accent') : v('border')), strokeWidth: lit ? 1.6 : 1, vectorEffect: "non-scaling-stroke", style: { transition: 'stroke .4s, fill .4s, stroke-width .4s' } }) })), inside ? _jsx("span", { style: { position: 'relative' }, children: label }) : outside] }, n.id));
|
|
432
|
+
};
|
|
325
433
|
const renderItem = (item, depth) => {
|
|
434
|
+
if (timelineFig && item === layout) {
|
|
435
|
+
const lanesList = layout.children;
|
|
436
|
+
// The playhead follows the latest dated focus in the step, and rests at the last date before one and in the hold.
|
|
437
|
+
const focused = holding || still ? undefined : playheadItem(timelineFig.items, beats, beat);
|
|
438
|
+
const home = timelineFig.last ?? 0;
|
|
439
|
+
const at = focused ? focused.x + (focused.milestone ? focused.w / 2 : 0) : home;
|
|
440
|
+
const dateText = focused ? focused.date : timelineFig.lastDate;
|
|
441
|
+
const rowsPx = timelineFig.rows.map((r) => r * TL_BAR_H + (r - 1) * TL_ROW_GAP + LANE_PAD * 2);
|
|
442
|
+
return (_jsxs("div", { style: {
|
|
443
|
+
display: 'grid',
|
|
444
|
+
gridTemplateColumns: `max-content minmax(${TL_AXIS_W}px, 1fr) 18px`,
|
|
445
|
+
// The axis strip sits over the gap above the first band, as in the SVG.
|
|
446
|
+
gridTemplateRows: `${TL_AXIS_H - LANE_ROW_GAP}px ${rowsPx.map((h) => h + 'px').join(' ')}`,
|
|
447
|
+
rowGap: LANE_ROW_GAP,
|
|
448
|
+
position: 'relative',
|
|
449
|
+
}, children: [_jsxs("div", { "data-fig-axis": "", style: { gridColumn: '2 / 4', gridRow: 1, position: 'relative', height: TL_AXIS_H, alignSelf: 'start' }, children: [_jsx("div", { style: { position: 'absolute', left: 0, right: 0, top: TL_AXIS_H - 1, height: 1, background: v('border') } }), timelineFig.ticks.map((k) => (_jsxs(Fragment, { children: [_jsx("div", { style: { position: 'absolute', left: k.x, top: TL_AXIS_H - 5, width: 1, height: 4, background: v('border') } }), _jsx("div", { style: { position: 'absolute', left: k.x + 3, top: 0, fontSize: 11, lineHeight: '13px', color: v('muted') }, children: k.label })] }, k.x)))] }), lanesList.map((lane, li) => {
|
|
450
|
+
const lit = lane.id != null && litNodes.has(lane.id);
|
|
451
|
+
return (_jsxs(Fragment, { children: [_jsx("div", { "data-fig": lane.id, "data-fig-lane": "", style: {
|
|
452
|
+
gridColumn: '1 / -1',
|
|
453
|
+
gridRow: li + 2,
|
|
454
|
+
background: v('surface'),
|
|
455
|
+
border: `1px solid ${lit ? v('accent') : v('border')}`,
|
|
456
|
+
boxShadow: lit ? glow : undefined,
|
|
457
|
+
transition: 'border-color .25s, box-shadow .25s',
|
|
458
|
+
borderRadius: 14,
|
|
459
|
+
zIndex: 0,
|
|
460
|
+
} }), _jsx("div", { style: {
|
|
461
|
+
gridColumn: 1,
|
|
462
|
+
gridRow: li + 2,
|
|
463
|
+
alignSelf: 'center',
|
|
464
|
+
zIndex: 1,
|
|
465
|
+
padding: '0 18px',
|
|
466
|
+
fontSize: 12,
|
|
467
|
+
fontWeight: 600,
|
|
468
|
+
letterSpacing: '.04em',
|
|
469
|
+
textTransform: 'uppercase',
|
|
470
|
+
color: v('muted'),
|
|
471
|
+
}, children: lane.label }), _jsx("div", { ref: li === 0 ? area : undefined, style: { gridColumn: 2, gridRow: li + 2, position: 'relative', zIndex: 1 }, children: timelineFig.items
|
|
472
|
+
.filter((it) => it.track === li)
|
|
473
|
+
.map((it) => renderBar(lane.children.find((k) => k.id === it.id), it)) })] }, lane.id ?? String(lane.label)));
|
|
474
|
+
}), timelineFig.last != null && (_jsxs("div", { style: { gridColumn: '2 / 4', gridRow: '1 / -1', position: 'relative', zIndex: 2, pointerEvents: 'none' }, children: [timelineFig.today != null && (_jsxs("div", { "data-fig-today": "", style: { position: 'absolute', left: timelineFig.today, top: 12, bottom: 0, width: 0 }, children: [_jsx("div", { style: { position: 'absolute', top: 0, bottom: 0, borderLeft: `1px dashed ${v('accent')}`, opacity: 0.6 } }), _jsx("span", { style: {
|
|
475
|
+
position: 'absolute',
|
|
476
|
+
right: 3,
|
|
477
|
+
top: -1,
|
|
478
|
+
fontSize: 11,
|
|
479
|
+
lineHeight: '11px',
|
|
480
|
+
fontWeight: 600,
|
|
481
|
+
color: v('accent'),
|
|
482
|
+
}, children: "today" })] })), _jsx("div", { "data-fig-playhead": "", style: {
|
|
483
|
+
position: 'absolute',
|
|
484
|
+
left: 0,
|
|
485
|
+
top: 12,
|
|
486
|
+
bottom: 0,
|
|
487
|
+
width: 1.5,
|
|
488
|
+
background: v('accent'),
|
|
489
|
+
transform: `translateX(${at}px)`,
|
|
490
|
+
transition: still ? 'none' : 'transform .4s linear',
|
|
491
|
+
}, children: _jsx("span", { style: {
|
|
492
|
+
position: 'absolute',
|
|
493
|
+
left: 3,
|
|
494
|
+
top: dateLabelRaised(at, timelineFig.today, textWidth(dateText, 11), textWidth('today', 11)) ? -13 : -1,
|
|
495
|
+
fontSize: 11,
|
|
496
|
+
lineHeight: '11px',
|
|
497
|
+
fontWeight: 600,
|
|
498
|
+
whiteSpace: 'nowrap',
|
|
499
|
+
color: v('accent'),
|
|
500
|
+
animation: still ? undefined : 'flowfig-label .4s step-end',
|
|
501
|
+
}, children: dateText }, dateText) })] }))] }));
|
|
502
|
+
}
|
|
503
|
+
if (lanes && item === layout && isLanesLayout(layout)) {
|
|
504
|
+
const cols = laneColumns({ layout, edges, steps, lanes });
|
|
505
|
+
const n = Math.max(0, ...cols.values()) + 1;
|
|
506
|
+
return (_jsx("div", { style: {
|
|
507
|
+
display: 'grid',
|
|
508
|
+
gridTemplateColumns: `max-content repeat(${n}, max-content)`,
|
|
509
|
+
columnGap: 0,
|
|
510
|
+
rowGap: LANE_ROW_GAP,
|
|
511
|
+
position: 'relative',
|
|
512
|
+
}, children: layout.children.map((lane, li) => {
|
|
513
|
+
const lit = lane.id != null && litNodes.has(lane.id);
|
|
514
|
+
return (_jsxs(Fragment, { children: [_jsx("div", { "data-fig": lane.id, "data-fig-lane": "", style: {
|
|
515
|
+
gridColumn: '1 / -1',
|
|
516
|
+
gridRow: li + 1,
|
|
517
|
+
background: v('surface'),
|
|
518
|
+
border: `1px solid ${lit ? v('accent') : v('border')}`,
|
|
519
|
+
boxShadow: lit ? glow : undefined,
|
|
520
|
+
transition: 'border-color .25s, box-shadow .25s',
|
|
521
|
+
borderRadius: 14,
|
|
522
|
+
zIndex: 0,
|
|
523
|
+
} }), _jsx("div", { style: {
|
|
524
|
+
gridColumn: 1,
|
|
525
|
+
gridRow: li + 1,
|
|
526
|
+
alignSelf: 'center',
|
|
527
|
+
zIndex: 1,
|
|
528
|
+
padding: '0 18px',
|
|
529
|
+
fontSize: 12,
|
|
530
|
+
fontWeight: 600,
|
|
531
|
+
letterSpacing: '.04em',
|
|
532
|
+
textTransform: 'uppercase',
|
|
533
|
+
color: v('muted'),
|
|
534
|
+
}, children: lane.label }), lane.children.map((b) => (_jsx("div", { style: {
|
|
535
|
+
gridColumn: cols.get(b.id) + 2,
|
|
536
|
+
gridRow: li + 1,
|
|
537
|
+
alignSelf: 'center',
|
|
538
|
+
zIndex: 1,
|
|
539
|
+
// The last time column keeps the frame side, as in the SVG.
|
|
540
|
+
padding: `${LANE_PAD}px ${cols.get(b.id) === n - 1 ? 18 : LANE_GAP}px ${LANE_PAD}px 0`,
|
|
541
|
+
}, children: renderItem(b, 1) }, b.id)))] }, lane.id ?? String(lane.label)));
|
|
542
|
+
}) }));
|
|
543
|
+
}
|
|
326
544
|
if (isGroup(item)) {
|
|
327
545
|
const lit = item.id != null && litNodes.has(item.id);
|
|
328
546
|
const framed = item.label != null;
|
|
@@ -360,7 +578,8 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
360
578
|
const diamond = item.shape === 'decision';
|
|
361
579
|
const store = item.shape === 'store';
|
|
362
580
|
const card = carded.has(item.id);
|
|
363
|
-
|
|
581
|
+
const bt = item.tone && TONES[item.tone];
|
|
582
|
+
return (_jsxs("div", { "data-fig": item.id, "data-diamond": diamond || undefined, onMouseEnter: () => setHover(item.id), onMouseLeave: () => setHover(null), style: {
|
|
364
583
|
position: 'relative',
|
|
365
584
|
isolation: 'isolate',
|
|
366
585
|
minWidth: 100,
|
|
@@ -371,17 +590,20 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
371
590
|
display: 'flex',
|
|
372
591
|
flexDirection: 'column',
|
|
373
592
|
justifyContent: 'center',
|
|
374
|
-
padding: diamond
|
|
593
|
+
padding: diamond
|
|
594
|
+
? '22px 34px'
|
|
595
|
+
: `calc(${store ? 24 : 10}px - var(--ff-b, 0px)) calc(${card ? 10 : 16}px - var(--ff-b, 0px)) calc(10px - var(--ff-b, 0px))`,
|
|
375
596
|
textAlign: 'center',
|
|
376
|
-
background: diamond ? undefined : v('bg'),
|
|
597
|
+
background: diamond ? undefined : bt ? toneTint(bt, v('bg')) : v('bg'),
|
|
377
598
|
color: v('fg'),
|
|
378
|
-
border: diamond ? undefined : `1px solid ${lit ? v('accent') : v('border')}`,
|
|
379
|
-
|
|
599
|
+
border: diamond ? undefined : `1px solid ${bt ?? (lit ? v('accent') : v('border'))}`,
|
|
600
|
+
...(bt && { '--ff-box': bt }),
|
|
601
|
+
boxShadow: diamond ? undefined : '0 1px 2px rgba(0,0,0,.06)',
|
|
380
602
|
opacity: focus && !lit ? 0.7 : 1,
|
|
381
603
|
borderRadius: store ? '50% / 12px' : 10,
|
|
382
604
|
fontSize: 14,
|
|
383
605
|
fontWeight: 500,
|
|
384
|
-
transition: 'border-color .
|
|
606
|
+
transition: 'border-color .4s, background .4s, box-shadow .4s, border-width .4s, padding .4s, opacity .25s',
|
|
385
607
|
cursor: 'default',
|
|
386
608
|
}, children: [diamond && (_jsx("svg", { viewBox: "0 0 100 100", preserveAspectRatio: "none", style: {
|
|
387
609
|
position: 'absolute',
|
|
@@ -390,7 +612,7 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
390
612
|
height: '100%',
|
|
391
613
|
overflow: 'visible',
|
|
392
614
|
zIndex: -1,
|
|
393
|
-
}, children: _jsx("polygon", { points: "50,0 100,50 50,100 0,50", fill: v('bg'), stroke: lit ? v('accent') : v('border'), strokeWidth: lit ? 1.6 : 1, vectorEffect: "non-scaling-stroke", style: { transition: 'stroke .
|
|
615
|
+
}, children: _jsx("polygon", { points: "50,0 100,50 50,100 0,50", fill: bt ? toneTint(bt, v('bg')) : v('bg'), stroke: bt ?? (lit ? v('accent') : v('border')), strokeWidth: lit ? 1.6 : 1, vectorEffect: "non-scaling-stroke", style: { transition: 'stroke .4s, fill .4s, stroke-width .4s' } }) })), store && (_jsx("div", { style: {
|
|
394
616
|
position: 'absolute',
|
|
395
617
|
top: -1,
|
|
396
618
|
left: -1,
|
|
@@ -398,8 +620,23 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
398
620
|
height: 24,
|
|
399
621
|
boxSizing: 'border-box',
|
|
400
622
|
borderRadius: '50%',
|
|
401
|
-
border: `1px solid ${lit ? v('accent') : v('border')}`,
|
|
623
|
+
border: `1px solid ${bt ?? (lit ? v('accent') : v('border'))}`,
|
|
402
624
|
transition: 'border-color .25s',
|
|
625
|
+
} })), item.mark && (_jsx("span", { "aria-hidden": true, style: {
|
|
626
|
+
position: 'absolute',
|
|
627
|
+
top: '50%',
|
|
628
|
+
transform: 'translateY(-50%)',
|
|
629
|
+
boxSizing: 'border-box',
|
|
630
|
+
borderRadius: '50%',
|
|
631
|
+
...(item.mark === 'start'
|
|
632
|
+
? { left: -18, width: 10, height: 10, background: bt ?? v('accent') }
|
|
633
|
+
: {
|
|
634
|
+
right: -20,
|
|
635
|
+
width: 14,
|
|
636
|
+
height: 14,
|
|
637
|
+
border: `1.5px solid ${bt ?? v('accent')}`,
|
|
638
|
+
background: `radial-gradient(circle, ${bt ?? v('accent')} 0 4px, transparent 4.5px)`,
|
|
639
|
+
}),
|
|
403
640
|
} })), _jsx("div", { children: item.label }), item.sub != null && _jsx("div", { style: { fontSize: 12, fontWeight: 400, color: v('muted'), marginTop: 2 }, children: item.sub }), card && (_jsxs("div", { style: {
|
|
404
641
|
marginTop: 8,
|
|
405
642
|
padding: '6px 8px',
|
|
@@ -452,7 +689,10 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
452
689
|
zIndex: 3,
|
|
453
690
|
width: 28,
|
|
454
691
|
height: 28,
|
|
455
|
-
}, children: _jsx(Icon, { d: full ? 'M4 4l8 8M12 4l-8 8' : 'M9 3h4v4M7 13H3V9M13 3L9 7M3 13l4-4' }) }), _jsx("style", { children: '@keyframes flowfig-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}'
|
|
692
|
+
}, children: _jsx(Icon, { d: full ? 'M4 4l8 8M12 4l-8 8' : 'M9 3h4v4M7 13H3V9M13 3L9 7M3 13l4-4' }) }), _jsx("style", { children: '@keyframes flowfig-label{from{opacity:0}to{opacity:0}}@keyframes flowfig-in{from{opacity:0;transform:translateY(3px)}to{opacity:1;transform:none}}' +
|
|
693
|
+
// The active box has a 2 px border, a tint and a glow. The padding shrinks by 1 px. The box size stays the same, so the edges keep their route.
|
|
694
|
+
`.${ACTIVE}:not([data-diamond]){--ff-b:1px;border-width:2px!important;background:color-mix(in srgb, ${hue} 10%, ${v('surface')})!important;box-shadow:${glow}!important;border-color:${hue}!important}` +
|
|
695
|
+
`.${ACTIVE} polygon{fill:color-mix(in srgb, ${hue} 10%, ${v('surface')});stroke:${hue};stroke-width:2px}` }), !noMap && (_jsx(_Fragment, { children: _jsx("div", { style: {
|
|
456
696
|
margin: full ? '-48px -24px 0' : '-20px -16px 0',
|
|
457
697
|
padding: full ? '48px 24px 24px' : '24px 16px',
|
|
458
698
|
borderRadius: full ? 0 : '16px 16px 0 0',
|
|
@@ -479,15 +719,18 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
479
719
|
height: '100%',
|
|
480
720
|
overflow: 'visible',
|
|
481
721
|
pointerEvents: 'none',
|
|
482
|
-
|
|
722
|
+
// A timeline dependency runs under the bars (z-index 1) and over the bands (0).
|
|
723
|
+
zIndex: tl ? 0 : undefined,
|
|
724
|
+
}, children: [_jsx("defs", { children: ['off', 'on'].map((k) => (_jsx("marker", { id: `fig-arrow-${k}`, viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: _jsx("path", { d: "M 0 1 L 9 5 L 0 9 z", fill: "context-stroke" }) }, k))) }), routed.map((r) => {
|
|
483
725
|
const on = litEdges.has(r.id);
|
|
726
|
+
const tone = hopTone(r.id);
|
|
484
727
|
const hidden = !on && edges[ids.indexOf(r.id)].quiet;
|
|
485
728
|
return (_jsx("path", { ref: (p) => {
|
|
486
729
|
paths.current[r.id] = p;
|
|
487
|
-
}, d: r.d, fill: "none", stroke: on ? v('accent') : v('muted'), strokeWidth: on ? EDGE_ON : EDGE_OFF, strokeOpacity: hidden ? 0 : focus && !on ? 0.35 : 1, markerEnd: hidden ? undefined : `url(#fig-arrow-${on ? 'on' : 'off'})`, style: { transition: 'stroke .25s, stroke-opacity .25s' } }, r.id));
|
|
730
|
+
}, d: r.d, fill: "none", stroke: on ? (tone ?? v('accent')) : v('muted'), strokeWidth: on ? EDGE_ON : EDGE_OFF, strokeOpacity: hidden ? 0 : focus && !on ? 0.35 : 1, markerEnd: hidden ? undefined : `url(#fig-arrow-${on ? 'on' : 'off'})`, style: { transition: 'stroke .25s, stroke-opacity .25s' } }, r.id));
|
|
488
731
|
}), Array.from({ length: Math.max(1, ...beats.map((b) => b.hops.length)) }, (_, j) => (_jsxs("g", { ref: (g) => {
|
|
489
732
|
dots.current[j] = g;
|
|
490
|
-
}, style: { opacity: 0 }, children: [_jsx("circle", { r: 10, fill:
|
|
733
|
+
}, style: { opacity: 0, color: v('accent') }, children: [_jsx("circle", { r: 10, fill: "currentColor", opacity: 0.2 }), _jsx("circle", { r: 4.5, fill: "currentColor" })] }, j)))] }), Array.from({ length: Math.max(1, ...beats.map((b) => b.hops.length)) }, (_, j) => (_jsx("div", { ref: (c) => {
|
|
491
734
|
chips.current[j] = c;
|
|
492
735
|
}, style: {
|
|
493
736
|
position: 'absolute',
|
|
@@ -508,9 +751,10 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
508
751
|
transition: 'opacity .2s',
|
|
509
752
|
}, children: cur?.hops[j]?.data }, j))), routed.map((r) => {
|
|
510
753
|
const e = edges[ids.indexOf(r.id)];
|
|
511
|
-
if (e.label == null)
|
|
754
|
+
if (e.label == null || tl)
|
|
512
755
|
return null;
|
|
513
756
|
const on = litEdges.has(r.id);
|
|
757
|
+
const tone = hopTone(r.id);
|
|
514
758
|
return (_jsx("div", { "data-fig-label": r.id, style: {
|
|
515
759
|
position: 'absolute',
|
|
516
760
|
left: r.mid.x,
|
|
@@ -522,9 +766,9 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
522
766
|
borderRadius: 999,
|
|
523
767
|
whiteSpace: 'nowrap',
|
|
524
768
|
pointerEvents: 'none',
|
|
525
|
-
background: on ? v('accent') : v('bg'),
|
|
769
|
+
background: on ? (tone ? toneFill(tone) : v('accent')) : v('bg'),
|
|
526
770
|
color: on ? ON_ACCENT : v('muted'),
|
|
527
|
-
border: `1px solid ${on ? v('accent') : v('border')}`,
|
|
771
|
+
border: `1px solid ${on ? (tone ?? v('accent')) : v('border')}`,
|
|
528
772
|
opacity: !on && e.quiet ? 0 : focus && !on ? 0.6 : 1,
|
|
529
773
|
fontFamily: MONO,
|
|
530
774
|
transition: 'background .25s, color .25s, opacity .25s',
|
|
@@ -565,16 +809,17 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
565
809
|
(hover != null && [rail.columns[row.from].id, rail.columns[row.to].id].includes(hover));
|
|
566
810
|
const [x1, x2] = [rail.columns[row.from].x, rail.columns[row.to].x];
|
|
567
811
|
const ly = y + RAIL.row / 2;
|
|
812
|
+
const tone = state === 'now' && row.tone ? TONES[row.tone] : undefined;
|
|
568
813
|
const g = row.group != null ? rail.groups[row.group] : null;
|
|
569
|
-
return (_jsxs("g", { "data-rail-row": row.n, "data-state": state, opacity: state === 'next' && !on ? 0.45 : 1, style: { cursor: 'pointer' }, onClick: () => goTo(row.step, row.beat), onMouseEnter: () => setHoverEdge(row.edge), onMouseLeave: () => setHoverEdge(null), children: [g && g.rows[0] === i && (_jsx("rect", { x: groupBox(rail, g).x, y: y + groupBox(rail, g).y, width: groupBox(rail, g).w, height: groupBox(rail, g).h, rx: 6, fill: `color-mix(in srgb, ${v('accent')} 8%, ${v('bg')})`, stroke: v('accent'), strokeDasharray: "3 3" })), _jsx("path", { d: `M ${x1} ${ly} H ${x2}`, fill: "none", stroke: on ? v('accent') : v('muted'), strokeWidth: on ? EDGE_ON : EDGE_OFF, strokeDasharray: row.async ? '4 3' : undefined, markerEnd: "url(#flowfig-rail-arrow)" }), row.async &&
|
|
814
|
+
return (_jsxs("g", { "data-rail-row": row.n, "data-state": state, opacity: state === 'next' && !on ? 0.45 : 1, style: { cursor: 'pointer' }, onClick: () => goTo(row.step, row.beat), onMouseEnter: () => setHoverEdge(row.edge), onMouseLeave: () => setHoverEdge(null), children: [g && g.rows[0] === i && (_jsx("rect", { x: groupBox(rail, g).x, y: y + groupBox(rail, g).y, width: groupBox(rail, g).w, height: groupBox(rail, g).h, rx: 6, fill: `color-mix(in srgb, ${v('accent')} 8%, ${v('bg')})`, stroke: v('accent'), strokeDasharray: "3 3" })), _jsx("path", { d: `M ${x1} ${ly} H ${x2}`, fill: "none", stroke: on ? (tone ?? v('accent')) : v('muted'), strokeWidth: on ? EDGE_ON : EDGE_OFF, strokeDasharray: row.async ? '4 3' : undefined, markerEnd: "url(#flowfig-rail-arrow)" }), row.async &&
|
|
570
815
|
(() => {
|
|
571
816
|
const tagW = ASYNC_TAG_W;
|
|
572
817
|
// The tag sits on the tail side of the pill, so it never hides the arrowhead.
|
|
573
818
|
const tx = !row.pill ? (x1 + x2) / 2 - tagW / 2 : x2 > x1 ? row.pill.x - tagW - 4 : row.pill.x + row.pill.w + 4;
|
|
574
819
|
return (_jsxs(_Fragment, { children: [_jsx("rect", { x: tx, y: ly - 6, width: tagW, height: 12, rx: 4, fill: v('bg') }), _jsx("rect", { x: tx, y: ly - 6, width: tagW, height: 12, rx: 4, fill: TONES.gray, fillOpacity: 0.15 }), _jsx("text", { x: tx + tagW / 2, y: ly + 3, fill: TONES.gray, fontSize: 9, fontWeight: 600, letterSpacing: ".03em", textAnchor: "middle", "data-fig-tag": true, children: "ASYNC" })] }));
|
|
575
|
-
})(), row.pill && (_jsxs("g", { "data-fig-label": `rail:${row.n}`, children: [_jsx("rect", { x: row.pill.x, y: ly - 9, width: row.pill.w, height: 18, rx: 9, fill: on ? v('accent') : v('bg'), stroke: on ? v('accent') : v('border') }), _jsx("text", { x: row.pill.x + row.pill.w / 2, y: ly + 4, fill: on ? ON_ACCENT : v('muted'), fontSize: 11, fontFamily: MONO, textAnchor: "middle", children: row.text })] }))] }, `m${i}`));
|
|
820
|
+
})(), row.pill && (_jsxs("g", { "data-fig-label": `rail:${row.n}`, children: [_jsx("rect", { x: row.pill.x, y: ly - 9, width: row.pill.w, height: 18, rx: 9, fill: on ? (tone ? toneFill(tone) : v('accent')) : v('bg'), stroke: on ? (tone ?? v('accent')) : v('border') }), _jsx("text", { x: row.pill.x + row.pill.w / 2, y: ly + 4, fill: on ? ON_ACCENT : v('muted'), fontSize: 11, fontFamily: MONO, textAnchor: "middle", children: row.text })] }))] }, `m${i}`));
|
|
576
821
|
});
|
|
577
|
-
})(), _jsx("defs", { children: _jsx("marker", { id: "flowfig-rail-arrow", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: _jsx("path", { d: "M 0 1 L 9 5 L 0 9 z", fill: v('muted') }) }) })] })), steps.length > 0 && (_jsxs("figcaption", { style: { marginTop: 14, textAlign: 'center' }, children: [_jsxs("div", { style: { display: 'flex', gap: 10, justifyContent: 'center', alignItems: 'center' }, children: [_jsx("button", { type: "button", "aria-label": playing ? 'Pause' : 'Play', title: playing ? 'Pause' : 'Play', style: iconBtn, onClick: () => setPlaying((p) => !p), children: _jsx(Icon, { d: playing ? 'M5.5 4v8M10.5 4v8' : 'M5 3.5v9l7.5-4.5z', fill: !playing }) }), _jsx("div", { role: "tablist", style: {
|
|
822
|
+
})(), _jsx("defs", { children: _jsx("marker", { id: "flowfig-rail-arrow", viewBox: "0 0 10 10", refX: "9", refY: "5", markerWidth: "7", markerHeight: "7", orient: "auto-start-reverse", children: _jsx("path", { d: "M 0 1 L 9 5 L 0 9 z", fill: v('muted') }) }) })] })), steps.length > 0 && (_jsxs("figcaption", { style: { marginTop: 14, textAlign: 'center' }, children: [_jsxs("div", { style: { display: 'flex', gap: 10, justifyContent: 'center', alignItems: 'center' }, children: [_jsx("button", { type: "button", "aria-label": playing ? 'Pause' : 'Play', title: playing ? 'Pause' : 'Play', style: iconBtn, onClick: () => setPlaying((p) => !p), children: _jsx(Icon, { d: playing ? 'M5.5 4v8M10.5 4v8' : 'M5 3.5v9l7.5-4.5z', fill: !playing }) }), !synthetic && (_jsx("div", { role: "tablist", style: {
|
|
578
823
|
display: 'inline-flex',
|
|
579
824
|
gap: 2,
|
|
580
825
|
padding: 3,
|
|
@@ -613,7 +858,7 @@ export function Flow({ layout, edges, steps = [], theme, speed = 900, autoplay =
|
|
|
613
858
|
transform: 'scaleX(0)',
|
|
614
859
|
opacity: playing ? 1 : 0.35,
|
|
615
860
|
} }))] }, i));
|
|
616
|
-
}) }), _jsxs("button", { type: "button", "aria-label": `Speed ${rate}×, switch to ${rate === 1 ? 2 : 1}×`, title: "Playback speed", style: {
|
|
861
|
+
}) })), _jsxs("button", { type: "button", "aria-label": `Speed ${rate}×, switch to ${rate === 1 ? 2 : 1}×`, title: "Playback speed", style: {
|
|
617
862
|
...iconBtn,
|
|
618
863
|
width: 'auto',
|
|
619
864
|
padding: '0 10px',
|
package/dist/init.d.ts
CHANGED
|
@@ -19,6 +19,11 @@ type Agent = {
|
|
|
19
19
|
};
|
|
20
20
|
/** Shown in place of the MCP file when init cannot write the config. */
|
|
21
21
|
mcpNote?: string;
|
|
22
|
+
/** A second file, a marked section in a file the user owns. Written only in a repo run, never with `--global`. */
|
|
23
|
+
also?: {
|
|
24
|
+
file: string;
|
|
25
|
+
text: string;
|
|
26
|
+
};
|
|
22
27
|
};
|
|
23
28
|
export declare const MCP_ENTRY: {
|
|
24
29
|
command: string;
|