my-you-eye 0.4.0 → 0.5.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/COMPONENTS.md +4 -4
- package/components.json +6 -3
- package/dist/{chunk-ZWLDKVXQ.js → chunk-3XU2AZIQ.js} +3 -3
- package/dist/{chunk-VU2IBHKV.js → chunk-AAPYU7IQ.js} +225 -118
- package/dist/{chunk-CTIFISBJ.js → chunk-FVGQI3XE.js} +64 -32
- package/dist/{chunk-NZO3FHA7.js → chunk-OBWYV42U.js} +467 -446
- package/dist/{chunk-VFEYYIUB.js → chunk-YWX2ZFH4.js} +61 -24
- package/dist/index.d.ts +12 -4
- package/dist/index.js +5 -5
- package/dist/motion.d.ts +23 -1
- package/dist/motion.js +1 -1
- package/dist/present-player.d.ts +1 -1
- package/dist/present-player.js +4 -4
- package/dist/present.d.ts +1 -1
- package/dist/present.js +4 -4
- package/dist/scenes.d.ts +6 -5
- package/dist/scenes.js +3 -3
- package/dist/{video-DaZDielA.d.ts → video-BgntpcEO.d.ts} +1 -1
- package/dist/video.d.ts +1 -1
- package/dist/video.js +4 -4
- package/package.json +2 -2
- package/references/data-display.md +2 -2
- package/references/scenes.md +3 -2
package/COMPONENTS.md
CHANGED
|
@@ -132,7 +132,7 @@ Step (`TerminalStep`):
|
|
|
132
132
|
| `command?` | `string` | Command typed after the prompt. Omit for an output-only entry (a banner, a log tail). |
|
|
133
133
|
| `output?` | `string` | Output body shown under the command. |
|
|
134
134
|
| `language?` | `string` | Language id for highlighting `output`. |
|
|
135
|
-
| `exitCode?` | `number` | Process exit code — renders a
|
|
135
|
+
| `exitCode?` | `number` | Process exit code — renders a plain status line (0 reads success, non-zero danger). |
|
|
136
136
|
| `spinner?` | `string` | Shows a spinner with this label while the step runs, before `output` lands. For a step that should read as "this takes a while". |
|
|
137
137
|
| `cwd?` | `string` | Per-entry overrides of the prompt chrome — each independently optional, and each PERSISTS to every following entry until overridden again, mirroring `Terminal`'s own per-entry override semantics (a real shell's `cd`/`ssh` changes the prompt for every command after it, not just that one line). Falls back to the scene-level prop of the same name when never set by any entry up to this point. |
|
|
138
138
|
| `user?` | `string` | — |
|
|
@@ -672,7 +672,7 @@ Also accepts everything from `HTMLAttributes<HTMLDivElement>`.
|
|
|
672
672
|
| `Card` | `my-you-eye` | variant: **default** / elevated / outlined | Variants, With footer actions, Size |
|
|
673
673
|
| `CodeBlock` | `my-you-eye` | variant: **default** / elevated | Bare (no header, no language), Language-only (badge overlay, no header bar), With header + language, Elevated, Line numbers, No wrap (horizontal scroll), Syntax highlighting (TS), Line highlights, Line highlights (implicit gutter), Multi-color highlights, Substring highlights, Substring highlights on a long line (wrap forced off), Merged highlights, Focus range (dims everything outside it), Bare (embedded in another surface), Syntax highlighting (CSS / HTML / SQL / YAML / Python) |
|
|
674
674
|
| `DeviceFrame` | `my-you-eye` | variant: **browser** / phone / window | Browser, Window, Phone |
|
|
675
|
-
| `DiffBlock` | `my-you-eye` | variant: **default** / elevated | Unified, Unified — word diff, Split, Split — word diff, Elevated |
|
|
675
|
+
| `DiffBlock` | `my-you-eye` | variant: **default** / elevated | Unified, Unified — word diff, Unified — word diff, heavily rewritten lines, Split, Split — word diff, Elevated |
|
|
676
676
|
| `EmptyState` | `my-you-eye` | — | Default, With icon and action |
|
|
677
677
|
| `Image` | `my-you-eye` | fit: contain / **cover** / fill / none / scaleDown<br>radius: full / lg / **md** / none / sm<br>aspect: **auto** / square / tall / video / wide<br>bordered: true<br>shadowed: true | Fit modes, Border radius, Aspect ratio, Styles, With caption |
|
|
678
678
|
| `Kbd` | `my-you-eye` | — | Default, Combinations |
|
|
@@ -680,7 +680,7 @@ Also accepts everything from `HTMLAttributes<HTMLDivElement>`.
|
|
|
680
680
|
| `ScrollArea` | `my-you-eye` | orientation: **both** / horizontal / vertical | Vertical scroll, Horizontal scroll, Both axes, Rounded corners (radius on ScrollArea itself, not a wrapper), Edge fade |
|
|
681
681
|
| `Separator` | `my-you-eye` | orientation: **horizontal** / vertical | Horizontal, Vertical |
|
|
682
682
|
| `StatusDot` | `my-you-eye` | variant: danger / info / **neutral** / success / warning<br>size: **md** / sm | Variants, Sizes, Pulsing |
|
|
683
|
-
| `Terminal` | `my-you-eye` | variant: **default** / elevated<br>scheme: amber / **default** / matrix<br>chrome: **dots** / none | Prompt glyphs, Title bar, Variant, Color schemes, Chrome decorator, Fixed height, scrolls as content grows, Prompt segments, changed mid-session |
|
|
683
|
+
| `Terminal` | `my-you-eye` | variant: **default** / elevated<br>scheme: amber / **default** / matrix<br>chrome: **dots** / none | Prompt glyphs, Title bar, Exit status & spinner, Variant, Color schemes, Chrome decorator, Fixed height, scrolls as content grows, Prompt segments, changed mid-session |
|
|
684
684
|
|
|
685
685
|
### display — props
|
|
686
686
|
|
|
@@ -1450,7 +1450,7 @@ Also accepts everything from `HTMLAttributes<HTMLDivElement>`.
|
|
|
1450
1450
|
| `SceneRenderer` | `my-you-eye/scenes` | — | Title, Bullets, Diagram, chart, stat |
|
|
1451
1451
|
| `SequenceScene` | `my-you-eye/scenes` | — | Playing |
|
|
1452
1452
|
| `StatScene` | `my-you-eye/scenes` | — | Playing |
|
|
1453
|
-
| `TerminalScene` | `my-you-eye/scenes` | — | Playing, Pinned mid-typing, entry 1 (frame 12/160), Pinned on entry 2's spinner (frame 72/160), Pinned at rest (frame 160/160), Prompt change (rows + scheme) |
|
|
1453
|
+
| `TerminalScene` | `my-you-eye/scenes` | — | Playing, Pinned mid-typing, entry 1 (frame 12/160), Pinned on entry 2's spinner (frame 72/160), Spinner, animating, Pinned at rest (frame 160/160), Prompt change (rows + scheme) |
|
|
1454
1454
|
| `TitleScene` | `my-you-eye/scenes` | — | Centered, playing, Left-aligned, no chapter/subtitle, Pinned mid-stagger (frame 12/90), Pinned at rest (frame 90/90) |
|
|
1455
1455
|
| `Validation` | `my-you-eye/scenes` | — | Deliberately broken video, Valid, minimal video |
|
|
1456
1456
|
| `WalkthroughScene` | `my-you-eye/scenes` | — | Playing |
|
package/components.json
CHANGED
|
@@ -1148,10 +1148,11 @@
|
|
|
1148
1148
|
"Playing",
|
|
1149
1149
|
"Pinned mid-typing, entry 1 (frame 12/160)",
|
|
1150
1150
|
"Pinned on entry 2's spinner (frame 72/160)",
|
|
1151
|
+
"Spinner, animating",
|
|
1151
1152
|
"Pinned at rest (frame 160/160)",
|
|
1152
1153
|
"Prompt change (rows + scheme)"
|
|
1153
1154
|
],
|
|
1154
|
-
"description": "Wraps Terminal, revealing one entry per step: command types in, spinner
|
|
1155
|
+
"description": "Wraps Terminal, revealing one entry per step: command types in, spinner cycles, output and exit status land."
|
|
1155
1156
|
},
|
|
1156
1157
|
{
|
|
1157
1158
|
"name": "TitleScene",
|
|
@@ -2918,6 +2919,7 @@
|
|
|
2918
2919
|
"demos": [
|
|
2919
2920
|
"Unified",
|
|
2920
2921
|
"Unified — word diff",
|
|
2922
|
+
"Unified — word diff, heavily rewritten lines",
|
|
2921
2923
|
"Split",
|
|
2922
2924
|
"Split — word diff",
|
|
2923
2925
|
"Elevated"
|
|
@@ -3221,13 +3223,14 @@
|
|
|
3221
3223
|
"demos": [
|
|
3222
3224
|
"Prompt glyphs",
|
|
3223
3225
|
"Title bar",
|
|
3226
|
+
"Exit status & spinner",
|
|
3224
3227
|
"Variant",
|
|
3225
3228
|
"Color schemes",
|
|
3226
3229
|
"Chrome decorator",
|
|
3227
3230
|
"Fixed height, scrolls as content grows",
|
|
3228
3231
|
"Prompt segments, changed mid-session"
|
|
3229
3232
|
],
|
|
3230
|
-
"description": "Prompt/command/output/exit-
|
|
3233
|
+
"description": "Prompt/command/output/exit-status sequences, composing CodeBlock for output bodies. Data-driven via a typed entries array.",
|
|
3231
3234
|
"variantDefaults": {
|
|
3232
3235
|
"variant": "default",
|
|
3233
3236
|
"scheme": "default",
|
|
@@ -4862,7 +4865,7 @@
|
|
|
4862
4865
|
"name": "exitCode",
|
|
4863
4866
|
"optional": true,
|
|
4864
4867
|
"type": "number",
|
|
4865
|
-
"description": "Process exit code — renders a
|
|
4868
|
+
"description": "Process exit code — renders a plain status line (0 reads success, non-zero danger)."
|
|
4866
4869
|
},
|
|
4867
4870
|
{
|
|
4868
4871
|
"name": "spinner",
|
|
@@ -4,16 +4,16 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
SceneRenderer,
|
|
6
6
|
sceneDuration
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-AAPYU7IQ.js";
|
|
8
8
|
import {
|
|
9
9
|
Badge,
|
|
10
10
|
Progress,
|
|
11
11
|
cn
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-OBWYV42U.js";
|
|
13
13
|
import {
|
|
14
14
|
MotionRoot,
|
|
15
15
|
resolveBeatFrames
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-YWX2ZFH4.js";
|
|
17
17
|
|
|
18
18
|
// src/video/VideoRoot.tsx
|
|
19
19
|
import { Fragment as Fragment2 } from "react";
|
|
@@ -20,6 +20,7 @@ import {
|
|
|
20
20
|
Link,
|
|
21
21
|
MergedHighlight,
|
|
22
22
|
PieChart,
|
|
23
|
+
SPINNER_FRAMES,
|
|
23
24
|
ScatterPlot,
|
|
24
25
|
ScrollArea,
|
|
25
26
|
SequenceDiagram,
|
|
@@ -34,12 +35,12 @@ import {
|
|
|
34
35
|
layered,
|
|
35
36
|
lcsDiffFlags,
|
|
36
37
|
pairDiffLines,
|
|
38
|
+
refinedLineDiff,
|
|
37
39
|
renderHighlightedLine,
|
|
38
40
|
snap,
|
|
39
41
|
splitTokensByLine,
|
|
40
|
-
tokenize
|
|
41
|
-
|
|
42
|
-
} from "./chunk-NZO3FHA7.js";
|
|
42
|
+
tokenize
|
|
43
|
+
} from "./chunk-OBWYV42U.js";
|
|
43
44
|
import {
|
|
44
45
|
Camera,
|
|
45
46
|
CountUp,
|
|
@@ -57,7 +58,7 @@ import {
|
|
|
57
58
|
resolveBeatFrames,
|
|
58
59
|
useProgress,
|
|
59
60
|
useSequence
|
|
60
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-YWX2ZFH4.js";
|
|
61
62
|
import {
|
|
62
63
|
formatBytesParts,
|
|
63
64
|
formatCurrencyParts,
|
|
@@ -1156,7 +1157,7 @@ function CodeDiffRow({ row, language, delayFrames, durationFrames, lineHeight, c
|
|
|
1156
1157
|
const isAddedOnly = Boolean(row.right && !row.left);
|
|
1157
1158
|
const isRemovedOnly = Boolean(row.left && !row.right);
|
|
1158
1159
|
const { ref: markerRef, anchorX } = useAnchorX();
|
|
1159
|
-
const diff = isChanged ?
|
|
1160
|
+
const diff = isChanged ? refinedLineDiff(row.left.content, row.right.content) : null;
|
|
1160
1161
|
const oldRects = useMemo(
|
|
1161
1162
|
() => diff ? segmentsToRects(diff.oldSegments, anchorX, charWidth, lineHeight) : [],
|
|
1162
1163
|
[diff, anchorX, charWidth, lineHeight]
|
|
@@ -1165,7 +1166,7 @@ function CodeDiffRow({ row, language, delayFrames, durationFrames, lineHeight, c
|
|
|
1165
1166
|
() => diff ? segmentsToRects(diff.newSegments, anchorX, charWidth, lineHeight) : [],
|
|
1166
1167
|
[diff, anchorX, charWidth, lineHeight]
|
|
1167
1168
|
);
|
|
1168
|
-
if (isChanged
|
|
1169
|
+
if (isChanged) {
|
|
1169
1170
|
const oldOpacity = Math.max(0, 1 - progress / 0.45);
|
|
1170
1171
|
const newOpacity = Math.max(0, (progress - 0.55) / 0.45);
|
|
1171
1172
|
return /* @__PURE__ */ jsxs3("div", { className: "relative", children: [
|
|
@@ -1176,7 +1177,7 @@ function CodeDiffRow({ row, language, delayFrames, durationFrames, lineHeight, c
|
|
|
1176
1177
|
style: { opacity: oldOpacity },
|
|
1177
1178
|
children: [
|
|
1178
1179
|
/* @__PURE__ */ jsx3("span", { ref: markerRef, className: cn("w-5 shrink-0 text-center", MARKER_TEXT.removed), children: MARKER_GLYPH.removed }),
|
|
1179
|
-
|
|
1180
|
+
row.left.content,
|
|
1180
1181
|
oldRects.length > 0 && /* @__PURE__ */ jsx3(MergedHighlight, { rects: oldRects, color: WORD_COLOR.old, strokeColor: WORD_COLOR.old })
|
|
1181
1182
|
]
|
|
1182
1183
|
}
|
|
@@ -1188,7 +1189,7 @@ function CodeDiffRow({ row, language, delayFrames, durationFrames, lineHeight, c
|
|
|
1188
1189
|
style: { opacity: newOpacity },
|
|
1189
1190
|
children: [
|
|
1190
1191
|
/* @__PURE__ */ jsx3("span", { className: cn("w-5 shrink-0 text-center", MARKER_TEXT.added), children: MARKER_GLYPH.added }),
|
|
1191
|
-
|
|
1192
|
+
row.right.content,
|
|
1192
1193
|
newRects.length > 0 && /* @__PURE__ */ jsx3(MergedHighlight, { rects: newRects, color: WORD_COLOR.new, strokeColor: WORD_COLOR.new })
|
|
1193
1194
|
]
|
|
1194
1195
|
}
|
|
@@ -1409,6 +1410,10 @@ function useCodeAnnotations(annotations, containerRef, blockId) {
|
|
|
1409
1410
|
// src/scenes/code-scene/CodeScene.tsx
|
|
1410
1411
|
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
1411
1412
|
var DIFF_SHARE = 0.55;
|
|
1413
|
+
function typedSlice(source, progress) {
|
|
1414
|
+
const cut = Math.floor(source.length * progress);
|
|
1415
|
+
return source.slice(0, cut) + source.slice(cut).replace(/[^\n]/g, "");
|
|
1416
|
+
}
|
|
1412
1417
|
function currentStepIndex(scene, ranges, frame) {
|
|
1413
1418
|
let index = 0;
|
|
1414
1419
|
scene.steps.forEach((step, i) => {
|
|
@@ -1439,7 +1444,15 @@ function CodeScene({ scene }) {
|
|
|
1439
1444
|
const diffFrames = Math.max(1, Math.round((range.endFrame - range.startFrame) * DIFF_SHARE));
|
|
1440
1445
|
const isDiff = !step.typed && nextSource !== prevSource && frame < range.startFrame + diffFrames;
|
|
1441
1446
|
const isTyped = Boolean(step.typed);
|
|
1442
|
-
const typedCode = isTyped ? nextSource
|
|
1447
|
+
const typedCode = isTyped ? typedSlice(nextSource, stepProgress) : nextSource;
|
|
1448
|
+
const blockProps = {
|
|
1449
|
+
language: scene.lang,
|
|
1450
|
+
header: scene.file,
|
|
1451
|
+
highlight: highlightEnabled,
|
|
1452
|
+
showLineNumbers,
|
|
1453
|
+
focusRange: step.focus,
|
|
1454
|
+
highlightRanges: highlightRanges.length > 0 ? highlightRanges : void 0
|
|
1455
|
+
};
|
|
1443
1456
|
return /* @__PURE__ */ jsx5("div", { className: "flex h-full w-full flex-col gap-stack bg-bg p-panel-xl text-fg", children: /* @__PURE__ */ jsx5("div", { ref: containerRef, className: "relative min-h-0 flex-1", children: /* @__PURE__ */ jsxs5(Camera, { keyframes, className: "h-full w-full", children: [
|
|
1444
1457
|
/* @__PURE__ */ jsx5("div", { className: "flex h-full w-full items-center justify-center", children: isDiff ? /* @__PURE__ */ jsx5(
|
|
1445
1458
|
CodeDiff,
|
|
@@ -1453,16 +1466,38 @@ function CodeScene({ scene }) {
|
|
|
1453
1466
|
className: "max-h-full"
|
|
1454
1467
|
},
|
|
1455
1468
|
index
|
|
1469
|
+
) : isTyped ? (
|
|
1470
|
+
/* Reserve the FINAL layout up front — the same trick TypeText's
|
|
1471
|
+
`preserveLayout` uses (src/motion/type-text/TypeText.tsx), for
|
|
1472
|
+
the same reason: a box that grows with the text is a box that
|
|
1473
|
+
moves under everything measuring it. Both blocks occupy the
|
|
1474
|
+
one grid cell, the hidden one (full source) sizing it and the
|
|
1475
|
+
visible one (typed prefix) stretching into it, so block width,
|
|
1476
|
+
block height and gutter width are final from frame 1 and the
|
|
1477
|
+
only thing that changes per frame is which characters are
|
|
1478
|
+
painted. The container therefore never resizes while typing,
|
|
1479
|
+
so `useCodeCameraKeyframes`' ResizeObserver stays quiet and
|
|
1480
|
+
the centring wrapper below has nothing left to re-centre.
|
|
1481
|
+
The ghost deliberately gets NO `lineId`: duplicate element ids
|
|
1482
|
+
would make the camera's `getElementById` measure the invisible
|
|
1483
|
+
copy instead of the real one. */
|
|
1484
|
+
/* @__PURE__ */ jsxs5("div", { className: "grid max-h-full", children: [
|
|
1485
|
+
/* @__PURE__ */ jsx5("div", { "aria-hidden": true, className: "invisible col-start-1 row-start-1 min-w-0", children: /* @__PURE__ */ jsx5(CodeBlock, { code: nextSource, ...blockProps, className: "max-h-full" }) }),
|
|
1486
|
+
/* @__PURE__ */ jsx5(
|
|
1487
|
+
CodeBlock,
|
|
1488
|
+
{
|
|
1489
|
+
code: typedCode,
|
|
1490
|
+
...blockProps,
|
|
1491
|
+
lineId: (n) => lineElementId(blockId, n),
|
|
1492
|
+
className: "col-start-1 row-start-1 min-w-0 max-h-full"
|
|
1493
|
+
}
|
|
1494
|
+
)
|
|
1495
|
+
] }, index)
|
|
1456
1496
|
) : /* @__PURE__ */ jsx5(
|
|
1457
1497
|
CodeBlock,
|
|
1458
1498
|
{
|
|
1459
|
-
code:
|
|
1460
|
-
|
|
1461
|
-
header: scene.file,
|
|
1462
|
-
highlight: highlightEnabled,
|
|
1463
|
-
showLineNumbers,
|
|
1464
|
-
focusRange: step.focus,
|
|
1465
|
-
highlightRanges: highlightRanges.length > 0 ? highlightRanges : void 0,
|
|
1499
|
+
code: nextSource,
|
|
1500
|
+
...blockProps,
|
|
1466
1501
|
lineId: (n) => lineElementId(blockId, n),
|
|
1467
1502
|
className: "max-h-full"
|
|
1468
1503
|
},
|
|
@@ -1501,7 +1536,11 @@ function phaseWindows(entry) {
|
|
|
1501
1536
|
}
|
|
1502
1537
|
return windows;
|
|
1503
1538
|
}
|
|
1504
|
-
|
|
1539
|
+
var SPINNER_FRAME_HOLD = 3;
|
|
1540
|
+
function spinnerGlyphAt(frame) {
|
|
1541
|
+
return SPINNER_FRAMES[Math.floor(frame / SPINNER_FRAME_HOLD) % SPINNER_FRAMES.length];
|
|
1542
|
+
}
|
|
1543
|
+
function currentEntryView(entry, localProgress, frame) {
|
|
1505
1544
|
const windows = phaseWindows(entry);
|
|
1506
1545
|
const command = entry.command != null && windows.command ? entry.command.slice(0, Math.floor(entry.command.length * clamp01((localProgress - windows.command.start) / (windows.command.end - windows.command.start)))) : entry.command;
|
|
1507
1546
|
const spinnerVisible = windows.spinner != null && localProgress >= windows.spinner.start && localProgress < windows.spinner.end;
|
|
@@ -1510,6 +1549,7 @@ function currentEntryView(entry, localProgress) {
|
|
|
1510
1549
|
return {
|
|
1511
1550
|
command,
|
|
1512
1551
|
spinner: spinnerVisible ? entry.spinner : void 0,
|
|
1552
|
+
spinnerGlyph: spinnerVisible ? spinnerGlyphAt(frame) : void 0,
|
|
1513
1553
|
output: outputVisible ? entry.output : void 0,
|
|
1514
1554
|
language: entry.language,
|
|
1515
1555
|
exitCode: exitVisible ? entry.exitCode : void 0,
|
|
@@ -1544,7 +1584,7 @@ function TerminalScene({ scene }) {
|
|
|
1544
1584
|
}
|
|
1545
1585
|
const range = ranges[stepName(entry.id, i)];
|
|
1546
1586
|
const span = Math.max(1, range.endFrame - range.startFrame);
|
|
1547
|
-
visible.push(currentEntryView(entry, clamp01((frame - range.startFrame) / span)));
|
|
1587
|
+
visible.push(currentEntryView(entry, clamp01((frame - range.startFrame) / span), frame));
|
|
1548
1588
|
}
|
|
1549
1589
|
return /* @__PURE__ */ jsx6("div", { className: "flex h-full w-full items-center justify-center bg-bg px-panel-xl text-fg", children: /* @__PURE__ */ jsx6(
|
|
1550
1590
|
Terminal,
|
|
@@ -1577,10 +1617,13 @@ function OutroScene({ scene }) {
|
|
|
1577
1617
|
}
|
|
1578
1618
|
|
|
1579
1619
|
// src/scenes/diagram-scene/DiagramScene.tsx
|
|
1580
|
-
import { useMemo as useMemo4 } from "react";
|
|
1620
|
+
import { useMemo as useMemo4, useState as useState6 } from "react";
|
|
1581
1621
|
|
|
1582
1622
|
// src/scenes/diagram-scene/DiagramScene.layout.ts
|
|
1583
1623
|
var NODE_WIDTH = 10 * GRID;
|
|
1624
|
+
var LAYER_GAP = 5;
|
|
1625
|
+
var NODE_GAP = 5;
|
|
1626
|
+
var GROUP_LABEL_BAND = 1.5 * GRID;
|
|
1584
1627
|
var PRESET_DEFAULTS = {
|
|
1585
1628
|
architecture: { layout: "layered-horizontal", edgeKind: "sync", edgeRoute: "orthogonal", nodeShape: "box" },
|
|
1586
1629
|
dataflow: { layout: "layered-horizontal", edgeKind: "data", edgeRoute: "orthogonal", nodeShape: "box" },
|
|
@@ -1606,10 +1649,8 @@ function resolveNodeRects(nodes, edges, preset, layoutOverride) {
|
|
|
1606
1649
|
const footprint = { nodeWidth: NODE_WIDTH / GRID, nodeHeight: tallest / GRID };
|
|
1607
1650
|
const positions = layoutKind === "grid" ? grid(layoutNodes, footprint) : layered(layoutNodes, layoutEdges, {
|
|
1608
1651
|
direction: layoutKind === "layered-vertical" ? "vertical" : "horizontal",
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
// pushed a routine 4-node chain past the width of a 16:9 stage.
|
|
1612
|
-
layerGap: 3,
|
|
1652
|
+
layerGap: LAYER_GAP,
|
|
1653
|
+
nodeGap: NODE_GAP,
|
|
1613
1654
|
...footprint
|
|
1614
1655
|
});
|
|
1615
1656
|
const positionOf = new Map(positions.map((p) => [p.id, p]));
|
|
@@ -1640,10 +1681,20 @@ function resolveGroupRects(groups, nodeRects) {
|
|
|
1640
1681
|
}
|
|
1641
1682
|
return out;
|
|
1642
1683
|
}
|
|
1643
|
-
|
|
1684
|
+
var FIT_PADDING = 2 * GRID;
|
|
1685
|
+
function fitScale(bounds, canvas) {
|
|
1686
|
+
const width = canvas.width - 2 * FIT_PADDING;
|
|
1687
|
+
const height = canvas.height - 2 * FIT_PADDING;
|
|
1688
|
+
if (width <= 0 || height <= 0) return 1;
|
|
1689
|
+
return Math.min(1, fitZoom({ x: 0, y: 0, width: bounds.width, height: bounds.height }, width, height, 1));
|
|
1690
|
+
}
|
|
1691
|
+
function centerOffset(bounds, canvas, zoom = 1) {
|
|
1692
|
+
const scale = zoom > 0 ? zoom : 1;
|
|
1693
|
+
const viewWidth = canvas.width / scale;
|
|
1694
|
+
const viewHeight = canvas.height / scale;
|
|
1644
1695
|
return {
|
|
1645
|
-
dx: Math.max(0,
|
|
1646
|
-
dy: Math.max(0,
|
|
1696
|
+
dx: snap(-bounds.x + Math.max(0, (viewWidth - bounds.width) / 2)),
|
|
1697
|
+
dy: snap(-bounds.y + Math.max(0, (viewHeight - bounds.height) / 2))
|
|
1647
1698
|
};
|
|
1648
1699
|
}
|
|
1649
1700
|
function shiftRects(rects, dx, dy) {
|
|
@@ -1654,10 +1705,13 @@ function shiftRects(rects, dx, dy) {
|
|
|
1654
1705
|
}
|
|
1655
1706
|
function contentBounds(nodeRects, groupRects) {
|
|
1656
1707
|
const rects = [...nodeRects.values(), ...groupRects.values()];
|
|
1657
|
-
if (rects.length === 0) return { width: 4 * NODE_WIDTH, height: 8 * GRID };
|
|
1708
|
+
if (rects.length === 0) return { x: 0, y: 0, width: 4 * NODE_WIDTH, height: 8 * GRID };
|
|
1709
|
+
const tops = [...nodeRects.values()].map((r) => r.y).concat([...groupRects.values()].map((r) => r.y - GROUP_LABEL_BAND));
|
|
1710
|
+
const minX = Math.min(...rects.map((r) => r.x));
|
|
1711
|
+
const minY = Math.min(...tops);
|
|
1658
1712
|
const maxX = Math.max(...rects.map((r) => r.x + r.width));
|
|
1659
1713
|
const maxY = Math.max(...rects.map((r) => r.y + r.height));
|
|
1660
|
-
return { width: maxX + GRID, height: maxY + GRID };
|
|
1714
|
+
return { x: minX - GRID, y: minY - GRID, width: maxX - minX + 2 * GRID, height: maxY - minY + 2 * GRID };
|
|
1661
1715
|
}
|
|
1662
1716
|
|
|
1663
1717
|
// src/scenes/diagram-scene/DiagramScene.geometry.ts
|
|
@@ -1794,7 +1848,9 @@ function DiagramScene({ scene }) {
|
|
|
1794
1848
|
const laidOutGroups = useMemo4(() => resolveGroupRects(scene.groups ?? [], laidOutNodes), [scene.groups, laidOutNodes]);
|
|
1795
1849
|
const bounds = useMemo4(() => contentBounds(laidOutNodes, laidOutGroups), [laidOutNodes, laidOutGroups]);
|
|
1796
1850
|
const { ref: canvasRef, size: canvasSize } = useCanvasSize(bounds);
|
|
1797
|
-
const
|
|
1851
|
+
const [zoomOverride, setZoomOverride] = useState6(null);
|
|
1852
|
+
const zoom = zoomOverride ?? fitScale(bounds, canvasSize);
|
|
1853
|
+
const offset = useMemo4(() => centerOffset(bounds, canvasSize, zoom), [bounds, canvasSize, zoom]);
|
|
1798
1854
|
const nodeRects = useMemo4(() => shiftRects(laidOutNodes, offset.dx, offset.dy), [laidOutNodes, offset]);
|
|
1799
1855
|
const groupRects = useMemo4(() => shiftRects(laidOutGroups, offset.dx, offset.dy), [laidOutGroups, offset]);
|
|
1800
1856
|
const index = currentDiagramStepIndex(scene, ranges, frame);
|
|
@@ -1850,87 +1906,97 @@ function DiagramScene({ scene }) {
|
|
|
1850
1906
|
const annotations = currentStep?.annotate ?? [];
|
|
1851
1907
|
return /* @__PURE__ */ jsxs7("div", { className: "flex h-full w-full flex-col gap-stack bg-bg p-panel-xl text-fg", children: [
|
|
1852
1908
|
scene.title && /* @__PURE__ */ jsx8("h2", { className: "text-xl font-semibold text-fg", children: scene.title }),
|
|
1853
|
-
/* @__PURE__ */ jsxs7(
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1909
|
+
/* @__PURE__ */ jsxs7(
|
|
1910
|
+
Canvas,
|
|
1911
|
+
{
|
|
1912
|
+
ref: canvasRef,
|
|
1913
|
+
zoom,
|
|
1914
|
+
onZoomChange: setZoomOverride,
|
|
1915
|
+
className: "min-h-0 flex-1 rounded-ui border border-border",
|
|
1916
|
+
children: [
|
|
1917
|
+
[...groupRects.values()].map((rect) => {
|
|
1918
|
+
const dimmed = focusIds != null && !focusIds.has(rect.id);
|
|
1919
|
+
const revealRange = findRevealRange(rect.id, scene, ranges);
|
|
1920
|
+
return /* @__PURE__ */ jsx8(Reveal, { asChild: true, from: "fade", delay: revealRange?.startFrame ?? 0, duration: revealRange ? "normal" : "instant", children: /* @__PURE__ */ jsx8(
|
|
1921
|
+
GraphGroup,
|
|
1922
|
+
{
|
|
1923
|
+
x: rect.x,
|
|
1924
|
+
y: rect.y,
|
|
1925
|
+
width: rect.width,
|
|
1926
|
+
height: rect.height,
|
|
1927
|
+
label: rect.data.label,
|
|
1928
|
+
border: rect.data.border,
|
|
1929
|
+
labelPlacement: "outside-top",
|
|
1930
|
+
className: cn(dimmed && "opacity-dim")
|
|
1931
|
+
}
|
|
1932
|
+
) }, rect.id);
|
|
1933
|
+
}),
|
|
1934
|
+
[...nodeRects.values()].map((rect) => {
|
|
1935
|
+
const node = rect.data;
|
|
1936
|
+
const dimmed = focusIds != null && !focusIds.has(node.id);
|
|
1937
|
+
const revealRange = findRevealRange(node.id, scene, ranges);
|
|
1938
|
+
const isExpanded = live.expandedNodeId === node.id;
|
|
1939
|
+
return /* @__PURE__ */ jsx8(Reveal, { asChild: true, from: "scale", delay: revealRange?.startFrame ?? 0, duration: revealRange ? "normal" : "instant", children: /* @__PURE__ */ jsx8(
|
|
1940
|
+
GraphNode,
|
|
1941
|
+
{
|
|
1942
|
+
x: rect.x,
|
|
1943
|
+
y: rect.y,
|
|
1944
|
+
style: { width: rect.width },
|
|
1945
|
+
variant: isExpanded ? "selected" : rect.simple ? "simple" : "default",
|
|
1946
|
+
shape: preset.nodeShape,
|
|
1947
|
+
header: node.label,
|
|
1948
|
+
subtitle: node.sublabel,
|
|
1949
|
+
headerDots: false,
|
|
1950
|
+
headerStatus: node.status ? /* @__PURE__ */ jsx8(StatusDot, { variant: node.status, size: "sm" }) : void 0,
|
|
1951
|
+
footer: node.metric,
|
|
1952
|
+
accent: Boolean(node.accent),
|
|
1953
|
+
accentColor: node.accent,
|
|
1954
|
+
className: cn(dimmed && "opacity-dim", live.isLive && "cursor-pointer"),
|
|
1955
|
+
onMouseEnter: () => live.onNodeHover(node.id),
|
|
1956
|
+
onMouseLeave: () => live.onNodeHover(null),
|
|
1957
|
+
onClick: (e) => {
|
|
1958
|
+
e.stopPropagation();
|
|
1959
|
+
live.onNodeClick(node.id);
|
|
1960
|
+
}
|
|
1961
|
+
}
|
|
1962
|
+
) }, node.id);
|
|
1963
|
+
}),
|
|
1964
|
+
/* @__PURE__ */ jsx8(ConnectionLayer, { edges: connectionEdges }),
|
|
1965
|
+
flowingEdgeIds.map((edgeId2) => {
|
|
1966
|
+
const geo = edgeGeometry.get(edgeId2);
|
|
1967
|
+
if (!geo) return null;
|
|
1968
|
+
const d = generatePath(geo.from, geo.to, geo.route, { obstacles: geo.obstacles });
|
|
1969
|
+
return /* @__PURE__ */ jsx8(
|
|
1970
|
+
Trace,
|
|
1971
|
+
{
|
|
1972
|
+
d,
|
|
1973
|
+
viewBox: `0 0 ${canvasSize.width} ${canvasSize.height}`,
|
|
1974
|
+
className: "absolute inset-0 h-full w-full",
|
|
1975
|
+
color: KIND_TRACE_COLOR[geo.kind],
|
|
1976
|
+
count: 2,
|
|
1977
|
+
duration: "slow"
|
|
1978
|
+
},
|
|
1979
|
+
edgeId2
|
|
1980
|
+
);
|
|
1981
|
+
}),
|
|
1982
|
+
annotations.map((a, i) => {
|
|
1983
|
+
const rect = nodeRects.get(a.target) ?? groupRects.get(a.target);
|
|
1984
|
+
if (!rect) return null;
|
|
1985
|
+
const side = a.side ?? "right";
|
|
1986
|
+
return /* @__PURE__ */ jsx8(
|
|
1987
|
+
Annotation,
|
|
1988
|
+
{
|
|
1989
|
+
target: borderCenter(rect, side),
|
|
1990
|
+
label: a.text,
|
|
1991
|
+
side,
|
|
1992
|
+
progress: annotateProgress
|
|
1993
|
+
},
|
|
1994
|
+
`${a.target}-${i}`
|
|
1995
|
+
);
|
|
1996
|
+
})
|
|
1997
|
+
]
|
|
1998
|
+
}
|
|
1999
|
+
)
|
|
1934
2000
|
] });
|
|
1935
2001
|
}
|
|
1936
2002
|
|
|
@@ -2239,7 +2305,7 @@ import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
|
2239
2305
|
function renderPane(pane) {
|
|
2240
2306
|
switch (pane.content) {
|
|
2241
2307
|
case "code":
|
|
2242
|
-
return /* @__PURE__ */ jsx13(CodeBlock, { code: pane.code, language: pane.lang, header: pane.label, className: "h-full" });
|
|
2308
|
+
return /* @__PURE__ */ jsx13(CodeBlock, { code: pane.code, language: pane.lang, header: pane.label, className: "h-full border-0 rounded-none" });
|
|
2243
2309
|
case "text":
|
|
2244
2310
|
return /* @__PURE__ */ jsx13("div", { className: "whitespace-pre-wrap p-panel text-sm text-fg", children: pane.text });
|
|
2245
2311
|
case "image":
|
|
@@ -2250,6 +2316,9 @@ function renderPane(pane) {
|
|
|
2250
2316
|
}
|
|
2251
2317
|
}
|
|
2252
2318
|
}
|
|
2319
|
+
function paneLabel(pane) {
|
|
2320
|
+
return pane.content === "code" ? void 0 : pane.label;
|
|
2321
|
+
}
|
|
2253
2322
|
function CompareScene({ scene }) {
|
|
2254
2323
|
const ranges = useSequence(sceneSteps(scene), scene.pace);
|
|
2255
2324
|
const range = Object.values(ranges)[0];
|
|
@@ -2266,8 +2335,8 @@ function CompareScene({ scene }) {
|
|
|
2266
2335
|
mode: mode === "wipe" ? "wipe" : "side-by-side",
|
|
2267
2336
|
before: beforeNode,
|
|
2268
2337
|
after: afterNode,
|
|
2269
|
-
beforeLabel: scene.before
|
|
2270
|
-
afterLabel: scene.after
|
|
2338
|
+
beforeLabel: paneLabel(scene.before),
|
|
2339
|
+
afterLabel: paneLabel(scene.after),
|
|
2271
2340
|
progress,
|
|
2272
2341
|
className: "w-full"
|
|
2273
2342
|
}
|
|
@@ -2276,7 +2345,45 @@ function CompareScene({ scene }) {
|
|
|
2276
2345
|
}
|
|
2277
2346
|
|
|
2278
2347
|
// src/scenes/walkthrough-scene/WalkthroughScene.tsx
|
|
2279
|
-
import { useLayoutEffect as useLayoutEffect6, useMemo as useMemo6, useRef as useRef5, useState as
|
|
2348
|
+
import { useLayoutEffect as useLayoutEffect6, useMemo as useMemo6, useRef as useRef5, useState as useState7 } from "react";
|
|
2349
|
+
|
|
2350
|
+
// src/scenes/walkthrough-scene/WalkthroughScene.spotlight.ts
|
|
2351
|
+
var SPOTLIGHT_DIM = 0.6;
|
|
2352
|
+
var FADE_SHARE = 0.2;
|
|
2353
|
+
var FADE_CAP_FRAMES = 12;
|
|
2354
|
+
function lerp(a, b, t) {
|
|
2355
|
+
return a + (b - a) * t;
|
|
2356
|
+
}
|
|
2357
|
+
function lerpRect(from, to, t) {
|
|
2358
|
+
return {
|
|
2359
|
+
x: lerp(from.x, to.x, t),
|
|
2360
|
+
y: lerp(from.y, to.y, t),
|
|
2361
|
+
width: lerp(from.width, to.width, t),
|
|
2362
|
+
height: lerp(from.height, to.height, t)
|
|
2363
|
+
};
|
|
2364
|
+
}
|
|
2365
|
+
function fadeFrames(span) {
|
|
2366
|
+
return Math.min(FADE_CAP_FRAMES, Math.max(1, Math.round(span * FADE_SHARE)));
|
|
2367
|
+
}
|
|
2368
|
+
function spotlightPlan(steps, ranges, index, frame) {
|
|
2369
|
+
const step = steps[index];
|
|
2370
|
+
const range = step ? ranges[stepName(step.id, index)] : void 0;
|
|
2371
|
+
const fallbackDelay = range?.startFrame ?? 0;
|
|
2372
|
+
if (!step?.spotlight || !range) return { focus: null, dim: 0, delay: fallbackDelay };
|
|
2373
|
+
let runStart = index;
|
|
2374
|
+
while (runStart > 0 && steps[runStart - 1].spotlight) runStart -= 1;
|
|
2375
|
+
const delay = ranges[stepName(steps[runStart].id, runStart)]?.startFrame ?? fallbackDelay;
|
|
2376
|
+
const span = Math.max(1, range.endFrame - range.startFrame);
|
|
2377
|
+
const ramp = fadeFrames(span);
|
|
2378
|
+
const previous = index > runStart ? steps[index - 1].spotlight : void 0;
|
|
2379
|
+
const entry = previous ? clamp01((frame - range.startFrame) / ramp) : 1;
|
|
2380
|
+
const focus = previous ? lerpRect(previous, step.spotlight, entry) : step.spotlight;
|
|
2381
|
+
const handsOverToDark = !steps[index + 1]?.spotlight;
|
|
2382
|
+
const exit = handsOverToDark ? clamp01((range.endFrame - frame) / ramp) : 1;
|
|
2383
|
+
return { focus, dim: SPOTLIGHT_DIM * exit, delay };
|
|
2384
|
+
}
|
|
2385
|
+
|
|
2386
|
+
// src/scenes/walkthrough-scene/WalkthroughScene.tsx
|
|
2280
2387
|
import { jsx as jsx14, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
2281
2388
|
var ACTION_MAP = {
|
|
2282
2389
|
none: void 0,
|
|
@@ -2293,7 +2400,7 @@ function currentIndex3(steps, ranges, frame) {
|
|
|
2293
2400
|
}
|
|
2294
2401
|
function useFrameSize() {
|
|
2295
2402
|
const ref = useRef5(null);
|
|
2296
|
-
const [size, setSize] =
|
|
2403
|
+
const [size, setSize] = useState7({ width: 0, height: 0 });
|
|
2297
2404
|
useLayoutEffect6(() => {
|
|
2298
2405
|
const el = ref.current;
|
|
2299
2406
|
if (!el) return void 0;
|
|
@@ -2344,13 +2451,13 @@ function WalkthroughScene({ scene }) {
|
|
|
2344
2451
|
});
|
|
2345
2452
|
return out;
|
|
2346
2453
|
}, [scene.steps, ranges, size]);
|
|
2347
|
-
const
|
|
2348
|
-
const
|
|
2454
|
+
const plan = spotlightPlan(scene.steps, ranges, index, frame);
|
|
2455
|
+
const spotlightFocus = plan.focus && size.width > 0 ? rectToPx(plan.focus, size) : { x: 0, y: 0, width: 0, height: 0 };
|
|
2349
2456
|
const target = step ? resolveTarget(step) : void 0;
|
|
2350
2457
|
const annotateTarget = step?.annotate && target && size.width > 0 ? toPx(target, size) : void 0;
|
|
2351
2458
|
const screenshot = /* @__PURE__ */ jsx14(Image, { src: scene.image, alt: scene.title ?? "screenshot", fit: "cover", radius: "none", className: "h-full w-full" });
|
|
2352
2459
|
return /* @__PURE__ */ jsx14("div", { className: "flex h-full w-full items-center justify-center bg-bg p-panel-xl text-fg", children: /* @__PURE__ */ jsx14("div", { className: "w-full max-w-4xl", children: /* @__PURE__ */ jsx14(DeviceFrame, { variant: scene.frame ?? "browser", url: scene.url, title: scene.title, className: "aspect-video w-full", children: /* @__PURE__ */ jsxs12("div", { ref: frameRef, className: "relative h-full w-full", children: [
|
|
2353
|
-
/* @__PURE__ */ jsx14(Spotlight, { focus: spotlightFocus, dim:
|
|
2460
|
+
/* @__PURE__ */ jsx14(Spotlight, { focus: spotlightFocus, dim: plan.dim, delay: plan.delay, duration: "normal", className: "h-full w-full", children: screenshot }),
|
|
2354
2461
|
/* @__PURE__ */ jsx14(Cursor, { events }),
|
|
2355
2462
|
annotateTarget && /* @__PURE__ */ jsx14(
|
|
2356
2463
|
"div",
|