Skip to content

Releases: runkids/figlens

v0.12.1

Choose a tag to compare

@github-actions github-actions released this 04 Nov 02:13
abee62e

figlens 0.12.1 reads a Figma copy straight from the clipboard, so turning a copied screen into a .fig is one command, and <command> --help now shows only that command.

Read the clipboard directly (#220)

In 0.12.0, converting a copied selection meant saving the clipboard's HTML first with a long osascript pipeline. Pasting the copy into a text field gives only the layers' text, so it was hard to tell what to save. Now clipboard reads the system clipboard itself:

# In Figma: select the frame or layers, then right-click > Copy (or Cmd/Ctrl+C)
figlens clipboard screen.fig
figlens bundle screen.fig <node> frame.zip

You can also ask your agent to build "the screen I just copied"; it runs the same command. A saved HTML file still works as before: figlens clipboard copied.html screen.fig.

If the clipboard holds no Figma layers (for example, text copied from a web page), the error says to copy layers in Figma. If a clipboard reader is missing, the error names it.

When you have a saved .fig, a Figma link is still the better route: the agent guidance now looks for the project's .fig first and confirms the link's node in it, because a saved file keeps the image fills and vector outlines that a copy lacks. The user guide has a new Copy a selection section comparing the two.

Limitations:

  • Reading the clipboard is a CLI command. MCP, Pi, OpenCode and OMP read the converted .fig; an agent with no shell asks you to run the command.
  • macOS is checked against real copies. Windows (PowerShell 5.1) and Linux (wl-paste or xclip) are covered by tests with stand-in programs only.

Help for one command (#221)

figlens --help is 373 lines. figlens <command> --help (or -h) now prints only that command's entries, then a pointer to figlens --help. A usage error, such as figlens diff with no files, prints the same short section instead of the whole text. figlens --help itself is unchanged.

Update

figlens update            # or: brew upgrade figlens
npx @runkids/figlens@0.12.1 --version

v0.12.0

Choose a tag to compare

@github-actions github-actions released this 03 Nov 15:21
84143c5

figlens 0.12.0 reads a selection you copied in Figma, with no saved .fig needed, and reads the Dev Mode notes designers leave on a design.

Read a copied selection without saving the file (#212)

Before this release, figlens needed a whole local copy of the file (File > Save local copy) to read even one screen. Now you can copy the selection in Figma (Cmd/Ctrl+C), save the clipboard's HTML, and turn it into a .fig that every command and MCP tool can read:

# macOS: save the clipboard's HTML
osascript -e 'the clipboard as «class HTML»' | sed 's/^«data HTML//;s/»$//' | xxd -r -p > copied.html
figlens clipboard copied.html screen.fig
figlens bundle screen.fig <node> frame.zip

The copy keeps the source file's node ids and brings along the components its instances use. It never overwrites an existing file.

A copy does not include image bytes or vector outlines. The command reports what is missing (missingImages, missingComponents, layersWithoutOutlines). Exports and bundles then warn wherever they draw without those inputs: an image drawn empty, a vector drawn as its box, or an instance whose component is not in the file. These export warnings apply to any .fig that lacks those inputs, not only copies.

Limitations:

  • Converting a copy is a CLI command only. MCP, Pi, OpenCode and OMP read the converted file with their existing tools.
  • Only copies from the macOS desktop app have been checked.

Dev Mode annotations and Ready for dev status (#213)

Dev Mode annotations are stored in .fig files, but figlens used to drop them, so agents reported the designer's notes as unavailable. Now:

  • nodes and bundles (design/nodes.json) put annotations on each annotated layer: the plain-text label, labelMarkdown, the pinned properties (such as fills or itemSpacing) and categoryId. The response lists the named categories it uses in annotationCategories.
  • Ready for dev status: frames and sections marked Ready for dev carry devStatus (READY_FOR_DEV or COMPLETED, with the description when one was written).
  • inspect shows bounded previews of these notes. inspect --flat adds devStatus= and annotations=N.
  • diff and change tracking report edited annotations, statuses and categories. A diff scoped below the page or document that holds them marks them as not compared, instead of reporting the result as complete.
  • The preview's Copy/Download Context keeps the category of each annotation.

The guidance for agents treats annotations as designer notes to check against the design, never as instructions.

Limitations:

  • Saved Dev Mode measurements are not emitted yet. How their stored values map to the ones Figma shows is not established, so they are listed as a known handoff gap.
  • The mapping was checked against stored data in four production files, two of them held out from development. It was not compared with Figma's own API output.

Update

figlens update            # or: brew upgrade figlens
npx @runkids/figlens@0.12.0 --version

v0.11.4

Choose a tag to compare

@github-actions github-actions released this 02 Nov 18:54
e998f8c

Smaller node handoffs and clearer design limits

Compact nodes, when you want them

Large frames often repeat the same styles and component structure. nodes --compact shares repeated style values; add --subtrees to share repeated subtrees while preserving every node's ID and bounds:

figlens nodes design.fig 12:345 --compact --subtrees

MCP: call nodes with compact: true and optionally subtrees: true. This works through the shared MCP tools in Pi, OpenCode and OMP. Without these options, nodes stays REST-shaped.

Compact output is a versioned wrapper, not ordinary REST JSON. Resolve its references before using REST paths; see the lossless wire contract. On 58 screens across four kits, per-kit median JSON-byte savings were 9–26% with style sharing and 11–27% with subtree sharing. These are byte measurements, not token guarantees. Independent decoding reproduced the default JSON exactly on those screens, five held-out screens and an expanded instance sublayer. Complete MCP replies still have an 8 MiB cap; large inputs also have finite construction guards.

More useful layout and limitation evidence

  • Grid auto layout now includes recorded row/column counts, gaps, child spans/alignment and ordered track sizes. Track sizes are exposed in _figlens.gridTracks, not guessed CSS strings. Stored cell anchors can disagree with saved positions, so cell indices are not emitted.
  • Stored Motion and code-layer content is identified as a handoff gap, rather than silently suggesting a complete static handoff. Animation and code are not reconstructed; detection has synthetic/schema coverage, not real-file confirmation.
  • Render warnings now identify paint types figlens does not draw, including shader, noise, pattern, video and emoji paints. This adds diagnostics, not rendering support.

Migration: semantic metadata is now sparse

Breaking change for metadata consumers: fields with no stored semantic source no longer produce state: "absent" entries. Empty semantic wrappers are omitted too. Verification is stated once at response-level _figlens.semantics.verification: "not-compared-with-figma", rather than repeated on each node. This also affects bundle design/nodes.json; inspect reports verification at response level.

Update consumers to tolerate missing semantic entries/wrappers and read verification from the response root. Missing evidence is not a Figma default or proof of support. Recorded and unsupported entries remain distinct. Removing this boilerplate made the measured default node output 18–26% smaller; compact-mode savings above are additional, measured after that change.

Update

figlens update
# Or run this exact npm version:
npx --yes @runkids/figlens@0.11.4 --version

Binaries remain unsigned. Snapshot mappings and figlens renders are not a claim of same-version Figma API or visual parity.

Changes: #201, #202, #209, #210, #211.

v0.11.3

Choose a tag to compare

@github-actions github-actions released this 02 Nov 06:06
fa001ad

Trace a built screen back to the design revision it used

A bundle names its frame by node id and Figma file key, but saving the same .fig again keeps both. A screen built from a bundle could not be tied to the exact revision it came from. bundle can now record the source file's hash:

figlens bundle design.fig 12:8 handoff.zip --hash-source
figlens bundle design.fig --frames frames.txt --out-dir handoff --hash-source

Over MCP, pass hashSource: true to bundle.

manifest.json then carries source.sha256, the SHA-256 of the saved .fig:

  • It matches shasum -a 256 design.fig.
  • A search result's snapshotId is sha256: plus the same digest.
  • Keep it with the node ids to tell one save of a file from the next.
  • It is null when the file changed while it was decoded or bundled, so the digest never names bytes the bundle did not come from.
  • Without the flag the field is absent, so existing bundles and tooling are unchanged.

It is opt-in because hashing reads the whole .fig once more. On a 409 MB file this took 0.15 s of a 1.15 s single-frame bundle. A batch or a running MCP server hashes each saved revision only once.

Update

figlens update

Or brew upgrade runkids/tap/figlens, or npx @runkids/figlens@0.11.3.

v0.11.2

Choose a tag to compare

@github-actions github-actions released this 01 Nov 18:12
7b284ba

figlens 0.11.2 changes how figlens tells an agent to build a screen. It ships no new commands or flags.

The problem

An agent asked to implement a whole design with /figlens yolo did not write frontend code. It converted the design's node JSON into per-screen data files and shipped a generic renderer that replayed the layer tree with inline coordinates. A rerun on a single screen was closer, but it still put every component in one file, ignored the requested styling system, used plain JavaScript with no lint or format, and baked text into images. It matched the reference image reasonably well, yet no frontend reviewer would merge the code.

The guidance treated building as matching pixels, and its only structural advice was to follow the project's conventions, which an empty project does not have.

What changes

figlens reads the design; the agent builds the frontend. Every path that asks an agent to implement a design now says so: the build, plan, yolo and audit workflows, the MCP server instructions, a bundle's context.md and the viewer's copied prompt.

  • Structure before markup. Component sets become components, with appearance variants and component properties as props and hover, focus or disabled variants as CSS and ARIA states. Repeated instances render from data. Sections use semantic elements, layout follows auto-layout intent, and shared headers and menus become one layout.
  • Project conventions, or a baseline. In an existing project, its conventions decide the language, file layout, styling and tooling. A new project first gets a professional baseline: strict TypeScript, a linter and formatter, code organized by responsibility, and styling through the requested system with the design's tokens.
  • Code review before pixels. Before compare-image, the agent checks that each design component maps to a component or data array, that markup and layout follow the design, that images, fonts and code are delivered efficiently, and that the project's type check, lint and format pass.
  • yolo builds in order, one screen at a time. Setup, tokens, components, the shared shell, then screens. It reports a design-component-to-code map, and a scope larger than one run stops at its last finished screen instead of generating the rest in bulk.
  • An instruction to keep code minimal cuts scope and features, not this bar.

The skill is also lighter. Index, watch, semantic and image search guidance moved from SKILL.md into references/search.md (78 KB to 54 KB), so an agent that loads the whole skill reads it only when it needs an index. figlens workflow search and the MCP workflow tool serve the same content as before.

Update

figlens update
# or
brew upgrade runkids/tap/figlens
npx @runkids/figlens@0.11.2 --version

Pi, OpenCode and OMP pick up the new guidance after pi update or a reinstall of @runkids/figlens. Claude Code, Codex, Cursor, Antigravity and Kimi plugins pin @runkids/figlens@0.11.2.

Verification and limits

The full Rust, npm, packaging and documentation checks pass. This release changes guidance, not decoding or rendering, so output files are unchanged. Whether a given model follows the new guidance has not been measured on a full agent run yet; a host or user instruction that strongly prefers minimal code can still compete with it.

v0.11.1

Choose a tag to compare

@github-actions github-actions released this 01 Nov 14:29
8c9edc0

figlens 0.11.1 makes compare-image usable on real screens: a full-page capture shorter than the frame, content that comes from data, and light borders the default threshold cannot see. Each change applies to the CLI and MCP; Pi, OpenCode V2 and OMP pass the same parameters and results through.

Compare a full-page capture shorter than the frame

A built page is often shorter than its Figma frame, for example when a list has fewer rows on the test account. Before, a screenshot more than 1% shorter was refused. With --full-page (MCP fullPage), it is compared over the rows it has, and the missing rows count as differing and form their own region, so the score does not hide them.

figlens compare-image design.fig 12:34 page.png --full-page

The reply's uncompared.missing says how many frame rows (device px) the capture lacked. Without --full-page, a screenshot more than 1% shorter is still refused with a hint to capture the full page.

Leave data-driven content out of the score

On a screen with back-office banners, user names or balances, those differences dominated the score, so fixing a real layout bug barely moved it.

  • --ignore <id>[,<id>...] (MCP ignore) leaves each listed layer's render box out of mismatch, its splits and the regions; its pixels leave the denominator too. The reply lists what was ignored under ignored, and the diff image paints those pixels gray. A layer that draws nothing (hidden, a mask, clipped away) is refused, since its layout box could cover content that does draw.
  • An ignored layer's own size and position are no longer measured. Check them with inspect.
  • A region over image fills is marked image: true, and the reply adds imageMismatch and nonImageMismatch beside the text split. Confirm the asset, crop and fit of such a region before ignoring it; it is not a layout match by itself.
figlens compare-image design.fig 12:34 page.png --ignore 12:40,12:41 --out diff.png

See light borders and dividers

The default threshold, 48/255 per channel, keeps anti-aliasing and font smoothing out but misses a light gray border on off-white. --threshold N (MCP threshold, 1-254) lowers it for a second pass; the reply reports the threshold it used. In a test, a 1 px #e4edff divider on white is missed at 48 and found at 16. A lower threshold also sees more rendering noise, so read only the new regions.

Agent guidance

The audit and build guidance say when to mask data-driven layers, to report what was ignored, and to rerun at --threshold 16 after a clean default run.

Also

The website figlens.runkids.cc now serves the docs in every README language, generated from the repository.

Verification

Each change has unit tests on the public fixture; the full Rust, npm and docs checks and the installation lanes passed on the merged main. The --threshold 16 noise level was not measured on real pages.

Update

Script installs: figlens update. Homebrew: brew upgrade figlens. Plugin installs: update the plugin in your host. For Pi, OpenCode V2 and OMP, update @runkids/figlens through your host and restart it so its tools and skill match the binary.

Full changelog

v0.11.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 19:50
d40ccb2

figlens 0.11.0 installs as one plugin in more agents, and closes the steps agents used to do by hand when building a screen: checking the result against the design, finding font files, exporting assets one call at a time, and comparing two frames. Unless noted, each change applies to the CLI and MCP; Pi, OpenCode V2 and OMP pass the same results through.

Install as a plugin

Claude Code, Codex, Antigravity, Cursor and Kimi Code can now install the figlens skill and MCP server together as one plugin. The server is pinned to the plugin's release (npx -y @runkids/figlens@0.11.0 mcp), so the skill never describes tools the server lacks.

claude plugin marketplace add runkids/figlens && claude plugin install figlens@figlens
codex plugin marketplace add runkids/figlens && codex plugin add figlens@figlens

The other hosts' commands are in Plugin setup. A Claude Code install from the release tag was run end to end in an isolated configuration, and Codex loaded the plugin from a local checkout; Antigravity's manifest passed agy plugin validate; the Cursor and Kimi Code manifests follow their published plugin references but were not installed on those hosts.

Check a build against its frame

figlens compare-image <file.fig> <node> <screenshot.png> [--out diff.png] (MCP compare_image) measures a screenshot of the built screen against the frame as figlens renders it, instead of comparing the two by eye. It reports the share of differing pixels, separates text from non-text differences, and lists the largest regions in frame coordinates with the layers under each. --out writes a diff image.

  • A pixel counts as different only when nothing within one CSS pixel of it in the other image is within 48/255 on every channel, so subpixel and anti-aliasing noise stays out. It is not a perceptual metric: a light gray border on off-white is below the threshold; use inspect --colors for exact colors.
  • A region over text layers is marked text: true. That covers font fallback, and also wrong text color, size or copy, so check each text region before you call it font noise.
  • One viewport and one state per screenshot. A screenshot narrower than the frame, or more than 1% shorter, is refused with what to capture instead; a taller one is compared over the frame's height.

Fewer calls to read a screen

  • inspect --full-text (MCP fullText) returns each text layer's complete characters, up to 10,000, instead of a 200-character preview. inspect --colors (MCP colors) adds each layer's solid fills as hex, with the bound variable's name. Both work with --flat. Gradients, images, strokes and effects are not included.
  • inspect <file> <nodeA> --compare <nodeB> (MCP compare) compares any two nodes that are not nested in each other, such as an empty and a filled state or a mobile and a desktop frame, with the same field list and limits as --compare-variant. It adds rootDelta (size and position change of the two roots) and a reason for each unpaired node.
  • export <file.fig> --batch items.json (MCP export_batch) exports up to 64 nodes in one call and returns one manifest with each file's size, layout and render boxes and warnings. A bad item or path fails the whole call before anything is written; a missing node or an over-budget item is reported per item while the others are written.

Fonts and tokens

  • verify looks for each design font face in the project and the system font folders and marks it local_file with the matching paths, or unverified. With --fonts <dir> (MCP fonts), a match inside that folder also gets a ready @font-face rule with a relative src. Nothing is copied or downloaded, and a found file is not a browser check: font licensing and rendering are still yours to confirm.
  • variables --format dtcg exports an alias into another collection, such as a Light/Dark collection that aliases primitives. Before, the whole export failed with cross_collection_alias. The token gets the resolved value and records the target in $extensions.org.figlens.aliasOf. When the target collection has several modes, choose one with --alias-mode COLLECTION=MODE (MCP aliasModes).

Measure a layer's share of an export

export <file> <node> <out> --hide ID[,ID] (MCP hide) renders the node with those layers hidden, over the same area. Diffing it with the normal export shows where that layer, as figlens draws it, changes the result. For a warned image adjustment figlens cannot draw, this shows how much of the layer reaches the export; it does not measure the adjustment itself.

Agent guidance

/figlens yolo runs keep their exports in their own directory and, after a verified success, delete everything there except the verification record and the final screenshot; they also stop the servers and browsers they started. The build guidance now uses compare-image for the visual check, and a single-screen build loads only the guidance it needs.

Verification

Each change has unit tests. The full Rust, npm and docs checks and the installation lanes passed on the merged main. Token export, --hide, --compare and compare-image were also checked on private designs; the measured numbers are in the pull requests.

Update

Script installs: figlens update. Homebrew: brew upgrade figlens. Plugin installs: update the plugin in your host. For Pi, OpenCode V2 and OMP, update @runkids/figlens through your host and restart it so its tools and skill match the binary.

Full changelog

v0.10.1

Choose a tag to compare

@github-actions github-actions released this 31 Oct 15:09
6fd395e

figlens 0.10.1 fixes output that looked complete when it was not, and adds the details agents kept fetching with an extra call. Everything applies to the CLI and MCP; Pi, OpenCode V2 and OMP pass the same results through.

Output that no longer reads as complete when it is not

  • export now lists image adjustments it did not render (exposure, contrast and the like) in its warnings, as bundle and verify already did. Before, an exported PNG could differ from Figma with an empty warning list.
  • diff that hits a work limit prints stopped early with the stopped sides and a hint to narrow it with --node, instead of no changes.
  • MCP calls whose output folder does not exist now say so and name the folder, instead of reporting it as outside the allowed roots. The CLI error names the folder too.

More detail where agents needed a second call

  • inspect --flat lines carry auto-layout alignment (align=space-between/center), fixed or fill sizing (size=fixed/hug, left out when both axes hug), and visible shadows and blurs (shadow=0,8,24,#1e56e6/0.1, inner-shadow, blur, bg-blur).

    1:1 FRAME "bar" shadow=0,8,24,#1e56e6/0.1 H gap=20 pad=0,0,0,0 align=space-between/center size=fixed/hug
    
  • inspect --compare-variant pairs a child with its counterpart when it is the only child of that name and type on both sides (pairedNodes, with per-pair changes), and names bound variables (variableId, variableName), including library variables from their cached copies. Duplicate or renamed layers stay unpaired, as before.

  • variables --format dtcg exports a library collection that variables <file> <node> lists, from the copies cached in the file, marked $extensions.org.figlens.libraryCopy: true. These are the values saved with this file and may lag the published library. usages also names a library variable resource; existing indexes pick this up when rebuilt.

  • MCP workflow has a search topic for search and index guidance, so workflow(topic=audit) drops from about 34 KB to 11 KB and build from about 50 KB to 27 KB.

Verification

Each change has unit tests; the full Rust, npm and docs checks passed on the merged main. None of these changes was checked against a private design in this release.

Update

Script installs: figlens update. Homebrew: brew upgrade figlens. For agent-host installations, update @runkids/figlens through your host and restart it so its tools and skill match the binary.

Full changelog

v0.10.0

Choose a tag to compare

@github-actions github-actions released this 31 Oct 09:52
445ad4f

figlens 0.10.0 adds local design search, optional persistent indexes, and tools for following changes across saved .fig files. These capabilities are available through the CLI, MCP, Pi, OpenCode V2 and OMP, with semantic and image search also available in the viewer.

Search and follow a design

  • Ranked text search reads effective instance content, including overrides. Ordinary search creates no index database and needs no model.
  • Opt into a local SQLite index for repeated or cross-file search. Storage is remembered per workspace; refresh, relocate, rebuild and clear operations manage its lifecycle.
  • Find exact component, variable, style and image usages, compare saved revisions, and follow affected scopes with opt-in history and subscriptions.
  • Optional, experimental semantic, image and hybrid search use models you configure locally. figlens does not download models automatically.
# Ordinary search: no index setup
figlens search design.fig "checkout button"

# Optional: prepare an index for repeated searches
figlens index prepare design.fig --scope 0:0
figlens search --file design.fig "checkout button" --node 0:0

Design handoff and rendering

List screens with tree --top-frames, pipe frame lists into batch operations, inspect compact per-node output, match exported icons to project assets, and export variables as DTCG tokens. Viewer results and handoffs identify their source and snapshot.

This release also fixes repeating STRETCH image fills, avoids embedding the same image repeatedly in an SVG, optimizes PNG output losslessly, and improves viewer startup and tracked-save processing.

Before you use advanced search

Semantic and visual ranking remain experimental: their held-out quality targets and the full native performance acceptance matrix are not yet satisfied. Inspect returned matches; no universal save-to-event latency is guaranteed. Normal platform and host installation CI passed on the release commit. All five release archives passed checksum, bundled-skill and system-SQLite dependency checks; the macOS arm64 binaries also passed the public example and bundle smoke checks.

A .fig remains a saved snapshot. Export a new local copy from Figma for figlens to detect design changes. Watching runs while a host or process holds the tracked scope. Clearing an index keeps your source designs and model files.

See the search and index guide and model setup.

Update

Script installs: figlens update. Homebrew: brew upgrade figlens. For agent-host installations, update @runkids/figlens through your host and restart it so its tools and skill match the binary.

Full changelog

v0.9.3

Choose a tag to compare

@github-actions github-actions released this 29 Oct 11:41
682788e

This release speeds up asset export, makes exported sizes predictable, reads more node data, and fixes the node types tree prints.

Export many nodes from one decode

export used to decode the whole .fig for every node, which made exporting hundreds of frames slow. --frames now exports a list of nodes from a single decode:

figlens export design.fig --frames ids.txt --out-dir exports --format png --scale 2

The list has one node id per line, and - reads it from stdin. Each node is written to <dir>/<nodeId>.<format> (: becomes -). The command prints one JSON line per id: the usual export output plus nodeId, or {nodeId, error}. The export options are the same as for a single export, and a list can hold at most 10,000 ids.

Export at the layout box

By default, an export covers everything the node draws. Shadows, outside strokes and overflowing children can therefore make the image larger than the frame. Every export now reports layoutBox (the node's own box) and renderBox (what it draws). When the two differ, warnings names what reaches past the frame.

When the asset must fill its layout slot exactly, add --bounds layout (MCP: bounds: "layout"). The export is then cut to the node's own box.

Per-side stroke weights

A stroke with a different weight on each side, such as a bottom-only divider, used to read as strokeWeight: 0 and disappear from exports. nodes and inspect now emit individualStrokeWeights ({top, right, bottom, left}), and export and preview draw each side at its own weight.

tree --json carries size and visibility

tree --json now includes the following, so you can classify frames without a second nodes call:

  • size: [width, height], the same values find and nodes print.
  • visible: false: on hidden layers only.
  • layoutMode: on auto-layout frames.

The text output and the existing JSON keys are unchanged.

tree prints the same node types as nodes and find

Behavior change. tree and the preview layer tree used to print the raw stored type, so a component appeared as SYMBOL and a component set as FRAME. They now print the REST types that nodes, find and inspect already use:

Before Now
SYMBOL COMPONENT
FRAME (component set) COMPONENT_SET
FRAME (resize-to-fit group) GROUP
ROUNDED_RECTANGLE RECTANGLE

Update any script that filters tree output by the old names.

Inlining exported SVGs

Exported SVGs draw in page coordinates under one root <g transform>, and their definition ids are scoped to the node. Tools that rename ids can break this. For example, SVGO's default cleanupIds renames them to a, b, …, so when several such SVGs are inlined into one page, a later icon picks up an earlier one's clip and vanishes. Keep the root transform, and use prefixIds or disable cleanupIds. The skill and docs now say so.

Update

figlens update
# or
brew upgrade runkids/tap/figlens
# or
npx @runkids/figlens@0.9.3 --version