@graphysdk/node-renderer 1.8.1-beta.1786024899180

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/LICENSE.md ADDED
@@ -0,0 +1,17 @@
1
+ # License
2
+
3
+ This software is available under any of the following licenses, at your option,
4
+ depending on which one's conditions you meet:
5
+
6
+ - **PolyForm Noncommercial 1.0.0** — for noncommercial use (personal,
7
+ charitable, educational, research, government).
8
+ See [licenses/PolyForm-NonCommercial-1.0.0.md](licenses/PolyForm-NonCommercial-1.0.0.md)
9
+
10
+ - **PolyForm Small Business 1.0.0** — free commercial use for organizations
11
+ under the size thresholds defined in the license.
12
+ See [licenses/PolyForm-Small-Business-1.0.0.md](licenses/PolyForm-Small-Business-1.0.0.md)
13
+
14
+ - **PolyForm Free Trial 1.0.0** — a 32-day evaluation for anyone else.
15
+ See [licenses/PolyForm-Free-Trial-1.0.0.md](licenses/PolyForm-Free-Trial-1.0.0.md)
16
+
17
+ For commercial use beyond these terms, contact [hello@graphy.dev](mailto:hello@graphy.dev) or visit [https://graphy.dev](https://graphy.dev).
package/README.md ADDED
@@ -0,0 +1,88 @@
1
+ # @graphysdk/node-renderer
2
+
3
+ Headless chart renderer for Node.js. Compiles specs via `@graphysdk/viz-engine` and paints to PNG using `@napi-rs/canvas` (Skia).
4
+
5
+ ## Docs
6
+
7
+ - [Provider and renderer](https://docs.graphy.dev/sdk-next/rendering/provider-and-renderer)
8
+ - [Quickstart](https://docs.graphy.dev/sdk-next/quickstart)
9
+
10
+ ## Serverless (Lambda, Vercel, etc.)
11
+
12
+ This package is designed for ephemeral Node runtimes:
13
+
14
+ - **Native module at runtime:** `@napi-rs/canvas` ships platform prebuilds via npm optional dependencies. Do not bundle it into your serverless JS artifact — keep it as a `dependencies` install so the correct `.node` binary is present on the target OS/arch.
15
+ - **Lazy load:** The Skia binding loads on the first `renderGraphToPng` call, not when the module is imported.
16
+ - **No temp files:** Rendering is in-memory (`Buffer` in, PNG `Buffer` out).
17
+ - **Inter via npm:** Default fonts load from `@fontsource/inter` at runtime using `require.resolve` (WOFF files in `node_modules`). No writable disk or checked-in TTFs required.
18
+
19
+ Host apps using Nitro, esbuild, or similar should treat `@graphysdk/node-renderer`, `@napi-rs/canvas`, and `@fontsource/inter` as **external** runtime dependencies (same as other native addons). For Nitro on Vercel, include `@fontsource/inter` in `traceDeps` so font files are copied into the function bundle:
20
+
21
+ ```ts
22
+ traceDeps: ['@graphysdk/node-renderer', '@napi-rs/canvas', '@fontsource/inter'],
23
+ ```
24
+
25
+ ## Fonts
26
+
27
+ Text measurement and drawing use the same `@napi-rs/canvas` context with family **`Inter`** (aligned with the browser renderer).
28
+
29
+ 1. **Default (automatic):** `ensureDefaultFonts()` registers Latin Inter 400 and 600 from `@fontsource/inter` on first render. Works on Vercel as long as `@fontsource/inter` is installed (declared as a dependency of this package).
30
+
31
+ 2. **Optional `assets/` override:** place TTF files at `assets/Inter-Regular.ttf` and `assets/Inter-SemiBold.ttf` beside the installed package if you need custom binaries instead of Fontsource.
32
+
33
+ 3. **In-memory:** pass `fonts` in `RenderOptions` or call `registerFont({ family, data: Buffer })`. Optional `weight` / `style` on `FontRegistration` are dedup-cache keys only — Skia reads metrics from the font bytes.
34
+
35
+ Without registered fonts, Skia uses built-in fallbacks; layout may differ from the browser renderer.
36
+
37
+ ## Usage
38
+
39
+ ### Low-level spec API
40
+
41
+ ```ts
42
+ import { renderGraphToPng } from '@graphysdk/node-renderer';
43
+ import { createSpec, geom, pipe, scale } from '@graphysdk/viz-engine';
44
+
45
+ const input = pipe(createSpec({ x: 'category', y: 'value' }), geom.bar(), scale.x(), scale.y());
46
+
47
+ const png = await renderGraphToPng({ input, data }, { width: 1200, height: 800, theme: 'light' });
48
+ ```
49
+
50
+ ### Stored `GraphConfig` (agents / Redis shape)
51
+
52
+ Core- and API-shaped configs embed `data` on the object. Use the capability guard before rendering in production so you can fall back to Microlink when needed:
53
+
54
+ ```ts
55
+ import { canRenderConfigWithCanvas, renderChartConfigToPng } from '@graphysdk/node-renderer';
56
+
57
+ const capability = canRenderConfigWithCanvas(storedConfig);
58
+ if (!capability.ok) {
59
+ // fall back to browser screenshot path
60
+ }
61
+
62
+ const png = await renderChartConfigToPng(storedConfig, { width: 1200, height: 800, theme: 'light' });
63
+ ```
64
+
65
+ `renderChartConfigToPng` compiles via `@graphysdk/viz-engine` (same `GraphConfig` → spec conversion as the React renderer). Conversion is **lossy** — see the matrix below.
66
+
67
+ ## Canvas capability matrix
68
+
69
+ | Category | Details |
70
+ | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
71
+ | **Supported `type`** | `column`, `columnStacked`, `columnStackedFill`, `bar`, `barStacked`, `barStackedFill`, `line`, `areaStacked`, `pie`, `donut`, `scatter`, `bubble`, `combo` |
72
+ | **Unsupported `type`** | `funnel`, `heatmap`, `waterfall`, `mekko`, `table` (viz-engine throws) |
73
+ | **Canvas geoms** | Cartesian: bar, line, area, point, rule — Polar: bar (pie/donut) |
74
+ | **Rejected up front** | `_unstable_mapping`; `referenceLines.trendline` / `referenceLines.averageLine` |
75
+ | **Supported referenceLines** | `goalLine` (compiled to `geom.rule`) |
76
+ | **Legend display** | `pill` (color, stacked swatches, bubble/size); `direct` (series labels on panel edge) |
77
+ | **Lossy at compile** | Annotations, `themeOverrides`, series styles, headline numbers (see viz-engine `graph-config.converter`) |
78
+ | **Visual deltas vs browser** | Font metrics, anti-aliasing |
79
+
80
+ `CANVAS_SUPPORTED_GRAPH_TYPES`, `CANVAS_UNSUPPORTED_GRAPH_TYPES`, and `CANVAS_CAPABILITY_MATRIX` are exported from the package entrypoint.
81
+
82
+ ## Dev server
83
+
84
+ ```bash
85
+ pnpm --filter @graphysdk/node-renderer dev
86
+ ```
87
+
88
+ Serves preset chart PNGs at `http://localhost:4310` (see `src/dev/server.ts`).
File without changes
package/dist/index.cjs ADDED
@@ -0,0 +1 @@
1
+ "use strict";var it=Object.create;var ue=Object.defineProperty;var st=Object.getOwnPropertyDescriptor;var at=Object.getOwnPropertyNames;var lt=Object.getPrototypeOf,dt=Object.prototype.hasOwnProperty;var ct=(e,t,n,o)=>{if(t&&typeof t=="object"||typeof t=="function")for(let i of at(t))!dt.call(e,i)&&i!==n&&ue(e,i,{get:()=>t[i],enumerable:!(o=st(t,i))||o.enumerable});return e};var ut=(e,t,n)=>(n=e!=null?it(lt(e)):{},ct(t||!e||!e.__esModule?ue(n,"default",{value:e,enumerable:!0}):n,e));Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const fe=require("node:fs"),Le=require("node:module"),D=require("node:path"),ft=require("node:url"),gt=require("@graphysdk/viz-engine/graph-config"),N=require("d3-shape"),c=require("@graphysdk/viz-engine"),ht=require("node:buffer");var w=typeof document<"u"?document.currentScript:null;let U,J;const _e=()=>U?Promise.resolve(U):(J??=import("@napi-rs/canvas").then(e=>(U=e,e)).catch(e=>{throw J=void 0,e}),J),W=()=>{if(!U)throw new Error("Canvas runtime is not loaded. Call loadCanvasRuntime() before rendering.");return U},y="Inter",x=new Set;let ge=!1,q=!1;const we=e=>`${e.family}|${e.weight??400}|${e.style??"normal"}`,re=e=>{const t=we(e);x.has(t)||(W().GlobalFonts.register(e.data,e.family),x.add(t))},mt=[{file:"Inter-Regular.ttf",family:y,weight:400},{file:"Inter-SemiBold.ttf",family:y,weight:600}],bt=[{file:"inter-latin-400-normal.woff",family:y,weight:400},{file:"inter-latin-600-normal.woff",family:y,weight:600}],At="@fontsource/inter",pt=()=>{try{const e=Le.createRequire(typeof document>"u"?require("url").pathToFileURL(__filename).href:w&&w.tagName.toUpperCase()==="SCRIPT"&&w.src||new URL("index.cjs",document.baseURI).href),t=D.dirname(e.resolve("@graphysdk/node-renderer/package.json"));return D.join(t,"assets")}catch{return D.resolve(D.dirname(ft.fileURLToPath(typeof document>"u"?require("url").pathToFileURL(__filename).href:w&&w.tagName.toUpperCase()==="SCRIPT"&&w.src||new URL("index.cjs",document.baseURI).href)),"../assets")}},Ce=(e,t,n)=>{try{return fe.existsSync(e)?(re({family:t,data:fe.readFileSync(e),weight:n}),!0):!1}catch{return!1}},Tt=()=>{const e=pt();for(const{file:t,family:n,weight:o}of mt)Ce(D.join(e,t),n,o)&&(q=!0)},St=()=>{const e=Le.createRequire(typeof document>"u"?require("url").pathToFileURL(__filename).href:w&&w.tagName.toUpperCase()==="SCRIPT"&&w.src||new URL("index.cjs",document.baseURI).href);for(const{file:t,family:n,weight:o}of bt)if(!x.has(we({family:n,data:Buffer.alloc(0),weight:o})))try{const i=e.resolve(`${At}/files/${t}`);Ce(i,n,o)&&(q=!0)}catch{}},Ie=()=>(ge||(ge=!0,Tt(),St()),q),C=(e,t)=>e.x+t*e.width,_=(e,t)=>e.y+(1-t)*e.height,ee=e=>e==="catmull-rom"?N.curveCatmullRom:N.curveLinear,Be=e=>{switch(e){case"dashed":return[8,4];case"dotted":return[2,2];case"solid":return[]}},k=e=>e,yt=(e,t)=>t!=="connect"?e:e.filter(n=>c.getX(n)!==null&&c.getY(n)!==null),Rt=(e,t,n)=>{const o=ee(t.interpolate),i=N.area().curve(o),r=N.line().curve(o);if(e.mainAxis==="y"){const s=l=>_(n,c.getY(l)??0),a=l=>C(n,c.getXMin(l)??0),d=l=>C(n,c.getXMax(l)??c.getX(l)??0);i.y(s).x0(a).x1(d),r.y(s).x(d)}else{const s=l=>C(n,c.getX(l)??0),a=l=>_(n,c.getYMin(l)??0),d=l=>_(n,c.getYMax(l)??c.getY(l)??0);i.x(s).y0(a).y1(d),r.x(s).y(d)}if(t.missingValues==="gap"){const s=a=>c.getX(a)!==null&&c.getY(a)!==null;i.defined(s),r.defined(s)}return{areaGenerator:i,lineGenerator:r}},Et=k({geom:"area",coord:"cartesian",draw:(e,{layer:t,coordSystem:n,panel:o,colorScheme:i})=>{const r=c.createStyleResolver({colorScheme:i}).geomReaders(t),{areaGenerator:s,lineGenerator:a}=Rt(n,t.params,o),d=e;t.data.groupBy(c.GROUP_VARIABLES.group).forEach(l=>{const u=yt([...l],t.params.missingValues),f=u[0];if(!f)return;const g=r.get("color",f),h=r.get("alpha",f),m=r.get("strokeWidth",f),A=Be(r.get("lineType",f));e.save(),e.globalAlpha=h,e.fillStyle=g,e.beginPath(),s.context(d)(u),e.fill(),e.restore(),e.save(),e.globalAlpha=r.get("strokeAlpha",f),e.strokeStyle=g,e.lineWidth=m,e.setLineDash(A),e.lineJoin="round",e.lineCap="round",e.beginPath(),a.context(d)(u),e.stroke(),e.restore()})}}),he=(e,t,n,o,i,r)=>{const s=Math.max(0,Math.min(r.rx??r.ry??0,o/2)),a=Math.max(0,Math.min(r.ry??r.rx??0,i/2)),d=Math.PI/2;e.moveTo(t+s,n),e.lineTo(t+o-s,n),e.ellipse(t+o-s,n+a,s,a,0,-d,0),e.lineTo(t+o,n+i-a),e.ellipse(t+o-s,n+i-a,s,a,0,0,d),e.lineTo(t+s,n+i),e.ellipse(t+s,n+i-a,s,a,0,d,Math.PI),e.lineTo(t,n+a),e.ellipse(t+s,n+a,s,a,0,Math.PI,Math.PI*1.5),e.closePath()},Lt=k({geom:"bar",coord:"cartesian",draw:(e,{layer:t,coordSystem:n,panel:o,colorScheme:i})=>{const{Path2D:r}=W(),s=c.createStyleResolver({colorScheme:i}).geomReaders(t),a=n.mainAxis,d=new Map;for(const l of t.data){const u=c.getBarRectBounds(a,l);if(!u)continue;const f=C(o,u.x),g=o.y+u.y*o.height,h={x:f,y:g,width:u.width*o.width,height:u.height*o.height,fill:s.get("color",l),opacity:s.get("alpha",l)},m=c.buildColumnKey(a,u),A=d.get(m);A?A.bars.push(h):d.set(m,{bars:[h],style:{borderColor:s.get("borderColor",l),borderWidth:s.get("borderWidth",l),borderRadius:s.get("borderRadius",l),borderAlpha:s.get("alpha",l)}})}for(const{bars:l,style:u}of d.values()){const{borderColor:f,borderRadius:g,borderWidth:h,borderAlpha:m}=u,A=c.resolveBarBorderTreatment(h),p=g==="full"?void 0:c.resolveBarCornerRadiiPx({borderRadius:g,mainAxis:a});if(!(l.length>1)){const[b]=l;if(!b)continue;const v=p??c.resolveBarCornerRadiiPx({borderRadius:g,mainAxis:a,bounds:{width:b.width,height:b.height}}),E=new r;he(E,b.x,b.y,b.width,b.height,v),e.save(),e.globalAlpha=b.opacity,e.fillStyle=b.fill,e.fill(E),f!==void 0&&(e.clip(E),e.strokeStyle=f,e.lineWidth=A.outlineWidth,e.stroke(E)),e.restore();continue}const T=c.getBoundingRect(l),L=p??c.resolveBarCornerRadiiPx({borderRadius:g,mainAxis:a,bounds:{width:T.width,height:T.height}}),S=new r;he(S,T.x,T.y,T.width,T.height,L),e.save(),e.clip(S);for(const b of l)e.globalAlpha=b.opacity,e.fillStyle=b.fill,e.fillRect(b.x,b.y,b.width,b.height),f!==void 0&&(e.strokeStyle=f,e.lineWidth=A.separatorWidth,e.strokeRect(b.x,b.y,b.width,b.height));f!==void 0&&(e.globalAlpha=m,e.strokeStyle=f,e.lineWidth=A.outlineWidth,e.stroke(S)),e.restore()}}}),me=(e,t)=>{const n=/^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(e.trim());if(n?.[1]!==void 0){const i=n[1],r=i.length===3?[...i].map(l=>l+l).join(""):i.slice(0,6),s=Number.parseInt(r.slice(0,2),16),a=Number.parseInt(r.slice(2,4),16),d=Number.parseInt(r.slice(4,6),16);return`rgba(${s}, ${a}, ${d}, ${t})`}const o=/^rgba?\(([^)]+)\)$/i.exec(e.trim());if(o?.[1]!==void 0){const i=o[1].split(",").map(d=>d.trim()),[r,s,a]=i;if(r!==void 0&&s!==void 0&&a!==void 0)return`rgba(${r}, ${s}, ${a}, ${t})`}return t===0?"rgba(0, 0, 0, 0)":e},_t=(e,t)=>t!=="connect"?e:e.filter(n=>c.getX(n)!==null&&c.getY(n)!==null),wt=(e,t)=>{const{seriesObservations:n,fillGenerator:o,panel:i,color:r,fillAlpha:s}=t,a=n.map(l=>c.getY(l)).filter(l=>l!==null).map(l=>_(i,l));if(a.length===0)return;const d=e.createLinearGradient(0,Math.min(...a),0,i.y+i.height);d.addColorStop(0,r),d.addColorStop(.7,me(r,0)),d.addColorStop(1,me(r,0)),e.save(),e.globalAlpha=s,e.fillStyle=d,e.beginPath(),o.context(e)(n),e.fill(),e.restore()},Ct=k({geom:"line",coord:"cartesian",draw:(e,{layer:t,panel:n,colorScheme:o})=>{const i=c.createStyleResolver({colorScheme:o}).geomReaders(t),r=N.line().x(a=>C(n,c.getX(a)??0)).y(a=>_(n,c.getY(a)??0)).curve(ee(t.params.interpolate)),s=N.area().x(a=>C(n,c.getX(a)??0)).y0(n.y+n.height).y1(a=>_(n,c.getY(a)??0)).curve(ee(t.params.interpolate));if(t.params.missingValues==="gap"){const a=d=>c.getX(d)!==null&&c.getY(d)!==null;r.defined(a),s.defined(a)}t.data.groupBy(c.GROUP_VARIABLES.group).forEach(a=>{const d=_t([...a],t.params.missingValues),l=d[0];if(!l)return;const u=i.get("strokeWidth",l),f=Be(i.get("lineType",l)),g=i.get("color",l),h=i.get("fillAlpha",l);h!==void 0&&wt(e,{seriesObservations:d,fillGenerator:s,panel:n,color:g,fillAlpha:h}),e.save(),e.globalAlpha=i.get("alpha",l),e.strokeStyle=g,e.lineWidth=u,e.setLineDash(f),e.lineJoin="round",e.lineCap="round",e.beginPath(),r.context(e)(d),e.stroke(),e.restore()})}}),It=k({geom:"point",coord:"cartesian",draw:(e,{layer:t,panel:n,colorScheme:o})=>{const i=c.createStyleResolver({colorScheme:o}).geomReaders(t);for(const r of t.data){const s=c.getX(r),a=c.getY(r);if(s===null||a===null)continue;const d=C(n,s),l=_(n,a),u=i.get("size",r)/2,f=i.get("borderWidth",r);e.save(),e.globalAlpha=i.get("alpha",r),e.fillStyle=i.get("color",r),e.strokeStyle=i.get("borderColor",r),e.lineWidth=f,e.beginPath(),e.arc(d,l,u,0,Math.PI*2),e.fill(),f>0&&e.stroke(),e.restore()}}}),ie=11,Bt="sans-serif",Nt=500,P=5,Ne=5,te=13,ve=6,V=12,be=21.6,vt=(e,t)=>{const n=e.mapping.y!==void 0,o=t.mainAxis==="y";return n!==o},Pt=(e,t,n)=>{const o=c.getRuleDashPattern(t,n);e.setLineDash(o),o.length>0&&(e.lineCap="round")},Ft=(e,t)=>X(e)&&X(t)?`${e}: ${t}`:X(e)?e:X(t)?t:null,X=e=>e!=null&&e.trim()!=="",kt=e=>{e.beginPath(),e.moveTo(2.48935,16.9323),e.lineTo(9.07098,9.82964),e.bezierCurveTo(9.51595,9.34944,9.51504,8.60728,9.0689,8.12818),e.lineTo(2.48866,1.06172),e.bezierCurveTo(1.85811,.384586,.974477,0,.049217,0),e.lineTo(0,0),e.lineTo(0,18),e.lineTo(.0443689,18),e.bezierCurveTo(.972463,18,1.85854,17.6131,2.48935,16.9323),e.closePath()},Pe=(e,t,n,o,i,r)=>{const s=Math.min(r,o/2,i/2);e.beginPath(),e.moveTo(t+s,n),e.lineTo(t+o-s,n),e.arcTo(t+o,n,t+o,n+s,s),e.lineTo(t+o,n+i-s),e.arcTo(t+o,n+i,t+o-s,n+i,s),e.lineTo(t+s,n+i),e.arcTo(t,n+i,t,n+i-s,s),e.lineTo(t,n+s),e.arcTo(t,n,t+s,n,s),e.closePath(),e.fill()},Dt=(e,t,n,o,i,r,s,a)=>{const d=n+P*2,l=ie+Ne*2,u=o==="start",f=u?r.x:r.x+r.width,g=_(r,i),h=u?-te:-(d-te),m=u?h+d-P:h+P-V;e.save(),e.translate(f,g),e.translate(0,-l/2),e.fillStyle=s,Pe(e,h,0,d,l,ve),e.save(),u||(e.translate(m+V/2,l/2),e.rotate(Math.PI),e.translate(-(m+V/2),-l/2)),e.translate(m,(l-be)/2),e.scale(V/10,be/18),e.fillStyle=s,kt(e),e.fill(),e.restore(),e.fillStyle=a,e.textAlign="left",e.textBaseline="middle",e.fillText(t,h+P,l/2),e.restore()},Ot=(e,t,n,o,i,r,s,a)=>{const d=n+P*2,l=ie+Ne*2,u=o==="start",f=C(r,i),g=u?r.y+r.height:r.y,h=u?-te:-8;e.save(),e.translate(f,g),e.translate(-d/2,0),e.fillStyle=s,Pe(e,0,h,d,l,ve),e.fillStyle=a,e.textAlign="left",e.textBaseline="middle",e.fillText(t,P,h+l/2),e.restore()},Ut=k({geom:"rule",coord:"cartesian",draw:(e,{layer:t,coordSystem:n,panel:o,compiled:i,textMeasurer:r,formattingLocale:s,colorScheme:a})=>{const d=t.data.getFirst();if(!d)return;const l=vt(t,n),u=l?c.getY(d):c.getX(d);if(u===null)return;const f=c.createStyleResolver({colorScheme:a}).geomReaders(t),g=f.get("color",d),h=c.getRuleLabelTextColor(g),m=f.get("strokeWidth",d);if(e.save(),e.strokeStyle=g,e.lineWidth=m,Pt(e,f.get("lineType",d),m),e.beginPath(),l){const S=_(o,u);e.moveTo(o.x,S),e.lineTo(o.x+o.width,S)}else{const S=C(o,u);e.moveTo(S,o.y),e.lineTo(S,o.y+o.height)}e.stroke(),e.restore();const A=Ft(t.params.label,c.formatRuleValue({guides:i.guides,numberFormat:i.config.numberFormat,layer:t,locale:s??i.config.parsingLocale}));if(A===null)return;const p=i.config.appearance.textScale,R={family:Bt,size:ie*p,weight:Nt};e.font=c.buildFontString(R);const T=r.measureText(A,R).width;(l?Dt:Ot)(e,A,T,t.params.labelPosition,u,o,g,h)}}),O=N.arc();O.digits(8);const Ht=k({geom:"bar",coord:"polar",draw:(e,{layer:t,coordSystem:n,panel:o,colorScheme:i})=>{const r=o.x+o.width/2,s=o.y+o.height/2,a=Math.min(o.width,o.height)/2,{Path2D:d}=W(),l=c.createStyleResolver({colorScheme:i}).geomReaders(t);e.save(),e.translate(r,s),e.scale(a,a);const u=e,f=new Map;for(const g of t.data){const{startAngle:h,endAngle:m}=c.getAngleExtent(g),{innerRadius:A,outerRadius:p}=c.getRadiusExtent(g);if(h===null||m===null||A===null||p===null)continue;const R={startAngle:h,endAngle:m,innerRadius:A,outerRadius:p,fill:l.get("color",g),opacity:l.get("alpha",g)},T=c.buildSliceGroupKey(n.bandAxis,R),L=f.get(T);L?L.items.push(R):f.set(T,{items:[R],style:{borderColor:l.get("borderColor",g),borderWidth:l.get("borderWidth",g),borderRadius:l.get("borderRadius",g),borderAlpha:l.get("alpha",g)}})}for(const{items:g,style:h}of f.values()){const{borderColor:m,borderRadius:A,borderWidth:p,borderAlpha:R}=h,T=c.resolveBarBorderTreatment(p),L=m!==void 0&&a>0,S=c.getUnionExtent(g),b=c.resolvePolarBarCornerRadiusUnit(A,S.outerRadius-S.innerRadius),v=a>0&&(L||b>0);e.save();let E;v&&(E=new d,O.cornerRadius(b),O.context(E)(S),e.clip(E)),O.cornerRadius(0);for(const Q of g)e.globalAlpha=Q.opacity,e.fillStyle=Q.fill,e.beginPath(),O.context(u)(Q),e.fill(),L&&(e.strokeStyle=m,e.lineWidth=T.separatorWidth/a,e.stroke());L&&E&&(e.globalAlpha=R,e.strokeStyle=m,e.lineWidth=T.outlineWidth/a,e.stroke(E)),e.restore()}e.restore()}}),Mt={bar:{cartesian:Lt,polar:Ht},line:{cartesian:Ct},area:{cartesian:Et},point:{cartesian:It},rule:{cartesian:Ut}},Fe=(e,t)=>Mt[e.geom]?.[t.type]??null,Gt=(e,t,n)=>{e.draw(t,n)};let Ae;const ke=()=>(Ae??=c.createCompiler(),Ae),se=["line","areaStacked","bar","barStacked","barStackedFill","column","columnStacked","columnStackedFill","combo","pie","donut","scatter","bubble"],De=["funnel","heatmap","waterfall","mekko","table"],Wt=new Set(se),Vt={supportedGraphTypes:[...se],unsupportedGraphTypes:[...De],supportedGeoms:{cartesian:["bar","line","area","point","rule"],polar:["bar"]}},B=e=>({ok:!1,reason:e}),j=e=>typeof e=="object"&&e!==null&&!Array.isArray(e),Xt=e=>{if(!j(e))return!1;const{columns:t,rows:n}=e;return!Array.isArray(t)||!Array.isArray(n)?!1:t.every(o=>j(o)&&typeof o.key=="string")},Kt=e=>{if("_unstable_mapping"in e&&e._unstable_mapping!==void 0)return!0;const t=e.referenceLines;return j(t)?t.trendline!==void 0||t.averageLine!==void 0:!1},Oe=e=>{if(!j(e))return B("Chart config must be a plain object.");if(!Xt(e.data))return B("Chart config is missing a valid `data` object (columns + rows).");const{data:t,...n}=e;return{ok:!0,input:n,data:t}},Ue=e=>{const t=Oe(e);if(!t.ok)return t;if(Kt(e))return B("Canvas renderer does not support `_unstable_mapping`, `referenceLines.trendline`, or `referenceLines.averageLine`.");const n=t.input.type??"column";if(!Wt.has(n))return B(`Unsupported chart type for canvas rendering: ${n}.`);try{const o=ke(),i=gt.convertGraphConfig(t.input,t.data),r=o.compile({input:i,data:t.data});if(!r.ok)return B(`Chart config failed to compile: ${r.errors.map(s=>s.message).join("; ")}`);for(const s of r.compiled.layers)if(Fe(s,r.compiled.coordSystem)===null)return B(`No canvas geom renderer for ${s.geom} (${r.compiled.coordSystem.type} coord).`);return{ok:!0,compiled:r.compiled}}catch(o){const i=o instanceof Error?o.message:String(o);return B(`Chart config failed to compile: ${i}`)}},K=6,z=10,Yt=20,ae=y,le=12,He=y,H=12,de=1.3,M=10,G=12,F=6,Me=16,zt=24,Ge=6,We=3,ne=4,qt=8,jt="#9ca3af",Ve=16,Zt=1.3,Xe=13,Qt=1.4,Ke=12,Jt=1.4,Z=8,ce=12,$t=1.3,xt=200,en=120,tn=80,Ye=22,nn=9,on=11,rn=10,Y=9,pe=12,sn='-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif',an=11.5,ln=550,dn={light:.55,mid:.75,dark:.08},cn={light:.62,mid:.8,dark:.85},un=.22,fn=.65,gn=y,hn=12,mn={family:ae,size:le},bn={family:gn,size:hn},An={family:He,size:H},pn=8,Te=1,Tn=[2,3],Sn=[1,3],yn=(e,t,n,o,i)=>{if(!t.isVisible||t.ticks.length===0)return;const r=t.position==="bottom"||t.position==="top",s=t.ticksVisible?1:0;e.save(),e.font=c.buildFontString(mn),e.fillStyle=i.textSecondary,e.strokeStyle=i.gridLine,e.lineWidth=1,e.setLineDash([]);for(const a of t.ticks){const d=r?a.position:1-a.position;t.ticksVisible&&En(e,t.position,d,n,o),_n(e,t.position,d,n,o,a.formattedLabel,t.labelRotation,s)}e.restore()},Rn=(e,t,n,o)=>{if(e==="bottom"||e==="top"){const a=o.x+t*o.width,d=e==="top"?n.y+n.height:n.y,l=e==="top"?-K:K;return{x1:a,y1:d,x2:a,y2:d+l}}const i=o.y+t*o.height,r=e==="right"?n.x:n.x+n.width,s=e==="right"?K:-K;return{x1:r,y1:i,x2:r+s,y2:i}},En=(e,t,n,o,i)=>{const{x1:r,y1:s,x2:a,y2:d}=Rn(t,n,o,i);e.beginPath(),e.moveTo(r,s),e.lineTo(a,d),e.stroke()},Ln=(e,t,n,o,i)=>{if(e==="bottom"||e==="top"){const s=o.x+t*o.width;return e==="bottom"?{x:s,y:n.y+i,textAlign:"center",textBaseline:"top"}:{x:s,y:n.y+n.height-i,textAlign:"center",textBaseline:"alphabetic"}}const r=o.y+t*o.height;return e==="left"?{x:n.x+n.width-i,y:r,textAlign:"right",textBaseline:"middle"}:{x:n.x+i,y:r,textAlign:"left",textBaseline:"middle"}},_n=(e,t,n,o,i,r,s,a)=>{const d=z*a;if(s!==0&&(t==="top"||t==="bottom")){const u=i.x+n*i.width,f=t==="top";e.save(),e.translate(u,f?o.y+o.height-d:o.y+d),e.rotate(s*Math.PI/180),e.textAlign=f?"left":"right",e.textBaseline="middle",e.fillText(r,0,0),e.restore();return}const l=Ln(t,n,o,i,d);e.textAlign=l.textAlign,e.textBaseline=l.textBaseline,e.fillText(r,l.x,l.y)},wn=(e,t,n,o)=>{if(!t.isVisible||!t.label)return;e.save(),e.font=c.buildFontString(bn),e.fillStyle=o.textPrimary,e.textBaseline="middle";const{x:i,y:r,alignment:s}=Cn(t.position,n);e.textAlign=s,e.fillText(t.label,i,r),e.restore()},Cn=(e,t)=>{const n=t.y+t.height/2;return e==="left"?{x:t.x,y:n,alignment:"start"}:e==="right"?{x:t.x+t.width,y:n,alignment:"end"}:{x:t.x+t.width/2,y:n,alignment:"center"}},In="data:image/png;base64,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",I=Ye;let $;const Bn=()=>{if($)return $;const{Image:e}=W(),t=new e,n=In.replace(/^data:image\/png;base64,/,"");return t.src=ht.Buffer.from(n,"base64"),$=t,t},ze=(e,t,n="full")=>!e||t.width<en||t.height<tn?"hidden":t.width<xt||n==="mini"?"mini":"full",Nn=e=>{const t=vn(e);if(t===null)return"light";const n=Pn(t);return n<=un?"dark":n<=fn?"mid":"light"},oe=(e,t,n)=>e.placement!==n||ze(e.enabled,t,e.variant)==="hidden"?0:Ye,qe=(e,t,n,o,i,r)=>{if(t.placement!==r)return;const s=ze(t.enabled,i,t.variant);if(s==="hidden")return;const a=Nn(o.background),d=a==="dark",l={family:sn,size:an,weight:400},u={...l,weight:ln};let f=0;s==="full"&&(e.font=c.buildFontString(l),f=e.measureText("Made with ").width,e.font=c.buildFontString(u),f+=e.measureText("Graphy").width);const g=s==="mini"?0:nn,h=s==="mini"?0:on,m=s==="mini"?0:rn,A=s==="mini"?I:g+Y+m+f+h,p=n.x+n.width-A,R=n.y+Math.max(0,(n.height||I)/2-I/2);e.save(),e.globalAlpha=cn[a],e.textBaseline="middle",e.textAlign="left",d||(e.shadowColor="rgba(0, 0, 0, 0.09)",e.shadowBlur=4,e.shadowOffsetY=1),e.beginPath(),e.roundRect(p,R,A,I,I/2),e.fillStyle=`rgba(255, 255, 255, ${dn[a]})`,e.fill(),e.shadowColor="transparent",e.shadowBlur=0,e.shadowOffsetY=0;const T=d?"#FFFFFF":"#2A2A28",L=s==="mini"?p+(A-Y)/2:p+g,S=R+(I-pe)/2;if(e.save(),d&&(e.filter="invert(1)"),e.drawImage(Bn(),L,S,Y,pe),e.restore(),s==="full"){const b=p+g+Y+m,v=R+I/2;e.fillStyle=T,e.font=c.buildFontString(l),e.fillText("Made with ",b,v);const E=e.measureText("Made with ").width;e.font=c.buildFontString(u),e.fillText("Graphy",b+E,v)}e.restore()},vn=e=>{const t=e.trim();if(!t.startsWith("#"))return null;const n=t.slice(1);if(n.length===3){const o=n[0],i=n[1],r=n[2];return o===void 0||i===void 0||r===void 0?null:{red:Number.parseInt(o+o,16),green:Number.parseInt(i+i,16),blue:Number.parseInt(r+r,16)}}return n.length>=6?{red:Number.parseInt(n.slice(0,2),16),green:Number.parseInt(n.slice(2,4),16),blue:Number.parseInt(n.slice(4,6),16)}:null},Pn=({red:e,green:t,blue:n})=>{const o=i=>{const r=i/255;return r<=.03928?r/12.92:((r+.055)/1.055)**2.4};return .2126*o(e)+.7152*o(t)+.0722*o(n)},Fn={family:y,size:Ve,weight:600},kn={family:y,size:Xe},Dn={family:y,size:Ke},On={family:y,size:ce,weight:500},Un={family:y,size:ce},je=Ve*Zt,Hn=Xe*Qt,Ze=Ke*Jt,Qe=ce*$t,Je=e=>{if(e===null)return null;if(e.label!==void 0&&e.label!=="")return e.label;if(e.url!==void 0&&e.url!==""&&c.isSafeUrl(e.url))try{return new URL(e.url).hostname}catch{return e.url}return null},Mn=(e,t)=>{let n=0;const o=e.isTitleVisible&&e.title!==null,i=e.isSubtitleVisible&&e.subtitle!==null;o&&(n+=je),i&&(n>0&&(n+=Z),n+=Hn);const r=oe(e.brandMark,t,"header");!o&&!i&&r>0&&(n=r);const s=e.isCaptionVisible&&e.caption!==null,a=e.isSourceVisible&&Je(e.source)!==null,d=oe(e.brandMark,t,"footer");let l=0;return s&&(l+=Ze),(a||d>0)&&(l>0&&(l+=Z),l+=Math.max(Qe,d)),{headerHeight:n,footerHeight:l}},Gn=(e,t,n,o,i)=>{if(n.height===0)return;e.save(),e.textBaseline="top",e.textAlign="left";let r=n.y;t.isTitleVisible&&t.title!==null&&(e.font=c.buildFontString(Fn),e.fillStyle=o.textPrimary,e.fillText(c.extractPlainText(t.title),n.x,r),r+=je+Z),t.isSubtitleVisible&&t.subtitle!==null&&(e.font=c.buildFontString(kn),e.fillStyle=o.textSecondary,e.fillText(c.extractPlainText(t.subtitle),n.x,r)),e.restore(),qe(e,t.brandMark,n,o,i,"header")},Wn=(e,t,n,o,i)=>{if(n.height===0)return;const r=t.isCaptionVisible?t.caption:null,s=t.isSourceVisible?Je(t.source):null,a=oe(t.brandMark,i,"footer");if(r===null&&s===null&&a===0)return;e.save(),e.textBaseline="top",e.textAlign="left";let d=n.y;if(r!==null&&(e.font=c.buildFontString(Dn),e.fillStyle=o.textSecondary,e.fillText(c.extractPlainText(r),n.x,d),d+=Ze+Z),s!==null){let l=n.x;l=Se(e,"Source: ",On,o.textSecondary,l,d),Se(e,s,Un,o.textSecondary,l,d)}e.restore(),qe(e,t.brandMark,{x:n.x,y:d,width:n.width,height:Math.max(Qe,a)},o,i,"footer")},Se=(e,t,n,o,i,r)=>(e.font=c.buildFontString(n),e.fillStyle=o,e.fillText(t,i,r),i+e.measureText(t).width),Vn=(e,t,n,o,i)=>{e.save(),e.lineCap="round",e.strokeStyle=i.gridLine;for(const r of c.computePanelBorderPaths(n.border,o,pn))e.lineWidth=r.lineWidth??Te,e.setLineDash(ye(r.lineStyle)),Xn(e,r.segments),e.stroke();for(const r of t){if(!r.gridVisible)continue;const s=r.position==="top"||r.position==="bottom";e.lineWidth=r.gridLineWidth??Te,e.setLineDash(ye(r.gridLineStyle));for(const a of r.ticks){if(a.position<=0||a.position>=1)continue;const d=s?a.position:1-a.position;if(e.beginPath(),s){const l=o.x+d*o.width;e.moveTo(l,o.y),e.lineTo(l,o.y+o.height)}else{const l=o.y+d*o.height;e.moveTo(o.x,l),e.lineTo(o.x+o.width,l)}e.stroke()}}e.restore()},ye=e=>{switch(e){case"dashed":return Tn;case"dotted":return Sn;case"solid":return[]}},Xn=(e,t)=>{e.beginPath();for(const n of t)switch(n.type){case"move":e.moveTo(n.x,n.y);break;case"line":e.lineTo(n.x,n.y);break;case"arc":e.arc(n.cx,n.cy,n.radius,n.startAngle,n.endAngle);break;case"close":e.closePath();break}},Kn=(e,t,n,o)=>{e.save(),e.font=c.buildFontString(An),e.textBaseline="middle";for(const r of t)r.display==="direct"&&Yn(e,r,n,o);const i=new Map;for(const r of t){if(r.display!=="pill"||r.items.length===0)continue;const s=i.get(r.position);s?s.push(r):i.set(r.position,[r])}for(const[r,s]of i){const a=n[r];a&&(r==="top"||r==="bottom"?zn(e,s,a,o):qn(e,s,a,o))}e.restore()},Yn=(e,t,n,o)=>{const i=n[t.position];if(i){e.textAlign="left";for(const r of t.items)r.normalizedY!==null&&(e.fillStyle=typeof r.visual.color=="string"?r.visual.color:o.textPrimary,e.fillText(r.formattedLabel,i.x+qt,_(i,r.normalizedY)))}},zn=(e,t,n,o)=>{const i=$e(t),r=i.reduce((l,u,f)=>l+Zn(u)+e.measureText(u.label).width+(f===0?0:Re(u)),0),s=H*de;let a=n.x+Math.max(0,(n.width-r)/2);const d=n.y+Ge+Math.max(s,G)/2;for(let l=0;l<i.length;l++){const u=i[l];u&&(l>0&&(a+=Re(u)),a=xe(e,u,a,d,o))}},qn=(e,t,n,o)=>{const i=$e(t),r=Math.max(H*de,G),s=4,a=i.length*r+Math.max(0,i.length-1)*s;let d=n.y+Math.max(0,(n.height-a)/2)+r/2;for(const l of i)xe(e,l,n.x,d,o),d+=r+s},$e=e=>{const t=[];for(const n of e){const o=n.aesthetics.includes("size");n.items.forEach((i,r)=>{const s=i.visual.size,a=o&&typeof s=="number"&&Number.isFinite(s)&&s>0?s:void 0;t.push({label:i.formattedLabel,colors:jn(i.visual.color),bubbleSize:a,isFirstInGroup:r===0})})}return t},jn=e=>typeof e=="string"?[e]:Array.isArray(e)?e.filter(t=>typeof t=="string"):[],Re=e=>e.isFirstInGroup?zt:Me,Zn=e=>{if(e.bubbleSize!==void 0)return e.bubbleSize+F;const t=e.colors.length;if(t===0)return 0;if(t===1)return M+F;const n=Math.min(t,We);return M+(n-1)*ne+F},xe=(e,t,n,o,i)=>{let r=n;if(t.bubbleSize!==void 0){const s=t.bubbleSize/2;e.fillStyle=t.colors[0]??jt,e.beginPath(),e.arc(r+s,o,s,0,Math.PI*2),e.fill(),r+=t.bubbleSize+F}else if(t.colors.length>0){const s=t.colors.slice(0,We);for(let a=0;a<s.length;a++){const d=s[a];d&&(e.fillStyle=d,e.fillRect(r+a*ne,o-G/2,M,G))}r+=M+(s.length-1)*ne+F}return e.fillStyle=i.textPrimary,e.textAlign="left",e.fillText(t.label,r,o),r+e.measureText(t.label).width},Ee=(e,t)=>Math.min(Math.max(...e),t),Qn=e=>t=>{if(!t.isVisible||t.ticks.length===0)return 0;const n=t.position==="top"||t.position==="bottom",o=t.ticks.map(s=>e.measureText(s.formattedLabel,{family:ae,size:le})),i=t.labelMaxWidthPx??Number.POSITIVE_INFINITY;if(n){if(t.labelRotation!==0){const s=o.map(a=>a.width);return Ee(s,i)+2*z}return Math.max(...o.map(s=>s.height))+z}const r=o.map(s=>s.width);return Ee(r,i)+z},Jn=e=>t=>{if(t.items.length===0)return 0;const n={family:He,size:H};if(t.position==="top"||t.position==="bottom"){const r=H*de;return Math.max(r,G)+Ge*2}const i=t.items.map(r=>{const s=e.measureText(r.formattedLabel,n).width;return M+F+s});return Math.max(...i)+Me},$n=e=>(t,n)=>{const{labelLineHeight:o,labelFontWeight:i}=c.getDifferenceArrowDimensions(n,1);return e.measureText(t,{family:y,size:o,weight:i})},xn=e=>(t,n)=>{const{fontSize:o,fontWeight:i}=c.getDataLabelDimensions(t,1);return e.measureText(n,{family:y,size:o,weight:i})},eo=e=>({measureAxis:Qn(e),measureAxisLabel:()=>Yt,measureLegend:Jn(e),measureTickLabel:t=>e.measureText(t,{family:ae,size:le}),measureDifferenceArrowLabel:$n(e),measureDataLabel:xn(e),measureHeadline:()=>({width:0,height:0}),measureHeadlineItemWidths:()=>[]});class et{constructor(){this.currentFont="";const t=W().createCanvas(1,1);this.ctx=t.getContext("2d")}measureText(t,n){const o=c.buildFontString(n);this.currentFont!==o&&(this.ctx.font=o,this.currentFont=o);const i=this.ctx.measureText(t);return{width:i.width,height:i.fontBoundingBoxAscent+i.fontBoundingBoxDescent,ascent:i.fontBoundingBoxAscent,descent:i.fontBoundingBoxDescent}}}const tt={background:"#ffffff",textPrimary:"#111827",textSecondary:"#6b7280",gridLine:"#e5e7eb",panelBorder:"#d1d5db",legendPillBackground:"rgba(0, 0, 0, 0.04)",legendPillBorder:"rgba(0, 0, 0, 0.08)"},nt={background:"#0b0f17",textPrimary:"#e5e7eb",textSecondary:"#9ca3af",gridLine:"#1f2937",panelBorder:"#374151",legendPillBackground:"rgba(255, 255, 255, 0.06)",legendPillBorder:"rgba(255, 255, 255, 0.12)"},to=async({input:e,data:t},n)=>{const i=ke().compile({input:e,data:t});if(!i.ok)throw new Error(`Failed to compile graph spec: ${i.errors.map(r=>r.message).join("; ")}`);return ot(i.compiled,n)},ot=async(e,t)=>{const{Canvas:n}=await _e();if(Ie(),t.fonts)for(const p of t.fonts)re(p);const o=c.formatLegends({legends:e.guides.legends,numberFormat:e.config.numberFormat,parsingLocale:e.config.parsingLocale,formattingLocale:t.formattingLocale}),i={width:t.width,height:t.height},{headerHeight:r,footerHeight:s}=Mn(e.config.content,i),a={headerSize:{width:t.width,height:r},footerSize:{width:t.width,height:s}},d=new et,{layout:l,formattedAxes:u}=new c.LayoutCompiler(eo(d)).compile({axes:e.guides.axes,numberFormat:e.config.numberFormat,parsingLocale:e.config.parsingLocale,formattedLegends:o,containerSize:{width:t.width,height:t.height},externalMeasurements:a,formattingLocale:t.formattingLocale,layout:e.config.layout}),f=t.pixelRatio??2,g=new n(t.width*f,t.height*f),h=g.getContext("2d");h.scale(f,f);const m=t.colorScheme??"light";return no(h,m==="dark"?nt:tt,m,t.width,t.height,e,u,o,l,d,t.formattingLocale),g.encode("png")},no=(e,t,n,o,i,r,s,a,d,l,u)=>{e.fillStyle=t.background,e.fillRect(0,0,o,i),Vn(e,s,r.guides.panel,d.panel,t),e.save(),e.beginPath(),e.rect(d.panel.x,d.panel.y,d.panel.width,d.panel.height),e.clip();const f={coordSystem:r.coordSystem,panel:d.panel,compiled:r,theme:t,colorScheme:n,textMeasurer:l,formattingLocale:u};for(const g of r.layers){const h=Fe(g,r.coordSystem);h&&Gt(h,e,{...f,layer:g})}e.restore();for(const g of s){const h=d.axes[g.position];h&&yn(e,g,h,d.panel,t)}for(const g of r.guides.axes){const h=d.axisLabels[g.position];h&&wn(e,g,h,t)}Kn(e,a,d.legends,t),Gn(e,r.config.content,d.header,t,{width:o,height:i}),Wn(e,r.config.content,d.footer,t,{width:o,height:i})};class rt extends Error{constructor(t){super(t),this.name="ChartConfigRenderError"}}const oo=async(e,t)=>{const n=Ue(e);if(!n.ok)throw new rt(n.reason);return ot(n.compiled,t)};exports.CANVAS_CAPABILITY_MATRIX=Vt;exports.CANVAS_SUPPORTED_GRAPH_TYPES=se;exports.CANVAS_UNSUPPORTED_GRAPH_TYPES=De;exports.ChartConfigRenderError=rt;exports.NodeCanvasTextMeasurer=et;exports.canRenderConfigWithCanvas=Ue;exports.darkTheme=nt;exports.ensureDefaultFonts=Ie;exports.lightTheme=tt;exports.loadCanvasRuntime=_e;exports.parseChartConfig=Oe;exports.registerFont=re;exports.renderChartConfigToPng=oo;exports.renderGraphToPng=to;
@@ -0,0 +1,160 @@
1
+ import { ColorScheme } from '@graphysdk/viz-engine';
2
+ import { CompiledSpec } from '@graphysdk/viz-engine';
3
+ import { Data } from '@graphysdk/viz-engine';
4
+ import { FontSpec } from '@graphysdk/viz-engine';
5
+ import { GraphConfig } from '@graphysdk/viz-engine/graph-config';
6
+ import { Locale } from '@graphysdk/viz-engine';
7
+ import { MeasuredText } from '@graphysdk/viz-engine';
8
+ import type * as NapiCanvas from '@napi-rs/canvas';
9
+ import { SpecInput } from '@graphysdk/viz-engine';
10
+ import { TextMeasurer } from '@graphysdk/viz-engine';
11
+
12
+ /**
13
+ * Whether a stored chart config can be rendered with the canvas PNG pipeline.
14
+ * Compiles via viz-engine and checks each layer has a node geom renderer.
15
+ */
16
+ export declare const canRenderConfigWithCanvas: (config: unknown) => CanvasChartConfigResult;
17
+
18
+ export declare const CANVAS_CAPABILITY_MATRIX: {
19
+ readonly supportedGraphTypes: readonly ["line", "areaStacked", "bar", "barStacked", "barStackedFill", "column", "columnStacked", "columnStackedFill", "combo", "pie", "donut", "scatter", "bubble"];
20
+ readonly unsupportedGraphTypes: readonly ["funnel", "heatmap", "waterfall", "mekko", "table"];
21
+ readonly supportedGeoms: {
22
+ readonly cartesian: readonly ["bar", "line", "area", "point", "rule"];
23
+ readonly polar: readonly ["bar"];
24
+ };
25
+ };
26
+
27
+ /** Chart types the canvas pipeline supports (maps to node-renderer geoms after viz-engine conversion). */
28
+ export declare const CANVAS_SUPPORTED_GRAPH_TYPES: readonly ["line", "areaStacked", "bar", "barStacked", "barStackedFill", "column", "columnStacked", "columnStackedFill", "combo", "pie", "donut", "scatter", "bubble"];
29
+
30
+ /** Chart types viz-engine cannot convert (compile throws). */
31
+ export declare const CANVAS_UNSUPPORTED_GRAPH_TYPES: readonly ["funnel", "heatmap", "waterfall", "mekko", "table"];
32
+
33
+ /**
34
+ * Outcome of {@link canRenderConfigWithCanvas}: the {@link CompiledSpec} the check compiled to
35
+ * validate, so the render that follows paints it directly instead of compiling a second time.
36
+ */
37
+ export declare type CanvasChartConfigResult = {
38
+ ok: true;
39
+ compiled: CompiledSpec;
40
+ } | ChartConfigRejection;
41
+
42
+ /**
43
+ * Lazy loader for `@napi-rs/canvas`. The native binding is platform-specific and
44
+ * must stay a runtime dependency (not bundled into serverless JS). Deferring the
45
+ * import until the first render avoids loading Skia when the module is imported
46
+ * but charts are not rendered.
47
+ */
48
+ declare type CanvasModule = typeof NapiCanvas;
49
+
50
+ /** A rejection carries the reason both checks below report. */
51
+ declare type ChartConfigRejection = {
52
+ ok: false;
53
+ reason: string;
54
+ };
55
+
56
+ export declare class ChartConfigRenderError extends Error {
57
+ constructor(reason: string);
58
+ }
59
+
60
+ /** Split of core/API-shaped JSON into the high-level config and the table it embeds. */
61
+ export declare type ChartConfigResult = {
62
+ ok: true;
63
+ input: GraphConfig;
64
+ data: Data;
65
+ } | ChartConfigRejection;
66
+
67
+ export declare const darkTheme: RenderTheme;
68
+
69
+ /**
70
+ * Loads Inter from `@fontsource/inter` (npm dependency) and optional `assets/` TTF overrides.
71
+ * Safe on Vercel: font files resolve through `node_modules` at runtime; no writable disk required.
72
+ * Callers can still pass `fonts: [{ family, data: Buffer }]` in {@link RenderOptions} for custom faces.
73
+ */
74
+ export declare const ensureDefaultFonts: () => boolean;
75
+
76
+ /**
77
+ * Caller-supplied font registration. Buffer-based so callers can ship fonts without disk I/O.
78
+ *
79
+ * `weight` and `style` are used only for the idempotency cache key. `GlobalFonts.register`
80
+ * reads weight and style from the font file itself — register separate files per weight,
81
+ * and use distinct `family` strings when multiple weights must be addressable in `ctx.font`.
82
+ */
83
+ export declare interface FontRegistration {
84
+ family: string;
85
+ data: Buffer;
86
+ /** Cache-key only; not passed to `GlobalFonts.register`. */
87
+ weight?: number;
88
+ /** Cache-key only; not passed to `GlobalFonts.register`. */
89
+ style?: 'normal' | 'italic';
90
+ }
91
+
92
+ export declare const lightTheme: RenderTheme;
93
+
94
+ export declare const loadCanvasRuntime: () => Promise<CanvasModule>;
95
+
96
+ /**
97
+ * `TextMeasurer` backed by `@napi-rs/canvas`. Used both during layout (the
98
+ * engine's `LayoutMeasurer` wraps it) and at draw time (the renderer reuses the
99
+ * same `ctx.measureText()` semantics) — measurements and drawing stay
100
+ * consistent.
101
+ *
102
+ * Note: no emoji-width correction is needed here. The browser measurer applies
103
+ * one because it measures with Canvas but renders text via DOM `<text>`, and
104
+ * the two engines disagree on emoji width. The Node renderer measures *and*
105
+ * draws with the same `@napi-rs/canvas` context, so layout is internally
106
+ * consistent — raw widths are correct.
107
+ */
108
+ export declare class NodeCanvasTextMeasurer implements TextMeasurer {
109
+ private readonly ctx;
110
+ private currentFont;
111
+ constructor();
112
+ measureText(text: string, font: FontSpec): MeasuredText;
113
+ }
114
+
115
+ /**
116
+ * Splits core/API-shaped `{ type, data, … }` JSON into viz-engine's `{ input, data }` pair.
117
+ */
118
+ export declare const parseChartConfig: (config: unknown) => ChartConfigResult;
119
+
120
+ /** Registers a font with @napi-rs/canvas. Idempotent per {@link registrationKey} (family + weight + style). */
121
+ export declare const registerFont: (registration: FontRegistration) => void;
122
+
123
+ /** Renders a stored chart config (core-shaped JSON with embedded `data`) to PNG. */
124
+ export declare const renderChartConfigToPng: (config: unknown, options: RenderOptions) => Promise<Buffer>;
125
+
126
+ /** Compiles `input` against `data`, then renders the result to a PNG buffer (static output, no hover). */
127
+ export declare const renderGraphToPng: ({ input, data }: RenderGraphToPngInput, options: RenderOptions) => Promise<Buffer>;
128
+
129
+ export declare interface RenderGraphToPngInput {
130
+ input: SpecInput;
131
+ data: Data;
132
+ }
133
+
134
+ export declare interface RenderOptions {
135
+ width: number;
136
+ height: number;
137
+ /** Backing buffer scale for retina-quality output. Defaults to 2. */
138
+ pixelRatio?: number;
139
+ /** Picks the render theme and resolves light-dark stylesheet colors. Defaults to `'light'`. */
140
+ colorScheme?: ColorScheme;
141
+ fonts?: FontRegistration[];
142
+ formattingLocale?: Locale;
143
+ }
144
+
145
+ /**
146
+ * Resolved color palette used by the Node renderer. Mirrors the role-based theme
147
+ * variables in `@graphysdk/react-renderer/theme` but as plain hex values — Node has
148
+ * no CSS-variable resolution.
149
+ */
150
+ export declare interface RenderTheme {
151
+ background: string;
152
+ textPrimary: string;
153
+ textSecondary: string;
154
+ gridLine: string;
155
+ panelBorder: string;
156
+ legendPillBackground: string;
157
+ legendPillBorder: string;
158
+ }
159
+
160
+ export { }
package/dist/index.mjs ADDED
@@ -0,0 +1,812 @@
1
+ import { existsSync as tt, readFileSync as ot } from "node:fs";
2
+ import { createRequire as Le } from "node:module";
3
+ import O from "node:path";
4
+ import { fileURLToPath as nt } from "node:url";
5
+ import { convertGraphConfig as rt } from "@graphysdk/viz-engine/graph-config";
6
+ import { curveCatmullRom as st, curveLinear as it, area as Re, line as _e, arc as at } from "d3-shape";
7
+ import { createStyleResolver as D, GROUP_VARIABLES as we, getX as w, getY as _, getXMin as lt, getXMax as dt, getYMin as ct, getYMax as ft, getBarRectBounds as ut, buildColumnKey as ht, resolveBarBorderTreatment as Ie, resolveBarCornerRadiiPx as $, getBoundingRect as gt, getRuleDashPattern as mt, getRuleLabelTextColor as At, formatRuleValue as bt, buildFontString as S, getAngleExtent as pt, getRadiusExtent as yt, buildSliceGroupKey as Tt, getUnionExtent as Et, resolvePolarBarCornerRadiusUnit as St, createCompiler as Lt, isSafeUrl as Rt, extractPlainText as te, computePanelBorderPaths as _t, getDataLabelDimensions as wt, getDifferenceArrowDimensions as It, formatLegends as Bt, LayoutCompiler as Nt } from "@graphysdk/viz-engine";
8
+ import { Buffer as Ct } from "node:buffer";
9
+ let M, x;
10
+ const vt = () => M ? Promise.resolve(M) : (x ??= import("@napi-rs/canvas").then((e) => (M = e, e)).catch((e) => {
11
+ throw x = void 0, e;
12
+ }), x), V = () => {
13
+ if (!M)
14
+ throw new Error("Canvas runtime is not loaded. Call loadCanvasRuntime() before rendering.");
15
+ return M;
16
+ }, T = "Inter", oe = /* @__PURE__ */ new Set();
17
+ let ue = !1, Z = !1;
18
+ const Be = (e) => `${e.family}|${e.weight ?? 400}|${e.style ?? "normal"}`, Ne = (e) => {
19
+ const t = Be(e);
20
+ oe.has(t) || (V().GlobalFonts.register(e.data, e.family), oe.add(t));
21
+ }, Pt = [
22
+ { file: "Inter-Regular.ttf", family: T, weight: 400 },
23
+ { file: "Inter-SemiBold.ttf", family: T, weight: 600 }
24
+ ], kt = [
25
+ { file: "inter-latin-400-normal.woff", family: T, weight: 400 },
26
+ { file: "inter-latin-600-normal.woff", family: T, weight: 600 }
27
+ ], Dt = "@fontsource/inter", Ft = () => {
28
+ try {
29
+ const e = Le(import.meta.url), t = O.dirname(e.resolve("@graphysdk/node-renderer/package.json"));
30
+ return O.join(t, "assets");
31
+ } catch {
32
+ return O.resolve(O.dirname(nt(import.meta.url)), "../assets");
33
+ }
34
+ }, Ce = (e, t, o) => {
35
+ try {
36
+ return tt(e) ? (Ne({
37
+ family: t,
38
+ data: ot(e),
39
+ weight: o
40
+ }), !0) : !1;
41
+ } catch {
42
+ return !1;
43
+ }
44
+ }, Ot = () => {
45
+ const e = Ft();
46
+ for (const { file: t, family: o, weight: n } of Pt)
47
+ Ce(O.join(e, t), o, n) && (Z = !0);
48
+ }, Ht = () => {
49
+ const e = Le(import.meta.url);
50
+ for (const { file: t, family: o, weight: n } of kt)
51
+ if (!oe.has(Be({ family: o, data: Buffer.alloc(0), weight: n })))
52
+ try {
53
+ const s = e.resolve(`${Dt}/files/${t}`);
54
+ Ce(s, o, n) && (Z = !0);
55
+ } catch {
56
+ }
57
+ }, Mt = () => (ue || (ue = !0, Ot(), Ht()), Z), B = (e, t) => e.x + t * e.width, I = (e, t) => e.y + (1 - t) * e.height, ne = (e) => e === "catmull-rom" ? st : it, ve = (e) => {
58
+ switch (e) {
59
+ case "dashed":
60
+ return [8, 4];
61
+ case "dotted":
62
+ return [2, 2];
63
+ case "solid":
64
+ return [];
65
+ }
66
+ }, F = (e) => e, Gt = (e, t) => t !== "connect" ? e : e.filter((o) => w(o) !== null && _(o) !== null), Ut = (e, t, o) => {
67
+ const n = ne(t.interpolate), s = Re().curve(n), r = _e().curve(n);
68
+ if (e.mainAxis === "y") {
69
+ const i = (l) => I(o, _(l) ?? 0), a = (l) => B(o, lt(l) ?? 0), d = (l) => B(o, dt(l) ?? w(l) ?? 0);
70
+ s.y(i).x0(a).x1(d), r.y(i).x(d);
71
+ } else {
72
+ const i = (l) => B(o, w(l) ?? 0), a = (l) => I(o, ct(l) ?? 0), d = (l) => I(o, ft(l) ?? _(l) ?? 0);
73
+ s.x(i).y0(a).y1(d), r.x(i).y(d);
74
+ }
75
+ if (t.missingValues === "gap") {
76
+ const i = (a) => w(a) !== null && _(a) !== null;
77
+ s.defined(i), r.defined(i);
78
+ }
79
+ return { areaGenerator: s, lineGenerator: r };
80
+ }, Wt = F({
81
+ geom: "area",
82
+ coord: "cartesian",
83
+ draw: (e, { layer: t, coordSystem: o, panel: n, colorScheme: s }) => {
84
+ const r = D({ colorScheme: s }).geomReaders(t), { areaGenerator: i, lineGenerator: a } = Ut(o, t.params, n), d = e;
85
+ t.data.groupBy(we.group).forEach((l) => {
86
+ const c = Gt([...l], t.params.missingValues), f = c[0];
87
+ if (!f) return;
88
+ const u = r.get("color", f), h = r.get("alpha", f), g = r.get("strokeWidth", f), A = ve(r.get("lineType", f));
89
+ e.save(), e.globalAlpha = h, e.fillStyle = u, e.beginPath(), i.context(d)(c), e.fill(), e.restore(), e.save(), e.globalAlpha = r.get("strokeAlpha", f), e.strokeStyle = u, e.lineWidth = g, e.setLineDash(A), e.lineJoin = "round", e.lineCap = "round", e.beginPath(), a.context(d)(c), e.stroke(), e.restore();
90
+ });
91
+ }
92
+ }), he = (e, t, o, n, s, r) => {
93
+ const i = Math.max(0, Math.min(r.rx ?? r.ry ?? 0, n / 2)), a = Math.max(0, Math.min(r.ry ?? r.rx ?? 0, s / 2)), d = Math.PI / 2;
94
+ e.moveTo(t + i, o), e.lineTo(t + n - i, o), e.ellipse(t + n - i, o + a, i, a, 0, -d, 0), e.lineTo(t + n, o + s - a), e.ellipse(t + n - i, o + s - a, i, a, 0, 0, d), e.lineTo(t + i, o + s), e.ellipse(t + i, o + s - a, i, a, 0, d, Math.PI), e.lineTo(t, o + a), e.ellipse(t + i, o + a, i, a, 0, Math.PI, Math.PI * 1.5), e.closePath();
95
+ }, Vt = F({
96
+ geom: "bar",
97
+ coord: "cartesian",
98
+ draw: (e, { layer: t, coordSystem: o, panel: n, colorScheme: s }) => {
99
+ const { Path2D: r } = V(), i = D({ colorScheme: s }).geomReaders(t), a = o.mainAxis, d = /* @__PURE__ */ new Map();
100
+ for (const l of t.data) {
101
+ const c = ut(a, l);
102
+ if (!c) continue;
103
+ const f = B(n, c.x), u = n.y + c.y * n.height, h = {
104
+ x: f,
105
+ y: u,
106
+ width: c.width * n.width,
107
+ height: c.height * n.height,
108
+ fill: i.get("color", l),
109
+ opacity: i.get("alpha", l)
110
+ }, g = ht(a, c), A = d.get(g);
111
+ A ? A.bars.push(h) : d.set(g, {
112
+ bars: [h],
113
+ style: {
114
+ borderColor: i.get("borderColor", l),
115
+ borderWidth: i.get("borderWidth", l),
116
+ borderRadius: i.get("borderRadius", l),
117
+ borderAlpha: i.get("alpha", l)
118
+ }
119
+ });
120
+ }
121
+ for (const { bars: l, style: c } of d.values()) {
122
+ const { borderColor: f, borderRadius: u, borderWidth: h, borderAlpha: g } = c, A = Ie(h), b = u === "full" ? void 0 : $({ borderRadius: u, mainAxis: a });
123
+ if (!(l.length > 1)) {
124
+ const [m] = l;
125
+ if (!m) continue;
126
+ const v = b ?? $({ borderRadius: u, mainAxis: a, bounds: { width: m.width, height: m.height } }), L = new r();
127
+ he(L, m.x, m.y, m.width, m.height, v), e.save(), e.globalAlpha = m.opacity, e.fillStyle = m.fill, e.fill(L), f !== void 0 && (e.clip(L), e.strokeStyle = f, e.lineWidth = A.outlineWidth, e.stroke(L)), e.restore();
128
+ continue;
129
+ }
130
+ const p = gt(l), R = b ?? $({ borderRadius: u, mainAxis: a, bounds: { width: p.width, height: p.height } }), y = new r();
131
+ he(y, p.x, p.y, p.width, p.height, R), e.save(), e.clip(y);
132
+ for (const m of l)
133
+ e.globalAlpha = m.opacity, e.fillStyle = m.fill, e.fillRect(m.x, m.y, m.width, m.height), f !== void 0 && (e.strokeStyle = f, e.lineWidth = A.separatorWidth, e.strokeRect(m.x, m.y, m.width, m.height));
134
+ f !== void 0 && (e.globalAlpha = g, e.strokeStyle = f, e.lineWidth = A.outlineWidth, e.stroke(y)), e.restore();
135
+ }
136
+ }
137
+ }), ge = (e, t) => {
138
+ const o = /^#([0-9a-f]{3}|[0-9a-f]{6}|[0-9a-f]{8})$/i.exec(e.trim());
139
+ if (o?.[1] !== void 0) {
140
+ const s = o[1], r = s.length === 3 ? [...s].map((l) => l + l).join("") : s.slice(0, 6), i = Number.parseInt(r.slice(0, 2), 16), a = Number.parseInt(r.slice(2, 4), 16), d = Number.parseInt(r.slice(4, 6), 16);
141
+ return `rgba(${i}, ${a}, ${d}, ${t})`;
142
+ }
143
+ const n = /^rgba?\(([^)]+)\)$/i.exec(e.trim());
144
+ if (n?.[1] !== void 0) {
145
+ const s = n[1].split(",").map((d) => d.trim()), [r, i, a] = s;
146
+ if (r !== void 0 && i !== void 0 && a !== void 0)
147
+ return `rgba(${r}, ${i}, ${a}, ${t})`;
148
+ }
149
+ return t === 0 ? "rgba(0, 0, 0, 0)" : e;
150
+ }, Kt = (e, t) => t !== "connect" ? e : e.filter((o) => w(o) !== null && _(o) !== null), Xt = (e, t) => {
151
+ const { seriesObservations: o, fillGenerator: n, panel: s, color: r, fillAlpha: i } = t, a = o.map((l) => _(l)).filter((l) => l !== null).map((l) => I(s, l));
152
+ if (a.length === 0) return;
153
+ const d = e.createLinearGradient(0, Math.min(...a), 0, s.y + s.height);
154
+ d.addColorStop(0, r), d.addColorStop(0.7, ge(r, 0)), d.addColorStop(1, ge(r, 0)), e.save(), e.globalAlpha = i, e.fillStyle = d, e.beginPath(), n.context(e)(o), e.fill(), e.restore();
155
+ }, zt = F({
156
+ geom: "line",
157
+ coord: "cartesian",
158
+ draw: (e, { layer: t, panel: o, colorScheme: n }) => {
159
+ const s = D({ colorScheme: n }).geomReaders(t), r = _e().x((a) => B(o, w(a) ?? 0)).y((a) => I(o, _(a) ?? 0)).curve(ne(t.params.interpolate)), i = Re().x((a) => B(o, w(a) ?? 0)).y0(o.y + o.height).y1((a) => I(o, _(a) ?? 0)).curve(ne(t.params.interpolate));
160
+ if (t.params.missingValues === "gap") {
161
+ const a = (d) => w(d) !== null && _(d) !== null;
162
+ r.defined(a), i.defined(a);
163
+ }
164
+ t.data.groupBy(we.group).forEach((a) => {
165
+ const d = Kt([...a], t.params.missingValues), l = d[0];
166
+ if (!l) return;
167
+ const c = s.get("strokeWidth", l), f = ve(s.get("lineType", l)), u = s.get("color", l), h = s.get("fillAlpha", l);
168
+ h !== void 0 && Xt(e, { seriesObservations: d, fillGenerator: i, panel: o, color: u, fillAlpha: h }), e.save(), e.globalAlpha = s.get("alpha", l), e.strokeStyle = u, e.lineWidth = c, e.setLineDash(f), e.lineJoin = "round", e.lineCap = "round", e.beginPath(), r.context(e)(d), e.stroke(), e.restore();
169
+ });
170
+ }
171
+ }), Yt = F({
172
+ geom: "point",
173
+ coord: "cartesian",
174
+ draw: (e, { layer: t, panel: o, colorScheme: n }) => {
175
+ const s = D({ colorScheme: n }).geomReaders(t);
176
+ for (const r of t.data) {
177
+ const i = w(r), a = _(r);
178
+ if (i === null || a === null) continue;
179
+ const d = B(o, i), l = I(o, a), c = s.get("size", r) / 2, f = s.get("borderWidth", r);
180
+ e.save(), e.globalAlpha = s.get("alpha", r), e.fillStyle = s.get("color", r), e.strokeStyle = s.get("borderColor", r), e.lineWidth = f, e.beginPath(), e.arc(d, l, c, 0, Math.PI * 2), e.fill(), f > 0 && e.stroke(), e.restore();
181
+ }
182
+ }
183
+ }), ae = 11, qt = "sans-serif", Zt = 500, P = 5, Pe = 5, re = 13, ke = 6, K = 12, me = 21.6, jt = (e, t) => {
184
+ const o = e.mapping.y !== void 0, n = t.mainAxis === "y";
185
+ return o !== n;
186
+ }, Qt = (e, t, o) => {
187
+ const n = mt(t, o);
188
+ e.setLineDash(n), n.length > 0 && (e.lineCap = "round");
189
+ }, Jt = (e, t) => X(e) && X(t) ? `${e}: ${t}` : X(e) ? e : X(t) ? t : null, X = (e) => e != null && e.trim() !== "", $t = (e) => {
190
+ e.beginPath(), e.moveTo(2.48935, 16.9323), e.lineTo(9.07098, 9.82964), e.bezierCurveTo(9.51595, 9.34944, 9.51504, 8.60728, 9.0689, 8.12818), e.lineTo(2.48866, 1.06172), e.bezierCurveTo(1.85811, 0.384586, 0.974477, 0, 0.049217, 0), e.lineTo(0, 0), e.lineTo(0, 18), e.lineTo(0.0443689, 18), e.bezierCurveTo(0.972463, 18, 1.85854, 17.6131, 2.48935, 16.9323), e.closePath();
191
+ }, De = (e, t, o, n, s, r) => {
192
+ const i = Math.min(r, n / 2, s / 2);
193
+ e.beginPath(), e.moveTo(t + i, o), e.lineTo(t + n - i, o), e.arcTo(t + n, o, t + n, o + i, i), e.lineTo(t + n, o + s - i), e.arcTo(t + n, o + s, t + n - i, o + s, i), e.lineTo(t + i, o + s), e.arcTo(t, o + s, t, o + s - i, i), e.lineTo(t, o + i), e.arcTo(t, o, t + i, o, i), e.closePath(), e.fill();
194
+ }, xt = (e, t, o, n, s, r, i, a) => {
195
+ const d = o + P * 2, l = ae + Pe * 2, c = n === "start", f = c ? r.x : r.x + r.width, u = I(r, s), h = c ? -re : -(d - re), g = c ? h + d - P : h + P - K;
196
+ e.save(), e.translate(f, u), e.translate(0, -l / 2), e.fillStyle = i, De(e, h, 0, d, l, ke), e.save(), c || (e.translate(g + K / 2, l / 2), e.rotate(Math.PI), e.translate(-(g + K / 2), -l / 2)), e.translate(g, (l - me) / 2), e.scale(K / 10, me / 18), e.fillStyle = i, $t(e), e.fill(), e.restore(), e.fillStyle = a, e.textAlign = "left", e.textBaseline = "middle", e.fillText(t, h + P, l / 2), e.restore();
197
+ }, eo = (e, t, o, n, s, r, i, a) => {
198
+ const d = o + P * 2, l = ae + Pe * 2, c = n === "start", f = B(r, s), u = c ? r.y + r.height : r.y, h = c ? -re : -8;
199
+ e.save(), e.translate(f, u), e.translate(-d / 2, 0), e.fillStyle = i, De(e, 0, h, d, l, ke), e.fillStyle = a, e.textAlign = "left", e.textBaseline = "middle", e.fillText(t, P, h + l / 2), e.restore();
200
+ }, to = F({
201
+ geom: "rule",
202
+ coord: "cartesian",
203
+ draw: (e, { layer: t, coordSystem: o, panel: n, compiled: s, textMeasurer: r, formattingLocale: i, colorScheme: a }) => {
204
+ const d = t.data.getFirst();
205
+ if (!d) return;
206
+ const l = jt(t, o), c = l ? _(d) : w(d);
207
+ if (c === null) return;
208
+ const f = D({ colorScheme: a }).geomReaders(t), u = f.get("color", d), h = At(u), g = f.get("strokeWidth", d);
209
+ if (e.save(), e.strokeStyle = u, e.lineWidth = g, Qt(e, f.get("lineType", d), g), e.beginPath(), l) {
210
+ const y = I(n, c);
211
+ e.moveTo(n.x, y), e.lineTo(n.x + n.width, y);
212
+ } else {
213
+ const y = B(n, c);
214
+ e.moveTo(y, n.y), e.lineTo(y, n.y + n.height);
215
+ }
216
+ e.stroke(), e.restore();
217
+ const A = Jt(
218
+ t.params.label,
219
+ bt({
220
+ guides: s.guides,
221
+ numberFormat: s.config.numberFormat,
222
+ layer: t,
223
+ locale: i ?? s.config.parsingLocale
224
+ })
225
+ );
226
+ if (A === null) return;
227
+ const b = s.config.appearance.textScale, E = {
228
+ family: qt,
229
+ size: ae * b,
230
+ weight: Zt
231
+ };
232
+ e.font = S(E);
233
+ const p = r.measureText(A, E).width;
234
+ (l ? xt : eo)(e, A, p, t.params.labelPosition, c, n, u, h);
235
+ }
236
+ }), H = at();
237
+ H.digits(8);
238
+ const oo = F({
239
+ geom: "bar",
240
+ coord: "polar",
241
+ draw: (e, { layer: t, coordSystem: o, panel: n, colorScheme: s }) => {
242
+ const r = n.x + n.width / 2, i = n.y + n.height / 2, a = Math.min(n.width, n.height) / 2, { Path2D: d } = V(), l = D({ colorScheme: s }).geomReaders(t);
243
+ e.save(), e.translate(r, i), e.scale(a, a);
244
+ const c = e, f = /* @__PURE__ */ new Map();
245
+ for (const u of t.data) {
246
+ const { startAngle: h, endAngle: g } = pt(u), { innerRadius: A, outerRadius: b } = yt(u);
247
+ if (h === null || g === null || A === null || b === null) continue;
248
+ const E = {
249
+ startAngle: h,
250
+ endAngle: g,
251
+ innerRadius: A,
252
+ outerRadius: b,
253
+ fill: l.get("color", u),
254
+ opacity: l.get("alpha", u)
255
+ }, p = Tt(o.bandAxis, E), R = f.get(p);
256
+ R ? R.items.push(E) : f.set(p, {
257
+ items: [E],
258
+ style: {
259
+ borderColor: l.get("borderColor", u),
260
+ borderWidth: l.get("borderWidth", u),
261
+ borderRadius: l.get("borderRadius", u),
262
+ borderAlpha: l.get("alpha", u)
263
+ }
264
+ });
265
+ }
266
+ for (const { items: u, style: h } of f.values()) {
267
+ const { borderColor: g, borderRadius: A, borderWidth: b, borderAlpha: E } = h, p = Ie(b), R = g !== void 0 && a > 0, y = Et(u), m = St(A, y.outerRadius - y.innerRadius), v = a > 0 && (R || m > 0);
268
+ e.save();
269
+ let L;
270
+ v && (L = new d(), H.cornerRadius(m), H.context(L)(y), e.clip(L)), H.cornerRadius(0);
271
+ for (const J of u)
272
+ e.globalAlpha = J.opacity, e.fillStyle = J.fill, e.beginPath(), H.context(c)(J), e.fill(), R && (e.strokeStyle = g, e.lineWidth = p.separatorWidth / a, e.stroke());
273
+ R && L && (e.globalAlpha = E, e.strokeStyle = g, e.lineWidth = p.outlineWidth / a, e.stroke(L)), e.restore();
274
+ }
275
+ e.restore();
276
+ }
277
+ }), no = {
278
+ bar: { cartesian: Vt, polar: oo },
279
+ line: { cartesian: zt },
280
+ area: { cartesian: Wt },
281
+ point: { cartesian: Yt },
282
+ rule: { cartesian: to }
283
+ }, Fe = (e, t) => (
284
+ // `CompiledLayer.geom` is a plain string (a custom geom carries a name outside `GeomName`); the
285
+ // node renderer ships only built-ins, so an unregistered name simply resolves to `null`.
286
+ no[e.geom]?.[t.type] ?? null
287
+ ), ro = (e, t, o) => {
288
+ e.draw(t, o);
289
+ };
290
+ let Ae;
291
+ const Oe = () => (Ae ??= Lt(), Ae), He = [
292
+ "line",
293
+ "areaStacked",
294
+ "bar",
295
+ "barStacked",
296
+ "barStackedFill",
297
+ "column",
298
+ "columnStacked",
299
+ "columnStackedFill",
300
+ "combo",
301
+ "pie",
302
+ "donut",
303
+ "scatter",
304
+ "bubble"
305
+ ], so = [
306
+ "funnel",
307
+ "heatmap",
308
+ "waterfall",
309
+ "mekko",
310
+ "table"
311
+ ], io = new Set(He), Fn = {
312
+ supportedGraphTypes: [...He],
313
+ unsupportedGraphTypes: [...so],
314
+ supportedGeoms: {
315
+ cartesian: ["bar", "line", "area", "point", "rule"],
316
+ polar: ["bar"]
317
+ }
318
+ }, C = (e) => ({ ok: !1, reason: e }), j = (e) => typeof e == "object" && e !== null && !Array.isArray(e), ao = (e) => {
319
+ if (!j(e)) return !1;
320
+ const { columns: t, rows: o } = e;
321
+ return !Array.isArray(t) || !Array.isArray(o) ? !1 : t.every((n) => j(n) && typeof n.key == "string");
322
+ }, lo = (e) => {
323
+ if ("_unstable_mapping" in e && e._unstable_mapping !== void 0) return !0;
324
+ const t = e.referenceLines;
325
+ return j(t) ? t.trendline !== void 0 || t.averageLine !== void 0 : !1;
326
+ }, co = (e) => {
327
+ if (!j(e))
328
+ return C("Chart config must be a plain object.");
329
+ if (!ao(e.data))
330
+ return C("Chart config is missing a valid `data` object (columns + rows).");
331
+ const { data: t, ...o } = e;
332
+ return { ok: !0, input: o, data: t };
333
+ }, fo = (e) => {
334
+ const t = co(e);
335
+ if (!t.ok) return t;
336
+ if (lo(e))
337
+ return C(
338
+ "Canvas renderer does not support `_unstable_mapping`, `referenceLines.trendline`, or `referenceLines.averageLine`."
339
+ );
340
+ const o = t.input.type ?? "column";
341
+ if (!io.has(o))
342
+ return C(`Unsupported chart type for canvas rendering: ${o}.`);
343
+ try {
344
+ const n = Oe(), s = rt(t.input, t.data), r = n.compile({ input: s, data: t.data });
345
+ if (!r.ok)
346
+ return C(
347
+ `Chart config failed to compile: ${r.errors.map((i) => i.message).join("; ")}`
348
+ );
349
+ for (const i of r.compiled.layers)
350
+ if (Fe(i, r.compiled.coordSystem) === null)
351
+ return C(`No canvas geom renderer for ${i.geom} (${r.compiled.coordSystem.type} coord).`);
352
+ return { ok: !0, compiled: r.compiled };
353
+ } catch (n) {
354
+ const s = n instanceof Error ? n.message : String(n);
355
+ return C(`Chart config failed to compile: ${s}`);
356
+ }
357
+ }, z = 6, q = 10, uo = 20, le = T, de = 12, Me = T, G = 12, ce = 1.3, U = 10, W = 12, k = 6, Ge = 16, ho = 24, Ue = 6, We = 3, se = 4, go = 8, mo = "#9ca3af", Ve = 16, Ao = 1.3, Ke = 13, bo = 1.4, Xe = 12, po = 1.4, Q = 8, fe = 12, yo = 1.3, To = 200, Eo = 120, So = 80, ze = 22, Lo = 9, Ro = 11, _o = 10, Y = 9, be = 12, wo = '-apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif', Io = 11.5, Bo = 550, No = { light: 0.55, mid: 0.75, dark: 0.08 }, Co = { light: 0.62, mid: 0.8, dark: 0.85 }, vo = 0.22, Po = 0.65, ko = T, Do = 12, Fo = { family: le, size: de }, Oo = { family: ko, size: Do }, Ho = { family: Me, size: G }, Mo = 8, pe = 1, Go = [2, 3], Uo = [1, 3], Wo = (e, t, o, n, s) => {
358
+ if (!t.isVisible || t.ticks.length === 0) return;
359
+ const r = t.position === "bottom" || t.position === "top", i = t.ticksVisible ? 1 : 0;
360
+ e.save(), e.font = S(Fo), e.fillStyle = s.textSecondary, e.strokeStyle = s.gridLine, e.lineWidth = 1, e.setLineDash([]);
361
+ for (const a of t.ticks) {
362
+ const d = r ? a.position : 1 - a.position;
363
+ t.ticksVisible && Ko(e, t.position, d, o, n), zo(
364
+ e,
365
+ t.position,
366
+ d,
367
+ o,
368
+ n,
369
+ a.formattedLabel,
370
+ t.labelRotation,
371
+ i
372
+ );
373
+ }
374
+ e.restore();
375
+ }, Vo = (e, t, o, n) => {
376
+ if (e === "bottom" || e === "top") {
377
+ const a = n.x + t * n.width, d = e === "top" ? o.y + o.height : o.y, l = e === "top" ? -z : z;
378
+ return { x1: a, y1: d, x2: a, y2: d + l };
379
+ }
380
+ const s = n.y + t * n.height, r = e === "right" ? o.x : o.x + o.width, i = e === "right" ? z : -z;
381
+ return { x1: r, y1: s, x2: r + i, y2: s };
382
+ }, Ko = (e, t, o, n, s) => {
383
+ const { x1: r, y1: i, x2: a, y2: d } = Vo(t, o, n, s);
384
+ e.beginPath(), e.moveTo(r, i), e.lineTo(a, d), e.stroke();
385
+ }, Xo = (e, t, o, n, s) => {
386
+ if (e === "bottom" || e === "top") {
387
+ const i = n.x + t * n.width;
388
+ return e === "bottom" ? { x: i, y: o.y + s, textAlign: "center", textBaseline: "top" } : { x: i, y: o.y + o.height - s, textAlign: "center", textBaseline: "alphabetic" };
389
+ }
390
+ const r = n.y + t * n.height;
391
+ return e === "left" ? { x: o.x + o.width - s, y: r, textAlign: "right", textBaseline: "middle" } : { x: o.x + s, y: r, textAlign: "left", textBaseline: "middle" };
392
+ }, zo = (e, t, o, n, s, r, i, a) => {
393
+ const d = q * a;
394
+ if (i !== 0 && (t === "top" || t === "bottom")) {
395
+ const c = s.x + o * s.width, f = t === "top";
396
+ e.save(), e.translate(c, f ? n.y + n.height - d : n.y + d), e.rotate(i * Math.PI / 180), e.textAlign = f ? "left" : "right", e.textBaseline = "middle", e.fillText(r, 0, 0), e.restore();
397
+ return;
398
+ }
399
+ const l = Xo(t, o, n, s, d);
400
+ e.textAlign = l.textAlign, e.textBaseline = l.textBaseline, e.fillText(r, l.x, l.y);
401
+ }, Yo = (e, t, o, n) => {
402
+ if (!t.isVisible || !t.label) return;
403
+ e.save(), e.font = S(Oo), e.fillStyle = n.textPrimary, e.textBaseline = "middle";
404
+ const { x: s, y: r, alignment: i } = qo(t.position, o);
405
+ e.textAlign = i, e.fillText(t.label, s, r), e.restore();
406
+ }, qo = (e, t) => {
407
+ const o = t.y + t.height / 2;
408
+ return e === "left" ? { x: t.x, y: o, alignment: "start" } : e === "right" ? { x: t.x + t.width, y: o, alignment: "end" } : { x: t.x + t.width / 2, y: o, alignment: "center" };
409
+ }, Zo = "data:image/png;base64,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", N = ze;
410
+ let ee;
411
+ const jo = () => {
412
+ if (ee) return ee;
413
+ const { Image: e } = V(), t = new e(), o = Zo.replace(/^data:image\/png;base64,/, "");
414
+ return t.src = Ct.from(o, "base64"), ee = t, t;
415
+ }, Ye = (e, t, o = "full") => !e || t.width < Eo || t.height < So ? "hidden" : t.width < To || o === "mini" ? "mini" : "full", Qo = (e) => {
416
+ const t = Jo(e);
417
+ if (t === null) return "light";
418
+ const o = $o(t);
419
+ return o <= vo ? "dark" : o <= Po ? "mid" : "light";
420
+ }, ie = (e, t, o) => e.placement !== o || Ye(e.enabled, t, e.variant) === "hidden" ? 0 : ze, qe = (e, t, o, n, s, r) => {
421
+ if (t.placement !== r) return;
422
+ const i = Ye(t.enabled, s, t.variant);
423
+ if (i === "hidden") return;
424
+ const a = Qo(n.background), d = a === "dark", l = { family: wo, size: Io, weight: 400 }, c = { ...l, weight: Bo };
425
+ let f = 0;
426
+ i === "full" && (e.font = S(l), f = e.measureText("Made with ").width, e.font = S(c), f += e.measureText("Graphy").width);
427
+ const u = i === "mini" ? 0 : Lo, h = i === "mini" ? 0 : Ro, g = i === "mini" ? 0 : _o, A = i === "mini" ? N : u + Y + g + f + h, b = o.x + o.width - A, E = o.y + Math.max(0, (o.height || N) / 2 - N / 2);
428
+ e.save(), e.globalAlpha = Co[a], e.textBaseline = "middle", e.textAlign = "left", d || (e.shadowColor = "rgba(0, 0, 0, 0.09)", e.shadowBlur = 4, e.shadowOffsetY = 1), e.beginPath(), e.roundRect(b, E, A, N, N / 2), e.fillStyle = `rgba(255, 255, 255, ${No[a]})`, e.fill(), e.shadowColor = "transparent", e.shadowBlur = 0, e.shadowOffsetY = 0;
429
+ const p = d ? "#FFFFFF" : "#2A2A28", R = i === "mini" ? b + (A - Y) / 2 : b + u, y = E + (N - be) / 2;
430
+ if (e.save(), d && (e.filter = "invert(1)"), e.drawImage(jo(), R, y, Y, be), e.restore(), i === "full") {
431
+ const m = b + u + Y + g, v = E + N / 2;
432
+ e.fillStyle = p, e.font = S(l), e.fillText("Made with ", m, v);
433
+ const L = e.measureText("Made with ").width;
434
+ e.font = S(c), e.fillText("Graphy", m + L, v);
435
+ }
436
+ e.restore();
437
+ }, Jo = (e) => {
438
+ const t = e.trim();
439
+ if (!t.startsWith("#")) return null;
440
+ const o = t.slice(1);
441
+ if (o.length === 3) {
442
+ const n = o[0], s = o[1], r = o[2];
443
+ return n === void 0 || s === void 0 || r === void 0 ? null : {
444
+ red: Number.parseInt(n + n, 16),
445
+ green: Number.parseInt(s + s, 16),
446
+ blue: Number.parseInt(r + r, 16)
447
+ };
448
+ }
449
+ return o.length >= 6 ? {
450
+ red: Number.parseInt(o.slice(0, 2), 16),
451
+ green: Number.parseInt(o.slice(2, 4), 16),
452
+ blue: Number.parseInt(o.slice(4, 6), 16)
453
+ } : null;
454
+ }, $o = ({ red: e, green: t, blue: o }) => {
455
+ const n = (s) => {
456
+ const r = s / 255;
457
+ return r <= 0.03928 ? r / 12.92 : ((r + 0.055) / 1.055) ** 2.4;
458
+ };
459
+ return 0.2126 * n(e) + 0.7152 * n(t) + 0.0722 * n(o);
460
+ }, xo = { family: T, size: Ve, weight: 600 }, en = { family: T, size: Ke }, tn = { family: T, size: Xe }, on = { family: T, size: fe, weight: 500 }, nn = { family: T, size: fe }, Ze = Ve * Ao, rn = Ke * bo, je = Xe * po, Qe = fe * yo, Je = (e) => {
461
+ if (e === null) return null;
462
+ if (e.label !== void 0 && e.label !== "") return e.label;
463
+ if (e.url !== void 0 && e.url !== "" && Rt(e.url))
464
+ try {
465
+ return new URL(e.url).hostname;
466
+ } catch {
467
+ return e.url;
468
+ }
469
+ return null;
470
+ }, sn = (e, t) => {
471
+ let o = 0;
472
+ const n = e.isTitleVisible && e.title !== null, s = e.isSubtitleVisible && e.subtitle !== null;
473
+ n && (o += Ze), s && (o > 0 && (o += Q), o += rn);
474
+ const r = ie(e.brandMark, t, "header");
475
+ !n && !s && r > 0 && (o = r);
476
+ const i = e.isCaptionVisible && e.caption !== null, a = e.isSourceVisible && Je(e.source) !== null, d = ie(e.brandMark, t, "footer");
477
+ let l = 0;
478
+ return i && (l += je), (a || d > 0) && (l > 0 && (l += Q), l += Math.max(Qe, d)), { headerHeight: o, footerHeight: l };
479
+ }, an = (e, t, o, n, s) => {
480
+ if (o.height === 0) return;
481
+ e.save(), e.textBaseline = "top", e.textAlign = "left";
482
+ let r = o.y;
483
+ t.isTitleVisible && t.title !== null && (e.font = S(xo), e.fillStyle = n.textPrimary, e.fillText(te(t.title), o.x, r), r += Ze + Q), t.isSubtitleVisible && t.subtitle !== null && (e.font = S(en), e.fillStyle = n.textSecondary, e.fillText(te(t.subtitle), o.x, r)), e.restore(), qe(e, t.brandMark, o, n, s, "header");
484
+ }, ln = (e, t, o, n, s) => {
485
+ if (o.height === 0) return;
486
+ const r = t.isCaptionVisible ? t.caption : null, i = t.isSourceVisible ? Je(t.source) : null, a = ie(t.brandMark, s, "footer");
487
+ if (r === null && i === null && a === 0) return;
488
+ e.save(), e.textBaseline = "top", e.textAlign = "left";
489
+ let d = o.y;
490
+ if (r !== null && (e.font = S(tn), e.fillStyle = n.textSecondary, e.fillText(te(r), o.x, d), d += je + Q), i !== null) {
491
+ let l = o.x;
492
+ l = ye(e, "Source: ", on, n.textSecondary, l, d), ye(e, i, nn, n.textSecondary, l, d);
493
+ }
494
+ e.restore(), qe(
495
+ e,
496
+ t.brandMark,
497
+ { x: o.x, y: d, width: o.width, height: Math.max(Qe, a) },
498
+ n,
499
+ s,
500
+ "footer"
501
+ );
502
+ }, ye = (e, t, o, n, s, r) => (e.font = S(o), e.fillStyle = n, e.fillText(t, s, r), s + e.measureText(t).width), dn = (e, t, o, n, s) => {
503
+ e.save(), e.lineCap = "round", e.strokeStyle = s.gridLine;
504
+ for (const r of _t(o.border, n, Mo))
505
+ e.lineWidth = r.lineWidth ?? pe, e.setLineDash(Te(r.lineStyle)), cn(e, r.segments), e.stroke();
506
+ for (const r of t) {
507
+ if (!r.gridVisible) continue;
508
+ const i = r.position === "top" || r.position === "bottom";
509
+ e.lineWidth = r.gridLineWidth ?? pe, e.setLineDash(Te(r.gridLineStyle));
510
+ for (const a of r.ticks) {
511
+ if (a.position <= 0 || a.position >= 1) continue;
512
+ const d = i ? a.position : 1 - a.position;
513
+ if (e.beginPath(), i) {
514
+ const l = n.x + d * n.width;
515
+ e.moveTo(l, n.y), e.lineTo(l, n.y + n.height);
516
+ } else {
517
+ const l = n.y + d * n.height;
518
+ e.moveTo(n.x, l), e.lineTo(n.x + n.width, l);
519
+ }
520
+ e.stroke();
521
+ }
522
+ }
523
+ e.restore();
524
+ }, Te = (e) => {
525
+ switch (e) {
526
+ case "dashed":
527
+ return Go;
528
+ case "dotted":
529
+ return Uo;
530
+ case "solid":
531
+ return [];
532
+ }
533
+ }, cn = (e, t) => {
534
+ e.beginPath();
535
+ for (const o of t)
536
+ switch (o.type) {
537
+ case "move":
538
+ e.moveTo(o.x, o.y);
539
+ break;
540
+ case "line":
541
+ e.lineTo(o.x, o.y);
542
+ break;
543
+ case "arc":
544
+ e.arc(o.cx, o.cy, o.radius, o.startAngle, o.endAngle);
545
+ break;
546
+ case "close":
547
+ e.closePath();
548
+ break;
549
+ }
550
+ }, fn = (e, t, o, n) => {
551
+ e.save(), e.font = S(Ho), e.textBaseline = "middle";
552
+ for (const r of t)
553
+ r.display === "direct" && un(e, r, o, n);
554
+ const s = /* @__PURE__ */ new Map();
555
+ for (const r of t) {
556
+ if (r.display !== "pill" || r.items.length === 0) continue;
557
+ const i = s.get(r.position);
558
+ i ? i.push(r) : s.set(r.position, [r]);
559
+ }
560
+ for (const [r, i] of s) {
561
+ const a = o[r];
562
+ a && (r === "top" || r === "bottom" ? hn(e, i, a, n) : gn(e, i, a, n));
563
+ }
564
+ e.restore();
565
+ }, un = (e, t, o, n) => {
566
+ const s = o[t.position];
567
+ if (s) {
568
+ e.textAlign = "left";
569
+ for (const r of t.items)
570
+ r.normalizedY !== null && (e.fillStyle = typeof r.visual.color == "string" ? r.visual.color : n.textPrimary, e.fillText(r.formattedLabel, s.x + go, I(s, r.normalizedY)));
571
+ }
572
+ }, hn = (e, t, o, n) => {
573
+ const s = $e(t), r = s.reduce(
574
+ (l, c, f) => l + An(c) + e.measureText(c.label).width + (f === 0 ? 0 : Ee(c)),
575
+ 0
576
+ ), i = G * ce;
577
+ let a = o.x + Math.max(0, (o.width - r) / 2);
578
+ const d = o.y + Ue + Math.max(i, W) / 2;
579
+ for (let l = 0; l < s.length; l++) {
580
+ const c = s[l];
581
+ c && (l > 0 && (a += Ee(c)), a = xe(e, c, a, d, n));
582
+ }
583
+ }, gn = (e, t, o, n) => {
584
+ const s = $e(t), r = Math.max(G * ce, W), i = 4, a = s.length * r + Math.max(0, s.length - 1) * i;
585
+ let d = o.y + Math.max(0, (o.height - a) / 2) + r / 2;
586
+ for (const l of s)
587
+ xe(e, l, o.x, d, n), d += r + i;
588
+ }, $e = (e) => {
589
+ const t = [];
590
+ for (const o of e) {
591
+ const n = o.aesthetics.includes("size");
592
+ o.items.forEach((s, r) => {
593
+ const i = s.visual.size, a = n && typeof i == "number" && Number.isFinite(i) && i > 0 ? i : void 0;
594
+ t.push({
595
+ label: s.formattedLabel,
596
+ colors: mn(s.visual.color),
597
+ bubbleSize: a,
598
+ isFirstInGroup: r === 0
599
+ });
600
+ });
601
+ }
602
+ return t;
603
+ }, mn = (e) => typeof e == "string" ? [e] : Array.isArray(e) ? e.filter((t) => typeof t == "string") : [], Ee = (e) => e.isFirstInGroup ? ho : Ge, An = (e) => {
604
+ if (e.bubbleSize !== void 0) return e.bubbleSize + k;
605
+ const t = e.colors.length;
606
+ if (t === 0) return 0;
607
+ if (t === 1) return U + k;
608
+ const o = Math.min(t, We);
609
+ return U + (o - 1) * se + k;
610
+ }, xe = (e, t, o, n, s) => {
611
+ let r = o;
612
+ if (t.bubbleSize !== void 0) {
613
+ const i = t.bubbleSize / 2;
614
+ e.fillStyle = t.colors[0] ?? mo, e.beginPath(), e.arc(r + i, n, i, 0, Math.PI * 2), e.fill(), r += t.bubbleSize + k;
615
+ } else if (t.colors.length > 0) {
616
+ const i = t.colors.slice(0, We);
617
+ for (let a = 0; a < i.length; a++) {
618
+ const d = i[a];
619
+ d && (e.fillStyle = d, e.fillRect(
620
+ r + a * se,
621
+ n - W / 2,
622
+ U,
623
+ W
624
+ ));
625
+ }
626
+ r += U + (i.length - 1) * se + k;
627
+ }
628
+ return e.fillStyle = s.textPrimary, e.textAlign = "left", e.fillText(t.label, r, n), r + e.measureText(t.label).width;
629
+ }, Se = (e, t) => Math.min(Math.max(...e), t), bn = (e) => (t) => {
630
+ if (!t.isVisible || t.ticks.length === 0) return 0;
631
+ const o = t.position === "top" || t.position === "bottom", n = t.ticks.map(
632
+ (i) => e.measureText(i.formattedLabel, {
633
+ family: le,
634
+ size: de
635
+ })
636
+ ), s = t.labelMaxWidthPx ?? Number.POSITIVE_INFINITY;
637
+ if (o) {
638
+ if (t.labelRotation !== 0) {
639
+ const i = n.map((a) => a.width);
640
+ return Se(i, s) + 2 * q;
641
+ }
642
+ return Math.max(...n.map((i) => i.height)) + q;
643
+ }
644
+ const r = n.map((i) => i.width);
645
+ return Se(r, s) + q;
646
+ }, pn = (e) => (t) => {
647
+ if (t.items.length === 0) return 0;
648
+ const o = { family: Me, size: G };
649
+ if (t.position === "top" || t.position === "bottom") {
650
+ const r = G * ce;
651
+ return Math.max(r, W) + Ue * 2;
652
+ }
653
+ const s = t.items.map((r) => {
654
+ const i = e.measureText(r.formattedLabel, o).width;
655
+ return U + k + i;
656
+ });
657
+ return Math.max(...s) + Ge;
658
+ }, yn = (e) => (t, o) => {
659
+ const { labelLineHeight: n, labelFontWeight: s } = It(o, 1);
660
+ return e.measureText(t, {
661
+ family: T,
662
+ size: n,
663
+ weight: s
664
+ });
665
+ }, Tn = (e) => (t, o) => {
666
+ const { fontSize: n, fontWeight: s } = wt(t, 1);
667
+ return e.measureText(o, {
668
+ family: T,
669
+ size: n,
670
+ weight: s
671
+ });
672
+ }, En = (e) => ({
673
+ measureAxis: bn(e),
674
+ measureAxisLabel: () => uo,
675
+ measureLegend: pn(e),
676
+ measureTickLabel: (t) => e.measureText(t, { family: le, size: de }),
677
+ measureDifferenceArrowLabel: yn(e),
678
+ measureDataLabel: Tn(e),
679
+ measureHeadline: () => ({ width: 0, height: 0 }),
680
+ measureHeadlineItemWidths: () => []
681
+ });
682
+ class Sn {
683
+ constructor() {
684
+ this.currentFont = "";
685
+ const t = V().createCanvas(1, 1);
686
+ this.ctx = t.getContext("2d");
687
+ }
688
+ measureText(t, o) {
689
+ const n = S(o);
690
+ this.currentFont !== n && (this.ctx.font = n, this.currentFont = n);
691
+ const s = this.ctx.measureText(t);
692
+ return {
693
+ width: s.width,
694
+ height: s.fontBoundingBoxAscent + s.fontBoundingBoxDescent,
695
+ ascent: s.fontBoundingBoxAscent,
696
+ descent: s.fontBoundingBoxDescent
697
+ };
698
+ }
699
+ }
700
+ const Ln = {
701
+ background: "#ffffff",
702
+ textPrimary: "#111827",
703
+ textSecondary: "#6b7280",
704
+ gridLine: "#e5e7eb",
705
+ panelBorder: "#d1d5db",
706
+ legendPillBackground: "rgba(0, 0, 0, 0.04)",
707
+ legendPillBorder: "rgba(0, 0, 0, 0.08)"
708
+ }, Rn = {
709
+ background: "#0b0f17",
710
+ textPrimary: "#e5e7eb",
711
+ textSecondary: "#9ca3af",
712
+ gridLine: "#1f2937",
713
+ panelBorder: "#374151",
714
+ legendPillBackground: "rgba(255, 255, 255, 0.06)",
715
+ legendPillBorder: "rgba(255, 255, 255, 0.12)"
716
+ }, On = async ({ input: e, data: t }, o) => {
717
+ const s = Oe().compile({ input: e, data: t });
718
+ if (!s.ok)
719
+ throw new Error(
720
+ `Failed to compile graph spec: ${s.errors.map((r) => r.message).join("; ")}`
721
+ );
722
+ return et(s.compiled, o);
723
+ }, et = async (e, t) => {
724
+ const { Canvas: o } = await vt();
725
+ if (Mt(), t.fonts)
726
+ for (const b of t.fonts) Ne(b);
727
+ const n = Bt({
728
+ legends: e.guides.legends,
729
+ numberFormat: e.config.numberFormat,
730
+ parsingLocale: e.config.parsingLocale,
731
+ formattingLocale: t.formattingLocale
732
+ }), s = { width: t.width, height: t.height }, { headerHeight: r, footerHeight: i } = sn(e.config.content, s), a = {
733
+ headerSize: { width: t.width, height: r },
734
+ footerSize: { width: t.width, height: i }
735
+ }, d = new Sn(), { layout: l, formattedAxes: c } = new Nt(En(d)).compile({
736
+ axes: e.guides.axes,
737
+ numberFormat: e.config.numberFormat,
738
+ parsingLocale: e.config.parsingLocale,
739
+ formattedLegends: n,
740
+ containerSize: { width: t.width, height: t.height },
741
+ externalMeasurements: a,
742
+ formattingLocale: t.formattingLocale,
743
+ layout: e.config.layout
744
+ }), f = t.pixelRatio ?? 2, u = new o(t.width * f, t.height * f), h = u.getContext("2d");
745
+ h.scale(f, f);
746
+ const g = t.colorScheme ?? "light";
747
+ return _n(
748
+ h,
749
+ g === "dark" ? Rn : Ln,
750
+ g,
751
+ t.width,
752
+ t.height,
753
+ e,
754
+ c,
755
+ n,
756
+ l,
757
+ d,
758
+ t.formattingLocale
759
+ ), u.encode("png");
760
+ }, _n = (e, t, o, n, s, r, i, a, d, l, c) => {
761
+ e.fillStyle = t.background, e.fillRect(0, 0, n, s), dn(e, i, r.guides.panel, d.panel, t), e.save(), e.beginPath(), e.rect(d.panel.x, d.panel.y, d.panel.width, d.panel.height), e.clip();
762
+ const f = {
763
+ coordSystem: r.coordSystem,
764
+ panel: d.panel,
765
+ compiled: r,
766
+ theme: t,
767
+ colorScheme: o,
768
+ textMeasurer: l,
769
+ formattingLocale: c
770
+ };
771
+ for (const u of r.layers) {
772
+ const h = Fe(u, r.coordSystem);
773
+ h && ro(h, e, { ...f, layer: u });
774
+ }
775
+ e.restore();
776
+ for (const u of i) {
777
+ const h = d.axes[u.position];
778
+ h && Wo(e, u, h, d.panel, t);
779
+ }
780
+ for (const u of r.guides.axes) {
781
+ const h = d.axisLabels[u.position];
782
+ h && Yo(e, u, h, t);
783
+ }
784
+ fn(e, a, d.legends, t), an(e, r.config.content, d.header, t, { width: n, height: s }), ln(e, r.config.content, d.footer, t, { width: n, height: s });
785
+ };
786
+ class wn extends Error {
787
+ constructor(t) {
788
+ super(t), this.name = "ChartConfigRenderError";
789
+ }
790
+ }
791
+ const Hn = async (e, t) => {
792
+ const o = fo(e);
793
+ if (!o.ok)
794
+ throw new wn(o.reason);
795
+ return et(o.compiled, t);
796
+ };
797
+ export {
798
+ Fn as CANVAS_CAPABILITY_MATRIX,
799
+ He as CANVAS_SUPPORTED_GRAPH_TYPES,
800
+ so as CANVAS_UNSUPPORTED_GRAPH_TYPES,
801
+ wn as ChartConfigRenderError,
802
+ Sn as NodeCanvasTextMeasurer,
803
+ fo as canRenderConfigWithCanvas,
804
+ Rn as darkTheme,
805
+ Mt as ensureDefaultFonts,
806
+ Ln as lightTheme,
807
+ vt as loadCanvasRuntime,
808
+ co as parseChartConfig,
809
+ Ne as registerFont,
810
+ Hn as renderChartConfigToPng,
811
+ On as renderGraphToPng
812
+ };
package/package.json ADDED
@@ -0,0 +1,79 @@
1
+ {
2
+ "name": "@graphysdk/node-renderer",
3
+ "author": "Graphy",
4
+ "description": "Headless PNG renderer for Graphy charts (Node)",
5
+ "homepage": "https://docs.graphy.dev/sdk-next/rendering/provider-and-renderer",
6
+ "license": "SEE LICENSE IN LICENSE.md",
7
+ "keywords": [
8
+ "graphy",
9
+ "chart",
10
+ "png",
11
+ "node",
12
+ "headless",
13
+ "renderer"
14
+ ],
15
+ "version": "1.8.1-beta.1786024899180",
16
+ "type": "module",
17
+ "sideEffects": [
18
+ "**/version.ts"
19
+ ],
20
+ "types": "./dist/index.d.ts",
21
+ "engines": {
22
+ "node": ">=20"
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "assets",
27
+ "package.json",
28
+ "LICENSE.md",
29
+ "README.md"
30
+ ],
31
+ "exports": {
32
+ ".": {
33
+ "types": "./dist/index.d.ts",
34
+ "import": "./dist/index.mjs",
35
+ "require": "./dist/index.cjs"
36
+ }
37
+ },
38
+ "dependencies": {
39
+ "@fontsource/inter": "5.2.8",
40
+ "@napi-rs/canvas": "^0.1.76",
41
+ "d3-shape": "^3.2.0",
42
+ "@graphysdk/viz-engine": "1.8.1-beta.1786024899180"
43
+ },
44
+ "devDependencies": {
45
+ "@rollup/plugin-replace": "^6.0.3",
46
+ "@types/d3-shape": "^3.1.8",
47
+ "@types/node": "^24.1.0",
48
+ "rollup-plugin-node-externals": "^8.1.1",
49
+ "tslib": "^2.8.1",
50
+ "tsx": "^4.7.1",
51
+ "vite": "^6.4.3",
52
+ "vite-plugin-dts": "^4.5.4",
53
+ "vitest": "^4.1.0",
54
+ "@graphytools/eslint-plugin-graphy": "1.0.0",
55
+ "@graphytools/eslint-config": "0.0.1",
56
+ "@graphytools/typescript-config": "0.0.1",
57
+ "@graphytools/vitest-config": "1.0.0"
58
+ },
59
+ "lint-staged": {
60
+ "*.{ts,tsx,js,jsx,json,css,md}": [
61
+ "prettier --write"
62
+ ],
63
+ "*.{ts,tsx}": [
64
+ "eslint --max-warnings=0 --fix"
65
+ ]
66
+ },
67
+ "scripts": {
68
+ "build": "vite build",
69
+ "dev": "tsx watch --conditions=local src/dev/server.ts",
70
+ "test": "vitest run",
71
+ "test:dist": "vitest run src/__tests__/build-output.spec.ts",
72
+ "test:watch": "TZ=utc vitest",
73
+ "lint": "eslint . --max-warnings 0",
74
+ "lint:fix": "eslint . --fix",
75
+ "format": "prettier --write .",
76
+ "format:check": "prettier --check .",
77
+ "typecheck": "tsc --noEmit"
78
+ }
79
+ }