Skip to content

Latest commit

 

History

History
264 lines (201 loc) · 12.2 KB

File metadata and controls

264 lines (201 loc) · 12.2 KB

Kickoff

Start here. This is the whole system in order, with the manual steps marked.

A shareable version of this page, for anyone who needs the map without the repository: https://claude.ai/code/artifact/ce8c8c27-cc27-48be-aeb2-a19d482a7c95

Changing any of it rather than using it? DEVELOPMENT.md — languages, tools, how to run each piece, and what will bite you.

If you read one thing: the pipeline is record → brand → edit → share. Four steps, four tools, and every one of them can be driven from the Studio.


One repo, not four

Clone this one. It is the only one you edit.

git clone https://github.com/RoleModel/rolemodel-openscreen.git
cd rolemodel-openscreen
pnpm run forks         # only if you need to build the app or run the review instance

The other three are not places to work:

  • homebrew-tap is a publish target. Homebrew resolves rolemodel/tap to a repository named homebrew-tap and nothing else, which is the whole reason it exists. The formula and casks live in packaging/ here; pnpm run sync-tap copies them across, and pnpm run check fails if the two drift.
  • the two forks stay forks, deliberately. Our diff on OpenScreen is 661 lines on top of 2260 upstream commits, and it is small on purpose — that is what makes git pull upstream main a non-event. Folding them into a monorepo would turn every upstream release into a manual merge of somebody else's project, which is a bill you pay forever to save a clone you do once. pnpm run forks fetches them as siblings when you need them, with the upstream remote already set up.

What exists, and why there are three repos

repo what it is you touch it when
rolemodel-openscreen The brand layer and the Studio — presets, wallpapers, narration, demo scripting, and the web UI that drives all of it. Almost always. This is the surface.
RoleModel/openscreen A fork of OpenScreen. Records the screen, edits the document, exports the MP4. Rarely — only for the app itself.

The fork is small on purpose: one new file plus a one-line change per call site, which is what keeps rebasing on upstream cheap. What it adds:

  • openscreen — openscreen open <doc> so a document can be handed to the editor; .openscreen registered as a document type; and this Studio hosted as a window in the app. Upstream has no way in from outside: its bundle declares no document type, open -a Openscreen <file> launches and discards the argument, and a bare path is a silent no-op.

1. Install

brew trust --tap rolemodel/tap
brew install rolemodel/tap/rm-video

That installs Node and twenty-three commands:

command does
rm-studio serves the Studio to a browser on :4600 — the developer path, not the one to run day to day
rm-video applies a brand preset to a document
rm-demo drives a browser from a script, or records one
rm-voice narration → audio + an exact SRT
rm-transcribe recording → local timed VTT transcript
rm-fal restyles one clip with a fal.ai model, back into the project
rm-mux reconciles narration timing against a recast render
rm-library builds the library index
rm-compose cuts scenes and footage into one document the editor opens
rm-cut reads a composition into a cut.json, and caches the proxies, filmstrips and peaks an editor draws from
rm-insert drops a title card into a recording you already have
rm-share sends a finished video for review
rm-setup checks every piece of the install and repairs what it can
rm-reconcile recomputes a cut's derived timings from its clips; --check fails when they disagree

pnpm run check asserts that this table, bin/, package.json and the formula all name the same set. They drifted three times before it did: rm-setup was in neither list even though install.sh ends by handing off to it — so the one-command install died at the finish line on a clean machine — and rm-share was in the package but not the formula, so brew shipped six while this table promised seven.

And the app:

brew trust --tap rolemodel/tap
brew install --cask rolemodel/tap/rolemodel-openscreen

It installs as Openscreen (/Applications/Openscreen.app). Open that app from Applications or Spotlight; RoleModel Studio is the workspace inside it. The cask's app stanza, the openscreen shim that puts the CLI on your PATH, and the DMG name the release job writes all use that filename. It is a RoleModel build of OpenScreen, and the About panel says so with a link to the original.

Manual step, once. The cask needs a release to point at, and GitHub disables all workflows on a forked repo until someone acknowledges it:

  1. Open https://github.com/RoleModel/openscreen/actions
  2. Click "I understand my workflows, go ahead and enable them"
  3. The v1.9.6-rm.1 tag is already pushed, so the build starts on its own
  4. Then, in the tap: node scripts/update-cask.mjs rolemodel-openscreen <tag> (same file as packaging/update-cask.mjs here — pnpm run sync-tap copies it)

That last command reads the release's DMGs, hashes them, and writes the version and checksums into the cask. Until it runs, the cask's version is 0.0.0-unreleased and installing it will 404.

Use the fork, not upstream. The Studio hands documents to the editor with openscreen open, and upstream has no such verb.


2. Make a video

Open Openscreen from Applications or Spotlight. RoleModel Studio opens inside it — main.ts opens it right after the first window, so there is nothing to start and no address to type. The port it uses is whatever was free, not 4600.

Not from a terminal: macOS gives Screen Recording to whatever hosts Electron, so a shell launch grants it to the terminal and the recorder fails looking like a bug. rm-studio and :4600 are for working on the Studio itself — see DEVELOPMENT.md.

