Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 43 additions & 1 deletion promo/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,50 @@ Remotion project for the marketing videos. Compositions (`src/Root.tsx`):
|---|---|---|
| `Demo` | README / landing hero demo (~40 s, 1600×900) | `src/demo/`, footage `public/demo/` |
| `Trailer` | 30 s launch trailer | `src/Trailer.tsx`, footage `public/footage/` |
| `ImportClip` | import-focused social clip | `src/ImportClip.tsx` |
| `DeviceSync` | 3D laptop + phone, one session live on both (~18 s) | `src/devices/`, devices + scene in `../shared/devices/` (also used by the landing), footage `public/devices/` |
| `ImportClip` | import from `source`: `termius`, `mobaxterm`, `putty`, `securecrt`, `zoc`, `csv`, or `supercut` (all six) | `src/social/`, takes `public/social/import-*` |
| `FirstRun` | no account: chooser → add a host → connect | takes `cold1`, `cold2` |
| `KillRestore` | kill -9 mid-build, relaunch, workspace and build still there | take `kill` |
| `Broadcast` | twelve hosts, broadcast on, one `apt upgrade` everywhere | take `broadcast` (1920×1200) |
| `ThemeSpeedrun` | the six built-in themes from the side panel | take `themes` |
| `ResumeTransfer` | 4 GB folder upload, link drops, transfer resumes | take `resume` |
| `SyncClip` | the landing's E2EE sync scene; `store`: `cloud` (Pro) or `byo` | `../shared/devices/SyncScene.tsx`, stills `public/sync/` |

## Tweet videos

`social.json` maps tweet ids from `../social/queue.json` to a composition and its props.

```bash
npm ci --cache .npm-cache # once per fresh copy
npm run render:all # renders what is missing in out/social/<id>.mp4 (--gl=angle)
npm run render:all -- --only 1,23 # re-render some
npm run render:all -- --force # re-render everything
```

It prints the list in posting order (and the video tweets that have no composition yet) and writes `out/social/review.html`: each video beside its tweet text. To approve one, copy it to `social/media/<id>.mp4`, push, then label the tweet's "Approve tweet #N" issue `approved`.

Clips frame the footage on the 2.5D laptop (`FirstRun`, `KillRestore`) or as a floating app window (the rest); the device and camera shots live in `src/social/FootageClip.tsx`. Cuts and captions are relative to the marks in `src/social/takes/*.json`, so a retake keeps the edit.

### Retaking the tweet footage

Everything runs in one capture container, `promo-social`, on its own network with a 12-host Debian fleet (`promo-s-web-01` … `promo-s-worker-02`, `deploy`/`deploy`; an older Debian release so `apt upgrade` has work to do).

```bash
cd capture/social
WT=<voltius worktree at the release> BIN=<dir with its debug binary> ./setup.sh
./take-import.sh termius mobaxterm putty securecrt zoc csv # import-*
./vault-fleet.sh && ./resume.sh rec # resume (≈8 min of real transfer)
./kill.sh # kill
./themes.sh # themes
FLAT=1 ./vault-fleet.sh && WT=… BIN=… ./broadcast.sh # broadcast, recreates the fleet first
```

`cold1.mjs`/`cold2.mjs` need a never-launched app: record them first on a fresh container (`./rec.sh cold1.mjs cold1`, `./rec.sh cold2.mjs cold2`, then `./pull.sh cold1 cold2`). Every take script ends with `pull.sh`, which copies the recording to `public/social/` and writes `src/social/takes/<take>.json`.

- PuTTY, SecureCRT and ZOC import from real config files `sources.mjs` writes where the Linux app looks. Termius and MobaXterm can't run here: `stubs.mjs` replaces only their native read with records in their real formats; the parsers and the import UI are the app's own.
- The link drop is `docker pause` on the host: a blackhole like a dead Wi-Fi link, while the name still resolves (`docker network disconnect` breaks DNS and the app gives up).
- Never `pkill -f` a pattern that appears in your own command line; kill the app with `pkill -x voltius`.
- Half-scale stills here: `node stills.mjs <Composition> <frame>…` (`PROPS='{"source":"putty"}'` for props). They use `--gl=swangle`; other software GL paths draw the 3D devices with the wrong faces in front.

