Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
c1767bb
fix(plugin): finish dynamic-page migration
Jun 23, 2026
53f2d89
fix(server): per-tool request timeout so large exports don't false-ti…
Jun 23, 2026
1714a96
perf(unity): drop per-sprite SaveAssets in ProceduralSprites
Jun 23, 2026
ad66fb8
fix(unity): make styled List/Table rows selectable
Jun 23, 2026
a2a55cb
ci: gate manifest version + Manifest field parity; scaffold Unity job
Jun 23, 2026
350b8ab
docs: correct stale class, tool, field, and dependency references
Jun 23, 2026
9427bdc
ci(release): use Node 22 and run contract + typecheck/tests before pa…
Jun 23, 2026
0053b43
fix(plugin): prevent wrong-sprite dedup collisions + drop dangling ch…
Jun 23, 2026
1e7f595
fix(unity): preserve modal listeners + stage live-import before swap
Jun 23, 2026
32820cb
fix(unity): unique canonical prefab paths + per-ref radio groups
Jun 23, 2026
631aa38
fix(server): survive post-bind server errors + close path-guard TOCTOU
Jun 23, 2026
4820d10
fix(unity): guard prefab-instantiate NRE, accessor recursion, frame-g…
Jun 23, 2026
83ee543
fix(server): bounded wait for plugin reconnect after leader takeover
Jun 23, 2026
dc9288d
fix(unity): guard FrameManager against OnHide re-entrant navigation
Jun 23, 2026
dfe9afa
fix(unity): wire each nav link's button exactly once
Jun 23, 2026
167b25f
fix(unity): codegen file lifecycle — orphan sweep, AssetDatabase dele…
Jun 23, 2026
311f862
fix(plugin): guard empty font list, gate create-* during export, repo…
Jun 23, 2026
7d13d32
fix(plugin): honest blocked-port message, socket reconnect race, drop…
Jun 23, 2026
fc3dce8
fix(plugin): strip baked effects on PNG nodes; disambiguate variant a…
Jun 23, 2026
c3bd805
fix(unity): dispose WindowStyles textures + confirm-before-replace zi…
Jun 23, 2026
367bc31
perf(unity): drop redundant material upload in materialForRendering
Jun 23, 2026
85dd2b6
chore(unity): menu to clean orphaned generated sprites
Jun 23, 2026
ff98f05
docs(contract): document legacy ButtonShape shadow/shadows fields
Jun 23, 2026
0356d8f
fix(plugin): low-severity exporter + vector fixes
Jun 23, 2026
a0f14f8
fix(plugin): low-severity core + ui fixes
Jun 23, 2026
d78b69d
fix(server): low-severity hardening
Jun 23, 2026
5388f29
fix(unity): low-severity codegen fixes
Jun 23, 2026
39b3e7e
fix(unity): low-severity editor/import fixes
Jun 23, 2026
8c6b8f9
fix(unity): low-severity hierarchy-builder fixes
Jun 23, 2026
7cc58bf
fix(unity): low-severity runtime fixes
Jun 23, 2026
88a2385
ci/chore: npm cache, UPM tarball, CanonicalKind gate, contract comments
Jun 23, 2026
5a6ee02
chore(plugin): address nit findings
Jun 23, 2026
e33b049
chore(server): unambiguous duplicate-folder suffix in export_project_…
Jun 23, 2026
24bc777
chore(unity): address nit findings
Jun 23, 2026
6d37ae9
fix(unity): hoist live-import index declaration out of the try (CS0103)
Jun 24, 2026
1809c90
feat(unity): persistent overlay frames + dropdown/radio fixes
Jun 24, 2026
9c45259
feat(unity): row/column accessors for List & Table
Jun 24, 2026
d653239
release: List/Table row & column accessors (v1.0.58)
Jun 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 39 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,18 @@ jobs:
# files directly via `node --test test/*.test.ts` (native TypeScript
# type stripping landed in Node 22.6).
node-version: 22
# Cache the npm download cache, keyed off both lockfiles, so the
# per-package `npm ci` steps below restore from cache instead of
# re-downloading every dependency on each run.
cache: 'npm'
cache-dependency-path: |
plugin/package-lock.json
server/package-lock.json

