Skip to content
Open
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
95 changes: 95 additions & 0 deletions .github/workflows/render.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
name: Render launch film

# Re-renders the v0.3 launch film from source and commits the finished LFS media
# (MP4 + the synthesized music.mp3) back. This closes the loop for environments
# that can't push Git LFS objects: the source lands via PR, CI produces and
# commits the binaries. Triggers only on source changes (never on the media it
# writes, so it can't loop) and on manual dispatch.

on:
push:
branches: [main]
paths:
- 'v0.3/src/index.html'
- 'v0.3/src/animations.jsx'
- 'v0.3/src/render.mjs'
- 'v0.3/src/vendor/**'
- 'v0.3/src/fonts/**'
- 'v0.3/src/shots/**'
- 'v0.3/src/clips/**'
- 'v0.3/src/scripts/synth-music.mjs'
- '.github/workflows/render.yml'
workflow_dispatch:

permissions:
contents: write

concurrency:
group: render-v03
cancel-in-progress: true

jobs:
render:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
lfs: true

- uses: actions/setup-node@v4
with:
node-version: 20

- name: Install ffmpeg
run: sudo apt-get update && sudo apt-get install -y ffmpeg

- name: Install Chrome
id: chrome
uses: browser-actions/setup-chrome@v1
with:
chrome-version: stable

- name: Install puppeteer-core
working-directory: v0.3/src
run: npm install --no-save puppeteer-core@23

- name: Regenerate music (deterministic synth)
working-directory: v0.3/src
run: |
node scripts/synth-music.mjs music.wav
ffmpeg -y -i music.wav \
-af "highpass=f=30,lowpass=f=15800,alimiter=limit=0.97:level=false" \
-c:a libmp3lame -b:a 192k music.mp3
rm -f music.wav

- name: Render frames
working-directory: v0.3/src
env:
CHROME: ${{ steps.chrome.outputs.chrome-path }}
run: |
rm -rf frames && mkdir -p frames
node render.mjs

- name: Encode MP4
working-directory: v0.3/src
run: |
DUR=$(cat render-duration.txt)
FADE=$(awk "BEGIN{printf \"%.3f\", $DUR - 1}")
echo "duration=$DUR fade_start=$FADE"
ffmpeg -y -framerate 30 -i frames/f%05d.png -i music.mp3 \
-vf "scale=1920:1080:flags=lanczos,format=yuv420p" \
-c:v libx264 -profile:v high -preset slow -crf 16 \
-c:a aac -b:a 192k -af "afade=out:st=${FADE}:d=1" \
-t "$DUR" -movflags +faststart ../chmonitor-v03-design-launch.mp4

- name: Commit rendered media
run: |
git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
git add v0.3/chmonitor-v03-design-launch.mp4 v0.3/src/music.mp3
if git diff --cached --quiet; then
echo "No media changes — nothing to commit."
exit 0
fi
git commit -m "chore(v0.3): re-render launch film from source [ci]"
git push
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,8 @@ node_modules/
frames/
renders/work-*/
.DS_Store

# render/encode scratch
render-duration.txt
music.wav
package-lock.json
25 changes: 21 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,7 +274,24 @@ Auto-deployed on every push to `main` via `.github/workflows/pages.yml`.

To add a new release to the landing page, copy the `.vcard` block in `index.html` and update `data-src`, `href`, version chip, title, and description.

## Known outstanding items (v0.3)

- End card reads `github.com/duyet/clickhouse-monitoring` (pre-rebrand). Fix in `EndScene` in `v0.3/src/index.html` before next render.
- AI Agent and Topology scenes use static screenshots — replace with live recordings when `agent.mov` and `topology.mov` are available.
## Redesign notes (v0.3, current cut)

The v0.3 film was redesigned to a flat "instrument / editorial" language — grid
ground (no breathing glow), numbered cues with ruled kickers + mask-revealed
titles, flat monospace "spec" tags (`Tag`) instead of glass pills, a hairline
product window (`Window`, no traffic-light chrome), a persistent `FilmRail`, and
an original synthesized score. Durable changes worth knowing before the next cut:

