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
Binary file added demo-sample.mp4
Binary file not shown.
131 changes: 114 additions & 17 deletions index.html

Large diffs are not rendered by default.

51 changes: 51 additions & 0 deletions proof/demo-video/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# Signed-out sample run — video result

Proof that a signed-out visitor who adds an Image→Video node to the starter graph gets a real
video result, and that the run costs nothing.

## Run it

```
node proof/demo-video/verify-demo-video.mjs
```

The check is offline and deterministic. It serves the worktree, starts Microsoft Edge in headless
mode, and drives the real `index.html` through CDP. It blocks the whole `nano-gpt.com` origin, and
it records each request that the page tries to make. The run must show 36 passed, 0 failed.

The check makes these statements, and it tests each one:

- The starter graph, and the starter graph plus one Image→Video node, are both sample runs.
- The run-cost chip stays visible in a sample run. It reads `~$X to run for real`.
- The chip figure increases when the visitor adds the video node. The video node also shows its
own price.
- All 4 nodes finish. The video node output is a `data:video/mp4` URL. The clip decodes to
640x360 and 5.04 s.
- The result carries the `✨ sample result` badge. The ⬇ save and ↗ open buttons are present.
- The pill says that the clip is a canned example and that it ignores the visitor's prompt.
- `sw.js` does not precache the clip. The service-worker fetch handler caches it at first use,
and the cached bytes are the shipped file.
- The page calls no billable endpoint. No request reaches `nano-gpt.com`. The sample run finishes
with the API unreachable.

## The catalog fixture

`catalog-fixture.json` holds 3 normalized catalog entries: the two models of the starter graph, and
the model that an appended Image→Video node selects. The check puts them in `localStorage`, because
the run-cost chip needs prices and the network is off.

To record the fixture again:

```
node proof/demo-video/verify-demo-video.mjs --capture
```

This is the only mode that contacts `nano-gpt.com`. It reads the free public model lists
(`GET /api/v1/{models,image-models,video-models}`) and takes the entries from the app's own
`localStorage` cache, so the shape cannot differ from the app's normalizers. It never runs a graph,
and it spends nothing. Prices change, so record the fixture again instead of an edit by hand.

## The clip

`demo-sample.mp4` in the repository root is a real clip. `scripts/gen-demo-clip.mjs` renders it from
the shipped `demo-sample.jpg` and records the model and the cost.
147 changes: 147 additions & 0 deletions proof/demo-video/catalog-fixture.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
{
"note": "Normalized catalog entries for the 3 models the covered demo path uses, read out of the app's own localStorage cache. Re-record with --capture; prices move.",
"captured": "2026-07-30",
"catalogs": {
"chat": [
{
"id": "zai-org/glm-5.2",
"name": "GLM 5.2",
"created": 1781481600,
"sortUsd": 0.00108,
"vision": false,
"nsfw": false,
"reasoning": true,
"structured_output": true,
"audio_input": false,
"video_input": false,
"pdf_upload": false,
"maxOut": 131072,
"promptUsd1M": 0.42000000000000004,
"completionUsd1M": 1.32,
"price": "$0.42/$1.32 ·1M"
}
],
"image": [
{
"id": "nano-banana-2-lite",
"name": "Nano Banana 2 Lite",
"created": 1782777600,
"sortUsd": 0.04,
"edit": true,
"gen": true,
"inpaint": false,
"maxIn": 14,
"lora": false,
"resolutions": [
"1k"
],
"maxOut": 4,
"fixedCount": 0,
"sizePrices": {
"1k": 0.04,
"auto": 0.04
},
"nsfw": false,
"price": "$0.040/img"
}
],
"video": [
{
"id": "google/gemini-omni-flash",
"name": "Gemini Omni Flash",
"created": 1782777600,
"sortUsd": 1.04,
"t2v": true,
"i2v": true,
"v2v": true,
"avatar": false,
"lora": false,
"audioGen": false,
"params": {
"duration": {
"label": "Duration",
"description": "Video duration in seconds",
"type": "select",
"options": [
{
"value": "3",
"label": "3 seconds"
},
{
"value": "4",
"label": "4 seconds"
},
{
"value": "5",
"label": "5 seconds"
},
{
"value": "6",
"label": "6 seconds"
},
{
"value": "7",
"label": "7 seconds"
},
{
"value": "8",
"label": "8 seconds"
},
{
"value": "9",
"label": "9 seconds"
},
{
"value": "10",
"label": "10 seconds"
}
],
"default": "8"
},
"aspect_ratio": {
"label": "Aspect Ratio",
"description": "Output aspect ratio",
"type": "select",
"options": [
{
"value": "16:9",
"label": "Landscape (16:9)"
},
{
"value": "9:16",
"label": "Portrait (9:16)"
}
],
"default": "16:9"
}
},
"defaults": {
"duration": "8",
"aspect_ratio": "16:9"
},
"pricing": {
"currency": "USD",
"per_second_by_mode": {
"text_to_video": 0.13,
"image_to_video": 0.14,
"reference_to_video": 0.16,
"video_edit": 0.16
},
"default_duration": 8,
"supported_durations": [
3,
4,
5,
6,
7,
8,
9,
10
]
},
"nsfw": false,
"price": "~$1.04/clip"
}
]
}
}
Loading
Loading