You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The example canvases ship inside the app. mockups/canvases is 23 folders (22 canvases and templates), 2,773 files and 422 MB, and desktop/package.json copies it whole into the package. Two plans do not fit that shape:
Examples should grow without an app release. Today a new example reaches a user only in the next dmg or exe.
A community library is planned, where other people contribute canvases. Their folders cannot land in the app's installer, and should not land in the app's repository either.
The costs already measured:
Today
macOS dmg, 1.5.3
538 MB arm64, 545 MB x64. About 130 MB of that is the app.
Windows exe, 1.5.3
515 MB
macOS differential update
51 MB in about 1,550 range requests, because the examples are thousands of small files inside the zip (docs/2026-09-21-auto-update.md)
A plugin install
About 430 MB on every product. The sparsePaths escape in the README works on Claude Code only; on Codex a sparse marketplace install fails.
Git pack
464 MiB, and every contributed canvas adds tens of MB to it for good
#122 proposed fetching the examples on first run and was closed as not planned: it saved size and update traffic only, against a downloader, a checksum, an unpack step, an offline failure path and a progress state. That reasoning was right for that goal. #122 also put "Updating examples between app releases" out of scope. That is now the goal, and so is the community library, so this issue reopens the question with a different answer to "what does it buy". The costs #122 named still apply and are priced below.
Proposal
1. A second repository holds the examples. For instance ReScienceLab/super-prototyping-examples. It is an ordinary project that uses this plugin: mockups/canvases/<slug>/ with gen.py as the source of truth, the same folder rules, the same README-with-evidence rule. refkit, sp and the skills already work against any project's boards, so nothing new is needed to regenerate a board there. Community canvases arrive as pull requests to that repository.
2. One archive per canvas, and one index.json, published as GitHub release assets of that repository. A workflow there packs each canvas that changed and publishes a release whose index.json lists every canvas:
A canvas that did not change keeps the URL of the release it was last packed in, so it is neither uploaded nor downloaded again. The client reads https://github.com/<org>/<repo>/releases/latest/download/index.json, which GitHub redirects to the newest release (checked against this repo: a 302 to the tagged asset). No API call, so no 60-per-hour limit, and no tag to overwrite. That URL only works in a repository whose latest release is the examples, which is one more reason for the second repository.
One archive for everything does not hold up: mockups/canvases changed in 177 commits on 21 of the last 31 days, so a single 325 MB pack would be downloaded again almost daily.
3. One fetch, in canvas/server. As #122 designed it, and as docs/2026-09-20-desktop-onboarding-and-skills.md decided for the skills copy: dist/server.mjs is the one piece of code the app and sp start both run. At start the server reads the index, downloads each canvas whose sha256 is not in ~/.cache/super-prototyping/examples/ yet, checks it, unpacks it with the system tar (bsdtar ships with macOS and with Windows 10 1803 and later; the Windows half still needs a check on a real machine), and renames it into place. That is the cache directory sp start already uses for canvas-dist.tgz, so there is still no configuration file. No new dependency. The app and sp start then show the same examples, which closes the disagreement #122 described.
4. The first screen does not wait. Start here opens at once and a canvas appears when its archive lands, with one line saying that examples are downloading. Offline, the bundled folders are all there is, and the next start tries again. No card for a canvas that is not downloaded yet, no picker.
5. What stays in this repo and in the app:templates, 00-welcome, and the four folders a skill reads by literal path (luma-ios, duolingo-ios, apple-settings, claude-ios). Together 59 MB and 497 files. The other 17 folders (362 MB, 325 MB gzipped) move. canvas/server reads one examples directory today, so showing the bundled folders beside the fetched ones is a design point for the change: a second root, or the bundled ones listed in the index like any other.
Why a second repository now, and not later
Blast radius. GitHub's DMCA policy can disable an entire repository. This repository also carries the app's releases, the updater feed and the URL the cask downloads from. Content that clones third-party interfaces should not be able to take those down with it.
The URL is baked into every shipped app. Moving the examples later strands the versions already installed. Where they live has to be settled before the first fetch ships.
Install and clone size. A plugin install drops from about 430 MB to about 65 MB of worktree once the 17 folders leave. History keeps the old blobs; a rewrite is not proposed here.
It is what the comparable projects do. Obsidian keeps a pointer-only index in obsidian-releases and the content in each author's release. Excalidraw keeps excalidraw-libraries apart from the app, with libraries.json and pull requests. Zed, Raycast and shadcn fetch from a registry and never clone the gallery. Homebrew, which this project already uses, is a pointer plus a sha256.
It costs no code. A repository is not machinery.
Before the first community canvas is shown to anyone
Board HTML must not be able to script the app's origin. On the canvas a board renders in <iframe srcDoc sandbox="">, which is already strict. The two places that load a board by URL (the boards sheet, and "Open this board as a page") should get the same content-security-policy: sandbox header that canvas/server/sp.ts already sends for chat attachments. Small, independent of the rest, and worth doing first.
A check in the examples repository's CI that rejects <script, on*= handlers and javascript: URLs in a board. None of today's 352 boards contains a script tag.
gen.py is arbitrary Python. The app never runs it, but a person or an agent may. It is reviewed in the pull request like any other code.
A notice and a removal contact. There is none in the repository today. One file in the examples repository.
Expected numbers
Today
After
macOS dmg
538 / 545 MB
about 190 MB (130 MB plus the 59 MB kept; an estimate, not a built dmg)
Windows exe
515 MB
the same proportion
macOS differential update
51 MB
not measured; 82% of the small files leave the zip
First run
nothing to fetch
325 MB once, in the background; later only the canvases that changed
Plugin install
about 430 MB
about 65 MB of worktree
What it costs
The list from #122, unchanged: a downloader, a checksum, an unpack step, an offline failure path, and one line of progress in canvas/src. Plus one workflow in the new repository. An honest guess is 100 to 150 lines in canvas/server.
Order of work
The content-security-policy: sandbox header on board responses. Independent.
The fetch in canvas/server, against the published index.
Narrow extraResources to the kept folders. Steps 3 and 4 ship in the same release: narrowing alone would take 17 examples away from users.
Remove the moved folders here, point CONTRIBUTING.md at the new repository for canvases, and update the install-size note in the README and the examples row in docs/2026-09-20-desktop-onboarding-and-skills.md.
Out of scope, and what would bring each in
A submission site, accounts, ratings or search. When "open a pull request" demonstrably turns contributors away.
Author, licence or tag fields in the index. When the first outside canvas merges.
A click-to-download card per canvas, or a picker. When the background download proves too heavy.
A mirror or CDN. When GitHub release downloads prove too slow for real users.
Rewriting this repository's history to shrink the pack.
A CLA. A Signed-off-by trailer is the cheaper answer if one is ever needed.
Decisions to make in the change
The second repository now, or only when the first outside contributor shows up. Proposed: now, for the reasons above.
Download in the background at first start, or only on a click. Proposed: in the background, which matches today's "everything is there".
Context
Supersedes #122. Related: #125 (comments on a read-only example canvas), #111. The research behind this was a read-only sweep of this repository, the comparable products, GitHub's hosting limits and policies, and the canvas's rendering and server code, with each angle fact-checked against primary sources.
Problem
The example canvases ship inside the app.
mockups/canvasesis 23 folders (22 canvases andtemplates), 2,773 files and 422 MB, anddesktop/package.jsoncopies it whole into the package. Two plans do not fit that shape:The costs already measured:
docs/2026-09-21-auto-update.md)sparsePathsescape in the README works on Claude Code only; on Codex a sparse marketplace install fails.What changed since #122
#122 proposed fetching the examples on first run and was closed as not planned: it saved size and update traffic only, against a downloader, a checksum, an unpack step, an offline failure path and a progress state. That reasoning was right for that goal. #122 also put "Updating examples between app releases" out of scope. That is now the goal, and so is the community library, so this issue reopens the question with a different answer to "what does it buy". The costs #122 named still apply and are priced below.
Proposal
1. A second repository holds the examples. For instance
ReScienceLab/super-prototyping-examples. It is an ordinary project that uses this plugin:mockups/canvases/<slug>/withgen.pyas the source of truth, the same folder rules, the same README-with-evidence rule.refkit,spand the skills already work against any project's boards, so nothing new is needed to regenerate a board there. Community canvases arrive as pull requests to that repository.2. One archive per canvas, and one
index.json, published as GitHub release assets of that repository. A workflow there packs each canvas that changed and publishes a release whoseindex.jsonlists every canvas:[ { "slug": "spotify-ios", "bytes": 31457280, "sha256": "…", "url": "https://github.com/…/releases/download/<tag>/spotify-ios.tgz" } ]A canvas that did not change keeps the URL of the release it was last packed in, so it is neither uploaded nor downloaded again. The client reads
https://github.com/<org>/<repo>/releases/latest/download/index.json, which GitHub redirects to the newest release (checked against this repo: a 302 to the tagged asset). No API call, so no 60-per-hour limit, and no tag to overwrite. That URL only works in a repository whose latest release is the examples, which is one more reason for the second repository.One archive for everything does not hold up:
mockups/canvaseschanged in 177 commits on 21 of the last 31 days, so a single 325 MB pack would be downloaded again almost daily.3. One fetch, in
canvas/server. As #122 designed it, and asdocs/2026-09-20-desktop-onboarding-and-skills.mddecided for the skills copy:dist/server.mjsis the one piece of code the app andsp startboth run. At start the server reads the index, downloads each canvas whosesha256is not in~/.cache/super-prototyping/examples/yet, checks it, unpacks it with the systemtar(bsdtar ships with macOS and with Windows 10 1803 and later; the Windows half still needs a check on a real machine), and renames it into place. That is the cache directorysp startalready uses forcanvas-dist.tgz, so there is still no configuration file. No new dependency. The app andsp startthen show the same examples, which closes the disagreement #122 described.4. The first screen does not wait. Start here opens at once and a canvas appears when its archive lands, with one line saying that examples are downloading. Offline, the bundled folders are all there is, and the next start tries again. No card for a canvas that is not downloaded yet, no picker.
5. What stays in this repo and in the app:
templates,00-welcome, and the four folders a skill reads by literal path (luma-ios,duolingo-ios,apple-settings,claude-ios). Together 59 MB and 497 files. The other 17 folders (362 MB, 325 MB gzipped) move.canvas/serverreads one examples directory today, so showing the bundled folders beside the fetched ones is a design point for the change: a second root, or the bundled ones listed in the index like any other.Why a second repository now, and not later
obsidian-releasesand the content in each author's release. Excalidraw keepsexcalidraw-librariesapart from the app, withlibraries.jsonand pull requests. Zed, Raycast and shadcn fetch from a registry and never clone the gallery. Homebrew, which this project already uses, is a pointer plus a sha256.Before the first community canvas is shown to anyone
<iframe srcDoc sandbox="">, which is already strict. The two places that load a board by URL (the boards sheet, and "Open this board as a page") should get the samecontent-security-policy: sandboxheader thatcanvas/server/sp.tsalready sends for chat attachments. Small, independent of the rest, and worth doing first.<script,on*=handlers andjavascript:URLs in a board. None of today's 352 boards contains a script tag.gen.pyis arbitrary Python. The app never runs it, but a person or an agent may. It is reviewed in the pull request like any other code.Expected numbers
What it costs
The list from #122, unchanged: a downloader, a checksum, an unpack step, an offline failure path, and one line of progress in
canvas/src. Plus one workflow in the new repository. An honest guess is 100 to 150 lines incanvas/server.Order of work
content-security-policy: sandboxheader on board responses. Independent.mockups/canvasesand need settling first.canvas/server, against the published index.extraResourcesto the kept folders. Steps 3 and 4 ship in the same release: narrowing alone would take 17 examples away from users.CONTRIBUTING.mdat the new repository for canvases, and update the install-size note in the README and the examples row indocs/2026-09-20-desktop-onboarding-and-skills.md.Out of scope, and what would bring each in
Signed-off-bytrailer is the cheaper answer if one is ever needed.Decisions to make in the change
00-welcomeandtemplates(1.3 MB) as Download the example canvases on first run instead of shipping them in the dmg #122 had it, which needs the skills to stop reading examples by path.Context
Supersedes #122. Related: #125 (comments on a read-only example canvas), #111. The research behind this was a read-only sweep of this repository, the comparable products, GitHub's hosting limits and policies, and the canvas's rendering and server code, with each angle fact-checked against primary sources.