Saltar al contenido principal
H

figcraft

harryheyu/figcraft

Scientific figures as code: agents create a versioned figure IR, patch it incrementally (one node at a time — untouched objects never move), and export publication-ready, true-vector SVG / PDF / PPTX. Seven papercanvas_* tools (create, get, patch, layout, lint, render, inspect) over a deterministic engine with FC001-FC008 quality lint and CJK-aware font resolution. Verified on DSH 0.1.5-rc.3 through 0.2.2-alpha.2. By the author of dsh-sessionflow. Requires the PaperCanvas engine installed separately: Python 3.11+ and the papercanvas CLI (not yet on PyPI — install it from this repository, put it on PATH or set papercanvasBin); the plugin is a thin adapter and ships no engine.

Instalar

dsh plugin --profile web add github:harryheyu/figcraft

README

PaperCanvas

Agent-native scientific figure engine. Create, edit, and export publication-ready SVG, PDF & PPTX with MCP, CLI, and structured patches — the figure stays editable vector, never a bitmap.

CI Python License Code style: ruff

A versioned Figure IR (JSON) is the single source of truth. Coding agents modify it through semantic patches — change one node without re-drawing the figure — and deterministic renderers produce true-vector output for papers, theses, and slides.

What it looks like

Real rendered output from the bundled examples (committed SVGs, shown inline):

Linear ML pipeline: Input Video to Video Encoder to Prediction Video unlearning framework with Forget and Retain heads Multi-branch architecture with a Dual Branch group

ExampleFigureSources
basic_pipelineLinear ML pipelineSVG · PDF · PPTX
video_unlearningVideo unlearning frameworkSVG · PDF · PPTX
multi_branchMulti-branch architecture with grouped headsSVG · PDF · PPTX

Why PaperCanvas

  • Agent-native: seven MCP tools plus a CLI on one shared service layer — agents patch figures incrementally; untouched objects never move.
  • Figure IR as the single source of truth: SVG/PDF/PPTX are build products; nothing is hand-edited, everything is reproducible.
  • Transactional semantic patches: nine operations applied all-or-nothing with revision control — a failed patch leaves the file byte-identical.
  • Truly vector, truly editable: SVG <text>, PDF with embedded TTF subsets, PPTX with native shapes/connectors — no rasterization anywhere.
  • Deterministic: same IR + same fonts ⇒ byte-identical output; safe to re-export and diff.
  • Quality lint built in: FC001–FC008 (overflow, overlap, arrows through nodes, font substitution, …) with machine-readable JSON output.

60-second quickstart

pip install papercanvas        # PyPI publication pending final approval
# for now, from a clone:
uv sync

papercanvas init figures.json --id demo

# 2. describe the figure as a patch (save as ops.json)
cat > ops.json <<'JSON'
{
  "operations": [
    {"op": "add_node", "node": {"id": "input",  "type": "rounded_rect", "label": "Input Video"}},
    {"op": "add_node", "node": {"id": "encoder", "type": "rounded_rect", "label": "Video Encoder"}},
    {"op": "add_node", "node": {"id": "output",  "type": "rounded_rect", "label": "Prediction"}},
    {"op": "add_edge", "edge": {"id": "e1", "source": "input",  "target": "encoder"}},
    {"op": "add_edge", "edge": {"id": "e2", "source": "encoder", "target": "output"}}
  ]
}
JSON

papercanvas patch figures.json --ops ops.json
papercanvas layout figures.json --preset layered
papercanvas lint figures.json
papercanvas render figures.json --format all
# -> figures.svg figures.pdf figures.pptx

# 3. revise in place — rename ONE node and re-render, no rebuild:
echo '{"operations": [{"op": "update_label", "target": "output", "label": "Final Prediction"}]}' > round2.json
papercanvas patch figures.json --ops round2.json
papercanvas render figures.json --format all

Windows PowerShell: replace the cat > ops.json <<'JSON' ... JSON block with @' ... '@ | Set-Content ops.json (same JSON), and the last patch with echo '...' | ... via a file — process substitution <(...) is bash-only.

Try it without writing anything:

uv run papercanvas build examples/basic_pipeline.json --format all
uv run papercanvas lint examples/basic_pipeline.json

CLI

papercanvas init PATH --id FIGURE_ID [--width] [--height] [--margin]
papercanvas validate PATH
papercanvas patch PATH --ops ops.json        # {"operations": [...], "expected_revision": N}
papercanvas layout PATH --preset linear-h|linear-v|layered
papercanvas lint PATH [--min-font-size PT] [--format text|json]
papercanvas inspect PATH [OBJECT_ID] [--format text|json]
papercanvas render PATH [--format svg|pdf|pptx|all] [--out FILE]
papercanvas build PATH [--preset ...] [--format ...]   # layout + render in one command

