diff --git a/.gitignore b/.gitignore index 9108ea0..4fc542a 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ ui/storybook-fixtures/ .pnpm-store/ *.tsbuildinfo .claude/worktrees/ +.DS_Store diff --git a/demo-video/.gitignore b/demo-video/.gitignore new file mode 100644 index 0000000..33609ed --- /dev/null +++ b/demo-video/.gitignore @@ -0,0 +1,5 @@ +renders/ +snapshots/ +.hyperframes/ +.thumbnails/ +.waveform-cache/ diff --git a/demo-video/README.md b/demo-video/README.md new file mode 100644 index 0000000..8f37ad6 --- /dev/null +++ b/demo-video/README.md @@ -0,0 +1,28 @@ +# Demo video + +A [HyperFrames](https://hyperframes.heygen.com) film: HTML and GSAP, rendered to MP4. + +| File | Owns | +|---|---| +| `SCRIPT.md` | The words. One paragraph is one scene, and the bold line under it is the visual hint. | +| `assets/narration.mp3`, `assets/narration-timing.json` | The voiceover, and when each word of it happens, from ElevenLabs. | +| `scenes.json` | The scenes, in the same order as the paragraphs. Scenes start and end on the words they cover. | +| `compositions/.html` | Each scene's pictures. | +| `assets/lib.js`, `assets/style.css` | The shared art: robot, cards, grid, booth, rocket. | +| `index.html`, `compositions/captions.html`, `subtitles.srt`, `subtitles.vtt` | Generated. Don't edit them. | + +```sh +npm install +node build.mjs # after changing SCRIPT.md or scenes.json +npx hyperframes preview # edit and scrub in the browser +npx hyperframes check # lint, layout, and motion +npx hyperframes render -o renders/selfie-demo.mp4 +``` + +## Changing the voiceover + +Replace the two `assets/narration*` files with a new take, and run `node build.mjs`. Scene lengths, subtitles and any beat written as `w("word")` all follow the words. The build stops with an error if `SCRIPT.md` and the audio disagree on a word. + +`"draftScript": true` in `scenes.json` allows that while editing: an edited paragraph keeps its slot in the take, and the build lists it as out of date. Remove it once a new take is in. + +To turn burned-in subtitles off, remove the captions host from the template in `build.mjs`. diff --git a/demo-video/REVIEW.md b/demo-video/REVIEW.md new file mode 100644 index 0000000..c7eab8d --- /dev/null +++ b/demo-video/REVIEW.md @@ -0,0 +1,10 @@ +# Review notes + +For whoever changes this film next, including an agent. + +- **Creative calls:** one flat, inked paper world, with one grid of components carried through the first act; a plain robot in the mascot's colors for acting, and the real mascot kept for the end card; a dull fog for Chromatic and a yellow flood for Selfie. +- **Illustrative, not real:** the app, its commits and their hashes. The commit status wording is what `ui/src/shared/commit-status.ts` really posts. +- **Claims with a source:** $0.008 per extra snapshot from chromatic.com/pricing; 541 stories in about 70 s on an M4 MacBook Air, as given by Ned and in the README, not re-measured here. +- **Timing:** scenes, subtitles and key beats come from the word times ElevenLabs gave for this take. +- **Not checked:** the sound was measured, not heard. +- **Kept warnings:** `timeline_track_too_dense`, since there is one clip per subtitle; `dead_stretch`, judged against a frame-by-frame check of the rendered file. diff --git a/demo-video/SCRIPT.md b/demo-video/SCRIPT.md new file mode 100644 index 0000000..645d189 --- /dev/null +++ b/demo-video/SCRIPT.md @@ -0,0 +1,58 @@ +## COLD OPEN + +You point your coding agent at one button. But most changes have unforeseen consequences. +**[A whole app on screen. Human hand points at a button, robot drops in and hammers it, panels far away from the button crack and tilt]** + +## INTRO STORYBOOK + +What if you took every page and component of your UI, and laid them out next to each other? +**[The app splits into pieces, which line up in a row]** + +Now, let’s copy those components into every state the user will see: empty states, error states, simple states, and complex ones. +**[Zoom in on one, and copy it down the column into each state as it’s named, only ever moving down. Then zoom out as every column fills the grid]** + +StorybookJS has made that easy for the past 10 years. With coding agents, it’s more useful than ever: you can point at the specific page you want changed. +**[The grid gets a Storybook frame. Human hand points to a cell, robot comes and hammers on it]** + +But how can you understand the full ripples of a code change? +**[Radial ripple from the hammer across the whole grid, knocking loose distant cells the robot can’t see]** + +## INTRO CHROMATIC + +Enter Chromatic. This is a cloud service which renders every component of a Storybook to an image. +**[A photo booth in a cloud. Components ride past the camera, flash, a photo comes out]** + +Change the code, render every component to another image, and compare. +**[Two photos of the same component line up, and the difference is highlighted]** + +This works great, but it requires waiting on a cloud service. At 0.8 cents per image, this was a reasonable expense at the speed of human code. +**[Coin meter on the booth, with a waiting bar. A person presses the button now and then, and a coin drops]** + +But for a modest library of 1,000 stories, that’s 8 bucks for a full run, each time you even look at a code change! +**[A robot takes over the button, and the meter spins up to $8, $80, $800 under a flood of coins]** + +## INTRO SELFIE + +Introducing Selfie.dev: a snapshot testing tool which runs locally and in the cloud. Your agent takes its own picture. +**[Flash floods the screen with a new color. Selfie wordmark, robot with a pink phone takes a picture of itself]** + +No waiting on a cloud rendering service, and no paying for each image. On an M4 MacBook Air, Selfie’s own test suite of 541 stories runs in about 70 seconds. +**[Robot taking lots of goofy selfies with the components, photos pile up, the numbers land]** + +It works just like git. Images are stored locally wherever the agent is running, and also in the cloud, so that every commit on GitHub shows the complete visual consequences of the code change. +**[Several robots on the ground, each with their own suitcase of photos, waving up to a shared photo wall in a cloud. Cut to a commit list with a `selfie` status on each commit]** + +Approvals follow your history too, so they survive a commit, a rebase, or a merge. +**[A stamped photo rides along a git graph, keeping its stamp at each node]** + +Every image is rendered in the same pinned environment, wherever it runs. +**[Identical containers on different machines, each producing the identical photo]** + +Each image is named by a hash of its pixels. Most changes touch only a few, so only those few get sent. +**[Robot takes a single photo out of the suitcase, folds it into a paper airplane, and throws it up to the cloud]** + +If you’re using StorybookJS, you can start using Selfie locally to review your changes right now, at selfie.dev. +**[Robots signing up outside of a space pad]** + +If you want to use it in the cloud on your GitHub repositories, join our waitlist, and get a discount when we launch in mid-November. +**[Robots launch into space as they take selfies out the window. End card with the full mascot and the URL]** diff --git a/demo-video/assets/chromatic-logo.svg b/demo-video/assets/chromatic-logo.svg new file mode 100644 index 0000000..5188159 --- /dev/null +++ b/demo-video/assets/chromatic-logo.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/demo-video/assets/fonts/BagelFatOne.woff2 b/demo-video/assets/fonts/BagelFatOne.woff2 new file mode 100644 index 0000000..bd96304 Binary files /dev/null and b/demo-video/assets/fonts/BagelFatOne.woff2 differ diff --git a/demo-video/assets/fonts/Jost.woff2 b/demo-video/assets/fonts/Jost.woff2 new file mode 100644 index 0000000..08a7b80 Binary files /dev/null and b/demo-video/assets/fonts/Jost.woff2 differ diff --git a/demo-video/assets/lib.js b/demo-video/assets/lib.js new file mode 100644 index 0000000..6bbdb7b --- /dev/null +++ b/demo-video/assets/lib.js @@ -0,0 +1,335 @@ +// Shared art and helpers for the scenes. Everything here is deterministic, so every render is identical. +(function () { + const INK = "#23232b", PINK = "#f4689f", CORAL = "#e35c61", TEAL = "#78acae", BLUE = "#63b9e3", ORANGE = "#f58a3c", SUN = "#ffd95e", PALE_BLUE = "#abcbf2", GREY = "#d7dbe3"; + + /** A scene is authored against a nominal length, and its beats stretch to its slot. Move speeds stay as written. */ + function clock(id, authored) { + const k = window.SCENE_DURATIONS[id] / authored; + return (t) => t * k; + } + + /** + * When a scene's narration says a word, in seconds within the scene. nth counts repeats of it. + * Pass after: true for when the word ends. An unknown word is an error, so a beat can't silently come loose. + */ + function say(id, word, { nth = 0, after = false } = {}) { + const plain = word.toLowerCase().replace(/[^a-z0-9]/g, ""); + const hit = window.SCENE_WORDS[id].filter(([w]) => w === plain)[nth]; + if (!hit) throw new Error(`Scene ${id} never says "${word}"`); + return after ? hit[2] : hit[1]; + } + + /** Seeded, so every render is identical. */ + function rand(seed) { + let s = seed >>> 0; + return () => { + s = (s + 0x6d2b79f5) >>> 0; + let t = s; + t = Math.imul(t ^ (t >>> 15), t | 1); + t ^= t + Math.imul(t ^ (t >>> 7), t | 61); + return ((t ^ (t >>> 14)) >>> 0) / 4294967296; + }; + } + + function html(parent, markup) { + const t = document.createElement("template"); + t.innerHTML = markup.trim(); + const el = t.content.firstElementChild; + parent.appendChild(el); + return el; + } + + const bar = (x, y, w, h, fill) => ``; + const dot = (x, y, r, fill) => ``; + + const KINDS = { + chart: { title: "Revenue", long: "Quarterly recurring revenue by reg…", draw(dense) { + const hs = dense ? [40, 62, 30, 88, 52, 104, 70, 34, 96, 58, 110, 76] : [46, 74, 58, 104, 86, 118]; + const w = 196 / hs.length; + return hs.map((h, i) => bar(4 + i * w, 128 - h, w - 5, h, i % 2 ? BLUE : TEAL)).join(""); + }, simple: () => bar(60, 70, 40, 58, TEAL) + bar(112, 40, 40, 88, BLUE) }, + list: { title: "Team", long: "Everyone in the Northern Hemisphere o…", draw(dense) { + const n = dense ? 6 : 3, step = 128 / n; + return Array.from({ length: n }, (_, i) => dot(16, step * i + step / 2, Math.min(step / 2 - 2, 15), [PINK, BLUE, ORANGE][i % 3]) + bar(40, step * i + step / 2 - 6, 150 - i * 12, 12, GREY)).join(""); + }, simple: () => dot(30, 64, 22, PINK) + bar(66, 56, 130, 16, GREY) }, + form: { title: "Sign up", long: "Create your extremely long account na…", draw(dense) { + return bar(2, 4, 208, 30, GREY) + bar(2, 42, 208, 30, GREY) + (dense ? bar(2, 42, 208, 30, CORAL) : ""); + }, simple: () => bar(2, 26, 208, 30, GREY), button: true }, + profile: { title: "Profile", long: "Wolfeschlegelsteinhausenbergerdorff…", draw(dense) { + return dot(50, 58, 42, ORANGE) + dot(50, 44, 15, "#fff") + `` + bar(108, 26, 96, 14, GREY) + bar(108, 52, 72, 14, GREY) + bar(108, 78, dense ? 104 : 54, 14, dense ? CORAL : TEAL); + }, simple: () => dot(106, 62, 44, ORANGE) + dot(106, 48, 15, "#fff") + `` }, + stat: { title: "Sales", long: "Sales in every currency since the d…", draw(dense) { + return `${dense ? "$1,204,399,210.07" : "$4,210"}` + ``; + }, simple: () => `$5` }, + tabs: { title: "Settings", long: "Notification preferences for all of…", draw(dense) { + const n = dense ? 5 : 3, step = 128 / n; + return Array.from({ length: n }, (_, i) => bar(4, step * i + step / 2 - 6, 110, 12, GREY) + bar(150, step * i + step / 2 - 12, 54, 24, i % 2 ? GREY : TEAL) + dot(i % 2 ? 163 : 192, step * i + step / 2, 10, "#fff")).join(""); + }, simple: () => bar(4, 58, 110, 12, GREY) + bar(150, 52, 54, 24, TEAL) + dot(192, 64, 10, "#fff") }, + }; + + const crackPath = ``; + + function cardBody(kind, state, opts) { + const k = KINDS[kind]; + if (state === "empty") { + return `Nothing yet`; + } + if (state === "error") { + return `Couldn’t load`; + } + const dense = state === "complex"; + let out = state === "simple" ? k.simple() : k.draw(dense); + if (k.button) { + const fill = opts.changed ? BLUE : PINK; + const w = opts.changed ? 208 : 120, x = 2; + out += `Join`; + } + return out; + } + + /** A component in a state: easy, empty, error, simple or complex. 245 x 190 before scaling. */ + function card(parent, kind, state = "easy", opts = {}) { + const k = KINDS[kind]; + const title = state === "complex" ? k.long : k.title; + return html(parent, `
${title}
${cardBody(kind, state, opts)}
${crackPath}
`); + } + + // The app and the grid put the same cards in different places, which is what lets a scene hand over to the next. + const KIND_ORDER = ["chart", "form", "profile", "list", "stat", "tabs"]; + const STATES = ["easy", "empty", "error", "simple", "complex"]; + const APP = { x: 366, y: 142, scale: 1.55 }; + const appPos = (i) => ({ x: APP.x + 30 + (i % 3) * 392, y: APP.y + 28 + Math.floor(i / 3) * 312, scale: APP.scale, rotation: 0 }); + const rowPos = (c) => ({ x: 116 + c * 284, y: 330, scale: 1.1, rotation: 0 }); + const gridPos = (c, r) => ({ x: 214 + c * 268, y: 42 + r * 212, scale: 1, rotation: 0 }); + const FORM_BUTTON = (() => { const p = appPos(1); return { x: p.x + 110, y: p.y + 225 }; })(); + + function appWindow(parent, id) { + return html(parent, `
acme.app
`); + } + + const place = (el, p) => gsap.set(el, { x: p.x, y: p.y, scale: p.scale ?? 1, rotation: p.rotation ?? 0, transformOrigin: "0 0" }); + + /** + * The robot, 240 x 340 at scale 1: a plain cousin of the mascot, with its eyes and colors. + * Returns its parts so a scene can act with them: head, armL, armR, legL, legR, eyes, smile, o, prop. + */ + function robot(parent, opts = {}) { + const { id = "", prop = "", scale = 1 } = opts; + const props = { + hammer: ``, + phone: ``, + suitcase: ``, + }; + const el = html(parent, ` + + + + + + ${props[prop] ?? ""} + + + + + + + `); + const part = (c) => el.querySelector(c); + gsap.set(part(".rb-armR"), { svgOrigin: "197 158" }); + gsap.set(part(".rb-armL"), { svgOrigin: "43 158" }); + gsap.set(part(".rb-head"), { svgOrigin: "120 130" }); + gsap.set(part(".rb-eyes"), { svgOrigin: "120 80" }); + if (part(".rb-prop")) gsap.set(part(".rb-prop"), { svgOrigin: "0 0" }); + return { el, head: part(".rb-head"), armL: part(".rb-armL"), armR: part(".rb-armR"), legL: part(".rb-legL"), legR: part(".rb-legR"), eyes: part(".rb-eyes"), smile: part(".rb-smile"), o: part(".rb-o"), prop: part(".rb-prop") }; + } + + /** + * A person's right hand, seen from the back, pointing up and to the left, with its sleeve running off-screen. + * The element's origin is the fingertip, so placing the element places the point. + */ + function hand(parent, id) { + const SKIN = "#f3c6a5", CREASE = "#c98f6c"; + const line = `stroke="${INK}" stroke-width="7" stroke-linejoin="round" stroke-linecap="round"`; + const knuckle = (x0, x1, top) => ``; + const crease = (x0, x1, y) => ``; + return html(parent, ` + + ${crease(-16, 16, -62)} + + + ${crease(-17, 17, 92)}${crease(-17, 17, 158)} + ${knuckle(145, 192, 212)}${knuckle(90, 146, 188)}${knuckle(31, 91, 172)} + ${crease(46, 76, 214)}${crease(104, 132, 228)}${crease(156, 182, 248)} + + `); + } + + /** The cloud, flat with an ink outline. width x width * 0.56. */ + function cloud(parent, id, width) { + return html(parent, ``); + } + + /** A photo of a component: the card, in a white frame. 283 x 256 before scaling. */ + function photo(parent, kind, state, opts = {}) { + const el = html(parent, `
`); + const c = card(el, kind, state, opts); + gsap.set(c, { x: 14, y: 14 }); + c.style.boxShadow = "none"; + if (opts.caption) html(el, `
${opts.caption}
`); + return el; + } + + /** A shock ring centered on (x, y), expanding once. */ + function ringAt(tl, parent, x, y, t, { size = 900, color = CORAL, dur = 0.8 } = {}) { + const r = html(parent, `
`); + gsap.set(r, { x: x - 50, y: y - 50, scale: 0.2, opacity: 0 }); + tl.set(r, { opacity: 0.95 }, t); + tl.to(r, { scale: size / 100, opacity: 0, duration: dur, ease: "power2.out" }, t + 0.001); + return r; + } + + /** One swing of an arm-held prop: wind up, strike. The hit lands at t. */ + function swing(tl, arm, t, { rest = null, wind = 0.2 } = {}) { + tl.to(arm, { rotation: -95, duration: wind, ease: "power2.out" }, t - 0.08 - wind); + tl.to(arm, { rotation: HAMMER.angle, duration: 0.08, ease: "power3.in" }, t - 0.08); + if (rest !== null) tl.to(arm, { rotation: rest, duration: 0.25, ease: "power2.out" }, t + 0.1); + } + + /** A mouth change goes through a take rather than a snap. */ + function gasp(tl, r, t) { + tl.to(r.smile, { opacity: 0, duration: 0.05 }, t); + tl.to(r.o, { opacity: 1, duration: 0.05 }, t); + tl.set(r.head, { scaleY: 0.92 }, t); + tl.to(r.head, { scaleY: 1, duration: 0.45, ease: "elastic.out(1, 0.5)" }, t + 0.001); + } + + function blink(tl, r, t) { + tl.to(r.eyes, { scaleY: 0.1, duration: 0.05 }, t); + tl.to(r.eyes, { scaleY: 1, duration: 0.07 }, t + 0.08); + } + + /** A flash that lands at t. */ + function flash(tl, el, t, peak) { + tl.set(el, { opacity: peak }, t); + tl.to(el, { opacity: 0, duration: 0.32, ease: "power2.out" }, t + 0.001); + } + + /** Text swapped at set times; each entry is [time, element]. Seek-safe, unlike writing into one node. */ + function ticker(tl, entries) { + entries.forEach(([t, el], i) => { + gsap.set(el, { opacity: 0 }); + tl.set(el, { opacity: 1 }, t); + if (entries[i + 1]) tl.set(el, { opacity: 0 }, entries[i + 1][0]); + }); + } + + // Where the hammer's face lands, in the robot's own 240 x 340 space, at the strike angle. + const HAMMER = { x: 347, y: 248, angle: -15 }; + + /** Build a scene's stage. Returns the timeline, its clock and the element to draw into. */ + function begin(id, authored = window.SCENE_DURATIONS[id]) { + return { tl: gsap.timeline({ paused: true }), at: clock(id, authored), w: (word, o) => say(id, word, o), stage: document.getElementById(`${id}-stage`) }; + } + + /** Every component in every state, in the grid's places. cards[column][row]. */ + function grid(parent) { + return KIND_ORDER.map((k, c) => STATES.map((st, r) => { const el = card(parent, k, st); place(el, gridPos(c, r)); return el; })); + } + const ROW_NAMES = ["", "Empty", "Error", "Simple", "Complex"]; + function rowLabels(parent) { + return ROW_NAMES.map((n, r) => { + const el = html(parent, `
${n}
`); + gsap.set(el, { x: 8, y: gridPos(0, r).y + 70 }); + return el; + }); + } + + // The grid framed by Storybook, shared by the scenes on either side of the ripple. + const GRID_CAM = { x: 184, y: 0, scale: 0.85 }; // The whole grid, clear of the subtitles. + const FRAMED = { x: 530, y: 85, scale: 0.66 }; + const framedCell = (c, r) => { const p = gridPos(c, r); return { x: FRAMED.x + (p.x + 122) * FRAMED.scale, y: FRAMED.y + (p.y + 95) * FRAMED.scale }; }; + const STORIES = KIND_ORDER.map((k) => KINDS[k].title); + function storybookFrame(parent) { + const el = html(parent, `
localhost:6006
Storybook
${STORIES.map((n, i) => `
${n}
`).join("")}
`); + return { el, items: [...el.querySelectorAll(".sb-item")] }; + } + + function camera(parent, id, scale = 1) { + return html(parent, ``); + } + + function rocket(parent, id) { + const head = (cy) => ``; + const el = html(parent, `${head(240)}${head(372)}${head(504)}`); + gsap.set(el.querySelector(".rk-fire"), { svgOrigin: "180 684" }); + return { el, fire: el.querySelector(".rk-fire") }; + } + + function plane(parent, id, scale = 1) { + return html(parent, ``); + } + + /** A selfie: the robot's face crowding a component. 283 x 256 before scaling. */ + function selfiePhoto(parent, kind, tilt) { + const el = photo(parent, kind, "easy"); + const r = robot(el, { scale: 0.62 }); + gsap.set(r.el, { x: 136, y: 30 }); + gsap.set(r.head, { rotation: tilt }); + return el; + } + + const DAMAGED = [[0, -6], [3, 8], [4, -4], [5, 7]]; + /** Where to stand a robot of this scale, so its hammer's face lands on this point. */ + const hammerAt = ({ x, y }, scale) => ({ x: x - HAMMER.x * scale, y: y - HAMMER.y * scale }); + const COLD_ROBOT = { scale: 1.6, ...hammerAt(FORM_BUTTON, 1.6) }; + + window.S = { say, rand, hammerAt, html, card, photo, robot, hand, cloud, appWindow, appPos, rowPos, gridPos, place, ringAt, swing, gasp, blink, flash, ticker, FORM_BUTTON, begin, grid, rowLabels, GRID_CAM, FRAMED, framedCell, storybookFrame, camera, rocket, plane, selfiePhoto, DAMAGED, COLD_ROBOT, HAMMER, KIND_ORDER, INK, CORAL, TEAL, ORANGE, SUN, GREY }; + + /** The pay-per-image booth: a cloud with a waiting bar, a button, a meter and a coin jar. */ + S.booth = function (stage) { + const cl = S.cloud(stage, "", 800); + gsap.set(cl, { x: 40, y: 20 }); + S.html(stage, `
`); + S.html(stage, `
Waiting…
`); + S.html(stage, `
`); + S.html(stage, `Chromatic`); + const bar = S.html(stage, `
`); + gsap.set(bar, { scaleX: 0.02 }); + + S.html(stage, `
`); + const screen = S.html(stage, `
`); + const per = S.html(stage, `
0.8¢ per image
`); + const digit = (t) => { const d = S.html(stage, `
${t}
`); gsap.set(d, { opacity: 0 }); return d; }; + + S.html(stage, `
`); + const button = S.html(stage, `
`); + + const jar = S.html(stage, ``); + gsap.set(jar, { x: 1150, y: 410 }); + return { bar, screen, per, digit, button, jar }; + }; + // Seams. A flash is a photo being taken, and a whip carries the eye sideways into the next idea. + S.flashIn = function (tl, stage) { + const white = S.html(stage, `
`); + tl.to(white, { opacity: 0, duration: 0.34, ease: "power2.out" }, 0.001); + }; + S.whipOut = function (tl, stage, id) { + tl.to(stage, { x: -1920, duration: 0.2, ease: "power3.in" }, window.SCENE_DURATIONS[id] - 0.21); + }; + S.whipIn = function (tl, stage) { + tl.fromTo(stage, { x: 1920 }, { x: 0, duration: 0.42, ease: "expo.out" }, 0); + }; + S.press = function (tl, button, t) { + tl.to(button, { scaleY: 0.72, duration: 0.07, ease: "power2.in" }, t); + tl.to(button, { scaleY: 1, duration: 0.24, ease: "power2.out" }, t + 0.07); + }; + /** A coin from the meter into the jar, landing at (x, y). */ + S.coinDrop = function (tl, stage, t, x, y, fall = 0.42) { + const c = S.html(stage, `
`); + gsap.set(c, { x: 1370, y: 340, opacity: 0 }); + tl.set(c, { opacity: 1 }, t); + tl.to(c, { x, duration: fall, ease: "none" }, t); + tl.to(c, { y, duration: fall, ease: "bounce.out" }, t); + return c; + }; +})(); diff --git a/demo-video/assets/mascot-cutout.png b/demo-video/assets/mascot-cutout.png new file mode 100644 index 0000000..5a2b786 Binary files /dev/null and b/demo-video/assets/mascot-cutout.png differ diff --git a/demo-video/assets/narration-timing.json b/demo-video/assets/narration-timing.json new file mode 100644 index 0000000..2b6110d --- /dev/null +++ b/demo-video/assets/narration-timing.json @@ -0,0 +1,3511 @@ +{ + "language_code": null, + "segments": [ + { + "text": "You point your coding agent at one button, but most changes have unforeseen consequences. What if you took every page and component of your UI and laid them out next to each other? Now, let's copy those components into every state the user will see: empty states, error states, simple states, and complex ones. Storybook JS has made that easy for the past 10 years. With coding agents, it's more useful than ever. You can point at the specific page you want changed. But how can you understand the full ripples of a code change? Enter Chromatic. This is a cloud service which renders every component of a storybook to an image. Change the code, render every component to another image, and compare. This works great, but it requires waiting on a cloud service. At 0.8 cents per image, this was a reasonable expense at the speed of human code. But for a modest library of 1,000 stories, that's eight bucks for a full run each time you even look at a code change. Introducing selfie.dev, a snapshot testing tool which runs locally and in the cloud. Your agent takes its own picture. No waiting on a cloud rendering service and no paying for each image. On an M4 MacBook Air, Selfie's own test suite of 541 stories runs in about 70 seconds. It works just like Git. Images are stored locally wherever the agent is running, and also in the cloud, so that every commit on GitHub shows the complete visual consequences of the code change. Approvals follow your history too, so they survive a commit, a rebase, or a merge. Every image is rendered in the same pinned environment, wherever it runs. Each image is named by a hash of its pixels. Most changes touch only a few, so only those few get sent. If you're using Storybook JS, you can start using Selfie locally to review your changes right now at selfie.dev. If you want to use it in the cloud on your GitHub repositories, join our waitlist and get a discount when we launch in mid-November.", + "start_time": 0.12, + "end_time": 124.26, + "speaker": { + "id": "speaker_0", + "name": "Speaker 0" + }, + "words": [ + { + "text": "You", + "start_time": 0.12, + "end_time": 0.22 + }, + { + "text": " ", + "start_time": 0.22, + "end_time": 0.26 + }, + { + "text": "point", + "start_time": 0.26, + "end_time": 0.44 + }, + { + "text": " ", + "start_time": 0.44, + "end_time": 0.48 + }, + { + "text": "your", + "start_time": 0.48, + "end_time": 0.6 + }, + { + "text": " ", + "start_time": 0.6, + "end_time": 0.66 + }, + { + "text": "coding", + "start_time": 0.66, + "end_time": 0.94 + }, + { + "text": " ", + "start_time": 0.94, + "end_time": 1.0 + }, + { + "text": "agent", + "start_time": 1.0, + "end_time": 1.24 + }, + { + "text": " ", + "start_time": 1.24, + "end_time": 1.28 + }, + { + "text": "at", + "start_time": 1.28, + "end_time": 1.42 + }, + { + "text": " ", + "start_time": 1.42, + "end_time": 1.5 + }, + { + "text": "one", + "start_time": 1.5, + "end_time": 1.68 + }, + { + "text": " ", + "start_time": 1.68, + "end_time": 1.78 + }, + { + "text": "button,", + "start_time": 1.78, + "end_time": 2.1 + }, + { + "text": " ", + "start_time": 2.1, + "end_time": 2.1 + }, + { + "text": "but", + "start_time": 2.72, + "end_time": 2.9 + }, + { + "text": " ", + "start_time": 2.9, + "end_time": 2.96 + }, + { + "text": "most", + "start_time": 2.96, + "end_time": 3.24 + }, + { + "text": " ", + "start_time": 3.24, + "end_time": 3.3 + }, + { + "text": "changes", + "start_time": 3.3, + "end_time": 3.82 + }, + { + "text": " ", + "start_time": 3.82, + "end_time": 3.88 + }, + { + "text": "have", + "start_time": 3.88, + "end_time": 4.12 + }, + { + "text": " ", + "start_time": 4.12, + "end_time": 4.18 + }, + { + "text": "unforeseen", + "start_time": 4.18, + "end_time": 4.96 + }, + { + "text": " ", + "start_time": 4.96, + "end_time": 5.1 + }, + { + "text": "consequences.", + "start_time": 5.1, + "end_time": 5.92 + }, + { + "text": " ", + "start_time": 5.92, + "end_time": 5.92 + }, + { + "text": "What", + "start_time": 6.54, + "end_time": 6.64 + }, + { + "text": " ", + "start_time": 6.64, + "end_time": 6.7 + }, + { + "text": "if", + "start_time": 6.7, + "end_time": 6.78 + }, + { + "text": " ", + "start_time": 6.78, + "end_time": 6.82 + }, + { + "text": "you", + "start_time": 6.82, + "end_time": 6.92 + }, + { + "text": " ", + "start_time": 6.92, + "end_time": 6.96 + }, + { + "text": "took", + "start_time": 6.96, + "end_time": 7.2 + }, + { + "text": " ", + "start_time": 7.2, + "end_time": 7.28 + }, + { + "text": "every", + "start_time": 7.28, + "end_time": 7.5 + }, + { + "text": " ", + "start_time": 7.5, + "end_time": 7.68 + }, + { + "text": "page", + "start_time": 7.68, + "end_time": 7.98 + }, + { + "text": " ", + "start_time": 7.98, + "end_time": 8.0 + }, + { + "text": "and", + "start_time": 8.0, + "end_time": 8.08 + }, + { + "text": " ", + "start_time": 8.08, + "end_time": 8.16 + }, + { + "text": "component", + "start_time": 8.16, + "end_time": 8.68 + }, + { + "text": " ", + "start_time": 8.68, + "end_time": 8.74 + }, + { + "text": "of", + "start_time": 8.74, + "end_time": 8.82 + }, + { + "text": " ", + "start_time": 8.82, + "end_time": 8.86 + }, + { + "text": "your", + "start_time": 8.86, + "end_time": 9.0 + }, + { + "text": " ", + "start_time": 9.0, + "end_time": 9.06 + }, + { + "text": "UI", + "start_time": 9.06, + "end_time": 9.48 + }, + { + "text": " ", + "start_time": 9.48, + "end_time": 9.76 + }, + { + "text": "and", + "start_time": 9.76, + "end_time": 9.88 + }, + { + "text": " ", + "start_time": 9.88, + "end_time": 9.96 + }, + { + "text": "laid", + "start_time": 9.96, + "end_time": 10.12 + }, + { + "text": " ", + "start_time": 10.12, + "end_time": 10.16 + }, + { + "text": "them", + "start_time": 10.16, + "end_time": 10.26 + }, + { + "text": " ", + "start_time": 10.26, + "end_time": 10.32 + }, + { + "text": "out", + "start_time": 10.32, + "end_time": 10.48 + }, + { + "text": " ", + "start_time": 10.48, + "end_time": 10.54 + }, + { + "text": "next", + "start_time": 10.54, + "end_time": 10.74 + }, + { + "text": " ", + "start_time": 10.74, + "end_time": 10.78 + }, + { + "text": "to", + "start_time": 10.78, + "end_time": 10.86 + }, + { + "text": " ", + "start_time": 10.86, + "end_time": 10.9 + }, + { + "text": "each", + "start_time": 10.9, + "end_time": 11.04 + }, + { + "text": " ", + "start_time": 11.04, + "end_time": 11.12 + }, + { + "text": "other?", + "start_time": 11.12, + "end_time": 11.32 + }, + { + "text": " ", + "start_time": 11.32, + "end_time": 11.32 + }, + { + "text": "Now,", + "start_time": 11.84, + "end_time": 12.06 + }, + { + "text": " ", + "start_time": 12.06, + "end_time": 12.36 + }, + { + "text": "let's", + "start_time": 12.36, + "end_time": 12.6 + }, + { + "text": " ", + "start_time": 12.6, + "end_time": 12.64 + }, + { + "text": "copy", + "start_time": 12.64, + "end_time": 12.94 + }, + { + "text": " ", + "start_time": 12.94, + "end_time": 12.98 + }, + { + "text": "those", + "start_time": 12.98, + "end_time": 13.16 + }, + { + "text": " ", + "start_time": 13.16, + "end_time": 13.2 + }, + { + "text": "components", + "start_time": 13.2, + "end_time": 13.72 + }, + { + "text": " ", + "start_time": 13.72, + "end_time": 13.78 + }, + { + "text": "into", + "start_time": 13.78, + "end_time": 14.04 + }, + { + "text": " ", + "start_time": 14.04, + "end_time": 14.12 + }, + { + "text": "every", + "start_time": 14.12, + "end_time": 14.36 + }, + { + "text": " ", + "start_time": 14.36, + "end_time": 14.52 + }, + { + "text": "state", + "start_time": 14.52, + "end_time": 14.76 + }, + { + "text": " ", + "start_time": 14.76, + "end_time": 14.78 + }, + { + "text": "the", + "start_time": 14.78, + "end_time": 14.9 + }, + { + "text": " ", + "start_time": 14.9, + "end_time": 14.98 + }, + { + "text": "user", + "start_time": 14.98, + "end_time": 15.24 + }, + { + "text": " ", + "start_time": 15.24, + "end_time": 15.26 + }, + { + "text": "will", + "start_time": 15.26, + "end_time": 15.42 + }, + { + "text": " ", + "start_time": 15.42, + "end_time": 15.5 + }, + { + "text": "see:", + "start_time": 15.5, + "end_time": 15.74 + }, + { + "text": " ", + "start_time": 15.74, + "end_time": 16.2 + }, + { + "text": "empty", + "start_time": 16.2, + "end_time": 16.42 + }, + { + "text": " ", + "start_time": 16.42, + "end_time": 16.54 + }, + { + "text": "states,", + "start_time": 16.54, + "end_time": 16.88 + }, + { + "text": " ", + "start_time": 16.88, + "end_time": 17.2 + }, + { + "text": "error", + "start_time": 17.2, + "end_time": 17.42 + }, + { + "text": " ", + "start_time": 17.42, + "end_time": 17.54 + }, + { + "text": "states,", + "start_time": 17.54, + "end_time": 17.86 + }, + { + "text": " ", + "start_time": 17.86, + "end_time": 18.22 + }, + { + "text": "simple", + "start_time": 18.22, + "end_time": 18.48 + }, + { + "text": " ", + "start_time": 18.48, + "end_time": 18.62 + }, + { + "text": "states,", + "start_time": 18.62, + "end_time": 18.92 + }, + { + "text": " ", + "start_time": 18.92, + "end_time": 19.18 + }, + { + "text": "and", + "start_time": 19.18, + "end_time": 19.26 + }, + { + "text": " ", + "start_time": 19.26, + "end_time": 19.36 + }, + { + "text": "complex", + "start_time": 19.36, + "end_time": 19.8 + }, + { + "text": " ", + "start_time": 19.8, + "end_time": 19.9 + }, + { + "text": "ones.", + "start_time": 19.9, + "end_time": 20.18 + }, + { + "text": " ", + "start_time": 20.18, + "end_time": 20.18 + }, + { + "text": "Storybook", + "start_time": 20.72, + "end_time": 21.22 + }, + { + "text": " ", + "start_time": 21.22, + "end_time": 21.28 + }, + { + "text": "JS", + "start_time": 21.28, + "end_time": 21.7 + }, + { + "text": " ", + "start_time": 21.7, + "end_time": 21.74 + }, + { + "text": "has", + "start_time": 21.74, + "end_time": 21.88 + }, + { + "text": " ", + "start_time": 21.88, + "end_time": 21.94 + }, + { + "text": "made", + "start_time": 21.94, + "end_time": 22.1 + }, + { + "text": " ", + "start_time": 22.1, + "end_time": 22.16 + }, + { + "text": "that", + "start_time": 22.16, + "end_time": 22.38 + }, + { + "text": " ", + "start_time": 22.38, + "end_time": 22.46 + }, + { + "text": "easy", + "start_time": 22.46, + "end_time": 22.8 + }, + { + "text": " ", + "start_time": 22.8, + "end_time": 22.82 + }, + { + "text": "for", + "start_time": 22.82, + "end_time": 22.92 + }, + { + "text": " ", + "start_time": 22.92, + "end_time": 22.96 + }, + { + "text": "the", + "start_time": 22.96, + "end_time": 23.06 + }, + { + "text": " ", + "start_time": 23.06, + "end_time": 23.1 + }, + { + "text": "past", + "start_time": 23.1, + "end_time": 23.42 + }, + { + "text": " ", + "start_time": 23.42, + "end_time": 23.46 + }, + { + "text": "10", + "start_time": 23.46, + "end_time": 23.68 + }, + { + "text": " ", + "start_time": 23.68, + "end_time": 23.78 + }, + { + "text": "years.", + "start_time": 23.78, + "end_time": 24.18 + }, + { + "text": " ", + "start_time": 24.18, + "end_time": 24.6 + }, + { + "text": "With", + "start_time": 24.6, + "end_time": 24.76 + }, + { + "text": " ", + "start_time": 24.76, + "end_time": 24.84 + }, + { + "text": "coding", + "start_time": 24.84, + "end_time": 25.18 + }, + { + "text": " ", + "start_time": 25.18, + "end_time": 25.24 + }, + { + "text": "agents,", + "start_time": 25.24, + "end_time": 25.54 + }, + { + "text": " ", + "start_time": 25.54, + "end_time": 25.68 + }, + { + "text": "it's", + "start_time": 25.68, + "end_time": 25.84 + }, + { + "text": " ", + "start_time": 25.84, + "end_time": 25.9 + }, + { + "text": "more", + "start_time": 25.9, + "end_time": 26.12 + }, + { + "text": " ", + "start_time": 26.12, + "end_time": 26.22 + }, + { + "text": "useful", + "start_time": 26.22, + "end_time": 26.52 + }, + { + "text": " ", + "start_time": 26.52, + "end_time": 26.56 + }, + { + "text": "than", + "start_time": 26.56, + "end_time": 26.76 + }, + { + "text": " ", + "start_time": 26.76, + "end_time": 26.84 + }, + { + "text": "ever.", + "start_time": 26.84, + "end_time": 27.1 + }, + { + "text": " ", + "start_time": 27.1, + "end_time": 27.44 + }, + { + "text": "You", + "start_time": 27.44, + "end_time": 27.52 + }, + { + "text": " ", + "start_time": 27.52, + "end_time": 27.54 + }, + { + "text": "can", + "start_time": 27.54, + "end_time": 27.74 + }, + { + "text": " ", + "start_time": 27.74, + "end_time": 27.8 + }, + { + "text": "point", + "start_time": 27.8, + "end_time": 28.04 + }, + { + "text": " ", + "start_time": 28.04, + "end_time": 28.08 + }, + { + "text": "at", + "start_time": 28.08, + "end_time": 28.16 + }, + { + "text": " ", + "start_time": 28.16, + "end_time": 28.2 + }, + { + "text": "the", + "start_time": 28.2, + "end_time": 28.28 + }, + { + "text": " ", + "start_time": 28.28, + "end_time": 28.38 + }, + { + "text": "specific", + "start_time": 28.38, + "end_time": 28.88 + }, + { + "text": " ", + "start_time": 28.88, + "end_time": 28.98 + }, + { + "text": "page", + "start_time": 28.98, + "end_time": 29.26 + }, + { + "text": " ", + "start_time": 29.26, + "end_time": 29.3 + }, + { + "text": "you", + "start_time": 29.3, + "end_time": 29.4 + }, + { + "text": " ", + "start_time": 29.4, + "end_time": 29.44 + }, + { + "text": "want", + "start_time": 29.44, + "end_time": 29.62 + }, + { + "text": " ", + "start_time": 29.62, + "end_time": 29.68 + }, + { + "text": "changed.", + "start_time": 29.68, + "end_time": 30.18 + }, + { + "text": " ", + "start_time": 30.18, + "end_time": 30.58 + }, + { + "text": "But", + "start_time": 30.58, + "end_time": 30.7 + }, + { + "text": " ", + "start_time": 30.7, + "end_time": 30.76 + }, + { + "text": "how", + "start_time": 30.76, + "end_time": 30.92 + }, + { + "text": " ", + "start_time": 30.92, + "end_time": 30.96 + }, + { + "text": "can", + "start_time": 30.96, + "end_time": 31.08 + }, + { + "text": " ", + "start_time": 31.08, + "end_time": 31.12 + }, + { + "text": "you", + "start_time": 31.12, + "end_time": 31.2 + }, + { + "text": " ", + "start_time": 31.2, + "end_time": 31.24 + }, + { + "text": "understand", + "start_time": 31.24, + "end_time": 31.82 + }, + { + "text": " ", + "start_time": 31.82, + "end_time": 31.86 + }, + { + "text": "the", + "start_time": 31.86, + "end_time": 32.04 + }, + { + "text": " ", + "start_time": 32.04, + "end_time": 32.1 + }, + { + "text": "full", + "start_time": 32.1, + "end_time": 32.28 + }, + { + "text": " ", + "start_time": 32.28, + "end_time": 32.46 + }, + { + "text": "ripples", + "start_time": 32.46, + "end_time": 32.88 + }, + { + "text": " ", + "start_time": 32.88, + "end_time": 32.94 + }, + { + "text": "of", + "start_time": 32.94, + "end_time": 33.0 + }, + { + "text": " ", + "start_time": 33.0, + "end_time": 33.04 + }, + { + "text": "a", + "start_time": 33.04, + "end_time": 33.1 + }, + { + "text": " ", + "start_time": 33.1, + "end_time": 33.14 + }, + { + "text": "code", + "start_time": 33.14, + "end_time": 33.4 + }, + { + "text": " ", + "start_time": 33.4, + "end_time": 33.46 + }, + { + "text": "change?", + "start_time": 33.46, + "end_time": 33.88 + }, + { + "text": " ", + "start_time": 33.88, + "end_time": 34.36 + }, + { + "text": "Enter", + "start_time": 34.36, + "end_time": 34.62 + }, + { + "text": " ", + "start_time": 34.62, + "end_time": 34.78 + }, + { + "text": "Chromatic.", + "start_time": 34.78, + "end_time": 35.44 + }, + { + "text": " ", + "start_time": 35.44, + "end_time": 35.88 + }, + { + "text": "This", + "start_time": 35.88, + "end_time": 36.0 + }, + { + "text": " ", + "start_time": 36.0, + "end_time": 36.04 + }, + { + "text": "is", + "start_time": 36.04, + "end_time": 36.14 + }, + { + "text": " ", + "start_time": 36.14, + "end_time": 36.2 + }, + { + "text": "a", + "start_time": 36.2, + "end_time": 36.26 + }, + { + "text": " ", + "start_time": 36.26, + "end_time": 36.32 + }, + { + "text": "cloud", + "start_time": 36.32, + "end_time": 36.62 + }, + { + "text": " ", + "start_time": 36.62, + "end_time": 36.7 + }, + { + "text": "service", + "start_time": 36.7, + "end_time": 37.04 + }, + { + "text": " ", + "start_time": 37.04, + "end_time": 37.12 + }, + { + "text": "which", + "start_time": 37.12, + "end_time": 37.28 + }, + { + "text": " ", + "start_time": 37.28, + "end_time": 37.34 + }, + { + "text": "renders", + "start_time": 37.34, + "end_time": 37.76 + }, + { + "text": " ", + "start_time": 37.76, + "end_time": 37.82 + }, + { + "text": "every", + "start_time": 37.82, + "end_time": 38.08 + }, + { + "text": " ", + "start_time": 38.08, + "end_time": 38.12 + }, + { + "text": "component", + "start_time": 38.12, + "end_time": 38.58 + }, + { + "text": " ", + "start_time": 38.58, + "end_time": 38.62 + }, + { + "text": "of", + "start_time": 38.62, + "end_time": 38.66 + }, + { + "text": " ", + "start_time": 38.66, + "end_time": 38.72 + }, + { + "text": "a", + "start_time": 38.72, + "end_time": 38.78 + }, + { + "text": " ", + "start_time": 38.78, + "end_time": 38.86 + }, + { + "text": "storybook", + "start_time": 38.86, + "end_time": 39.4 + }, + { + "text": " ", + "start_time": 39.4, + "end_time": 39.44 + }, + { + "text": "to", + "start_time": 39.44, + "end_time": 39.52 + }, + { + "text": " ", + "start_time": 39.52, + "end_time": 39.56 + }, + { + "text": "an", + "start_time": 39.56, + "end_time": 39.68 + }, + { + "text": " ", + "start_time": 39.68, + "end_time": 39.76 + }, + { + "text": "image.", + "start_time": 39.76, + "end_time": 40.08 + }, + { + "text": " ", + "start_time": 40.08, + "end_time": 40.52 + }, + { + "text": "Change", + "start_time": 40.52, + "end_time": 40.8 + }, + { + "text": " ", + "start_time": 40.8, + "end_time": 40.84 + }, + { + "text": "the", + "start_time": 40.84, + "end_time": 40.92 + }, + { + "text": " ", + "start_time": 40.92, + "end_time": 40.96 + }, + { + "text": "code,", + "start_time": 40.96, + "end_time": 41.28 + }, + { + "text": " ", + "start_time": 41.28, + "end_time": 41.62 + }, + { + "text": "render", + "start_time": 41.62, + "end_time": 41.96 + }, + { + "text": " ", + "start_time": 41.96, + "end_time": 42.02 + }, + { + "text": "every", + "start_time": 42.02, + "end_time": 42.24 + }, + { + "text": " ", + "start_time": 42.24, + "end_time": 42.28 + }, + { + "text": "component", + "start_time": 42.28, + "end_time": 42.74 + }, + { + "text": " ", + "start_time": 42.74, + "end_time": 42.78 + }, + { + "text": "to", + "start_time": 42.78, + "end_time": 42.86 + }, + { + "text": " ", + "start_time": 42.86, + "end_time": 42.9 + }, + { + "text": "another", + "start_time": 42.9, + "end_time": 43.26 + }, + { + "text": " ", + "start_time": 43.26, + "end_time": 43.32 + }, + { + "text": "image,", + "start_time": 43.32, + "end_time": 43.6 + }, + { + "text": " ", + "start_time": 43.6, + "end_time": 43.84 + }, + { + "text": "and", + "start_time": 43.84, + "end_time": 43.92 + }, + { + "text": " ", + "start_time": 43.92, + "end_time": 44.0 + }, + { + "text": "compare.", + "start_time": 44.0, + "end_time": 44.46 + }, + { + "text": " ", + "start_time": 44.46, + "end_time": 44.86 + }, + { + "text": "This", + "start_time": 44.86, + "end_time": 45.04 + }, + { + "text": " ", + "start_time": 45.04, + "end_time": 45.08 + }, + { + "text": "works", + "start_time": 45.08, + "end_time": 45.4 + }, + { + "text": " ", + "start_time": 45.4, + "end_time": 45.46 + }, + { + "text": "great,", + "start_time": 45.46, + "end_time": 45.88 + }, + { + "text": " ", + "start_time": 45.88, + "end_time": 46.36 + }, + { + "text": "but", + "start_time": 46.36, + "end_time": 46.7 + }, + { + "text": " ", + "start_time": 46.7, + "end_time": 46.76 + }, + { + "text": "it", + "start_time": 46.76, + "end_time": 46.86 + }, + { + "text": " ", + "start_time": 46.86, + "end_time": 46.9 + }, + { + "text": "requires", + "start_time": 46.9, + "end_time": 47.4 + }, + { + "text": " ", + "start_time": 47.4, + "end_time": 47.52 + }, + { + "text": "waiting", + "start_time": 47.52, + "end_time": 47.82 + }, + { + "text": " ", + "start_time": 47.82, + "end_time": 47.86 + }, + { + "text": "on", + "start_time": 47.86, + "end_time": 47.92 + }, + { + "text": " ", + "start_time": 47.92, + "end_time": 47.96 + }, + { + "text": "a", + "start_time": 47.96, + "end_time": 48.04 + }, + { + "text": " ", + "start_time": 48.04, + "end_time": 48.08 + }, + { + "text": "cloud", + "start_time": 48.08, + "end_time": 48.42 + }, + { + "text": " ", + "start_time": 48.42, + "end_time": 48.52 + }, + { + "text": "service.", + "start_time": 48.52, + "end_time": 48.92 + }, + { + "text": " ", + "start_time": 48.92, + "end_time": 49.38 + }, + { + "text": "At", + "start_time": 49.38, + "end_time": 49.5 + }, + { + "text": " ", + "start_time": 49.5, + "end_time": 49.78 + }, + { + "text": "0.8", + "start_time": 49.78, + "end_time": 50.28 + }, + { + "text": " ", + "start_time": 50.28, + "end_time": 50.32 + }, + { + "text": "cents", + "start_time": 50.34, + "end_time": 50.58 + }, + { + "text": " ", + "start_time": 50.58, + "end_time": 50.62 + }, + { + "text": "per", + "start_time": 50.62, + "end_time": 50.74 + }, + { + "text": " ", + "start_time": 50.74, + "end_time": 50.82 + }, + { + "text": "image,", + "start_time": 50.82, + "end_time": 51.16 + }, + { + "text": " ", + "start_time": 51.16, + "end_time": 51.36 + }, + { + "text": "this", + "start_time": 51.36, + "end_time": 51.5 + }, + { + "text": " ", + "start_time": 51.5, + "end_time": 51.56 + }, + { + "text": "was", + "start_time": 51.56, + "end_time": 51.66 + }, + { + "text": " ", + "start_time": 51.66, + "end_time": 51.74 + }, + { + "text": "a", + "start_time": 51.74, + "end_time": 51.8 + }, + { + "text": " ", + "start_time": 51.8, + "end_time": 51.86 + }, + { + "text": "reasonable", + "start_time": 51.86, + "end_time": 52.3 + }, + { + "text": " ", + "start_time": 52.3, + "end_time": 52.34 + }, + { + "text": "expense", + "start_time": 52.34, + "end_time": 52.84 + }, + { + "text": " ", + "start_time": 52.84, + "end_time": 52.88 + }, + { + "text": "at", + "start_time": 52.88, + "end_time": 52.96 + }, + { + "text": " ", + "start_time": 52.96, + "end_time": 53.0 + }, + { + "text": "the", + "start_time": 53.0, + "end_time": 53.08 + }, + { + "text": " ", + "start_time": 53.08, + "end_time": 53.16 + }, + { + "text": "speed", + "start_time": 53.16, + "end_time": 53.36 + }, + { + "text": " ", + "start_time": 53.36, + "end_time": 53.4 + }, + { + "text": "of", + "start_time": 53.4, + "end_time": 53.5 + }, + { + "text": " ", + "start_time": 53.5, + "end_time": 53.56 + }, + { + "text": "human", + "start_time": 53.56, + "end_time": 53.84 + }, + { + "text": " ", + "start_time": 53.84, + "end_time": 53.96 + }, + { + "text": "code.", + "start_time": 53.96, + "end_time": 54.28 + }, + { + "text": " ", + "start_time": 54.28, + "end_time": 54.66 + }, + { + "text": "But", + "start_time": 54.66, + "end_time": 54.78 + }, + { + "text": " ", + "start_time": 54.78, + "end_time": 54.84 + }, + { + "text": "for", + "start_time": 54.84, + "end_time": 54.92 + }, + { + "text": " ", + "start_time": 54.92, + "end_time": 54.98 + }, + { + "text": "a", + "start_time": 54.98, + "end_time": 55.04 + }, + { + "text": " ", + "start_time": 55.04, + "end_time": 55.1 + }, + { + "text": "modest", + "start_time": 55.1, + "end_time": 55.44 + }, + { + "text": " ", + "start_time": 55.44, + "end_time": 55.5 + }, + { + "text": "library", + "start_time": 55.5, + "end_time": 55.96 + }, + { + "text": " ", + "start_time": 55.96, + "end_time": 56.02 + }, + { + "text": "of", + "start_time": 56.02, + "end_time": 56.12 + }, + { + "text": " ", + "start_time": 56.12, + "end_time": 56.32 + }, + { + "text": "1,000", + "start_time": 56.32, + "end_time": 56.86 + }, + { + "text": " ", + "start_time": 56.86, + "end_time": 56.96 + }, + { + "text": "stories,", + "start_time": 56.96, + "end_time": 57.38 + }, + { + "text": " ", + "start_time": 57.38, + "end_time": 57.68 + }, + { + "text": "that's", + "start_time": 57.68, + "end_time": 57.96 + }, + { + "text": " ", + "start_time": 57.96, + "end_time": 58.02 + }, + { + "text": "eight", + "start_time": 58.02, + "end_time": 58.3 + }, + { + "text": " ", + "start_time": 58.3, + "end_time": 58.34 + }, + { + "text": "bucks", + "start_time": 58.34, + "end_time": 58.64 + }, + { + "text": " ", + "start_time": 58.64, + "end_time": 58.7 + }, + { + "text": "for", + "start_time": 58.7, + "end_time": 58.78 + }, + { + "text": " ", + "start_time": 58.78, + "end_time": 58.84 + }, + { + "text": "a", + "start_time": 58.84, + "end_time": 58.94 + }, + { + "text": " ", + "start_time": 58.94, + "end_time": 58.98 + }, + { + "text": "full", + "start_time": 59.0, + "end_time": 59.16 + }, + { + "text": " ", + "start_time": 59.16, + "end_time": 59.28 + }, + { + "text": "run", + "start_time": 59.28, + "end_time": 59.54 + }, + { + "text": " ", + "start_time": 59.54, + "end_time": 59.88 + }, + { + "text": "each", + "start_time": 59.88, + "end_time": 60.1 + }, + { + "text": " ", + "start_time": 60.1, + "end_time": 60.14 + }, + { + "text": "time", + "start_time": 60.14, + "end_time": 60.34 + }, + { + "text": " ", + "start_time": 60.34, + "end_time": 60.36 + }, + { + "text": "you", + "start_time": 60.36, + "end_time": 60.44 + }, + { + "text": " ", + "start_time": 60.44, + "end_time": 60.5 + }, + { + "text": "even", + "start_time": 60.5, + "end_time": 60.68 + }, + { + "text": " ", + "start_time": 60.68, + "end_time": 60.86 + }, + { + "text": "look", + "start_time": 60.86, + "end_time": 61.04 + }, + { + "text": " ", + "start_time": 61.04, + "end_time": 61.06 + }, + { + "text": "at", + "start_time": 61.06, + "end_time": 61.14 + }, + { + "text": " ", + "start_time": 61.14, + "end_time": 61.18 + }, + { + "text": "a", + "start_time": 61.18, + "end_time": 61.24 + }, + { + "text": " ", + "start_time": 61.24, + "end_time": 61.3 + }, + { + "text": "code", + "start_time": 61.3, + "end_time": 61.56 + }, + { + "text": " ", + "start_time": 61.56, + "end_time": 61.62 + }, + { + "text": "change.", + "start_time": 61.62, + "end_time": 62.04 + }, + { + "text": " ", + "start_time": 62.04, + "end_time": 62.52 + }, + { + "text": "Introducing", + "start_time": 62.52, + "end_time": 63.18 + }, + { + "text": " ", + "start_time": 63.18, + "end_time": 63.4 + }, + { + "text": "selfie.dev,", + "start_time": 63.4, + "end_time": 64.56 + }, + { + "text": " ", + "start_time": 64.56, + "end_time": 64.92 + }, + { + "text": "a", + "start_time": 64.92, + "end_time": 65.0 + }, + { + "text": " ", + "start_time": 65.0, + "end_time": 65.08 + }, + { + "text": "snapshot", + "start_time": 65.08, + "end_time": 65.62 + }, + { + "text": " ", + "start_time": 65.62, + "end_time": 65.66 + }, + { + "text": "testing", + "start_time": 65.66, + "end_time": 66.06 + }, + { + "text": " ", + "start_time": 66.06, + "end_time": 66.1 + }, + { + "text": "tool", + "start_time": 66.1, + "end_time": 66.34 + }, + { + "text": " ", + "start_time": 66.34, + "end_time": 66.42 + }, + { + "text": "which", + "start_time": 66.42, + "end_time": 66.58 + }, + { + "text": " ", + "start_time": 66.58, + "end_time": 66.64 + }, + { + "text": "runs", + "start_time": 66.64, + "end_time": 66.9 + }, + { + "text": " ", + "start_time": 66.9, + "end_time": 66.96 + }, + { + "text": "locally", + "start_time": 66.98, + "end_time": 67.5 + }, + { + "text": " ", + "start_time": 67.5, + "end_time": 67.6 + }, + { + "text": "and", + "start_time": 67.6, + "end_time": 67.8 + }, + { + "text": " ", + "start_time": 67.8, + "end_time": 67.84 + }, + { + "text": "in", + "start_time": 67.84, + "end_time": 67.9 + }, + { + "text": " ", + "start_time": 67.9, + "end_time": 67.94 + }, + { + "text": "the", + "start_time": 67.94, + "end_time": 68.02 + }, + { + "text": " ", + "start_time": 68.02, + "end_time": 68.06 + }, + { + "text": "cloud.", + "start_time": 68.06, + "end_time": 68.5 + }, + { + "text": " ", + "start_time": 68.5, + "end_time": 68.84 + }, + { + "text": "Your", + "start_time": 68.84, + "end_time": 69.0 + }, + { + "text": " ", + "start_time": 69.0, + "end_time": 69.08 + }, + { + "text": "agent", + "start_time": 69.08, + "end_time": 69.42 + }, + { + "text": " ", + "start_time": 69.42, + "end_time": 69.48 + }, + { + "text": "takes", + "start_time": 69.48, + "end_time": 69.66 + }, + { + "text": " ", + "start_time": 69.66, + "end_time": 69.7 + }, + { + "text": "its", + "start_time": 69.7, + "end_time": 69.82 + }, + { + "text": " ", + "start_time": 69.82, + "end_time": 69.86 + }, + { + "text": "own", + "start_time": 69.86, + "end_time": 70.04 + }, + { + "text": " ", + "start_time": 70.04, + "end_time": 70.08 + }, + { + "text": "picture.", + "start_time": 70.08, + "end_time": 70.44 + }, + { + "text": " ", + "start_time": 70.44, + "end_time": 70.86 + }, + { + "text": "No", + "start_time": 70.86, + "end_time": 71.06 + }, + { + "text": " ", + "start_time": 71.06, + "end_time": 71.08 + }, + { + "text": "waiting", + "start_time": 71.08, + "end_time": 71.34 + }, + { + "text": " ", + "start_time": 71.34, + "end_time": 71.38 + }, + { + "text": "on", + "start_time": 71.38, + "end_time": 71.46 + }, + { + "text": " ", + "start_time": 71.46, + "end_time": 71.5 + }, + { + "text": "a", + "start_time": 71.5, + "end_time": 71.56 + }, + { + "text": " ", + "start_time": 71.56, + "end_time": 71.6 + }, + { + "text": "cloud", + "start_time": 71.6, + "end_time": 71.86 + }, + { + "text": " ", + "start_time": 71.86, + "end_time": 71.92 + }, + { + "text": "rendering", + "start_time": 71.92, + "end_time": 72.26 + }, + { + "text": " ", + "start_time": 72.26, + "end_time": 72.36 + }, + { + "text": "service", + "start_time": 72.36, + "end_time": 72.7 + }, + { + "text": " ", + "start_time": 72.7, + "end_time": 72.94 + }, + { + "text": "and", + "start_time": 72.94, + "end_time": 73.06 + }, + { + "text": " ", + "start_time": 73.06, + "end_time": 73.18 + }, + { + "text": "no", + "start_time": 73.18, + "end_time": 73.32 + }, + { + "text": " ", + "start_time": 73.32, + "end_time": 73.46 + }, + { + "text": "paying", + "start_time": 73.46, + "end_time": 73.8 + }, + { + "text": " ", + "start_time": 73.8, + "end_time": 73.86 + }, + { + "text": "for", + "start_time": 73.86, + "end_time": 73.96 + }, + { + "text": " ", + "start_time": 73.96, + "end_time": 74.02 + }, + { + "text": "each", + "start_time": 74.02, + "end_time": 74.16 + }, + { + "text": " ", + "start_time": 74.16, + "end_time": 74.24 + }, + { + "text": "image.", + "start_time": 74.24, + "end_time": 74.56 + }, + { + "text": " ", + "start_time": 74.56, + "end_time": 75.0 + }, + { + "text": "On", + "start_time": 75.0, + "end_time": 75.08 + }, + { + "text": " ", + "start_time": 75.08, + "end_time": 75.12 + }, + { + "text": "an", + "start_time": 75.12, + "end_time": 75.24 + }, + { + "text": " ", + "start_time": 75.24, + "end_time": 75.34 + }, + { + "text": "M4", + "start_time": 75.34, + "end_time": 75.76 + }, + { + "text": " ", + "start_time": 75.76, + "end_time": 75.8 + }, + { + "text": "MacBook", + "start_time": 75.8, + "end_time": 76.18 + }, + { + "text": " ", + "start_time": 76.18, + "end_time": 76.26 + }, + { + "text": "Air,", + "start_time": 76.26, + "end_time": 76.46 + }, + { + "text": " ", + "start_time": 76.46, + "end_time": 76.76 + }, + { + "text": "Selfie's", + "start_time": 76.76, + "end_time": 77.24 + }, + { + "text": " ", + "start_time": 77.24, + "end_time": 77.3 + }, + { + "text": "own", + "start_time": 77.3, + "end_time": 77.5 + }, + { + "text": " ", + "start_time": 77.5, + "end_time": 77.56 + }, + { + "text": "test", + "start_time": 77.56, + "end_time": 77.8 + }, + { + "text": " ", + "start_time": 77.8, + "end_time": 77.9 + }, + { + "text": "suite", + "start_time": 77.9, + "end_time": 78.1 + }, + { + "text": " ", + "start_time": 78.1, + "end_time": 78.14 + }, + { + "text": "of", + "start_time": 78.14, + "end_time": 78.3 + }, + { + "text": " ", + "start_time": 78.3, + "end_time": 78.32 + }, + { + "text": "541", + "start_time": 78.54, + "end_time": 79.36 + }, + { + "text": " ", + "start_time": 79.36, + "end_time": 79.44 + }, + { + "text": "stories", + "start_time": 79.44, + "end_time": 79.84 + }, + { + "text": " ", + "start_time": 79.84, + "end_time": 79.96 + }, + { + "text": "runs", + "start_time": 79.96, + "end_time": 80.2 + }, + { + "text": " ", + "start_time": 80.2, + "end_time": 80.24 + }, + { + "text": "in", + "start_time": 80.24, + "end_time": 80.3 + }, + { + "text": " ", + "start_time": 80.3, + "end_time": 80.36 + }, + { + "text": "about", + "start_time": 80.36, + "end_time": 80.66 + }, + { + "text": " ", + "start_time": 80.66, + "end_time": 80.7 + }, + { + "text": "70", + "start_time": 80.9, + "end_time": 81.04 + }, + { + "text": " ", + "start_time": 81.04, + "end_time": 81.16 + }, + { + "text": "seconds.", + "start_time": 81.18, + "end_time": 81.62 + }, + { + "text": " ", + "start_time": 81.62, + "end_time": 82.02 + }, + { + "text": "It", + "start_time": 82.02, + "end_time": 82.12 + }, + { + "text": " ", + "start_time": 82.12, + "end_time": 82.14 + }, + { + "text": "works", + "start_time": 82.14, + "end_time": 82.42 + }, + { + "text": " ", + "start_time": 82.42, + "end_time": 82.48 + }, + { + "text": "just", + "start_time": 82.48, + "end_time": 82.62 + }, + { + "text": " ", + "start_time": 82.62, + "end_time": 82.66 + }, + { + "text": "like", + "start_time": 82.66, + "end_time": 82.84 + }, + { + "text": " ", + "start_time": 82.84, + "end_time": 82.88 + }, + { + "text": "Git.", + "start_time": 82.88, + "end_time": 83.12 + }, + { + "text": " ", + "start_time": 83.12, + "end_time": 83.56 + }, + { + "text": "Images", + "start_time": 83.58, + "end_time": 83.92 + }, + { + "text": " ", + "start_time": 83.92, + "end_time": 83.96 + }, + { + "text": "are", + "start_time": 83.96, + "end_time": 84.08 + }, + { + "text": " ", + "start_time": 84.08, + "end_time": 84.16 + }, + { + "text": "stored", + "start_time": 84.16, + "end_time": 84.4 + }, + { + "text": " ", + "start_time": 84.4, + "end_time": 84.52 + }, + { + "text": "locally", + "start_time": 84.52, + "end_time": 84.98 + }, + { + "text": " ", + "start_time": 84.98, + "end_time": 85.0 + }, + { + "text": "wherever", + "start_time": 85.0, + "end_time": 85.36 + }, + { + "text": " ", + "start_time": 85.36, + "end_time": 85.4 + }, + { + "text": "the", + "start_time": 85.4, + "end_time": 85.48 + }, + { + "text": " ", + "start_time": 85.48, + "end_time": 85.56 + }, + { + "text": "agent", + "start_time": 85.56, + "end_time": 85.8 + }, + { + "text": " ", + "start_time": 85.8, + "end_time": 85.84 + }, + { + "text": "is", + "start_time": 85.84, + "end_time": 85.98 + }, + { + "text": " ", + "start_time": 85.98, + "end_time": 86.04 + }, + { + "text": "running,", + "start_time": 86.04, + "end_time": 86.32 + }, + { + "text": " ", + "start_time": 86.32, + "end_time": 86.56 + }, + { + "text": "and", + "start_time": 86.56, + "end_time": 86.66 + }, + { + "text": " ", + "start_time": 86.66, + "end_time": 86.74 + }, + { + "text": "also", + "start_time": 86.74, + "end_time": 87.04 + }, + { + "text": " ", + "start_time": 87.04, + "end_time": 87.08 + }, + { + "text": "in", + "start_time": 87.08, + "end_time": 87.14 + }, + { + "text": " ", + "start_time": 87.14, + "end_time": 87.16 + }, + { + "text": "the", + "start_time": 87.16, + "end_time": 87.24 + }, + { + "text": " ", + "start_time": 87.24, + "end_time": 87.28 + }, + { + "text": "cloud,", + "start_time": 87.28, + "end_time": 87.7 + }, + { + "text": " ", + "start_time": 87.7, + "end_time": 87.96 + }, + { + "text": "so", + "start_time": 87.96, + "end_time": 88.06 + }, + { + "text": " ", + "start_time": 88.06, + "end_time": 88.1 + }, + { + "text": "that", + "start_time": 88.1, + "end_time": 88.28 + }, + { + "text": " ", + "start_time": 88.28, + "end_time": 88.34 + }, + { + "text": "every", + "start_time": 88.34, + "end_time": 88.6 + }, + { + "text": " ", + "start_time": 88.6, + "end_time": 88.64 + }, + { + "text": "commit", + "start_time": 88.64, + "end_time": 88.9 + }, + { + "text": " ", + "start_time": 88.9, + "end_time": 88.94 + }, + { + "text": "on", + "start_time": 88.94, + "end_time": 89.12 + }, + { + "text": " ", + "start_time": 89.12, + "end_time": 89.14 + }, + { + "text": "GitHub", + "start_time": 89.14, + "end_time": 89.54 + }, + { + "text": " ", + "start_time": 89.54, + "end_time": 89.64 + }, + { + "text": "shows", + "start_time": 89.64, + "end_time": 89.88 + }, + { + "text": " ", + "start_time": 89.88, + "end_time": 89.92 + }, + { + "text": "the", + "start_time": 89.94, + "end_time": 90.02 + }, + { + "text": " ", + "start_time": 90.02, + "end_time": 90.06 + }, + { + "text": "complete", + "start_time": 90.06, + "end_time": 90.54 + }, + { + "text": " ", + "start_time": 90.54, + "end_time": 90.66 + }, + { + "text": "visual", + "start_time": 90.66, + "end_time": 91.0 + }, + { + "text": " ", + "start_time": 91.0, + "end_time": 91.1 + }, + { + "text": "consequences", + "start_time": 91.1, + "end_time": 91.8 + }, + { + "text": " ", + "start_time": 91.8, + "end_time": 91.84 + }, + { + "text": "of", + "start_time": 91.84, + "end_time": 91.92 + }, + { + "text": " ", + "start_time": 91.92, + "end_time": 91.94 + }, + { + "text": "the", + "start_time": 91.94, + "end_time": 92.02 + }, + { + "text": " ", + "start_time": 92.02, + "end_time": 92.06 + }, + { + "text": "code", + "start_time": 92.06, + "end_time": 92.32 + }, + { + "text": " ", + "start_time": 92.32, + "end_time": 92.38 + }, + { + "text": "change.", + "start_time": 92.38, + "end_time": 92.78 + }, + { + "text": " ", + "start_time": 92.78, + "end_time": 93.16 + }, + { + "text": "Approvals", + "start_time": 93.16, + "end_time": 93.76 + }, + { + "text": " ", + "start_time": 93.76, + "end_time": 93.8 + }, + { + "text": "follow", + "start_time": 93.8, + "end_time": 94.06 + }, + { + "text": " ", + "start_time": 94.06, + "end_time": 94.1 + }, + { + "text": "your", + "start_time": 94.1, + "end_time": 94.2 + }, + { + "text": " ", + "start_time": 94.2, + "end_time": 94.24 + }, + { + "text": "history", + "start_time": 94.24, + "end_time": 94.6 + }, + { + "text": " ", + "start_time": 94.6, + "end_time": 94.72 + }, + { + "text": "too,", + "start_time": 94.72, + "end_time": 94.98 + }, + { + "text": " ", + "start_time": 94.98, + "end_time": 95.28 + }, + { + "text": "so", + "start_time": 95.28, + "end_time": 95.38 + }, + { + "text": " ", + "start_time": 95.38, + "end_time": 95.42 + }, + { + "text": "they", + "start_time": 95.42, + "end_time": 95.54 + }, + { + "text": " ", + "start_time": 95.54, + "end_time": 95.58 + }, + { + "text": "survive", + "start_time": 95.58, + "end_time": 95.9 + }, + { + "text": " ", + "start_time": 95.9, + "end_time": 95.94 + }, + { + "text": "a", + "start_time": 95.94, + "end_time": 96.0 + }, + { + "text": " ", + "start_time": 96.0, + "end_time": 96.04 + }, + { + "text": "commit,", + "start_time": 96.04, + "end_time": 96.38 + }, + { + "text": " ", + "start_time": 96.38, + "end_time": 96.68 + }, + { + "text": "a", + "start_time": 96.68, + "end_time": 96.7 + }, + { + "text": " ", + "start_time": 96.7, + "end_time": 96.78 + }, + { + "text": "rebase,", + "start_time": 96.78, + "end_time": 97.26 + }, + { + "text": " ", + "start_time": 97.26, + "end_time": 97.44 + }, + { + "text": "or", + "start_time": 97.44, + "end_time": 97.54 + }, + { + "text": " ", + "start_time": 97.54, + "end_time": 97.58 + }, + { + "text": "a", + "start_time": 97.58, + "end_time": 97.64 + }, + { + "text": " ", + "start_time": 97.64, + "end_time": 97.68 + }, + { + "text": "merge.", + "start_time": 97.68, + "end_time": 98.08 + }, + { + "text": " ", + "start_time": 98.08, + "end_time": 98.54 + }, + { + "text": "Every", + "start_time": 98.54, + "end_time": 98.78 + }, + { + "text": " ", + "start_time": 98.78, + "end_time": 98.86 + }, + { + "text": "image", + "start_time": 98.86, + "end_time": 99.1 + }, + { + "text": " ", + "start_time": 99.1, + "end_time": 99.14 + }, + { + "text": "is", + "start_time": 99.14, + "end_time": 99.28 + }, + { + "text": " ", + "start_time": 99.28, + "end_time": 99.34 + }, + { + "text": "rendered", + "start_time": 99.34, + "end_time": 99.64 + }, + { + "text": " ", + "start_time": 99.64, + "end_time": 99.68 + }, + { + "text": "in", + "start_time": 99.68, + "end_time": 99.74 + }, + { + "text": " ", + "start_time": 99.74, + "end_time": 99.78 + }, + { + "text": "the", + "start_time": 99.78, + "end_time": 99.92 + }, + { + "text": " ", + "start_time": 99.92, + "end_time": 100.0 + }, + { + "text": "same", + "start_time": 100.0, + "end_time": 100.26 + }, + { + "text": " ", + "start_time": 100.26, + "end_time": 100.42 + }, + { + "text": "pinned", + "start_time": 100.42, + "end_time": 100.68 + }, + { + "text": " ", + "start_time": 100.68, + "end_time": 100.72 + }, + { + "text": "environment,", + "start_time": 100.72, + "end_time": 101.36 + }, + { + "text": " ", + "start_time": 101.36, + "end_time": 101.54 + }, + { + "text": "wherever", + "start_time": 101.54, + "end_time": 101.92 + }, + { + "text": " ", + "start_time": 101.92, + "end_time": 101.96 + }, + { + "text": "it", + "start_time": 101.96, + "end_time": 102.08 + }, + { + "text": " ", + "start_time": 102.08, + "end_time": 102.12 + }, + { + "text": "runs.", + "start_time": 102.12, + "end_time": 102.5 + }, + { + "text": " ", + "start_time": 102.5, + "end_time": 102.92 + }, + { + "text": "Each", + "start_time": 102.92, + "end_time": 103.08 + }, + { + "text": " ", + "start_time": 103.08, + "end_time": 103.16 + }, + { + "text": "image", + "start_time": 103.16, + "end_time": 103.38 + }, + { + "text": " ", + "start_time": 103.38, + "end_time": 103.44 + }, + { + "text": "is", + "start_time": 103.44, + "end_time": 103.58 + }, + { + "text": " ", + "start_time": 103.58, + "end_time": 103.64 + }, + { + "text": "named", + "start_time": 103.64, + "end_time": 103.98 + }, + { + "text": " ", + "start_time": 103.98, + "end_time": 104.02 + }, + { + "text": "by", + "start_time": 104.02, + "end_time": 104.16 + }, + { + "text": " ", + "start_time": 104.16, + "end_time": 104.22 + }, + { + "text": "a", + "start_time": 104.22, + "end_time": 104.32 + }, + { + "text": " ", + "start_time": 104.32, + "end_time": 104.36 + }, + { + "text": "hash", + "start_time": 104.36, + "end_time": 104.64 + }, + { + "text": " ", + "start_time": 104.64, + "end_time": 104.68 + }, + { + "text": "of", + "start_time": 104.68, + "end_time": 104.74 + }, + { + "text": " ", + "start_time": 104.74, + "end_time": 104.76 + }, + { + "text": "its", + "start_time": 104.76, + "end_time": 104.92 + }, + { + "text": " ", + "start_time": 104.92, + "end_time": 104.96 + }, + { + "text": "pixels.", + "start_time": 104.96, + "end_time": 105.42 + }, + { + "text": " ", + "start_time": 105.42, + "end_time": 105.82 + }, + { + "text": "Most", + "start_time": 105.82, + "end_time": 106.04 + }, + { + "text": " ", + "start_time": 106.04, + "end_time": 106.1 + }, + { + "text": "changes", + "start_time": 106.1, + "end_time": 106.52 + }, + { + "text": " ", + "start_time": 106.52, + "end_time": 106.56 + }, + { + "text": "touch", + "start_time": 106.56, + "end_time": 106.74 + }, + { + "text": " ", + "start_time": 106.74, + "end_time": 106.8 + }, + { + "text": "only", + "start_time": 106.8, + "end_time": 106.98 + }, + { + "text": " ", + "start_time": 106.98, + "end_time": 107.04 + }, + { + "text": "a", + "start_time": 107.04, + "end_time": 107.1 + }, + { + "text": " ", + "start_time": 107.1, + "end_time": 107.16 + }, + { + "text": "few,", + "start_time": 107.16, + "end_time": 107.42 + }, + { + "text": " ", + "start_time": 107.42, + "end_time": 107.74 + }, + { + "text": "so", + "start_time": 107.74, + "end_time": 107.88 + }, + { + "text": " ", + "start_time": 107.88, + "end_time": 107.96 + }, + { + "text": "only", + "start_time": 107.96, + "end_time": 108.22 + }, + { + "text": " ", + "start_time": 108.22, + "end_time": 108.26 + }, + { + "text": "those", + "start_time": 108.26, + "end_time": 108.56 + }, + { + "text": " ", + "start_time": 108.56, + "end_time": 108.6 + }, + { + "text": "few", + "start_time": 108.6, + "end_time": 108.82 + }, + { + "text": " ", + "start_time": 108.82, + "end_time": 108.92 + }, + { + "text": "get", + "start_time": 108.92, + "end_time": 109.1 + }, + { + "text": " ", + "start_time": 109.1, + "end_time": 109.18 + }, + { + "text": "sent.", + "start_time": 109.18, + "end_time": 109.46 + }, + { + "text": " ", + "start_time": 109.46, + "end_time": 109.9 + }, + { + "text": "If", + "start_time": 109.9, + "end_time": 109.98 + }, + { + "text": " ", + "start_time": 109.98, + "end_time": 110.02 + }, + { + "text": "you're", + "start_time": 110.02, + "end_time": 110.18 + }, + { + "text": " ", + "start_time": 110.18, + "end_time": 110.24 + }, + { + "text": "using", + "start_time": 110.24, + "end_time": 110.56 + }, + { + "text": " ", + "start_time": 110.56, + "end_time": 110.62 + }, + { + "text": "Storybook", + "start_time": 110.62, + "end_time": 111.04 + }, + { + "text": " ", + "start_time": 111.04, + "end_time": 111.14 + }, + { + "text": "JS,", + "start_time": 111.14, + "end_time": 111.66 + }, + { + "text": " ", + "start_time": 111.66, + "end_time": 111.94 + }, + { + "text": "you", + "start_time": 111.94, + "end_time": 112.02 + }, + { + "text": " ", + "start_time": 112.02, + "end_time": 112.06 + }, + { + "text": "can", + "start_time": 112.06, + "end_time": 112.2 + }, + { + "text": " ", + "start_time": 112.2, + "end_time": 112.26 + }, + { + "text": "start", + "start_time": 112.26, + "end_time": 112.48 + }, + { + "text": " ", + "start_time": 112.48, + "end_time": 112.54 + }, + { + "text": "using", + "start_time": 112.54, + "end_time": 112.82 + }, + { + "text": " ", + "start_time": 112.82, + "end_time": 112.86 + }, + { + "text": "Selfie", + "start_time": 112.88, + "end_time": 113.18 + }, + { + "text": " ", + "start_time": 113.18, + "end_time": 113.32 + }, + { + "text": "locally", + "start_time": 113.32, + "end_time": 113.76 + }, + { + "text": " ", + "start_time": 113.76, + "end_time": 113.8 + }, + { + "text": "to", + "start_time": 113.8, + "end_time": 113.88 + }, + { + "text": " ", + "start_time": 113.88, + "end_time": 113.92 + }, + { + "text": "review", + "start_time": 113.92, + "end_time": 114.26 + }, + { + "text": " ", + "start_time": 114.26, + "end_time": 114.3 + }, + { + "text": "your", + "start_time": 114.3, + "end_time": 114.44 + }, + { + "text": " ", + "start_time": 114.44, + "end_time": 114.48 + }, + { + "text": "changes", + "start_time": 114.48, + "end_time": 114.9 + }, + { + "text": " ", + "start_time": 114.9, + "end_time": 115.06 + }, + { + "text": "right", + "start_time": 115.06, + "end_time": 115.28 + }, + { + "text": " ", + "start_time": 115.28, + "end_time": 115.34 + }, + { + "text": "now", + "start_time": 115.34, + "end_time": 115.82 + }, + { + "text": " ", + "start_time": 115.82, + "end_time": 115.88 + }, + { + "text": "at", + "start_time": 115.88, + "end_time": 116.02 + }, + { + "text": " ", + "start_time": 116.02, + "end_time": 116.12 + }, + { + "text": "selfie.dev.", + "start_time": 116.12, + "end_time": 117.22 + }, + { + "text": " ", + "start_time": 117.22, + "end_time": 117.62 + }, + { + "text": "If", + "start_time": 117.62, + "end_time": 117.7 + }, + { + "text": " ", + "start_time": 117.7, + "end_time": 117.74 + }, + { + "text": "you", + "start_time": 117.74, + "end_time": 117.82 + }, + { + "text": " ", + "start_time": 117.82, + "end_time": 117.84 + }, + { + "text": "want", + "start_time": 117.84, + "end_time": 117.94 + }, + { + "text": " ", + "start_time": 117.94, + "end_time": 117.98 + }, + { + "text": "to", + "start_time": 117.98, + "end_time": 118.06 + }, + { + "text": " ", + "start_time": 118.06, + "end_time": 118.12 + }, + { + "text": "use", + "start_time": 118.12, + "end_time": 118.24 + }, + { + "text": " ", + "start_time": 118.24, + "end_time": 118.28 + }, + { + "text": "it", + "start_time": 118.28, + "end_time": 118.34 + }, + { + "text": " ", + "start_time": 118.34, + "end_time": 118.38 + }, + { + "text": "in", + "start_time": 118.38, + "end_time": 118.44 + }, + { + "text": " ", + "start_time": 118.44, + "end_time": 118.48 + }, + { + "text": "the", + "start_time": 118.48, + "end_time": 118.58 + }, + { + "text": " ", + "start_time": 118.58, + "end_time": 118.62 + }, + { + "text": "cloud", + "start_time": 118.62, + "end_time": 118.98 + }, + { + "text": " ", + "start_time": 118.98, + "end_time": 119.02 + }, + { + "text": "on", + "start_time": 119.02, + "end_time": 119.12 + }, + { + "text": " ", + "start_time": 119.12, + "end_time": 119.14 + }, + { + "text": "your", + "start_time": 119.14, + "end_time": 119.28 + }, + { + "text": " ", + "start_time": 119.28, + "end_time": 119.3 + }, + { + "text": "GitHub", + "start_time": 119.3, + "end_time": 119.6 + }, + { + "text": " ", + "start_time": 119.6, + "end_time": 119.64 + }, + { + "text": "repositories,", + "start_time": 119.64, + "end_time": 120.42 + }, + { + "text": " ", + "start_time": 120.42, + "end_time": 120.74 + }, + { + "text": "join", + "start_time": 120.74, + "end_time": 120.94 + }, + { + "text": " ", + "start_time": 120.94, + "end_time": 120.98 + }, + { + "text": "our", + "start_time": 120.98, + "end_time": 121.12 + }, + { + "text": " ", + "start_time": 121.12, + "end_time": 121.16 + }, + { + "text": "waitlist", + "start_time": 121.16, + "end_time": 121.66 + }, + { + "text": " ", + "start_time": 121.66, + "end_time": 121.86 + }, + { + "text": "and", + "start_time": 121.86, + "end_time": 121.98 + }, + { + "text": " ", + "start_time": 121.98, + "end_time": 122.02 + }, + { + "text": "get", + "start_time": 122.02, + "end_time": 122.12 + }, + { + "text": " ", + "start_time": 122.12, + "end_time": 122.18 + }, + { + "text": "a", + "start_time": 122.18, + "end_time": 122.26 + }, + { + "text": " ", + "start_time": 122.26, + "end_time": 122.3 + }, + { + "text": "discount", + "start_time": 122.3, + "end_time": 122.76 + }, + { + "text": " ", + "start_time": 122.76, + "end_time": 122.8 + }, + { + "text": "when", + "start_time": 122.8, + "end_time": 122.9 + }, + { + "text": " ", + "start_time": 122.9, + "end_time": 122.94 + }, + { + "text": "we", + "start_time": 122.94, + "end_time": 123.04 + }, + { + "text": " ", + "start_time": 123.04, + "end_time": 123.08 + }, + { + "text": "launch", + "start_time": 123.1, + "end_time": 123.34 + }, + { + "text": " ", + "start_time": 123.34, + "end_time": 123.4 + }, + { + "text": "in", + "start_time": 123.4, + "end_time": 123.5 + }, + { + "text": " ", + "start_time": 123.5, + "end_time": 123.56 + }, + { + "text": "mid-November.", + "start_time": 123.56, + "end_time": 124.26 + } + ] + } + ] +} \ No newline at end of file diff --git a/demo-video/assets/narration.mp3 b/demo-video/assets/narration.mp3 new file mode 100644 index 0000000..d49ef17 Binary files /dev/null and b/demo-video/assets/narration.mp3 differ diff --git a/demo-video/assets/style.css b/demo-video/assets/style.css new file mode 100644 index 0000000..3f64c11 --- /dev/null +++ b/demo-video/assets/style.css @@ -0,0 +1,43 @@ +@font-face { font-family: "Bagel Fat One"; src: url("fonts/BagelFatOne.woff2") format("woff2"); } +@font-face { font-family: "Jost"; src: url("fonts/Jost.woff2") format("woff2"); font-weight: 100 900; } + +:root { + --sky: #bfd9f5; --fog: #dde1ea; --sun: #ffd95e; + --ink: #23232b; --paper: #ffffff; --pink: #f4689f; --coral: #e35c61; + --teal: #78acae; --orange: #f58a3c; --grey: #d7dbe3; +} +* { box-sizing: border-box; margin: 0; padding: 0; } +html, body { width: 1920px; height: 1080px; overflow: hidden; background: var(--sky); font-family: "Jost", sans-serif; color: var(--ink); } + +.stage { position: absolute; inset: 0; overflow: hidden; } +.abs { position: absolute; left: 0; top: 0; } +.display { font-family: "Bagel Fat One", sans-serif; font-weight: 400; line-height: 1; } +.label { font: 600 34px/1.1 "Jost", sans-serif; } + +/* Everything is flat paper with a hard ink outline and a hard shadow, like the logo. */ +.card { + position: absolute; left: 0; top: 0; width: 245px; height: 190px; overflow: hidden; + background: var(--paper); border: 5px solid var(--ink); border-radius: 16px; + box-shadow: 6px 6px 0 var(--ink); padding: 10px 13px; +} +.card .t { font: 700 19px/1.1 "Jost", sans-serif; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 6px; } +.card svg.body { display: block; width: 100%; height: 132px; } +.card .crack { position: absolute; inset: 0; width: 100%; height: 100%; opacity: 0; } +.card .flag { position: absolute; inset: 0; opacity: 0; background: rgba(227, 92, 97, 0.28); } + +.window { position: absolute; background: var(--paper); border: 6px solid var(--ink); border-radius: 22px; box-shadow: 10px 10px 0 var(--ink); } +.window .bar { height: 58px; border-bottom: 6px solid var(--ink); display: flex; align-items: center; gap: 12px; padding: 0 22px; } +.window .dot { width: 20px; height: 20px; border-radius: 50%; border: 4px solid var(--ink); } +.window .url { margin-left: 14px; font: 600 28px "Jost", sans-serif; } + +.photo { position: absolute; left: 0; top: 0; background: var(--paper); border: 5px solid var(--ink); box-shadow: 6px 6px 0 var(--ink); } +.prop { position: absolute; left: 0; top: 0; overflow: visible; } +.flash { position: absolute; inset: 0; background: #fff; opacity: 0; } +.ring { position: absolute; left: 0; top: 0; border-radius: 50%; border: 10px solid var(--coral); opacity: 0; } +.tag { position: absolute; left: 0; top: 0; padding: 8px 20px 10px; background: var(--ink); color: #fff; border-radius: 10px; font: 600 32px/1.1 "Jost", sans-serif; white-space: nowrap; } +.coin { position: absolute; left: 0; top: 0; width: 64px; height: 64px; border-radius: 50%; background: var(--sun); border: 5px solid var(--ink); } +.wordmark { + font-family: "Bagel Fat One", sans-serif; line-height: 1; + background: linear-gradient(90deg, #63b9e3, #78acae 50%, #e35c61); -webkit-background-clip: text; background-clip: text; color: transparent; + filter: drop-shadow(7px 7px 0 #4d4d4d); +} diff --git a/demo-video/build.mjs b/demo-video/build.mjs new file mode 100644 index 0000000..5e63ab9 --- /dev/null +++ b/demo-video/build.mjs @@ -0,0 +1,211 @@ +// Generates index.html, compositions/captions.html and the subtitle files from SCRIPT.md, scenes.json and the voiceover. +// SCRIPT.md owns the words, the voiceover's timing file owns when they happen, compositions/.html owns the pictures. +import { readFileSync, writeFileSync } from "node:fs"; + +const AUDIO = "assets/narration.mp3"; +const LEAD_IN = 0.08; // A scene's picture arrives just before its first word. + +const { scenes, draftScript = false } = JSON.parse(readFileSync("scenes.json", "utf8")); +const lines = readFileSync("SCRIPT.md", "utf8").split("\n").map((l) => l.trim()); +const narration = lines.filter((l) => l && !l.startsWith("#") && !l.startsWith("**")); +if (narration.length !== scenes.length) { + throw new Error(`SCRIPT.md has ${narration.length} paragraphs, scenes.json has ${scenes.length}`); +} + +// The timing file spells some words its own way, so compare them in a plain form. +const plain = (w) => w.toLowerCase().replace(/[^a-z0-9]/g, ""); +const SPOKEN_AS = { storybookjs: ["storybook", "js"], 8: ["eight"] }; +const spoken = JSON.parse(readFileSync("assets/narration-timing.json", "utf8")) + .segments.flatMap((s) => s.words) + .filter((w) => w.text.trim()) + .map((w) => ({ word: plain(w.text), start: w.start_time, end: w.end_time })); + +// Each word of the script takes its time from what the voice said, and a difference is an error. +// With "draftScript" in scenes.json, a paragraph that differs keeps its slot in the take instead, so later scenes don't drift. +// The words it shares with the take keep their times, and the rest share what's left of the slot by length. +const expect = (text) => SPOKEN_AS[plain(text)] ?? [plain(text)]; +const saidAt = (at, tokens) => tokens.every((t, i) => spoken[at + i]?.word === t); +function endOfParagraph(from, later) { + for (const paragraph of later) { + const opening = paragraph.split(/\s+/).slice(0, 4).flatMap(expect); + for (let at = from; at < spoken.length; at++) if (saidAt(at, opening)) return at; + } + return spoken.length; +} +let cursor = 0; +const edited = []; +const wordsByScene = narration.map((paragraph, i) => { + const words = paragraph.split(/\s+/); + const tokens = words.flatMap(expect); + if (saidAt(cursor, tokens) && (i + 1 < narration.length || cursor + tokens.length === spoken.length)) { + return words.map((text) => { + const n = expect(text).length, first = spoken[cursor], last = spoken[cursor + n - 1]; + cursor += n; + return { text, start: first.start, end: last.end }; + }); + } + if (!draftScript) { + throw new Error(`Paragraph ${i + 1} of SCRIPT.md doesn't match the voiceover at ${spoken[cursor]?.start} s: "${paragraph}". Regenerate the audio, or set "draftScript" in scenes.json for now.`); + } + edited.push(i + 1); + const end = endOfParagraph(cursor, narration.slice(i + 1)); + const timedWords = Array(words.length); + let head = 0, at = cursor; + while (head < words.length && saidAt(at, expect(words[head]))) { + const n = expect(words[head]).length; + timedWords[head++] = { text: words[head - 1], start: spoken[at].start, end: spoken[at + n - 1].end }; + at += n; + } + let tail = words.length, back = end; + while (tail > head && back - expect(words[tail - 1]).length >= at && saidAt(back - expect(words[tail - 1]).length, expect(words[tail - 1]))) { + const n = expect(words[tail - 1]).length; + back -= n; + timedWords[--tail] = { text: words[tail], start: spoken[back].start, end: spoken[back + n - 1].end }; + } + const from = at < spoken.length ? spoken[at].start : spoken.at(-1).end; + const to = back > at ? spoken[back - 1].end : from; + const chars = words.slice(head, tail).reduce((n, w) => n + w.length + 1, 0); + let clock = from; + for (let k = head; k < tail; k++) { + const length = ((to - from) * (words[k].length + 1)) / chars; + timedWords[k] = { text: words[k], start: clock, end: clock + length }; + clock += length; + } + cursor = end; + return timedWords; +}); +if (cursor !== spoken.length) throw new Error(`The voiceover goes on after SCRIPT.md ends, at "${spoken[cursor].word}".`); +if (edited.length) console.warn(`DRAFT: paragraphs ${edited.join(", ")} don't match the voiceover, so their audio is out of date. Regenerate it before publishing.`); + +const timed = scenes.map((s, i) => { + const start = i === 0 ? 0 : wordsByScene[i][0].start - LEAD_IN; + const end = i + 1 < scenes.length ? wordsByScene[i + 1][0].start - LEAD_IN : wordsByScene[i].at(-1).end + (s.hold ?? 0.35); + return { id: s.id, start: +start.toFixed(3), dur: +(end - start).toFixed(3), words: wordsByScene[i], text: narration[i] }; +}); +const total = +(timed.at(-1).start + timed.at(-1).dur).toFixed(3); + +const MAX_CHARS = 58; +const MIN_CLAUSE = 24; +function groupsOf(text) { + // A short clause joins the next one when they fit together, so "Now," never stands alone. + const clauses = []; + for (const sentence of text.split(/(?<=[.?!])\s+/)) { + const parts = sentence.split(/(?<=[,:])\s+/); + let line = ""; + for (const part of parts) { + if (line && (line.length < MIN_CLAUSE || (part.length < MIN_CLAUSE && !line.endsWith(":")))) line += " " + part; + else { + if (line) clauses.push(line); + line = part; + } + } + clauses.push(line); + } + // A clause that is still too long is split into even lines at word boundaries. + return clauses.flatMap((clause) => { + if (clause.length <= MAX_CHARS) return [clause]; + const n = Math.ceil(clause.length / MAX_CHARS); + const target = clause.length / n; + const lines = []; + let line = ""; + for (const word of clause.split(" ")) { + if (line && lines.length < n - 1 && line.length + word.length / 2 >= target) { + lines.push(line); + line = word; + } else line = line ? `${line} ${word}` : word; + } + lines.push(line); + return lines; + }); +} + +// A subtitle opens on its first word and stays until the next one opens, unless the voice pauses for long. +const MAX_LINGER = 0.5; +const flat = timed.flatMap((s) => groupsOf(s.text).reduce((out, text) => { + const count = text.split(" ").length, from = out.used; + out.groups.push({ text, first: s.words[from], last: s.words[from + count - 1] }); + out.used += count; + return out; +}, { groups: [], used: 0 }).groups); +const captions = flat.map((g, i) => { + const next = flat[i + 1]?.first.start ?? Infinity; + const until = Math.min(next, g.last.end + MAX_LINGER); + return { text: g.text, start: +g.first.start.toFixed(3), duration: +(until - g.first.start).toFixed(3) }; +}); + +const esc = (t) => t.replace(/&/g, "&").replace(/ `
`, + ) + .join("\n"); +const durations = Object.fromEntries(timed.map((s) => [s.id, s.dur])); +// What each scene's words are, at what time within the scene, so a beat can land on the word that names it. +const spokenInScene = Object.fromEntries(timed.map((s) => [s.id, s.words.map((w) => [plain(w.text), +(w.start - s.start).toFixed(3), +(w.end - s.start).toFixed(3)])])); + +writeFileSync( + "index.html", + ` + + + + + + + + + + + +
+${hosts} +
+ +
+ + + +`, +); + +writeFileSync( + "compositions/captions.html", + ` +`, +); + +const stamp = (t, sep) => { + const ms = Math.round(t * 1000); + const p = (n, w = 2) => String(n).padStart(w, "0"); + return `${p(Math.floor(ms / 3600000))}:${p(Math.floor(ms / 60000) % 60)}:${p(Math.floor(ms / 1000) % 60)}${sep}${p(ms % 1000, 3)}`; +}; +const cues = (sep) => captions.map((c) => `${stamp(c.start, sep)} --> ${stamp(c.start + c.duration, sep)}\n${c.text}\n`); +writeFileSync("subtitles.srt", cues(",").map((c, i) => `${i + 1}\n${c}`).join("\n")); +writeFileSync("subtitles.vtt", `WEBVTT\n\n${cues(".").join("\n")}`); + +for (const s of timed) console.log(`${s.id.padEnd(10)} ${s.start.toFixed(2).padStart(7)} ${s.dur.toFixed(2).padStart(6)} s`); +const words = narration.join(" ").split(/\s+/).length; +console.log(`${scenes.length} scenes, ${captions.length} captions, ${words} words, ${total.toFixed(1)} s (${((words / total) * 60).toFixed(0)} wpm)`); diff --git a/demo-video/compositions/approvals.html b/demo-video/compositions/approvals.html new file mode 100644 index 0000000..381ef04 --- /dev/null +++ b/demo-video/compositions/approvals.html @@ -0,0 +1,54 @@ + diff --git a/demo-video/compositions/bucks.html b/demo-video/compositions/bucks.html new file mode 100644 index 0000000..1dea999 --- /dev/null +++ b/demo-video/compositions/bucks.html @@ -0,0 +1,52 @@ + diff --git a/demo-video/compositions/captions.html b/demo-video/compositions/captions.html new file mode 100644 index 0000000..96a640b --- /dev/null +++ b/demo-video/compositions/captions.html @@ -0,0 +1,67 @@ + diff --git a/demo-video/compositions/chromatic.html b/demo-video/compositions/chromatic.html new file mode 100644 index 0000000..5bdc5a3 --- /dev/null +++ b/demo-video/compositions/chromatic.html @@ -0,0 +1,45 @@ + diff --git a/demo-video/compositions/cold.html b/demo-video/compositions/cold.html new file mode 100644 index 0000000..363913c --- /dev/null +++ b/demo-video/compositions/cold.html @@ -0,0 +1,56 @@ + diff --git a/demo-video/compositions/compare.html b/demo-video/compositions/compare.html new file mode 100644 index 0000000..50f8807 --- /dev/null +++ b/demo-video/compositions/compare.html @@ -0,0 +1,39 @@ + diff --git a/demo-video/compositions/cost.html b/demo-video/compositions/cost.html new file mode 100644 index 0000000..95a5b15 --- /dev/null +++ b/demo-video/compositions/cost.html @@ -0,0 +1,38 @@ + diff --git a/demo-video/compositions/git.html b/demo-video/compositions/git.html new file mode 100644 index 0000000..87eac58 --- /dev/null +++ b/demo-video/compositions/git.html @@ -0,0 +1,70 @@ + diff --git a/demo-video/compositions/goofy.html b/demo-video/compositions/goofy.html new file mode 100644 index 0000000..123f049 --- /dev/null +++ b/demo-video/compositions/goofy.html @@ -0,0 +1,63 @@ + diff --git a/demo-video/compositions/hash.html b/demo-video/compositions/hash.html new file mode 100644 index 0000000..be0f9a8 --- /dev/null +++ b/demo-video/compositions/hash.html @@ -0,0 +1,67 @@ + diff --git a/demo-video/compositions/launch.html b/demo-video/compositions/launch.html new file mode 100644 index 0000000..05c6ad8 --- /dev/null +++ b/demo-video/compositions/launch.html @@ -0,0 +1,79 @@ + diff --git a/demo-video/compositions/pinned.html b/demo-video/compositions/pinned.html new file mode 100644 index 0000000..a006d9f --- /dev/null +++ b/demo-video/compositions/pinned.html @@ -0,0 +1,36 @@ + diff --git a/demo-video/compositions/reveal.html b/demo-video/compositions/reveal.html new file mode 100644 index 0000000..c41cefb --- /dev/null +++ b/demo-video/compositions/reveal.html @@ -0,0 +1,60 @@ + diff --git a/demo-video/compositions/ripple.html b/demo-video/compositions/ripple.html new file mode 100644 index 0000000..605e1c3 --- /dev/null +++ b/demo-video/compositions/ripple.html @@ -0,0 +1,56 @@ + diff --git a/demo-video/compositions/split.html b/demo-video/compositions/split.html new file mode 100644 index 0000000..4cf818e --- /dev/null +++ b/demo-video/compositions/split.html @@ -0,0 +1,44 @@ + diff --git a/demo-video/compositions/start.html b/demo-video/compositions/start.html new file mode 100644 index 0000000..5520662 --- /dev/null +++ b/demo-video/compositions/start.html @@ -0,0 +1,51 @@ + diff --git a/demo-video/compositions/states.html b/demo-video/compositions/states.html new file mode 100644 index 0000000..df84558 --- /dev/null +++ b/demo-video/compositions/states.html @@ -0,0 +1,64 @@ + diff --git a/demo-video/compositions/storybook.html b/demo-video/compositions/storybook.html new file mode 100644 index 0000000..449915c --- /dev/null +++ b/demo-video/compositions/storybook.html @@ -0,0 +1,56 @@ + diff --git a/demo-video/hyperframes.json b/demo-video/hyperframes.json new file mode 100644 index 0000000..dc9ec2e --- /dev/null +++ b/demo-video/hyperframes.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://hyperframes.heygen.com/schema/hyperframes.json", + "registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry", + "paths": { + "blocks": "compositions", + "components": "compositions/components", + "assets": "assets" + }, + "media": { + "autoProxy": true + } +} diff --git a/demo-video/index.html b/demo-video/index.html new file mode 100644 index 0000000..3805eac --- /dev/null +++ b/demo-video/index.html @@ -0,0 +1,38 @@ + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + diff --git a/demo-video/meta.json b/demo-video/meta.json new file mode 100644 index 0000000..442a2e9 --- /dev/null +++ b/demo-video/meta.json @@ -0,0 +1,5 @@ +{ + "id": "selfie-demo", + "name": "selfie-demo", + "createdAt": "2026-11-04T21:26:45.270Z" +} diff --git a/demo-video/package-lock.json b/demo-video/package-lock.json new file mode 100644 index 0000000..70600e6 --- /dev/null +++ b/demo-video/package-lock.json @@ -0,0 +1,2049 @@ +{ + "name": "selfie-demo-video", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "selfie-demo-video", + "devDependencies": { + "hyperframes": "0.9.9" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.6.tgz", + "integrity": "sha512-p/KH9MnfNcsm3sR6x4lNgm5j+QN4Et8srKredl9DZfXiyZ1UQ3aQ2YVpFLbqktYN5JI+DB+VZFKM2PqE5hLKjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", + "integrity": "sha512-MJP29gVoHznmqmwhH1tlZqzfeQ3aylTpSSa6WKZXtkwYUH02i9imBmWGTyZrbnAou5paX78SE4jMAUqnEPf8ug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "modern-tar": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.1.tgz", + "integrity": "sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-bidi": { + "version": "157.0.8090-0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-157.0.8090-0.tgz", + "integrity": "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^4.6.5" + }, + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1704951", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1704951.tgz", + "integrity": "sha512-GQhQOs9u/Mvm1zMtJIH872JS4AskuZdUSsPNPk4ChahtWAUqgz6atEtIsMt+nO7MqF1Ic4HxII/k8Cu36JivNg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "dev": true, + "license": "MIT", + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/hono": { + "version": "4.13.16", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.16.tgz", + "integrity": "sha512-AOLOEhvrV+iH92xXCnFr/5+3KjPKcI3xP63XJtX5spAnkznPdHijlIsUTpi7/9EhWqrTCekw5c5zh1K+YsVw7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hyperframes": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/hyperframes/-/hyperframes-0.9.9.tgz", + "integrity": "sha512-JpNQ0dY9X5FPbAkftSopz/P3LMCc+UOPUnNDl0LcFW32U/bOmjlQiOPQoOSEyHCiYi8N5g47yGyoJNjEjRoACw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@hono/node-server": "^2.0.5", + "@puppeteer/browsers": "^3.2.2", + "acorn": "^8.17.0", + "adm-zip": "^0.6.0", + "citty": "^0.2.1", + "compare-versions": "^6.1.1", + "css-tree": "^3.2.1", + "debug": "^4.4.0", + "esbuild": "^0.28.2", + "fontkit": "^2.0.4", + "giget": "^3.2.0", + "hono": "^4.0.0", + "ignore": "^5.3.2", + "open": "^10.0.0", + "postcss": "^8.5.8", + "puppeteer-core": "^25.10.0", + "undici": "7.25.0", + "yauzl": "^3.4.0" + }, + "bin": { + "hyperframes": "bin/hyperframes.mjs", + "hyperframes-localize-fonts": "bin/hyperframes-localize-fonts.mjs" + }, + "engines": { + "node": ">=22" + }, + "optionalDependencies": { + "sharp": "^0.35.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/modern-tar": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.6.tgz", + "integrity": "sha512-LYSks0tNVqbtez/9OB0YFP6qLRSqkdAxJWOQpVaR67lNTbj2zNLyYpowkRMia5lXLCZKzxwoxkxH2MbbBR2aEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/puppeteer-core": { + "version": "25.16.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.16.0.tgz", + "integrity": "sha512-VA5Pf54N8shfopNkZZXCdiiwfnnY8ZJYAMqFQRhlH5mGUhfwf53Ar770GPtdpTDLUqvQeYpIBtC9/G2Jij2IJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.2.6", + "chromium-bidi": "157.0.8090-0", + "devtools-protocol": "0.0.1704951", + "typed-query-selector": "^2.12.3", + "webdriver-bidi-protocol": "1.0.1", + "ws": "^8.22.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.6.tgz", + "integrity": "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typed-query-selector": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", + "integrity": "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/webdriver-bidi-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-1.0.1.tgz", + "integrity": "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ws": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/demo-video/package.json b/demo-video/package.json new file mode 100644 index 0000000..c3da2e0 --- /dev/null +++ b/demo-video/package.json @@ -0,0 +1,8 @@ +{ + "name": "selfie-demo-video", + "private": true, + "type": "module", + "devDependencies": { + "hyperframes": "0.9.9" + } +} diff --git a/demo-video/scenes.json b/demo-video/scenes.json new file mode 100644 index 0000000..1368142 --- /dev/null +++ b/demo-video/scenes.json @@ -0,0 +1,22 @@ +{ + "comment": "One scene per narration paragraph of SCRIPT.md, in order. Timing comes from the voiceover. hold is how long a scene stays up after its last word.", + "scenes": [ + { "id": "cold" }, + { "id": "split" }, + { "id": "states" }, + { "id": "storybook" }, + { "id": "ripple" }, + { "id": "chromatic" }, + { "id": "compare" }, + { "id": "cost" }, + { "id": "bucks" }, + { "id": "reveal" }, + { "id": "goofy" }, + { "id": "git" }, + { "id": "approvals" }, + { "id": "pinned" }, + { "id": "hash" }, + { "id": "start" }, + { "id": "launch", "hold": 3.5 } + ] +} diff --git a/demo-video/subtitles.srt b/demo-video/subtitles.srt new file mode 100644 index 0000000..4880e85 --- /dev/null +++ b/demo-video/subtitles.srt @@ -0,0 +1,199 @@ +1 +00:00:00,120 --> 00:00:02,600 +You point your coding agent at one button. + +2 +00:00:02,720 --> 00:00:06,420 +But most changes have unforeseen consequences. + +3 +00:00:06,540 --> 00:00:09,760 +What if you took every page and component of your UI, + +4 +00:00:09,760 --> 00:00:11,820 +and laid them out next to each other? + +5 +00:00:11,840 --> 00:00:13,780 +Now, let’s copy those components + +6 +00:00:13,780 --> 00:00:16,200 +into every state the user will see: + +7 +00:00:16,200 --> 00:00:18,220 +empty states, error states, + +8 +00:00:18,220 --> 00:00:20,680 +simple states, and complex ones. + +9 +00:00:20,720 --> 00:00:24,600 +StorybookJS has made that easy for the past 10 years. + +10 +00:00:24,600 --> 00:00:27,440 +With coding agents, it’s more useful than ever: + +11 +00:00:27,440 --> 00:00:30,580 +you can point at the specific page you want changed. + +12 +00:00:30,580 --> 00:00:32,100 +But how can you understand the + +13 +00:00:32,100 --> 00:00:34,360 +full ripples of a code change? + +14 +00:00:34,360 --> 00:00:35,880 +Enter Chromatic. + +15 +00:00:35,880 --> 00:00:38,120 +This is a cloud service which renders every + +16 +00:00:38,120 --> 00:00:40,520 +component of a Storybook to an image. + +17 +00:00:40,520 --> 00:00:42,780 +Change the code, render every component + +18 +00:00:42,780 --> 00:00:44,860 +to another image, and compare. + +19 +00:00:44,860 --> 00:00:47,520 +This works great, but it requires + +20 +00:00:47,520 --> 00:00:49,380 +waiting on a cloud service. + +21 +00:00:49,380 --> 00:00:52,340 +At 0.8 cents per image, this was a reasonable + +22 +00:00:52,340 --> 00:00:54,660 +expense at the speed of human code. + +23 +00:00:54,660 --> 00:00:57,680 +But for a modest library of 1,000 stories, + +24 +00:00:57,680 --> 00:00:59,880 +that’s 8 bucks for a full run, + +25 +00:00:59,880 --> 00:01:02,520 +each time you even look at a code change! + +26 +00:01:02,520 --> 00:01:06,100 +Introducing Selfie.dev: a snapshot testing + +27 +00:01:06,100 --> 00:01:08,840 +tool which runs locally and in the cloud. + +28 +00:01:08,840 --> 00:01:10,860 +Your agent takes its own picture. + +29 +00:01:10,860 --> 00:01:12,940 +No waiting on a cloud rendering service, + +30 +00:01:12,940 --> 00:01:15,000 +and no paying for each image. + +31 +00:01:15,000 --> 00:01:18,140 +On an M4 MacBook Air, Selfie’s own test suite + +32 +00:01:18,140 --> 00:01:22,020 +of 541 stories runs in about 70 seconds. + +33 +00:01:22,020 --> 00:01:23,580 +It works just like git. + +34 +00:01:23,580 --> 00:01:25,560 +Images are stored locally wherever the + +35 +00:01:25,560 --> 00:01:27,960 +agent is running, and also in the cloud, + +36 +00:01:27,960 --> 00:01:30,660 +so that every commit on GitHub shows the complete + +37 +00:01:30,660 --> 00:01:33,160 +visual consequences of the code change. + +38 +00:01:33,160 --> 00:01:35,280 +Approvals follow your history too, + +39 +00:01:35,280 --> 00:01:38,540 +so they survive a commit, a rebase, or a merge. + +40 +00:01:38,540 --> 00:01:40,420 +Every image is rendered in the same + +41 +00:01:40,420 --> 00:01:42,920 +pinned environment, wherever it runs. + +42 +00:01:42,920 --> 00:01:45,820 +Each image is named by a hash of its pixels. + +43 +00:01:45,820 --> 00:01:47,740 +Most changes touch only a few, + +44 +00:01:47,740 --> 00:01:49,900 +so only those few get sent. + +45 +00:01:49,900 --> 00:01:51,940 +If you’re using StorybookJS, + +46 +00:01:51,940 --> 00:01:54,300 +you can start using Selfie locally to review + +47 +00:01:54,300 --> 00:01:57,620 +your changes right now, at selfie.dev. + +48 +00:01:57,620 --> 00:01:59,300 +If you want to use it in the cloud on your + +49 +00:01:59,300 --> 00:02:01,860 +GitHub repositories, join our waitlist, + +50 +00:02:01,860 --> 00:02:04,760 +and get a discount when we launch in mid-November. diff --git a/demo-video/subtitles.vtt b/demo-video/subtitles.vtt new file mode 100644 index 0000000..a838364 --- /dev/null +++ b/demo-video/subtitles.vtt @@ -0,0 +1,151 @@ +WEBVTT + +00:00:00.120 --> 00:00:02.600 +You point your coding agent at one button. + +00:00:02.720 --> 00:00:06.420 +But most changes have unforeseen consequences. + +00:00:06.540 --> 00:00:09.760 +What if you took every page and component of your UI, + +00:00:09.760 --> 00:00:11.820 +and laid them out next to each other? + +00:00:11.840 --> 00:00:13.780 +Now, let’s copy those components + +00:00:13.780 --> 00:00:16.200 +into every state the user will see: + +00:00:16.200 --> 00:00:18.220 +empty states, error states, + +00:00:18.220 --> 00:00:20.680 +simple states, and complex ones. + +00:00:20.720 --> 00:00:24.600 +StorybookJS has made that easy for the past 10 years. + +00:00:24.600 --> 00:00:27.440 +With coding agents, it’s more useful than ever: + +00:00:27.440 --> 00:00:30.580 +you can point at the specific page you want changed. + +00:00:30.580 --> 00:00:32.100 +But how can you understand the + +00:00:32.100 --> 00:00:34.360 +full ripples of a code change? + +00:00:34.360 --> 00:00:35.880 +Enter Chromatic. + +00:00:35.880 --> 00:00:38.120 +This is a cloud service which renders every + +00:00:38.120 --> 00:00:40.520 +component of a Storybook to an image. + +00:00:40.520 --> 00:00:42.780 +Change the code, render every component + +00:00:42.780 --> 00:00:44.860 +to another image, and compare. + +00:00:44.860 --> 00:00:47.520 +This works great, but it requires + +00:00:47.520 --> 00:00:49.380 +waiting on a cloud service. + +00:00:49.380 --> 00:00:52.340 +At 0.8 cents per image, this was a reasonable + +00:00:52.340 --> 00:00:54.660 +expense at the speed of human code. + +00:00:54.660 --> 00:00:57.680 +But for a modest library of 1,000 stories, + +00:00:57.680 --> 00:00:59.880 +that’s 8 bucks for a full run, + +00:00:59.880 --> 00:01:02.520 +each time you even look at a code change! + +00:01:02.520 --> 00:01:06.100 +Introducing Selfie.dev: a snapshot testing + +00:01:06.100 --> 00:01:08.840 +tool which runs locally and in the cloud. + +00:01:08.840 --> 00:01:10.860 +Your agent takes its own picture. + +00:01:10.860 --> 00:01:12.940 +No waiting on a cloud rendering service, + +00:01:12.940 --> 00:01:15.000 +and no paying for each image. + +00:01:15.000 --> 00:01:18.140 +On an M4 MacBook Air, Selfie’s own test suite + +00:01:18.140 --> 00:01:22.020 +of 541 stories runs in about 70 seconds. + +00:01:22.020 --> 00:01:23.580 +It works just like git. + +00:01:23.580 --> 00:01:25.560 +Images are stored locally wherever the + +00:01:25.560 --> 00:01:27.960 +agent is running, and also in the cloud, + +00:01:27.960 --> 00:01:30.660 +so that every commit on GitHub shows the complete + +00:01:30.660 --> 00:01:33.160 +visual consequences of the code change. + +00:01:33.160 --> 00:01:35.280 +Approvals follow your history too, + +00:01:35.280 --> 00:01:38.540 +so they survive a commit, a rebase, or a merge. + +00:01:38.540 --> 00:01:40.420 +Every image is rendered in the same + +00:01:40.420 --> 00:01:42.920 +pinned environment, wherever it runs. + +00:01:42.920 --> 00:01:45.820 +Each image is named by a hash of its pixels. + +00:01:45.820 --> 00:01:47.740 +Most changes touch only a few, + +00:01:47.740 --> 00:01:49.900 +so only those few get sent. + +00:01:49.900 --> 00:01:51.940 +If you’re using StorybookJS, + +00:01:51.940 --> 00:01:54.300 +you can start using Selfie locally to review + +00:01:54.300 --> 00:01:57.620 +your changes right now, at selfie.dev. + +00:01:57.620 --> 00:01:59.300 +If you want to use it in the cloud on your + +00:01:59.300 --> 00:02:01.860 +GitHub repositories, join our waitlist, + +00:02:01.860 --> 00:02:04.760 +and get a discount when we launch in mid-November. diff --git a/product-demo/.gitignore b/product-demo/.gitignore new file mode 100644 index 0000000..33609ed --- /dev/null +++ b/product-demo/.gitignore @@ -0,0 +1,5 @@ +renders/ +snapshots/ +.hyperframes/ +.thumbnails/ +.waveform-cache/ diff --git a/product-demo/README.md b/product-demo/README.md new file mode 100644 index 0000000..bf352be --- /dev/null +++ b/product-demo/README.md @@ -0,0 +1,25 @@ +# Product demo + +A [HyperFrames](https://hyperframes.heygen.com) film of the real product, reviewing its own Storybook. + +| File | Owns | +|---|---| +| `SCRIPT.md` | The words. One paragraph is one scene, and the bold line under it is what's on screen. | +| `scenes.json` | The scenes, in the same order, and how long the last one holds after its last word. | +| `assets/narration.mp3`, `assets/narration-timing.json` | The voiceover, and when each word of it happens, from ElevenLabs. | +| `compositions/.html` | Each scene. Beats are written as `w("word")`, so they follow the voice. | +| `assets/ui/` | Screenshots of the real app, 2x, each one a state it was really in. `points.json` is where the pointer was. | +| `assets/lib.js`, `assets/style.css` | The app window, its pointer and camera, and the terminal. | +| `index.html`, `compositions/captions.html`, `subtitles.srt`, `subtitles.vtt` | Generated. Don't edit them. | + +```sh +npm install +node build.mjs # after changing SCRIPT.md, scenes.json, or the voiceover +npx hyperframes preview # edit and scrub in the browser +npx hyperframes check # lint, layout, and motion +npx hyperframes render -o renders/selfie-product-demo.mp4 +``` + +## Changing the voiceover + +Generate the whole script as one take, replace the two `assets/narration*` files with it, and run `node build.mjs`. Scene lengths, subtitles, and beats all follow the words. The build stops with an error if the script and the audio disagree on a word; a word the voice spells differently goes in `SPOKEN_AS` in `build.mjs`. diff --git a/product-demo/REVIEW.md b/product-demo/REVIEW.md new file mode 100644 index 0000000..c56fad4 --- /dev/null +++ b/product-demo/REVIEW.md @@ -0,0 +1,10 @@ +# Review notes + +For whoever changes this film next, including an agent. + +- **Real:** every screen is a screenshot of the real app in a state it really reached, running on an M4 MacBook Air against this repository's Storybook, in a throwaway clone: `selfie review`, the 124 changed, the 48 spacing-only, 85,143 pixels down to 13, the note, the reply, and the final review. Every terminal line is from those runs. GitHub and `hackathon.selfie.dev` are the live public pages, read only, for the real `main` commit, which isn't the commit in the story. +- **Staged:** the bug. The agent's change was written to hard-code a light color, so that dark mode would break. The request, the fix, and the reply were written for the film too, though each really ran. +- **Added on top:** the pointer, the camera moves, and the callouts. The render log is sped up, and ends on what it really took. JSON is trimmed, with `…` where it is. The note's typing is uncovered from its finished screenshot. +- **Timing:** scenes, subtitles, and beats come from the word times ElevenLabs gave for this take. +- **Not checked:** the sound was measured, not heard. +- **Kept warnings:** `timeline_track_too_dense`, one clip per subtitle; `dead_stretch`, judged against freeze detection on the rendered file. diff --git a/product-demo/SCRIPT.md b/product-demo/SCRIPT.md new file mode 100644 index 0000000..35c4ac3 --- /dev/null +++ b/product-demo/SCRIPT.md @@ -0,0 +1,55 @@ +## THE ASK + +I asked my coding agent for one small thing: give the top bar a bit more room. It changed two lines of CSS. Looks fine, right? +**[Terminal: the request, and the real two-line diff]** + +So the agent runs selfie review. That renders every story in our Storybook, 124 of them, in a pinned container, right here on this MacBook Air. Twenty-five seconds, with no cloud and no bill per image. +**[Terminal: the real render log, ending on its real timings]** + +What comes back is one JSON document and an exit code, so any agent can read it. Exit 1, because that two-line change touched all 124 stories. +**[Terminal: the real JSON, trimmed, and the exit code]** + +## THE REVIEW + +Now it’s my turn. The selfie ui command opens the same review for a person: 124 changed, and nothing decided yet. +**[Selfie UI: the batch, its counts]** + +That’s a lot to look at, but Selfie works out what kind of change each one is. 48 of them only moved or gained spacing, with nothing new in the image. +**[Show filter: Moved and spacing only, 48 of 124]** + +Here’s one. Yellow is spacing that grew. Press F, and the two images flip in place, so you can see exactly what moved. +**[A spacing-only story. Stack it, and flip between golden and current]** + +That looks right, so I approve those 48 together. +**[The bulk dialog, listing what it will approve]** + +But what about dark mode? I search for dark, and there it is: 85,000 changed pixels. The agent hard-coded a light color, and the top bar went white. +**[Search for dark. The dark story, its numbers, and the white bar]** + +## THE LOOP + +I don’t have to describe where. I click the change, write what’s wrong, and send it to the agent, which has been waiting. +**[Click the hunk, type the note, press Send to agent]** + +The agent gets my words, the story, the exact box, and a picture of what I saw. +**[Terminal: the real note it received, beside the real image with the box drawn on it]** + +It puts the theme’s color back, renders again, and answers my note. +**[Terminal: the real fix, render, and reply]** + +Selfie tells me there’s a newer batch, and the reply is right there on the story. The bar is dark again, and 85,000 changed pixels are down to 13. +**[The newer batch banner, the reply, the white and fixed images, and the new number]** + +I resolve the note, and approve the rest. The 48 I already approved are simply unchanged now, because an approval follows your Git history. +**[Resolve, Approve all, and the batch with nothing blocking and 48 unchanged]** + +The agent commits and reviews once more. It’s the exact same build, so nothing renders. A third of a second, and exit 0. +**[Terminal: the real commit and final review, reusing the images]** + +## THE CLOUD + +Push it, and Selfie renders in CI and puts a status on the commit. Click through, and it’s the same review, in the cloud. +**[The real GitHub commit list, its selfie status, and the hosted review]** + +Selfie works with any coding agent. If you have a Storybook, start locally today at selfie.dev, and join the waitlist for the cloud. +**[End card]** diff --git a/product-demo/assets/fonts/BagelFatOne.woff2 b/product-demo/assets/fonts/BagelFatOne.woff2 new file mode 100644 index 0000000..bd96304 Binary files /dev/null and b/product-demo/assets/fonts/BagelFatOne.woff2 differ diff --git a/product-demo/assets/fonts/Jost.woff2 b/product-demo/assets/fonts/Jost.woff2 new file mode 100644 index 0000000..08a7b80 Binary files /dev/null and b/product-demo/assets/fonts/Jost.woff2 differ diff --git a/product-demo/assets/lib.js b/product-demo/assets/lib.js new file mode 100644 index 0000000..f859837 --- /dev/null +++ b/product-demo/assets/lib.js @@ -0,0 +1,120 @@ +// What the scenes share: the app window and its pointer, the terminal, and when a word is said. +(function () { + const say = (id, word, { nth = 0, after = false } = {}) => { + const plain = word.toLowerCase().replace(/[^a-z0-9]/g, ""); + const hit = window.SCENE_WORDS[id].filter(([w]) => w === plain)[nth]; + if (!hit) throw new Error(`Scene ${id} never says "${word}"`); + return after ? hit[2] : hit[1]; + }; + + /** Where the pointer really was for a shot. An unknown name is an error, so it can't silently land elsewhere. */ + function pointNamed(name) { + const point = window.UI_POINTS[name]; + if (!point) throw new Error(`No pointer position for "${name}"`); + return point; + } + + function html(parent, markup) { + const t = document.createElement("template"); + t.innerHTML = markup.trim(); + return parent.appendChild(t.content.firstElementChild); + } + + function begin(id) { + return { tl: gsap.timeline({ paused: true }), w: (word, o) => say(id, word, o), stage: document.getElementById(`${id}-stage`), D: window.SCENE_DURATIONS[id] }; + } + + /** + * The real app: each shot is a screenshot of a state it was really in, so a change of state is a cut, as it is on + * screen. The pointer and the camera are the only things added. Coordinates are the page's, 1600 x 900. + */ + function app(stage, names, { pointer = [1000, 500] } = {}) { + const start = typeof pointer === "string" ? pointNamed(pointer) : pointer; + const win = html(stage, `
`); + const cam = html(win, `
`); + const shots = Object.fromEntries(names.map((n, i) => [n, html(cam, ``)])); + const arrow = html(cam, ``); + gsap.set(arrow, { x: start[0], y: start[1], transformOrigin: "3px 2px" }); + let showing = names[0]; + const api = { + win, cam, arrow, + show(tl, name, t) { + tl.set(shots[name], { opacity: 1 }, t); + tl.set(shots[showing], { opacity: 0 }, t); + showing = name; + }, + point(tl, t, to, dur = 0.55) { + const [x, y] = typeof to === "string" ? pointNamed(to) : to; + tl.to(arrow, { x, y, duration: dur, ease: "power3.inOut" }, t); + }, + /** The press lands at t. */ + press(tl, t) { + tl.to(arrow, { scale: 0.82, duration: 0.07, ease: "power2.in" }, t - 0.07); + tl.to(arrow, { scale: 1, duration: 0.16, ease: "power2.out" }, t + 0.02); + }, + /** Moves to fit this region of the page, or back out with no region. */ + zoom(tl, t, region, dur = 0.7) { + const to = region ? fit(region) : { x: 0, y: 0, scale: 1 }; + tl.to(cam, { ...to, duration: dur, ease: "power2.inOut" }, t); + }, + /** Starts already zoomed, to continue from a shot before. */ + zoomNow(region) { + gsap.set(cam, fit(region)); + }, + }; + function fit({ x, y, w }) { + const scale = 1600 / w; + const h = 900 / scale; + return { x: -Math.min(Math.max(x, 0), 1600 - w) * scale, y: -Math.min(Math.max(y, 0), 900 - h) * scale, scale }; + } + return api; + } + + /** The terminal. `write` takes rows of [time, markup, options]; `type` reveals a monospaced line a character at a time. */ + function term(stage) { + const win = html(stage, `
`); + const rowY = (row) => 26 + row * 46; + return { + win, + rowY, + write(tl, rows) { + rows.forEach(([t, markup, o = {}], i) => { + const ln = html(win, `
${markup}
`); + if (t <= 0) return; + if (o.type) { + const chars = ln.textContent.length; + gsap.set(ln, { clipPath: "inset(0 100% 0 0)" }); + tl.to(ln, { clipPath: "inset(0 0% 0 0)", duration: Math.min(chars * 0.028, 1.1), ease: `steps(${chars})` }, t); + } else { + gsap.set(ln, { opacity: 0 }); + tl.set(ln, { opacity: 1 }, t); + } + }); + }, + }; + } + + /** Text swapped at set times, one node at a time: seek-safe, unlike writing into one node. */ + function ticker(tl, entries) { + entries.forEach(([t, el], i) => { + gsap.set(el, { opacity: 0 }); + tl.set(el, { opacity: 1 }, t); + if (entries[i + 1]) tl.set(el, { opacity: 0 }, entries[i + 1][0]); + }); + } + + /** A word or number set on the frame, in frame coordinates. */ + function call(tl, stage, text, x, y, t, { until = null, style = "" } = {}) { + const el = html(stage, `
${text}
`); + gsap.set(el, { x, y, opacity: 0, scale: 0.86, transformOrigin: "50% 50%" }); + tl.to(el, { opacity: 1, scale: 1, duration: 0.26, ease: "back.out(2)" }, t); + if (until !== null) tl.to(el, { opacity: 0, duration: 0.14 }, until); + return el; + } + + /** Terminal and app are different places, so going between them is a quick move. */ + const slideIn = (tl, el) => tl.fromTo(el, { x: 1920 }, { x: 0, duration: 0.4, ease: "expo.out" }, 0); + const slideOut = (tl, el, D) => tl.to(el, { x: -1920, duration: 0.2, ease: "power3.in" }, D - 0.21); + + window.S = { html, begin, app, term, ticker, call, slideIn, slideOut }; +})(); diff --git a/product-demo/assets/mascot-cutout.png b/product-demo/assets/mascot-cutout.png new file mode 100644 index 0000000..5a2b786 Binary files /dev/null and b/product-demo/assets/mascot-cutout.png differ diff --git a/product-demo/assets/narration-timing.json b/product-demo/assets/narration-timing.json new file mode 100644 index 0000000..23a21eb --- /dev/null +++ b/product-demo/assets/narration-timing.json @@ -0,0 +1,3771 @@ +{ + "language_code": null, + "segments": [ + { + "text": "I asked my coding agent for one small thing: give the top bar a bit more room. It changed two lines of CSS. Looks fine, right? So the agent runs Selfie review. That renders every story in our storybook, 124 of them, in a pinned container right here on this MacBook Air. 25 seconds with no cloud and no bill per image. What comes back is one JSON document and an exit code, so any agent can read it. Exit one, because that two-line change touched all 124 stories. Now it's my turn. The Selfie UI command opens the same review for a person. 124 changed and nothing decided yet. That's a lot to look at, but Selfie works out what kind of change each one is. 48 of them only moved or gained spacing with nothing new in the image. Here's one. Yellow is spacing that grew. Press F and the two images flip in place so you can see exactly what moved. That looks right, so I approve those 48 together. But what about dark mode? I search for dark, and there it is, 85,000 changed pixels. The agent hardcoded a light color, and the top bar went white. I don't have to describe where. I click the change, write what's wrong, and send it to the agent, which has been waiting. The agent gets my words, the story, the exact box, and a picture of what I saw. It puts the theme's color back, renders again, and answers my note. Selfie tells me there's a newer batch, and the reply is right there on the story. The bar is dark again, and 85,000 changed pixels are down to 13. I resolve the note and approve the rest. The 48 I already approved are simply unchanged now, because an approval follows your Git history. The agent commits and reviews once more. It's the exact same build, so nothing renders. A third of a second and exit zero. Push it, and Selfie renders in CI and puts a status on the commit. Click through, and it's the same review in the cloud. Selfie works with any coding agent. If you have a storybook, start locally today at selfie.dev and join the waitlist for the cloud.", + "start_time": 0.22, + "end_time": 133.36, + "speaker": { + "id": "speaker_0", + "name": "Speaker 0" + }, + "words": [ + { + "text": "I", + "start_time": 0.22, + "end_time": 0.28 + }, + { + "text": " ", + "start_time": 0.28, + "end_time": 0.34 + }, + { + "text": "asked", + "start_time": 0.34, + "end_time": 0.54 + }, + { + "text": " ", + "start_time": 0.54, + "end_time": 0.58 + }, + { + "text": "my", + "start_time": 0.58, + "end_time": 0.74 + }, + { + "text": " ", + "start_time": 0.74, + "end_time": 0.8 + }, + { + "text": "coding", + "start_time": 0.8, + "end_time": 1.1 + }, + { + "text": " ", + "start_time": 1.1, + "end_time": 1.16 + }, + { + "text": "agent", + "start_time": 1.16, + "end_time": 1.44 + }, + { + "text": " ", + "start_time": 1.44, + "end_time": 1.48 + }, + { + "text": "for", + "start_time": 1.48, + "end_time": 1.56 + }, + { + "text": " ", + "start_time": 1.56, + "end_time": 1.72 + }, + { + "text": "one", + "start_time": 1.72, + "end_time": 1.9 + }, + { + "text": " ", + "start_time": 1.9, + "end_time": 2.04 + }, + { + "text": "small", + "start_time": 2.1, + "end_time": 2.32 + }, + { + "text": " ", + "start_time": 2.32, + "end_time": 2.5 + }, + { + "text": "thing:", + "start_time": 2.5, + "end_time": 2.78 + }, + { + "text": " ", + "start_time": 2.78, + "end_time": 2.78 + }, + { + "text": "give", + "start_time": 3.22, + "end_time": 3.34 + }, + { + "text": " ", + "start_time": 3.34, + "end_time": 3.4 + }, + { + "text": "the", + "start_time": 3.4, + "end_time": 3.52 + }, + { + "text": " ", + "start_time": 3.52, + "end_time": 3.6 + }, + { + "text": "top", + "start_time": 3.6, + "end_time": 3.88 + }, + { + "text": " ", + "start_time": 3.88, + "end_time": 3.92 + }, + { + "text": "bar", + "start_time": 3.92, + "end_time": 4.12 + }, + { + "text": " ", + "start_time": 4.12, + "end_time": 4.18 + }, + { + "text": "a", + "start_time": 4.18, + "end_time": 4.2 + }, + { + "text": " ", + "start_time": 4.2, + "end_time": 4.38 + }, + { + "text": "bit", + "start_time": 4.38, + "end_time": 4.54 + }, + { + "text": " ", + "start_time": 4.54, + "end_time": 4.6 + }, + { + "text": "more", + "start_time": 4.6, + "end_time": 4.76 + }, + { + "text": " ", + "start_time": 4.76, + "end_time": 4.88 + }, + { + "text": "room.", + "start_time": 4.88, + "end_time": 5.18 + }, + { + "text": " ", + "start_time": 5.18, + "end_time": 5.18 + }, + { + "text": "It", + "start_time": 5.7, + "end_time": 5.78 + }, + { + "text": " ", + "start_time": 5.78, + "end_time": 5.84 + }, + { + "text": "changed", + "start_time": 5.84, + "end_time": 6.18 + }, + { + "text": " ", + "start_time": 6.18, + "end_time": 6.24 + }, + { + "text": "two", + "start_time": 6.24, + "end_time": 6.34 + }, + { + "text": " ", + "start_time": 6.34, + "end_time": 6.44 + }, + { + "text": "lines", + "start_time": 6.44, + "end_time": 6.72 + }, + { + "text": " ", + "start_time": 6.72, + "end_time": 6.76 + }, + { + "text": "of", + "start_time": 6.76, + "end_time": 6.86 + }, + { + "text": " ", + "start_time": 6.86, + "end_time": 6.9 + }, + { + "text": "CSS.", + "start_time": 6.9, + "end_time": 7.54 + }, + { + "text": " ", + "start_time": 7.54, + "end_time": 7.54 + }, + { + "text": "Looks", + "start_time": 8.1, + "end_time": 8.28 + }, + { + "text": " ", + "start_time": 8.28, + "end_time": 8.38 + }, + { + "text": "fine,", + "start_time": 8.38, + "end_time": 8.68 + }, + { + "text": " ", + "start_time": 8.68, + "end_time": 8.78 + }, + { + "text": "right?", + "start_time": 8.78, + "end_time": 9.0 + }, + { + "text": " ", + "start_time": 9.0, + "end_time": 9.0 + }, + { + "text": "So", + "start_time": 9.56, + "end_time": 9.66 + }, + { + "text": " ", + "start_time": 9.66, + "end_time": 9.72 + }, + { + "text": "the", + "start_time": 9.72, + "end_time": 9.8 + }, + { + "text": " ", + "start_time": 9.8, + "end_time": 9.86 + }, + { + "text": "agent", + "start_time": 9.86, + "end_time": 10.2 + }, + { + "text": " ", + "start_time": 10.2, + "end_time": 10.26 + }, + { + "text": "runs", + "start_time": 10.26, + "end_time": 10.54 + }, + { + "text": " ", + "start_time": 10.54, + "end_time": 10.78 + }, + { + "text": "Selfie", + "start_time": 10.78, + "end_time": 11.1 + }, + { + "text": " ", + "start_time": 11.1, + "end_time": 11.2 + }, + { + "text": "review.", + "start_time": 11.2, + "end_time": 11.56 + }, + { + "text": " ", + "start_time": 11.56, + "end_time": 11.56 + }, + { + "text": "That", + "start_time": 12.06, + "end_time": 12.24 + }, + { + "text": " ", + "start_time": 12.24, + "end_time": 12.28 + }, + { + "text": "renders", + "start_time": 12.28, + "end_time": 12.7 + }, + { + "text": " ", + "start_time": 12.7, + "end_time": 12.76 + }, + { + "text": "every", + "start_time": 12.76, + "end_time": 13.0 + }, + { + "text": " ", + "start_time": 13.0, + "end_time": 13.14 + }, + { + "text": "story", + "start_time": 13.14, + "end_time": 13.46 + }, + { + "text": " ", + "start_time": 13.46, + "end_time": 13.5 + }, + { + "text": "in", + "start_time": 13.5, + "end_time": 13.58 + }, + { + "text": " ", + "start_time": 13.58, + "end_time": 13.62 + }, + { + "text": "our", + "start_time": 13.62, + "end_time": 13.76 + }, + { + "text": " ", + "start_time": 13.76, + "end_time": 13.84 + }, + { + "text": "storybook,", + "start_time": 13.84, + "end_time": 14.38 + }, + { + "text": " ", + "start_time": 14.38, + "end_time": 14.8 + }, + { + "text": "124", + "start_time": 14.86, + "end_time": 15.8 + }, + { + "text": " ", + "start_time": 15.8, + "end_time": 15.84 + }, + { + "text": "of", + "start_time": 15.84, + "end_time": 15.94 + }, + { + "text": " ", + "start_time": 15.94, + "end_time": 15.98 + }, + { + "text": "them,", + "start_time": 15.98, + "end_time": 16.1 + }, + { + "text": " ", + "start_time": 16.1, + "end_time": 16.379 + }, + { + "text": "in", + "start_time": 16.379, + "end_time": 16.44 + }, + { + "text": " ", + "start_time": 16.44, + "end_time": 16.5 + }, + { + "text": "a", + "start_time": 16.5, + "end_time": 16.62 + }, + { + "text": " ", + "start_time": 16.62, + "end_time": 16.66 + }, + { + "text": "pinned", + "start_time": 16.66, + "end_time": 16.9 + }, + { + "text": " ", + "start_time": 16.9, + "end_time": 16.98 + }, + { + "text": "container", + "start_time": 16.98, + "end_time": 17.48 + }, + { + "text": " ", + "start_time": 17.48, + "end_time": 17.8 + }, + { + "text": "right", + "start_time": 17.8, + "end_time": 18.0 + }, + { + "text": " ", + "start_time": 18.0, + "end_time": 18.04 + }, + { + "text": "here", + "start_time": 18.04, + "end_time": 18.28 + }, + { + "text": " ", + "start_time": 18.28, + "end_time": 18.34 + }, + { + "text": "on", + "start_time": 18.34, + "end_time": 18.42 + }, + { + "text": " ", + "start_time": 18.42, + "end_time": 18.46 + }, + { + "text": "this", + "start_time": 18.46, + "end_time": 18.62 + }, + { + "text": " ", + "start_time": 18.62, + "end_time": 18.68 + }, + { + "text": "MacBook", + "start_time": 18.68, + "end_time": 19.04 + }, + { + "text": " ", + "start_time": 19.04, + "end_time": 19.12 + }, + { + "text": "Air.", + "start_time": 19.12, + "end_time": 19.32 + }, + { + "text": " ", + "start_time": 19.32, + "end_time": 19.88 + }, + { + "text": "25", + "start_time": 19.88, + "end_time": 20.34 + }, + { + "text": " ", + "start_time": 20.34, + "end_time": 20.4 + }, + { + "text": "seconds", + "start_time": 20.4, + "end_time": 20.82 + }, + { + "text": " ", + "start_time": 20.82, + "end_time": 20.98 + }, + { + "text": "with", + "start_time": 20.98, + "end_time": 21.12 + }, + { + "text": " ", + "start_time": 21.12, + "end_time": 21.18 + }, + { + "text": "no", + "start_time": 21.2, + "end_time": 21.38 + }, + { + "text": " ", + "start_time": 21.38, + "end_time": 21.42 + }, + { + "text": "cloud", + "start_time": 21.42, + "end_time": 21.8 + }, + { + "text": " ", + "start_time": 21.8, + "end_time": 21.96 + }, + { + "text": "and", + "start_time": 21.96, + "end_time": 22.08 + }, + { + "text": " ", + "start_time": 22.08, + "end_time": 22.2 + }, + { + "text": "no", + "start_time": 22.2, + "end_time": 22.34 + }, + { + "text": " ", + "start_time": 22.34, + "end_time": 22.46 + }, + { + "text": "bill", + "start_time": 22.46, + "end_time": 22.74 + }, + { + "text": " ", + "start_time": 22.74, + "end_time": 22.78 + }, + { + "text": "per", + "start_time": 22.78, + "end_time": 22.9 + }, + { + "text": " ", + "start_time": 22.9, + "end_time": 22.98 + }, + { + "text": "image.", + "start_time": 22.98, + "end_time": 23.3 + }, + { + "text": " ", + "start_time": 23.3, + "end_time": 23.76 + }, + { + "text": "What", + "start_time": 23.76, + "end_time": 23.9 + }, + { + "text": " ", + "start_time": 23.9, + "end_time": 23.94 + }, + { + "text": "comes", + "start_time": 23.94, + "end_time": 24.2 + }, + { + "text": " ", + "start_time": 24.2, + "end_time": 24.24 + }, + { + "text": "back", + "start_time": 24.24, + "end_time": 24.52 + }, + { + "text": " ", + "start_time": 24.52, + "end_time": 24.58 + }, + { + "text": "is", + "start_time": 24.58, + "end_time": 24.74 + }, + { + "text": " ", + "start_time": 24.74, + "end_time": 24.8 + }, + { + "text": "one", + "start_time": 24.8, + "end_time": 25.04 + }, + { + "text": " ", + "start_time": 25.04, + "end_time": 25.08 + }, + { + "text": "JSON", + "start_time": 25.08, + "end_time": 25.5 + }, + { + "text": " ", + "start_time": 25.5, + "end_time": 25.58 + }, + { + "text": "document", + "start_time": 25.58, + "end_time": 26.12 + }, + { + "text": " ", + "start_time": 26.12, + "end_time": 26.14 + }, + { + "text": "and", + "start_time": 26.14, + "end_time": 26.24 + }, + { + "text": " ", + "start_time": 26.24, + "end_time": 26.28 + }, + { + "text": "an", + "start_time": 26.28, + "end_time": 26.34 + }, + { + "text": " ", + "start_time": 26.34, + "end_time": 26.4 + }, + { + "text": "exit", + "start_time": 26.4, + "end_time": 26.66 + }, + { + "text": " ", + "start_time": 26.66, + "end_time": 26.74 + }, + { + "text": "code,", + "start_time": 26.74, + "end_time": 27.02 + }, + { + "text": " ", + "start_time": 27.02, + "end_time": 27.3 + }, + { + "text": "so", + "start_time": 27.3, + "end_time": 27.46 + }, + { + "text": " ", + "start_time": 27.46, + "end_time": 27.54 + }, + { + "text": "any", + "start_time": 27.54, + "end_time": 27.72 + }, + { + "text": " ", + "start_time": 27.72, + "end_time": 27.78 + }, + { + "text": "agent", + "start_time": 27.78, + "end_time": 28.06 + }, + { + "text": " ", + "start_time": 28.06, + "end_time": 28.08 + }, + { + "text": "can", + "start_time": 28.08, + "end_time": 28.22 + }, + { + "text": " ", + "start_time": 28.22, + "end_time": 28.28 + }, + { + "text": "read", + "start_time": 28.28, + "end_time": 28.44 + }, + { + "text": " ", + "start_time": 28.44, + "end_time": 28.48 + }, + { + "text": "it.", + "start_time": 28.48, + "end_time": 28.6 + }, + { + "text": " ", + "start_time": 28.6, + "end_time": 29.08 + }, + { + "text": "Exit", + "start_time": 29.08, + "end_time": 29.36 + }, + { + "text": " ", + "start_time": 29.36, + "end_time": 29.54 + }, + { + "text": "one,", + "start_time": 29.54, + "end_time": 29.82 + }, + { + "text": " ", + "start_time": 29.82, + "end_time": 30.06 + }, + { + "text": "because", + "start_time": 30.06, + "end_time": 30.3 + }, + { + "text": " ", + "start_time": 30.3, + "end_time": 30.36 + }, + { + "text": "that", + "start_time": 30.36, + "end_time": 30.54 + }, + { + "text": " ", + "start_time": 30.54, + "end_time": 30.6 + }, + { + "text": "two-line", + "start_time": 30.6, + "end_time": 31.04 + }, + { + "text": " ", + "start_time": 31.04, + "end_time": 31.16 + }, + { + "text": "change", + "start_time": 31.16, + "end_time": 31.54 + }, + { + "text": " ", + "start_time": 31.54, + "end_time": 31.66 + }, + { + "text": "touched", + "start_time": 31.66, + "end_time": 32.0 + }, + { + "text": " ", + "start_time": 32.0, + "end_time": 32.1 + }, + { + "text": "all", + "start_time": 32.1, + "end_time": 32.36 + }, + { + "text": " ", + "start_time": 32.36, + "end_time": 32.6 + }, + { + "text": "124", + "start_time": 32.6, + "end_time": 33.54 + }, + { + "text": " ", + "start_time": 33.54, + "end_time": 33.62 + }, + { + "text": "stories.", + "start_time": 33.62, + "end_time": 34.1 + }, + { + "text": " ", + "start_time": 34.1, + "end_time": 34.1 + }, + { + "text": "Now", + "start_time": 34.62, + "end_time": 34.78 + }, + { + "text": " ", + "start_time": 34.78, + "end_time": 34.82 + }, + { + "text": "it's", + "start_time": 34.82, + "end_time": 34.96 + }, + { + "text": " ", + "start_time": 34.96, + "end_time": 35.02 + }, + { + "text": "my", + "start_time": 35.02, + "end_time": 35.2 + }, + { + "text": " ", + "start_time": 35.2, + "end_time": 35.26 + }, + { + "text": "turn.", + "start_time": 35.26, + "end_time": 35.52 + }, + { + "text": " ", + "start_time": 35.52, + "end_time": 35.96 + }, + { + "text": "The", + "start_time": 35.96, + "end_time": 36.06 + }, + { + "text": " ", + "start_time": 36.06, + "end_time": 36.12 + }, + { + "text": "Selfie", + "start_time": 36.12, + "end_time": 36.46 + }, + { + "text": " ", + "start_time": 36.46, + "end_time": 36.58 + }, + { + "text": "UI", + "start_time": 36.58, + "end_time": 36.88 + }, + { + "text": " ", + "start_time": 36.88, + "end_time": 36.98 + }, + { + "text": "command", + "start_time": 36.98, + "end_time": 37.34 + }, + { + "text": " ", + "start_time": 37.34, + "end_time": 37.5 + }, + { + "text": "opens", + "start_time": 37.5, + "end_time": 37.82 + }, + { + "text": " ", + "start_time": 37.82, + "end_time": 37.86 + }, + { + "text": "the", + "start_time": 37.86, + "end_time": 37.98 + }, + { + "text": " ", + "start_time": 37.98, + "end_time": 38.06 + }, + { + "text": "same", + "start_time": 38.06, + "end_time": 38.28 + }, + { + "text": " ", + "start_time": 38.28, + "end_time": 38.34 + }, + { + "text": "review", + "start_time": 38.34, + "end_time": 38.72 + }, + { + "text": " ", + "start_time": 38.72, + "end_time": 38.76 + }, + { + "text": "for", + "start_time": 38.76, + "end_time": 38.86 + }, + { + "text": " ", + "start_time": 38.86, + "end_time": 38.9 + }, + { + "text": "a", + "start_time": 38.9, + "end_time": 39.0 + }, + { + "text": " ", + "start_time": 39.0, + "end_time": 39.06 + }, + { + "text": "person.", + "start_time": 39.06, + "end_time": 39.48 + }, + { + "text": " ", + "start_time": 39.48, + "end_time": 39.48 + }, + { + "text": "124", + "start_time": 40.12, + "end_time": 41.04 + }, + { + "text": " ", + "start_time": 41.04, + "end_time": 41.1 + }, + { + "text": "changed", + "start_time": 41.1, + "end_time": 41.56 + }, + { + "text": " ", + "start_time": 41.56, + "end_time": 41.76 + }, + { + "text": "and", + "start_time": 41.76, + "end_time": 41.88 + }, + { + "text": " ", + "start_time": 41.88, + "end_time": 42.04 + }, + { + "text": "nothing", + "start_time": 42.04, + "end_time": 42.38 + }, + { + "text": " ", + "start_time": 42.38, + "end_time": 42.4 + }, + { + "text": "decided", + "start_time": 42.4, + "end_time": 42.92 + }, + { + "text": " ", + "start_time": 42.92, + "end_time": 42.96 + }, + { + "text": "yet.", + "start_time": 42.96, + "end_time": 43.16 + }, + { + "text": " ", + "start_time": 43.16, + "end_time": 43.16 + }, + { + "text": "That's", + "start_time": 43.58, + "end_time": 43.8 + }, + { + "text": " ", + "start_time": 43.8, + "end_time": 43.88 + }, + { + "text": "a", + "start_time": 43.88, + "end_time": 43.9 + }, + { + "text": " ", + "start_time": 43.9, + "end_time": 43.96 + }, + { + "text": "lot", + "start_time": 43.96, + "end_time": 44.14 + }, + { + "text": " ", + "start_time": 44.14, + "end_time": 44.18 + }, + { + "text": "to", + "start_time": 44.18, + "end_time": 44.26 + }, + { + "text": " ", + "start_time": 44.26, + "end_time": 44.32 + }, + { + "text": "look", + "start_time": 44.32, + "end_time": 44.5 + }, + { + "text": " ", + "start_time": 44.5, + "end_time": 44.58 + }, + { + "text": "at,", + "start_time": 44.58, + "end_time": 44.74 + }, + { + "text": " ", + "start_time": 44.74, + "end_time": 45.06 + }, + { + "text": "but", + "start_time": 45.06, + "end_time": 45.2 + }, + { + "text": " ", + "start_time": 45.2, + "end_time": 45.26 + }, + { + "text": "Selfie", + "start_time": 45.26, + "end_time": 45.58 + }, + { + "text": " ", + "start_time": 45.58, + "end_time": 45.66 + }, + { + "text": "works", + "start_time": 45.66, + "end_time": 45.9 + }, + { + "text": " ", + "start_time": 45.9, + "end_time": 45.98 + }, + { + "text": "out", + "start_time": 45.98, + "end_time": 46.18 + }, + { + "text": " ", + "start_time": 46.18, + "end_time": 46.22 + }, + { + "text": "what", + "start_time": 46.22, + "end_time": 46.4 + }, + { + "text": " ", + "start_time": 46.4, + "end_time": 46.46 + }, + { + "text": "kind", + "start_time": 46.46, + "end_time": 46.7 + }, + { + "text": " ", + "start_time": 46.7, + "end_time": 46.74 + }, + { + "text": "of", + "start_time": 46.74, + "end_time": 46.84 + }, + { + "text": " ", + "start_time": 46.84, + "end_time": 46.9 + }, + { + "text": "change", + "start_time": 46.9, + "end_time": 47.22 + }, + { + "text": " ", + "start_time": 47.22, + "end_time": 47.26 + }, + { + "text": "each", + "start_time": 47.26, + "end_time": 47.4 + }, + { + "text": " ", + "start_time": 47.4, + "end_time": 47.48 + }, + { + "text": "one", + "start_time": 47.48, + "end_time": 47.64 + }, + { + "text": " ", + "start_time": 47.64, + "end_time": 47.74 + }, + { + "text": "is.", + "start_time": 47.74, + "end_time": 48.0 + }, + { + "text": " ", + "start_time": 48.0, + "end_time": 48.0 + }, + { + "text": "48", + "start_time": 48.64, + "end_time": 48.98 + }, + { + "text": " ", + "start_time": 48.98, + "end_time": 49.0 + }, + { + "text": "of", + "start_time": 49.0, + "end_time": 49.08 + }, + { + "text": " ", + "start_time": 49.08, + "end_time": 49.12 + }, + { + "text": "them", + "start_time": 49.12, + "end_time": 49.32 + }, + { + "text": " ", + "start_time": 49.32, + "end_time": 49.4 + }, + { + "text": "only", + "start_time": 49.4, + "end_time": 49.7 + }, + { + "text": " ", + "start_time": 49.7, + "end_time": 49.78 + }, + { + "text": "moved", + "start_time": 49.78, + "end_time": 50.1 + }, + { + "text": " ", + "start_time": 50.1, + "end_time": 50.14 + }, + { + "text": "or", + "start_time": 50.14, + "end_time": 50.2 + }, + { + "text": " ", + "start_time": 50.2, + "end_time": 50.32 + }, + { + "text": "gained", + "start_time": 50.32, + "end_time": 50.6 + }, + { + "text": " ", + "start_time": 50.6, + "end_time": 50.7 + }, + { + "text": "spacing", + "start_time": 50.7, + "end_time": 51.12 + }, + { + "text": " ", + "start_time": 51.12, + "end_time": 51.28 + }, + { + "text": "with", + "start_time": 51.3, + "end_time": 51.44 + }, + { + "text": " ", + "start_time": 51.44, + "end_time": 51.5 + }, + { + "text": "nothing", + "start_time": 51.5, + "end_time": 51.76 + }, + { + "text": " ", + "start_time": 51.76, + "end_time": 51.86 + }, + { + "text": "new", + "start_time": 51.86, + "end_time": 52.02 + }, + { + "text": " ", + "start_time": 52.02, + "end_time": 52.06 + }, + { + "text": "in", + "start_time": 52.06, + "end_time": 52.12 + }, + { + "text": " ", + "start_time": 52.12, + "end_time": 52.16 + }, + { + "text": "the", + "start_time": 52.16, + "end_time": 52.24 + }, + { + "text": " ", + "start_time": 52.24, + "end_time": 52.3 + }, + { + "text": "image.", + "start_time": 52.3, + "end_time": 52.6 + }, + { + "text": " ", + "start_time": 52.6, + "end_time": 53.0 + }, + { + "text": "Here's", + "start_time": 53.02, + "end_time": 53.22 + }, + { + "text": " ", + "start_time": 53.22, + "end_time": 53.3 + }, + { + "text": "one.", + "start_time": 53.3, + "end_time": 53.44 + }, + { + "text": " ", + "start_time": 53.44, + "end_time": 53.94 + }, + { + "text": "Yellow", + "start_time": 53.94, + "end_time": 54.34 + }, + { + "text": " ", + "start_time": 54.34, + "end_time": 54.4 + }, + { + "text": "is", + "start_time": 54.4, + "end_time": 54.52 + }, + { + "text": " ", + "start_time": 54.52, + "end_time": 54.62 + }, + { + "text": "spacing", + "start_time": 54.62, + "end_time": 55.02 + }, + { + "text": " ", + "start_time": 55.02, + "end_time": 55.06 + }, + { + "text": "that", + "start_time": 55.06, + "end_time": 55.2 + }, + { + "text": " ", + "start_time": 55.2, + "end_time": 55.24 + }, + { + "text": "grew.", + "start_time": 55.24, + "end_time": 55.56 + }, + { + "text": " ", + "start_time": 55.56, + "end_time": 55.96 + }, + { + "text": "Press", + "start_time": 55.96, + "end_time": 56.2 + }, + { + "text": " ", + "start_time": 56.2, + "end_time": 56.48 + }, + { + "text": "F", + "start_time": 56.48, + "end_time": 56.64 + }, + { + "text": " ", + "start_time": 56.64, + "end_time": 56.86 + }, + { + "text": "and", + "start_time": 56.86, + "end_time": 56.94 + }, + { + "text": " ", + "start_time": 56.94, + "end_time": 56.98 + }, + { + "text": "the", + "start_time": 56.98, + "end_time": 57.08 + }, + { + "text": " ", + "start_time": 57.08, + "end_time": 57.12 + }, + { + "text": "two", + "start_time": 57.12, + "end_time": 57.28 + }, + { + "text": " ", + "start_time": 57.28, + "end_time": 57.34 + }, + { + "text": "images", + "start_time": 57.34, + "end_time": 57.78 + }, + { + "text": " ", + "start_time": 57.78, + "end_time": 57.82 + }, + { + "text": "flip", + "start_time": 57.84, + "end_time": 58.02 + }, + { + "text": " ", + "start_time": 58.02, + "end_time": 58.06 + }, + { + "text": "in", + "start_time": 58.06, + "end_time": 58.14 + }, + { + "text": " ", + "start_time": 58.14, + "end_time": 58.24 + }, + { + "text": "place", + "start_time": 58.26, + "end_time": 58.58 + }, + { + "text": " ", + "start_time": 58.58, + "end_time": 58.78 + }, + { + "text": "so", + "start_time": 58.78, + "end_time": 58.9 + }, + { + "text": " ", + "start_time": 58.9, + "end_time": 58.94 + }, + { + "text": "you", + "start_time": 58.94, + "end_time": 59.0 + }, + { + "text": " ", + "start_time": 59.0, + "end_time": 59.04 + }, + { + "text": "can", + "start_time": 59.04, + "end_time": 59.18 + }, + { + "text": " ", + "start_time": 59.18, + "end_time": 59.22 + }, + { + "text": "see", + "start_time": 59.22, + "end_time": 59.34 + }, + { + "text": " ", + "start_time": 59.34, + "end_time": 59.38 + }, + { + "text": "exactly", + "start_time": 59.38, + "end_time": 59.98 + }, + { + "text": " ", + "start_time": 59.98, + "end_time": 60.02 + }, + { + "text": "what", + "start_time": 60.02, + "end_time": 60.2 + }, + { + "text": " ", + "start_time": 60.2, + "end_time": 60.26 + }, + { + "text": "moved.", + "start_time": 60.26, + "end_time": 60.62 + }, + { + "text": " ", + "start_time": 60.62, + "end_time": 60.98 + }, + { + "text": "That", + "start_time": 60.98, + "end_time": 61.12 + }, + { + "text": " ", + "start_time": 61.12, + "end_time": 61.16 + }, + { + "text": "looks", + "start_time": 61.16, + "end_time": 61.36 + }, + { + "text": " ", + "start_time": 61.36, + "end_time": 61.4 + }, + { + "text": "right,", + "start_time": 61.4, + "end_time": 61.62 + }, + { + "text": " ", + "start_time": 61.62, + "end_time": 61.96 + }, + { + "text": "so", + "start_time": 61.96, + "end_time": 62.06 + }, + { + "text": " ", + "start_time": 62.06, + "end_time": 62.16 + }, + { + "text": "I", + "start_time": 62.16, + "end_time": 62.18 + }, + { + "text": " ", + "start_time": 62.18, + "end_time": 62.24 + }, + { + "text": "approve", + "start_time": 62.24, + "end_time": 62.62 + }, + { + "text": " ", + "start_time": 62.62, + "end_time": 62.64 + }, + { + "text": "those", + "start_time": 62.64, + "end_time": 62.9 + }, + { + "text": " ", + "start_time": 62.9, + "end_time": 63.04 + }, + { + "text": "48", + "start_time": 63.04, + "end_time": 63.36 + }, + { + "text": " ", + "start_time": 63.36, + "end_time": 63.4 + }, + { + "text": "together.", + "start_time": 63.4, + "end_time": 63.8 + }, + { + "text": " ", + "start_time": 63.8, + "end_time": 64.26 + }, + { + "text": "But", + "start_time": 64.26, + "end_time": 64.4 + }, + { + "text": " ", + "start_time": 64.4, + "end_time": 64.44 + }, + { + "text": "what", + "start_time": 64.44, + "end_time": 64.519 + }, + { + "text": " ", + "start_time": 64.519, + "end_time": 64.58 + }, + { + "text": "about", + "start_time": 64.58, + "end_time": 64.92 + }, + { + "text": " ", + "start_time": 64.92, + "end_time": 64.959 + }, + { + "text": "dark", + "start_time": 64.959, + "end_time": 65.22 + }, + { + "text": " ", + "start_time": 65.22, + "end_time": 65.319 + }, + { + "text": "mode?", + "start_time": 65.319, + "end_time": 65.62 + }, + { + "text": " ", + "start_time": 65.62, + "end_time": 65.62 + }, + { + "text": "I", + "start_time": 66.24, + "end_time": 66.24 + }, + { + "text": " ", + "start_time": 66.24, + "end_time": 66.28 + }, + { + "text": "search", + "start_time": 66.28, + "end_time": 66.52 + }, + { + "text": " ", + "start_time": 66.52, + "end_time": 66.56 + }, + { + "text": "for", + "start_time": 66.56, + "end_time": 66.64 + }, + { + "text": " ", + "start_time": 66.64, + "end_time": 66.78 + }, + { + "text": "dark,", + "start_time": 66.78, + "end_time": 67.12 + }, + { + "text": " ", + "start_time": 67.12, + "end_time": 67.42 + }, + { + "text": "and", + "start_time": 67.42, + "end_time": 67.52 + }, + { + "text": " ", + "start_time": 67.52, + "end_time": 67.58 + }, + { + "text": "there", + "start_time": 67.58, + "end_time": 67.72 + }, + { + "text": " ", + "start_time": 67.72, + "end_time": 67.76 + }, + { + "text": "it", + "start_time": 67.76, + "end_time": 67.84 + }, + { + "text": " ", + "start_time": 67.84, + "end_time": 67.92 + }, + { + "text": "is,", + "start_time": 67.92, + "end_time": 68.16 + }, + { + "text": " ", + "start_time": 68.16, + "end_time": 68.7 + }, + { + "text": "85,000", + "start_time": 68.7, + "end_time": 69.96 + }, + { + "text": " ", + "start_time": 69.96, + "end_time": 70.02 + }, + { + "text": "changed", + "start_time": 70.02, + "end_time": 70.36 + }, + { + "text": " ", + "start_time": 70.36, + "end_time": 70.44 + }, + { + "text": "pixels.", + "start_time": 70.44, + "end_time": 70.92 + }, + { + "text": " ", + "start_time": 70.92, + "end_time": 71.32 + }, + { + "text": "The", + "start_time": 71.32, + "end_time": 71.42 + }, + { + "text": " ", + "start_time": 71.42, + "end_time": 71.48 + }, + { + "text": "agent", + "start_time": 71.48, + "end_time": 71.74 + }, + { + "text": " ", + "start_time": 71.74, + "end_time": 71.86 + }, + { + "text": "hardcoded", + "start_time": 71.86, + "end_time": 72.5 + }, + { + "text": " ", + "start_time": 72.5, + "end_time": 72.58 + }, + { + "text": "a", + "start_time": 72.58, + "end_time": 72.64 + }, + { + "text": " ", + "start_time": 72.64, + "end_time": 72.7 + }, + { + "text": "light", + "start_time": 72.7, + "end_time": 72.88 + }, + { + "text": " ", + "start_time": 72.88, + "end_time": 72.98 + }, + { + "text": "color,", + "start_time": 72.98, + "end_time": 73.24 + }, + { + "text": " ", + "start_time": 73.24, + "end_time": 73.42 + }, + { + "text": "and", + "start_time": 73.42, + "end_time": 73.5 + }, + { + "text": " ", + "start_time": 73.5, + "end_time": 73.52 + }, + { + "text": "the", + "start_time": 73.52, + "end_time": 73.64 + }, + { + "text": " ", + "start_time": 73.64, + "end_time": 73.68 + }, + { + "text": "top", + "start_time": 73.68, + "end_time": 73.92 + }, + { + "text": " ", + "start_time": 73.92, + "end_time": 73.94 + }, + { + "text": "bar", + "start_time": 73.94, + "end_time": 74.12 + }, + { + "text": " ", + "start_time": 74.12, + "end_time": 74.2 + }, + { + "text": "went", + "start_time": 74.2, + "end_time": 74.38 + }, + { + "text": " ", + "start_time": 74.38, + "end_time": 74.44 + }, + { + "text": "white.", + "start_time": 74.44, + "end_time": 74.7 + }, + { + "text": " ", + "start_time": 74.7, + "end_time": 74.7 + }, + { + "text": "I", + "start_time": 75.18, + "end_time": 75.22 + }, + { + "text": " ", + "start_time": 75.22, + "end_time": 75.26 + }, + { + "text": "don't", + "start_time": 75.26, + "end_time": 75.38 + }, + { + "text": " ", + "start_time": 75.38, + "end_time": 75.42 + }, + { + "text": "have", + "start_time": 75.42, + "end_time": 75.52 + }, + { + "text": " ", + "start_time": 75.52, + "end_time": 75.56 + }, + { + "text": "to", + "start_time": 75.56, + "end_time": 75.64 + }, + { + "text": " ", + "start_time": 75.64, + "end_time": 75.66 + }, + { + "text": "describe", + "start_time": 75.66, + "end_time": 76.16 + }, + { + "text": " ", + "start_time": 76.16, + "end_time": 76.22 + }, + { + "text": "where.", + "start_time": 76.22, + "end_time": 76.56 + }, + { + "text": " ", + "start_time": 76.56, + "end_time": 77.12 + }, + { + "text": "I", + "start_time": 77.12, + "end_time": 77.12 + }, + { + "text": " ", + "start_time": 77.12, + "end_time": 77.16 + }, + { + "text": "click", + "start_time": 77.16, + "end_time": 77.34 + }, + { + "text": " ", + "start_time": 77.34, + "end_time": 77.4 + }, + { + "text": "the", + "start_time": 77.4, + "end_time": 77.48 + }, + { + "text": " ", + "start_time": 77.48, + "end_time": 77.52 + }, + { + "text": "change,", + "start_time": 77.52, + "end_time": 77.94 + }, + { + "text": " ", + "start_time": 77.94, + "end_time": 78.22 + }, + { + "text": "write", + "start_time": 78.22, + "end_time": 78.44 + }, + { + "text": " ", + "start_time": 78.44, + "end_time": 78.46 + }, + { + "text": "what's", + "start_time": 78.46, + "end_time": 78.68 + }, + { + "text": " ", + "start_time": 78.68, + "end_time": 78.7 + }, + { + "text": "wrong,", + "start_time": 78.7, + "end_time": 79.0 + }, + { + "text": " ", + "start_time": 79.0, + "end_time": 79.28 + }, + { + "text": "and", + "start_time": 79.28, + "end_time": 79.36 + }, + { + "text": " ", + "start_time": 79.36, + "end_time": 79.48 + }, + { + "text": "send", + "start_time": 79.48, + "end_time": 79.66 + }, + { + "text": " ", + "start_time": 79.66, + "end_time": 79.68 + }, + { + "text": "it", + "start_time": 79.68, + "end_time": 79.76 + }, + { + "text": " ", + "start_time": 79.76, + "end_time": 79.8 + }, + { + "text": "to", + "start_time": 79.8, + "end_time": 79.86 + }, + { + "text": " ", + "start_time": 79.86, + "end_time": 79.9 + }, + { + "text": "the", + "start_time": 79.9, + "end_time": 79.98 + }, + { + "text": " ", + "start_time": 79.98, + "end_time": 80.06 + }, + { + "text": "agent,", + "start_time": 80.06, + "end_time": 80.36 + }, + { + "text": " ", + "start_time": 80.36, + "end_time": 80.52 + }, + { + "text": "which", + "start_time": 80.52, + "end_time": 80.62 + }, + { + "text": " ", + "start_time": 80.62, + "end_time": 80.66 + }, + { + "text": "has", + "start_time": 80.66, + "end_time": 80.76 + }, + { + "text": " ", + "start_time": 80.76, + "end_time": 80.8 + }, + { + "text": "been", + "start_time": 80.8, + "end_time": 80.94 + }, + { + "text": " ", + "start_time": 80.94, + "end_time": 80.98 + }, + { + "text": "waiting.", + "start_time": 80.98, + "end_time": 81.3 + }, + { + "text": " ", + "start_time": 81.3, + "end_time": 81.74 + }, + { + "text": "The", + "start_time": 81.74, + "end_time": 81.86 + }, + { + "text": " ", + "start_time": 81.86, + "end_time": 81.92 + }, + { + "text": "agent", + "start_time": 81.92, + "end_time": 82.2 + }, + { + "text": " ", + "start_time": 82.2, + "end_time": 82.22 + }, + { + "text": "gets", + "start_time": 82.22, + "end_time": 82.44 + }, + { + "text": " ", + "start_time": 82.44, + "end_time": 82.48 + }, + { + "text": "my", + "start_time": 82.48, + "end_time": 82.64 + }, + { + "text": " ", + "start_time": 82.64, + "end_time": 82.7 + }, + { + "text": "words,", + "start_time": 82.7, + "end_time": 83.1 + }, + { + "text": " ", + "start_time": 83.1, + "end_time": 83.34 + }, + { + "text": "the", + "start_time": 83.34, + "end_time": 83.44 + }, + { + "text": " ", + "start_time": 83.44, + "end_time": 83.52 + }, + { + "text": "story,", + "start_time": 83.52, + "end_time": 83.88 + }, + { + "text": " ", + "start_time": 83.88, + "end_time": 84.12 + }, + { + "text": "the", + "start_time": 84.12, + "end_time": 84.2 + }, + { + "text": " ", + "start_time": 84.2, + "end_time": 84.24 + }, + { + "text": "exact", + "start_time": 84.24, + "end_time": 84.68 + }, + { + "text": " ", + "start_time": 84.68, + "end_time": 84.76 + }, + { + "text": "box,", + "start_time": 84.76, + "end_time": 85.12 + }, + { + "text": " ", + "start_time": 85.12, + "end_time": 85.32 + }, + { + "text": "and", + "start_time": 85.32, + "end_time": 85.42 + }, + { + "text": " ", + "start_time": 85.42, + "end_time": 85.46 + }, + { + "text": "a", + "start_time": 85.46, + "end_time": 85.56 + }, + { + "text": " ", + "start_time": 85.56, + "end_time": 85.6 + }, + { + "text": "picture", + "start_time": 85.6, + "end_time": 85.9 + }, + { + "text": " ", + "start_time": 85.9, + "end_time": 85.96 + }, + { + "text": "of", + "start_time": 85.96, + "end_time": 86.04 + }, + { + "text": " ", + "start_time": 86.04, + "end_time": 86.06 + }, + { + "text": "what", + "start_time": 86.06, + "end_time": 86.16 + }, + { + "text": " ", + "start_time": 86.16, + "end_time": 86.26 + }, + { + "text": "I", + "start_time": 86.26, + "end_time": 86.34 + }, + { + "text": " ", + "start_time": 86.34, + "end_time": 86.4 + }, + { + "text": "saw.", + "start_time": 86.4, + "end_time": 86.7 + }, + { + "text": " ", + "start_time": 86.7, + "end_time": 87.14 + }, + { + "text": "It", + "start_time": 87.14, + "end_time": 87.24 + }, + { + "text": " ", + "start_time": 87.24, + "end_time": 87.28 + }, + { + "text": "puts", + "start_time": 87.28, + "end_time": 87.5 + }, + { + "text": " ", + "start_time": 87.5, + "end_time": 87.54 + }, + { + "text": "the", + "start_time": 87.54, + "end_time": 87.64 + }, + { + "text": " ", + "start_time": 87.64, + "end_time": 87.68 + }, + { + "text": "theme's", + "start_time": 87.68, + "end_time": 88.0 + }, + { + "text": " ", + "start_time": 88.0, + "end_time": 88.04 + }, + { + "text": "color", + "start_time": 88.04, + "end_time": 88.26 + }, + { + "text": " ", + "start_time": 88.26, + "end_time": 88.38 + }, + { + "text": "back,", + "start_time": 88.38, + "end_time": 88.64 + }, + { + "text": " ", + "start_time": 88.64, + "end_time": 88.96 + }, + { + "text": "renders", + "start_time": 88.96, + "end_time": 89.28 + }, + { + "text": " ", + "start_time": 89.28, + "end_time": 89.34 + }, + { + "text": "again,", + "start_time": 89.34, + "end_time": 89.66 + }, + { + "text": " ", + "start_time": 89.66, + "end_time": 89.9 + }, + { + "text": "and", + "start_time": 89.9, + "end_time": 90.0 + }, + { + "text": " ", + "start_time": 90.0, + "end_time": 90.08 + }, + { + "text": "answers", + "start_time": 90.08, + "end_time": 90.4 + }, + { + "text": " ", + "start_time": 90.4, + "end_time": 90.46 + }, + { + "text": "my", + "start_time": 90.46, + "end_time": 90.6 + }, + { + "text": " ", + "start_time": 90.6, + "end_time": 90.64 + }, + { + "text": "note.", + "start_time": 90.64, + "end_time": 90.86 + }, + { + "text": " ", + "start_time": 90.86, + "end_time": 91.38 + }, + { + "text": "Selfie", + "start_time": 91.38, + "end_time": 91.64 + }, + { + "text": " ", + "start_time": 91.64, + "end_time": 91.74 + }, + { + "text": "tells", + "start_time": 91.74, + "end_time": 91.96 + }, + { + "text": " ", + "start_time": 91.96, + "end_time": 92.0 + }, + { + "text": "me", + "start_time": 92.0, + "end_time": 92.1 + }, + { + "text": " ", + "start_time": 92.1, + "end_time": 92.12 + }, + { + "text": "there's", + "start_time": 92.12, + "end_time": 92.28 + }, + { + "text": " ", + "start_time": 92.28, + "end_time": 92.32 + }, + { + "text": "a", + "start_time": 92.32, + "end_time": 92.36 + }, + { + "text": " ", + "start_time": 92.36, + "end_time": 92.42 + }, + { + "text": "newer", + "start_time": 92.42, + "end_time": 92.64 + }, + { + "text": " ", + "start_time": 92.64, + "end_time": 92.74 + }, + { + "text": "batch,", + "start_time": 92.74, + "end_time": 93.06 + }, + { + "text": " ", + "start_time": 93.06, + "end_time": 93.24 + }, + { + "text": "and", + "start_time": 93.24, + "end_time": 93.32 + }, + { + "text": " ", + "start_time": 93.32, + "end_time": 93.36 + }, + { + "text": "the", + "start_time": 93.36, + "end_time": 93.42 + }, + { + "text": " ", + "start_time": 93.42, + "end_time": 93.46 + }, + { + "text": "reply", + "start_time": 93.46, + "end_time": 93.9 + }, + { + "text": " ", + "start_time": 93.9, + "end_time": 93.96 + }, + { + "text": "is", + "start_time": 93.96, + "end_time": 94.14 + }, + { + "text": " ", + "start_time": 94.14, + "end_time": 94.22 + }, + { + "text": "right", + "start_time": 94.22, + "end_time": 94.4 + }, + { + "text": " ", + "start_time": 94.4, + "end_time": 94.46 + }, + { + "text": "there", + "start_time": 94.46, + "end_time": 94.62 + }, + { + "text": " ", + "start_time": 94.62, + "end_time": 94.66 + }, + { + "text": "on", + "start_time": 94.66, + "end_time": 94.74 + }, + { + "text": " ", + "start_time": 94.74, + "end_time": 94.78 + }, + { + "text": "the", + "start_time": 94.78, + "end_time": 94.88 + }, + { + "text": " ", + "start_time": 94.88, + "end_time": 94.94 + }, + { + "text": "story.", + "start_time": 94.94, + "end_time": 95.28 + }, + { + "text": " ", + "start_time": 95.28, + "end_time": 95.68 + }, + { + "text": "The", + "start_time": 95.68, + "end_time": 95.76 + }, + { + "text": " ", + "start_time": 95.76, + "end_time": 95.8 + }, + { + "text": "bar", + "start_time": 95.8, + "end_time": 96.0 + }, + { + "text": " ", + "start_time": 96.0, + "end_time": 96.06 + }, + { + "text": "is", + "start_time": 96.06, + "end_time": 96.2 + }, + { + "text": " ", + "start_time": 96.2, + "end_time": 96.22 + }, + { + "text": "dark", + "start_time": 96.22, + "end_time": 96.46 + }, + { + "text": " ", + "start_time": 96.46, + "end_time": 96.52 + }, + { + "text": "again,", + "start_time": 96.52, + "end_time": 96.82 + }, + { + "text": " ", + "start_time": 96.82, + "end_time": 97.1 + }, + { + "text": "and", + "start_time": 97.1, + "end_time": 97.22 + }, + { + "text": " ", + "start_time": 97.22, + "end_time": 97.46 + }, + { + "text": "85,000", + "start_time": 97.46, + "end_time": 98.4 + }, + { + "text": " ", + "start_time": 98.4, + "end_time": 98.44 + }, + { + "text": "changed", + "start_time": 98.44, + "end_time": 98.76 + }, + { + "text": " ", + "start_time": 98.76, + "end_time": 98.84 + }, + { + "text": "pixels", + "start_time": 98.84, + "end_time": 99.24 + }, + { + "text": " ", + "start_time": 99.24, + "end_time": 99.38 + }, + { + "text": "are", + "start_time": 99.38, + "end_time": 99.5 + }, + { + "text": " ", + "start_time": 99.5, + "end_time": 99.54 + }, + { + "text": "down", + "start_time": 99.54, + "end_time": 99.72 + }, + { + "text": " ", + "start_time": 99.72, + "end_time": 99.76 + }, + { + "text": "to", + "start_time": 99.76, + "end_time": 99.86 + }, + { + "text": " ", + "start_time": 99.86, + "end_time": 99.9 + }, + { + "text": "13.", + "start_time": 99.9, + "end_time": 100.48 + }, + { + "text": " ", + "start_time": 100.48, + "end_time": 100.98 + }, + { + "text": "I", + "start_time": 100.98, + "end_time": 101.04 + }, + { + "text": " ", + "start_time": 101.04, + "end_time": 101.08 + }, + { + "text": "resolve", + "start_time": 101.08, + "end_time": 101.48 + }, + { + "text": " ", + "start_time": 101.48, + "end_time": 101.5 + }, + { + "text": "the", + "start_time": 101.5, + "end_time": 101.6 + }, + { + "text": " ", + "start_time": 101.6, + "end_time": 101.62 + }, + { + "text": "note", + "start_time": 101.62, + "end_time": 101.84 + }, + { + "text": " ", + "start_time": 101.84, + "end_time": 102.06 + }, + { + "text": "and", + "start_time": 102.06, + "end_time": 102.14 + }, + { + "text": " ", + "start_time": 102.14, + "end_time": 102.18 + }, + { + "text": "approve", + "start_time": 102.18, + "end_time": 102.54 + }, + { + "text": " ", + "start_time": 102.54, + "end_time": 102.56 + }, + { + "text": "the", + "start_time": 102.58, + "end_time": 102.64 + }, + { + "text": " ", + "start_time": 102.64, + "end_time": 102.72 + }, + { + "text": "rest.", + "start_time": 102.72, + "end_time": 103.06 + }, + { + "text": " ", + "start_time": 103.06, + "end_time": 103.46 + }, + { + "text": "The", + "start_time": 103.46, + "end_time": 103.56 + }, + { + "text": " ", + "start_time": 103.56, + "end_time": 103.72 + }, + { + "text": "48", + "start_time": 103.72, + "end_time": 104.04 + }, + { + "text": " ", + "start_time": 104.04, + "end_time": 104.18 + }, + { + "text": "I", + "start_time": 104.18, + "end_time": 104.18 + }, + { + "text": " ", + "start_time": 104.18, + "end_time": 104.26 + }, + { + "text": "already", + "start_time": 104.26, + "end_time": 104.62 + }, + { + "text": " ", + "start_time": 104.62, + "end_time": 104.66 + }, + { + "text": "approved", + "start_time": 104.66, + "end_time": 105.1 + }, + { + "text": " ", + "start_time": 105.1, + "end_time": 105.14 + }, + { + "text": "are", + "start_time": 105.14, + "end_time": 105.24 + }, + { + "text": " ", + "start_time": 105.24, + "end_time": 105.3 + }, + { + "text": "simply", + "start_time": 105.3, + "end_time": 105.64 + }, + { + "text": " ", + "start_time": 105.64, + "end_time": 105.7 + }, + { + "text": "unchanged", + "start_time": 105.7, + "end_time": 106.3 + }, + { + "text": " ", + "start_time": 106.3, + "end_time": 106.34 + }, + { + "text": "now,", + "start_time": 106.34, + "end_time": 106.56 + }, + { + "text": " ", + "start_time": 106.56, + "end_time": 106.78 + }, + { + "text": "because", + "start_time": 106.78, + "end_time": 107.0 + }, + { + "text": " ", + "start_time": 107.0, + "end_time": 107.04 + }, + { + "text": "an", + "start_time": 107.04, + "end_time": 107.1 + }, + { + "text": " ", + "start_time": 107.1, + "end_time": 107.14 + }, + { + "text": "approval", + "start_time": 107.14, + "end_time": 107.6 + }, + { + "text": " ", + "start_time": 107.6, + "end_time": 107.76 + }, + { + "text": "follows", + "start_time": 107.76, + "end_time": 108.12 + }, + { + "text": " ", + "start_time": 108.12, + "end_time": 108.16 + }, + { + "text": "your", + "start_time": 108.16, + "end_time": 108.24 + }, + { + "text": " ", + "start_time": 108.24, + "end_time": 108.34 + }, + { + "text": "Git", + "start_time": 108.34, + "end_time": 108.48 + }, + { + "text": " ", + "start_time": 108.48, + "end_time": 108.52 + }, + { + "text": "history.", + "start_time": 108.52, + "end_time": 108.92 + }, + { + "text": " ", + "start_time": 108.92, + "end_time": 109.3 + }, + { + "text": "The", + "start_time": 109.34, + "end_time": 109.42 + }, + { + "text": " ", + "start_time": 109.42, + "end_time": 109.48 + }, + { + "text": "agent", + "start_time": 109.48, + "end_time": 109.76 + }, + { + "text": " ", + "start_time": 109.76, + "end_time": 109.8 + }, + { + "text": "commits", + "start_time": 109.8, + "end_time": 110.32 + }, + { + "text": " ", + "start_time": 110.32, + "end_time": 110.34 + }, + { + "text": "and", + "start_time": 110.34, + "end_time": 110.44 + }, + { + "text": " ", + "start_time": 110.44, + "end_time": 110.48 + }, + { + "text": "reviews", + "start_time": 110.48, + "end_time": 110.9 + }, + { + "text": " ", + "start_time": 110.9, + "end_time": 111.0 + }, + { + "text": "once", + "start_time": 111.0, + "end_time": 111.14 + }, + { + "text": " ", + "start_time": 111.14, + "end_time": 111.26 + }, + { + "text": "more.", + "start_time": 111.26, + "end_time": 111.52 + }, + { + "text": " ", + "start_time": 111.52, + "end_time": 111.92 + }, + { + "text": "It's", + "start_time": 111.92, + "end_time": 112.06 + }, + { + "text": " ", + "start_time": 112.06, + "end_time": 112.1 + }, + { + "text": "the", + "start_time": 112.1, + "end_time": 112.18 + }, + { + "text": " ", + "start_time": 112.18, + "end_time": 112.22 + }, + { + "text": "exact", + "start_time": 112.22, + "end_time": 112.66 + }, + { + "text": " ", + "start_time": 112.66, + "end_time": 112.72 + }, + { + "text": "same", + "start_time": 112.72, + "end_time": 112.92 + }, + { + "text": " ", + "start_time": 112.92, + "end_time": 113.0 + }, + { + "text": "build,", + "start_time": 113.0, + "end_time": 113.3 + }, + { + "text": " ", + "start_time": 113.3, + "end_time": 113.56 + }, + { + "text": "so", + "start_time": 113.56, + "end_time": 113.7 + }, + { + "text": " ", + "start_time": 113.7, + "end_time": 113.72 + }, + { + "text": "nothing", + "start_time": 113.72, + "end_time": 114.02 + }, + { + "text": " ", + "start_time": 114.02, + "end_time": 114.08 + }, + { + "text": "renders.", + "start_time": 114.08, + "end_time": 114.48 + }, + { + "text": " ", + "start_time": 114.48, + "end_time": 114.92 + }, + { + "text": "A", + "start_time": 114.92, + "end_time": 115.06 + }, + { + "text": " ", + "start_time": 115.06, + "end_time": 115.12 + }, + { + "text": "third", + "start_time": 115.12, + "end_time": 115.3 + }, + { + "text": " ", + "start_time": 115.3, + "end_time": 115.34 + }, + { + "text": "of", + "start_time": 115.34, + "end_time": 115.4 + }, + { + "text": " ", + "start_time": 115.4, + "end_time": 115.44 + }, + { + "text": "a", + "start_time": 115.44, + "end_time": 115.52 + }, + { + "text": " ", + "start_time": 115.52, + "end_time": 115.56 + }, + { + "text": "second", + "start_time": 115.56, + "end_time": 115.92 + }, + { + "text": " ", + "start_time": 115.92, + "end_time": 116.16 + }, + { + "text": "and", + "start_time": 116.16, + "end_time": 116.26 + }, + { + "text": " ", + "start_time": 116.26, + "end_time": 116.42 + }, + { + "text": "exit", + "start_time": 116.44, + "end_time": 116.72 + }, + { + "text": " ", + "start_time": 116.72, + "end_time": 116.84 + }, + { + "text": "zero.", + "start_time": 116.84, + "end_time": 117.2 + }, + { + "text": " ", + "start_time": 117.2, + "end_time": 117.6 + }, + { + "text": "Push", + "start_time": 117.6, + "end_time": 117.8 + }, + { + "text": " ", + "start_time": 117.8, + "end_time": 117.84 + }, + { + "text": "it,", + "start_time": 117.84, + "end_time": 117.96 + }, + { + "text": " ", + "start_time": 117.96, + "end_time": 118.2 + }, + { + "text": "and", + "start_time": 118.2, + "end_time": 118.28 + }, + { + "text": " ", + "start_time": 118.28, + "end_time": 118.42 + }, + { + "text": "Selfie", + "start_time": 118.42, + "end_time": 118.74 + }, + { + "text": " ", + "start_time": 118.74, + "end_time": 118.84 + }, + { + "text": "renders", + "start_time": 118.84, + "end_time": 119.16 + }, + { + "text": " ", + "start_time": 119.16, + "end_time": 119.18 + }, + { + "text": "in", + "start_time": 119.18, + "end_time": 119.32 + }, + { + "text": " ", + "start_time": 119.32, + "end_time": 119.38 + }, + { + "text": "CI", + "start_time": 119.38, + "end_time": 119.84 + }, + { + "text": " ", + "start_time": 119.84, + "end_time": 119.96 + }, + { + "text": "and", + "start_time": 119.96, + "end_time": 120.04 + }, + { + "text": " ", + "start_time": 120.04, + "end_time": 120.12 + }, + { + "text": "puts", + "start_time": 120.12, + "end_time": 120.28 + }, + { + "text": " ", + "start_time": 120.28, + "end_time": 120.32 + }, + { + "text": "a", + "start_time": 120.32, + "end_time": 120.4 + }, + { + "text": " ", + "start_time": 120.4, + "end_time": 120.48 + }, + { + "text": "status", + "start_time": 120.48, + "end_time": 120.84 + }, + { + "text": " ", + "start_time": 120.84, + "end_time": 120.9 + }, + { + "text": "on", + "start_time": 120.9, + "end_time": 120.96 + }, + { + "text": " ", + "start_time": 120.96, + "end_time": 121.0 + }, + { + "text": "the", + "start_time": 121.0, + "end_time": 121.08 + }, + { + "text": " ", + "start_time": 121.08, + "end_time": 121.1 + }, + { + "text": "commit.", + "start_time": 121.1, + "end_time": 121.44 + }, + { + "text": " ", + "start_time": 121.44, + "end_time": 121.86 + }, + { + "text": "Click", + "start_time": 121.86, + "end_time": 122.04 + }, + { + "text": " ", + "start_time": 122.04, + "end_time": 122.1 + }, + { + "text": "through,", + "start_time": 122.1, + "end_time": 122.38 + }, + { + "text": " ", + "start_time": 122.38, + "end_time": 122.62 + }, + { + "text": "and", + "start_time": 122.62, + "end_time": 122.7 + }, + { + "text": " ", + "start_time": 122.7, + "end_time": 122.74 + }, + { + "text": "it's", + "start_time": 122.74, + "end_time": 122.9 + }, + { + "text": " ", + "start_time": 122.9, + "end_time": 122.94 + }, + { + "text": "the", + "start_time": 122.94, + "end_time": 123.08 + }, + { + "text": " ", + "start_time": 123.08, + "end_time": 123.16 + }, + { + "text": "same", + "start_time": 123.16, + "end_time": 123.4 + }, + { + "text": " ", + "start_time": 123.4, + "end_time": 123.44 + }, + { + "text": "review", + "start_time": 123.44, + "end_time": 123.84 + }, + { + "text": " ", + "start_time": 123.84, + "end_time": 124.14 + }, + { + "text": "in", + "start_time": 124.14, + "end_time": 124.22 + }, + { + "text": " ", + "start_time": 124.22, + "end_time": 124.28 + }, + { + "text": "the", + "start_time": 124.28, + "end_time": 124.36 + }, + { + "text": " ", + "start_time": 124.36, + "end_time": 124.4 + }, + { + "text": "cloud.", + "start_time": 124.4, + "end_time": 124.8 + }, + { + "text": " ", + "start_time": 124.8, + "end_time": 124.8 + }, + { + "text": "Selfie", + "start_time": 125.32, + "end_time": 125.6 + }, + { + "text": " ", + "start_time": 125.6, + "end_time": 125.7 + }, + { + "text": "works", + "start_time": 125.7, + "end_time": 125.94 + }, + { + "text": " ", + "start_time": 125.94, + "end_time": 125.98 + }, + { + "text": "with", + "start_time": 125.98, + "end_time": 126.18 + }, + { + "text": " ", + "start_time": 126.18, + "end_time": 126.26 + }, + { + "text": "any", + "start_time": 126.26, + "end_time": 126.42 + }, + { + "text": " ", + "start_time": 126.42, + "end_time": 126.54 + }, + { + "text": "coding", + "start_time": 126.54, + "end_time": 126.84 + }, + { + "text": " ", + "start_time": 126.84, + "end_time": 126.9 + }, + { + "text": "agent.", + "start_time": 126.9, + "end_time": 127.22 + }, + { + "text": " ", + "start_time": 127.22, + "end_time": 127.64 + }, + { + "text": "If", + "start_time": 127.64, + "end_time": 127.7 + }, + { + "text": " ", + "start_time": 127.7, + "end_time": 127.76 + }, + { + "text": "you", + "start_time": 127.76, + "end_time": 127.84 + }, + { + "text": " ", + "start_time": 127.84, + "end_time": 127.88 + }, + { + "text": "have", + "start_time": 127.88, + "end_time": 127.98 + }, + { + "text": " ", + "start_time": 127.98, + "end_time": 128.02 + }, + { + "text": "a", + "start_time": 128.02, + "end_time": 128.1 + }, + { + "text": " ", + "start_time": 128.1, + "end_time": 128.18 + }, + { + "text": "storybook,", + "start_time": 128.18, + "end_time": 128.68 + }, + { + "text": " ", + "start_time": 128.68, + "end_time": 128.96 + }, + { + "text": "start", + "start_time": 128.96, + "end_time": 129.18 + }, + { + "text": " ", + "start_time": 129.18, + "end_time": 129.28 + }, + { + "text": "locally", + "start_time": 129.28, + "end_time": 129.66 + }, + { + "text": " ", + "start_time": 129.66, + "end_time": 129.699 + }, + { + "text": "today", + "start_time": 129.699, + "end_time": 130.08 + }, + { + "text": " ", + "start_time": 130.08, + "end_time": 130.139 + }, + { + "text": "at", + "start_time": 130.139, + "end_time": 130.28 + }, + { + "text": " ", + "start_time": 130.28, + "end_time": 130.36 + }, + { + "text": "selfie.dev", + "start_time": 130.36, + "end_time": 131.38 + }, + { + "text": " ", + "start_time": 131.38, + "end_time": 131.58 + }, + { + "text": "and", + "start_time": 131.6, + "end_time": 131.76 + }, + { + "text": " ", + "start_time": 131.76, + "end_time": 131.82 + }, + { + "text": "join", + "start_time": 131.82, + "end_time": 132.04 + }, + { + "text": " ", + "start_time": 132.04, + "end_time": 132.08 + }, + { + "text": "the", + "start_time": 132.08, + "end_time": 132.18 + }, + { + "text": " ", + "start_time": 132.18, + "end_time": 132.2 + }, + { + "text": "waitlist", + "start_time": 132.2, + "end_time": 132.68 + }, + { + "text": " ", + "start_time": 132.68, + "end_time": 132.72 + }, + { + "text": "for", + "start_time": 132.72, + "end_time": 132.8 + }, + { + "text": " ", + "start_time": 132.8, + "end_time": 132.84 + }, + { + "text": "the", + "start_time": 132.84, + "end_time": 132.92 + }, + { + "text": " ", + "start_time": 132.92, + "end_time": 132.96 + }, + { + "text": "cloud.", + "start_time": 132.96, + "end_time": 133.36 + } + ] + } + ] +} \ No newline at end of file diff --git a/product-demo/assets/narration.mp3 b/product-demo/assets/narration.mp3 new file mode 100644 index 0000000..6565cd0 Binary files /dev/null and b/product-demo/assets/narration.mp3 differ diff --git a/product-demo/assets/style.css b/product-demo/assets/style.css new file mode 100644 index 0000000..78239e0 --- /dev/null +++ b/product-demo/assets/style.css @@ -0,0 +1,32 @@ +@font-face { font-family: "Bagel Fat One"; src: url("fonts/BagelFatOne.woff2") format("woff2"); } +@font-face { font-family: "Jost"; src: url("fonts/Jost.woff2") format("woff2"); font-weight: 100 900; } + +:root { --ground: #bfd9f5; --sun: #ffd95e; --ink: #23232b; } +* { box-sizing: border-box; margin: 0; padding: 0; } +html, body { width: 1920px; height: 1080px; overflow: hidden; background: var(--ground); font-family: "Jost", sans-serif; color: var(--ink); } + +.stage { position: absolute; inset: 0; overflow: hidden; background: var(--ground); } +.abs { position: absolute; left: 0; top: 0; } + +/* One window, in one place, for the whole film: the app or the terminal. */ +.win { position: absolute; left: 160px; top: 20px; width: 1600px; height: 900px; overflow: hidden; border-radius: 14px; outline: 5px solid var(--ink); background: #fff; box-shadow: 10px 10px 0 var(--ink); } +.cam { position: absolute; left: 0; top: 0; width: 1600px; height: 900px; transform-origin: 0 0; } +.shot { position: absolute; left: 0; top: 0; width: 1600px; height: 900px; } +.pointer { position: absolute; left: 0; top: 0; width: 34px; height: 46px; overflow: visible; } + +.term { background: #17181f; color: #e8e9ee; font: 500 30px/46px "JetBrains Mono", monospace; padding: 26px 34px; } +.term .ln { position: absolute; left: 34px; right: 34px; white-space: pre; isolation: isolate; } +.term .ln.hl::before { content: ""; position: absolute; left: -14px; right: -14px; top: 1px; bottom: 1px; border-radius: 6px; background: rgba(244, 104, 159, 0.2); z-index: -1; } +.term .p { color: #f4689f; font-weight: 700; } +.term .c { color: #8b8fa3; } +.term .k { color: #8fc7ee; } +.term .n { color: #ffd95e; } +.term .s { color: #a9dcb4; } +.term .add { color: #7ee2a0; } +.term .del { color: #ff8b92; } +.term .b { color: #fff; font-weight: 700; } + +/* A word or number the voice is saying, set beside what it's about. */ +.call { position: absolute; left: 0; top: 0; padding: 10px 26px 15px; border-radius: 12px; background: var(--sun); color: var(--ink); border: 5px solid var(--ink); box-shadow: 6px 6px 0 var(--ink); font: 700 46px/1.1 "Jost", sans-serif; white-space: nowrap; } +.flash { position: absolute; inset: 0; background: #fff; opacity: 0; } +.wordmark { font-family: "Bagel Fat One", sans-serif; line-height: 1; background: linear-gradient(90deg, #63b9e3, #78acae 50%, #e35c61); -webkit-background-clip: text; background-clip: text; color: transparent; filter: drop-shadow(7px 7px 0 #4d4d4d); } diff --git a/product-demo/assets/ui/a1-batch.png b/product-demo/assets/ui/a1-batch.png new file mode 100644 index 0000000..972e6dc Binary files /dev/null and b/product-demo/assets/ui/a1-batch.png differ diff --git a/product-demo/assets/ui/a2-layout.png b/product-demo/assets/ui/a2-layout.png new file mode 100644 index 0000000..de9a915 Binary files /dev/null and b/product-demo/assets/ui/a2-layout.png differ diff --git a/product-demo/assets/ui/b1-layout-story.png b/product-demo/assets/ui/b1-layout-story.png new file mode 100644 index 0000000..5786ef3 Binary files /dev/null and b/product-demo/assets/ui/b1-layout-story.png differ diff --git a/product-demo/assets/ui/b2-stacked-a.png b/product-demo/assets/ui/b2-stacked-a.png new file mode 100644 index 0000000..9401711 Binary files /dev/null and b/product-demo/assets/ui/b2-stacked-a.png differ diff --git a/product-demo/assets/ui/b3-stacked-b.png b/product-demo/assets/ui/b3-stacked-b.png new file mode 100644 index 0000000..5c437bc Binary files /dev/null and b/product-demo/assets/ui/b3-stacked-b.png differ diff --git a/product-demo/assets/ui/c1-dialog.png b/product-demo/assets/ui/c1-dialog.png new file mode 100644 index 0000000..9785b17 Binary files /dev/null and b/product-demo/assets/ui/c1-dialog.png differ diff --git a/product-demo/assets/ui/c2-approved.png b/product-demo/assets/ui/c2-approved.png new file mode 100644 index 0000000..1fb30bc Binary files /dev/null and b/product-demo/assets/ui/c2-approved.png differ diff --git a/product-demo/assets/ui/d1-search.png b/product-demo/assets/ui/d1-search.png new file mode 100644 index 0000000..f05fd13 Binary files /dev/null and b/product-demo/assets/ui/d1-search.png differ diff --git a/product-demo/assets/ui/d2-dark.png b/product-demo/assets/ui/d2-dark.png new file mode 100644 index 0000000..9dadee3 Binary files /dev/null and b/product-demo/assets/ui/d2-dark.png differ diff --git a/product-demo/assets/ui/d4-dark-golden.png b/product-demo/assets/ui/d4-dark-golden.png new file mode 100644 index 0000000..802efb3 Binary files /dev/null and b/product-demo/assets/ui/d4-dark-golden.png differ diff --git a/product-demo/assets/ui/d5-dark-current.png b/product-demo/assets/ui/d5-dark-current.png new file mode 100644 index 0000000..e6eaa7e Binary files /dev/null and b/product-demo/assets/ui/d5-dark-current.png differ diff --git a/product-demo/assets/ui/dark-after.png b/product-demo/assets/ui/dark-after.png new file mode 100644 index 0000000..dbb3c78 Binary files /dev/null and b/product-demo/assets/ui/dark-after.png differ diff --git a/product-demo/assets/ui/dark-before.png b/product-demo/assets/ui/dark-before.png new file mode 100644 index 0000000..11665cf Binary files /dev/null and b/product-demo/assets/ui/dark-before.png differ diff --git a/product-demo/assets/ui/e2-box.png b/product-demo/assets/ui/e2-box.png new file mode 100644 index 0000000..da45ce4 Binary files /dev/null and b/product-demo/assets/ui/e2-box.png differ diff --git a/product-demo/assets/ui/f1-note-typed.png b/product-demo/assets/ui/f1-note-typed.png new file mode 100644 index 0000000..d77b548 Binary files /dev/null and b/product-demo/assets/ui/f1-note-typed.png differ diff --git a/product-demo/assets/ui/f3-note-added.png b/product-demo/assets/ui/f3-note-added.png new file mode 100644 index 0000000..f84e71a Binary files /dev/null and b/product-demo/assets/ui/f3-note-added.png differ diff --git a/product-demo/assets/ui/g1-sent.png b/product-demo/assets/ui/g1-sent.png new file mode 100644 index 0000000..923f511 Binary files /dev/null and b/product-demo/assets/ui/g1-sent.png differ diff --git a/product-demo/assets/ui/h1-newer.png b/product-demo/assets/ui/h1-newer.png new file mode 100644 index 0000000..59b3d81 Binary files /dev/null and b/product-demo/assets/ui/h1-newer.png differ diff --git a/product-demo/assets/ui/h2-replied.png b/product-demo/assets/ui/h2-replied.png new file mode 100644 index 0000000..4e44237 Binary files /dev/null and b/product-demo/assets/ui/h2-replied.png differ diff --git a/product-demo/assets/ui/i4-resolved.png b/product-demo/assets/ui/i4-resolved.png new file mode 100644 index 0000000..a9bcde8 Binary files /dev/null and b/product-demo/assets/ui/i4-resolved.png differ diff --git a/product-demo/assets/ui/j1-batch.png b/product-demo/assets/ui/j1-batch.png new file mode 100644 index 0000000..27b3692 Binary files /dev/null and b/product-demo/assets/ui/j1-batch.png differ diff --git a/product-demo/assets/ui/k1-dialog.png b/product-demo/assets/ui/k1-dialog.png new file mode 100644 index 0000000..b0f6808 Binary files /dev/null and b/product-demo/assets/ui/k1-dialog.png differ diff --git a/product-demo/assets/ui/k2-all-approved.png b/product-demo/assets/ui/k2-all-approved.png new file mode 100644 index 0000000..95e2533 Binary files /dev/null and b/product-demo/assets/ui/k2-all-approved.png differ diff --git a/product-demo/assets/ui/m1-github-commits.png b/product-demo/assets/ui/m1-github-commits.png new file mode 100644 index 0000000..23df096 Binary files /dev/null and b/product-demo/assets/ui/m1-github-commits.png differ diff --git a/product-demo/assets/ui/m1b-github-status.png b/product-demo/assets/ui/m1b-github-status.png new file mode 100644 index 0000000..2f8d7f9 Binary files /dev/null and b/product-demo/assets/ui/m1b-github-status.png differ diff --git a/product-demo/assets/ui/m2-hosted-batch.png b/product-demo/assets/ui/m2-hosted-batch.png new file mode 100644 index 0000000..cda36b8 Binary files /dev/null and b/product-demo/assets/ui/m2-hosted-batch.png differ diff --git a/product-demo/assets/ui/note-image.png b/product-demo/assets/ui/note-image.png new file mode 100644 index 0000000..c8477ad Binary files /dev/null and b/product-demo/assets/ui/note-image.png differ diff --git a/product-demo/assets/ui/points.json b/product-demo/assets/ui/points.json new file mode 100644 index 0000000..f0ae5d9 --- /dev/null +++ b/product-demo/assets/ui/points.json @@ -0,0 +1,102 @@ +{ +"a1-batch": [ +1000, +500 +], +"a2-layout": [ +1000, +500 +], +"b1-layout-story": [ +200, +177 +], +"b2-stacked-a": [ +200, +177 +], +"b3-stacked-b": [ +200, +177 +], +"c1-dialog": [ +1078, +462 +], +"c2-approved": [ +947, +644 +], +"d1-search": [ +1000, +500 +], +"d2-dark": [ +148, +177 +], +"d4-dark-golden": [ +148, +177 +], +"d5-dark-current": [ +148, +177 +], +"e2-box": [ +1514, +677 +], +"f1-note-typed": [ +800, +450 +], +"f2-note-hover": [ +1533, +565 +], +"f3-note-added": [ +1533, +565 +], +"g1-sent": [ +1504, +104 +], +"h1-newer": [ +1000, +500 +], +"h2-replied": [ +461, +132 +], +"i4-resolved": [ +648, +573 +], +"j1-batch": [ +1000, +500 +], +"k1-dialog": [ +1385, +612 +], +"k2-all-approved": [ +948, +644 +], +"m2-hosted-batch": [ +1000, +500 +], +"m1-github-commits": [ +1000, +500 +], +"m1b-github-status": [ +454, +384 +] +} diff --git a/product-demo/build.mjs b/product-demo/build.mjs new file mode 100644 index 0000000..18c06f2 --- /dev/null +++ b/product-demo/build.mjs @@ -0,0 +1,171 @@ +// Generates index.html, compositions/captions.html and the subtitle files from SCRIPT.md, scenes.json and the voiceover. +// SCRIPT.md owns the words, the voiceover's timing file owns when they happen, compositions/.html owns the pictures. +import { readFileSync, writeFileSync } from "node:fs"; + +const AUDIO = "assets/narration.mp3"; +const TIMING = "assets/narration-timing.json"; +const LEAD_IN = 0.08; // A scene's picture arrives just before its first word. + +const { scenes } = JSON.parse(readFileSync("scenes.json", "utf8")); +const lines = readFileSync("SCRIPT.md", "utf8").split("\n").map((l) => l.trim()); +const narration = lines.filter((l) => l && !l.startsWith("#") && !l.startsWith("**")); +if (narration.length !== scenes.length) { + throw new Error(`SCRIPT.md has ${narration.length} paragraphs, scenes.json has ${scenes.length}`); +} + +const plain = (w) => w.toLowerCase().replace(/[^a-z0-9]/g, ""); +// Each word of the script takes its time from what the voice said, and a difference is an error. +function fromVoice() { + const SPOKEN_AS = { "1": ["one"], "0": ["zero"], "13": ["thirteen"], twentyfive: ["25"] }; + const spoken = JSON.parse(readFileSync(TIMING, "utf8")) + .segments.flatMap((s) => s.words) + .filter((w) => w.text.trim()) + .map((w) => ({ word: plain(w.text), start: w.start_time, end: w.end_time })); + let cursor = 0; + const timeWord = (text) => { + const options = [[plain(text)], ...(SPOKEN_AS[plain(text)] ? [SPOKEN_AS[plain(text)]] : [])]; + const expected = options.find((o) => o.every((t, i) => spoken[cursor + i]?.word === t)); + if (!expected) throw new Error(`SCRIPT.md says "${text}", but the voiceover says "${spoken[cursor]?.word}" at ${spoken[cursor]?.start} s.`); + const first = spoken[cursor], last = spoken[cursor + expected.length - 1]; + cursor += expected.length; + return { text, start: first.start, end: last.end }; + }; + const words = narration.map((paragraph) => paragraph.split(/\s+/).map(timeWord)); + if (cursor !== spoken.length) throw new Error(`The voiceover goes on after SCRIPT.md ends, at "${spoken[cursor].word}".`); + return words; +} + +const wordsByScene = fromVoice(); + +const timed = scenes.map((s, i) => { + const start = i === 0 ? 0 : wordsByScene[i][0].start - LEAD_IN; + const end = i + 1 < scenes.length ? wordsByScene[i + 1][0].start - LEAD_IN : wordsByScene[i].at(-1).end + (s.hold ?? 0.35); + return { id: s.id, start: +start.toFixed(3), dur: +(end - start).toFixed(3), words: wordsByScene[i], text: narration[i] }; +}); +const total = +(timed.at(-1).start + timed.at(-1).dur).toFixed(3); + +const MAX_CHARS = 58; +const MIN_CLAUSE = 24; +function groupsOf(text) { + // A short clause joins the next one when they fit together, so "Now," never stands alone. + const clauses = []; + for (const sentence of text.split(/(?<=[.?!])\s+/)) { + const parts = sentence.split(/(?<=[,:])\s+/); + let line = ""; + for (const part of parts) { + if (line && (line.length < MIN_CLAUSE || (part.length < MIN_CLAUSE && !line.endsWith(":")))) line += " " + part; + else { + if (line) clauses.push(line); + line = part; + } + } + clauses.push(line); + } + // A clause that is still too long is split into even lines at word boundaries. + return clauses.flatMap((clause) => { + if (clause.length <= MAX_CHARS) return [clause]; + const n = Math.ceil(clause.length / MAX_CHARS); + const target = clause.length / n; + const lines = []; + let line = ""; + for (const word of clause.split(" ")) { + if (line && lines.length < n - 1 && line.length + word.length / 2 >= target) { + lines.push(line); + line = word; + } else line = line ? `${line} ${word}` : word; + } + lines.push(line); + return lines; + }); +} + +// A subtitle opens on its first word and stays until the next one opens, unless the voice pauses for long. +const MAX_LINGER = 0.5; +const flat = timed.flatMap((s) => groupsOf(s.text).reduce((out, text) => { + const count = text.split(" ").length, from = out.used; + out.groups.push({ text, first: s.words[from], last: s.words[from + count - 1] }); + out.used += count; + return out; +}, { groups: [], used: 0 }).groups); +const captions = flat.map((g, i) => { + const next = flat[i + 1]?.first.start ?? Infinity; + const until = Math.min(next, g.last.end + MAX_LINGER); + return { text: g.text, start: +g.first.start.toFixed(3), duration: +(until - g.first.start).toFixed(3) }; +}); + +const esc = (t) => t.replace(/&/g, "&").replace(/ `
`, + ) + .join("\n"); +const durations = Object.fromEntries(timed.map((s) => [s.id, s.dur])); +// What each scene's words are, at what time within the scene, so a beat can land on the word that names it. +const spokenInScene = Object.fromEntries(timed.map((s) => [s.id, s.words.map((w) => [plain(w.text), +(w.start - s.start).toFixed(3), +(w.end - s.start).toFixed(3)])])); + +writeFileSync( + "index.html", + ` + + + + + + + + + + + +
+${hosts} +
+ +
+ + + +`, +); + +writeFileSync( + "compositions/captions.html", + ` +`, +); + +const stamp = (t, sep) => { + const ms = Math.round(t * 1000); + const p = (n, w = 2) => String(n).padStart(w, "0"); + return `${p(Math.floor(ms / 3600000))}:${p(Math.floor(ms / 60000) % 60)}:${p(Math.floor(ms / 1000) % 60)}${sep}${p(ms % 1000, 3)}`; +}; +const cues = (sep) => captions.map((c) => `${stamp(c.start, sep)} --> ${stamp(c.start + c.duration, sep)}\n${c.text}\n`); +writeFileSync("subtitles.srt", cues(",").map((c, i) => `${i + 1}\n${c}`).join("\n")); +writeFileSync("subtitles.vtt", `WEBVTT\n\n${cues(".").join("\n")}`); + +for (const s of timed) console.log(`${s.id.padEnd(10)} ${s.start.toFixed(2).padStart(7)} ${s.dur.toFixed(2).padStart(6)} s`); +const words = narration.join(" ").split(/\s+/).length; +console.log(`${scenes.length} scenes, ${captions.length} captions, ${words} words, ${total.toFixed(1)} s (${((words / total) * 60).toFixed(0)} wpm)`); diff --git a/product-demo/compositions/approve.html b/product-demo/compositions/approve.html new file mode 100644 index 0000000..41127c4 --- /dev/null +++ b/product-demo/compositions/approve.html @@ -0,0 +1,21 @@ + diff --git a/product-demo/compositions/ask.html b/product-demo/compositions/ask.html new file mode 100644 index 0000000..5d7eeba --- /dev/null +++ b/product-demo/compositions/ask.html @@ -0,0 +1,31 @@ + diff --git a/product-demo/compositions/batch.html b/product-demo/compositions/batch.html new file mode 100644 index 0000000..a73b3ac --- /dev/null +++ b/product-demo/compositions/batch.html @@ -0,0 +1,20 @@ + diff --git a/product-demo/compositions/captions.html b/product-demo/compositions/captions.html new file mode 100644 index 0000000..ce8b0c2 --- /dev/null +++ b/product-demo/compositions/captions.html @@ -0,0 +1,71 @@ + diff --git a/product-demo/compositions/cloud.html b/product-demo/compositions/cloud.html new file mode 100644 index 0000000..bbebdf8 --- /dev/null +++ b/product-demo/compositions/cloud.html @@ -0,0 +1,26 @@ + diff --git a/product-demo/compositions/dark.html b/product-demo/compositions/dark.html new file mode 100644 index 0000000..2acbc8b --- /dev/null +++ b/product-demo/compositions/dark.html @@ -0,0 +1,29 @@ + diff --git a/product-demo/compositions/decide.html b/product-demo/compositions/decide.html new file mode 100644 index 0000000..d2b47d1 --- /dev/null +++ b/product-demo/compositions/decide.html @@ -0,0 +1,30 @@ + diff --git a/product-demo/compositions/end.html b/product-demo/compositions/end.html new file mode 100644 index 0000000..5946796 --- /dev/null +++ b/product-demo/compositions/end.html @@ -0,0 +1,28 @@ + diff --git a/product-demo/compositions/final.html b/product-demo/compositions/final.html new file mode 100644 index 0000000..7df6d04 --- /dev/null +++ b/product-demo/compositions/final.html @@ -0,0 +1,30 @@ + diff --git a/product-demo/compositions/fix.html b/product-demo/compositions/fix.html new file mode 100644 index 0000000..36497dd --- /dev/null +++ b/product-demo/compositions/fix.html @@ -0,0 +1,26 @@ + diff --git a/product-demo/compositions/flip.html b/product-demo/compositions/flip.html new file mode 100644 index 0000000..a314073 --- /dev/null +++ b/product-demo/compositions/flip.html @@ -0,0 +1,27 @@ + diff --git a/product-demo/compositions/json.html b/product-demo/compositions/json.html new file mode 100644 index 0000000..20f5714 --- /dev/null +++ b/product-demo/compositions/json.html @@ -0,0 +1,34 @@ + diff --git a/product-demo/compositions/layout.html b/product-demo/compositions/layout.html new file mode 100644 index 0000000..eb9a4b5 --- /dev/null +++ b/product-demo/compositions/layout.html @@ -0,0 +1,24 @@ + diff --git a/product-demo/compositions/note.html b/product-demo/compositions/note.html new file mode 100644 index 0000000..2b43930 --- /dev/null +++ b/product-demo/compositions/note.html @@ -0,0 +1,38 @@ + diff --git a/product-demo/compositions/received.html b/product-demo/compositions/received.html new file mode 100644 index 0000000..8ecad24 --- /dev/null +++ b/product-demo/compositions/received.html @@ -0,0 +1,32 @@ + diff --git a/product-demo/compositions/render.html b/product-demo/compositions/render.html new file mode 100644 index 0000000..6a3a11b --- /dev/null +++ b/product-demo/compositions/render.html @@ -0,0 +1,32 @@ + diff --git a/product-demo/compositions/verify.html b/product-demo/compositions/verify.html new file mode 100644 index 0000000..d59def8 --- /dev/null +++ b/product-demo/compositions/verify.html @@ -0,0 +1,29 @@ + diff --git a/product-demo/hyperframes.json b/product-demo/hyperframes.json new file mode 100644 index 0000000..dc9ec2e --- /dev/null +++ b/product-demo/hyperframes.json @@ -0,0 +1,12 @@ +{ + "$schema": "https://hyperframes.heygen.com/schema/hyperframes.json", + "registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry", + "paths": { + "blocks": "compositions", + "components": "compositions/components", + "assets": "assets" + }, + "media": { + "autoProxy": true + } +} diff --git a/product-demo/index.html b/product-demo/index.html new file mode 100644 index 0000000..963370b --- /dev/null +++ b/product-demo/index.html @@ -0,0 +1,37 @@ + + + + + + + + + + + + +
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ +
+ + + diff --git a/product-demo/meta.json b/product-demo/meta.json new file mode 100644 index 0000000..3106b1e --- /dev/null +++ b/product-demo/meta.json @@ -0,0 +1,5 @@ +{ + "id": "selfie-product-demo", + "name": "selfie-product-demo", + "createdAt": "2026-11-04T23:30:00.000Z" +} diff --git a/product-demo/package-lock.json b/product-demo/package-lock.json new file mode 100644 index 0000000..0a80c29 --- /dev/null +++ b/product-demo/package-lock.json @@ -0,0 +1,2049 @@ +{ + "name": "selfie-product-demo", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "selfie-product-demo", + "devDependencies": { + "hyperframes": "0.9.9" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@hono/node-server": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-2.1.6.tgz", + "integrity": "sha512-p/KH9MnfNcsm3sR6x4lNgm5j+QN4Et8srKredl9DZfXiyZ1UQ3aQ2YVpFLbqktYN5JI+DB+VZFKM2PqE5hLKjQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "peerDependencies": { + "hono": "^4 || ^5.0.0-0" + } + }, + "node_modules/@img/colour": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", + "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.3.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@puppeteer/browsers": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@puppeteer/browsers/-/browsers-3.2.6.tgz", + "integrity": "sha512-MJP29gVoHznmqmwhH1tlZqzfeQ3aylTpSSa6WKZXtkwYUH02i9imBmWGTyZrbnAou5paX78SE4jMAUqnEPf8ug==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "modern-tar": "^0.8.6", + "yargs": "^18.0.0" + }, + "bin": { + "browsers": "lib/main-cli.js" + }, + "engines": { + "node": ">=22.12.0" + }, + "peerDependencies": { + "proxy-agent": ">=8.0.1", + "yauzl": "^2.10.0 || ^3.4.0" + }, + "peerDependenciesMeta": { + "proxy-agent": { + "optional": true + }, + "yauzl": { + "optional": true + } + } + }, + "node_modules/@swc/helpers": { + "version": "0.5.23", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", + "integrity": "sha512-5lSsMOTXURePglDfvuAQUqkGek9Hg2kksOYay2m0+XR++b2NWYL/4sWyuvVBIs8oKnJaxkdi9whaL/sqN13afw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/acorn": { + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/adm-zip": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.6.1.tgz", + "integrity": "sha512-Xwrja8nx9e5o2N1my4DsKCeKpdrnACyr1wtbPxBDgGzKzKyE9kRtBFA8mWldI+RVlD7CBZNWY/wQ2+ydwOR6kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0" + } + }, + "node_modules/ansi-regex": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.5.0.tgz", + "integrity": "sha512-zhvbbbdt1kbw7EJ9OQoxmQcuaNR9P5kTj7e/YdmOSU10aHWbycofJxA6jKfoEy3vlLdctOF9ZHaxLtmSm998wQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/brotli": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/brotli/-/brotli-1.3.3.tgz", + "integrity": "sha512-oTKjJdShmDuGW94SyyaoQvAjf30dZaHnjJ8uAF+u2/vGJkJbJPJAT1gDiOJP5v1Zb6f9KEyW/1HpuaWIXtGHPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.1.2" + } + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/chromium-bidi": { + "version": "157.0.8090-0", + "resolved": "https://registry.npmjs.org/chromium-bidi/-/chromium-bidi-157.0.8090-0.tgz", + "integrity": "sha512-KJw7IQgqXwxpJgJlEIPg5mr2ZoqA+jkTRtc5EABI7sUJ3bu3cpI7NuEBoj4Gn1AsF78CaT9So5WQWspK3/wYag==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "mitt": "^3.0.1", + "zod": "^4.6.5" + }, + "engines": { + "node": ">=20.19.0 <22.0.0 || >=22.12.0" + }, + "peerDependencies": { + "devtools-protocol": "*" + } + }, + "node_modules/citty": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/citty/-/citty-0.2.2.tgz", + "integrity": "sha512-+6vJA3L98yv+IdfKGZHBNiGW5KHn22e/JwID0Strsz8h4S/csAu/OuICwxrg44k5MRiZHWIo8XXuJgQTriRP4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", + "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0", + "wrap-ansi": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/compare-versions": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", + "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", + "dev": true, + "license": "MIT" + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/devtools-protocol": { + "version": "0.0.1704951", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1704951.tgz", + "integrity": "sha512-GQhQOs9u/Mvm1zMtJIH872JS4AskuZdUSsPNPk4ChahtWAUqgz6atEtIsMt+nO7MqF1Ic4HxII/k8Cu36JivNg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/dfa": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/dfa/-/dfa-1.2.0.tgz", + "integrity": "sha512-ED3jP8saaweFTjeGX8HQPjeC1YYyZs98jGNZx6IiBvxW7JG5v492kamAQB3m2wop07CvU/RQmzcKr6bgcC5D/Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", + "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fontkit": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/fontkit/-/fontkit-2.0.4.tgz", + "integrity": "sha512-syetQadaUEDNdxdugga9CpEYVaQIxOwk7GlwZWWZ19//qW4zE5bknOKeMBDYAASwnpaSHKJITRLMF9m1fp3s6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@swc/helpers": "^0.5.12", + "brotli": "^1.3.2", + "clone": "^2.1.2", + "dfa": "^1.2.0", + "fast-deep-equal": "^3.1.3", + "restructure": "^3.0.0", + "tiny-inflate": "^1.0.3", + "unicode-properties": "^1.4.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.7.0.tgz", + "integrity": "sha512-XjH1AECxf0giL2V1aU8vKyRR2ppRUb5c0EvT7zuJTokQ74bNo52zOtghqdWIqrhUD79fo3x0WfKZdOqxF6LG1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/giget": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/giget/-/giget-3.3.1.tgz", + "integrity": "sha512-r+mvuDjrjMpsdw46Kmeydb8bdHm7wOKw8wNBtTndkjbPjgAp5oUJUxRE76wZFknxIPokfWvep2qSXK37aXE6zg==", + "dev": true, + "license": "MIT", + "bin": { + "giget": "dist/cli.mjs" + } + }, + "node_modules/hono": { + "version": "4.13.16", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.13.16.tgz", + "integrity": "sha512-AOLOEhvrV+iH92xXCnFr/5+3KjPKcI3xP63XJtX5spAnkznPdHijlIsUTpi7/9EhWqrTCekw5c5zh1K+YsVw7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/hyperframes": { + "version": "0.9.9", + "resolved": "https://registry.npmjs.org/hyperframes/-/hyperframes-0.9.9.tgz", + "integrity": "sha512-JpNQ0dY9X5FPbAkftSopz/P3LMCc+UOPUnNDl0LcFW32U/bOmjlQiOPQoOSEyHCiYi8N5g47yGyoJNjEjRoACw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@hono/node-server": "^2.0.5", + "@puppeteer/browsers": "^3.2.2", + "acorn": "^8.17.0", + "adm-zip": "^0.6.0", + "citty": "^0.2.1", + "compare-versions": "^6.1.1", + "css-tree": "^3.2.1", + "debug": "^4.4.0", + "esbuild": "^0.28.2", + "fontkit": "^2.0.4", + "giget": "^3.2.0", + "hono": "^4.0.0", + "ignore": "^5.3.2", + "open": "^10.0.0", + "postcss": "^8.5.8", + "puppeteer-core": "^25.10.0", + "undici": "7.25.0", + "yauzl": "^3.4.0" + }, + "bin": { + "hyperframes": "bin/hyperframes.mjs", + "hyperframes-localize-fonts": "bin/hyperframes-localize-fonts.mjs" + }, + "engines": { + "node": ">=22" + }, + "optionalDependencies": { + "sharp": "^0.35.0" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "dev": true, + "license": "MIT" + }, + "node_modules/modern-tar": { + "version": "0.8.6", + "resolved": "https://registry.npmjs.org/modern-tar/-/modern-tar-0.8.6.tgz", + "integrity": "sha512-LYSks0tNVqbtez/9OB0YFP6qLRSqkdAxJWOQpVaR67lNTbj2zNLyYpowkRMia5lXLCZKzxwoxkxH2MbbBR2aEg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pako": { + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/pako/-/pako-0.2.9.tgz", + "integrity": "sha512-NUcwaKxUxWrZLpDG+z/xZaCgQITkA/Dv4V/T6bw7VON6l1Xz/VnrBqrYjZQ12TamKHzITTfOEIYUj48y2KXImA==", + "dev": true, + "license": "MIT" + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.32", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.32.tgz", + "integrity": "sha512-LJlKCCAUFKOuZxipUdYw1NzIITEsPcgyOG9LkvRJvuKj/bRzbuRY0ARXvL0pWGpsZvn8jku+LMep2eU0seHp8g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.20", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/puppeteer-core": { + "version": "25.16.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-25.16.0.tgz", + "integrity": "sha512-VA5Pf54N8shfopNkZZXCdiiwfnnY8ZJYAMqFQRhlH5mGUhfwf53Ar770GPtdpTDLUqvQeYpIBtC9/G2Jij2IJw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@puppeteer/browsers": "3.2.6", + "chromium-bidi": "157.0.8090-0", + "devtools-protocol": "0.0.1704951", + "typed-query-selector": "^2.12.3", + "webdriver-bidi-protocol": "1.0.1", + "ws": "^8.22.0" + }, + "engines": { + "node": ">=22.12.0" + } + }, + "node_modules/restructure": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/restructure/-/restructure-3.0.2.tgz", + "integrity": "sha512-gSfoiOEA0VPE6Tukkrr7I0RBdE0s7H1eFCDBk05l1KIQT1UIKNc5JZy6jdyW6eYH3aR3g5b3PuL77rq0hvwtAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/semver": { + "version": "7.8.6", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.6.tgz", + "integrity": "sha512-9lpSSU4Mly3mrtruwLs1eLK6+gzbzqeBFwRNjIjE+9LuDpWdYctrQlQ3ej1Ilgi7gknpsTRnPoLP8DpCHqHIJw==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/sharp": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "@img/colour": "^1.1.0", + "detect-libc": "^2.1.2", + "semver": "^7.8.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } + } + }, + "node_modules/source-map-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/string-width": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.3.0.tgz", + "integrity": "sha512-ZbmZM0JCihQN91dWnxoipT2KOEyHqEyfRXUyjuRhW8b/xnqPDoq4gWEVApTVa9db2wN8mmoikgFBbjh71+cGeQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-east-asian-width": "^1.5.0", + "strip-ansi": "^7.1.2" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/tiny-inflate": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tiny-inflate/-/tiny-inflate-1.0.3.tgz", + "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.9.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.9.1.tgz", + "integrity": "sha512-a4Ux9VKuIMHsOS7ENbEq6hK2ZGYIT27Rtl0E4eD9KqwxhKiQeMZE/9xb3IdYwWaSOyIBQLExhQgXDTYoiCxnWA==", + "dev": true, + "license": "0BSD" + }, + "node_modules/typed-query-selector": { + "version": "2.12.3", + "resolved": "https://registry.npmjs.org/typed-query-selector/-/typed-query-selector-2.12.3.tgz", + "integrity": "sha512-/dtuMZyutHLlrxWhhC1uib00ELUr2dZ8C9B8YzZYsJmeE3V56OxRblrSM2UaF142pcMtak7ehZ6+poWGeehNIg==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.25.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", + "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unicode-properties": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/unicode-properties/-/unicode-properties-1.4.1.tgz", + "integrity": "sha512-CLjCCLQ6UuMxWnbIylkisbRj31qxHPAurvena/0iwSVbQ2G1VY5/HjV0IRabOEbDHlzZlRdCrD4NhB0JtU40Pg==", + "dev": true, + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.0", + "unicode-trie": "^2.0.0" + } + }, + "node_modules/unicode-trie": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/unicode-trie/-/unicode-trie-2.0.0.tgz", + "integrity": "sha512-x7bc76x0bm4prf1VLg79uhAzKw8DVboClSN5VxJuQ+LKDOVEW9CdH+VY7SP+vX7xCYQqzzgQpFqz15zeLvAtZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "pako": "^0.2.5", + "tiny-inflate": "^1.0.0" + } + }, + "node_modules/webdriver-bidi-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/webdriver-bidi-protocol/-/webdriver-bidi-protocol-1.0.1.tgz", + "integrity": "sha512-WXWs/aXF2a6Al1Z2/V/7Gp2oPZOAAmHjJOTmv4KCXRJ122IMj3tSSMc6a4q3K4QOmhQbse/Ahr+oTVJOU2dUiw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", + "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.2.1", + "string-width": "^7.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ws": { + "version": "8.22.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.22.1.tgz", + "integrity": "sha512-oyL6OPYme/CmiFvNuGzeoxCJ4nOrZE2O1FwM9MWM1AsGri6nLXB2pg+lNA8aLeXMSav0J6eIVS1j6vUPzUtK9g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "18.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.2.0.tgz", + "integrity": "sha512-9OpKOLeaoNFecEp7P6iYbzze/5CqWoH7N3SMs/6y1XF4nMvFMspEGZzJ6uFi9MmQwXhyzqYoSEKO3tvA3Z/o2w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^9.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "string-width": "^8.2.1", + "y18n": "^5.0.5", + "yargs-parser": "^22.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yargs-parser": { + "version": "22.0.0", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", + "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=23" + } + }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/zod": { + "version": "4.6.7", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.6.7.tgz", + "integrity": "sha512-g8W2D2kU6a8untc053fX1uVBKzM7yqAxW/3zwk83+6RyF2+SKOuV+c0wBDhUwzV1PaGapmZH+rmcUzPf+3CGwg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/product-demo/package.json b/product-demo/package.json new file mode 100644 index 0000000..71ba9ab --- /dev/null +++ b/product-demo/package.json @@ -0,0 +1,8 @@ +{ + "name": "selfie-product-demo", + "private": true, + "type": "module", + "devDependencies": { + "hyperframes": "0.9.9" + } +} diff --git a/product-demo/scenes.json b/product-demo/scenes.json new file mode 100644 index 0000000..77e37c4 --- /dev/null +++ b/product-demo/scenes.json @@ -0,0 +1,21 @@ +{ + "comment": "One scene per narration paragraph of SCRIPT.md, in order. Timing comes from the voiceover. hold is how long the last scene stays up after its last word.", + "scenes": [ + { "id": "ask" }, + { "id": "render" }, + { "id": "json" }, + { "id": "batch" }, + { "id": "layout" }, + { "id": "flip" }, + { "id": "approve" }, + { "id": "dark" }, + { "id": "note" }, + { "id": "received" }, + { "id": "fix" }, + { "id": "verify" }, + { "id": "decide" }, + { "id": "final" }, + { "id": "cloud" }, + { "id": "end", "hold": 3.5 } + ] +} diff --git a/product-demo/subtitles.srt b/product-demo/subtitles.srt new file mode 100644 index 0000000..f0b0c17 --- /dev/null +++ b/product-demo/subtitles.srt @@ -0,0 +1,215 @@ +1 +00:00:00,220 --> 00:00:03,220 +I asked my coding agent for one small thing: + +2 +00:00:03,220 --> 00:00:05,680 +give the top bar a bit more room. + +3 +00:00:05,700 --> 00:00:08,040 +It changed two lines of CSS. + +4 +00:00:08,100 --> 00:00:09,500 +Looks fine, right? + +5 +00:00:09,560 --> 00:00:12,060 +So the agent runs selfie review. + +6 +00:00:12,060 --> 00:00:14,860 +That renders every story in our Storybook, + +7 +00:00:14,860 --> 00:00:17,800 +124 of them, in a pinned container, + +8 +00:00:17,800 --> 00:00:19,820 +right here on this MacBook Air. + +9 +00:00:19,880 --> 00:00:23,760 +Twenty-five seconds, with no cloud and no bill per image. + +10 +00:00:23,760 --> 00:00:27,300 +What comes back is one JSON document and an exit code, + +11 +00:00:27,300 --> 00:00:29,080 +so any agent can read it. + +12 +00:00:29,080 --> 00:00:31,160 +Exit 1, because that two-line + +13 +00:00:31,160 --> 00:00:34,600 +change touched all 124 stories. + +14 +00:00:34,620 --> 00:00:35,960 +Now it’s my turn. + +15 +00:00:35,960 --> 00:00:39,980 +The selfie ui command opens the same review for a person: + +16 +00:00:40,120 --> 00:00:43,580 +124 changed, and nothing decided yet. + +17 +00:00:43,580 --> 00:00:45,060 +That’s a lot to look at, + +18 +00:00:45,060 --> 00:00:48,500 +but Selfie works out what kind of change each one is. + +19 +00:00:48,640 --> 00:00:51,300 +48 of them only moved or gained spacing, + +20 +00:00:51,300 --> 00:00:53,020 +with nothing new in the image. + +21 +00:00:53,020 --> 00:00:53,940 +Here’s one. + +22 +00:00:53,940 --> 00:00:55,960 +Yellow is spacing that grew. + +23 +00:00:55,960 --> 00:00:58,780 +Press F, and the two images flip in place, + +24 +00:00:58,780 --> 00:01:00,980 +so you can see exactly what moved. + +25 +00:01:00,980 --> 00:01:04,260 +That looks right, so I approve those 48 together. + +26 +00:01:04,260 --> 00:01:06,120 +But what about dark mode? + +27 +00:01:06,240 --> 00:01:08,660 +I search for dark, and there it is: + +28 +00:01:08,700 --> 00:01:11,320 +85,000 changed pixels. + +29 +00:01:11,320 --> 00:01:13,420 +The agent hard-coded a light color, + +30 +00:01:13,420 --> 00:01:15,180 +and the top bar went white. + +31 +00:01:15,180 --> 00:01:17,060 +I don’t have to describe where. + +32 +00:01:17,120 --> 00:01:19,280 +I click the change, write what’s wrong, + +33 +00:01:19,280 --> 00:01:21,740 +and send it to the agent, which has been waiting. + +34 +00:01:21,740 --> 00:01:25,320 +The agent gets my words, the story, the exact box, + +35 +00:01:25,320 --> 00:01:27,140 +and a picture of what I saw. + +36 +00:01:27,140 --> 00:01:28,960 +It puts the theme’s color back, + +37 +00:01:28,960 --> 00:01:31,360 +renders again, and answers my note. + +38 +00:01:31,380 --> 00:01:33,240 +Selfie tells me there’s a newer batch, + +39 +00:01:33,240 --> 00:01:35,680 +and the reply is right there on the story. + +40 +00:01:35,680 --> 00:01:38,440 +The bar is dark again, and 85,000 + +41 +00:01:38,440 --> 00:01:40,980 +changed pixels are down to 13. + +42 +00:01:40,980 --> 00:01:43,460 +I resolve the note, and approve the rest. + +43 +00:01:43,460 --> 00:01:46,780 +The 48 I already approved are simply unchanged now, + +44 +00:01:46,780 --> 00:01:49,340 +because an approval follows your Git history. + +45 +00:01:49,340 --> 00:01:51,920 +The agent commits and reviews once more. + +46 +00:01:51,920 --> 00:01:54,920 +It’s the exact same build, so nothing renders. + +47 +00:01:54,920 --> 00:01:57,600 +A third of a second, and exit 0. + +48 +00:01:57,600 --> 00:01:59,960 +Push it, and Selfie renders in CI + +49 +00:01:59,960 --> 00:02:01,860 +and puts a status on the commit. + +50 +00:02:01,860 --> 00:02:05,300 +Click through, and it’s the same review, in the cloud. + +51 +00:02:05,320 --> 00:02:07,640 +Selfie works with any coding agent. + +52 +00:02:07,640 --> 00:02:08,960 +If you have a Storybook, + +53 +00:02:08,960 --> 00:02:11,600 +start locally today at selfie.dev, + +54 +00:02:11,600 --> 00:02:13,860 +and join the waitlist for the cloud. diff --git a/product-demo/subtitles.vtt b/product-demo/subtitles.vtt new file mode 100644 index 0000000..6eb9ecc --- /dev/null +++ b/product-demo/subtitles.vtt @@ -0,0 +1,163 @@ +WEBVTT + +00:00:00.220 --> 00:00:03.220 +I asked my coding agent for one small thing: + +00:00:03.220 --> 00:00:05.680 +give the top bar a bit more room. + +00:00:05.700 --> 00:00:08.040 +It changed two lines of CSS. + +00:00:08.100 --> 00:00:09.500 +Looks fine, right? + +00:00:09.560 --> 00:00:12.060 +So the agent runs selfie review. + +00:00:12.060 --> 00:00:14.860 +That renders every story in our Storybook, + +00:00:14.860 --> 00:00:17.800 +124 of them, in a pinned container, + +00:00:17.800 --> 00:00:19.820 +right here on this MacBook Air. + +00:00:19.880 --> 00:00:23.760 +Twenty-five seconds, with no cloud and no bill per image. + +00:00:23.760 --> 00:00:27.300 +What comes back is one JSON document and an exit code, + +00:00:27.300 --> 00:00:29.080 +so any agent can read it. + +00:00:29.080 --> 00:00:31.160 +Exit 1, because that two-line + +00:00:31.160 --> 00:00:34.600 +change touched all 124 stories. + +00:00:34.620 --> 00:00:35.960 +Now it’s my turn. + +00:00:35.960 --> 00:00:39.980 +The selfie ui command opens the same review for a person: + +00:00:40.120 --> 00:00:43.580 +124 changed, and nothing decided yet. + +00:00:43.580 --> 00:00:45.060 +That’s a lot to look at, + +00:00:45.060 --> 00:00:48.500 +but Selfie works out what kind of change each one is. + +00:00:48.640 --> 00:00:51.300 +48 of them only moved or gained spacing, + +00:00:51.300 --> 00:00:53.020 +with nothing new in the image. + +00:00:53.020 --> 00:00:53.940 +Here’s one. + +00:00:53.940 --> 00:00:55.960 +Yellow is spacing that grew. + +00:00:55.960 --> 00:00:58.780 +Press F, and the two images flip in place, + +00:00:58.780 --> 00:01:00.980 +so you can see exactly what moved. + +00:01:00.980 --> 00:01:04.260 +That looks right, so I approve those 48 together. + +00:01:04.260 --> 00:01:06.120 +But what about dark mode? + +00:01:06.240 --> 00:01:08.660 +I search for dark, and there it is: + +00:01:08.700 --> 00:01:11.320 +85,000 changed pixels. + +00:01:11.320 --> 00:01:13.420 +The agent hard-coded a light color, + +00:01:13.420 --> 00:01:15.180 +and the top bar went white. + +00:01:15.180 --> 00:01:17.060 +I don’t have to describe where. + +00:01:17.120 --> 00:01:19.280 +I click the change, write what’s wrong, + +00:01:19.280 --> 00:01:21.740 +and send it to the agent, which has been waiting. + +00:01:21.740 --> 00:01:25.320 +The agent gets my words, the story, the exact box, + +00:01:25.320 --> 00:01:27.140 +and a picture of what I saw. + +00:01:27.140 --> 00:01:28.960 +It puts the theme’s color back, + +00:01:28.960 --> 00:01:31.360 +renders again, and answers my note. + +00:01:31.380 --> 00:01:33.240 +Selfie tells me there’s a newer batch, + +00:01:33.240 --> 00:01:35.680 +and the reply is right there on the story. + +00:01:35.680 --> 00:01:38.440 +The bar is dark again, and 85,000 + +00:01:38.440 --> 00:01:40.980 +changed pixels are down to 13. + +00:01:40.980 --> 00:01:43.460 +I resolve the note, and approve the rest. + +00:01:43.460 --> 00:01:46.780 +The 48 I already approved are simply unchanged now, + +00:01:46.780 --> 00:01:49.340 +because an approval follows your Git history. + +00:01:49.340 --> 00:01:51.920 +The agent commits and reviews once more. + +00:01:51.920 --> 00:01:54.920 +It’s the exact same build, so nothing renders. + +00:01:54.920 --> 00:01:57.600 +A third of a second, and exit 0. + +00:01:57.600 --> 00:01:59.960 +Push it, and Selfie renders in CI + +00:01:59.960 --> 00:02:01.860 +and puts a status on the commit. + +00:02:01.860 --> 00:02:05.300 +Click through, and it’s the same review, in the cloud. + +00:02:05.320 --> 00:02:07.640 +Selfie works with any coding agent. + +00:02:07.640 --> 00:02:08.960 +If you have a Storybook, + +00:02:08.960 --> 00:02:11.600 +start locally today at selfie.dev, + +00:02:11.600 --> 00:02:13.860 +and join the waitlist for the cloud.