Metadata-Version: 2.5
Name: pac-vector
Version: 0.2.0
Summary: Local stdio MCP server for SVG editing via in-memory virtual documents, with a live browser preview
Requires-Python: >=3.11
Requires-Dist: fonttools>=4.40
Requires-Dist: lxml>=5.0
Requires-Dist: mcp[cli]<2.0.0,>=1.10.0
Requires-Dist: pillow>=10.0
Requires-Dist: resvg-py>=0.5.0
Requires-Dist: skia-pathops>=0.8
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == 'dev'
Description-Content-Type: text/markdown

# pac-vector

Local stdio MCP server: an SVG editor over in-memory **virtual documents** (one per
"window", like Inkscape), with a **live browser preview** of every edit.

- Content goes in as SVG markup; structure (layers, groups, masks, z-order) has typed tools.
- Layers are real Inkscape layers, so exported files open in Inkscape with layers intact.
- Path booleans (union, difference, intersection, exclusion, division) via Skia PathOps.
- Every edit is atomic (a failed call changes nothing), one undo/redo step, and autosaved:
  windows of a crashed session reopen in the next one.
- `render` returns a PNG the agent can look at; rendering is resvg (no browser, no Cairo).

## Install

Same shape as pac-pincers: a pip package, run as a module by whatever Python it
was installed into.

```powershell
python -m pip install --upgrade pac-vector
claude mcp add pac-vector --scope user -- python -m pac_vector
```

As a plugin `.mcp.json` entry (mirrors the datatopia plugin's pac-pincers entry):

```json
"pac-vector": {
  "type": "stdio",
  "command": "${PAC_VECTOR_PYTHON:-python}",
  "args": ["-m", "pac_vector"],
  "env": {}
}
```

Development:

```powershell
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[dev]"
.\.venv\Scripts\python.exe run_tests.py -q
.\.venv\Scripts\python.exe -m build          # dist/pac_vector-<ver>.tar.gz + .whl
```

## Tools (21)

| Group | Tools |
|---|---|
| Documents | `doc_create` (blank, file, markup, or duplicate an open window), `doc_list`, `doc_set` (size, units, viewBox, background, fit), `doc_export` (svg/png, page/drawing/ids, plain), `doc_close` |
| Reading | `inspect` (outline or xml), `render` (PNG, optional crop to ids + bbox) |
| Editing | `add` (markup → layer/group, defs auto-routed; SMIL animations into any element), `update` (attrs/style/text/transform/label/hidden/locked/text-on-path on many ids), `replace` (one element or the whole drawing), `remove`, `arrange` (z-order, move between layers, duplicate, offset), `import_svg` (file / other window / markup → editable group or embedded image, fitted into a box) |
| Structure | `group` (also new layers), `ungroup`, `mask` (mask or clip from id or markup), `unmask` |
| Geometry | `boolean` (union, difference, intersection, exclusion, division), `align` (align edges/centers, distribute evenly or by gap; relative to selection, page, first, last or an element) |
| History | `undo`, `redo` |

## Preview

`doc_create` / `doc_list` / `inspect` print `preview: http://127.0.0.1:<port>/#<doc>`.
The page has a tab per document, outlines the elements each edit touched, and
shows (and copies) the id of anything you click. The server lives inside the MCP
process: it starts on the first `doc_create` and dies with the session.

## Environment

| Variable | Default | Meaning |
|---|---|---|
| `PAC_VECTOR_PORT` | `0` (random) | Pin the preview port (falls back to random if busy) |
| `PAC_VECTOR_OPEN_BROWSER` | off | `1` opens the preview in the default browser on first start |
| `PAC_VECTOR_ROOT` | server cwd | Base directory for relative export/import paths |
| `PAC_VECTOR_LOG` | `WARNING` | Log level (stderr only) |
| `PAC_VECTOR_AUTOSAVE` | on | `0` disables the crash-recovery mirror |
| `PAC_VECTOR_AUTOSAVE_DIR` | `<tmp>/pac-vector-autosave` | Where the mirror lives (`<pid>/<doc>.svg` + `.json`) |

## Notes

- stdout is the JSON-RPC pipe: nothing may print to it. `tests/test_stdout_safety.py`
  scans the package for `print()` / `sys.stdout`, and `tests/test_stdio.py` runs a real session.
- Native modules are loaded before the stdio loop starts (`render.warm`): on Windows a
  first native load from inside the FastMCP loop can hang (see pac-pincers' `_excel.py`).
- Bounding boxes are measured from pixels (two-pass render), so they include strokes and
  filters and work for text and curves; content further than two page-widths off-canvas isn't found.
  `align` uses them, so it aligns what you see.
- Booleans use fill geometry only (strokes, text and images don't take part); the result
  is a `<path>` in the bottom operand's place, styled like it, keeping its id.
- Animations are SMIL: the browser preview plays them; `render` / PNG export show the
  static first frame.
- Version lives only in `pyproject.toml`; a release is a commit named after the version.