## Retaking the README demo

Expand Down
15 changes: 15 additions & 0 deletions promo/capture/container.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Sourced by the setup scripts. capture_container NAME HOSTNAME SCREEN WIN NET WT BIN
# A tauri-mcp container running the voltius worktree WT (vite) with the debug binary in BIN, tauri-driver on 4444.
capture_container() {
local name=$1 host=$2 screen=$3 win=$4 net=$5 wt=$6 bin=$7
docker ps --format '{{.Names}}' | grep -qx "$name" && return 0
docker rm -f "$name" >/dev/null 2>&1 || true
docker run -d --name "$name" --hostname "$host" --network "$net" --security-opt seccomp=unconfined \
-e WEBKIT_DISABLE_COMPOSITING_MODE=1 -e VOLTIUS_KEYCHAIN_NS="$name" -e WD_APP=/promobin/voltius -e WD_WIN="$win" \
-v "$wt":/app -v "$bin":/promobin:ro tauri-mcp \
bash -c "cd /app && (node node_modules/vite/bin/vite.js > /tmp/vite.log 2>&1 &) && keyctl session - xvfb-run --auto-servernum -s '-screen 0 ${screen}x24' sleep infinity" >/dev/null
until docker exec -w / "$name" sh -c 'ls -d /tmp/xvfb-run.* && grep -q "ready in" /tmp/vite.log' >/dev/null 2>&1; do sleep 1; done
local x; x=$(docker exec -w / "$name" sh -c 'ls -d /tmp/xvfb-run.*')/Xauthority
docker exec -w / "$name" sh -c "mkdir -p /tmp/work && echo ':99 $x' > /tmp/work/disp"
docker exec -d -w / "$name" sh -c "DISPLAY=:99 XAUTHORITY=$x tauri-driver --port 4444 --native-port 4446 > /tmp/driver.log 2>&1"
}
14 changes: 4 additions & 10 deletions promo/capture/demo/build-data.mjs
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
import { readFileSync, writeFileSync } from 'fs';
const dir = new URL('../../public/demo/', import.meta.url).pathname;
const out = {};
for (const k of ['A', 'B']) {
const start = +readFileSync(dir + k + '.start', 'utf8').trim();
const rel = (e) => +(+e - start).toFixed(3);
const cursor = readFileSync(dir + k + '.cursor', 'utf8').trim().split('\n').map((l) => { const [e, kind, x, y] = l.split(' '); return [rel(e), kind, +x, +y]; });
const marks = Object.fromEntries(readFileSync(dir + k + '.marks', 'utf8').trim().split('\n').map((l) => { const [e, n] = l.split(' '); return [n, rel(e)]; }));
out[k] = { cursor, marks };
}
import { writeFileSync } from 'fs';
import { takeData } from '../take-data.mjs';
const dir = new URL('../../public/demo', import.meta.url).pathname;
const out = { A: takeData(dir, 'A'), B: takeData(dir, 'B') };
writeFileSync(new URL('../../src/demo/data.json', import.meta.url), JSON.stringify(out));
console.log(JSON.stringify({ A: out.A.marks, B: out.B.marks }), out.A.cursor.length, out.B.cursor.length);
11 changes: 11 additions & 0 deletions promo/capture/demo/mouse.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,13 @@ export async function click(x, y, ms) {
await down(); await sleep(90); await up();
}

export async function dblclick(x, y, ms) {
if (x != null) await moveTo(x, y, ms);
await sleep(120);
await down(); await sleep(60); await up(); await sleep(90);
await down(); await sleep(60); await up();
}