- **Self-contained runtime, no CDN.** React / ReactDOM / Babel are vendored under
`v0.3/src/vendor/`, fonts under `v0.3/src/fonts/`. The render is fully offline.
Babel **7** standalone is required (Babel 8 broke the in-browser `text/babel`
runner). The Lottie pulse-ring dependency was removed.
- **Data-driven timeline.** Scene order/durations live in `buildCues()`; start/end
times and the total are derived (0.15s overlaps), exposed as `window.__duration`.
`render.mjs` reads it and takes the Chrome path from `$CHROME`.
- **Original score.** `music.mp3` is synthesized deterministically (no stock/AI
track). Regenerate with the Node synth described in `v0.3/README.md` § Music.
- End card now reads `github.com/chmonitor/chmonitor` (pre-rebrand URL fixed).
- Most feature scenes use static screenshots (`Frame`); Alerts is a fully-rendered
card (no screenshot). Replace AI Agent / Topology stills with live recordings
when `agent.mov` / `topology.mov` are available.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ source needed to re-render or edit them.

| Version | Folder | Highlights |
|---------|--------|------------|
| v0.3 | [`v0.3/`](v0.3/) | Full rebuild launch film — TanStack Start dashboard, AI agent, query monitoring, data explorer, topology & health, self-host. |
| v0.3 | [`v0.3/`](v0.3/) | Full rebuild launch film (redesigned cut) — TanStack Start, AI agent, query monitoring, SQL console, AI insights, alerts, topology & health, self-host. Editorial redesign + original score. |

## Conventions

Expand Down
6 changes: 3 additions & 3 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -201,13 +201,13 @@
</div>
</div>
<div class="vcard-version-chip">v0.3</div>
<div class="vcard-duration-chip">0:28</div>
<div class="vcard-duration-chip">0:31</div>
<div class="vcard-progress"></div>
</div>
<div class="vcard-footer">
<div class="vcard-meta">
<div class="vcard-title">Full rebuild launch film</div>
<div class="vcard-desc">TanStack Start · AI agent · query monitoring · data explorer · topology · health &amp; audit · 13 scenes</div>
<div class="vcard-title">Full rebuild launch film — redesigned cut</div>
<div class="vcard-desc">TanStack Start · AI agent · SQL console · AI insights · alerts · topology · health &amp; audit · 14 scenes, original score</div>
</div>
<div class="vcard-btns">
<a class="btn btn-primary" href="v0.3/src/" onclick="event.stopPropagation()">
Expand Down
133 changes: 88 additions & 45 deletions v0.3/README.md
Original file line number Diff line number Diff line change
@@ -1,67 +1,111 @@
# chmonitor v0.3 — launch film

`chmonitor-v03-design-launch.mp4`~28s · 1920×1080 · H.264 + AAC.

A faithful render of the "chmonitor v0.3 Launch" Claude Design film
(claude.ai/design project *CHM v0.3 Release*).

## Scenes (13)

1. **Intro** — logo + `v0.3`, "A full rebuild — here's what landed" (8 features · 70+ fixes · 13 perf wins · 3 breaking)
2. **The Dashboard** — Rebuilt on TanStack Start *(live screen recording)*
3. **AI Agent** — Ask your cluster anything (over MCP)
4. **Query Monitoring** — Watch every query, live *(live screen recording)*
5. **Data Query Explorer** — dependency graph → SQL console *(live screen recording)*
6. **AI Insights** — anomalies & regressions, ranked by severity
7. **Metrics & Profiler** — CPU / memory / IO + ClickHouse profiler events
8. **Query EXPLAIN** — the EXPLAIN plan as a tree
9. **Cluster Topology** — nodes, shards, replicas, Keeper quorum
10. **Health & Audit** — color-coded health → ready-made audit prompt
11. **And a lot more** — 15+ pages, 71 charts (montage)
12. **Self-host anywhere** — Cloudflare Workers / Docker / Kubernetes
13. **End card** — chmonitor.dev
`chmonitor-v03-design-launch.mp4`~31s · 1920×1080 · H.264 + AAC.