- name: Contract — canonical schema in lockstep
- name: Contract — schema + manifest version + field parity in lockstep
# Guards three plugin<->importer contract points: the canonical-schema
# generation, the manifest wire-format version (importer hard-fails on an
# unsupported one), and best-effort top-level Manifest field-name parity.
run: node scripts/check-canonical-schema.mjs

- name: Plugin — typecheck + build
Expand Down Expand Up @@ -46,3 +56,31 @@ jobs:
# a real compile job would also require a UNITY_LICENSE secret that is
# unavailable on fork PRs. Adding it now would either hard-fail or block the
# pipeline, so it is deferred. See the CI concern noted in the rollout.
#
# Future path forward (DISABLED — do NOT enable without the prerequisites below):
# spin up a throwaway Unity project that references this package via the local
# manifest, then run game-ci/unity-test-runner to compile + run EditMode tests.
# Prerequisites before un-commenting:
# 1. A UNITY_LICENSE (+ UNITY_EMAIL / UNITY_PASSWORD) repo secret. These are
# NOT available to fork PRs, so the job must be guarded so fork PRs skip it
# cleanly rather than fail.
# 2. A minimal Unity project under e.g. ci/unity-project/ whose Packages/
# manifest.json points "com.figforge.importer" at "file:../../unity".
#
# unity-compile:
# runs-on: ubuntu-latest
# # Skip on fork PRs: secrets (UNITY_LICENSE) are not exposed there, so the job
# # cannot authenticate and would otherwise hard-fail. Same-repo pushes/PRs run it.
# if: github.event.pull_request.head.repo.full_name == github.repository
# steps:
# - uses: actions/checkout@v4
# with:
# lfs: true
# - uses: game-ci/unity-test-runner@v4
# env:
# UNITY_LICENSE: ${{ secrets.UNITY_LICENSE }}
# UNITY_EMAIL: ${{ secrets.UNITY_EMAIL }}
# UNITY_PASSWORD: ${{ secrets.UNITY_PASSWORD }}
# with:
# projectPath: ci/unity-project # throwaway project referencing unity/ via file: dependency
# testMode: EditMode
12 changes: 10 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,31 @@ jobs:

- uses: actions/setup-node@v4
with:
node-version: 20
# Match CI and package.json engines (>=22.6) — the server tests run .ts
# files directly via `node --test`, which needs Node 22.6+.
node-version: 22

- name: Resolve version
id: ver
run: echo "version=${GITHUB_REF_NAME:-${{ github.event.inputs.tag }}}" >> "$GITHUB_OUTPUT"

- name: Contract — schema + manifest version + field parity in lockstep
run: node scripts/check-canonical-schema.mjs

- name: Build plugin
run: |
cd plugin
npm ci
npm run typecheck
npm run build

- name: Build server
- name: Build + test server
run: |
cd server
npm ci
npm run typecheck
npm run build
npm test

- name: Package artifacts
env:
Expand Down
20 changes: 12 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ importer reads. Change one side, change the other. Everything else is detail.
| ✎ Vector / icon | rasterized PNG, hash-deduped |
| 🅣 Text | `TextMeshProUGUI` + per-family/style font mapping |
| 🔘 `Btn_<name>_<Ref>` / `Inp_<name>_<Ref>` layer | a real **canonical prefab** instance |
| 🗂 Several frames | one navigable scene — `BaseScreen` pages under a `ScreenManager` |
| 🗂 Several frames | one navigable scene — `FigForgeScreen` frames under a `FrameManager` |
| 👻 Empty/placeholder paint, failed export | falls back to the fill colour — **no junk PNG, no white box** |

Plus, in the plugin itself: exclude layers, merge a container to one PNG,
Expand Down Expand Up @@ -128,7 +128,7 @@ Follow a single frame through the machine; every capability shows up along the w
>
> **5 · Unity rebuilds it.** Anchored hierarchy under a `Canvas`, fonts mapped to
> `TMP_FontAsset`s, canonical layers swapped for prefab instances, and each frame
> parented under a `ScreenManager` as one `BaseScreen` — many frames → one
> shown by a `FrameManager` as one `FigForgeScreen` — many frames → one
> navigable, multi-page scene.

