Shoot
shoot takes stills of real product pages and scripted flows, framed like Abdout's
14" MacBook at Chrome 125%, and its sim films the same flow into a branded tutorial.
Say it in prose: "shoot add-student in both languages", "publish the bulk flows".
Raw screen takes are record; cuts and reels are edit.
One name, everywhere
A flow is <verb>-<entity>, singular kebab-case (add-student, bulk-teacher). That one
word names the flow file, its local folder, its files on the CDN and its manifest ids.
hogwarts is the internal name in every path; balqalam is only the public brand
drawn on screen.
| Asset | Local (~/media/<repo>/) | Manifest id | CDN (cdn.databayt.org/hogwarts/) |
|---|---|---|---|
| page still | page/<slug>-<locale>.png | page/<slug>-<locale> | <slug>/page.avif (home → page.avif) |
| flow still | <flow>/NN-<step>-<locale>.png | <flow>/<step>-<locale> | <block>/<name>/<step>.avif (+ @2x, .webp) |
| phone still | <flow>/iphone-16/NN-<step>-<locale>.png | <flow>/iphone-16/<step>-<locale> | <block>/<name>/iphone-16/<step>.avif |
| tutorial | <flow>/<flow>-<locale>.mp4 (+ .av1.mp4, .poster.webp, .vtt) | <flow>/video-<locale> | <block>/<name>.mp4 (+ same sidecars) |
| reel | <flow>/<flow>-<locale>.reel.mp4 | <flow>/reel-<locale> | <block>/<name>/reel.mp4 |
| support clip / story cut | <flow>/<flow>-<clip>-<locale>.* | <flow>/<clip>-<locale> | <block>/<name>/<clip>.mp4 |
| block media (ad, block reel) | — | your id | <block>/<file>.mp4 |
| raw take | <flow>/raw/<flow>[-<part>]-<locale>.mov | — | — |
- Grouped by block, short names. A flow's
export const cdn = "<block>/<name>"is its home: add-student →students/addgiveshogwarts/students/add.mp4andhogwarts/students/add/list.avif; bulk-student →students/bulk; add-teacher →teachers/add. Block-wide media such ashogwarts/students/marketing.mp4orhogwarts/students/reel.mp4sits beside the flows. - Arabic is bare, English adds
-en(list.avif,list-en.avif). The narrowest width is bare and wider ones add@2x(list.avif1600 px,list@2x.avif2400 px). - Same name = overwrite. No
-v2/-final. TheNN-order prefix stays in the local file and becomes the manifest entry'sorder, so the docs list stills in shot order. - Keys are stable: a re-render overwrites the object and invalidates CloudFront for that key;
browsers cache an hour, the edge a year, and manifest URLs carry
?v=<sha8>so pages pick up new bytes at once. ~/mediasits outside every repo and mirrors to Drive (record.sh sync). Nothing is captured into a repo'spublic/.
Publish
node ~/.claude/skills/shoot/scripts/shoot.mjs --flow add-student --lang ar,en --publish
node ~/.claude/skills/shoot/scripts/publish.mjs --flow add-student [--only stills|video] [--dry-run]
node ~/.claude/skills/shoot/scripts/publish.mjs --flow pageEach asset is uploaded, HEAD-verified and upserted into the product's
media-manifest.json; pages render it by id — <FlowMedia flow="add-student" />,
<Shot id="add-student/created-ar" />, <TutorialVideo id="add-student/video-ar" /> — never
by URL. Publishing also stamps the repo sha into ~/media/manifest.json, so
record.sh stale flags a still whose source (export const block / paths) has changed.