@runkids/figlens 0.0.0-stage → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,3 +1,36 @@
1
- # Temporary Holding Version
1
+ <p align="center">
2
+ <img src="https://raw.githubusercontent.com/runkids/figlens/main/assets/logo.png" alt="figlens" width="320">
3
+ </p>
2
4
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
5
+ # @runkids/figlens
6
+
7
+ Build UI anywhere. Just bring a `.fig`.
8
+
9
+ Give your AI coding agent a saved Figma file (**File > Save local copy**). figlens reads the design offline, on your laptop, a CI runner or a cloud AI server. No Figma installation, login or API key needed.
10
+
11
+ This package runs the native `figlens` binary: the first run downloads the release build for your platform, checks it against the release's `SHA256SUMS` and caches it. Node.js 18 or later; macOS, Linux and Windows.
12
+
13
+ ## Use it
14
+
15
+ ```sh
16
+ npx @runkids/figlens tree design.fig --depth 2 # pages and frames, with their ids
17
+ npx @runkids/figlens nodes design.fig 12:345 > frame.json # a frame as Figma REST JSON
18
+ npx @runkids/figlens bundle design.fig 12:345 frame.zip # nodes, variables, assets and a reference render
19
+ npx @runkids/figlens export design.fig 12:345 frame.png --scale 2
20
+ ```
21
+
22
+ Or install it once: `npm i -g @runkids/figlens`, then run `figlens`.
23
+
24
+ ## MCP server
25
+
26
+ Let your agent call `tree`, `nodes`, `variables`, `bundle` and `export` as tools:
27
+
28
+ ```json
29
+ { "mcpServers": { "figlens": { "command": "npx", "args": ["-y", "@runkids/figlens", "mcp"] } } }
30
+ ```
31
+
32
+ ## More
33
+
34
+ Documentation, the design bundle layout, accuracy measurements and other install methods (Homebrew, install scripts): [github.com/runkids/figlens](https://github.com/runkids/figlens).
35
+
36
+ MIT license.
package/bin/figlens.js ADDED
@@ -0,0 +1,67 @@
1
+ #!/usr/bin/env node
2
+ // `npx @runkids/figlens ...`: downloads this version's release binary on first use, checks it against
3
+ // the release's SHA256SUMS, caches it, then runs it. No install script: npx and pnpm may skip those.
4
+ const { spawnSync } = require("node:child_process");
5
+ const crypto = require("node:crypto");
6
+ const fs = require("node:fs");
7
+ const os = require("node:os");
8
+ const path = require("node:path");
9
+ const { asset } = require("../target.js");
10
+ const { version } = require("../package.json");
11
+
12
+ const cacheRoot =
13
+ process.env.XDG_CACHE_HOME ||
14
+ (process.platform === "win32" ? process.env.LOCALAPPDATA : null) ||
15
+ path.join(os.homedir(), ".cache");
16
+ const { archive, binary } = asset(process.platform, process.arch, version);
17
+ const dir = path.join(cacheRoot, "figlens", "npm", version);
18
+ const bin = path.join(dir, binary);
19
+
20
+ async function download(url) {
21
+ const res = await fetch(url);
22
+ if (!res.ok) throw new Error(`${url}: HTTP ${res.status}`);
23
+ return Buffer.from(await res.arrayBuffer());
24
+ }
25
+
26
+ async function install() {
27
+ const base = `https://github.com/runkids/figlens/releases/download/v${version}`;
28
+ const [bytes, sums] = await Promise.all([
29
+ download(`${base}/${archive}`),
30
+ download(`${base}/SHA256SUMS`),
31
+ ]);
32
+ const want = sums
33
+ .toString()
34
+ .split("\n")
35
+ .find((line) => line.trim().endsWith(archive));
36
+ const got = crypto.createHash("sha256").update(bytes).digest("hex");
37
+ if (!want || want.split(/\s+/)[0] !== got) {
38
+ throw new Error(`${archive} does not match the release's SHA256SUMS`);
39
+ }
40
+ // Extract next to the cache, then rename into place, so a half-written binary is never run.
41
+ fs.mkdirSync(path.dirname(dir), { recursive: true });
42
+ const tmp = fs.mkdtempSync(path.join(path.dirname(dir), ".tmp-"));
43
+ try {
44
+ fs.writeFileSync(path.join(tmp, archive), bytes);
45
+ // bsdtar ships with Windows 10+ and extracts zips as well.
46
+ const tar = spawnSync("tar", ["-xf", archive, binary], { cwd: tmp, stdio: "inherit" });
47
+ if (tar.status !== 0) throw new Error("tar could not extract the archive");
48
+ fs.rmSync(path.join(tmp, archive));
49
+ fs.rmSync(dir, { recursive: true, force: true });
50
+ fs.renameSync(tmp, dir);
51
+ } catch (e) {
52
+ fs.rmSync(tmp, { recursive: true, force: true });
53
+ throw e;
54
+ }
55
+ }
56
+
57
+ async function main() {
58
+ if (!fs.existsSync(bin)) await install();
59
+ const run = spawnSync(bin, process.argv.slice(2), { stdio: "inherit" });
60
+ if (run.error) throw run.error;
61
+ process.exit(run.status ?? 1);
62
+ }
63
+
64
+ main().catch((e) => {
65
+ console.error(`figlens: ${e.message}`);
66
+ process.exit(1);
67
+ });
package/package.json CHANGED
@@ -1,6 +1,37 @@
1
1
  {
2
2
  "name": "@runkids/figlens",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "0.1.2",
4
+ "description": "Read Figma .fig files offline: REST nodes, variables, assets, renders and design bundles for AI agents",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git+https://github.com/runkids/figlens.git"
9
+ },
10
+ "homepage": "https://github.com/runkids/figlens",
11
+ "keywords": [
12
+ "figma",
13
+ "fig",
14
+ "design-to-code",
15
+ "cli",
16
+ "ai"
17
+ ],
18
+ "bin": {
19
+ "figlens": "bin/figlens.js"
20
+ },
21
+ "files": [
22
+ "bin/",
23
+ "target.js"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "os": [
29
+ "darwin",
30
+ "linux",
31
+ "win32"
32
+ ],
33
+ "cpu": [
34
+ "arm64",
35
+ "x64"
36
+ ]
37
+ }
package/target.js ADDED
@@ -0,0 +1,18 @@
1
+ // The release archive for a Node platform and arch (see scripts/build-dist.sh).
2
+ const OS = { darwin: "apple-darwin", linux: "unknown-linux-musl", win32: "pc-windows-gnu" };
3
+ const ARCH = { arm64: "aarch64", x64: "x86_64" };
4
+
5
+ function asset(platform, arch, version) {
6
+ // Windows on ARM runs the x64 build under emulation; there is no aarch64 Windows build.
7
+ const cpu = platform === "win32" ? "x64" : arch;
8
+ if (!OS[platform] || !ARCH[cpu]) {
9
+ throw new Error(`figlens has no build for ${platform}/${arch}`);
10
+ }
11
+ const windows = platform === "win32";
12
+ return {
13
+ archive: `figlens-${version}-${ARCH[cpu]}-${OS[platform]}.${windows ? "zip" : "tar.gz"}`,
14
+ binary: windows ? "figlens.exe" : "figlens",
15
+ };
16
+ }
17
+
18
+ module.exports = { asset };