graphics-debug 0.0.43 → 0.0.45
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 +33 -12
- package/dist/{chunk-W33G6ZE6.js → chunk-G7CTE4MX.js} +5 -2
- package/dist/chunk-G7CTE4MX.js.map +1 -0
- package/dist/{chunk-27C7TC5E.js → chunk-RBTAO2QZ.js} +2 -2
- package/dist/cli/cli.js +2 -2
- package/dist/lib/getSvgFromGraphicsObject.js +1 -1
- package/dist/lib/index.js +2 -2
- package/dist/lib/matcher.d.ts +4 -1
- package/dist/lib/matcher.js +6 -4
- package/dist/lib/matcher.js.map +1 -1
- package/dist/lib/react.js +2 -2
- package/package.json +1 -1
- package/dist/chunk-W33G6ZE6.js.map +0 -1
- /package/dist/{chunk-27C7TC5E.js.map → chunk-RBTAO2QZ.js.map} +0 -0
package/README.md
CHANGED
|
@@ -30,7 +30,6 @@ Don't have access to the cli? Paste into the online version: https://graphicsdeb
|
|
|
30
30
|
|
|
31
31
|
https://github.com/user-attachments/assets/9f3f41e6-b0fe-416a-a551-ba5c5b920cad
|
|
32
32
|
|
|
33
|
-
|
|
34
33
|
## Format
|
|
35
34
|
|
|
36
35
|
The `graphics` json object is very simple, here's the basic schema:
|
|
@@ -167,9 +166,20 @@ import { getSvgFromGraphicsObject } from "graphics-debug"
|
|
|
167
166
|
|
|
168
167
|
// Create your graphics object
|
|
169
168
|
const graphicsObject = {
|
|
170
|
-
points: [
|
|
171
|
-
|
|
172
|
-
|
|
169
|
+
points: [
|
|
170
|
+
{ x: 0, y: 0, label: "Origin" },
|
|
171
|
+
{ x: 100, y: 100, color: "red" },
|
|
172
|
+
],
|
|
173
|
+
lines: [
|
|
174
|
+
{
|
|
175
|
+
points: [
|
|
176
|
+
{ x: 0, y: 0 },
|
|
177
|
+
{ x: 100, y: 100 },
|
|
178
|
+
],
|
|
179
|
+
strokeColor: "blue",
|
|
180
|
+
},
|
|
181
|
+
],
|
|
182
|
+
title: "My Graph",
|
|
173
183
|
}
|
|
174
184
|
|
|
175
185
|
// Generate SVG string directly from the object
|
|
@@ -192,22 +202,35 @@ Then use the matcher in your tests:
|
|
|
192
202
|
```tsx
|
|
193
203
|
import { expect, test } from "bun:test"
|
|
194
204
|
import "graphics-debug/matcher"
|
|
195
|
-
import type { GraphicsObject } from "graphics-debug"
|
|
205
|
+
import type { GraphicsObject } from "graphics-debug"
|
|
196
206
|
|
|
197
207
|
// Your test graphics object
|
|
198
208
|
const graphicsObject: GraphicsObject = {
|
|
199
|
-
points: [
|
|
200
|
-
|
|
201
|
-
|
|
209
|
+
points: [
|
|
210
|
+
{ x: 0, y: 0, label: "Origin" },
|
|
211
|
+
{ x: 100, y: 100, color: "red" },
|
|
212
|
+
],
|
|
213
|
+
lines: [
|
|
214
|
+
{
|
|
215
|
+
points: [
|
|
216
|
+
{ x: 0, y: 0 },
|
|
217
|
+
{ x: 100, y: 100 },
|
|
218
|
+
],
|
|
219
|
+
strokeColor: "blue",
|
|
220
|
+
},
|
|
221
|
+
],
|
|
222
|
+
title: "My Test Graphics",
|
|
202
223
|
}
|
|
203
224
|
|
|
204
225
|
test("should match the expected visualization", async () => {
|
|
205
226
|
// First run creates the snapshot
|
|
206
227
|
// Subsequent runs will compare against saved snapshot
|
|
207
228
|
await expect(graphicsObject).toMatchGraphicsSvg(import.meta.path)
|
|
208
|
-
|
|
229
|
+
|
|
209
230
|
// You can also provide a custom name for the snapshot:
|
|
210
|
-
await expect(graphicsObject).toMatchGraphicsSvg(import.meta.path,
|
|
231
|
+
await expect(graphicsObject).toMatchGraphicsSvg(import.meta.path, {
|
|
232
|
+
svgName: "custom-name",
|
|
233
|
+
})
|
|
211
234
|
})
|
|
212
235
|
```
|
|
213
236
|
|
|
@@ -250,5 +273,3 @@ Here is the content of the `exampleGraphics.json` file:
|
|
|
250
273
|
```
|
|
251
274
|
|
|
252
275
|
You can load this example into the application to visualize the graphics objects and understand how the `graphics-debug` module works.
|
|
253
|
-
|
|
254
|
-
|
|
@@ -141,7 +141,10 @@ function getSvgFromGraphicsObject(graphics, {
|
|
|
141
141
|
points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
142
142
|
fill: "none",
|
|
143
143
|
stroke: line.strokeColor || "black",
|
|
144
|
-
"stroke-width": (line.strokeWidth ? line.strokeWidth * matrix.a : 1).toString()
|
|
144
|
+
"stroke-width": (line.strokeWidth ? line.strokeWidth * matrix.a : 1).toString(),
|
|
145
|
+
...line.strokeDash && {
|
|
146
|
+
"stroke-dasharray": Array.isArray(line.strokeDash) ? line.strokeDash.join(" ") : line.strokeDash
|
|
147
|
+
}
|
|
145
148
|
}
|
|
146
149
|
})),
|
|
147
150
|
// Rectangles
|
|
@@ -303,4 +306,4 @@ function getSvgFromGraphicsObject(graphics, {
|
|
|
303
306
|
export {
|
|
304
307
|
getSvgFromGraphicsObject
|
|
305
308
|
};
|
|
306
|
-
//# sourceMappingURL=chunk-
|
|
309
|
+
//# sourceMappingURL=chunk-G7CTE4MX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/getSvgFromGraphicsObject.ts"],"sourcesContent":["import {\n transform,\n compose,\n translate,\n scale,\n applyToPoint,\n identity,\n type Matrix,\n} from \"transformation-matrix\"\nimport type { GraphicsObject, Point } from \"./types\"\nimport { stringify } from \"svgson\"\nimport pretty from \"pretty\"\n\nconst DEFAULT_SVG_SIZE = 640\nconst PADDING = 40\n\ninterface Bounds {\n minX: number\n maxX: number\n minY: number\n maxY: number\n}\n\nfunction getBounds(graphics: GraphicsObject): Bounds {\n const points: Point[] = [\n ...(graphics.points || []),\n ...(graphics.lines || []).flatMap((line) => line.points),\n ...(graphics.rects || []).flatMap((rect) => {\n const halfWidth = rect.width / 2\n const halfHeight = rect.height / 2\n return [\n { x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y + halfHeight },\n ]\n }),\n ...(graphics.circles || []).flatMap((circle) => [\n { x: circle.center.x - circle.radius, y: circle.center.y }, // left\n { x: circle.center.x + circle.radius, y: circle.center.y }, // right\n { x: circle.center.x, y: circle.center.y - circle.radius }, // top\n { x: circle.center.x, y: circle.center.y + circle.radius }, // bottom\n ]),\n ]\n\n if (points.length === 0) {\n return { minX: -1, maxX: 1, minY: -1, maxY: 1 }\n }\n\n return points.reduce(\n (bounds, point) => ({\n minX: Math.min(bounds.minX, point.x),\n maxX: Math.max(bounds.maxX, point.x),\n minY: Math.min(bounds.minY, point.y),\n maxY: Math.max(bounds.maxY, point.y),\n }),\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity },\n )\n}\n\nfunction getProjectionMatrix(\n bounds: Bounds,\n coordinateSystem: GraphicsObject[\"coordinateSystem\"],\n) {\n const width = bounds.maxX - bounds.minX\n const height = bounds.maxY - bounds.minY\n\n const scale_factor = Math.min(\n (DEFAULT_SVG_SIZE - 2 * PADDING) / width,\n (DEFAULT_SVG_SIZE - 2 * PADDING) / height,\n )\n\n const yFlip = coordinateSystem === \"screen\" ? 1 : -1\n\n return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(scale_factor, yFlip * scale_factor),\n translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2)),\n )\n}\n\nfunction projectPoint(point: Point, matrix: Matrix) {\n const projected = applyToPoint(matrix, { x: point.x, y: point.y })\n return { ...point, ...projected }\n}\n\nexport function getSvgFromGraphicsObject(\n graphics: GraphicsObject,\n {\n includeTextLabels = false,\n backgroundColor,\n }: {\n includeTextLabels?: boolean\n backgroundColor?: string\n } = {},\n): string {\n const bounds = getBounds(graphics)\n const matrix = getProjectionMatrix(bounds, graphics.coordinateSystem)\n\n const svgObject = {\n name: \"svg\",\n type: \"element\",\n attributes: {\n width: DEFAULT_SVG_SIZE.toString(),\n height: DEFAULT_SVG_SIZE.toString(),\n viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,\n xmlns: \"http://www.w3.org/2000/svg\",\n },\n children: [\n // Background rectangle (optional)\n ...(backgroundColor\n ? [\n {\n name: \"rect\",\n type: \"element\",\n attributes: {\n width: \"100%\",\n height: \"100%\",\n fill: backgroundColor,\n },\n },\n ]\n : []),\n // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n attributes: {},\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n \"data-type\": \"point\",\n \"data-label\": point.label || \"\",\n \"data-x\": point.x.toString(),\n \"data-y\": point.y.toString(),\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(includeTextLabels && point.label\n ? [\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n x: (projected.x + 5).toString(),\n y: (projected.y - 5).toString(),\n \"font-family\": \"sans-serif\",\n \"font-size\": \"12\",\n },\n children: [{ type: \"text\", value: point.label }],\n },\n ]\n : []),\n ],\n }\n }),\n // Lines\n ...(graphics.lines || []).map((line) => ({\n name: \"polyline\",\n type: \"element\",\n attributes: {\n \"data-points\": line.points.map((p) => `${p.x},${p.y}`).join(\" \"),\n \"data-type\": \"line\",\n points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: line.strokeColor || \"black\",\n \"stroke-width\": (line.strokeWidth\n ? line.strokeWidth * matrix.a\n : 1\n ).toString(),\n ...(line.strokeDash && {\n \"stroke-dasharray\": Array.isArray(line.strokeDash)\n ? line.strokeDash.join(\" \")\n : line.strokeDash,\n }),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const corner1 = {\n x: rect.center.x - rect.width / 2,\n y: rect.center.y - rect.height / 2,\n }\n const projectedCorner1 = projectPoint(corner1, matrix)\n const corner2 = {\n x: rect.center.x + rect.width / 2,\n y: rect.center.y + rect.height / 2,\n }\n const projectedCorner2 = projectPoint(corner2, matrix)\n const scaledWidth = Math.abs(projectedCorner2.x - projectedCorner1.x)\n const scaledHeight = Math.abs(projectedCorner2.y - projectedCorner1.y)\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n \"data-type\": \"rect\",\n \"data-label\": \"\",\n \"data-x\": rect.center.x.toString(),\n \"data-y\": rect.center.y.toString(),\n x: Math.min(projectedCorner1.x, projectedCorner2.x).toString(),\n y: Math.min(projectedCorner1.y, projectedCorner2.y).toString(),\n width: scaledWidth.toString(),\n height: scaledHeight.toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\n \"stroke-width\": Math.abs(1 / matrix.a).toString(),\n },\n }\n }),\n // Circles\n ...(graphics.circles || []).map((circle) => {\n const projected = projectPoint(circle.center, matrix)\n const scaledRadius = circle.radius * Math.abs(matrix.a)\n return {\n name: \"circle\",\n type: \"element\",\n attributes: {\n \"data-type\": \"circle\",\n \"data-label\": \"\",\n \"data-x\": circle.center.x.toString(),\n \"data-y\": circle.center.y.toString(),\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\n \"stroke-width\": Math.abs(1 / matrix.a).toString(),\n },\n }\n }),\n // Crosshair lines and coordinates (initially hidden)\n {\n name: \"g\",\n type: \"element\",\n attributes: {\n id: \"crosshair\",\n style: \"display: none\",\n },\n children: [\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-h\",\n y1: \"0\",\n y2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-v\",\n x1: \"0\",\n x2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n id: \"coordinates\",\n \"font-family\": \"monospace\",\n \"font-size\": \"12\",\n fill: \"#666\",\n },\n children: [{ type: \"text\", value: \"\" }],\n },\n ],\n },\n // Mouse tracking script\n {\n name: \"script\",\n type: \"element\",\n children: [\n {\n type: \"text\",\n value: `\n document.currentScript.parentElement.addEventListener('mousemove', (e) => {\n const svg = e.currentTarget;\n const rect = svg.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const crosshair = svg.getElementById('crosshair');\n const h = svg.getElementById('crosshair-h');\n const v = svg.getElementById('crosshair-v');\n const coords = svg.getElementById('coordinates');\n \n crosshair.style.display = 'block';\n h.setAttribute('x1', '0');\n h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');\n h.setAttribute('y1', y);\n h.setAttribute('y2', y);\n v.setAttribute('x1', x);\n v.setAttribute('x2', x);\n v.setAttribute('y1', '0');\n v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');\n\n // Calculate real coordinates using inverse transformation\n const matrix = ${JSON.stringify(matrix)};\n // Manually invert and apply the affine transform\n // Since we only use translate and scale, we can directly compute:\n // x' = (x - tx) / sx\n // y' = (y - ty) / sy\n const sx = matrix.a;\n const sy = matrix.d;\n const tx = matrix.e; \n const ty = matrix.f;\n const realPoint = {\n x: (x - tx) / sx,\n y: (y - ty) / sy // Flip y back since we used negative scale\n }\n \n coords.textContent = \\`(\\${realPoint.x.toFixed(2)}, \\${realPoint.y.toFixed(2)})\\`;\n coords.setAttribute('x', (x + 5).toString());\n coords.setAttribute('y', (y - 5).toString());\n });\n document.currentScript.parentElement.addEventListener('mouseleave', () => {\n document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';\n });\n `,\n },\n ],\n },\n ],\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n return pretty(stringify(svgObject as any))\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-NG6H63SM.js";
|
|
4
4
|
import {
|
|
5
5
|
getSvgFromGraphicsObject
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-G7CTE4MX.js";
|
|
7
7
|
|
|
8
8
|
// lib/index.ts
|
|
9
9
|
function getSvgFromLogString(logString) {
|
|
@@ -53,4 +53,4 @@ export {
|
|
|
53
53
|
getHtmlFromLogString,
|
|
54
54
|
getSvgsFromLogString
|
|
55
55
|
};
|
|
56
|
-
//# sourceMappingURL=chunk-
|
|
56
|
+
//# sourceMappingURL=chunk-RBTAO2QZ.js.map
|
package/dist/cli/cli.js
CHANGED
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getHtmlFromLogString,
|
|
4
4
|
getSvgsFromLogString
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-RBTAO2QZ.js";
|
|
6
6
|
import "../chunk-G3XJ25G4.js";
|
|
7
7
|
import {
|
|
8
8
|
getGraphicsObjectsFromLogString
|
|
9
9
|
} from "../chunk-NG6H63SM.js";
|
|
10
|
-
import "../chunk-
|
|
10
|
+
import "../chunk-G7CTE4MX.js";
|
|
11
11
|
|
|
12
12
|
// cli/cli.ts
|
|
13
13
|
import { parseArgs } from "node:util";
|
package/dist/lib/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
getHtmlFromLogString,
|
|
3
3
|
getSvgFromLogString,
|
|
4
4
|
getSvgsFromLogString
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-RBTAO2QZ.js";
|
|
6
6
|
import {
|
|
7
7
|
computeTransformFromViewbox,
|
|
8
8
|
drawGraphicsToCanvas,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
} from "../chunk-NG6H63SM.js";
|
|
14
14
|
import {
|
|
15
15
|
getSvgFromGraphicsObject
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-G7CTE4MX.js";
|
|
17
17
|
export {
|
|
18
18
|
computeTransformFromViewbox,
|
|
19
19
|
drawGraphicsToCanvas,
|
package/dist/lib/matcher.d.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
declare module "bun:test" {
|
|
2
2
|
interface Matchers<T = unknown> {
|
|
3
|
-
toMatchGraphicsSvg(testPath: string,
|
|
3
|
+
toMatchGraphicsSvg(testPath: string, opts?: {
|
|
4
|
+
backgroundColor?: string;
|
|
5
|
+
svgName?: string;
|
|
6
|
+
}): Promise<MatcherResult>;
|
|
4
7
|
}
|
|
5
8
|
}
|
package/dist/lib/matcher.js
CHANGED
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getSvgFromGraphicsObject
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-G7CTE4MX.js";
|
|
4
4
|
|
|
5
5
|
// lib/matcher.ts
|
|
6
6
|
import { expect } from "bun:test";
|
|
7
7
|
import * as fs from "node:fs";
|
|
8
8
|
import * as path from "node:path";
|
|
9
9
|
import looksSame from "looks-same";
|
|
10
|
-
async function toMatchGraphicsSvg(receivedMaybePromise, testPathOriginal,
|
|
10
|
+
async function toMatchGraphicsSvg(receivedMaybePromise, testPathOriginal, opts = {}) {
|
|
11
11
|
const received = await receivedMaybePromise;
|
|
12
12
|
const testPath = testPathOriginal.replace(/\.test\.tsx?$/, "");
|
|
13
13
|
const snapshotDir = path.join(path.dirname(testPath), "__snapshots__");
|
|
14
|
-
const snapshotName = svgName ? `${svgName}.snap.svg` : `${path.basename(testPath)}.snap.svg`;
|
|
14
|
+
const snapshotName = opts.svgName ? `${opts.svgName}.snap.svg` : `${path.basename(testPath)}.snap.svg`;
|
|
15
15
|
const filePath = path.join(snapshotDir, snapshotName);
|
|
16
16
|
if (!fs.existsSync(snapshotDir)) {
|
|
17
17
|
fs.mkdirSync(snapshotDir, { recursive: true });
|
|
18
18
|
}
|
|
19
|
-
const receivedSvg = getSvgFromGraphicsObject(received
|
|
19
|
+
const receivedSvg = getSvgFromGraphicsObject(received, {
|
|
20
|
+
backgroundColor: opts.backgroundColor
|
|
21
|
+
});
|
|
20
22
|
const updateSnapshot = process.argv.includes("--update-snapshots") || process.argv.includes("-u") || Boolean(process.env["BUN_UPDATE_SNAPSHOTS"]);
|
|
21
23
|
if (!fs.existsSync(filePath) || updateSnapshot) {
|
|
22
24
|
console.log("Writing snapshot to", filePath);
|
package/dist/lib/matcher.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../lib/matcher.ts"],"sourcesContent":["import { expect, type MatcherResult } from \"bun:test\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport looksSame from \"looks-same\"\nimport { GraphicsObject } from \"./types\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\n/**\n * Custom matcher for Bun tests to compare GraphicsObjects as SVGs\n *\n * @param this Matcher context\n * @param receivedMaybePromise GraphicsObject or Promise<GraphicsObject> to test\n * @param testPathOriginal Path to the test file\n * @param svgName Optional name for the snapshot\n * @returns Matcher result\n */\nasync function toMatchGraphicsSvg(\n // biome-ignore lint/suspicious/noExplicitAny: bun doesn't expose matcher type\n this: any,\n receivedMaybePromise: GraphicsObject | Promise<GraphicsObject>,\n testPathOriginal: string,\n svgName?: string,\n): Promise<MatcherResult> {\n const received = await receivedMaybePromise\n const testPath = testPathOriginal.replace(/\\.test\\.tsx?$/, \"\")\n const snapshotDir = path.join(path.dirname(testPath), \"__snapshots__\")\n const snapshotName = svgName\n ? `${svgName}.snap.svg`\n : `${path.basename(testPath)}.snap.svg`\n const filePath = path.join(snapshotDir, snapshotName)\n\n if (!fs.existsSync(snapshotDir)) {\n fs.mkdirSync(snapshotDir, { recursive: true })\n }\n\n // Convert GraphicsObject to SVG\n const receivedSvg = getSvgFromGraphicsObject(received)\n\n const updateSnapshot =\n process.argv.includes(\"--update-snapshots\") ||\n process.argv.includes(\"-u\") ||\n Boolean(process.env[\"BUN_UPDATE_SNAPSHOTS\"])\n\n if (!fs.existsSync(filePath) || updateSnapshot) {\n console.log(\"Writing snapshot to\", filePath)\n fs.writeFileSync(filePath, receivedSvg)\n return {\n message: () => `Snapshot created at ${filePath}`,\n pass: true,\n }\n }\n\n const existingSnapshot = fs.readFileSync(filePath, \"utf-8\")\n\n const result: any = await looksSame(\n Buffer.from(receivedSvg),\n Buffer.from(existingSnapshot),\n {\n strict: false,\n tolerance: 2,\n },\n )\n\n if (result.equal) {\n return {\n message: () => \"Snapshot matches\",\n pass: true,\n }\n }\n\n const diffPath = filePath.replace(\".snap.svg\", \".diff.png\")\n await looksSame.createDiff({\n reference: Buffer.from(existingSnapshot),\n current: Buffer.from(receivedSvg),\n diff: diffPath,\n highlightColor: \"#ff00ff\",\n })\n\n return {\n message: () => `Snapshot does not match. Diff saved at ${diffPath}`,\n pass: false,\n }\n}\n\n// Add the custom matcher to the expect object\nexpect.extend({\n // biome-ignore lint/suspicious/noExplicitAny: bun's types don't expose matcher type\n toMatchGraphicsSvg: toMatchGraphicsSvg as any,\n})\n\n// Extend the TypeScript interface for the matcher\ndeclare module \"bun:test\" {\n interface Matchers<T = unknown> {\n toMatchGraphicsSvg(\n testPath: string,\n svgName?: string,\n ): Promise<MatcherResult>\n }\n}\n"],"mappings":";;;;;AAAA,SAAS,cAAkC;AAC3C,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,OAAO,eAAe;AAatB,eAAe,mBAGb,sBACA,kBACA,
|
|
1
|
+
{"version":3,"sources":["../../lib/matcher.ts"],"sourcesContent":["import { expect, type MatcherResult } from \"bun:test\"\nimport * as fs from \"node:fs\"\nimport * as path from \"node:path\"\nimport looksSame from \"looks-same\"\nimport { GraphicsObject } from \"./types\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\n/**\n * Custom matcher for Bun tests to compare GraphicsObjects as SVGs\n *\n * @param this Matcher context\n * @param receivedMaybePromise GraphicsObject or Promise<GraphicsObject> to test\n * @param testPathOriginal Path to the test file\n * @param svgName Optional name for the snapshot\n * @returns Matcher result\n */\nasync function toMatchGraphicsSvg(\n // biome-ignore lint/suspicious/noExplicitAny: bun doesn't expose matcher type\n this: any,\n receivedMaybePromise: GraphicsObject | Promise<GraphicsObject>,\n testPathOriginal: string,\n opts: { backgroundColor?: string; svgName?: string } = {},\n): Promise<MatcherResult> {\n const received = await receivedMaybePromise\n const testPath = testPathOriginal.replace(/\\.test\\.tsx?$/, \"\")\n const snapshotDir = path.join(path.dirname(testPath), \"__snapshots__\")\n const snapshotName = opts.svgName\n ? `${opts.svgName}.snap.svg`\n : `${path.basename(testPath)}.snap.svg`\n const filePath = path.join(snapshotDir, snapshotName)\n\n if (!fs.existsSync(snapshotDir)) {\n fs.mkdirSync(snapshotDir, { recursive: true })\n }\n\n // Convert GraphicsObject to SVG\n const receivedSvg = getSvgFromGraphicsObject(received, {\n backgroundColor: opts.backgroundColor,\n })\n\n const updateSnapshot =\n process.argv.includes(\"--update-snapshots\") ||\n process.argv.includes(\"-u\") ||\n Boolean(process.env[\"BUN_UPDATE_SNAPSHOTS\"])\n\n if (!fs.existsSync(filePath) || updateSnapshot) {\n console.log(\"Writing snapshot to\", filePath)\n fs.writeFileSync(filePath, receivedSvg)\n return {\n message: () => `Snapshot created at ${filePath}`,\n pass: true,\n }\n }\n\n const existingSnapshot = fs.readFileSync(filePath, \"utf-8\")\n\n const result: any = await looksSame(\n Buffer.from(receivedSvg),\n Buffer.from(existingSnapshot),\n {\n strict: false,\n tolerance: 2,\n },\n )\n\n if (result.equal) {\n return {\n message: () => \"Snapshot matches\",\n pass: true,\n }\n }\n\n const diffPath = filePath.replace(\".snap.svg\", \".diff.png\")\n await looksSame.createDiff({\n reference: Buffer.from(existingSnapshot),\n current: Buffer.from(receivedSvg),\n diff: diffPath,\n highlightColor: \"#ff00ff\",\n })\n\n return {\n message: () => `Snapshot does not match. Diff saved at ${diffPath}`,\n pass: false,\n }\n}\n\n// Add the custom matcher to the expect object\nexpect.extend({\n // biome-ignore lint/suspicious/noExplicitAny: bun's types don't expose matcher type\n toMatchGraphicsSvg: toMatchGraphicsSvg as any,\n})\n\n// Extend the TypeScript interface for the matcher\ndeclare module \"bun:test\" {\n interface Matchers<T = unknown> {\n toMatchGraphicsSvg(\n testPath: string,\n opts?: { backgroundColor?: string; svgName?: string },\n ): Promise<MatcherResult>\n }\n}\n"],"mappings":";;;;;AAAA,SAAS,cAAkC;AAC3C,YAAY,QAAQ;AACpB,YAAY,UAAU;AACtB,OAAO,eAAe;AAatB,eAAe,mBAGb,sBACA,kBACA,OAAuD,CAAC,GAChC;AACxB,QAAM,WAAW,MAAM;AACvB,QAAM,WAAW,iBAAiB,QAAQ,iBAAiB,EAAE;AAC7D,QAAM,cAAmB,UAAU,aAAQ,QAAQ,GAAG,eAAe;AACrE,QAAM,eAAe,KAAK,UACtB,GAAG,KAAK,OAAO,cACf,GAAQ,cAAS,QAAQ,CAAC;AAC9B,QAAM,WAAgB,UAAK,aAAa,YAAY;AAEpD,MAAI,CAAI,cAAW,WAAW,GAAG;AAC/B,IAAG,aAAU,aAAa,EAAE,WAAW,KAAK,CAAC;AAAA,EAC/C;AAGA,QAAM,cAAc,yBAAyB,UAAU;AAAA,IACrD,iBAAiB,KAAK;AAAA,EACxB,CAAC;AAED,QAAM,iBACJ,QAAQ,KAAK,SAAS,oBAAoB,KAC1C,QAAQ,KAAK,SAAS,IAAI,KAC1B,QAAQ,QAAQ,IAAI,sBAAsB,CAAC;AAE7C,MAAI,CAAI,cAAW,QAAQ,KAAK,gBAAgB;AAC9C,YAAQ,IAAI,uBAAuB,QAAQ;AAC3C,IAAG,iBAAc,UAAU,WAAW;AACtC,WAAO;AAAA,MACL,SAAS,MAAM,uBAAuB,QAAQ;AAAA,MAC9C,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,mBAAsB,gBAAa,UAAU,OAAO;AAE1D,QAAM,SAAc,MAAM;AAAA,IACxB,OAAO,KAAK,WAAW;AAAA,IACvB,OAAO,KAAK,gBAAgB;AAAA,IAC5B;AAAA,MACE,QAAQ;AAAA,MACR,WAAW;AAAA,IACb;AAAA,EACF;AAEA,MAAI,OAAO,OAAO;AAChB,WAAO;AAAA,MACL,SAAS,MAAM;AAAA,MACf,MAAM;AAAA,IACR;AAAA,EACF;AAEA,QAAM,WAAW,SAAS,QAAQ,aAAa,WAAW;AAC1D,QAAM,UAAU,WAAW;AAAA,IACzB,WAAW,OAAO,KAAK,gBAAgB;AAAA,IACvC,SAAS,OAAO,KAAK,WAAW;AAAA,IAChC,MAAM;AAAA,IACN,gBAAgB;AAAA,EAClB,CAAC;AAED,SAAO;AAAA,IACL,SAAS,MAAM,0CAA0C,QAAQ;AAAA,IACjE,MAAM;AAAA,EACR;AACF;AAGA,OAAO,OAAO;AAAA;AAAA,EAEZ;AACF,CAAC;","names":[]}
|
package/dist/lib/react.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import "../chunk-
|
|
1
|
+
import "../chunk-RBTAO2QZ.js";
|
|
2
2
|
import {
|
|
3
3
|
defaultColors,
|
|
4
4
|
drawGraphicsToCanvas,
|
|
5
5
|
getBounds
|
|
6
6
|
} from "../chunk-G3XJ25G4.js";
|
|
7
7
|
import "../chunk-NG6H63SM.js";
|
|
8
|
-
import "../chunk-
|
|
8
|
+
import "../chunk-G7CTE4MX.js";
|
|
9
9
|
|
|
10
10
|
// site/components/InteractiveGraphics/InteractiveGraphics.tsx
|
|
11
11
|
import {
|
package/package.json
CHANGED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/getSvgFromGraphicsObject.ts"],"sourcesContent":["import {\n transform,\n compose,\n translate,\n scale,\n applyToPoint,\n identity,\n type Matrix,\n} from \"transformation-matrix\"\nimport type { GraphicsObject, Point } from \"./types\"\nimport { stringify } from \"svgson\"\nimport pretty from \"pretty\"\n\nconst DEFAULT_SVG_SIZE = 640\nconst PADDING = 40\n\ninterface Bounds {\n minX: number\n maxX: number\n minY: number\n maxY: number\n}\n\nfunction getBounds(graphics: GraphicsObject): Bounds {\n const points: Point[] = [\n ...(graphics.points || []),\n ...(graphics.lines || []).flatMap((line) => line.points),\n ...(graphics.rects || []).flatMap((rect) => {\n const halfWidth = rect.width / 2\n const halfHeight = rect.height / 2\n return [\n { x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },\n { x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },\n { x: rect.center.x + halfWidth, y: rect.center.y + halfHeight },\n ]\n }),\n ...(graphics.circles || []).flatMap((circle) => [\n { x: circle.center.x - circle.radius, y: circle.center.y }, // left\n { x: circle.center.x + circle.radius, y: circle.center.y }, // right\n { x: circle.center.x, y: circle.center.y - circle.radius }, // top\n { x: circle.center.x, y: circle.center.y + circle.radius }, // bottom\n ]),\n ]\n\n if (points.length === 0) {\n return { minX: -1, maxX: 1, minY: -1, maxY: 1 }\n }\n\n return points.reduce(\n (bounds, point) => ({\n minX: Math.min(bounds.minX, point.x),\n maxX: Math.max(bounds.maxX, point.x),\n minY: Math.min(bounds.minY, point.y),\n maxY: Math.max(bounds.maxY, point.y),\n }),\n { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity },\n )\n}\n\nfunction getProjectionMatrix(\n bounds: Bounds,\n coordinateSystem: GraphicsObject[\"coordinateSystem\"],\n) {\n const width = bounds.maxX - bounds.minX\n const height = bounds.maxY - bounds.minY\n\n const scale_factor = Math.min(\n (DEFAULT_SVG_SIZE - 2 * PADDING) / width,\n (DEFAULT_SVG_SIZE - 2 * PADDING) / height,\n )\n\n const yFlip = coordinateSystem === \"screen\" ? 1 : -1\n\n return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(scale_factor, yFlip * scale_factor),\n translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2)),\n )\n}\n\nfunction projectPoint(point: Point, matrix: Matrix) {\n const projected = applyToPoint(matrix, { x: point.x, y: point.y })\n return { ...point, ...projected }\n}\n\nexport function getSvgFromGraphicsObject(\n graphics: GraphicsObject,\n {\n includeTextLabels = false,\n backgroundColor,\n }: {\n includeTextLabels?: boolean\n backgroundColor?: string\n } = {},\n): string {\n const bounds = getBounds(graphics)\n const matrix = getProjectionMatrix(bounds, graphics.coordinateSystem)\n\n const svgObject = {\n name: \"svg\",\n type: \"element\",\n attributes: {\n width: DEFAULT_SVG_SIZE.toString(),\n height: DEFAULT_SVG_SIZE.toString(),\n viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,\n xmlns: \"http://www.w3.org/2000/svg\",\n },\n children: [\n // Background rectangle (optional)\n ...(backgroundColor\n ? [\n {\n name: \"rect\",\n type: \"element\",\n attributes: {\n width: \"100%\",\n height: \"100%\",\n fill: backgroundColor,\n },\n },\n ]\n : []),\n // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n attributes: {},\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n \"data-type\": \"point\",\n \"data-label\": point.label || \"\",\n \"data-x\": point.x.toString(),\n \"data-y\": point.y.toString(),\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(includeTextLabels && point.label\n ? [\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n x: (projected.x + 5).toString(),\n y: (projected.y - 5).toString(),\n \"font-family\": \"sans-serif\",\n \"font-size\": \"12\",\n },\n children: [{ type: \"text\", value: point.label }],\n },\n ]\n : []),\n ],\n }\n }),\n // Lines\n ...(graphics.lines || []).map((line) => ({\n name: \"polyline\",\n type: \"element\",\n attributes: {\n \"data-points\": line.points.map((p) => `${p.x},${p.y}`).join(\" \"),\n \"data-type\": \"line\",\n points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: line.strokeColor || \"black\",\n \"stroke-width\": (line.strokeWidth\n ? line.strokeWidth * matrix.a\n : 1\n ).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const corner1 = {\n x: rect.center.x - rect.width / 2,\n y: rect.center.y - rect.height / 2,\n }\n const projectedCorner1 = projectPoint(corner1, matrix)\n const corner2 = {\n x: rect.center.x + rect.width / 2,\n y: rect.center.y + rect.height / 2,\n }\n const projectedCorner2 = projectPoint(corner2, matrix)\n const scaledWidth = Math.abs(projectedCorner2.x - projectedCorner1.x)\n const scaledHeight = Math.abs(projectedCorner2.y - projectedCorner1.y)\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n \"data-type\": \"rect\",\n \"data-label\": \"\",\n \"data-x\": rect.center.x.toString(),\n \"data-y\": rect.center.y.toString(),\n x: Math.min(projectedCorner1.x, projectedCorner2.x).toString(),\n y: Math.min(projectedCorner1.y, projectedCorner2.y).toString(),\n width: scaledWidth.toString(),\n height: scaledHeight.toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\n \"stroke-width\": Math.abs(1 / matrix.a).toString(),\n },\n }\n }),\n // Circles\n ...(graphics.circles || []).map((circle) => {\n const projected = projectPoint(circle.center, matrix)\n const scaledRadius = circle.radius * Math.abs(matrix.a)\n return {\n name: \"circle\",\n type: \"element\",\n attributes: {\n \"data-type\": \"circle\",\n \"data-label\": \"\",\n \"data-x\": circle.center.x.toString(),\n \"data-y\": circle.center.y.toString(),\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\n \"stroke-width\": Math.abs(1 / matrix.a).toString(),\n },\n }\n }),\n // Crosshair lines and coordinates (initially hidden)\n {\n name: \"g\",\n type: \"element\",\n attributes: {\n id: \"crosshair\",\n style: \"display: none\",\n },\n children: [\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-h\",\n y1: \"0\",\n y2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"line\",\n type: \"element\",\n attributes: {\n id: \"crosshair-v\",\n x1: \"0\",\n x2: DEFAULT_SVG_SIZE.toString(),\n stroke: \"#666\",\n \"stroke-width\": \"0.5\",\n },\n },\n {\n name: \"text\",\n type: \"element\",\n attributes: {\n id: \"coordinates\",\n \"font-family\": \"monospace\",\n \"font-size\": \"12\",\n fill: \"#666\",\n },\n children: [{ type: \"text\", value: \"\" }],\n },\n ],\n },\n // Mouse tracking script\n {\n name: \"script\",\n type: \"element\",\n children: [\n {\n type: \"text\",\n value: `\n document.currentScript.parentElement.addEventListener('mousemove', (e) => {\n const svg = e.currentTarget;\n const rect = svg.getBoundingClientRect();\n const x = e.clientX - rect.left;\n const y = e.clientY - rect.top;\n const crosshair = svg.getElementById('crosshair');\n const h = svg.getElementById('crosshair-h');\n const v = svg.getElementById('crosshair-v');\n const coords = svg.getElementById('coordinates');\n \n crosshair.style.display = 'block';\n h.setAttribute('x1', '0');\n h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');\n h.setAttribute('y1', y);\n h.setAttribute('y2', y);\n v.setAttribute('x1', x);\n v.setAttribute('x2', x);\n v.setAttribute('y1', '0');\n v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');\n\n // Calculate real coordinates using inverse transformation\n const matrix = ${JSON.stringify(matrix)};\n // Manually invert and apply the affine transform\n // Since we only use translate and scale, we can directly compute:\n // x' = (x - tx) / sx\n // y' = (y - ty) / sy\n const sx = matrix.a;\n const sy = matrix.d;\n const tx = matrix.e; \n const ty = matrix.f;\n const realPoint = {\n x: (x - tx) / sx,\n y: (y - ty) / sy // Flip y back since we used negative scale\n }\n \n coords.textContent = \\`(\\${realPoint.x.toFixed(2)}, \\${realPoint.y.toFixed(2)})\\`;\n coords.setAttribute('x', (x + 5).toString());\n coords.setAttribute('y', (y - 5).toString());\n });\n document.currentScript.parentElement.addEventListener('mouseleave', () => {\n document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';\n });\n `,\n },\n ],\n },\n ],\n }\n\n // biome-ignore lint/suspicious/noExplicitAny: TODO\n return pretty(stringify(svgObject as any))\n}\n"],"mappings":"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","names":[]}
|
|
File without changes
|