graphics-debug 0.0.1 → 0.0.3
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 +56 -37
- package/dist/{chunk-GDKEOGZB.js → chunk-2YVICPCE.js} +4 -4
- package/dist/{chunk-GDKEOGZB.js.map → chunk-2YVICPCE.js.map} +1 -1
- package/dist/{chunk-646ISP7J.js → chunk-NG6H63SM.js} +14 -5
- package/dist/chunk-NG6H63SM.js.map +1 -0
- package/dist/{chunk-G7UBPMLZ.js → chunk-X2DSMR32.js} +17 -3
- package/dist/chunk-X2DSMR32.js.map +1 -0
- package/dist/cli/cli.d.ts +1 -0
- package/dist/cli/cli.js +15 -6
- package/dist/cli/cli.js.map +1 -1
- package/dist/lib/getGraphicsObjectsFromLogString.d.ts +9 -0
- package/dist/lib/getGraphicsObjectsFromLogString.js +1 -1
- package/dist/lib/getSvgFromGraphicsObject.d.ts +5 -0
- package/dist/lib/getSvgFromGraphicsObject.js +1 -1
- package/dist/lib/index.d.ts +12 -0
- package/dist/lib/index.js +3 -3
- package/dist/lib/types.d.ts +43 -0
- package/package.json +16 -5
- package/dist/chunk-646ISP7J.js.map +0 -1
- package/dist/chunk-G7UBPMLZ.js.map +0 -1
- package/dist/cli.js +0 -386
- package/dist/cli.js.map +0 -1
package/README.md
CHANGED
|
@@ -6,11 +6,30 @@ Just pipe in output with graphics JSON objects into `graphics-debug` (or `gd`) t
|
|
|
6
6
|
with all your graphics drawn-in.
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
echo '
|
|
9
|
+
echo ':graphics { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" } }' | graphics-debug
|
|
10
10
|
# wrote to "test-graphic-1.debug.svg"
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
```bash
|
|
14
|
+
# Run a program that has debug logs and pipe the output to graphics-debug
|
|
15
|
+
bun test path/to/test.ts |& graphics-debug --html
|
|
16
|
+
# wrote to "graphics.debug.html"
|
|
17
|
+
|
|
18
|
+
# another syntax for the same thing
|
|
19
|
+
bun test path/to/test.ts 2>&1 | graphics-debug --html
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
Don't want to write files everywhere? Use the `--url` flag to get a url to view
|
|
23
|
+
the graphics in a browser.
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
node myscript.js |& graphics-debug --url
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
Don't have access to the cli? Paste into the online version: https://graphicsdebug.com
|
|
30
|
+
|
|
31
|
+
https://github.com/user-attachments/assets/9f3f41e6-b0fe-416a-a551-ba5c5b920cad
|
|
32
|
+
|
|
14
33
|
|
|
15
34
|
## Format
|
|
16
35
|
|
|
@@ -18,41 +37,37 @@ The `graphics` json object is very simple, here's the basic schema:
|
|
|
18
37
|
|
|
19
38
|
```typescript
|
|
20
39
|
interface GraphicsObject {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
title?: string
|
|
40
|
-
}
|
|
40
|
+
points?: { x: number; y: number; color?: string; label?: string }[]
|
|
41
|
+
lines?: { points: { x: number; y: number; stroke?: number }[] }[]
|
|
42
|
+
rects?: Array<{
|
|
43
|
+
center: { x: number; y: number }
|
|
44
|
+
width: number
|
|
45
|
+
height: number
|
|
46
|
+
fill?: string
|
|
47
|
+
stroke?: string
|
|
48
|
+
}>
|
|
49
|
+
circles?: Array<{
|
|
50
|
+
center: { x: number; y: number }
|
|
51
|
+
radius: number
|
|
52
|
+
fill?: string
|
|
53
|
+
stroke?: string
|
|
54
|
+
}>
|
|
55
|
+
grid?: { cellSize: number; label?: boolean }
|
|
56
|
+
coordinateSystem?: "cartesian" | "screen"
|
|
57
|
+
title?: string
|
|
41
58
|
}
|
|
42
59
|
```
|
|
43
60
|
|
|
44
|
-
When emiting a graphics object, keep the `{ graphics }` object on a single line,
|
|
45
|
-
`graphics-debug` won't parse multi-line `{ graphics }` objects. You can have
|
|
46
|
-
other content on same line as the `{ graphics }` object. This means you can't
|
|
47
|
-
use `console.log` to emit graphics objects, use the [debug](https://www.npmjs.com/package/debug)
|
|
48
|
-
library or `console.log(JSON.stringify(...))` instead.
|
|
49
|
-
|
|
50
61
|
## Library Usage
|
|
51
62
|
|
|
52
63
|
### Writing `graphics-debug` compatible logs
|
|
53
64
|
|
|
54
65
|
The best way to write `graphics-debug` compatible logs is to use the [`debug` library](https://www.npmjs.com/package/debug).
|
|
55
66
|
|
|
67
|
+
You should **always JSON.stringify graphics objects**, otherwise the graphics
|
|
68
|
+
will take up more lines and will not have the correct depth (there will be
|
|
69
|
+
missing information)
|
|
70
|
+
|
|
56
71
|
```tsx
|
|
57
72
|
import Debug from "debug"
|
|
58
73
|
|
|
@@ -61,22 +76,22 @@ const debugGraphics = Debug("mypackage:graphics")
|
|
|
61
76
|
const A = { x: 0, y: 0, label: "A" }
|
|
62
77
|
const B = { x: 1, y: 1, label: "B" }
|
|
63
78
|
|
|
64
|
-
debugGraphics(
|
|
65
|
-
|
|
79
|
+
debugGraphics(
|
|
80
|
+
JSON.stringify({
|
|
66
81
|
points: [A, B],
|
|
67
82
|
title: "initial points for my algorithm",
|
|
68
|
-
}
|
|
69
|
-
|
|
83
|
+
})
|
|
84
|
+
)
|
|
70
85
|
|
|
71
86
|
// ... do some algorithm stuff e.g....
|
|
72
87
|
const C = { x: (A.x + B.x) / 2, y: (A.y + B.y) / 2, label: "C" }
|
|
73
88
|
|
|
74
|
-
debugGraphics(
|
|
75
|
-
|
|
89
|
+
debugGraphics(
|
|
90
|
+
JSON.stringify({
|
|
76
91
|
points: [A, B, C],
|
|
77
92
|
title: "final points for my algorithm",
|
|
78
|
-
}
|
|
79
|
-
|
|
93
|
+
})
|
|
94
|
+
)
|
|
80
95
|
```
|
|
81
96
|
|
|
82
97
|
To see the output, you'll need to run `DEBUG=mypackage:graphics` in your terminal
|
|
@@ -96,7 +111,7 @@ import {
|
|
|
96
111
|
|
|
97
112
|
const logString = `
|
|
98
113
|
hello world! This is some other content that will be ignored
|
|
99
|
-
here's some
|
|
114
|
+
here's some :graphics { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" }
|
|
100
115
|
`
|
|
101
116
|
|
|
102
117
|
const svg = getSvgFromLogString(logString)
|
|
@@ -115,3 +130,7 @@ import { getGraphicsObjectsFromLogString } from "graphics-debug"
|
|
|
115
130
|
const graphicsObjects = getGraphicsObjectsFromLogString(logString)
|
|
116
131
|
// Array<GraphicsObject>
|
|
117
132
|
```
|
|
133
|
+
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
getGraphicsObjectsFromLogString
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-NG6H63SM.js";
|
|
4
4
|
import {
|
|
5
5
|
getSvgFromGraphicsObject
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-X2DSMR32.js";
|
|
7
7
|
|
|
8
8
|
// lib/index.ts
|
|
9
9
|
function getSvgFromLogString(logString) {
|
|
@@ -43,7 +43,7 @@ function getHtmlFromLogString(logString) {
|
|
|
43
43
|
function getSvgsFromLogString(logString) {
|
|
44
44
|
const objects = getGraphicsObjectsFromLogString(logString);
|
|
45
45
|
return objects.map((obj) => ({
|
|
46
|
-
title: obj.
|
|
46
|
+
title: obj.title || "Untitled Graphic",
|
|
47
47
|
svg: getSvgFromGraphicsObject(obj)
|
|
48
48
|
}));
|
|
49
49
|
}
|
|
@@ -53,4 +53,4 @@ export {
|
|
|
53
53
|
getHtmlFromLogString,
|
|
54
54
|
getSvgsFromLogString
|
|
55
55
|
};
|
|
56
|
-
//# sourceMappingURL=chunk-
|
|
56
|
+
//# sourceMappingURL=chunk-2YVICPCE.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/index.ts"],"sourcesContent":["import { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport type { Point, Line, Rect, Circle, GraphicsObject } from \"./types\"\nexport { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nexport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport function getSvgFromLogString(logString: string): string {\n const objects = getGraphicsObjectsFromLogString(logString)\n if (objects.length === 0) return \"\"\n return getSvgFromGraphicsObject(objects[0])\n}\n\nexport function getHtmlFromLogString(logString: string): string {\n const svgs = getSvgsFromLogString(logString)\n if (svgs.length === 0) return \"\"\n\n const sections = svgs\n .map(\n ({ title, svg }) => `\n <section>\n <h2>${title}</h2>\n ${svg}\n </section>\n `,\n )\n .join(\"\\n\")\n\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Graphics Debug Output</title>\n <style>\n body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }\n h1 { color: #333; }\n section { margin: 40px 0; }\n svg { max-width: 100%; height: auto; border: 1px solid #eee; }\n </style>\n</head>\n<body>\n <h1>Graphics Debug Output</h1>\n ${sections}\n</body>\n</html>`\n}\n\nexport function getSvgsFromLogString(\n logString: string,\n): Array<{ title: string; svg: string }> {\n const objects = getGraphicsObjectsFromLogString(logString)\n return objects.map((obj) => ({\n title: obj.
|
|
1
|
+
{"version":3,"sources":["../lib/index.ts"],"sourcesContent":["import { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport type { Point, Line, Rect, Circle, GraphicsObject } from \"./types\"\nexport { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nexport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport function getSvgFromLogString(logString: string): string {\n const objects = getGraphicsObjectsFromLogString(logString)\n if (objects.length === 0) return \"\"\n return getSvgFromGraphicsObject(objects[0])\n}\n\nexport function getHtmlFromLogString(logString: string): string {\n const svgs = getSvgsFromLogString(logString)\n if (svgs.length === 0) return \"\"\n\n const sections = svgs\n .map(\n ({ title, svg }) => `\n <section>\n <h2>${title}</h2>\n ${svg}\n </section>\n `,\n )\n .join(\"\\n\")\n\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Graphics Debug Output</title>\n <style>\n body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }\n h1 { color: #333; }\n section { margin: 40px 0; }\n svg { max-width: 100%; height: auto; border: 1px solid #eee; }\n </style>\n</head>\n<body>\n <h1>Graphics Debug Output</h1>\n ${sections}\n</body>\n</html>`\n}\n\nexport function getSvgsFromLogString(\n logString: string,\n): Array<{ title: string; svg: string }> {\n const objects = getGraphicsObjectsFromLogString(logString)\n return objects.map((obj) => ({\n title: obj.title || \"Untitled Graphic\",\n svg: getSvgFromGraphicsObject(obj),\n }))\n}\n"],"mappings":";;;;;;;;AAOO,SAAS,oBAAoB,WAA2B;AAC7D,QAAM,UAAU,gCAAgC,SAAS;AACzD,MAAI,QAAQ,WAAW,EAAG,QAAO;AACjC,SAAO,yBAAyB,QAAQ,CAAC,CAAC;AAC5C;AAEO,SAAS,qBAAqB,WAA2B;AAC9D,QAAM,OAAO,qBAAqB,SAAS;AAC3C,MAAI,KAAK,WAAW,EAAG,QAAO;AAE9B,QAAM,WAAW,KACd;AAAA,IACC,CAAC,EAAE,OAAO,IAAI,MAAM;AAAA;AAAA,YAEd,KAAK;AAAA,QACT,GAAG;AAAA;AAAA;AAAA,EAGP,EACC,KAAK,IAAI;AAEZ,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAcL,QAAQ;AAAA;AAAA;AAGZ;AAEO,SAAS,qBACd,WACuC;AACvC,QAAM,UAAU,gCAAgC,SAAS;AACzD,SAAO,QAAQ,IAAI,CAAC,SAAS;AAAA,IAC3B,OAAO,IAAI,SAAS;AAAA,IACpB,KAAK,yBAAyB,GAAG;AAAA,EACnC,EAAE;AACJ;","names":[]}
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
// lib/getGraphicsObjectsFromLogString.ts
|
|
2
2
|
function getGraphicsObjectsFromLogString(logString) {
|
|
3
3
|
const results = [];
|
|
4
|
-
const graphicsRegex =
|
|
4
|
+
const graphicsRegex = /(?:\{[\s]*(?:"graphics"|graphics)[\s]*:[\s]*\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}[\s]*\})|(?::graphics\s+\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\})/g;
|
|
5
5
|
const matches = logString.match(graphicsRegex);
|
|
6
6
|
if (!matches) return results;
|
|
7
7
|
for (const match of matches) {
|
|
8
8
|
try {
|
|
9
|
-
const
|
|
10
|
-
|
|
9
|
+
const jsonPart = match.includes(":graphics") ? match.substring(match.indexOf("{")) : match;
|
|
10
|
+
const parsed = JSON.parse(jsonPart);
|
|
11
|
+
if (parsed.graphics) {
|
|
12
|
+
results.push(parsed.graphics);
|
|
13
|
+
} else {
|
|
14
|
+
results.push(parsed);
|
|
15
|
+
}
|
|
11
16
|
} catch (e) {
|
|
12
17
|
try {
|
|
13
18
|
const fixed = match.replace(/(\b\w+)(?=\s*:)/g, '"$1"').replace(/,(\s*[}\]])/g, "$1").replace(/:\s*'([^']*)'/g, ':"$1"');
|
|
14
19
|
const parsed = JSON.parse(fixed);
|
|
15
|
-
|
|
20
|
+
if (parsed.graphics) {
|
|
21
|
+
results.push(parsed.graphics);
|
|
22
|
+
} else {
|
|
23
|
+
results.push(parsed);
|
|
24
|
+
}
|
|
16
25
|
} catch (e2) {
|
|
17
26
|
console.warn("Failed to parse graphics object:", match, e2);
|
|
18
27
|
}
|
|
@@ -24,4 +33,4 @@ function getGraphicsObjectsFromLogString(logString) {
|
|
|
24
33
|
export {
|
|
25
34
|
getGraphicsObjectsFromLogString
|
|
26
35
|
};
|
|
27
|
-
//# sourceMappingURL=chunk-
|
|
36
|
+
//# sourceMappingURL=chunk-NG6H63SM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../lib/getGraphicsObjectsFromLogString.ts"],"sourcesContent":["import type { GraphicsObject } from \"./types\"\n\n/**\n * Extracts graphics objects from a debug log string\n * Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)\n */\nexport function getGraphicsObjectsFromLogString(\n logString: string,\n): GraphicsObject[] {\n const results: GraphicsObject[] = []\n\n // Match both {graphics: ...} and :graphics {...} patterns\n const graphicsRegex =\n /(?:\\{[\\s]*(?:\"graphics\"|graphics)[\\s]*:[\\s]*\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}[\\s]*\\})|(?::graphics\\s+\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\})/g\n const matches = logString.match(graphicsRegex)\n\n if (!matches) return results\n\n for (const match of matches) {\n try {\n // First try parsing as regular JSON\n // Extract just the JSON part if it's a debug line\n const jsonPart = match.includes(\":graphics\")\n ? match.substring(match.indexOf(\"{\"))\n : match\n\n const parsed = JSON.parse(jsonPart)\n\n // Wrap non-graphics objects in a graphics wrapper\n if (parsed.graphics) {\n results.push(parsed.graphics)\n } else {\n results.push(parsed)\n }\n } catch (e) {\n try {\n // If that fails, fix JSON formatting issues:\n // 1. Add quotes around unquoted keys\n // 2. Handle potential trailing commas\n const fixed = match\n .replace(/(\\b\\w+)(?=\\s*:)/g, '\"$1\"') // Quote unquoted keys more precisely\n .replace(/,(\\s*[}\\]])/g, \"$1\") // Remove trailing commas\n .replace(/:\\s*'([^']*)'/g, ':\"$1\"') // Convert single quotes to double quotes\n\n const parsed = JSON.parse(fixed)\n if (parsed.graphics) {\n results.push(parsed.graphics)\n } else {\n results.push(parsed)\n }\n } catch (e) {\n // Skip invalid entries but log the error and the problematic match\n console.warn(\"Failed to parse graphics object:\", match, e)\n }\n }\n }\n\n return results\n}\n"],"mappings":";AAMO,SAAS,gCACd,WACkB;AAClB,QAAM,UAA4B,CAAC;AAGnC,QAAM,gBACJ;AACF,QAAM,UAAU,UAAU,MAAM,aAAa;AAE7C,MAAI,CAAC,QAAS,QAAO;AAErB,aAAW,SAAS,SAAS;AAC3B,QAAI;AAGF,YAAM,WAAW,MAAM,SAAS,WAAW,IACvC,MAAM,UAAU,MAAM,QAAQ,GAAG,CAAC,IAClC;AAEJ,YAAM,SAAS,KAAK,MAAM,QAAQ;AAGlC,UAAI,OAAO,UAAU;AACnB,gBAAQ,KAAK,OAAO,QAAQ;AAAA,MAC9B,OAAO;AACL,gBAAQ,KAAK,MAAM;AAAA,MACrB;AAAA,IACF,SAAS,GAAG;AACV,UAAI;AAIF,cAAM,QAAQ,MACX,QAAQ,oBAAoB,MAAM,EAClC,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,kBAAkB,OAAO;AAEpC,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,YAAI,OAAO,UAAU;AACnB,kBAAQ,KAAK,OAAO,QAAQ;AAAA,QAC9B,OAAO;AACL,kBAAQ,KAAK,MAAM;AAAA,QACrB;AAAA,MACF,SAASA,IAAG;AAEV,gBAAQ,KAAK,oCAAoC,OAAOA,EAAC;AAAA,MAC3D;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AACT;","names":["e"]}
|
|
@@ -67,8 +67,7 @@ function projectPoint(point, matrix) {
|
|
|
67
67
|
const projected = applyToPoint(matrix, { x: point.x, y: point.y });
|
|
68
68
|
return { ...point, ...projected };
|
|
69
69
|
}
|
|
70
|
-
function getSvgFromGraphicsObject(
|
|
71
|
-
const { graphics } = graphicsObj;
|
|
70
|
+
function getSvgFromGraphicsObject(graphics) {
|
|
72
71
|
const bounds = getBounds(graphics);
|
|
73
72
|
const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem));
|
|
74
73
|
const svgObject = {
|
|
@@ -87,11 +86,16 @@ function getSvgFromGraphicsObject(graphicsObj) {
|
|
|
87
86
|
return {
|
|
88
87
|
name: "g",
|
|
89
88
|
type: "element",
|
|
89
|
+
attributes: {},
|
|
90
90
|
children: [
|
|
91
91
|
{
|
|
92
92
|
name: "circle",
|
|
93
93
|
type: "element",
|
|
94
94
|
attributes: {
|
|
95
|
+
"data-type": "point",
|
|
96
|
+
"data-label": point.label || "",
|
|
97
|
+
"data-x": point.x.toString(),
|
|
98
|
+
"data-y": point.y.toString(),
|
|
95
99
|
cx: projected.x.toString(),
|
|
96
100
|
cy: projected.y.toString(),
|
|
97
101
|
r: "3",
|
|
@@ -119,6 +123,8 @@ function getSvgFromGraphicsObject(graphicsObj) {
|
|
|
119
123
|
name: "polyline",
|
|
120
124
|
type: "element",
|
|
121
125
|
attributes: {
|
|
126
|
+
"data-points": line.points.map((p) => `${p.x},${p.y}`).join(" "),
|
|
127
|
+
"data-type": "line",
|
|
122
128
|
points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
123
129
|
fill: "none",
|
|
124
130
|
stroke: "black",
|
|
@@ -134,6 +140,10 @@ function getSvgFromGraphicsObject(graphicsObj) {
|
|
|
134
140
|
name: "rect",
|
|
135
141
|
type: "element",
|
|
136
142
|
attributes: {
|
|
143
|
+
"data-type": "rect",
|
|
144
|
+
"data-label": "",
|
|
145
|
+
"data-x": rect.center.x.toString(),
|
|
146
|
+
"data-y": rect.center.y.toString(),
|
|
137
147
|
x: (projected.x - scaledWidth / 2).toString(),
|
|
138
148
|
y: (projected.y - scaledHeight / 2).toString(),
|
|
139
149
|
width: scaledWidth.toString(),
|
|
@@ -151,6 +161,10 @@ function getSvgFromGraphicsObject(graphicsObj) {
|
|
|
151
161
|
name: "circle",
|
|
152
162
|
type: "element",
|
|
153
163
|
attributes: {
|
|
164
|
+
"data-type": "circle",
|
|
165
|
+
"data-label": "",
|
|
166
|
+
"data-x": circle.center.x.toString(),
|
|
167
|
+
"data-y": circle.center.y.toString(),
|
|
154
168
|
cx: projected.x.toString(),
|
|
155
169
|
cy: projected.y.toString(),
|
|
156
170
|
r: scaledRadius.toString(),
|
|
@@ -265,4 +279,4 @@ function getSvgFromGraphicsObject(graphicsObj) {
|
|
|
265
279
|
export {
|
|
266
280
|
getSvgFromGraphicsObject
|
|
267
281
|
};
|
|
268
|
-
//# sourceMappingURL=chunk-
|
|
282
|
+
//# sourceMappingURL=chunk-X2DSMR32.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 return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(\n scale_factor,\n coordinateSystem === \"screen\" ? -scale_factor : scale_factor,\n ),\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(graphics: GraphicsObject): string {\n const bounds = getBounds(graphics)\n const matrix = compose(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 // 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 ...(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: \"black\",\n \"stroke-width\": (line.points[0].stroke || 1).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const projected = projectPoint(rect.center, matrix)\n const scaledWidth = rect.width * matrix.a\n const scaledHeight = rect.height * matrix.d\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: (projected.x - scaledWidth / 2).toString(),\n y: (projected.y - scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: Math.abs(scaledHeight).toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\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 },\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":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
package/dist/cli/cli.js
CHANGED
|
@@ -2,26 +2,28 @@
|
|
|
2
2
|
import {
|
|
3
3
|
getHtmlFromLogString,
|
|
4
4
|
getSvgsFromLogString
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-2YVICPCE.js";
|
|
6
6
|
import {
|
|
7
7
|
getGraphicsObjectsFromLogString
|
|
8
|
-
} from "../chunk-
|
|
9
|
-
import "../chunk-
|
|
8
|
+
} from "../chunk-NG6H63SM.js";
|
|
9
|
+
import "../chunk-X2DSMR32.js";
|
|
10
10
|
|
|
11
11
|
// cli/cli.ts
|
|
12
12
|
import { parseArgs } from "node:util";
|
|
13
13
|
import "node:fs";
|
|
14
14
|
import { writeFileSync } from "node:fs";
|
|
15
15
|
async function getInput() {
|
|
16
|
-
if (process.stdin.isTTY) {
|
|
16
|
+
if (process.stdin.isTTY && process.stderr.isTTY) {
|
|
17
17
|
console.error(
|
|
18
18
|
"Error: No input provided. Pipe in content with graphics objects."
|
|
19
19
|
);
|
|
20
20
|
process.exit(1);
|
|
21
21
|
}
|
|
22
22
|
const chunks = [];
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
if (!process.stdin.isTTY) {
|
|
24
|
+
for await (const chunk of process.stdin) {
|
|
25
|
+
chunks.push(chunk);
|
|
26
|
+
}
|
|
25
27
|
}
|
|
26
28
|
return chunks.join("");
|
|
27
29
|
}
|
|
@@ -54,6 +56,11 @@ Examples:
|
|
|
54
56
|
writeFileSync("graphicsdebug.debug.html", html);
|
|
55
57
|
console.log('Wrote to "graphicsdebug.debug.html"');
|
|
56
58
|
} else if (values.url) {
|
|
59
|
+
const graphicsObjects = getGraphicsObjectsFromLogString(input);
|
|
60
|
+
if (graphicsObjects.length === 0) {
|
|
61
|
+
console.error("No graphics objects found in input");
|
|
62
|
+
process.exit(0);
|
|
63
|
+
}
|
|
57
64
|
const { url } = await fetch("https://gdstore.seve.workers.dev/store", {
|
|
58
65
|
method: "POST",
|
|
59
66
|
body: JSON.stringify({
|
|
@@ -63,6 +70,8 @@ Examples:
|
|
|
63
70
|
"Content-Type": "application/json"
|
|
64
71
|
}
|
|
65
72
|
}).then((res) => res.json());
|
|
73
|
+
const token = url.split("/get/").pop();
|
|
74
|
+
console.log(`https://graphicsdebug.com/t/${token}`);
|
|
66
75
|
} else {
|
|
67
76
|
const svgs = getSvgsFromLogString(input);
|
|
68
77
|
svgs.forEach((svg, i) => {
|
package/dist/cli/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../cli/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { readFileSync } from \"node:fs\"\nimport { writeFileSync } from \"node:fs\"\nimport {\n getSvgsFromLogString,\n getHtmlFromLogString,\n getGraphicsObjectsFromLogString,\n} from \"../lib\"\n\nasync function getInput(): Promise<string> {\n // Check if there's data being piped in\n if (process.stdin.isTTY) {\n console.error(\n \"Error: No input provided. Pipe in content with graphics objects.\",\n )\n process.exit(1)\n }\n\n const chunks = []\n for await (const chunk of process.stdin) {\n
|
|
1
|
+
{"version":3,"sources":["../../cli/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { readFileSync } from \"node:fs\"\nimport { writeFileSync } from \"node:fs\"\nimport {\n getSvgsFromLogString,\n getHtmlFromLogString,\n getGraphicsObjectsFromLogString,\n} from \"../lib\"\n\nasync function getInput(): Promise<string> {\n // Check if there's data being piped in\n if (process.stdin.isTTY && process.stderr.isTTY) {\n console.error(\n \"Error: No input provided. Pipe in content with graphics objects.\",\n )\n process.exit(1)\n }\n\n const chunks = []\n\n // Read from stdin if available\n if (!process.stdin.isTTY) {\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n }\n\n return chunks.join(\"\")\n}\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n html: { type: \"boolean\" },\n url: { type: \"boolean\" },\n help: { type: \"boolean\" },\n },\n })\n\n if (values.help) {\n console.log(`\nUsage: graphics-debug [options]\n\nOptions:\n --html Output a single HTML file with all graphics\n --url Print a url to view the graphics in a browser\n --help Show this help message\n\nExamples:\n cat debug.log | graphics-debug\n echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html\n `)\n process.exit(0)\n }\n\n const input = await getInput()\n\n if (values.html) {\n const html = getHtmlFromLogString(input)\n writeFileSync(\"graphicsdebug.debug.html\", html)\n console.log('Wrote to \"graphicsdebug.debug.html\"')\n } else if (values.url) {\n const graphicsObjects = getGraphicsObjectsFromLogString(input)\n if (graphicsObjects.length === 0) {\n console.error(\"No graphics objects found in input\")\n process.exit(0)\n }\n\n const { url } = await fetch(\"https://gdstore.seve.workers.dev/store\", {\n method: \"POST\",\n body: JSON.stringify({\n graphicsObjects: getGraphicsObjectsFromLogString(input),\n }),\n headers: {\n \"Content-Type\": \"application/json\",\n },\n }).then((res) => res.json())\n\n const token = url.split(\"/get/\").pop()\n\n console.log(`https://graphicsdebug.com/t/${token}`)\n } else {\n const svgs = getSvgsFromLogString(input)\n svgs.forEach((svg, i) => {\n const filename = `${svg.title.toLowerCase().replace(/\\s+/g, \"-\")}-${i + 1}.debug.svg`\n writeFileSync(filename, svg.svg)\n console.log(`Wrote to \"${filename}\"`)\n })\n }\n}\n\nmain().catch((err) => {\n console.error(\"Error:\", err)\n process.exit(1)\n})\n"],"mappings":";;;;;;;;;;;AACA,SAAS,iBAAiB;AAC1B,OAA6B;AAC7B,SAAS,qBAAqB;AAO9B,eAAe,WAA4B;AAEzC,MAAI,QAAQ,MAAM,SAAS,QAAQ,OAAO,OAAO;AAC/C,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,CAAC;AAGhB,MAAI,CAAC,QAAQ,MAAM,OAAO;AACxB,qBAAiB,SAAS,QAAQ,OAAO;AACvC,aAAO,KAAK,KAAK;AAAA,IACnB;AAAA,EACF;AAEA,SAAO,OAAO,KAAK,EAAE;AACvB;AAEA,eAAe,OAAO;AACpB,QAAM,EAAE,OAAO,IAAI,UAAU;AAAA,IAC3B,SAAS;AAAA,MACP,MAAM,EAAE,MAAM,UAAU;AAAA,MACxB,KAAK,EAAE,MAAM,UAAU;AAAA,MACvB,MAAM,EAAE,MAAM,UAAU;AAAA,IAC1B;AAAA,EACF,CAAC;AAED,MAAI,OAAO,MAAM;AACf,YAAQ,IAAI;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,KAWX;AACD,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,QAAQ,MAAM,SAAS;AAE7B,MAAI,OAAO,MAAM;AACf,UAAM,OAAO,qBAAqB,KAAK;AACvC,kBAAc,4BAA4B,IAAI;AAC9C,YAAQ,IAAI,qCAAqC;AAAA,EACnD,WAAW,OAAO,KAAK;AACrB,UAAM,kBAAkB,gCAAgC,KAAK;AAC7D,QAAI,gBAAgB,WAAW,GAAG;AAChC,cAAQ,MAAM,oCAAoC;AAClD,cAAQ,KAAK,CAAC;AAAA,IAChB;AAEA,UAAM,EAAE,IAAI,IAAI,MAAM,MAAM,0CAA0C;AAAA,MACpE,QAAQ;AAAA,MACR,MAAM,KAAK,UAAU;AAAA,QACnB,iBAAiB,gCAAgC,KAAK;AAAA,MACxD,CAAC;AAAA,MACD,SAAS;AAAA,QACP,gBAAgB;AAAA,MAClB;AAAA,IACF,CAAC,EAAE,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC;AAE3B,UAAM,QAAQ,IAAI,MAAM,OAAO,EAAE,IAAI;AAErC,YAAQ,IAAI,+BAA+B,KAAK,EAAE;AAAA,EACpD,OAAO;AACL,UAAM,OAAO,qBAAqB,KAAK;AACvC,SAAK,QAAQ,CAAC,KAAK,MAAM;AACvB,YAAM,WAAW,GAAG,IAAI,MAAM,YAAY,EAAE,QAAQ,QAAQ,GAAG,CAAC,IAAI,IAAI,CAAC;AACzE,oBAAc,UAAU,IAAI,GAAG;AAC/B,cAAQ,IAAI,aAAa,QAAQ,GAAG;AAAA,IACtC,CAAC;AAAA,EACH;AACF;AAEA,KAAK,EAAE,MAAM,CAAC,QAAQ;AACpB,UAAQ,MAAM,UAAU,GAAG;AAC3B,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":[]}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GraphicsObject } from './types.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Extracts graphics objects from a debug log string
|
|
5
|
+
* Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)
|
|
6
|
+
*/
|
|
7
|
+
declare function getGraphicsObjectsFromLogString(logString: string): GraphicsObject[];
|
|
8
|
+
|
|
9
|
+
export { getGraphicsObjectsFromLogString };
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { Circle, GraphicsObject, Line, Point, Rect } from './types.js';
|
|
2
|
+
export { getGraphicsObjectsFromLogString } from './getGraphicsObjectsFromLogString.js';
|
|
3
|
+
export { getSvgFromGraphicsObject } from './getSvgFromGraphicsObject.js';
|
|
4
|
+
|
|
5
|
+
declare function getSvgFromLogString(logString: string): string;
|
|
6
|
+
declare function getHtmlFromLogString(logString: string): string;
|
|
7
|
+
declare function getSvgsFromLogString(logString: string): Array<{
|
|
8
|
+
title: string;
|
|
9
|
+
svg: string;
|
|
10
|
+
}>;
|
|
11
|
+
|
|
12
|
+
export { getHtmlFromLogString, getSvgFromLogString, getSvgsFromLogString };
|
package/dist/lib/index.js
CHANGED
|
@@ -2,13 +2,13 @@ import {
|
|
|
2
2
|
getHtmlFromLogString,
|
|
3
3
|
getSvgFromLogString,
|
|
4
4
|
getSvgsFromLogString
|
|
5
|
-
} from "../chunk-
|
|
5
|
+
} from "../chunk-2YVICPCE.js";
|
|
6
6
|
import {
|
|
7
7
|
getGraphicsObjectsFromLogString
|
|
8
|
-
} from "../chunk-
|
|
8
|
+
} from "../chunk-NG6H63SM.js";
|
|
9
9
|
import {
|
|
10
10
|
getSvgFromGraphicsObject
|
|
11
|
-
} from "../chunk-
|
|
11
|
+
} from "../chunk-X2DSMR32.js";
|
|
12
12
|
export {
|
|
13
13
|
getGraphicsObjectsFromLogString,
|
|
14
14
|
getHtmlFromLogString,
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
interface Point {
|
|
2
|
+
x: number;
|
|
3
|
+
y: number;
|
|
4
|
+
color?: string;
|
|
5
|
+
label?: string;
|
|
6
|
+
}
|
|
7
|
+
interface Line {
|
|
8
|
+
points: {
|
|
9
|
+
x: number;
|
|
10
|
+
y: number;
|
|
11
|
+
stroke?: number;
|
|
12
|
+
}[];
|
|
13
|
+
}
|
|
14
|
+
interface Rect {
|
|
15
|
+
center: {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
};
|
|
19
|
+
width: number;
|
|
20
|
+
height: number;
|
|
21
|
+
fill?: string;
|
|
22
|
+
stroke?: string;
|
|
23
|
+
color?: string;
|
|
24
|
+
}
|
|
25
|
+
interface Circle {
|
|
26
|
+
center: {
|
|
27
|
+
x: number;
|
|
28
|
+
y: number;
|
|
29
|
+
};
|
|
30
|
+
radius: number;
|
|
31
|
+
fill?: string;
|
|
32
|
+
stroke?: string;
|
|
33
|
+
}
|
|
34
|
+
interface GraphicsObject {
|
|
35
|
+
points?: Point[];
|
|
36
|
+
lines?: Line[];
|
|
37
|
+
rects?: Rect[];
|
|
38
|
+
circles?: Circle[];
|
|
39
|
+
coordinateSystem?: "cartesian" | "screen";
|
|
40
|
+
title?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type { Circle, GraphicsObject, Line, Point, Rect };
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "graphics-debug",
|
|
3
3
|
"main": "dist/lib/index.js",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.0.
|
|
5
|
+
"version": "0.0.3",
|
|
6
6
|
"files": [
|
|
7
7
|
"dist"
|
|
8
8
|
],
|
|
@@ -11,26 +11,37 @@
|
|
|
11
11
|
"gd": "./dist/cli/cli.js"
|
|
12
12
|
},
|
|
13
13
|
"scripts": {
|
|
14
|
-
"
|
|
14
|
+
"start": "vite",
|
|
15
|
+
"build": "tsup-node ./cli ./lib --dts --format esm --sourcemap",
|
|
15
16
|
"format": "biome format --write .",
|
|
16
|
-
"format:check": "biome format ."
|
|
17
|
+
"format:check": "biome format .",
|
|
18
|
+
"vercel-build": "vite build"
|
|
17
19
|
},
|
|
18
20
|
"devDependencies": {
|
|
19
21
|
"@biomejs/biome": "^1.9.4",
|
|
20
22
|
"@types/bun": "latest",
|
|
23
|
+
"@types/debug": "^4.1.12",
|
|
24
|
+
"@types/jsdom": "^21.1.7",
|
|
21
25
|
"@types/pretty": "^2.0.3",
|
|
22
26
|
"@types/react": "^18.3.12",
|
|
23
27
|
"@types/react-dom": "^18.3.1",
|
|
28
|
+
"@vitejs/plugin-react": "^4.3.3",
|
|
24
29
|
"bun-match-svg": "^0.0.8",
|
|
25
30
|
"commander": "^12.1.0",
|
|
31
|
+
"debug": "^4.3.7",
|
|
26
32
|
"react": "^18.3.1",
|
|
27
33
|
"react-dom": "^18.3.1",
|
|
28
|
-
"tsup": "^8.3.5"
|
|
34
|
+
"tsup": "^8.3.5",
|
|
35
|
+
"vite": "^5.4.11"
|
|
29
36
|
},
|
|
30
37
|
"peerDependencies": {
|
|
31
38
|
"typescript": "^5.0.0"
|
|
32
39
|
},
|
|
33
40
|
"dependencies": {
|
|
34
|
-
"
|
|
41
|
+
"@types/react-router-dom": "^5.3.3",
|
|
42
|
+
"pretty": "^2.0.0",
|
|
43
|
+
"react-router-dom": "^6.28.0",
|
|
44
|
+
"svgson": "^5.3.1",
|
|
45
|
+
"transformation-matrix": "^2.16.1"
|
|
35
46
|
}
|
|
36
47
|
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../lib/getGraphicsObjectsFromLogString.ts"],"sourcesContent":["import type { GraphicsObject } from \"./types\";\n\n/**\n * Extracts graphics objects from a debug log string\n * Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)\n */\nexport function getGraphicsObjectsFromLogString(\n\tlogString: string,\n): GraphicsObject[] {\n\tconst results: GraphicsObject[] = [];\n\n\t// Match { graphics: ... } patterns, allowing for other content on same line\n\t// Use a more precise regex that handles nested structures\n\tconst graphicsRegex =\n\t\t/\\{[\\s]*(?:\"graphics\"|graphics)[\\s]*:[\\s]*\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}[\\s]*\\}/g;\n\tconst matches = logString.match(graphicsRegex);\n\n\tif (!matches) return results;\n\n\tfor (const match of matches) {\n\t\ttry {\n\t\t\t// First try parsing as regular JSON\n\t\t\tconst parsed = JSON.parse(match);\n\t\t\tresults.push(parsed);\n\t\t} catch (e) {\n\t\t\ttry {\n\t\t\t\t// If that fails, fix JSON formatting issues:\n\t\t\t\t// 1. Add quotes around unquoted keys\n\t\t\t\t// 2. Handle potential trailing commas\n\t\t\t\tconst fixed = match\n\t\t\t\t\t.replace(/(\\b\\w+)(?=\\s*:)/g, '\"$1\"') // Quote unquoted keys more precisely\n\t\t\t\t\t.replace(/,(\\s*[}\\]])/g, \"$1\") // Remove trailing commas\n\t\t\t\t\t.replace(/:\\s*'([^']*)'/g, ':\"$1\"'); // Convert single quotes to double quotes\n\n\t\t\t\tconst parsed = JSON.parse(fixed);\n\t\t\t\tresults.push(parsed);\n\t\t\t} catch (e) {\n\t\t\t\t// Skip invalid entries but log the error and the problematic match\n\t\t\t\tconsole.warn(\"Failed to parse graphics object:\", match, e);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n"],"mappings":";AAMO,SAAS,gCACf,WACmB;AACnB,QAAM,UAA4B,CAAC;AAInC,QAAM,gBACL;AACD,QAAM,UAAU,UAAU,MAAM,aAAa;AAE7C,MAAI,CAAC,QAAS,QAAO;AAErB,aAAW,SAAS,SAAS;AAC5B,QAAI;AAEH,YAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,cAAQ,KAAK,MAAM;AAAA,IACpB,SAAS,GAAG;AACX,UAAI;AAIH,cAAM,QAAQ,MACZ,QAAQ,oBAAoB,MAAM,EAClC,QAAQ,gBAAgB,IAAI,EAC5B,QAAQ,kBAAkB,OAAO;AAEnC,cAAM,SAAS,KAAK,MAAM,KAAK;AAC/B,gBAAQ,KAAK,MAAM;AAAA,MACpB,SAASA,IAAG;AAEX,gBAAQ,KAAK,oCAAoC,OAAOA,EAAC;AAAA,MAC1D;AAAA,IACD;AAAA,EACD;AAEA,SAAO;AACR;","names":["e"]}
|
|
@@ -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[\"graphics\"]): 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[\"graphics\"][\"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 return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(\n scale_factor,\n coordinateSystem === \"screen\" ? -scale_factor : scale_factor,\n ),\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(graphicsObj: GraphicsObject): string {\n const { graphics } = graphicsObj\n const bounds = getBounds(graphics)\n const matrix = compose(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 // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(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 points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: \"black\",\n \"stroke-width\": (line.points[0].stroke || 1).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const projected = projectPoint(rect.center, matrix)\n const scaledWidth = rect.width * matrix.a\n const scaledHeight = rect.height * matrix.d\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n x: (projected.x - scaledWidth / 2).toString(),\n y: (projected.y - scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: Math.abs(scaledHeight).toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\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 cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\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":[]}
|
package/dist/cli.js
DELETED
|
@@ -1,386 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
// cli/cli.ts
|
|
4
|
-
import { parseArgs } from "node:util";
|
|
5
|
-
import "node:fs";
|
|
6
|
-
import { writeFileSync } from "node:fs";
|
|
7
|
-
|
|
8
|
-
// lib/getGraphicsObjectsFromLogString.ts
|
|
9
|
-
function getGraphicsObjectsFromLogString(logString) {
|
|
10
|
-
const results = [];
|
|
11
|
-
const graphicsRegex = /\{[\s]*(?:"graphics"|graphics)[\s]*:[\s]*\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}[\s]*\}/g;
|
|
12
|
-
const matches = logString.match(graphicsRegex);
|
|
13
|
-
if (!matches) return results;
|
|
14
|
-
for (const match of matches) {
|
|
15
|
-
try {
|
|
16
|
-
const parsed = JSON.parse(match);
|
|
17
|
-
results.push(parsed);
|
|
18
|
-
} catch (e) {
|
|
19
|
-
try {
|
|
20
|
-
const fixed = match.replace(/(\b\w+)(?=\s*:)/g, '"$1"').replace(/,(\s*[}\]])/g, "$1").replace(/:\s*'([^']*)'/g, ':"$1"');
|
|
21
|
-
const parsed = JSON.parse(fixed);
|
|
22
|
-
results.push(parsed);
|
|
23
|
-
} catch (e2) {
|
|
24
|
-
console.warn("Failed to parse graphics object:", match, e2);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
return results;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
// lib/getSvgFromGraphicsObject.ts
|
|
32
|
-
import {
|
|
33
|
-
compose,
|
|
34
|
-
translate,
|
|
35
|
-
scale,
|
|
36
|
-
applyToPoint
|
|
37
|
-
} from "transformation-matrix";
|
|
38
|
-
import { stringify } from "svgson";
|
|
39
|
-
import pretty from "pretty";
|
|
40
|
-
var DEFAULT_SVG_SIZE = 640;
|
|
41
|
-
var PADDING = 40;
|
|
42
|
-
function getBounds(graphics) {
|
|
43
|
-
const points = [
|
|
44
|
-
...graphics.points || [],
|
|
45
|
-
...(graphics.lines || []).flatMap((line) => line.points),
|
|
46
|
-
...(graphics.rects || []).flatMap((rect) => {
|
|
47
|
-
const halfWidth = rect.width / 2;
|
|
48
|
-
const halfHeight = rect.height / 2;
|
|
49
|
-
return [
|
|
50
|
-
{ x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },
|
|
51
|
-
{ x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },
|
|
52
|
-
{ x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },
|
|
53
|
-
{ x: rect.center.x + halfWidth, y: rect.center.y + halfHeight }
|
|
54
|
-
];
|
|
55
|
-
}),
|
|
56
|
-
...(graphics.circles || []).flatMap((circle) => [
|
|
57
|
-
{ x: circle.center.x - circle.radius, y: circle.center.y },
|
|
58
|
-
// left
|
|
59
|
-
{ x: circle.center.x + circle.radius, y: circle.center.y },
|
|
60
|
-
// right
|
|
61
|
-
{ x: circle.center.x, y: circle.center.y - circle.radius },
|
|
62
|
-
// top
|
|
63
|
-
{ x: circle.center.x, y: circle.center.y + circle.radius }
|
|
64
|
-
// bottom
|
|
65
|
-
])
|
|
66
|
-
];
|
|
67
|
-
if (points.length === 0) {
|
|
68
|
-
return { minX: -1, maxX: 1, minY: -1, maxY: 1 };
|
|
69
|
-
}
|
|
70
|
-
return points.reduce(
|
|
71
|
-
(bounds, point) => ({
|
|
72
|
-
minX: Math.min(bounds.minX, point.x),
|
|
73
|
-
maxX: Math.max(bounds.maxX, point.x),
|
|
74
|
-
minY: Math.min(bounds.minY, point.y),
|
|
75
|
-
maxY: Math.max(bounds.maxY, point.y)
|
|
76
|
-
}),
|
|
77
|
-
{ minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
function getProjectionMatrix(bounds, coordinateSystem) {
|
|
81
|
-
const width = bounds.maxX - bounds.minX;
|
|
82
|
-
const height = bounds.maxY - bounds.minY;
|
|
83
|
-
const scale_factor = Math.min(
|
|
84
|
-
(DEFAULT_SVG_SIZE - 2 * PADDING) / width,
|
|
85
|
-
(DEFAULT_SVG_SIZE - 2 * PADDING) / height
|
|
86
|
-
);
|
|
87
|
-
return compose(
|
|
88
|
-
translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),
|
|
89
|
-
scale(
|
|
90
|
-
scale_factor,
|
|
91
|
-
coordinateSystem === "screen" ? -scale_factor : scale_factor
|
|
92
|
-
),
|
|
93
|
-
translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
|
|
94
|
-
);
|
|
95
|
-
}
|
|
96
|
-
function projectPoint(point, matrix) {
|
|
97
|
-
const projected = applyToPoint(matrix, { x: point.x, y: point.y });
|
|
98
|
-
return { ...point, ...projected };
|
|
99
|
-
}
|
|
100
|
-
function getSvgFromGraphicsObject(graphicsObj) {
|
|
101
|
-
const { graphics } = graphicsObj;
|
|
102
|
-
const bounds = getBounds(graphics);
|
|
103
|
-
const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem));
|
|
104
|
-
const svgObject = {
|
|
105
|
-
name: "svg",
|
|
106
|
-
type: "element",
|
|
107
|
-
attributes: {
|
|
108
|
-
width: DEFAULT_SVG_SIZE.toString(),
|
|
109
|
-
height: DEFAULT_SVG_SIZE.toString(),
|
|
110
|
-
viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,
|
|
111
|
-
xmlns: "http://www.w3.org/2000/svg"
|
|
112
|
-
},
|
|
113
|
-
children: [
|
|
114
|
-
// Points
|
|
115
|
-
...(graphics.points || []).map((point) => {
|
|
116
|
-
const projected = projectPoint(point, matrix);
|
|
117
|
-
return {
|
|
118
|
-
name: "g",
|
|
119
|
-
type: "element",
|
|
120
|
-
children: [
|
|
121
|
-
{
|
|
122
|
-
name: "circle",
|
|
123
|
-
type: "element",
|
|
124
|
-
attributes: {
|
|
125
|
-
cx: projected.x.toString(),
|
|
126
|
-
cy: projected.y.toString(),
|
|
127
|
-
r: "3",
|
|
128
|
-
fill: point.color || "black"
|
|
129
|
-
}
|
|
130
|
-
},
|
|
131
|
-
...point.label ? [
|
|
132
|
-
{
|
|
133
|
-
name: "text",
|
|
134
|
-
type: "element",
|
|
135
|
-
attributes: {
|
|
136
|
-
x: (projected.x + 5).toString(),
|
|
137
|
-
y: (projected.y - 5).toString(),
|
|
138
|
-
"font-family": "sans-serif",
|
|
139
|
-
"font-size": "12"
|
|
140
|
-
},
|
|
141
|
-
children: [{ type: "text", value: point.label }]
|
|
142
|
-
}
|
|
143
|
-
] : []
|
|
144
|
-
]
|
|
145
|
-
};
|
|
146
|
-
}),
|
|
147
|
-
// Lines
|
|
148
|
-
...(graphics.lines || []).map((line) => ({
|
|
149
|
-
name: "polyline",
|
|
150
|
-
type: "element",
|
|
151
|
-
attributes: {
|
|
152
|
-
points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
|
|
153
|
-
fill: "none",
|
|
154
|
-
stroke: "black",
|
|
155
|
-
"stroke-width": (line.points[0].stroke || 1).toString()
|
|
156
|
-
}
|
|
157
|
-
})),
|
|
158
|
-
// Rectangles
|
|
159
|
-
...(graphics.rects || []).map((rect) => {
|
|
160
|
-
const projected = projectPoint(rect.center, matrix);
|
|
161
|
-
const scaledWidth = rect.width * matrix.a;
|
|
162
|
-
const scaledHeight = rect.height * matrix.d;
|
|
163
|
-
return {
|
|
164
|
-
name: "rect",
|
|
165
|
-
type: "element",
|
|
166
|
-
attributes: {
|
|
167
|
-
x: (projected.x - scaledWidth / 2).toString(),
|
|
168
|
-
y: (projected.y - scaledHeight / 2).toString(),
|
|
169
|
-
width: scaledWidth.toString(),
|
|
170
|
-
height: Math.abs(scaledHeight).toString(),
|
|
171
|
-
fill: rect.fill || "none",
|
|
172
|
-
stroke: rect.stroke || "black"
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
}),
|
|
176
|
-
// Circles
|
|
177
|
-
...(graphics.circles || []).map((circle) => {
|
|
178
|
-
const projected = projectPoint(circle.center, matrix);
|
|
179
|
-
const scaledRadius = circle.radius * Math.abs(matrix.a);
|
|
180
|
-
return {
|
|
181
|
-
name: "circle",
|
|
182
|
-
type: "element",
|
|
183
|
-
attributes: {
|
|
184
|
-
cx: projected.x.toString(),
|
|
185
|
-
cy: projected.y.toString(),
|
|
186
|
-
r: scaledRadius.toString(),
|
|
187
|
-
fill: circle.fill || "none",
|
|
188
|
-
stroke: circle.stroke || "black"
|
|
189
|
-
}
|
|
190
|
-
};
|
|
191
|
-
}),
|
|
192
|
-
// Crosshair lines and coordinates (initially hidden)
|
|
193
|
-
{
|
|
194
|
-
name: "g",
|
|
195
|
-
type: "element",
|
|
196
|
-
attributes: {
|
|
197
|
-
id: "crosshair",
|
|
198
|
-
style: "display: none"
|
|
199
|
-
},
|
|
200
|
-
children: [
|
|
201
|
-
{
|
|
202
|
-
name: "line",
|
|
203
|
-
type: "element",
|
|
204
|
-
attributes: {
|
|
205
|
-
id: "crosshair-h",
|
|
206
|
-
y1: "0",
|
|
207
|
-
y2: DEFAULT_SVG_SIZE.toString(),
|
|
208
|
-
stroke: "#666",
|
|
209
|
-
"stroke-width": "0.5"
|
|
210
|
-
}
|
|
211
|
-
},
|
|
212
|
-
{
|
|
213
|
-
name: "line",
|
|
214
|
-
type: "element",
|
|
215
|
-
attributes: {
|
|
216
|
-
id: "crosshair-v",
|
|
217
|
-
x1: "0",
|
|
218
|
-
x2: DEFAULT_SVG_SIZE.toString(),
|
|
219
|
-
stroke: "#666",
|
|
220
|
-
"stroke-width": "0.5"
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
{
|
|
224
|
-
name: "text",
|
|
225
|
-
type: "element",
|
|
226
|
-
attributes: {
|
|
227
|
-
id: "coordinates",
|
|
228
|
-
"font-family": "monospace",
|
|
229
|
-
"font-size": "12",
|
|
230
|
-
fill: "#666"
|
|
231
|
-
},
|
|
232
|
-
children: [{ type: "text", value: "" }]
|
|
233
|
-
}
|
|
234
|
-
]
|
|
235
|
-
},
|
|
236
|
-
// Mouse tracking script
|
|
237
|
-
{
|
|
238
|
-
name: "script",
|
|
239
|
-
type: "element",
|
|
240
|
-
children: [
|
|
241
|
-
{
|
|
242
|
-
type: "text",
|
|
243
|
-
value: `
|
|
244
|
-
document.currentScript.parentElement.addEventListener('mousemove', (e) => {
|
|
245
|
-
const svg = e.currentTarget;
|
|
246
|
-
const rect = svg.getBoundingClientRect();
|
|
247
|
-
const x = e.clientX - rect.left;
|
|
248
|
-
const y = e.clientY - rect.top;
|
|
249
|
-
const crosshair = svg.getElementById('crosshair');
|
|
250
|
-
const h = svg.getElementById('crosshair-h');
|
|
251
|
-
const v = svg.getElementById('crosshair-v');
|
|
252
|
-
const coords = svg.getElementById('coordinates');
|
|
253
|
-
|
|
254
|
-
crosshair.style.display = 'block';
|
|
255
|
-
h.setAttribute('x1', '0');
|
|
256
|
-
h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');
|
|
257
|
-
h.setAttribute('y1', y);
|
|
258
|
-
h.setAttribute('y2', y);
|
|
259
|
-
v.setAttribute('x1', x);
|
|
260
|
-
v.setAttribute('x2', x);
|
|
261
|
-
v.setAttribute('y1', '0');
|
|
262
|
-
v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');
|
|
263
|
-
|
|
264
|
-
// Calculate real coordinates using inverse transformation
|
|
265
|
-
const matrix = ${JSON.stringify(matrix)};
|
|
266
|
-
// Manually invert and apply the affine transform
|
|
267
|
-
// Since we only use translate and scale, we can directly compute:
|
|
268
|
-
// x' = (x - tx) / sx
|
|
269
|
-
// y' = (y - ty) / sy
|
|
270
|
-
const sx = matrix.a;
|
|
271
|
-
const sy = matrix.d;
|
|
272
|
-
const tx = matrix.e;
|
|
273
|
-
const ty = matrix.f;
|
|
274
|
-
const realPoint = {
|
|
275
|
-
x: (x - tx) / sx,
|
|
276
|
-
y: -(y - ty) / sy // Flip y back since we used negative scale
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
coords.textContent = \`(\${realPoint.x.toFixed(2)}, \${realPoint.y.toFixed(2)})\`;
|
|
280
|
-
coords.setAttribute('x', (x + 5).toString());
|
|
281
|
-
coords.setAttribute('y', (y - 5).toString());
|
|
282
|
-
});
|
|
283
|
-
document.currentScript.parentElement.addEventListener('mouseleave', () => {
|
|
284
|
-
document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
|
|
285
|
-
});
|
|
286
|
-
`
|
|
287
|
-
}
|
|
288
|
-
]
|
|
289
|
-
}
|
|
290
|
-
]
|
|
291
|
-
};
|
|
292
|
-
return pretty(stringify(svgObject));
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
// lib/index.ts
|
|
296
|
-
function getHtmlFromLogString(logString) {
|
|
297
|
-
const svgs = getSvgsFromLogString(logString);
|
|
298
|
-
if (svgs.length === 0) return "";
|
|
299
|
-
const sections = svgs.map(
|
|
300
|
-
({ title, svg }) => `
|
|
301
|
-
<section>
|
|
302
|
-
<h2>${title}</h2>
|
|
303
|
-
${svg}
|
|
304
|
-
</section>
|
|
305
|
-
`
|
|
306
|
-
).join("\n");
|
|
307
|
-
return `
|
|
308
|
-
<!DOCTYPE html>
|
|
309
|
-
<html>
|
|
310
|
-
<head>
|
|
311
|
-
<title>Graphics Debug Output</title>
|
|
312
|
-
<style>
|
|
313
|
-
body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }
|
|
314
|
-
h1 { color: #333; }
|
|
315
|
-
section { margin: 40px 0; }
|
|
316
|
-
svg { max-width: 100%; height: auto; border: 1px solid #eee; }
|
|
317
|
-
</style>
|
|
318
|
-
</head>
|
|
319
|
-
<body>
|
|
320
|
-
<h1>Graphics Debug Output</h1>
|
|
321
|
-
${sections}
|
|
322
|
-
</body>
|
|
323
|
-
</html>`;
|
|
324
|
-
}
|
|
325
|
-
function getSvgsFromLogString(logString) {
|
|
326
|
-
const objects = getGraphicsObjectsFromLogString(logString);
|
|
327
|
-
return objects.map((obj) => ({
|
|
328
|
-
title: obj.graphics.title || "Untitled Graphic",
|
|
329
|
-
svg: getSvgFromGraphicsObject(obj)
|
|
330
|
-
}));
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// cli/cli.ts
|
|
334
|
-
async function getInput() {
|
|
335
|
-
if (process.stdin.isTTY) {
|
|
336
|
-
console.error(
|
|
337
|
-
"Error: No input provided. Pipe in content with graphics objects."
|
|
338
|
-
);
|
|
339
|
-
process.exit(1);
|
|
340
|
-
}
|
|
341
|
-
const chunks = [];
|
|
342
|
-
for await (const chunk of process.stdin) {
|
|
343
|
-
chunks.push(chunk);
|
|
344
|
-
}
|
|
345
|
-
return chunks.join("");
|
|
346
|
-
}
|
|
347
|
-
async function main() {
|
|
348
|
-
const { values } = parseArgs({
|
|
349
|
-
options: {
|
|
350
|
-
html: { type: "boolean" },
|
|
351
|
-
help: { type: "boolean" }
|
|
352
|
-
}
|
|
353
|
-
});
|
|
354
|
-
if (values.help) {
|
|
355
|
-
console.log(`
|
|
356
|
-
Usage: graphics-debug [options]
|
|
357
|
-
|
|
358
|
-
Options:
|
|
359
|
-
--html Output a single HTML file with all graphics
|
|
360
|
-
--help Show this help message
|
|
361
|
-
|
|
362
|
-
Examples:
|
|
363
|
-
cat debug.log | graphics-debug
|
|
364
|
-
echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html
|
|
365
|
-
`);
|
|
366
|
-
process.exit(0);
|
|
367
|
-
}
|
|
368
|
-
const input = await getInput();
|
|
369
|
-
if (values.html) {
|
|
370
|
-
const html = getHtmlFromLogString(input);
|
|
371
|
-
writeFileSync("graphicsdebug.debug.html", html);
|
|
372
|
-
console.log('Wrote to "graphicsdebug.debug.html"');
|
|
373
|
-
} else {
|
|
374
|
-
const svgs = getSvgsFromLogString(input);
|
|
375
|
-
svgs.forEach((svg, i) => {
|
|
376
|
-
const filename = `${svg.title.toLowerCase().replace(/\s+/g, "-")}-${i + 1}.debug.svg`;
|
|
377
|
-
writeFileSync(filename, svg.svg);
|
|
378
|
-
console.log(`Wrote to "${filename}"`);
|
|
379
|
-
});
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
main().catch((err) => {
|
|
383
|
-
console.error("Error:", err);
|
|
384
|
-
process.exit(1);
|
|
385
|
-
});
|
|
386
|
-
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../cli/cli.ts","../lib/getGraphicsObjectsFromLogString.ts","../lib/getSvgFromGraphicsObject.ts","../lib/index.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { parseArgs } from \"node:util\"\nimport { readFileSync } from \"node:fs\"\nimport { writeFileSync } from \"node:fs\"\nimport { getSvgsFromLogString, getHtmlFromLogString } from \"../lib\"\n\nasync function getInput(): Promise<string> {\n // Check if there's data being piped in\n if (process.stdin.isTTY) {\n console.error(\n \"Error: No input provided. Pipe in content with graphics objects.\",\n )\n process.exit(1)\n }\n\n const chunks = []\n for await (const chunk of process.stdin) {\n chunks.push(chunk)\n }\n return chunks.join(\"\")\n}\n\nasync function main() {\n const { values } = parseArgs({\n options: {\n html: { type: \"boolean\" },\n help: { type: \"boolean\" },\n },\n })\n\n if (values.help) {\n console.log(`\nUsage: graphics-debug [options]\n\nOptions:\n --html Output a single HTML file with all graphics\n --help Show this help message\n\nExamples:\n cat debug.log | graphics-debug\n echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html\n `)\n process.exit(0)\n }\n\n const input = await getInput()\n\n if (values.html) {\n const html = getHtmlFromLogString(input)\n writeFileSync(\"graphicsdebug.debug.html\", html)\n console.log('Wrote to \"graphicsdebug.debug.html\"')\n } else {\n const svgs = getSvgsFromLogString(input)\n svgs.forEach((svg, i) => {\n const filename = `${svg.title.toLowerCase().replace(/\\s+/g, \"-\")}-${i + 1}.debug.svg`\n writeFileSync(filename, svg.svg)\n console.log(`Wrote to \"${filename}\"`)\n })\n }\n}\n\nmain().catch((err) => {\n console.error(\"Error:\", err)\n process.exit(1)\n})\n","import type { GraphicsObject } from \"./types\";\n\n/**\n * Extracts graphics objects from a debug log string\n * Handles both well-formatted JSON and relaxed JSON syntax (unquoted keys)\n */\nexport function getGraphicsObjectsFromLogString(\n\tlogString: string,\n): GraphicsObject[] {\n\tconst results: GraphicsObject[] = [];\n\n\t// Match { graphics: ... } patterns, allowing for other content on same line\n\t// Use a more precise regex that handles nested structures\n\tconst graphicsRegex =\n\t\t/\\{[\\s]*(?:\"graphics\"|graphics)[\\s]*:[\\s]*\\{(?:[^{}]|\\{(?:[^{}]|\\{[^{}]*\\})*\\})*\\}[\\s]*\\}/g;\n\tconst matches = logString.match(graphicsRegex);\n\n\tif (!matches) return results;\n\n\tfor (const match of matches) {\n\t\ttry {\n\t\t\t// First try parsing as regular JSON\n\t\t\tconst parsed = JSON.parse(match);\n\t\t\tresults.push(parsed);\n\t\t} catch (e) {\n\t\t\ttry {\n\t\t\t\t// If that fails, fix JSON formatting issues:\n\t\t\t\t// 1. Add quotes around unquoted keys\n\t\t\t\t// 2. Handle potential trailing commas\n\t\t\t\tconst fixed = match\n\t\t\t\t\t.replace(/(\\b\\w+)(?=\\s*:)/g, '\"$1\"') // Quote unquoted keys more precisely\n\t\t\t\t\t.replace(/,(\\s*[}\\]])/g, \"$1\") // Remove trailing commas\n\t\t\t\t\t.replace(/:\\s*'([^']*)'/g, ':\"$1\"'); // Convert single quotes to double quotes\n\n\t\t\t\tconst parsed = JSON.parse(fixed);\n\t\t\t\tresults.push(parsed);\n\t\t\t} catch (e) {\n\t\t\t\t// Skip invalid entries but log the error and the problematic match\n\t\t\t\tconsole.warn(\"Failed to parse graphics object:\", match, e);\n\t\t\t}\n\t\t}\n\t}\n\n\treturn results;\n}\n","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[\"graphics\"]): 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[\"graphics\"][\"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 return compose(\n translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),\n scale(\n scale_factor,\n coordinateSystem === \"screen\" ? -scale_factor : scale_factor,\n ),\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(graphicsObj: GraphicsObject): string {\n const { graphics } = graphicsObj\n const bounds = getBounds(graphics)\n const matrix = compose(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 // Points\n ...(graphics.points || []).map((point) => {\n const projected = projectPoint(point, matrix)\n return {\n name: \"g\",\n type: \"element\",\n children: [\n {\n name: \"circle\",\n type: \"element\",\n attributes: {\n cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: \"3\",\n fill: point.color || \"black\",\n },\n },\n ...(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 points: line.points\n .map((p) => projectPoint(p, matrix))\n .map((p) => `${p.x},${p.y}`)\n .join(\" \"),\n fill: \"none\",\n stroke: \"black\",\n \"stroke-width\": (line.points[0].stroke || 1).toString(),\n },\n })),\n // Rectangles\n ...(graphics.rects || []).map((rect) => {\n const projected = projectPoint(rect.center, matrix)\n const scaledWidth = rect.width * matrix.a\n const scaledHeight = rect.height * matrix.d\n return {\n name: \"rect\",\n type: \"element\",\n attributes: {\n x: (projected.x - scaledWidth / 2).toString(),\n y: (projected.y - scaledHeight / 2).toString(),\n width: scaledWidth.toString(),\n height: Math.abs(scaledHeight).toString(),\n fill: rect.fill || \"none\",\n stroke: rect.stroke || \"black\",\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 cx: projected.x.toString(),\n cy: projected.y.toString(),\n r: scaledRadius.toString(),\n fill: circle.fill || \"none\",\n stroke: circle.stroke || \"black\",\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","import { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nimport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport type { Point, Line, Rect, Circle, GraphicsObject } from \"./types\"\nexport { getGraphicsObjectsFromLogString } from \"./getGraphicsObjectsFromLogString\"\nexport { getSvgFromGraphicsObject } from \"./getSvgFromGraphicsObject\"\n\nexport function getSvgFromLogString(logString: string): string {\n const objects = getGraphicsObjectsFromLogString(logString)\n if (objects.length === 0) return \"\"\n return getSvgFromGraphicsObject(objects[0])\n}\n\nexport function getHtmlFromLogString(logString: string): string {\n const svgs = getSvgsFromLogString(logString)\n if (svgs.length === 0) return \"\"\n\n const sections = svgs\n .map(\n ({ title, svg }) => `\n <section>\n <h2>${title}</h2>\n ${svg}\n </section>\n `,\n )\n .join(\"\\n\")\n\n return `\n<!DOCTYPE html>\n<html>\n<head>\n <title>Graphics Debug Output</title>\n <style>\n body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }\n h1 { color: #333; }\n section { margin: 40px 0; }\n svg { max-width: 100%; height: auto; border: 1px solid #eee; }\n </style>\n</head>\n<body>\n <h1>Graphics Debug Output</h1>\n ${sections}\n</body>\n</html>`\n}\n\nexport function getSvgsFromLogString(\n logString: string,\n): Array<{ title: string; svg: string }> {\n const objects = getGraphicsObjectsFromLogString(logString)\n return objects.map((obj) => ({\n title: obj.graphics.title || \"Untitled Graphic\",\n svg: getSvgFromGraphicsObject(obj),\n }))\n}\n"],"mappings":"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","names":["e"]}
|