Every mutating command is transactional: validation errors leave the file byte-identical; successes bump revision by one.

MCP server (for coding agents)

papercanvas-mcp --root path/to/project

Seven tools: papercanvas_create, papercanvas_get, papercanvas_patch, papercanvas_layout, papercanvas_render, papercanvas_lint, papercanvas_inspect. All paths must stay inside --root; failures return structured {"ok": false, "error": ...} so agents can self-correct. See docs/mcp.md.

Codex / Claude Code / OpenCode

PaperCanvas speaks standard MCP stdio, so any MCP client works. Point the client at papercanvas-mcp with the project directory as --root:

  • Claude Code: claude mcp add papercanvas -- papercanvas-mcp --root . (or add the JSON below to .mcp.json in the project)
  • Codex CLI: add to ~/.codex/config.toml: [mcp_servers.papercanvas] with command = "papercanvas-mcp", args = ["--root", "."]
  • OpenCode: add to opencode.json: "mcp": {"papercanvas": {"type": "local", "command": ["papercanvas-mcp", "--root", "."]}}

Generic stdio JSON (most clients accept this shape):

{
  "mcpServers": {
    "papercanvas": {
      "command": "papercanvas-mcp",
      "args": ["--root", "."]
    }
  }
}

The Agent Skill at skills/papercanvas/SKILL.md (bundled inside the wheel at papercanvas/skills/SKILL.md) teaches agents the full workflow. Verification scope: all tool behavior is covered by real-protocol tests (tests/test_mcp_e2e.py, tests/test_mcp_stdio.py); the third-party client UIs themselves were not exercised in this project's environment.

Install as a DSH plugin

PaperCanvas is also a DeepSeek Harness plugin — the seven tools surface directly inside DSH sessions:

dsh plugin --profile <profile> add github:HarryHeYu/figcraft

The plugin is a thin adapter over the papercanvas CLI (which must be installed — from this repository until the PyPI publication). Verified channels, same discipline as dsh-sessionflow:

DSHinstall resolvesDSH loads ittool call answered
0.1.5-rc.3✅✅✅
0.2.0-rc.2 (next)✅✅✅
0.2.1-rc.5 (next)✅✅✅
0.2.2-alpha.2 (alpha)✅✅✅

Cold-start CI: cold-start-figcraft — 5 matrix rows (install path × DSH channel), each proving install → bundle → prepare → profile layer → a real bridge call into the engine.

The Figure IR in one minute

Copy-paste this into quickstart.json — it validates and renders as-is:

{
  "schema_version": "0.1",
  "id": "method_pipeline",
  "revision": 1,
  "canvas": {"width": 100, "height": 40, "unit": "mm", "margin": 4},
  "theme": {"font_family": "Arial", "font_size_pt": 8, "palette": {}},
  "nodes": [
    {
      "id": "input",
      "type": "rounded_rect",
      "label": "Input Video",
      "x": 8, "y": 13, "w": 34, "h": 14,
      "style": {"fill": "#E8F0FE", "stroke": "#4275C4"}
    },
    {
      "id": "encoder",
      "type": "rounded_rect",
      "label": "Video Encoder",
      "x": 58, "y": 13, "w": 34, "h": 14,
      "style": {"fill": "#E5F4EC", "stroke": "#418968"}
    }
  ],
  "edges": [
    {
      "id": "e1",
      "source": "input",
      "target": "encoder",
      "source_port": "right",
      "target_port": "left",
      "style": {"stroke": "#444444", "arrow": "end"}
    }
  ],
  "groups": []
}
uv run papercanvas validate quickstart.json   # -> valid
uv run papercanvas render quickstart.json     # -> quickstart.svg

Capabilities & limitations

Figure IR v0.1 supports boxes (rect / rounded_rect), port-annotated edges with orthogonal routing, groups, palette-driven styling, auto-sizing, layered/linear layouts, and FC001–FC008 lint. CJK labels are measured and rendered with a system CJK font when one exists (FC007 reports the substitution). The full IR reference is in docs/figure-ir.md.

Naming (final decision)

SurfaceName
GitHub repositoryHarryHeYu/figcraft ("FigCraft" is the historical codename)
PyPI packagepip install papercanvas (verified available)
CLIpapercanvas
MCP server & tool prefixpapercanvas-mcp, papercanvas_*
Python importpapercanvas

Documentation

Contributing

Issues and PRs welcome — see CONTRIBUTING.md. Security reports: SECURITY.md.

Roadmap

  • v0.2: obstacle-aware routing everywhere, broader CJK font fallback, two-column paper presets, visual regression baselines
  • later: additional figure types, web canvas exploration

License

MIT

Plugins relacionados