---
Expand Down Expand Up @@ -181,15 +181,15 @@ From a release instead: unzip `figforge-bridge-<ver>.zip`, then `npm install --o

```text
Package Manager ▸ Add package from git URL…
https://github.com/havokentity/FigForge.git?path=unity#v1.0.1
https://github.com/havokentity/FigForge.git?path=unity#v1.0.57
Package Manager ▸ Add package from tarball…
figforge-unity-importer-<ver>.tgz (from a release)
Package Manager ▸ Add package from disk…
unity/package.json
```

Pin the git URL to a tag (`#v1.0.1`) so upgrades stay deliberate. Deps — uGUI,
TextMeshPro, Newtonsoft JSON, 2D Sprite — resolve automatically.
Pin the git URL to a tag (`#v1.0.57`) so upgrades stay deliberate. Deps — uGUI,
TextMeshPro, Newtonsoft JSON, 2D Sprite, Input System — resolve automatically.
</details>

> [!IMPORTANT]
Expand Down Expand Up @@ -229,10 +229,14 @@ With the bridge running, an MCP client can drive the whole thing:
| Tool | Does |
|:--|:--|
| `get_metadata` | file name, pages, current page |
| `get_document` / `get_selection` / `get_node` | read the tree, the selection, or one node |
| `get_design_context` | a summarized design tree |
| `get_screenshot` / `save_screenshots` | render node(s) to PNG (returned, or written to disk) |
| `get_document` / `get_selection` / `get_node` / `get_node_details` | read the tree, the selection, or one node (deep) |
| `get_design_context` | a layout-aware, summarized design tree |
| `list_frames` / `list_screens` | enumerate top-level frames / export-eligible screens |
| `get_screenshot` / `save_screenshots` | render node(s) to PNG (returned base64, or written to disk) |
| `create_canonical` / `create_shell` | scaffold a canonical control / app-shell frame in the document |
| **`export_unity`** | run the real exporter and write `manifest.json` + PNGs to a folder |
| **`export_project_unity`** | export the page as a connected multi-page project bundle |
| `validate_manifest_contract` | check a manifest/project JSON against the importer contract |

`export_unity` is sandboxed to the workspace root. The plugin's header **MCP
toggle** dials out to `ws://127.0.0.1:1994/ws` (and auto-reconnects while on);
Expand Down
7 changes: 4 additions & 3 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,16 +44,17 @@ WebSocket to the single Figma plugin can exist — so processes elect a **leader
rest are **followers** proxying over `/rpc`. If the leader dies, a follower's next
call triggers a takeover.

- `bridge.ts` — request/response correlation over the plugin socket (30s timeout).
- `bridge.ts` — request/response correlation over the plugin socket (per-tool timeout: 30s for queries, minutes for exports).
- `leader.ts` / `follower.ts` / `election.ts` — the role machinery.
- `tools.ts` / `schema.ts` — MCP tool definitions (Zod-validated). `export_unity`
and `save_screenshots` validate that output paths stay within the server cwd.
and `save_screenshots` validate that output paths stay within the workspace root
(`FIGFORGE_WORKSPACE`, defaulting to the launch cwd).

## Importer (`unity/`)

- **Editor** — `FigForgeImporterWindow` (UI), `ManifestParser`, `TextureImportHelper`,
`SpriteAtlasHelper`, `HierarchyBuilder`, procedural sprite caches.
- **Runtime** — `ScreenManager`, `BaseScreen`, `CanonicalLibrary` (so built scenes
- **Runtime** — `FrameManager`, `FigForgeScreen`, `CanonicalLibrary` (so built scenes
and prefabs work at runtime without the editor assembly).

## The contract
Expand Down
5 changes: 3 additions & 2 deletions docs/plugin-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,9 @@ deduplicated by content hash.

## Manifest field reference

Root: `schema`, `version`, `generator`, `exportedAt`, `screen`, `elements[]`,
`assets[]`, `fonts[]`, `diagnostics`, `canonicalRefs[]`.
Root: `schema`, `version`, `canonicalSchema`, `generator`, `exportedAt`, `vanilla`,
`screen`, `elements[]`, `assets[]`, `fonts[]`, `diagnostics`, `settings`,
`canonicalRefs[]`.

`screen`: `{ id, name, figmaSize{w,h}, referenceResolution{w,h}, exportScale }`.

Expand Down
4 changes: 2 additions & 2 deletions plugin/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion plugin/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "figforge",
"version": "1.0.57",
"version": "1.0.58",
"description": "FigForge — export Figma frames as Unity uGUI-ready manifests + PNG assets",
"license": "MIT",
"private": true,
Expand Down
Loading
Loading