// Press, nudge past the app's drag threshold, glide, release.
export async function dragTo([x1, y1], [x2, y2], ms = 1000) {
await moveTo(x1, y1);
Expand Down Expand Up @@ -83,6 +90,10 @@ export async function clickOn(finder, args = [], { fx = 0.5, fy = 0.5, ms } = {}
return r;
}
export const clickText = (t, o) => clickOn(byText, [t], o);
export async function dblclickText(t) {
const r = await waitRect(byText, [t]);
await dblclick(r[0], r[1]);
}
export const clickCss = (css, o) => clickOn(byCss, [css], o);

export async function waitFor(cond, args = [], ms = 15000) {
Expand Down
2 changes: 1 addition & 1 deletion promo/capture/demo/rec.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ read D X < disp
export DISPLAY=$D XAUTHORITY=$X
: > cursor.txt; : > marks.txt
# -copyts keeps x11grab's wall-clock pts so cursor.txt/marks.txt epochs line up with frames.
ffmpeg -y -loglevel error -f x11grab -draw_mouse 0 -video_size 1280x800 -framerate 30 -i "$D+0,0" -copyts -c:v libx264 -preset ultrafast -qp 0 -f nut raw.nut &
ffmpeg -y -loglevel error -f x11grab -draw_mouse 0 -video_size ${SIZE:-1280x800} -framerate 30 -i "$D+0,0" -copyts -c:v libx264 -preset ultrafast -qp 0 -f nut raw.nut &
FF=$!
trap 'kill -INT $FF 2>/dev/null || true' EXIT
sleep 1.0
Expand Down
11 changes: 2 additions & 9 deletions promo/capture/devices/setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# WT = voltius worktree (platform.patch gets applied), BIN = dir with a voltius debug binary named `voltius`.
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
. "$HERE/../container.sh"
: "${WT:?voltius worktree}" "${BIN:?dir with voltius binary}"
NET=voltius-headless_voltius-test
ACCOUNT="http://promo-sync-server:8080 demo@voltius.app promo-demo-2026"
Expand All @@ -24,16 +25,8 @@ WEB01=$(docker inspect promo-web-01 --format '{{range .NetworkSettings.Networks}

device() { # name hostname screen win
if ! docker ps --format '{{.Names}}' | grep -qx "promo-$1"; then
docker rm -f "promo-$1" >/dev/null 2>&1 || true
docker run -d --name "promo-$1" --hostname "$2" --network $NET --security-opt seccomp=unconfined \
-e WEBKIT_DISABLE_COMPOSITING_MODE=1 -e VOLTIUS_KEYCHAIN_NS="promo$1" -e WD_APP=/promobin/voltius -e WD_WIN="$4" \
-v "$WT":/app -v "$BIN":/promobin:ro tauri-mcp \
bash -c "cd /app && (node node_modules/vite/bin/vite.js > /tmp/vite.log 2>&1 &) && keyctl session - xvfb-run --auto-servernum -s '-screen 0 ${3}x24' sleep infinity" >/dev/null
capture_container "promo-$1" "$2" "$3" "$4" $NET "$WT" "$BIN"
docker exec -u root -w / "promo-$1" sh -c "echo '$WEB01 web-01.acme.io' >> /etc/hosts"
until docker exec -w / "promo-$1" sh -c 'ls -d /tmp/xvfb-run.* && grep -q "ready in" /tmp/vite.log' >/dev/null 2>&1; do sleep 1; done
local X; X=$(docker exec -w / "promo-$1" sh -c 'ls -d /tmp/xvfb-run.*')/Xauthority
docker exec -w / "promo-$1" sh -c "mkdir -p /tmp/work && echo ':99 $X' > /tmp/work/disp"
docker exec -d -w / "promo-$1" sh -c "DISPLAY=:99 XAUTHORITY=$X tauri-driver --port 4444 --native-port 4446 > /tmp/driver.log 2>&1"
fi
docker cp "$HERE/../demo/wd.mjs" "promo-$1":/tmp/work/ >/dev/null
docker cp "$HERE/." "promo-$1":/tmp/work/ >/dev/null
Expand Down
31 changes: 31 additions & 0 deletions promo/capture/social/broadcast.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { existsSync, rmSync } from 'fs';
import { ensureSession, js, keys, KEY, typeHuman, sleep } from './wd.mjs';
import { moveTo, clickCss, clickOn, byCss, idle, mark } from './mouse.mjs';

// The host side (broadcast.sh) drops done.flag once apt has finished on all twelve.
rmSync('/tmp/work/done.flag', { force: true });
await ensureSession();
await moveTo(1500, 1050, 200);
await idle(1500);
mark('select');
await keys([KEY.Control, 'a']);
await idle(1300);
mark('connect');
await clickCss('button[title="Connect (or double-click)"]');
for (let i = 0; i < 120; i++) {
if ((await js(`return [...document.querySelectorAll('.xterm-rows')].filter(r=>/\\$ *$/.test(r.innerText.trim())).length`)) >= 12) break;
await sleep(250);
}
mark('grid');
await idle(1500);
mark('broadcast');
await clickCss('button[title="Broadcast input"]');
await idle(900);
await clickOn(byCss, ['.xterm-screen'], { fx: 0.5, fy: 0.6 });
await idle(400);
mark('type');
await typeHuman('sudo apt update && sudo apt upgrade -y\n', { min: 60, max: 120 });
mark('enter');
while (!existsSync('/tmp/work/done.flag')) await idle(300);
await idle(2500);
mark('end');
28 changes: 28 additions & 0 deletions promo/capture/social/broadcast.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# broadcast.sh — V7: a fresh 12-host fleet, select all, connect, broadcast one apt upgrade to every pane.
set -euo pipefail
HERE=$(cd "$(dirname "$0")" && pwd)
FLEET=(web-01 web-02 web-03 web-04 api-01 api-02 api-03 db-01 db-02 cache-01 worker-01 worker-02)
. "$HERE/lib.sh"
apt_busy() { for h in "${FLEET[@]}"; do docker exec promo-s-$h pgrep -x apt >/dev/null && return 0; done; return 1; }

# apt upgrade only has work to do once per container.
for h in "${FLEET[@]}"; do docker rm -f promo-s-$h >/dev/null; done
WT=${WT:?voltius worktree} BIN=${BIN:?dir with voltius binary} "$HERE/setup.sh" >/dev/null
stage
run node do.mjs sessions.js >/dev/null
run node do.mjs 'const { useUIStore: u } = await import("/src/stores/uiStore.ts"); u.getState().setTerminalFontSize(11); u.getState().setRightPanelOpen(false); u.getState().setActiveNav("hosts"); return 1' >/dev/null
run node -e "import('./wd.mjs').then(async m=>{await m.ensureSession(); await m.setWindow(1920,1200);})"
sleep 2
run node do.mjs vault.js
SIZE=1920x1200 run sh -c 'SIZE=1920x1200 ./rec.sh broadcast.mjs broadcast' > /tmp/promo-rec.log 2>&1 &
TAKE=$!
until run grep -q ' enter$' marks.txt 2>/dev/null; do sleep 0.5; done
sleep 5
while apt_busy; do sleep 1; done
mark done
run touch done.flag
wait $TAKE || true
tail -4 /tmp/promo-rec.log
"$HERE/pull.sh" broadcast
run node -e "import('./wd.mjs').then(async m=>{await m.ensureSession(); await m.setWindow(1280,800);})"
13 changes: 13 additions & 0 deletions promo/capture/social/clean.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
const { useConnectionStore } = await import('/src/stores/connectionStore.ts');
const { useFolderStore } = await import('/src/stores/folderStore.ts');
const { useKnownHostStore } = await import('/src/stores/knownHostStore.ts');
for (const c of [...useConnectionStore.getState().connections]) await useConnectionStore.getState().deleteConnection(c.id);
for (const f of [...useFolderStore.getState().folders]) { try { await useFolderStore.getState().deleteFolder(f.id, { cascade: true }); } catch {} }
const { useSnippetStore } = await import('/src/stores/snippetStore.ts');
const { useIdentityStore } = await import('/src/stores/identityStore.ts');
const sn = useSnippetStore.getState(); for (const x of [...(sn.snippets || [])]) { try { await sn.deleteSnippet(x.id); } catch {} }
const idn = useIdentityStore.getState(); for (const x of [...(idn.identities || [])]) { try { await idn.deleteIdentity(x.id); } catch {} }
const kh = useKnownHostStore.getState(); await kh.loadKnownHosts();
for (const k of [...useKnownHostStore.getState().knownHosts]) await kh.removeKnownHost(k.id);
if (!document.getElementById('promo-css')) document.head.appendChild(Object.assign(document.createElement('style'), { id: 'promo-css' }));
return [useConnectionStore.getState().connections.length, useFolderStore.getState().folders.length, (useSnippetStore.getState().snippets || []).length, (useIdentityStore.getState().identities || []).length];
2 changes: 2 additions & 0 deletions promo/capture/social/click.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import { click } from './mouse.mjs';
await click(+process.argv[2], +process.argv[3], 150);
11 changes: 11 additions & 0 deletions promo/capture/social/cold1.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { ensureSession } from './wd.mjs';
import { moveTo, clickText, idle, mark } from './mouse.mjs';

await ensureSession();
await moveTo(900, 640, 200);
await idle(1500);
mark('choose');
await clickText('Get started');
mark('started');
await idle(3500);
mark('end');
47 changes: 47 additions & 0 deletions promo/capture/social/cold2.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { ensureSession, keys, KEY, typeHuman, js } from './wd.mjs';
import { moveTo, clickOn, clickText, byCss, byText, waitRect, idle, mark, waitFor } from './mouse.mjs';

await ensureSession();
await moveTo(640, 520, 200);
await idle(1200);
mark('vault');
await clickOn(byCss, ['button[title="Personal"]']);
await idle(1400);
mark('new');
await clickText('New Host');
await idle(1200);
mark('host');
await clickOn(byCss, ['input[placeholder="192.168.1.1"]']);
await idle(350);
await typeHuman('web-01', { min: 70, max: 130 });
await idle(400);
const user = await js(`var i=[...document.querySelectorAll('input')].find(i=>i.value==='root'||i.placeholder==='root'); i.scrollIntoView({block:'nearest'}); return !!i`);
await idle(900);
mark('user');
await clickOn(`return [...document.querySelectorAll('input')].find(i=>i.value==='root'||i.placeholder==='root');`);
await idle(300);
await keys([KEY.Control, 'a']);
await typeHuman('deploy', { min: 70, max: 130 });
await idle(300);
await js(`document.querySelector('input[placeholder="••••••••"]').scrollIntoView({block:'nearest'}); return 1`);
await idle(500);
await clickOn(byCss, ['input[placeholder="••••••••"]']);
await idle(350);
console.log('focus', await js(`return document.activeElement.type`));
await typeHuman('deploy', { min: 60, max: 110 });
await idle(900);
mark('saved');
await clickOn(byCss, ['button[title="Close panel"]']);
await idle(1200);
mark('connect');
await clickOn(byCss, ['button[title="Connect (or double-click)"]']);
await idle(2500);
const trust = await js(`var b=[...document.querySelectorAll('button')].find(b=>/^(Trust|Accept|Trust & connect|Continue)/i.test(b.textContent.trim())&&b.getBoundingClientRect().width>0); return b?b.textContent.trim():null`);
if (trust) { mark('trust'); await clickText(trust); }
await waitFor(`return !!document.querySelector('.xterm-screen')`, [], 20000);
await idle(2200);
mark('shell');
await clickOn(byCss, ['.xterm-screen'], { fx: 0.4, fy: 0.5 });
await typeHuman('neofetch\n', { min: 70, max: 130 });
await idle(3500);
mark('end');
8 changes: 8 additions & 0 deletions promo/capture/social/fleet.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// The fleet every importer brings in: [name, folder, distro]. All resolve on promo-social-net, deploy/deploy on 22.
export const FLEET = [
['web-01', 'Production', 'debian'], ['web-02', 'Production', 'debian'], ['web-03', 'Production', 'debian'], ['web-04', 'Production', 'debian'],
['api-01', 'Production', 'debian'], ['api-02', 'Production', 'debian'], ['api-03', 'Production', 'debian'],
['db-01', 'Databases', 'debian'], ['db-02', 'Databases', 'debian'], ['cache-01', 'Databases', 'debian'],
['worker-01', 'Workers', 'debian'], ['worker-02', 'Workers', 'debian'],
];
export const FOLDERS = [...new Set(FLEET.map(([, f]) => f))];
16 changes: 16 additions & 0 deletions promo/capture/social/fleet/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# An older Debian point release, so `apt upgrade` has real work to do on camera.
FROM debian:bookworm-20250113-slim
RUN apt-get update && apt-get install -y --no-install-recommends openssh-server sudo tmux neofetch procps less ca-certificates \
&& rm -rf /var/lib/apt/lists/* && mkdir -p /run/sshd \
&& useradd -m -s /bin/bash deploy && echo deploy:deploy | chpasswd \
&& echo 'deploy ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/deploy \
&& sed -i 's/^#force_color_prompt=yes/force_color_prompt=yes/' /home/deploy/.bashrc
COPY build.sh /home/deploy/build.sh
COPY neofetch.conf /home/deploy/.config/neofetch/config.conf
# The kernel line and uptime would describe the capture box, not the pretend server.
RUN rm -f /etc/update-motd.d/10-uname && chown -R deploy:deploy /home/deploy && chmod +x /home/deploy/build.sh
# No debconf dialogs or conffile prompts in the middle of the broadcast take.
RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections \
&& echo 'libraries/restart-without-asking boolean true' | debconf-set-selections \
&& printf 'Dpkg::Options { "--force-confdef"; "--force-confold"; };\n' > /etc/apt/apt.conf.d/90promo
CMD ["/usr/sbin/sshd", "-D", "-e"]
16 changes: 16 additions & 0 deletions promo/capture/social/fleet/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#!/bin/sh
G='\033[32m'; C='\033[36m'; D='\033[2m'; B='\033[1m'; Y='\033[33m'; R='\033[0m'
printf "${B}make${R} release ${D}acme-api · 4 jobs${R}\n"
n=0
for dir in core net storage auth billing api workers cli; do
for f in config errors pool codec cache router handler metrics tracing retry queue schema; do
n=$((n + 1))
printf "${G} CC${R} src/%s/%s.o\n" "$dir" "$f"
sleep 0.5
[ $((n % 17)) -eq 0 ] && printf "${Y}warning:${R} ${D}src/%s/%s.c:%d: unused variable 'tmp'${R}\n" "$dir" "$f" $((n * 7))
done
printf "${C} AR${R} build/lib%s.a\n" "$dir"
sleep 0.6
done
printf "${C} LD${R} build/acme-api\n"; sleep 1.5
printf "\n${G}${B}✓ build finished${R} ${D}%d objects${R}\n" "$n"
9 changes: 9 additions & 0 deletions promo/capture/social/fleet/neofetch.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
print_info() {
info title
info underline
info "OS" distro
info "Packages" packages
info "Shell" shell
info "Terminal" term
info cols
}
Loading
Loading