graphics-debug 0.0.1

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 ADDED
@@ -0,0 +1,117 @@
1
+ # graphics-debug
2
+
3
+ Module for debugging graphics, turn log output into meaningful markdown and SVG diagrams.
4
+
5
+ Just pipe in output with graphics JSON objects into `graphics-debug` (or `gd`) to get an html file
6
+ with all your graphics drawn-in.
7
+
8
+ ```bash
9
+ echo '{ graphics: { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" } }' | graphics-debug
10
+ # wrote to "test-graphic-1.debug.svg"
11
+ ```
12
+
13
+ Don't have access to the cli? Paste into the online version: `TBA`
14
+
15
+ ## Format
16
+
17
+ The `graphics` json object is very simple, here's the basic schema:
18
+
19
+ ```typescript
20
+ interface GraphicsObject {
21
+ graphics: {
22
+ points?: { x: number; y: number; color?: string; label?: string }[]
23
+ lines?: { points: { x: number; y: number; stroke?: number }[] }[]
24
+ rects?: Array<{
25
+ center: { x: number; y: number }
26
+ width: number
27
+ height: number
28
+ fill?: string
29
+ stroke?: string
30
+ }>
31
+ circles?: Array<{
32
+ center: { x: number; y: number }
33
+ radius: number
34
+ fill?: string
35
+ stroke?: string
36
+ }>
37
+ grid?: { cellSize: number; label?: boolean }
38
+ coordinateSystem?: "cartesian" | "screen"
39
+ title?: string
40
+ }
41
+ }
42
+ ```
43
+
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
+ ## Library Usage
51
+
52
+ ### Writing `graphics-debug` compatible logs
53
+
54
+ The best way to write `graphics-debug` compatible logs is to use the [`debug` library](https://www.npmjs.com/package/debug).
55
+
56
+ ```tsx
57
+ import Debug from "debug"
58
+
59
+ const debugGraphics = Debug("mypackage:graphics")
60
+
61
+ const A = { x: 0, y: 0, label: "A" }
62
+ const B = { x: 1, y: 1, label: "B" }
63
+
64
+ debugGraphics({
65
+ graphics: {
66
+ points: [A, B],
67
+ title: "initial points for my algorithm",
68
+ },
69
+ })
70
+
71
+ // ... do some algorithm stuff e.g....
72
+ const C = { x: (A.x + B.x) / 2, y: (A.y + B.y) / 2, label: "C" }
73
+
74
+ debugGraphics({
75
+ graphics: {
76
+ points: [A, B, C],
77
+ title: "final points for my algorithm",
78
+ },
79
+ })
80
+ ```
81
+
82
+ To see the output, you'll need to run `DEBUG=mypackage:graphics` in your terminal
83
+ before running the program. This makes it easy to turn on/off the graphics output
84
+
85
+ You can also use `debugGraphics.enabled` to conditionally emit graphics based,
86
+ this is useful if it's expensive to compute the graphics object.
87
+
88
+ ### Process Log Strings into HTML or SVGs
89
+
90
+ ```tsx
91
+ import {
92
+ getSvgFromLogString,
93
+ getHtmlFromLogString,
94
+ getSvgsFromLogString,
95
+ } from "graphics-debug"
96
+
97
+ const logString = `
98
+ hello world! This is some other content that will be ignored
99
+ here's some graphics: { graphics: { points: [{x: 0, y: 0, label: "hello world" }], title: "test graphic" } }
100
+ `
101
+
102
+ const svg = getSvgFromLogString(logString)
103
+ const html = getHtmlFromLogString(logString)
104
+
105
+ // If you want to parse for multiple SVGs
106
+ const svgs = getSvgsFromLogString(logString)
107
+ // Array<{ title: string; svg: string }>
108
+ ```
109
+
110
+ ### Extract `graphics` objects from a Debug Log
111
+
112
+ ```tsx
113
+ import { getGraphicsObjectsFromLogString } from "graphics-debug"
114
+
115
+ const graphicsObjects = getGraphicsObjectsFromLogString(logString)
116
+ // Array<GraphicsObject>
117
+ ```
@@ -0,0 +1,27 @@
1
+ // lib/getGraphicsObjectsFromLogString.ts
2
+ function getGraphicsObjectsFromLogString(logString) {
3
+ const results = [];
4
+ const graphicsRegex = /\{[\s]*(?:"graphics"|graphics)[\s]*:[\s]*\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}[\s]*\}/g;
5
+ const matches = logString.match(graphicsRegex);
6
+ if (!matches) return results;
7
+ for (const match of matches) {
8
+ try {
9
+ const parsed = JSON.parse(match);
10
+ results.push(parsed);
11
+ } catch (e) {
12
+ try {
13
+ const fixed = match.replace(/(\b\w+)(?=\s*:)/g, '"$1"').replace(/,(\s*[}\]])/g, "$1").replace(/:\s*'([^']*)'/g, ':"$1"');
14
+ const parsed = JSON.parse(fixed);
15
+ results.push(parsed);
16
+ } catch (e2) {
17
+ console.warn("Failed to parse graphics object:", match, e2);
18
+ }
19
+ }
20
+ }
21
+ return results;
22
+ }
23
+
24
+ export {
25
+ getGraphicsObjectsFromLogString
26
+ };
27
+ //# sourceMappingURL=chunk-646ISP7J.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\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"]}
@@ -0,0 +1,268 @@
1
+ // lib/getSvgFromGraphicsObject.ts
2
+ import {
3
+ compose,
4
+ translate,
5
+ scale,
6
+ applyToPoint
7
+ } from "transformation-matrix";
8
+ import { stringify } from "svgson";
9
+ import pretty from "pretty";
10
+ var DEFAULT_SVG_SIZE = 640;
11
+ var PADDING = 40;
12
+ function getBounds(graphics) {
13
+ const points = [
14
+ ...graphics.points || [],
15
+ ...(graphics.lines || []).flatMap((line) => line.points),
16
+ ...(graphics.rects || []).flatMap((rect) => {
17
+ const halfWidth = rect.width / 2;
18
+ const halfHeight = rect.height / 2;
19
+ return [
20
+ { x: rect.center.x - halfWidth, y: rect.center.y - halfHeight },
21
+ { x: rect.center.x + halfWidth, y: rect.center.y - halfHeight },
22
+ { x: rect.center.x - halfWidth, y: rect.center.y + halfHeight },
23
+ { x: rect.center.x + halfWidth, y: rect.center.y + halfHeight }
24
+ ];
25
+ }),
26
+ ...(graphics.circles || []).flatMap((circle) => [
27
+ { x: circle.center.x - circle.radius, y: circle.center.y },
28
+ // left
29
+ { x: circle.center.x + circle.radius, y: circle.center.y },
30
+ // right
31
+ { x: circle.center.x, y: circle.center.y - circle.radius },
32
+ // top
33
+ { x: circle.center.x, y: circle.center.y + circle.radius }
34
+ // bottom
35
+ ])
36
+ ];
37
+ if (points.length === 0) {
38
+ return { minX: -1, maxX: 1, minY: -1, maxY: 1 };
39
+ }
40
+ return points.reduce(
41
+ (bounds, point) => ({
42
+ minX: Math.min(bounds.minX, point.x),
43
+ maxX: Math.max(bounds.maxX, point.x),
44
+ minY: Math.min(bounds.minY, point.y),
45
+ maxY: Math.max(bounds.maxY, point.y)
46
+ }),
47
+ { minX: Infinity, maxX: -Infinity, minY: Infinity, maxY: -Infinity }
48
+ );
49
+ }
50
+ function getProjectionMatrix(bounds, coordinateSystem) {
51
+ const width = bounds.maxX - bounds.minX;
52
+ const height = bounds.maxY - bounds.minY;
53
+ const scale_factor = Math.min(
54
+ (DEFAULT_SVG_SIZE - 2 * PADDING) / width,
55
+ (DEFAULT_SVG_SIZE - 2 * PADDING) / height
56
+ );
57
+ return compose(
58
+ translate(DEFAULT_SVG_SIZE / 2, DEFAULT_SVG_SIZE / 2),
59
+ scale(
60
+ scale_factor,
61
+ coordinateSystem === "screen" ? -scale_factor : scale_factor
62
+ ),
63
+ translate(-(bounds.minX + width / 2), -(bounds.minY + height / 2))
64
+ );
65
+ }
66
+ function projectPoint(point, matrix) {
67
+ const projected = applyToPoint(matrix, { x: point.x, y: point.y });
68
+ return { ...point, ...projected };
69
+ }
70
+ function getSvgFromGraphicsObject(graphicsObj) {
71
+ const { graphics } = graphicsObj;
72
+ const bounds = getBounds(graphics);
73
+ const matrix = compose(getProjectionMatrix(bounds, graphics.coordinateSystem));
74
+ const svgObject = {
75
+ name: "svg",
76
+ type: "element",
77
+ attributes: {
78
+ width: DEFAULT_SVG_SIZE.toString(),
79
+ height: DEFAULT_SVG_SIZE.toString(),
80
+ viewBox: `0 0 ${DEFAULT_SVG_SIZE} ${DEFAULT_SVG_SIZE}`,
81
+ xmlns: "http://www.w3.org/2000/svg"
82
+ },
83
+ children: [
84
+ // Points
85
+ ...(graphics.points || []).map((point) => {
86
+ const projected = projectPoint(point, matrix);
87
+ return {
88
+ name: "g",
89
+ type: "element",
90
+ children: [
91
+ {
92
+ name: "circle",
93
+ type: "element",
94
+ attributes: {
95
+ cx: projected.x.toString(),
96
+ cy: projected.y.toString(),
97
+ r: "3",
98
+ fill: point.color || "black"
99
+ }
100
+ },
101
+ ...point.label ? [
102
+ {
103
+ name: "text",
104
+ type: "element",
105
+ attributes: {
106
+ x: (projected.x + 5).toString(),
107
+ y: (projected.y - 5).toString(),
108
+ "font-family": "sans-serif",
109
+ "font-size": "12"
110
+ },
111
+ children: [{ type: "text", value: point.label }]
112
+ }
113
+ ] : []
114
+ ]
115
+ };
116
+ }),
117
+ // Lines
118
+ ...(graphics.lines || []).map((line) => ({
119
+ name: "polyline",
120
+ type: "element",
121
+ attributes: {
122
+ points: line.points.map((p) => projectPoint(p, matrix)).map((p) => `${p.x},${p.y}`).join(" "),
123
+ fill: "none",
124
+ stroke: "black",
125
+ "stroke-width": (line.points[0].stroke || 1).toString()
126
+ }
127
+ })),
128
+ // Rectangles
129
+ ...(graphics.rects || []).map((rect) => {
130
+ const projected = projectPoint(rect.center, matrix);
131
+ const scaledWidth = rect.width * matrix.a;
132
+ const scaledHeight = rect.height * matrix.d;
133
+ return {
134
+ name: "rect",
135
+ type: "element",
136
+ attributes: {
137
+ x: (projected.x - scaledWidth / 2).toString(),
138
+ y: (projected.y - scaledHeight / 2).toString(),
139
+ width: scaledWidth.toString(),
140
+ height: Math.abs(scaledHeight).toString(),
141
+ fill: rect.fill || "none",
142
+ stroke: rect.stroke || "black"
143
+ }
144
+ };
145
+ }),
146
+ // Circles
147
+ ...(graphics.circles || []).map((circle) => {
148
+ const projected = projectPoint(circle.center, matrix);
149
+ const scaledRadius = circle.radius * Math.abs(matrix.a);
150
+ return {
151
+ name: "circle",
152
+ type: "element",
153
+ attributes: {
154
+ cx: projected.x.toString(),
155
+ cy: projected.y.toString(),
156
+ r: scaledRadius.toString(),
157
+ fill: circle.fill || "none",
158
+ stroke: circle.stroke || "black"
159
+ }
160
+ };
161
+ }),
162
+ // Crosshair lines and coordinates (initially hidden)
163
+ {
164
+ name: "g",
165
+ type: "element",
166
+ attributes: {
167
+ id: "crosshair",
168
+ style: "display: none"
169
+ },
170
+ children: [
171
+ {
172
+ name: "line",
173
+ type: "element",
174
+ attributes: {
175
+ id: "crosshair-h",
176
+ y1: "0",
177
+ y2: DEFAULT_SVG_SIZE.toString(),
178
+ stroke: "#666",
179
+ "stroke-width": "0.5"
180
+ }
181
+ },
182
+ {
183
+ name: "line",
184
+ type: "element",
185
+ attributes: {
186
+ id: "crosshair-v",
187
+ x1: "0",
188
+ x2: DEFAULT_SVG_SIZE.toString(),
189
+ stroke: "#666",
190
+ "stroke-width": "0.5"
191
+ }
192
+ },
193
+ {
194
+ name: "text",
195
+ type: "element",
196
+ attributes: {
197
+ id: "coordinates",
198
+ "font-family": "monospace",
199
+ "font-size": "12",
200
+ fill: "#666"
201
+ },
202
+ children: [{ type: "text", value: "" }]
203
+ }
204
+ ]
205
+ },
206
+ // Mouse tracking script
207
+ {
208
+ name: "script",
209
+ type: "element",
210
+ children: [
211
+ {
212
+ type: "text",
213
+ value: `
214
+ document.currentScript.parentElement.addEventListener('mousemove', (e) => {
215
+ const svg = e.currentTarget;
216
+ const rect = svg.getBoundingClientRect();
217
+ const x = e.clientX - rect.left;
218
+ const y = e.clientY - rect.top;
219
+ const crosshair = svg.getElementById('crosshair');
220
+ const h = svg.getElementById('crosshair-h');
221
+ const v = svg.getElementById('crosshair-v');
222
+ const coords = svg.getElementById('coordinates');
223
+
224
+ crosshair.style.display = 'block';
225
+ h.setAttribute('x1', '0');
226
+ h.setAttribute('x2', '${DEFAULT_SVG_SIZE}');
227
+ h.setAttribute('y1', y);
228
+ h.setAttribute('y2', y);
229
+ v.setAttribute('x1', x);
230
+ v.setAttribute('x2', x);
231
+ v.setAttribute('y1', '0');
232
+ v.setAttribute('y2', '${DEFAULT_SVG_SIZE}');
233
+
234
+ // Calculate real coordinates using inverse transformation
235
+ const matrix = ${JSON.stringify(matrix)};
236
+ // Manually invert and apply the affine transform
237
+ // Since we only use translate and scale, we can directly compute:
238
+ // x' = (x - tx) / sx
239
+ // y' = (y - ty) / sy
240
+ const sx = matrix.a;
241
+ const sy = matrix.d;
242
+ const tx = matrix.e;
243
+ const ty = matrix.f;
244
+ const realPoint = {
245
+ x: (x - tx) / sx,
246
+ y: -(y - ty) / sy // Flip y back since we used negative scale
247
+ }
248
+
249
+ coords.textContent = \`(\${realPoint.x.toFixed(2)}, \${realPoint.y.toFixed(2)})\`;
250
+ coords.setAttribute('x', (x + 5).toString());
251
+ coords.setAttribute('y', (y - 5).toString());
252
+ });
253
+ document.currentScript.parentElement.addEventListener('mouseleave', () => {
254
+ document.currentScript.parentElement.getElementById('crosshair').style.display = 'none';
255
+ });
256
+ `
257
+ }
258
+ ]
259
+ }
260
+ ]
261
+ };
262
+ return pretty(stringify(svgObject));
263
+ }
264
+
265
+ export {
266
+ getSvgFromGraphicsObject
267
+ };
268
+ //# sourceMappingURL=chunk-G7UBPMLZ.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[\"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":[]}
@@ -0,0 +1,56 @@
1
+ import {
2
+ getGraphicsObjectsFromLogString
3
+ } from "./chunk-646ISP7J.js";
4
+ import {
5
+ getSvgFromGraphicsObject
6
+ } from "./chunk-G7UBPMLZ.js";
7
+
8
+ // lib/index.ts
9
+ function getSvgFromLogString(logString) {
10
+ const objects = getGraphicsObjectsFromLogString(logString);
11
+ if (objects.length === 0) return "";
12
+ return getSvgFromGraphicsObject(objects[0]);
13
+ }
14
+ function getHtmlFromLogString(logString) {
15
+ const svgs = getSvgsFromLogString(logString);
16
+ if (svgs.length === 0) return "";
17
+ const sections = svgs.map(
18
+ ({ title, svg }) => `
19
+ <section>
20
+ <h2>${title}</h2>
21
+ ${svg}
22
+ </section>
23
+ `
24
+ ).join("\n");
25
+ return `
26
+ <!DOCTYPE html>
27
+ <html>
28
+ <head>
29
+ <title>Graphics Debug Output</title>
30
+ <style>
31
+ body { font-family: system-ui; max-width: 1200px; margin: 0 auto; padding: 20px; }
32
+ h1 { color: #333; }
33
+ section { margin: 40px 0; }
34
+ svg { max-width: 100%; height: auto; border: 1px solid #eee; }
35
+ </style>
36
+ </head>
37
+ <body>
38
+ <h1>Graphics Debug Output</h1>
39
+ ${sections}
40
+ </body>
41
+ </html>`;
42
+ }
43
+ function getSvgsFromLogString(logString) {
44
+ const objects = getGraphicsObjectsFromLogString(logString);
45
+ return objects.map((obj) => ({
46
+ title: obj.graphics.title || "Untitled Graphic",
47
+ svg: getSvgFromGraphicsObject(obj)
48
+ }));
49
+ }
50
+
51
+ export {
52
+ getSvgFromLogString,
53
+ getHtmlFromLogString,
54
+ getSvgsFromLogString
55
+ };
56
+ //# sourceMappingURL=chunk-GDKEOGZB.js.map
@@ -0,0 +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.graphics.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,SAAS;AAAA,IAC7B,KAAK,yBAAyB,GAAG;AAAA,EACnC,EAAE;AACJ;","names":[]}
@@ -0,0 +1,79 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ getHtmlFromLogString,
4
+ getSvgsFromLogString
5
+ } from "../chunk-GDKEOGZB.js";
6
+ import {
7
+ getGraphicsObjectsFromLogString
8
+ } from "../chunk-646ISP7J.js";
9
+ import "../chunk-G7UBPMLZ.js";
10
+
11
+ // cli/cli.ts
12
+ import { parseArgs } from "node:util";
13
+ import "node:fs";
14
+ import { writeFileSync } from "node:fs";
15
+ async function getInput() {
16
+ if (process.stdin.isTTY) {
17
+ console.error(
18
+ "Error: No input provided. Pipe in content with graphics objects."
19
+ );
20
+ process.exit(1);
21
+ }
22
+ const chunks = [];
23
+ for await (const chunk of process.stdin) {
24
+ chunks.push(chunk);
25
+ }
26
+ return chunks.join("");
27
+ }
28
+ async function main() {
29
+ const { values } = parseArgs({
30
+ options: {
31
+ html: { type: "boolean" },
32
+ url: { type: "boolean" },
33
+ help: { type: "boolean" }
34
+ }
35
+ });
36
+ if (values.help) {
37
+ console.log(`
38
+ Usage: graphics-debug [options]
39
+
40
+ Options:
41
+ --html Output a single HTML file with all graphics
42
+ --url Print a url to view the graphics in a browser
43
+ --help Show this help message
44
+
45
+ Examples:
46
+ cat debug.log | graphics-debug
47
+ echo '{ graphics: { points: [{x: 0, y: 0}] } }' | graphics-debug --html
48
+ `);
49
+ process.exit(0);
50
+ }
51
+ const input = await getInput();
52
+ if (values.html) {
53
+ const html = getHtmlFromLogString(input);
54
+ writeFileSync("graphicsdebug.debug.html", html);
55
+ console.log('Wrote to "graphicsdebug.debug.html"');
56
+ } else if (values.url) {
57
+ const { url } = await fetch("https://gdstore.seve.workers.dev/store", {
58
+ method: "POST",
59
+ body: JSON.stringify({
60
+ graphicsObjects: getGraphicsObjectsFromLogString(input)
61
+ }),
62
+ headers: {
63
+ "Content-Type": "application/json"
64
+ }
65
+ }).then((res) => res.json());
66
+ } else {
67
+ const svgs = getSvgsFromLogString(input);
68
+ svgs.forEach((svg, i) => {
69
+ const filename = `${svg.title.toLowerCase().replace(/\s+/g, "-")}-${i + 1}.debug.svg`;
70
+ writeFileSync(filename, svg.svg);
71
+ console.log(`Wrote to "${filename}"`);
72
+ });
73
+ }
74
+ }
75
+ main().catch((err) => {
76
+ console.error("Error:", err);
77
+ process.exit(1);
78
+ });
79
+ //# sourceMappingURL=cli.js.map
@@ -0,0 +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 chunks.push(chunk)\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 { 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 // console.log(\"https://graphicsdebug.com/\")\n // console.log(`Debug Graphics: ${url}`)\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,OAAO;AACvB,YAAQ;AAAA,MACN;AAAA,IACF;AACA,YAAQ,KAAK,CAAC;AAAA,EAChB;AAEA,QAAM,SAAS,CAAC;AAChB,mBAAiB,SAAS,QAAQ,OAAO;AACvC,WAAO,KAAK,KAAK;AAAA,EACnB;AACA,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,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;AAAA,EAI7B,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":[]}
package/dist/cli.js ADDED
@@ -0,0 +1,386 @@
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
@@ -0,0 +1 @@
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"]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ getGraphicsObjectsFromLogString
3
+ } from "../chunk-646ISP7J.js";
4
+ export {
5
+ getGraphicsObjectsFromLogString
6
+ };
7
+ //# sourceMappingURL=getGraphicsObjectsFromLogString.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,7 @@
1
+ import {
2
+ getSvgFromGraphicsObject
3
+ } from "../chunk-G7UBPMLZ.js";
4
+ export {
5
+ getSvgFromGraphicsObject
6
+ };
7
+ //# sourceMappingURL=getSvgFromGraphicsObject.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,19 @@
1
+ import {
2
+ getHtmlFromLogString,
3
+ getSvgFromLogString,
4
+ getSvgsFromLogString
5
+ } from "../chunk-GDKEOGZB.js";
6
+ import {
7
+ getGraphicsObjectsFromLogString
8
+ } from "../chunk-646ISP7J.js";
9
+ import {
10
+ getSvgFromGraphicsObject
11
+ } from "../chunk-G7UBPMLZ.js";
12
+ export {
13
+ getGraphicsObjectsFromLogString,
14
+ getHtmlFromLogString,
15
+ getSvgFromGraphicsObject,
16
+ getSvgFromLogString,
17
+ getSvgsFromLogString
18
+ };
19
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
package/package.json ADDED
@@ -0,0 +1,36 @@
1
+ {
2
+ "name": "graphics-debug",
3
+ "main": "dist/lib/index.js",
4
+ "type": "module",
5
+ "version": "0.0.1",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "bin": {
10
+ "graphics-debug": "./dist/cli/cli.js",
11
+ "gd": "./dist/cli/cli.js"
12
+ },
13
+ "scripts": {
14
+ "build": "tsup-node ./cli ./lib --format esm --sourcemap",
15
+ "format": "biome format --write .",
16
+ "format:check": "biome format ."
17
+ },
18
+ "devDependencies": {
19
+ "@biomejs/biome": "^1.9.4",
20
+ "@types/bun": "latest",
21
+ "@types/pretty": "^2.0.3",
22
+ "@types/react": "^18.3.12",
23
+ "@types/react-dom": "^18.3.1",
24
+ "bun-match-svg": "^0.0.8",
25
+ "commander": "^12.1.0",
26
+ "react": "^18.3.1",
27
+ "react-dom": "^18.3.1",
28
+ "tsup": "^8.3.5"
29
+ },
30
+ "peerDependencies": {
31
+ "typescript": "^5.0.0"
32
+ },
33
+ "dependencies": {
34
+ "pretty": "^2.0.0"
35
+ }
36
+ }