@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 +35 -2
- package/bin/figlens.js +67 -0
- package/package.json +35 -4
- package/target.js +18 -0
package/README.md
CHANGED
|
@@ -1,3 +1,36 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
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.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
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 };
|