A redesigned cut of the chmonitor v0.3 launch film. The look is deliberately
un-generic — a flat instrument-panel ground with a faint measurement grid, an
editorial numbered-cue system with ruled kickers and mask-revealed titles, flat
monospace "spec" tags in place of glassy pills, a hairline product window (no
traffic-light chrome), and a persistent film rail (index · progress · domain).
The score is an original, deterministic synth bed (no stock/AI track).

## Scenes (14 cues · 11 numbered features)

1. **Intro** — logo + `v0.3`, "A full rebuild. Here's what shipped." (20 features · 120+ fixes · 15+ perf wins · 3 breaking, count-up)
2. **The Dashboard** — rebuilt on TanStack Start, 71 edge-cached charts *(live recording)*
3. **AI Agent** `NEW` — ask your cluster anything, over MCP, any LLM
4. **Query Monitoring** — running / slow / failed queries, live *(live recording)*
5. **SQL Console** `NEW` — multi-query results, database tree, live dependency graph *(live recording)*
6. **AI Insights** `NEW` — anomalies & regressions, ranked by severity
7. **Alerts** `NEW` — threshold alerts as browser notifications, on by default *(rendered card, no screenshot)*
8. **Metrics & Profiler** — CPU / memory / IO + ClickHouse profiler events
9. **Query EXPLAIN** — the EXPLAIN plan as a tree
10. **Cluster Topology** — nodes, shards, replicas, Keeper quorum
11. **Health & Audit** — color-coded health → ready-made audit prompt
12. **And a lot more** — 15+ pages, 71 charts; expensive queries, MCP server, replication (montage)
13. **Deploy anywhere** — Cloudflare Workers / Docker / Kubernetes; pluggable auth (none · Clerk · trusted proxy)
14. **End card** — chmonitor.dev · github.com/chmonitor/chmonitor

The timeline is **data-driven**: cue durations live in `buildCues()` in
`src/index.html`, and start/end times (with 0.15s cross-fade overlaps) plus the
total duration are derived automatically. To retime or reorder, edit that array —
`<Stage duration>` and the render both read the computed total via `window.__duration`.

## Self-contained runtime (no CDN)

The film no longer loads anything from a CDN. React 18, ReactDOM and Babel
standalone are vendored under [`src/vendor/`](src/vendor/), and the Space Grotesk
/ JetBrains Mono latin subsets under [`src/fonts/`](src/fonts/). This makes the
render fully offline and deterministic, and keeps GitHub Pages self-hosting the
player. (The Lottie pulse-ring dependency was removed in the redesign.)

## Re-rendering

The film is a self-contained, deterministic React timeline (`src/animations.jsx`)
mounted in `src/index.html` (React 18 + Babel standalone, loaded from CDN). It is
rendered by seeking its native `window.__seek(t)` clock frame-by-frame in headless
Chrome and encoding with ffmpeg.
mounted in `src/index.html`. It is rendered by seeking its native
`window.__seek(t)` clock frame-by-frame in headless Chrome and encoding with ffmpeg.

```bash
cd src
bun add puppeteer-core # uses an existing Chrome for Testing install
node render.mjs # captures 837 frames (30fps × 27.9s) to frames/ at 2× supersample
bun add puppeteer-core # first time only
CHROME=/path/to/chrome node render.mjs # captures 933 frames (30fps × 31.1s) to frames/ at 2× supersample
ffmpeg -y -framerate 30 -i frames/f%05d.png -i music.mp3 \
-vf "scale=1920:1080:flags=lanczos,format=yuv420p" \
-c:v libx264 -profile:v high -preset slow -crf 16 \
-c:a aac -b:a 192k -af "afade=out:st=26.9:d=1" \
-t 27.9 -movflags +faststart ../chmonitor-v03-design-launch.mp4
-c:a aac -b:a 192k -af "afade=out:st=30.1:d=1" \
-t 31.1 -movflags +faststart ../chmonitor-v03-design-launch.mp4
```

`render.mjs` hardcodes a Chrome-for-Testing path — update `CHROME` to your local
install. Frames and `node_modules/` are gitignored.
`render.mjs` reads the total length from `window.__duration` and takes the Chrome
path from `$CHROME` (any Chromium works). Frames and `node_modules/` are gitignored.

## Assets