Library is the index — one card per project, indexed automatically from whatever is on disk. Clicking a video opens it in the editor.

Editor lists every document in the library and opens one in the editor — which is a window in this same app, so nothing is exported until you say so. A video with no document yet gets one made and branded on the way in.

Review shares a finished video as a page of our own on the public bucket and shows the pages already out, with the notes people left on them. Notes are pinned to the moment they are about and come back into the Studio.

New video has three tabs:

  • Record a screen — pick a window by its title, name the output, run it. The chain is record → brand → stop, then a button to open it for editing. Export is a separate click, because exporting before you have edited produces a file nobody chose anything about.
  • Make from a script — hand a brief to Claude and let it build the pipeline.
  • From a test — a demo, either from a Playwright trace you already have or written here.

Record something happening. A capture of an idle window is eight seconds of nothing, and it looks like the pipeline is broken when it isn't.


3. Script a demo instead of recording one

A demo script is one markdown file. Prose is narration; fenced ```do blocks are what the browser does. Order is the timeline.

# Estimating walkthrough

We start on the estimating screen.

```do
goto https://your-app.example.com/quotes/new
expect "REQUEST QUOTE"
click "3D VIEW"
wait 800
```

Adding a railing is two clicks.

The same file feeds rm-voice unchanged — its parser ignores fenced blocks — so the narration and the actions can never drift into two files that disagree.

rm-demo check demo.md                  # what will it do?
rm-demo run demo.md --out ./out        # drive it, leaving a trace + screencast

Ten verbs: goto click dblclick hover type fill press wait scroll expect. A typo fails before a browser opens, with the line number and the correct form.

Or don't write it at all. lib/demo-record.mjs captures a demo by doing it: open the app, click through, and the clicks become the script. It names things by visible text, collapses per-character typing into one step, and keeps your real pauses as explicit waits — the pauses are what make a demo watchable and the first thing lost re-authoring by hand.


4. Share it for review

rm-share <project-id> Renders/demo.mp4 --title "Estimating walkthrough"
rm-share <project-id> --list
rm-share <project-id> --down estimating-walkthrough

Out comes a link a client opens with no account: the video, a poster, and a notes panel beside it. They leave notes on the frame they are about, rather than emailing "around the middle, the bit with the railing". The Review page in the Studio does the same with a button, and shows the notes as they arrive.

Where it goes: the public bucket and base URL set on a storage remote under Storage (https://assets.rolemodelsoftware.com/share/<project>/<slug>/). Notes live in the team database's video_comments table. The page reaches it through Neon's Data API as the anonymous role, which can read and add; the Studio reads and tidies through Drizzle as studio_app (lib/schema.mjs, lib/db.mjs). sql/video-comments.sql makes the table, the grants and the policies; run it once as the database owner.

Do I need Xcode?

No — and this is worth being precise about, because it reads like a much bigger requirement than it is.

you are you need
installing and using the pipeline nothing. Homebrew fetches a prebuilt app.
using the voice narration Command Line Tools, and only if pip has no wheel for your Python. xcode-select --install, a few hundred MB. rm-setup checks for it.
building the app from source full Xcode. Only the ScreenCaptureKit capture helper needs it — Swift with SwiftPM, which Command Line Tools alone cannot build.

That last row is why the app is built in CI: GitHub's macOS runners ship full Xcode, so nobody on the team has to. A release comes out of build.yml, the cask points at it, and brew install --cask puts a signed app on disk with no compiler involved.

If you do build locally without Xcode, you get a working app that cannot record. It brands, edits and exports fine — the capture helper is the only piece missing, and it fails loudly rather than producing empty video.

The parts that are not finished

Worth knowing before you promise any of it to anyone.

  • The cask needs the one click above. Until then there is no installable app, only a checkout.
  • A locally built openscreen cannot record. The ScreenCaptureKit helper needs full Xcode, which is why the release is built in CI. A local build brands, edits and exports fine.
  • The recorder is a library, not a Studio button yet. lib/demo-record.mjs works and is tested; nothing in the UI calls it.

When something breaks

symptom it is almost always
openscreen: not found on PATH the formula installs a shim; the upstream cask's symlink breaks Electron's helper resolution. Uninstall the upstream cask.
The Studio opens OpenScreen but not the editor you are on upstream's build, which has no open verb. Install the fork: brew install --cask rolemodel/tap/rolemodel-openscreen. From a checkout, npm run app in the fork does the same thing.
pnpm install installs no dev dependencies NODE_ENV=production is set in your shell. Remove it, then run pnpm install again.
Electron dies on Cannot find module …/record ELECTRON_RUN_AS_NODE leaked from an editor terminal. The toolkit strips it for children; a shell you ran it in yourself will not.
A capture has a black band under it the recorder padded a window into a display-sized buffer. rm-video brand detects it and writes a crop; no re-encode.
A thumbnail looks like a zoomed crop it was, and it is fixed — poster frames are chosen by measuring candidates, because --auto-zoom holds a zoom for seconds.

pnpm run check runs 354 assertions across the toolkit. It is the fastest way to find out whether something you changed broke something you were not looking at.