@plainviz/render-svg 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ export { render, renderBarChart } from './render';
2
+ export type { RenderOptions } from './render';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAClD,YAAY,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ export { render, renderBarChart } from './render';
@@ -0,0 +1,17 @@
1
+ /**
2
+ * PlainViz SVG Renderer
3
+ * Renders IR to SVG string (pure, no DOM dependency)
4
+ */
5
+ import type { PlainVizIR } from '@plainviz/core';
6
+ export interface RenderOptions {
7
+ width?: number;
8
+ height?: number;
9
+ padding?: number;
10
+ colors?: string[];
11
+ backgroundColor?: string;
12
+ textColor?: string;
13
+ gridColor?: string;
14
+ }
15
+ export declare function renderBarChart(ir: PlainVizIR, opts?: RenderOptions): string;
16
+ export declare function render(ir: PlainVizIR, opts?: RenderOptions): string;
17
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,WAAW,aAAa;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AA6BD,wBAAgB,cAAc,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CA4D/E;AAED,wBAAgB,MAAM,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,GAAE,aAAkB,GAAG,MAAM,CAYvE"}
package/dist/render.js ADDED
@@ -0,0 +1,87 @@
1
+ /**
2
+ * PlainViz SVG Renderer
3
+ * Renders IR to SVG string (pure, no DOM dependency)
4
+ */
5
+ const DEFAULT_COLORS = [
6
+ '#89b4fa', // Blue
7
+ '#a6e3a1', // Green
8
+ '#f9e2af', // Yellow
9
+ '#f38ba8', // Red
10
+ '#cba6f7', // Mauve
11
+ '#fab387', // Peach
12
+ ];
13
+ const DEFAULT_OPTIONS = {
14
+ width: 500,
15
+ height: 300,
16
+ padding: 60,
17
+ colors: DEFAULT_COLORS,
18
+ backgroundColor: '#1e1e2e',
19
+ textColor: '#cdd6f4',
20
+ gridColor: '#313244',
21
+ };
22
+ function escapeXml(str) {
23
+ return str
24
+ .replace(/&/g, '&')
25
+ .replace(/</g, '&lt;')
26
+ .replace(/>/g, '&gt;')
27
+ .replace(/"/g, '&quot;');
28
+ }
29
+ export function renderBarChart(ir, opts = {}) {
30
+ const options = { ...DEFAULT_OPTIONS, ...opts };
31
+ const { width, height, padding, colors, backgroundColor, textColor, gridColor } = options;
32
+ const chartWidth = width - padding * 2;
33
+ const chartHeight = height - padding * 2 - 30; // Reserve space for title
34
+ const maxValue = Math.max(...ir.values);
35
+ const barCount = ir.values.length;
36
+ const barWidth = (chartWidth / barCount) * 0.6;
37
+ const barGap = (chartWidth / barCount) * 0.4;
38
+ const lines = [];
39
+ // SVG header
40
+ lines.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" style="background-color: ${backgroundColor};">`);
41
+ // Title
42
+ if (ir.title) {
43
+ lines.push(` <text x="${width / 2}" y="25" text-anchor="middle" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" fill="${textColor}">${escapeXml(ir.title)}</text>`);
44
+ }
45
+ const chartTop = padding;
46
+ const chartBottom = height - padding;
47
+ // Y-axis
48
+ lines.push(` <line x1="${padding}" y1="${chartTop}" x2="${padding}" y2="${chartBottom}" stroke="${gridColor}" stroke-width="1"/>`);
49
+ // X-axis
50
+ lines.push(` <line x1="${padding}" y1="${chartBottom}" x2="${width - padding}" y2="${chartBottom}" stroke="${gridColor}" stroke-width="1"/>`);
51
+ // Grid lines (4 horizontal)
52
+ for (let i = 1; i <= 4; i++) {
53
+ const y = chartBottom - (chartHeight / 4) * i;
54
+ lines.push(` <line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}" stroke="${gridColor}" stroke-width="1" stroke-dasharray="3,3"/>`);
55
+ // Y-axis labels
56
+ const label = Math.round((maxValue / 4) * i);
57
+ lines.push(` <text x="${padding - 8}" y="${y + 4}" text-anchor="end" font-family="system-ui, sans-serif" font-size="11" fill="#6c7086">${label}</text>`);
58
+ }
59
+ // Bars
60
+ ir.values.forEach((value, i) => {
61
+ const barHeight = (value / maxValue) * chartHeight;
62
+ const x = padding + (chartWidth / barCount) * i + barGap / 2;
63
+ const y = chartBottom - barHeight;
64
+ const color = colors[i % colors.length];
65
+ // Bar
66
+ lines.push(` <rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" rx="4" fill="${color}"/>`);
67
+ // Value label
68
+ lines.push(` <text x="${x + barWidth / 2}" y="${y - 8}" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="${textColor}">${value}</text>`);
69
+ // X-axis label
70
+ lines.push(` <text x="${x + barWidth / 2}" y="${chartBottom + 18}" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6c7086">${escapeXml(ir.labels[i])}</text>`);
71
+ });
72
+ lines.push('</svg>');
73
+ return lines.join('\n');
74
+ }
75
+ export function render(ir, opts = {}) {
76
+ switch (ir.type) {
77
+ case 'bar':
78
+ return renderBarChart(ir, opts);
79
+ case 'line':
80
+ case 'pie':
81
+ case 'area':
82
+ // TODO: implement other chart types
83
+ throw new Error(`Chart type '${ir.type}' not yet implemented`);
84
+ default:
85
+ throw new Error(`Unknown chart type: ${ir.type}`);
86
+ }
87
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@plainviz/render-svg",
3
+ "version": "0.1.0",
4
+ "description": "PlainViz SVG renderer",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": "./dist/index.js",
11
+ "types": "./dist/index.d.ts"
12
+ }
13
+ },
14
+ "files": [
15
+ "dist",
16
+ "src"
17
+ ],
18
+ "scripts": {
19
+ "build": "tsc",
20
+ "dev": "tsc --watch"
21
+ },
22
+ "dependencies": {
23
+ "@plainviz/core": "workspace:*"
24
+ },
25
+ "devDependencies": {
26
+ "typescript": "^5.7.2"
27
+ },
28
+ "keywords": [
29
+ "plainviz",
30
+ "svg",
31
+ "render",
32
+ "chart"
33
+ ],
34
+ "license": "MIT"
35
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { render, renderBarChart } from './render';
2
+ export type { RenderOptions } from './render';
package/src/render.ts ADDED
@@ -0,0 +1,119 @@
1
+ /**
2
+ * PlainViz SVG Renderer
3
+ * Renders IR to SVG string (pure, no DOM dependency)
4
+ */
5
+
6
+ import type { PlainVizIR } from '@plainviz/core';
7
+
8
+ export interface RenderOptions {
9
+ width?: number;
10
+ height?: number;
11
+ padding?: number;
12
+ colors?: string[];
13
+ backgroundColor?: string;
14
+ textColor?: string;
15
+ gridColor?: string;
16
+ }
17
+
18
+ const DEFAULT_COLORS = [
19
+ '#89b4fa', // Blue
20
+ '#a6e3a1', // Green
21
+ '#f9e2af', // Yellow
22
+ '#f38ba8', // Red
23
+ '#cba6f7', // Mauve
24
+ '#fab387', // Peach
25
+ ];
26
+
27
+ const DEFAULT_OPTIONS: Required<RenderOptions> = {
28
+ width: 500,
29
+ height: 300,
30
+ padding: 60,
31
+ colors: DEFAULT_COLORS,
32
+ backgroundColor: '#1e1e2e',
33
+ textColor: '#cdd6f4',
34
+ gridColor: '#313244',
35
+ };
36
+
37
+ function escapeXml(str: string): string {
38
+ return str
39
+ .replace(/&/g, '&amp;')
40
+ .replace(/</g, '&lt;')
41
+ .replace(/>/g, '&gt;')
42
+ .replace(/"/g, '&quot;');
43
+ }
44
+
45
+ export function renderBarChart(ir: PlainVizIR, opts: RenderOptions = {}): string {
46
+ const options = { ...DEFAULT_OPTIONS, ...opts };
47
+ const { width, height, padding, colors, backgroundColor, textColor, gridColor } = options;
48
+
49
+ const chartWidth = width - padding * 2;
50
+ const chartHeight = height - padding * 2 - 30; // Reserve space for title
51
+ const maxValue = Math.max(...ir.values);
52
+ const barCount = ir.values.length;
53
+ const barWidth = (chartWidth / barCount) * 0.6;
54
+ const barGap = (chartWidth / barCount) * 0.4;
55
+
56
+ const lines: string[] = [];
57
+
58
+ // SVG header
59
+ lines.push(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${width} ${height}" style="background-color: ${backgroundColor};">`);
60
+
61
+ // Title
62
+ if (ir.title) {
63
+ lines.push(` <text x="${width / 2}" y="25" text-anchor="middle" font-family="system-ui, sans-serif" font-size="16" font-weight="bold" fill="${textColor}">${escapeXml(ir.title)}</text>`);
64
+ }
65
+
66
+ const chartTop = padding;
67
+ const chartBottom = height - padding;
68
+
69
+ // Y-axis
70
+ lines.push(` <line x1="${padding}" y1="${chartTop}" x2="${padding}" y2="${chartBottom}" stroke="${gridColor}" stroke-width="1"/>`);
71
+
72
+ // X-axis
73
+ lines.push(` <line x1="${padding}" y1="${chartBottom}" x2="${width - padding}" y2="${chartBottom}" stroke="${gridColor}" stroke-width="1"/>`);
74
+
75
+ // Grid lines (4 horizontal)
76
+ for (let i = 1; i <= 4; i++) {
77
+ const y = chartBottom - (chartHeight / 4) * i;
78
+ lines.push(` <line x1="${padding}" y1="${y}" x2="${width - padding}" y2="${y}" stroke="${gridColor}" stroke-width="1" stroke-dasharray="3,3"/>`);
79
+
80
+ // Y-axis labels
81
+ const label = Math.round((maxValue / 4) * i);
82
+ lines.push(` <text x="${padding - 8}" y="${y + 4}" text-anchor="end" font-family="system-ui, sans-serif" font-size="11" fill="#6c7086">${label}</text>`);
83
+ }
84
+
85
+ // Bars
86
+ ir.values.forEach((value, i) => {
87
+ const barHeight = (value / maxValue) * chartHeight;
88
+ const x = padding + (chartWidth / barCount) * i + barGap / 2;
89
+ const y = chartBottom - barHeight;
90
+ const color = colors[i % colors.length];
91
+
92
+ // Bar
93
+ lines.push(` <rect x="${x}" y="${y}" width="${barWidth}" height="${barHeight}" rx="4" fill="${color}"/>`);
94
+
95
+ // Value label
96
+ lines.push(` <text x="${x + barWidth / 2}" y="${y - 8}" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="${textColor}">${value}</text>`);
97
+
98
+ // X-axis label
99
+ lines.push(` <text x="${x + barWidth / 2}" y="${chartBottom + 18}" text-anchor="middle" font-family="system-ui, sans-serif" font-size="11" fill="#6c7086">${escapeXml(ir.labels[i])}</text>`);
100
+ });
101
+
102
+ lines.push('</svg>');
103
+
104
+ return lines.join('\n');
105
+ }
106
+
107
+ export function render(ir: PlainVizIR, opts: RenderOptions = {}): string {
108
+ switch (ir.type) {
109
+ case 'bar':
110
+ return renderBarChart(ir, opts);
111
+ case 'line':
112
+ case 'pie':
113
+ case 'area':
114
+ // TODO: implement other chart types
115
+ throw new Error(`Chart type '${ir.type}' not yet implemented`);
116
+ default:
117
+ throw new Error(`Unknown chart type: ${ir.type}`);
118
+ }
119
+ }