{
  "markdown": "# Truepane\n\n**Truepane** is a free, open-source screenshot-set builder for the App Store and Google\nPlay. Compose and localize a complete release visually in the browser, or hand the same\nproject to an AI agent through the bundled MCP server. Try it at\n[truepane.dev](https://truepane.dev).\n\nEverything renders to `<canvas>` in the browser. Screenshots stay on your machine.\nOptional AI helpers send only the text you ask them to process (and, when supplied,\nyour Groq API key) to the configured Edge Function and model provider. Google Fonts\nare also loaded from Google's CDN unless you use a system or uploaded font.\n\n![Truepane editor: compose slides, tune the background, and prepare store-ready exports](public/truepane-demo.gif)\n\n## Guides\n\n- [Create App Store screenshots with Codex or Claude Code](docs/tutorials/create-app-store-screenshots-with-codex-or-claude-code.md)\n- [Update localized App Store screenshots without uploading them](docs/tutorials/update-localized-app-store-screenshots-without-uploading.md)\n\n## Why this exists\n\nMost screenshot generators composite a pre-rendered PNG of a phone. This one **draws\nthe device frame procedurally** on a canvas — body, bezel, buttons, camera, and the\nscreen mask are all geometry, not bitmaps. That choice drives most of what makes the\ntool small, sharp, and cheap to run.\n\n## Features\n\n- **Procedural device frames** at exact store sizes: iPhone 6.9″ (1320×2868), iPad 13″\n  (2064×2752), Android phone (1080×2400), Android tablet (1600×2560).\n- **Backgrounds = fill + shape.** A fill layer (solid, or linear/radial gradient) plus an\n  optional shape overlay (rings, blobs, waves, dots, mesh, arcs, triangles, grid, zigzag,\n  bubbles), each with independent colors. Shapes flow continuously across the strip and\n  reproduce exactly from a stored seed.\n- **Custom background images**: upload a backdrop, then control blur, fit, opacity, scrim,\n  and whether it spans one slide or the full strip. Imported images are resized locally\n  to a practical render budget.\n- **One composable shape family.** Alongside the ten fixed looks, `Custom…` is a parameter\n  surface rather than a preset: pick a primitive (ring, disc, arc, triangle, bar, blob) and\n  an arrangement (scatter, grid, row, radial, wave), then tune count, size, spacing,\n  rotation, stroke weight, and a fade along the strip. It is **data, not code** — twelve\n  bounded numbers and two closed enums — so a shared project file stays inert, renders\n  identically forever, and can be diffed. That makes it the intended surface for\n  agent-driven variation over MCP: an agent composes a background by filling in numbers\n  instead of shipping drawing code. Its controls live behind **Advanced** in the background\n  panel so the picker stays uncluttered.\n- **Color tools**: content-based palette extraction from your screenshot, harmonized shape\n  suggestions, an eyedropper (native EyeDropper API + a click-a-slide fallback for\n  Safari/Firefox), and curated color-science presets.\n- **AI prompt → style** (optional): describe a vibe (\"calm, warm, organic\") and a Groq\n  model returns a style + palette. Bring your own Groq key, or use the hosted endpoint.\n- **Typography**: curated Google Fonts (incl. Apple/Android system fonts and Noto\n  multi-script + CJK) plus custom `.ttf/.otf/.woff(2)` upload.\n- **Flexible composition**: choose a preset or place text and devices directly,\n  resize them, rotate devices from −20° to +20°, and drag or nudge mockups on canvas.\n- **Cross-slide devices**: span one synchronized device across two adjacent slides.\n  Both clipped halves share their screenshot, position, scale, and rotation.\n- **Export**: per-slide PNG, one horizontal strip PNG, or a ZIP of everything. Plus JSON\n  project import/export. The editor auto-saves documents and content-addressed binary\n  assets to IndexedDB, with a localStorage fallback where IndexedDB is unavailable.\n- **Responsive editor with history**: purpose-built desktop and mobile layouts, light and\n  dark themes, and a 100-step undo/redo history with standard keyboard shortcuts.\n- **Multi-platform projects**: keep separate iPhone, iPad, Android phone, and Android\n  tablet captures on the same ordered slides. Locale screenshots fall back only to\n  their own target's source capture; they never borrow another platform's image.\n- **Localization**: store translated copy, locale-specific captures, and per-language\n  fonts in one project. Translation can be entered manually, generated through the\n  optional AI helper, or produced by an MCP-connected agent.\n- **Preview-first bulk import**: choose a folder or ZIP, review deterministic\n  target/locale/slide mappings, correct rows, then apply once. Explicit\n  `target/locale/NN-name.png` paths take priority; conflicts never overwrite silently.\n- **Shared release preflight**: the editor and MCP report the same stable issue\n  codes for missing captures/translations, locale fallbacks, crop risk, composition,\n  fonts, and fill/text contrast. Warnings are advisory and require “export anyway”\n  in the web editor.\n- **Local brand kits**: save typography, text colors, background, custom font, and\n  default composition for reuse. Kits are portable `.truepane-brand.json` files and\n  never contain slides, screenshots, targets, translations, credentials, or history.\n- **Flexible output surfaces**: keep the four native store screenshot sizes, render a\n  Google Play feature graphic at exactly 1024×500, or choose bounded custom dimensions.\n  The procedural device is scaled and placed as a layer; captures are never stretched.\n- **Release update mode**: explicitly save deterministic release signatures, compare\n  added/changed/unchanged/removed assets later, and export a changed-only ZIP with a\n  manifest. Baselines contain hashes, not rendered PNGs, and never update implicitly.\n\n## Design decisions (the interesting part)\n\nEach of these was a deliberate fork, chosen for a reason:\n\n- **Procedural frames, not image mockups.** The target is *flat store-submission*\n  screenshots at exact required resolutions — which procedural drawing nails: crisp at\n  any scale, no asset pipeline, and **no licensing exposure** (most \"free\" device-mockup\n  packs are not actually clean for commercial redistribution). Flat 2D rotation is\n  supported; perspective and photographic mockups remain out of scope.\n- **Parametric backgrounds, not diffusion images.** Backgrounds are seeded procedural\n  shapes that reproduce exactly and stay tasteful. A raster image model would be\n  unpredictable, costly per call, hard to keep consistent across a set, and would force a\n  heavier backend. AI is used only as a thin **prompt → parameters** layer.\n- **Content-based palette** runs entirely client-side — no model, no cost.\n- **$0-egress architecture.** The app is static and all image work happens in the\n  browser, so hosting bandwidth is effectively free. The only metered cost is the\n  optional AI prompt call, which is rate-limited and can be replaced with your own key.\n\n## Architecture\n\n- **`src/core/`** — platform-neutral project logic shared by the browser and MCP server:\n  canvas rendering, normalization, composition, output validation, preflight, bulk\n  import, brand kits, release comparison, and background-image preparation.\n  **`src/core/render.ts`** defines the device frames and paints every pixel.\n  - **Concentric-corner invariant:** the BODY / BEZEL / SCREEN rounded rects share a\n    center of curvature (`x + r` equal across all three; same for `y + r`). Breaking it\n    produces \"laddery\" corner kinks. New frames go through `defineFrame()`, which throws\n    on violation; the `shell()` helper derives the inner rects so the invariant holds by\n    construction.\n  - Backgrounds render in two layers: a **fill** (solid / linear / radial gradient) then an\n    optional **shape** overlay from a generator registry. Each shape lays out in strip-space\n    so it flows across slides; a seeded `mulberry32` PRNG keeps a strip reproducible.\n  - Masking uses offscreen canvases with `destination-in` / `destination-out` (rather\n    than `ctx.clip()`) to get antialiased edges.\n- **`src/App.tsx`** — editor state, history, hydration, persistence coordination, and web\n  export paths. **`src/Sidebar.tsx`** and **`src/MobileLayout.tsx`** provide the desktop\n  and mobile control surfaces; **`src/components.tsx`** contains shared controls and the\n  canvas preview.\n- **`src/storage/`** — IndexedDB document and content-addressed asset storage, including\n  migration from the original localStorage format and cleanup of unreferenced binaries.\n- **`src/Welcome.tsx`** and **`src/GuidePage.tsx`** — the public landing page and\n  prerendered guide/comparison routes. **`src/ai.ts`** is the browser client for the\n  optional AI style and translation endpoints.\n- **`server/mcp/`** — the Node MCP server and native-canvas adapter. The publishable npm\n  package metadata lives in **`packages/truepane-mcp/`**.\n- **`supabase/functions/generator-bg-prompt/`** — the Edge Function that turns a prompt\n  into validated, clamped style params (raw model output never reaches the renderer).\n- **`supabase/functions/generator-translate/`** — the optional title/subhead translation\n  endpoint.\n\n## Running locally\n\n```sh\nnpm install\nnpm run dev        # http://localhost:5173\nnpm run build      # tsc -b && vite build → dist/\nnpm run preview    # serve the production build\nnpm run typecheck\nnpm test           # vitest (pure-logic suite)\n```\n\n## Configuration\n\nThe AI prompt feature is optional. Without it, the app is fully functional and the\nAI controls are hidden. To enable them, copy `.env.example` to `.env` and opt in:\n\n```\nVITE_ENABLE_AI=true\nVITE_PUBLIC_SITE_URL=https://truepane.dev\nVITE_BG_PROMPT_URL=https://YOUR-PROJECT.supabase.co/functions/v1/generator-bg-prompt\nVITE_SUPABASE_ANON_KEY=your-anon-key\nVITE_TRANSLATE_URL=https://YOUR-PROJECT.supabase.co/functions/v1/generator-translate\n```\n\n`VITE_ENABLE_AI` gates both helpers. Configure either endpoint independently; an\nunconfigured helper stays hidden. The Edge Functions read `GROQ_API_KEY` from Supabase\nsecrets. The background helper also accepts an optional `BG_PROMPT_MODEL` (default\n`llama-3.3-70b-versatile`). Deploy them with:\n\n```sh\nsupabase functions deploy generator-bg-prompt --project-ref YOUR-REF --no-verify-jwt\nsupabase functions deploy generator-translate --project-ref YOUR-REF --no-verify-jwt\n```\n\nServer-side rate limiting is currently best-effort (in-memory, per isolate). Add a\ndurable limiter before a high-traffic public launch.\n\n### Temporary beta gate\n\nA soft, client-side password gate can be enabled during private beta by setting\n`VITE_GATE_PASSWORD_HASH` to the SHA-256 of your password (unset = no gate):\n\n```sh\nprintf '%s' 'your-password' | shasum -a 256   # put the hash in .env\n```\n\nIt's a deterrent, not real security (it's a static client app) — meant to be removed\nafter beta.\n\n## Use with AI agents (MCP)\n\nTruepane ships a local [MCP](https://modelcontextprotocol.io) server, so an AI agent\n(Claude Code, Codex, …) can take simulator screenshots and turn them into store-ready\nslides without a human driving the browser UI. It renders with a native canvas\n(`@napi-rs/canvas`) — screenshots are read from local paths and PNGs are written to\nlocal paths; nothing is uploaded anywhere, and no configuration or env vars are\nneeded: the agent is the LLM, so styling and translation are its own judgment calls\n(the web app's AI helpers are not involved).\n\nIt's a standard stdio MCP server published to npm as\n[`truepane-mcp`](https://www.npmjs.com/package/truepane-mcp), so any MCP-capable\nclient can launch it with `npx -y truepane-mcp` — no checkout needed. Setup for\nthe common ones:\n\n**Claude Code**\n\n```sh\nclaude mcp add --scope user truepane -- npx -y truepane-mcp\n```\n\n**Codex CLI**\n\n```sh\ncodex mcp add truepane -- npx -y truepane-mcp\n```\n\nOr configure Codex manually in `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.truepane]\ncommand = \"npx\"\nargs = [\"-y\", \"truepane-mcp\"]\n```\n\n**Cursor, Windsurf, Claude Desktop, and other JSON-config clients** — add to the\nclient's `mcpServers` block (e.g. `.cursor/mcp.json`,\n`claude_desktop_config.json`):\n\n```json\n{\n  \"mcpServers\": {\n    \"truepane\": { \"command\": \"npx\", \"args\": [\"-y\", \"truepane-mcp\"] }\n  }\n}\n```\n\nRestart the client, then confirm that the Truepane tools are available.\n\nThe server lives in [`packages/truepane-mcp`](packages/truepane-mcp). To run it\nfrom a repo checkout instead (for development), point the client's command at\n`npx tsx server/mcp/index.ts`, or `npm run mcp:build` and run\n`node packages/truepane-mcp/dist/index.js`.\n\n### Workflow the tools expect\n\n1. `list_options` — start here to discover the full workflow, platforms (with exact\n   store pixel sizes), output surfaces, fonts, fills, shapes, and composition presets.\n2. `create_project` — slide titles/subheads + absolute screenshot file paths. Pass\n   `targets` to start a multi-platform project.\n3. `set_style` — colors, background, typography (font, `titleScale`/`subtitleScale`,\n   and `titleWeight`/`subtitleWeight` from 100–900), chosen with the agent's own\n   design judgment (`suggest_palette_from_screenshot` extracts an accent + background\n   tint from a screenshot with pure local math if a starting point helps). Its\n   `composition` patch controls normalized text/device position, size, alignment,\n   and flat rotation. Use `slide_index` for a slide-specific composition.\n4. `set_screenshots` — attach each capture with its `target` and optional `language`.\n   A missing target stays visibly empty; Truepane never stretches a capture from a\n   different platform into it.\n   For a prepared directory, `import_screenshots` returns a dry-run mapping by default;\n   repeat with `apply: true, dry_run: false` to apply only non-conflicting files.\n5. `render` — writes full-resolution PNGs (e.g. iPhone 6.9″ = 1320×2868) into an\n   output dir you pass, and returns a small inline preview to inspect. Adjust and\n   re-render until it looks right. Pass `target: \"all\"` for one folder per target.\n   `render` summarizes advisory preflight warnings; call `validate_project` for the\n   complete ordered target/locale/slide matrix.\n6. `set_translations` — the agent translates the slide texts itself and stores the\n   results per language; then `render` with `language: \"all\"` writes per-language\n   subfolders (`source/`, `es/`, …), matching the web app's all-languages ZIP. A\n   locale can also carry its **own screenshots** (for apps whose UI is itself\n   localized): pass `screenshot_path` per slide here, or `set_screenshots` with a\n   `language`; a locale without its own screenshot reuses the base one. Each\n   language can render in its **own font** too (`font` here, or `set_style` with a\n   `language`) — e.g. San Francisco for the base and `Noto Sans Arabic` for `ar` —\n   since the server has no per-glyph fallback for scripts a font doesn't cover.\n7. `export_project` / `load_project` — round-trip v2 project JSON with the web\n   app's Import/Export Project, so a human can fine-tune the agent's work (or vice\n   versa).\n\nUse `span_device_across_slides` to place one device across an adjacent slide pair.\nThe two clipped halves keep their screenshot, position, scale, and rotation linked,\nincluding after project export/import and later screenshot or copy updates.\n\n`export_brand_kit` and `apply_brand_kit` move the current visual defaults between\nprojects without carrying project content. Applying preserves per-slide overrides\nunless `clear_slide_overrides: true` is explicitly supplied.\n\nUse `set_output` to persist a native, `play-feature`, or `custom` output on an MCP\nproject. `render` also accepts temporary `output_id`, `output_width`,\n`output_height`, and `output_frame` overrides.\n\nUse `compare_release`, `set_release_baseline`, and `render changed_only:true` for\nrelease updates through MCP. Any future pixel-affecting renderer change must bump\n`RENDERER_SCHEMA_VERSION` in `src/core/release.ts`, intentionally marking every\nasset changed.\n\nGoogle Fonts are fetched on demand and cached in `~/.cache/truepane/fonts` (Inter is\nbundled, so offline rendering works out of the box). The `-apple-system` font renders\nas real San Francisco on macOS — from your own installed system font, which is never\nbundled or redistributed (Apple's font is proprietary) — and falls back to Inter on\nLinux/CI. Because SF is a variable font, its full weight range resolves (including\nHeavy/Black via `titleWeight`/`subtitleWeight`), not just Regular/Bold. A curated set\nof Google fonts — **Inter, Manrope, and Fraunces** — is loaded as a single-file\nvariable font too, so their full weight range resolves as well; every other Google\nfont uses its static faces (typically up to 700). Want another font's full range?\nOpen an issue or PR adding it to `VARIABLE_FONT_URLS` in `server/mcp/fonts.ts`. For\nnon-Latin target languages,\npick a font that covers the script (Inter covers Cyrillic/Greek; `Noto Sans JP/KR/SC`\nfor CJK; `Noto Sans Arabic` for Arabic, which is shaped and laid out right-to-left\nautomatically) — unlike browsers, server-side rendering has no per-glyph system-font\nfallback, so glyphs a font lacks come out as boxes.\n\n## Deployment\n\nThe site builds to `dist/` and can be hosted as a static SPA anywhere. This repository is\nconfigured for Cloudflare Workers static assets through `wrangler.jsonc`; deploy it with:\n\n```sh\nnpm run deploy\n```\n\nSet `VITE_PUBLIC_SITE_URL` at build time for canonical URLs, `robots.txt`, the sitemap,\nand prerendered guide metadata. Set the optional AI variables above only when those\nhelpers should be exposed.\n\n## License\n\n[AGPL-3.0](LICENSE). Because this is a client-side app, the source is distributed to\nevery browser — so forks, **including publicly hosted ones**, must make their source\navailable under the same license.\n\nThe Inter font files bundled with `truepane-mcp` are distributed under the SIL Open\nFont License 1.1; the copyright notice and license travel with them in\n`server/mcp/assets/fonts/LICENSE-Inter.txt` and in the published npm package.\n\n## Support\n\nIf this is useful to you, sponsorship is welcome — see [`.github/FUNDING.yml`](.github/FUNDING.yml).\n",
  "bytes": 18190,
  "sha": "bccbd93516708ce0061c5cc629a3f59b2b7afa478b57f435ab45fa7d4e9df2cc",
  "repo_slug": "antonkarliner/truepane",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_antonkarliner_truepane_587709bf/readme"
}