Tavotto
AI drew your research figure. Tavotto makes it publication-ready: edit matplotlib figures on a canvas, lay out in mm, check journal rules. Free, open source.
翻譯準備中
個人 GitHub 帳號下公開儲存庫的擁有者可以登入並驗證所有權,自行管理介紹,無需管理員核准。
登入並產生驗證檔案,在 30 分鐘內將其提交至此儲存庫的預設分支。其他分支或分叉儲存庫中的檔案無效。然後在下方檢查檔案,即可無需管理員核准直接管理專案。請保留檔案以維持管理權限。
如需登入,將前往登入頁面。登入後請回到此處,再次點擊所選的驗證按鈕。
在專案資料夾中執行 /nomorevibe verify,即可驗證所有權並自行管理介紹。
一分鐘完成驗證A drawboard for your agent. Draw via MCP or CLI, watch changes live, and edit together. Self-hosted and open source.
翻譯準備中
取自儲存庫 README
正在載入完整 README。
檢視完整 README ↗Install Node.js 20+, pnpm, and Git, then run:
git clone https://github.com/krittaprot/draft.git
cd draft
pnpm install --frozen-lockfile
pnpm build
node bin/draft.js serve --data "$HOME/.local/share/draft/data"
Keep that terminal running and open http://127.0.0.1:3290.
cat "$HOME/.local/share/draft/data/owner-token" and use it to sign in. Keep this key private.Stop the server with Ctrl+C. Restart it with the same command and data directory to reopen your boards.
New installations store data in ~/.local/share/draft/data. Set DRAFT_DATA or use --data DIR to choose another location. Back up the entire data directory while the server is stopped; it includes designs, revisions, assets and access settings. Keep data outside the source checkout.
Common options:
node bin/draft.js serve --port 3290 --data "$HOME/.local/share/draft/data"
node bin/draft.js --help
The server binds to 127.0.0.1 by default. Local setup needs no domain, Google account or tunnel.
With Xcode Command Line Tools installed, run from the project root:
./scripts/install.sh
This installs ~/Applications/Draft.app and the draft command in ~/.local/bin. Add that directory to your PATH if needed. Open Draft to use a running local server, or let the app start its bundled server while it is open. Choose Draft → Settings… to connect to another server.
The web app can also be installed through Safari's File → Add to Dock, or Share → Add to Home Screen on iPhone.
From the machine hosting Draft:
draft connect claude
draft connect codex
draft connect opencode
Without the Mac installer, substitute node bin/draft.js for draft. Each command creates a named agent key and registers the MCP connection. Restart the agent to load its new tools.
For remote MCP clients, use https://your-host/mcp. Clients with OAuth sign in through the browser and request your approval. For bearer-token clients, create a named key with draft token create my-agent and use it as an Authorization bearer token. A viewer key can inspect designs and take screenshots without editing them.
Use the same drawing tools from a shell or an agent with terminal access. In a second terminal, set a key for your own server:
export DRAFT_TOKEN="$(cat "$HOME/.local/share/draft/data/owner-token")"
# Discover tools and their JSON input schemas.
draft tools
# Create a board. Copy its returned document ID into the next command.
draft call create_document '{"name":"My first board"}'
draft call create_nodes '{"document":"d_REPLACE_ME","nodes":[{"type":"frame","name":"Hello","width":640,"height":400,"fill":"#0D1838","layout":"column","padding":48,"children":[{"type":"text","text":"Hello, Draft","fontSize":40,"color":"#ffffff"}]}]}'
# Capture the board; the result includes a PNG share link.
draft call screenshot '{"document":"d_REPLACE_ME"}'
Use --url https://your-host for a remote server. For larger inputs, use draft call create_nodes --file nodes.json, or pipe JSON into draft call create_nodes --file -. Output is JSON; a failed tool call exits nonzero. Use a named editor key instead of the owner key for ongoing agent work.
Both MCP and CLI expose the same tools:
People get an infinite canvas, layers panel, style inspector, comment pins and live change highlights in the browser or Mac app.
For remote access, use a domain you control and Cloudflare Tunnel. Install cloudflared, sign in with cloudflared tunnel login, then copy deploy/deploy.env.example to deploy/deploy.env and set your hostname, service label and tunnel name.
Run from the project root:
./scripts/deploy.sh
./scripts/install-service.sh
./scripts/setup-tunnel.sh
The scripts publish a release, install the server launch agent and configure the tunnel. The server stays on loopback; the tunnel provides public HTTPS. These launchd scripts are macOS-specific. On Linux, run the Node server under your own process manager and HTTPS reverse proxy, with a persistent data directory.
pnpm install --frozen-lockfile, then ./scripts/deploy.sh.https://your-host/healthz, sign in, and open an existing board.current symlink at its previous release, then restart <your-label>.server with launchctl.~/.local/share/draft; logs live under ~/Library/Logs/Draft.Set DRAFT_NEON_AUTH_URL to your Neon Auth endpoint and add the public URL as a trusted domain. Configure Google sign-in in Neon, then allow members:
draft member add [email protected] --role owner
Set DRAFT_ALLOWED_EMAILS for a fixed email allowlist. With Google enabled, browser token login defaults to local-only; DRAFT_TOKEN_LOGIN accepts on, local, or off. Without Neon, sign in with an access key; explicitly set DRAFT_TOKEN_LOGIN=on if you want remote token sign-in.
Treat owner keys as secrets. MCP and API calls require authentication; signed screenshot links are viewable by anyone who has the link until they expire. Back up your data before updates.
pnpm install --frozen-lockfile
pnpm typecheck
pnpm test
node bin/draft.js serve --port 3299 --data /tmp/draft-dev
./scripts/build-app.sh
Use a scratch data directory for testing. src/shared contains the model and renderer; src/server contains storage, HTTP, MCP and CLI; src/web contains the editor and homepage; mac contains the native wrapper.
See CONTRIBUTING.md, SECURITY.md, and AGENTS.md for contribution and development guidance.
Text measurement uses the real metrics of the face that will be rendered for the 39 bundled families (advance widths, ascender/descender), so the editor, the SVG and the server PNG wrap identically. Families outside the catalogue fall back to Helvetica metrics. Server rendering also uses installed system fonts and any fonts added to <data>/fonts.
src/shared/font-catalog.json lists the families and weights Draft ships (Inter, Geist, Roboto, Open Sans, Lato, Montserrat, Poppins, Work Sans, DM Sans, Manrope, Space Grotesk, Plus Jakarta Sans, Figtree, Public Sans, Outfit, Archivo, Barlow, Rubik, IBM Plex Sans/Mono, Source Sans 3, Source Serif 4, Noto Sans, Oswald, Bebas Neue, Anton, Playfair Display, Merriweather, Lora, Libre Baskerville, EB Garamond, DM Serif Display, Instrument Serif, Fraunces, JetBrains Mono, Fira Code, Roboto Mono, Space Mono, Geist Mono) — 248 faces including the italics Google actually ships.
pnpm fonts downloads the families and regenerates src/shared/font-data.ts plus src/shared/fonts.lock.json (a sha256 of every font file); it is the only thing that writes the metrics. A build verifies that every declared face is on disk and that no binary changed since the metrics were written, and fails otherwise — so a release can never ship a stylesheet, a metric table and a set of font files that disagree. DRAFT_FONTS_OFFLINE=1 downgrades that to a warning for machines without network, and pnpm fonts:check reports missing faces on its own.
build.mjs writes /fonts.css with one @font-face per available face, each URL carrying its content hash, so a stylesheet and the binaries it names are always the same pair (the stylesheet is versioned like every other asset).
Typography rules the editor and the renderer share: canvas text sets font-synthesis: none, so a weight or italic the family does not actually ship is not faked — it falls back to the nearest real face, exactly as resvg renders it. New text nodes default to Inter; existing documents keep the family they were saved with. Agents can call list_fonts, and create_nodes/update_nodes warn when they use an unknown family or an italic the family cannot draw.
The font files are large, so src/web/fonts/<slug>/ is git-ignored while the generated metrics are committed.
Documents carry three authoring features that keep a large design consistent:
manage_variables sets document tokens ({paper:"#faf6ef", space:8}); props reference them as "var:paper", including fill, stroke, text colour, shadow and outline colours, plus radius, gap, padding and strokeWidth. Changing a token re-renders every node that uses it, and export_tokens emits the set as CSS custom properties, DTCG-shaped JSON and a Tailwind @theme block.component:true is a definition. {type:"instance", component:"button"} places it; overrides maps component-local child ids to props ({"cta-label":{text:"Save"}}, "@root" for the frame itself). Instances resolve at render time, so editing the definition — or moving a token — updates every instance; there is no flattening to keep in sync.states:{"hover":{"@root":{fill:"#2a261f"}, "cta-label":{color:"var:paper"}}}: prop patches over the same tree, so a state never duplicates nodes. Instances pick one with state, and screenshot({node, state:"hover"}) previews a definition's state without saving.Supporting tools close the loop: find_nodes queries by property rather than id, update_nodes({where, props}) edits every match in one revision, dry_run validates a batch without writing, create_nodes/update_nodes return warnings for text and children that a clipped frame will cut off, get_node.computed reports alpha colours flattened against their backdrop, and audit_contrast checks text against the colour actually behind it — alpha layers and every gradient stop at its worst — suggests the nearest passing token, groups failures by page so an archive is not mistaken for the live design, and reports text inside a disabled: true control as exempt under WCAG 1.4.3 rather than as a defect.
Painting has outline:{width, offset, color} (a ring outside the box that does not affect layout) and strokeAlign: inner|center|outer (an inner border that does not change a component's width). Text has wrap: auto|nowrap|truncate and maxLines. Measurement walks a per-glyph fallback chain, so symbols like ⌘ are measured with the face that draws them instead of being clipped.
MIT.