protoreel 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. protoreel-0.1.0/.claude/settings.json +3 -0
  2. protoreel-0.1.0/.github/workflows/ci.yml +31 -0
  3. protoreel-0.1.0/.github/workflows/release.yml +31 -0
  4. protoreel-0.1.0/.gitignore +11 -0
  5. protoreel-0.1.0/CHANGELOG.md +11 -0
  6. protoreel-0.1.0/CLAUDE.md +32 -0
  7. protoreel-0.1.0/CONTRIBUTING.md +28 -0
  8. protoreel-0.1.0/HANDOFF.md +21 -0
  9. protoreel-0.1.0/LICENSE +21 -0
  10. protoreel-0.1.0/PKG-INFO +192 -0
  11. protoreel-0.1.0/README.md +167 -0
  12. protoreel-0.1.0/docs/architecture.md +63 -0
  13. protoreel-0.1.0/examples/README.md +8 -0
  14. protoreel-0.1.0/examples/like-button.html +67 -0
  15. protoreel-0.1.0/examples/like-button.json +10 -0
  16. protoreel-0.1.0/pyproject.toml +54 -0
  17. protoreel-0.1.0/scripts/ship.sh +104 -0
  18. protoreel-0.1.0/src/protoreel/__init__.py +4 -0
  19. protoreel-0.1.0/src/protoreel/__main__.py +5 -0
  20. protoreel-0.1.0/src/protoreel/audio.py +29 -0
  21. protoreel-0.1.0/src/protoreel/browser.py +37 -0
  22. protoreel-0.1.0/src/protoreel/cli.py +176 -0
  23. protoreel-0.1.0/src/protoreel/encoder.py +127 -0
  24. protoreel-0.1.0/src/protoreel/mcp_server.py +264 -0
  25. protoreel-0.1.0/src/protoreel/page_scripts/__init__.py +47 -0
  26. protoreel-0.1.0/src/protoreel/page_scripts/background.js +32 -0
  27. protoreel-0.1.0/src/protoreel/page_scripts/clock.js +40 -0
  28. protoreel-0.1.0/src/protoreel/page_scripts/cursor.js +14 -0
  29. protoreel-0.1.0/src/protoreel/renderer.py +270 -0
  30. protoreel-0.1.0/src/protoreel/settings.py +89 -0
  31. protoreel-0.1.0/src/protoreel/studio/__init__.py +1 -0
  32. protoreel-0.1.0/src/protoreel/studio/server.py +277 -0
  33. protoreel-0.1.0/src/protoreel/studio/static/index.html +591 -0
  34. protoreel-0.1.0/src/protoreel/takes.py +112 -0
  35. protoreel-0.1.0/src/protoreel/timeline.py +239 -0
  36. protoreel-0.1.0/src/protoreel/workspace.py +158 -0
  37. protoreel-0.1.0/tests/conftest.py +22 -0
  38. protoreel-0.1.0/tests/test_mcp.py +42 -0
  39. protoreel-0.1.0/tests/test_render.py +28 -0
  40. protoreel-0.1.0/tests/test_settings.py +28 -0
  41. protoreel-0.1.0/tests/test_stdio.py +16 -0
  42. protoreel-0.1.0/tests/test_studio.py +64 -0
  43. protoreel-0.1.0/tests/test_takes.py +26 -0
  44. protoreel-0.1.0/tests/test_timeline.py +54 -0
  45. protoreel-0.1.0/uv.lock +1313 -0
