canicode 0.8.5 → 0.8.7
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 +2 -2
- package/dist/cli/index.js +70 -11
- package/dist/cli/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/mcp/server.js +277 -21
- package/dist/mcp/server.js.map +1 -1
- package/docs/{CUSTOMIZATION.md → REFERENCE.md} +1 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -108,7 +108,7 @@ Or with a Figma API token (no Figma MCP needed):
|
|
|
108
108
|
claude mcp add canicode -e FIGMA_TOKEN=figd_xxxxxxxxxxxxx -- npx -y -p canicode canicode-mcp
|
|
109
109
|
```
|
|
110
110
|
|
|
111
|
-
For Cursor / Claude Desktop config, see [`docs/
|
|
111
|
+
For Cursor / Claude Desktop config, see [`docs/REFERENCE.md`](docs/REFERENCE.md).
|
|
112
112
|
|
|
113
113
|
**Figma MCP Rate Limits**
|
|
114
114
|
|
|
@@ -160,7 +160,7 @@ Posts analysis as a PR comment. Fails if score is below threshold. See [**canico
|
|
|
160
160
|
|
|
161
161
|
> Ask any LLM *"Write a canicode custom rule that checks X"* — it can generate the JSON for you.
|
|
162
162
|
|
|
163
|
-
See [`docs/
|
|
163
|
+
See [`docs/REFERENCE.md`](docs/REFERENCE.md) for the full guide, examples, and all available options.
|
|
164
164
|
|
|
165
165
|
---
|
|
166
166
|
|
package/dist/cli/index.js
CHANGED
|
@@ -72,12 +72,26 @@ function resizePng(png, targetWidth, targetHeight) {
|
|
|
72
72
|
function compareScreenshots(path1, path2, diffOutputPath) {
|
|
73
73
|
const raw1 = PNG.sync.read(readFileSync(path1));
|
|
74
74
|
const raw2 = PNG.sync.read(readFileSync(path2));
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
75
|
+
if (raw1.width !== raw2.width || raw1.height !== raw2.height) {
|
|
76
|
+
const width2 = Math.max(raw1.width, raw2.width);
|
|
77
|
+
const height2 = Math.max(raw1.height, raw2.height);
|
|
78
|
+
const img1 = resizePng(raw1, width2, height2);
|
|
79
|
+
const img2 = resizePng(raw2, width2, height2);
|
|
80
|
+
const diff2 = new PNG({ width: width2, height: height2 });
|
|
81
|
+
pixelmatch(img1.data, img2.data, diff2.data, width2, height2, { threshold: 0.1 });
|
|
82
|
+
mkdirSync(dirname(diffOutputPath), { recursive: true });
|
|
83
|
+
writeFileSync(diffOutputPath, PNG.sync.write(diff2));
|
|
84
|
+
return {
|
|
85
|
+
similarity: 0,
|
|
86
|
+
diffPixels: width2 * height2,
|
|
87
|
+
totalPixels: width2 * height2,
|
|
88
|
+
width: width2,
|
|
89
|
+
height: height2
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
const { width, height } = raw1;
|
|
79
93
|
const diff = new PNG({ width, height });
|
|
80
|
-
const diffPixels = pixelmatch(
|
|
94
|
+
const diffPixels = pixelmatch(raw1.data, raw2.data, diff.data, width, height, {
|
|
81
95
|
threshold: 0.1
|
|
82
96
|
});
|
|
83
97
|
mkdirSync(dirname(diffOutputPath), { recursive: true });
|
|
@@ -1105,7 +1119,7 @@ async function loadFromApi(fileKey, nodeId, token) {
|
|
|
1105
1119
|
}
|
|
1106
1120
|
|
|
1107
1121
|
// package.json
|
|
1108
|
-
var version = "0.8.
|
|
1122
|
+
var version = "0.8.7";
|
|
1109
1123
|
var AnalysisNodeTypeSchema = z.enum([
|
|
1110
1124
|
"DOCUMENT",
|
|
1111
1125
|
"CANVAS",
|
|
@@ -4267,7 +4281,7 @@ EXAMPLE
|
|
|
4267
4281
|
USAGE
|
|
4268
4282
|
canicode analyze <url> --custom-rules ./my-rules.json
|
|
4269
4283
|
|
|
4270
|
-
Full guide: docs/
|
|
4284
|
+
Full guide: docs/REFERENCE.md
|
|
4271
4285
|
Examples: examples/custom-rules.json
|
|
4272
4286
|
|
|
4273
4287
|
TIP: Ask any LLM "Write a canicode custom rule that checks X" with the
|
|
@@ -4302,16 +4316,61 @@ EXAMPLE
|
|
|
4302
4316
|
USAGE
|
|
4303
4317
|
canicode analyze <url> --config ./my-config.json
|
|
4304
4318
|
|
|
4305
|
-
Full guide: docs/
|
|
4319
|
+
Full guide: docs/REFERENCE.md
|
|
4306
4320
|
Examples: examples/config.json
|
|
4307
4321
|
`.trimStart());
|
|
4308
4322
|
}
|
|
4323
|
+
function printDocsVisualCompare() {
|
|
4324
|
+
console.log(`
|
|
4325
|
+
VISUAL COMPARE \u2014 Pixel-level comparison between Figma and AI-generated code
|
|
4326
|
+
|
|
4327
|
+
USAGE
|
|
4328
|
+
canicode visual-compare ./index.html --figma-url 'https://www.figma.com/design/ABC/File?node-id=1-234'
|
|
4329
|
+
|
|
4330
|
+
OPTIONS
|
|
4331
|
+
--figma-url <url> Figma URL with node-id (required)
|
|
4332
|
+
--token <token> Figma API token (or FIGMA_TOKEN env var)
|
|
4333
|
+
--output <dir> Output directory (default: /tmp/canicode-visual-compare)
|
|
4334
|
+
--width <px> Viewport width override (default: match Figma screenshot)
|
|
4335
|
+
--height <px> Viewport height override (default: match Figma screenshot)
|
|
4336
|
+
|
|
4337
|
+
OUTPUT FILES
|
|
4338
|
+
/tmp/canicode-visual-compare/
|
|
4339
|
+
figma.png Figma screenshot (scale=2)
|
|
4340
|
+
code.png Playwright render of your HTML
|
|
4341
|
+
diff.png Pixel diff (red = different pixels)
|
|
4342
|
+
|
|
4343
|
+
JSON OUTPUT (stdout)
|
|
4344
|
+
{
|
|
4345
|
+
"similarity": 87, // 0-100%
|
|
4346
|
+
"diffPixels": 1340,
|
|
4347
|
+
"totalPixels": 102400,
|
|
4348
|
+
"width": 800,
|
|
4349
|
+
"height": 600,
|
|
4350
|
+
"figmaScreenshot": "/tmp/.../figma.png",
|
|
4351
|
+
"codeScreenshot": "/tmp/.../code.png",
|
|
4352
|
+
"diff": "/tmp/.../diff.png"
|
|
4353
|
+
}
|
|
4354
|
+
|
|
4355
|
+
HOW IT WORKS
|
|
4356
|
+
1. Fetches Figma screenshot via REST API (scale=2)
|
|
4357
|
+
2. Reads screenshot dimensions
|
|
4358
|
+
3. Renders your HTML with Playwright at the same viewport size
|
|
4359
|
+
4. Compares pixel-by-pixel with pixelmatch (threshold: 0.1)
|
|
4360
|
+
5. Returns similarity % and diff image
|
|
4361
|
+
|
|
4362
|
+
REQUIREMENTS
|
|
4363
|
+
npx playwright install chromium
|
|
4364
|
+
Figma API token with read access
|
|
4365
|
+
`.trimStart());
|
|
4366
|
+
}
|
|
4309
4367
|
var DOCS_TOPICS = {
|
|
4310
4368
|
setup: printDocsSetup,
|
|
4311
4369
|
install: printDocsSetup,
|
|
4312
4370
|
// alias
|
|
4313
4371
|
rules: printDocsRules,
|
|
4314
|
-
config: printDocsConfig
|
|
4372
|
+
config: printDocsConfig,
|
|
4373
|
+
"visual-compare": printDocsVisualCompare
|
|
4315
4374
|
};
|
|
4316
4375
|
function handleDocs(topic) {
|
|
4317
4376
|
if (!topic) {
|
|
@@ -4323,7 +4382,7 @@ function handleDocs(topic) {
|
|
|
4323
4382
|
handler();
|
|
4324
4383
|
} else {
|
|
4325
4384
|
console.error(`Unknown docs topic: ${topic}`);
|
|
4326
|
-
console.error(`Available topics: setup, rules, config`);
|
|
4385
|
+
console.error(`Available topics: setup, rules, config, visual-compare`);
|
|
4327
4386
|
process.exit(1);
|
|
4328
4387
|
}
|
|
4329
4388
|
}
|
|
@@ -5034,7 +5093,7 @@ cli.command("list-rules", "List all analysis rules with scores and severity").op
|
|
|
5034
5093
|
process.exit(1);
|
|
5035
5094
|
}
|
|
5036
5095
|
});
|
|
5037
|
-
cli.command("docs [topic]", "Show documentation (topics: setup, rules, config)").action((topic) => {
|
|
5096
|
+
cli.command("docs [topic]", "Show documentation (topics: setup, rules, config, visual-compare)").action((topic) => {
|
|
5038
5097
|
handleDocs(topic);
|
|
5039
5098
|
});
|
|
5040
5099
|
cli.help((sections) => {
|