- `src/animations.jsx` — the film (scenes, timing, layout, copy)
- `src/index.html`harness that mounts `<Video orientation="landscape">`
- `src/index.html` — the film (harness + engine + scenes); open in a browser to preview
- `src/animations.jsx`the babel script block mirrored for diffing
- `src/render.mjs` — puppeteer seek-and-screenshot renderer
- `src/music.mp3`~28s music bed (ElevenLabs Music)
- `src/shots/*.png` — product screenshots (from `apps/landing/public/landing-assets`)
- `src/clips/*.mp4` — live screen recordings (constant 30fps)
- `src/vendor/*` — React / ReactDOM / Babel standalone (vendored, no CDN)
- `src/fonts/*` — Space Grotesk + JetBrains Mono (latin woff2) + `fonts.css`
- `src/music.mp3` — original ~32s synth bed (deterministic; see *Music* below)
- `src/shots/*.png` — product screenshots (Git LFS)
- `src/clips/*.mp4` — live screen recordings, constant 30fps (Git LFS)

## Music

`src/music.mp3` is an original, fully synthesized bed — a steady "monitor pulse"
kick, a warm plucked bass, an arp and a soft pad over an Am–F–C–G loop, computed
sample-by-sample (no samples, no stock/AI track) so it is reproducible. It has a
soft intro, a fuller body and a gentle outro; the final 1s fade is applied by
ffmpeg at render time. Regenerate it deterministically with:

```bash
cd src
node scripts/synth-music.mjs music.wav
ffmpeg -y -i music.wav \
-af "highpass=f=30,lowpass=f=15800,alimiter=limit=0.97:level=false" \
-c:a libmp3lame -b:a 192k music.mp3 && rm music.wav
```

## Continuous rendering (CI)

`.github/workflows/render.yml` re-renders the film and commits the finished LFS
media (the MP4 and the synthesized `music.mp3`) back whenever the **source**
changes on `main` (or on manual dispatch). This lets the reproducible source land
via PR while CI produces the binaries — useful when a contributor's environment
can't push Git LFS objects. The workflow only triggers on source paths, never on
the media it writes, so it cannot loop.

## Mixed media (video + screenshots)

Scenes use either a screenshot (`<Frame>`) or a live recording (`<VideoFrame>`).
Scenes use a screenshot (`Frame`), a live recording (`VideoFrame`), a cycling
montage (`MontageScene`) or a fully-rendered mock (the Alerts cards).
`VideoFrame` drives the clip's `currentTime` from the scene clock
(`currentTime = trim + lt * speed`), so it renders deterministically frame-by-frame
just like the rest of the film — `render.mjs` waits for each `<video>` to finish
decoding (`readyState ≥ 2 && !seeking`) before each screenshot.
(`currentTime = trim + lt * speed`) so it renders deterministically frame-by-frame;
`render.mjs` waits for each `<video>` to finish decoding before each screenshot.

To add a recording: re-encode to **constant frame rate** first (macOS `.mov`
recordings are VFR and seek inaccurately), drop it in `src/clips/`, and point a
scene's `VideoFrame src` at it.
To add a recording, re-encode to **constant frame rate** first (macOS `.mov`
captures are VFR and seek inaccurately), drop it in `src/clips/`, and point a
scene's `VideoFrame src` at it:

```bash
ffmpeg -i screen.mov -vf "fps=30" -c:v libx264 -crf 18 -pix_fmt yuv420p -an \
Expand All @@ -71,7 +115,6 @@ ffmpeg -i screen.mov -vf "fps=30" -c:v libx264 -crf 18 -pix_fmt yuv420p -an \
## Notes

- A **9:16 vertical** cut is available from the same source — `Video` accepts
`orientation="portrait"` (1080×1920).
- The end card reads `github.com/duyet/clickhouse-monitoring` (pre-rebrand). The
repo is now `github.com/chmonitor/chmonitor` — update `EndScene` in
`src/animations.jsx` before the next cut if desired.
`orientation="portrait"` (1080×1920); all layout constants switch via `PORT`.
- The end card now reads `github.com/chmonitor/chmonitor` (the old pre-rebrand
`duyet/clickhouse-monitoring` URL was fixed in this cut).
Loading