@@ -0,0 +1,3 @@
1
+ {
2
+ "includeCoAuthoredBy": false
3
+ }
@@ -0,0 +1,31 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
6
+ pull_request:
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os: [ubuntu-latest, macos-latest]
14
+ python: ["3.10", "3.13"]
15
+ runs-on: ${{ matrix.os }}
16
+ steps:
17
+ - uses: actions/checkout@v4
18
+ - uses: astral-sh/setup-uv@v6
19
+ with:
20
+ python-version: ${{ matrix.python }}
21
+ - name: Install
22
+ run: |
23
+ uv venv
24
+ uv pip install -e ".[dev]"
25
+ uv run python -m playwright install --with-deps chromium
26
+ - name: Lint
27
+ run: |
28
+ uv run ruff check src tests
29
+ uv run ruff format --check src tests
30
+ - name: Test
31
+ run: uv run pytest -q
@@ -0,0 +1,31 @@
1
+ name: Release
2
+
3
+ # Push a tag like v0.2.0 to build Protoreel and publish it to PyPI.
4
+ # One-time setup: on pypi.org, add this repository as a "trusted publisher"
5
+ # (workflow: release.yml, environment: pypi). No API token is needed.
6
+ on:
7
+ push:
8
+ tags: ["v*"]
9
+
10
+ jobs:
11
+ publish:
12
+ runs-on: ubuntu-latest
13
+ environment: pypi
14
+ permissions:
15
+ id-token: write # lets PyPI verify this workflow instead of using a password
16
+ contents: write # lets the job create the GitHub release
17
+ steps:
18
+ - uses: actions/checkout@v4
19
+ - uses: astral-sh/setup-uv@v6
20
+ - name: Check the tag matches the package version
21
+ run: |
22
+ version=$(grep -oP '__version__ = "\K[^"]+' src/protoreel/__init__.py)
23
+ test "v$version" = "${GITHUB_REF_NAME}" || { echo "Tag ${GITHUB_REF_NAME} does not match version $version"; exit 1; }
24
+ - name: Build
25
+ run: uv build
26
+ - name: Publish to PyPI
27
+ uses: pypa/gh-action-pypi-publish@release/v1
28
+ - name: Create GitHub release
29
+ env:
30
+ GH_TOKEN: ${{ github.token }}
31
+ run: gh release create "${GITHUB_REF_NAME}" dist/* --title "${GITHUB_REF_NAME}" --notes-file CHANGELOG.md
@@ -0,0 +1,11 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ .venv/
4
+ .pytest_cache/
5
+ .ruff_cache/
6
+ dist/
7
+ build/
8
+ *.egg-info/
9
+ renders/
10
+ takes/
11
+ .DS_Store
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 — 2026-10-13
4
+
5
+ First open-source release, grown out of a personal "Recorder Studio" script.
6
+
7
+ - MCP server with `list_workspace`, `record_video`, `render_take`, `preview_frame`, `open_studio` and `check_setup`
8
+ - Studio web app for hand-captured takes; takes are now saved to `takes/` so they can be re-rendered later
9
+ - Scripts gain `click`, `hover`, `type` and `scroll` actions, CSS-selector points, and late element lookup
10
+ - Bundled ffmpeg fallback and automatic Chromium install
11
+ - Six social formats from 1080 to 4K, optional backdrop and caption, prototype sound
@@ -0,0 +1,32 @@
1
+ # Protoreel — notes for Claude Code
2
+
3
+ Protoreel films HTML prototypes into social videos. It is a Python package with a CLI, an MCP
4
+ server and a small web Studio. Read `docs/architecture.md` for the map of modules.
5
+
6
+ ## Strict rule: authorship
7
+
8
+ Never add Claude, Claude Code or Cowork as an author or co-author of anything in this project.
9
+ No `Co-Authored-By` trailers, no "Generated with Claude Code" lines in commits, pull requests,
10
+ releases or files. Commits are authored by the repository owner only.
11
+
12
+ ## Commands
13
+
14
+ ```bash
15
+ uv venv && source .venv/bin/activate && uv pip install -e ".[dev]" # set up
16
+ python -m playwright install chromium # once per machine
17
+ pytest -q # ~30 s, real renders
18
+ ruff check src tests && ruff format src tests # lint + format
19
+ protoreel studio --folder ~/Playwright # run the Studio
20
+ protoreel record examples/like-button.html examples/like-button.json # scripted render
21
+ ```
22
+
23
+ ## Conventions
24
+
25
+ - Keep it readable for a junior developer: one job per module, plain names, short docstrings,
26
+ comments that explain *why*.
27
+ - Never print to stdout from code the MCP server runs: stdout carries the MCP protocol.
28
+ Progress goes to stderr (CLI) or `ctx.report_progress` (MCP).
29
+ - Errors a user can fix raise `ValueError` / `FileNotFoundError` with a helpful message;
30
+ `mcp_server.user_errors` passes those messages through to the AI client.
31
+ - The MCP SDK is v2 (`mcp.server.mcpserver.MCPServer`), not v1's `FastMCP`.
32
+ - Version lives in `src/protoreel/__init__.py`; update `CHANGELOG.md` with it.
@@ -0,0 +1,28 @@
1
+ # Contributing
2
+
3
+ Thanks for helping! Protoreel aims to stay small and readable, so a new contributor can
4
+ understand any file in one sitting.
5
+
6
+ ## Set up
7
+
8
+ ```bash
9
+ git clone https://github.com/iamkingsleey/protoreel && cd protoreel
10
+ uv venv && source .venv/bin/activate
11
+ uv pip install -e ".[dev]"
12
+ python -m playwright install chromium
13
+ pytest
14
+ ```
15
+
16
+ ## House rules
17
+
18
+ - **Name things for what they mean** to someone making videos (`take`, `frame`, `format`), not how they're implemented.
19
+ - **One job per module.** If a file starts doing two things, split it. [docs/architecture.md](docs/architecture.md) is the map; update it when the map changes.
20
+ - **Explain why, not what,** in comments. Short docstrings on every public function.
21
+ - **Readable errors.** Anything a user can fix should say what went wrong and what to do.
22
+ - `ruff check` and `ruff format` before you push; CI runs both, plus the tests.
23
+
24
+ ## Making a release
25
+
26
+ 1. Bump `__version__` in `src/protoreel/__init__.py` and add a section to `CHANGELOG.md`.
27
+ 2. Commit, then tag: `git tag v0.2.0 && git push --tags`.
28
+ 3. The *Release* workflow builds the package and publishes it to PyPI.
@@ -0,0 +1,21 @@
1
+ # Handing Protoreel to Claude Code
2
+
3
+ Open Terminal and run:
4
+
5
+ ```bash
6
+ cd ~/Playwright/protoreel
7
+ claude
8
+ ```
9
+
10
+ Then paste this prompt:
11
+
12
+ > Read CLAUDE.md and docs/architecture.md, and follow its authorship rule strictly: never add
13
+ > yourself as a co-author or add "Generated with Claude" lines anywhere. Then ship Protoreel for me by running
14
+ > `scripts/ship.sh ~/Playwright`. If a step fails, fix the cause and re-run the script
15
+ > (it is safe to re-run). If GitHub login is needed, ask me to run `gh auth login`.
16
+ > When it finishes, walk me through the PyPI "pending publisher" step it prints, then push
17
+ > the v0.1.0 tag and watch the Release workflow until it succeeds. Finally, confirm the
18
+ > MCP server works by listing the protoreel tools.
19
+
20
+ That's it. After this, you never need `Recorder.command` again: ask Claude to
21
+ "open the Protoreel studio", or to record a prototype for you.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kingsley Mkpandiok
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,192 @@
1
+ Metadata-Version: 2.5
2
+ Name: protoreel
3
+ Version: 0.1.0
4
+ Summary: Turn HTML prototypes into frame-perfect social videos — from a Studio app or any MCP client.
5
+ Project-URL: Homepage, https://github.com/iamkingsleey/protoreel
6
+ Project-URL: Issues, https://github.com/iamkingsleey/protoreel/issues
7
+ Author: Kingsley Mkpandiok
8
+ License-Expression: MIT
9
+ License-File: LICENSE
10
+ Keywords: animation,design,mcp,playwright,prototype,screen-recording,video
11
+ Classifier: Development Status :: 4 - Beta
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Topic :: Multimedia :: Video
16
+ Requires-Python: >=3.10
17
+ Requires-Dist: imageio-ffmpeg>=0.5
18
+ Requires-Dist: mcp<3,>=2.0
19
+ Requires-Dist: playwright>=1.45
20
+ Provides-Extra: dev
21
+ Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
22
+ Requires-Dist: pytest>=8; extra == 'dev'
23
+ Requires-Dist: ruff>=0.6; extra == 'dev'
24
+ Description-Content-Type: text/markdown
25
+
26
+ # Protoreel
27
+
28
+ **Turn HTML prototypes into frame-perfect social videos — by hand in a small Studio app, or by asking your AI assistant.**
29
+
30
+ Protoreel opens your prototype in a headless browser, plays an interaction (clicks, drags, typing, scrolling) with a smooth on-screen cursor, and films it one frame at a time into an MP4. Because the page's clock only moves when Protoreel says so, videos never stutter or drop frames, whatever machine you run them on. If your prototype makes sound, the sound comes along too.
31
+
32
+ - **Six social formats** — 4:5 feed, 9:16 reels, 1:1, 16:9, and two Dribbble sizes — up to 4K
33
+ - **An MCP server** — Claude, Cursor, VS Code and other MCP clients can record videos for you
34
+ - **A Studio app** — perform the interaction yourself, then render it in any format
35
+ - **Sound** — prototypes can hand Protoreel their audio, mixed under the video
36
+ - **No setup beyond one command** — Chromium and ffmpeg are fetched for you
37
+
38
+ ---
39
+
40
+ ## Quick start
41
+
42
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/) (one line to install). Then:
43
+
44
+ ```bash
45
+ uvx protoreel setup --folder ~/Prototypes
46
+ ```
47
+
48
+ That downloads Chromium (once), creates the folder, and prints the config for your AI client. Put your `.html` prototypes in that folder.
49
+
50
+ ### Use it from Claude, Cursor or VS Code (MCP)
51
+
52
+ **Claude Desktop / Cowork** — open *Settings → Developer → Edit Config* and add:
53
+
54
+ ```json
55
+ {
56
+ "mcpServers": {
57
+ "protoreel": {
58
+ "command": "uvx",
59
+ "args": ["protoreel", "mcp", "--folder", "/Users/you/Prototypes"]
60
+ }
61
+ }
62
+ }
63
+ ```
64
+
65
+ If Claude says it can't start the server, replace `"uvx"` with the full path that `which uvx` prints (desktop apps don't always see your shell's PATH). `protoreel setup` prints the snippet with the full path filled in.
66
+
67
+ **Claude Code**
68
+
69
+ ```bash
70
+ claude mcp add protoreel -- uvx protoreel mcp --folder ~/Prototypes
71
+ ```
72
+
73
+ **Cursor** (`.cursor/mcp.json`) uses the same block as Claude Desktop. **VS Code** (`.vscode/mcp.json`) uses the key `"servers"` instead of `"mcpServers"`.
74
+
75
+ Restart the app, then just ask:
76
+
77
+ > *"Record checkout.html as a 9:16 video: tap the Add to bag button, wait a second, then open the bag."*
78
+
79
+ > *"Open the Protoreel studio for like-button.html so I can record it myself."*
80
+
81
+ > *"Re-render my last take as a 4:5 and a 16:9."*
82
+
83
+ ### Use the Studio yourself
84
+
85
+ ```bash
86
+ uvx protoreel studio --folder ~/Prototypes
87
+ ```
88
+
89
+ Pick a prototype, choose a format, press **Capture**, perform the interaction with your mouse, press **Esc**, then **Render**. Every take is saved to `takes/`, so you (or your AI assistant) can render it again later in another format.
90
+
91
+ ### Use the command line
92
+
93
+ ```bash
94
+ uvx protoreel record like-button.html examples/like-button.json --format 9x16 --zoom 2.4
95
+ uvx protoreel render-take like-button_20261013-101500.json --format 16x9 --res 4k
96
+ ```
97
+
98
+ ---
99
+
100
+ ## The MCP tools
101
+
102
+ | Tool | What it does |
103
+ | --- | --- |
104
+ | `list_workspace` | Lists prototypes, recent takes and recent videos |
105
+ | `record_video` | Films a prototype following a script (below) |
106
+ | `render_take` | Films a take captured in the Studio, in any format |
107
+ | `preview_frame` | Returns one frame as an image, to check framing and zoom |
108
+ | `open_studio` | Opens the Studio in your browser |
109
+ | `check_setup` | Checks Chromium and ffmpeg (and can install Chromium) |
110
+
111
+ ## Scripts
112
+
113
+ A script is a list of actions, played in order. Elements are found with CSS selectors *at the moment each action starts*, so menus that open mid-video work naturally.
114
+
115
+ ```json
116
+ {
117
+ "start": ["80%", "85%"],
118
+ "actions": [
119
+ {"wait": 400},
120
+ {"click": "#add-to-bag", "dur": 800},
121
+ {"wait": 900},
122
+ {"type": "Lekki Phase 1", "delay": 60},
123
+ {"key": "Enter"},
124
+ {"drag": {"by": [0, -220]}, "dur": 900},
125
+ {"scroll": {"to": "#reviews"}, "dur": 1000},
126
+ {"wait": 1200}
127
+ ]
128
+ }
129
+ ```
130
+
131
+ | Action | Meaning |
132
+ | --- | --- |
133
+ | `{"wait": 500}` | hold still for 500 ms |
134
+ | `{"move": POINT, "dur": 600}` / `{"hover": POINT}` | glide the pointer to a point |
135
+ | `{"click": POINT, "dur": 500}` | glide there, then press and release (`{"click": true}` clicks in place) |
136
+ | `{"drag": {"to": POINT} or {"by": [dx, dy]}, "dur": 900}` | press, move, release |
137
+ | `{"down": true}` / `{"up": true}` | press or release in place |
138
+ | `{"key": "Enter"}` | press a key ([names](https://playwright.dev/docs/api/class-keyboard#keyboard-press)) |
139
+ | `{"type": "Hello", "delay": 70}` | type text, one character every 70 ms |
140
+ | `{"scroll": {"by": [0, 400]} or {"to": POINT}, "dur": 800}` | scroll smoothly |
141
+
142
+ A **POINT** is `[x, y]` in CSS pixels, `["50%", "60%"]` of the frame, `"#selector"`, or `{"el": "#selector", "x": 0.5, "y": 0.5}` for a spot inside an element. Any action can take `"ease"`: `smooth` (default, like a hand), `linear`, `in`, `out` or `inout`.
143
+
144
+ ## Formats and zoom
145
+
146
+ | Format | Size at 1080 | Typical use |
147
+ | --- | --- | --- |
148
+ | `4x5` | 1080 × 1350 | Instagram / LinkedIn feed |
149
+ | `9x16` | 1080 × 1920 | Reels, TikTok, Shorts |
150
+ | `1x1` | 1080 × 1080 | Square |
151
+ | `16x9` | 1920 × 1080 | Landscape, YouTube, X |
152
+ | `4x3` | 1600 × 1200 | Dribbble |
153
+ | `3x2` | 1800 × 1200 | Dribbble wide |
154
+
155
+ **Zoom** (0.6–3.6, default 2) is how large the prototype appears: the page is laid out at *format ÷ zoom* CSS pixels and rendered sharply at that scale. **Resolution** (`1080`, `2k`, `3k`, `4k`) adds pixels without changing the framing.
156
+
157
+ ## Sound
158
+
159
+ Headless browsers can't record their own audio, so prototypes that make sound opt in with one function:
160
+
161
+ ```js
162
+ // Return a base64 WAV of the sounds that played between startMs and startMs + durationMs
163
+ // (on the page's clock, i.e. performance.now()). A Promise of the string is fine too.
164
+ window.__recorderAudio = function (startMs, durationMs) { /* ... */ };
165
+ ```
166
+
167
+ The usual pattern is to log each sound as it plays and replay the log into an `OfflineAudioContext`. [`examples/like-button.html`](examples/like-button.html) shows the whole thing in about 15 lines. Pages without the function produce silent videos.
168
+
169
+ ## Folder layout
170
+
171
+ ```
172
+ ~/Prototypes/ your .html prototypes (full pages or bare fragments)
173
+ ~/Prototypes/renders/ finished videos
174
+ ~/Prototypes/takes/ takes captured in the Studio
175
+ ```
176
+
177
+ Set the folder with `--folder`, or the `PROTOREEL_HOME` environment variable (default `~/Protoreel`).
178
+
179
+ ## Troubleshooting
180
+
181
+ - **"Chromium is not installed"** — run `uvx protoreel setup`. Behind a strict network? Point `PROTOREEL_CHROMIUM` at an existing Chrome/Chromium.
182
+ - **The Studio port is busy** — another Studio is probably running; `protoreel studio --port 8800` picks another.
183
+ - **The video is silent** — the page doesn't define `window.__recorderAudio`; see *Sound*.
184
+ - **Clicks miss** — use a selector rather than coordinates, and check with `preview_frame`.
185
+
186
+ ## Contributing
187
+
188
+ Protoreel is small on purpose and written to be easy to read. Start with [docs/architecture.md](docs/architecture.md), then see [CONTRIBUTING.md](CONTRIBUTING.md).
189
+
190
+ ## License
191
+
192
+ MIT © Kingsley Mkpandiok
@@ -0,0 +1,167 @@
1
+ # Protoreel
2
+
3
+ **Turn HTML prototypes into frame-perfect social videos — by hand in a small Studio app, or by asking your AI assistant.**
4
+
5
+ Protoreel opens your prototype in a headless browser, plays an interaction (clicks, drags, typing, scrolling) with a smooth on-screen cursor, and films it one frame at a time into an MP4. Because the page's clock only moves when Protoreel says so, videos never stutter or drop frames, whatever machine you run them on. If your prototype makes sound, the sound comes along too.
6
+
7
+ - **Six social formats** — 4:5 feed, 9:16 reels, 1:1, 16:9, and two Dribbble sizes — up to 4K
8
+ - **An MCP server** — Claude, Cursor, VS Code and other MCP clients can record videos for you
9
+ - **A Studio app** — perform the interaction yourself, then render it in any format
10
+ - **Sound** — prototypes can hand Protoreel their audio, mixed under the video
11
+ - **No setup beyond one command** — Chromium and ffmpeg are fetched for you
12
+
13
+ ---
14
+
15
+ ## Quick start
16
+
17
+ You need [uv](https://docs.astral.sh/uv/getting-started/installation/) (one line to install). Then:
18
+
19
+ ```bash
20
+ uvx protoreel setup --folder ~/Prototypes
21
+ ```
22
+
23
+ That downloads Chromium (once), creates the folder, and prints the config for your AI client. Put your `.html` prototypes in that folder.
24
+
25
+ ### Use it from Claude, Cursor or VS Code (MCP)
26
+
27
+ **Claude Desktop / Cowork** — open *Settings → Developer → Edit Config* and add:
28
+
29
+ ```json
30
+ {
31
+ "mcpServers": {
32
+ "protoreel": {
33
+ "command": "uvx",
34
+ "args": ["protoreel", "mcp", "--folder", "/Users/you/Prototypes"]
35
+ }
36
+ }
37
+ }
38
+ ```
39
+
40
+ If Claude says it can't start the server, replace `"uvx"` with the full path that `which uvx` prints (desktop apps don't always see your shell's PATH). `protoreel setup` prints the snippet with the full path filled in.
41
+
42
+ **Claude Code**
43
+
44
+ ```bash
45
+ claude mcp add protoreel -- uvx protoreel mcp --folder ~/Prototypes
46
+ ```
47
+
48
+ **Cursor** (`.cursor/mcp.json`) uses the same block as Claude Desktop. **VS Code** (`.vscode/mcp.json`) uses the key `"servers"` instead of `"mcpServers"`.
49
+
50
+ Restart the app, then just ask:
51
+
52
+ > *"Record checkout.html as a 9:16 video: tap the Add to bag button, wait a second, then open the bag."*
53
+
54
+ > *"Open the Protoreel studio for like-button.html so I can record it myself."*
55
+
56
+ > *"Re-render my last take as a 4:5 and a 16:9."*
57
+
58
+ ### Use the Studio yourself
59
+
60
+ ```bash
61
+ uvx protoreel studio --folder ~/Prototypes
62
+ ```
63
+
64
+ Pick a prototype, choose a format, press **Capture**, perform the interaction with your mouse, press **Esc**, then **Render**. Every take is saved to `takes/`, so you (or your AI assistant) can render it again later in another format.
65
+
66
+ ### Use the command line
67
+
68
+ ```bash
69
+ uvx protoreel record like-button.html examples/like-button.json --format 9x16 --zoom 2.4
70
+ uvx protoreel render-take like-button_20261013-101500.json --format 16x9 --res 4k
71
+ ```
72
+
73
+ ---
74
+
75
+ ## The MCP tools
76
+
77
+ | Tool | What it does |
78
+ | --- | --- |
79
+ | `list_workspace` | Lists prototypes, recent takes and recent videos |
80
+ | `record_video` | Films a prototype following a script (below) |
81
+ | `render_take` | Films a take captured in the Studio, in any format |
82
+ | `preview_frame` | Returns one frame as an image, to check framing and zoom |
83
+ | `open_studio` | Opens the Studio in your browser |
84
+ | `check_setup` | Checks Chromium and ffmpeg (and can install Chromium) |
85
+
86
+ ## Scripts
87
+
88
+ A script is a list of actions, played in order. Elements are found with CSS selectors *at the moment each action starts*, so menus that open mid-video work naturally.
89
+
90
+ ```json
91
+ {
92
+ "start": ["80%", "85%"],
93
+ "actions": [
94
+ {"wait": 400},
95
+ {"click": "#add-to-bag", "dur": 800},
96
+ {"wait": 900},
97
+ {"type": "Lekki Phase 1", "delay": 60},
98
+ {"key": "Enter"},
99
+ {"drag": {"by": [0, -220]}, "dur": 900},
100
+ {"scroll": {"to": "#reviews"}, "dur": 1000},
101
+ {"wait": 1200}
102
+ ]
103
+ }
104
+ ```
105
+
106
+ | Action | Meaning |
107
+ | --- | --- |
108
+ | `{"wait": 500}` | hold still for 500 ms |
109
+ | `{"move": POINT, "dur": 600}` / `{"hover": POINT}` | glide the pointer to a point |
110
+ | `{"click": POINT, "dur": 500}` | glide there, then press and release (`{"click": true}` clicks in place) |
111
+ | `{"drag": {"to": POINT} or {"by": [dx, dy]}, "dur": 900}` | press, move, release |
112
+ | `{"down": true}` / `{"up": true}` | press or release in place |
113
+ | `{"key": "Enter"}` | press a key ([names](https://playwright.dev/docs/api/class-keyboard#keyboard-press)) |
114
+ | `{"type": "Hello", "delay": 70}` | type text, one character every 70 ms |
115
+ | `{"scroll": {"by": [0, 400]} or {"to": POINT}, "dur": 800}` | scroll smoothly |
116
+
117
+ A **POINT** is `[x, y]` in CSS pixels, `["50%", "60%"]` of the frame, `"#selector"`, or `{"el": "#selector", "x": 0.5, "y": 0.5}` for a spot inside an element. Any action can take `"ease"`: `smooth` (default, like a hand), `linear`, `in`, `out` or `inout`.
118
+
119
+ ## Formats and zoom
120
+
121
+ | Format | Size at 1080 | Typical use |
122
+ | --- | --- | --- |
123
+ | `4x5` | 1080 × 1350 | Instagram / LinkedIn feed |
124
+ | `9x16` | 1080 × 1920 | Reels, TikTok, Shorts |
125
+ | `1x1` | 1080 × 1080 | Square |
126
+ | `16x9` | 1920 × 1080 | Landscape, YouTube, X |
127
+ | `4x3` | 1600 × 1200 | Dribbble |
128
+ | `3x2` | 1800 × 1200 | Dribbble wide |
129
+
130
+ **Zoom** (0.6–3.6, default 2) is how large the prototype appears: the page is laid out at *format ÷ zoom* CSS pixels and rendered sharply at that scale. **Resolution** (`1080`, `2k`, `3k`, `4k`) adds pixels without changing the framing.
131
+
132
+ ## Sound
133
+
134
+ Headless browsers can't record their own audio, so prototypes that make sound opt in with one function:
135
+
136
+ ```js
137
+ // Return a base64 WAV of the sounds that played between startMs and startMs + durationMs
138
+ // (on the page's clock, i.e. performance.now()). A Promise of the string is fine too.
139
+ window.__recorderAudio = function (startMs, durationMs) { /* ... */ };
140
+ ```
141
+
142
+ The usual pattern is to log each sound as it plays and replay the log into an `OfflineAudioContext`. [`examples/like-button.html`](examples/like-button.html) shows the whole thing in about 15 lines. Pages without the function produce silent videos.
143
+
144
+ ## Folder layout
145
+
146
+ ```
147
+ ~/Prototypes/ your .html prototypes (full pages or bare fragments)
148
+ ~/Prototypes/renders/ finished videos
149
+ ~/Prototypes/takes/ takes captured in the Studio
150
+ ```
151
+
152
+ Set the folder with `--folder`, or the `PROTOREEL_HOME` environment variable (default `~/Protoreel`).
153
+
154
+ ## Troubleshooting
155
+
156
+ - **"Chromium is not installed"** — run `uvx protoreel setup`. Behind a strict network? Point `PROTOREEL_CHROMIUM` at an existing Chrome/Chromium.
157
+ - **The Studio port is busy** — another Studio is probably running; `protoreel studio --port 8800` picks another.
158
+ - **The video is silent** — the page doesn't define `window.__recorderAudio`; see *Sound*.
159
+ - **Clicks miss** — use a selector rather than coordinates, and check with `preview_frame`.
160
+
161
+ ## Contributing
162
+
163
+ Protoreel is small on purpose and written to be easy to read. Start with [docs/architecture.md](docs/architecture.md), then see [CONTRIBUTING.md](CONTRIBUTING.md).
164
+
165
+ ## License
166
+
167
+ MIT © Kingsley Mkpandiok
@@ -0,0 +1,63 @@
1
+ # How Protoreel is built
2
+
3
+ Protoreel is about 1,800 lines of Python (half of it comments and docstrings) plus three small JavaScript files and the Studio page. Each module does one job.
4
+
5
+ ```
6
+ src/protoreel/
7
+ ├── cli.py command line: studio, record, render-take, mcp, setup
8
+ ├── mcp_server.py the MCP server and its six tools
9
+ ├── renderer.py opens a page, plays an interaction, films it frame by frame
10
+ ├── timeline.py scripts and takes → one input state per frame
11
+ ├── takes.py takes captured in the Studio: load, save, smooth, re-centre
12
+ ├── settings.py formats, resolutions and defaults
13
+ ├── workspace.py the folder of prototypes, renders and takes; a tiny file server
14
+ ├── encoder.py ffmpeg: PNG frames in, MP4 out, plus the soundtrack
15
+ ├── audio.py asks the page for its sound (window.__recorderAudio)
16
+ ├── browser.py launches Chromium, installing it on first use
17
+ ├── page_scripts/ JavaScript injected into the prototype
18
+ │ ├── clock.js freezes time so we can step it
19
+ │ ├── cursor.js the on-screen pointer
20
+ │ └── background.js optional backdrop colour
21
+ └── studio/
22
+ ├── server.py the Studio's web server and JSON API
23
+ └── static/index.html the Studio app (plain HTML/CSS/JS)
24
+ ```
25
+
26
+ ## The one idea that makes it work: a frozen clock
27
+
28
+ Normally a browser animates in real time, so a slow machine (or a slow screenshot) drops frames.
29
+ `page_scripts/clock.js` replaces `requestAnimationFrame`, `setTimeout`, `setInterval`,
30
+ `performance.now`, `Date.now` and every CSS/Web Animation with versions driven by a number we
31
+ control. The renderer then repeats, for every frame:
32
+
33
+ 1. apply this frame's input (move the mouse, press, type, scroll) — `renderer.Player.step`
34
+ 2. advance the page's clock by exactly 1/60 s — `window.__advance(ms)`
35
+ 3. take a screenshot and pipe it to ffmpeg — `encoder.VideoEncoder`
36
+
37
+ So a 5-second video at 60 fps is exactly 300 screenshots, each showing precisely the right moment.
38
+
39
+ ## From an interaction to frames
40
+
41
+ `timeline.py` turns both kinds of interaction into the same stream of `FrameInput` objects:
42
+
43
+ - **Takes** (`take_frames`) replay captured events at their recorded times.
44
+ - **Scripts** (`script_frames`) expand each action into frames. Element positions are looked up
45
+ *when the action starts*, so earlier actions can open menus or move things around.
46
+
47
+ The renderer doesn't know or care which kind it is playing.
48
+
49
+ ## Where things run
50
+
51
+ | You run | What happens |
52
+ | --- | --- |
53
+ | `protoreel studio` | `studio.server.Studio` serves the app at http://127.0.0.1:8765 |
54
+ | `protoreel mcp` | `mcp_server.build_server` serves tools over stdio; `open_studio` starts the Studio in a background thread |
55
+ | `protoreel record` | `renderer.render` directly |
56
+
57
+ Every render serves the prototype's folder from a throwaway local web server
58
+ (`workspace.PrototypeServer`), so pages load over `http://` and behave like they do in a real browser.
59
+
60
+ ## Tests
61
+
62
+ `pytest` runs everything, including real renders, a real Studio and a real MCP session over
63
+ stdio (about 30 seconds). Unit tests for the timeline and takes need no browser.
@@ -0,0 +1,8 @@
1
+ # Examples
2
+
3
+ `like-button.html` is a tiny prototype with an animation, a counter and sound (see how it
4
+ implements `window.__recorderAudio`). `like-button.json` is a script that likes it.
5
+
6
+ ```bash
7
+ uvx protoreel record examples/like-button.html examples/like-button.json --format 1x1 --zoom 2.5
8
+ ```