From efde557f0f41bc27ff449b31269b15d88007738a Mon Sep 17 00:00:00 2001 From: Willem Date: Thu, 27 Aug 2026 18:35:50 -0400 Subject: [PATCH 1/3] examples: add visko-orbis-distilled dev example MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Cloneable Next.js reference app for Visko Orbis · Distilled (v2.0.0), modelled on the helios typed example. T2V + I2V entry points, the hero per-chunk live prompt morphing surface, resolution picker driven from state.available_resolutions, seed, audio on/off + muted-by-default main_audio outlet, and an explicit generation_complete → start-again flow. The typed @reactor-models/visko-orbis-distilled@2.0.0 package is vendored under vendor/ (generated from the live PROD schema by @reactor-team/codegen, pre-npm-publication) and wired in via a pnpm link: override; package.json targets ^2.0.0 so dropping the override + pnpm up swaps to the published package with zero code changes. Honest-UX surfaces for measured runtime behaviour: the minutes-long waiting phase (SR compile + three warmup chunks), the zero-frame first chunk (SR priming, first picture ~2 chunks in), 832x480 generation with SparkVSR delivery tiers, apply-at-next-start session knobs that survive reset, and the no-audio-prompt guidance. Draft pending backend launch gates (schema/is_public/public_pricing) and a live e2e session stream. Final verification against the PUBLISHED package happens post-npm-publish on the pre-launch checklist. --- examples/visko-orbis-distilled/.env.example | 1 + examples/visko-orbis-distilled/.gitignore | 7 + examples/visko-orbis-distilled/README.md | 103 ++ .../app/SetupRequired.tsx | 76 ++ .../app/ViskoOrbisDistilledApp.tsx | 101 ++ .../app/api/reactor/token/route.ts | 96 ++ .../app/components/AudioOutlet.tsx | 52 + .../app/components/AudioPanel.tsx | 81 ++ .../app/components/CommandError.tsx | 42 + .../app/components/EvolveScene.tsx | 116 ++ .../app/components/Header.tsx | 17 + .../app/components/ImageStarter.tsx | 172 +++ .../app/components/NowPlaying.tsx | 135 ++ .../app/components/PromptComposer.tsx | 102 ++ .../app/components/SessionOptions.tsx | 128 ++ .../app/components/SnapClip.tsx | 172 +++ .../app/components/StatusBadge.tsx | 99 ++ .../app/components/Video.tsx | 60 + .../visko-orbis-distilled/app/globals.css | 44 + examples/visko-orbis-distilled/app/layout.tsx | 23 + .../visko-orbis-distilled/app/lib/prompts.ts | 203 +++ examples/visko-orbis-distilled/app/page.tsx | 22 + examples/visko-orbis-distilled/next-env.d.ts | 6 + examples/visko-orbis-distilled/next.config.ts | 5 + examples/visko-orbis-distilled/package.json | 33 + examples/visko-orbis-distilled/pnpm-lock.yaml | 1097 ++++++++++++++++ .../visko-orbis-distilled/pnpm-workspace.yaml | 6 + .../visko-orbis-distilled/postcss.config.mjs | 7 + .../public/images/coral-abyss.jpg | Bin 0 -> 280163 bytes .../public/images/desert-ruins.jpg | Bin 0 -> 234318 bytes .../public/images/neon-city.jpg | Bin 0 -> 258476 bytes .../public/images/sky-citadel.jpg | Bin 0 -> 233859 bytes examples/visko-orbis-distilled/skill/SKILL.md | 424 ++++++ examples/visko-orbis-distilled/tsconfig.json | 21 + .../visko-orbis-distilled/vendor/README.md | 63 + .../vendor/visko-orbis-distilled/README.md | 1140 +++++++++++++++++ .../vendor/visko-orbis-distilled/package.json | 54 + 37 files changed, 4708 insertions(+) create mode 100644 examples/visko-orbis-distilled/.env.example create mode 100644 examples/visko-orbis-distilled/.gitignore create mode 100644 examples/visko-orbis-distilled/README.md create mode 100644 examples/visko-orbis-distilled/app/SetupRequired.tsx create mode 100644 examples/visko-orbis-distilled/app/ViskoOrbisDistilledApp.tsx create mode 100644 examples/visko-orbis-distilled/app/api/reactor/token/route.ts create mode 100644 examples/visko-orbis-distilled/app/components/AudioOutlet.tsx create mode 100644 examples/visko-orbis-distilled/app/components/AudioPanel.tsx create mode 100644 examples/visko-orbis-distilled/app/components/CommandError.tsx create mode 100644 examples/visko-orbis-distilled/app/components/EvolveScene.tsx create mode 100644 examples/visko-orbis-distilled/app/components/Header.tsx create mode 100644 examples/visko-orbis-distilled/app/components/ImageStarter.tsx create mode 100644 examples/visko-orbis-distilled/app/components/NowPlaying.tsx create mode 100644 examples/visko-orbis-distilled/app/components/PromptComposer.tsx create mode 100644 examples/visko-orbis-distilled/app/components/SessionOptions.tsx create mode 100644 examples/visko-orbis-distilled/app/components/SnapClip.tsx create mode 100644 examples/visko-orbis-distilled/app/components/StatusBadge.tsx create mode 100644 examples/visko-orbis-distilled/app/components/Video.tsx create mode 100644 examples/visko-orbis-distilled/app/globals.css create mode 100644 examples/visko-orbis-distilled/app/layout.tsx create mode 100644 examples/visko-orbis-distilled/app/lib/prompts.ts create mode 100644 examples/visko-orbis-distilled/app/page.tsx create mode 100644 examples/visko-orbis-distilled/next-env.d.ts create mode 100644 examples/visko-orbis-distilled/next.config.ts create mode 100644 examples/visko-orbis-distilled/package.json create mode 100644 examples/visko-orbis-distilled/pnpm-lock.yaml create mode 100644 examples/visko-orbis-distilled/pnpm-workspace.yaml create mode 100644 examples/visko-orbis-distilled/postcss.config.mjs create mode 100644 examples/visko-orbis-distilled/public/images/coral-abyss.jpg create mode 100644 examples/visko-orbis-distilled/public/images/desert-ruins.jpg create mode 100644 examples/visko-orbis-distilled/public/images/neon-city.jpg create mode 100644 examples/visko-orbis-distilled/public/images/sky-citadel.jpg create mode 100644 examples/visko-orbis-distilled/skill/SKILL.md create mode 100644 examples/visko-orbis-distilled/tsconfig.json create mode 100644 examples/visko-orbis-distilled/vendor/README.md create mode 100644 examples/visko-orbis-distilled/vendor/visko-orbis-distilled/README.md create mode 100644 examples/visko-orbis-distilled/vendor/visko-orbis-distilled/package.json diff --git a/examples/visko-orbis-distilled/.env.example b/examples/visko-orbis-distilled/.env.example new file mode 100644 index 00000000..c8eeda28 --- /dev/null +++ b/examples/visko-orbis-distilled/.env.example @@ -0,0 +1 @@ +REACTOR_API_KEY=rk_your_api_key_here diff --git a/examples/visko-orbis-distilled/.gitignore b/examples/visko-orbis-distilled/.gitignore new file mode 100644 index 00000000..d9bc3132 --- /dev/null +++ b/examples/visko-orbis-distilled/.gitignore @@ -0,0 +1,7 @@ +node_modules +.next +.env +.env.local +*.log +.DS_Store +*.tsbuildinfo diff --git a/examples/visko-orbis-distilled/README.md b/examples/visko-orbis-distilled/README.md new file mode 100644 index 00000000..d622a300 --- /dev/null +++ b/examples/visko-orbis-distilled/README.md @@ -0,0 +1,103 @@ +# Visko Orbis Distilled + +A Next.js + TypeScript reference frontend for **Visko Orbis · Distilled** — Reactor's real-time, steerable video generation model (the Distilled tier of the Visko Orbis family). + +Connect, send a prompt, and watch the model produce a continuous video stream you can **steer mid-flight**. Start from a curated text prompt, an example image, or your own image — then hot-swap prompts to morph the scene live, no restart, no cut. Built on the typed `@reactor-models/visko-orbis-distilled` SDK. + +``` +┌──────────────────────┬─────────────────────────────────────┐ +│ Status ▸ ready │ │ +│ │ │ +│ Try a prompt │ │ +│ ┌────────┬────────┐ │ live video output │ +│ │ Coast │ Desert │ │ (ViskoOrbisDistilledMainVideoView)│ +│ └────────┴────────┘ │ │ +│ Or start from image │ │ +│ ┌────────┬────────┐ │ │ +│ │ Citadel│ Neon │ │ + muted-by-default main_audio│ +│ └────────┴────────┘ │ │ +│ Session ▾ resolution│ │ +│ Audio ▸ on │ │ +│ [Snap clip] │ │ +└──────────────────────┴─────────────────────────────────────┘ +``` + +## Quick start + +You'll need a Reactor API key — grab one at [reactor.inc/account/api-keys](https://www.reactor.inc/account/api-keys). It starts with `rk_`. + +```bash +cp .env.example .env +# add your key: REACTOR_API_KEY=rk_... + +pnpm install +pnpm dev +``` + +Open [http://localhost:3000](http://localhost:3000), click **Connect**, and pick a starting point. + +> **Typed package — read this if `@reactor-models/visko-orbis-distilled` 404s on npm.** +> This example ships with the typed SDK **vendored** at [`vendor/visko-orbis-distilled/`](vendor/visko-orbis-distilled) and wired in via a `pnpm` override (`link:./vendor/visko-orbis-distilled`). That's a pre-publication shim: the package is generated from the model's live PROD schema but isn't on the public registry yet. `package.json` declares `"@reactor-models/visko-orbis-distilled": "^2.0.0"`, so once the real package is published you can drop the `pnpm.overrides` block and `pnpm up` — nothing else changes. See [`vendor/README.md`](vendor/README.md). + +## What you can do with it + +- **Start a scene from text alone (T2V).** Curated prompt presets in the sidebar, plus free-text. No image required — the model invents the opening frame. +- **Start from an image (I2V).** Curated image+prompt cards, or upload your own. The image anchors the first chunk; every later chunk inherits it through the model's history. +- **Steer the scene live — the hero feature.** Prompts are **per-chunk**: send a new prompt mid-run and the picture morphs into it at the next chunk boundary (~1.8 s). One-click "evolutions" keep the same world and shift its mood (golden hour → storm, calm sea → night fog), or type any new scene. +- **Pick a delivery resolution.** Rendered from the deployment's `available_resolutions` (e.g. 1080p / 2k / 4k) — never hard-coded. Applies at the next `start`; survives `reset`. +- **Audio on/off + muted playback.** `main_audio` is a real 48 kHz mono track, chunk-aligned with the video. Playback starts muted; the "generate sound" toggle saves compute and applies at the next `start`. +- **Reproducible runs.** `set_seed` — same seed + same prompts reproduces the same video. Applies at the next `start`. +- **Pause / Resume / Reset.** Real-time transport. `generation_complete` returns to waiting (no auto-restart) — "Start again" re-runs with the same conditions, "Reset" clears prompt + image. +- **Snap a clip.** Capture the last 10 s of the live stream and download an MP4 — base-SDK recording, no extra services. + +## Reality notes (things that will look "broken" but aren't) + +These are measured behaviours of the deployed model, surfaced so you don't misread them: + +- **Connect takes MINUTES.** There's one live session per deployment, and startup pays for SR-model compile + three warmup chunks. The StatusBadge labels `waiting` honestly — it's not stuck. If it hangs in `waiting`, another tab (or a zombie session) is holding the pod. +- **The first chunk emits zero frames.** The super-resolution model primes on chunk 1 (`frames_emitted: 0`); first picture lands ~2 chunks / ~3.7 s in. The video panel holds a "Priming the stream…" overlay over this window instead of a black box. +- **The resolution picker changes the DELIVERED raster, not the dream.** The model generates at 832×480 and SparkVSR upscales to the picked tier. Picking 4k doesn't change what the model invents — only the output size. +- **Resolution, seed, and audio settings apply at the NEXT `start`** and survive `reset`; the prompt and image do not. +- **Non-16:9 images squash.** The reference image is resized to 832×480 with no crop. Use a 16:9 frame (the curated images are). +- **No audio prompt box on purpose.** Feeding a scene description into `set_audio_prompt` makes the audio measurably worse than leaving it unset (unset = sound generated from the picture alone). The example deliberately doesn't expose it. + +## Architecture at a glance + +The sidebar UI has two phases driven by the model's `state` snapshot (the single source of truth — never re-derive it from other messages): + +| Phase | When | What's visible | +| --------- | ----------------------------- | ------------------------------------------------------------------------------------- | +| **Setup** | before generation has started | prompt presets, example images, custom upload, free-text textarea, session options | +| **Live** | while generating or paused | active prompt, chunk counter, Pause / Resume / Reset, live steering, run-finished CTA | + +Each component subscribes to the snapshot itself and self-hides when it's not in its phase. No central orchestrator. + +## Code tour + +The interesting bits, in roughly the order you'd read them: + +| File | What's in it | +| ------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| [`app/page.tsx`](app/page.tsx) | Server Component. Checks `REACTOR_API_KEY` is set, otherwise renders [`SetupRequired.tsx`](app/SetupRequired.tsx). | +| [`app/api/reactor/token/route.ts`](app/api/reactor/token/route.ts) | GET route that mints a session-scoped Reactor JWT (pinned to `reactor/visko-orbis-distilled` via `authorization_details`) and sets `Cache-Control: private, max-age=`. The browser handles caching transparently. | +| [`app/ViskoOrbisDistilledApp.tsx`](app/ViskoOrbisDistilledApp.tsx) | First `"use client"` boundary. Wires ``, lays out the sidebar + video pane, mounts the hidden ``. | +| [`app/lib/prompts.ts`](app/lib/prompts.ts) | The scene library. Every prompt the app suggests — starting prompts and mid-stream evolutions — lives here. Same source feeds the setup presets, the example image cards, and the live steering picker. | +| [`app/components/PromptComposer.tsx`](app/components/PromptComposer.tsx) | Setup phase. Preset prompts + free-text input → `setPrompt` + `start`. Works text-only (T2V). | +| [`app/components/ImageStarter.tsx`](app/components/ImageStarter.tsx) | Setup phase. Curated image scenes run `uploadFile` → `setImage` → **wait for `image_accepted`** → `setPrompt` → `start` (no atomic `setConditioning` on this model, so the chain is explicit). Custom uploads just call `setImage`. Surfaces the 16:9 squash note. | +| [`app/components/EvolveScene.tsx`](app/components/EvolveScene.tsx) | **Live phase — the hero.** Matches the active prompt against the scene library and renders evolutions as one-click morphs, plus a free-form morph box. `setPrompt` only — no restart. | +| [`app/components/SessionOptions.tsx`](app/components/SessionOptions.tsx) | Resolution picker (from `available_resolutions`, never hard-coded) + seed. Both apply at the next `start`. | +| [`app/components/AudioPanel.tsx`](app/components/AudioPanel.tsx) | "Generate sound" toggle (`set_audio_enabled`) + why playback starts muted and there's no audio-prompt box. | +| [`app/components/AudioOutlet.tsx`](app/components/AudioOutlet.tsx) | Renders `main_audio` as a hidden, muted-by-default `