@xrn07/figure-renderer
v0.2.2
Published
Zero-dependency pure TypeScript figure renderer - pure SVG string generation
Maintainers
Readme
@xrn07/figure-renderer
Zero-dependency pure TypeScript figure renderer - pure SVG string generation.
✨ Version 0.2.2 - Truly Generic Figure Renderer Now supports ANY figure type without errors - physics, chemistry, math, biology, and more. No more "unknown figure type" errors!
✨ Version 0.2.1 - Enhanced API Format Support Real-world API responses with flexible parsing, numeric magnitudes, and automatic field normalization.
⚠️ Version 0.2.0 Breaking Change New flexible DSL format. See MIGRATION_V0.2.0.md for details.
Features
- ✅ Zero runtime dependencies - Only ~8KB minified
- ✅ Universal - Works in Node.js, browser, Next.js SSR, PDF export
- ✅ Type-safe - Full TypeScript support
- ✅ Truly Generic - Works with ANY figure type (physics, chemistry, math, biology, etc.)
- ✅ No Errors - Renders placeholder SVG for unknown types instead of crashing
- ✅ Testable - SVG strings are easily snapshot-able
- ✅ Accessible - React component with proper ARIA labels
- ✅ International - Localization support via
altfield - ✅ API-friendly - Handles both JSON strings and objects, numeric or string magnitudes
Installation
npm install @xrn07/figure-rendererQuick Start
Server-side (Node.js)
import { renderFigure, parseFigureDSL } from "@xrn07/figure-renderer";
const dsl = parseFigureDSL(jsonString);
const svg = renderFigure(dsl);
console.log(svg); // <svg>...</svg>React/Next.js
import { FigureView } from "@xrn07/figure-renderer/react";
import { parseFigureDSL } from "@xrn07/figure-renderer";
// Parse DSL from API response (can be JSON string or object)
const dsl = parseFigureDSL(question.figure_dsl);
return <FigureView dsl={dsl} alt={question.figure_alt_bn} className='w-full' />;Real-World API Format (v0.2.1+)
The parser now handles real API responses directly:
// API returns object with numeric magnitude
const apiResponse = {
type: "force_diagram",
alt_bn: "বস্তুর উপর ক্রিয়াশীল বল",
arrows: [
{
direction: "right",
label: "10 N",
magnitude: 10, // Number instead of string
unit: "N",
},
],
};
// Parse from object (not just JSON string)
const dsl = parseFigureDSL(apiResponse);
const svg = renderFigure(dsl);API
renderFigure(dsl: FigureDSL): string
Main rendering function that converts DSL to SVG string.
const svg = renderFigure({
type: 'circuit',
width: 400,
height: 300,
components: [...]
});parseFigureDSL(str: string | object): FigureDSL | null
Safe JSON parser with Zod validation. Accepts both JSON strings and objects. Returns null for invalid input.
New in v0.2.1:
- ✅ Accepts objects directly (not just JSON strings)
- ✅ Handles
alt_bnfield (automatically maps toalt) - ✅ Supports numeric or string magnitudes
- ✅ Automatic field normalization for API responses
// Parse from JSON string
const dsl = parseFigureDSL(jsonString);
// Parse from object (NEW!)
const dsl = parseFigureDSL(apiResponseObject);
if (dsl) {
// Valid DSL with normalized fields
}<FigureView /> (React component)
React wrapper component for rendering figures.
<FigureView dsl={dsl} alt='Description of figure' className='w-full h-auto' />Supported Figure Types
The package is truly generic and supports ANY figure type:
- ✅
force_diagram- Force vector diagrams with compass directions (full renderer) - ✅ ANY custom type - Renders placeholder SVG for unknown types (physics, chemistry, math, biology diagrams)
No more errors! If you try to render an unsupported type like circuit_diagram, graph, geometry, etc., the package will render a nice placeholder instead of crashing.
Registering Custom Renderers
For specialized rendering of specific types, you can register custom renderers:
import { registerRenderer } from "@xrn07/figure-renderer";
registerRenderer("circuit_diagram", (dsl) => {
// Your custom rendering logic
return `<svg>...</svg>`;
});DSL Schema (v0.2.0)
The DSL is now generic and flexible. All figures share common base properties:
interface FigureDSL {
type: string; // Generic - any figure type supported
width?: number; // Optional - defaults to 400
height?: number; // Optional - defaults to 300
title?: string;
alt?: string; // Localization support
[key: string]: any; // Additional properties per figure type
}Force Diagram DSL
{
"type": "force_diagram",
"alt": "বস্তুটির উপর ক্রিয়াশীল বলগুলোর চিত্র",
"width": 400,
"height": 300,
"arrows": [
{
"direction": "down",
"label": "মাধ্যাকর্ষণ",
"magnitude": "100 N",
"unit": "N",
"color": "#dc2626"
},
{
"direction": "up",
"label": "সমতলের প্রতিক্রিয়া বল",
"magnitude": 100,
"unit": "N",
"color": "#2563eb"
}
]
}Compass Directions: up, down, left, right, up-left, up-right, down-left, down-right
New in v0.2.1: Magnitude can be string ("100 N") or number (100). The parser handles both formats automatically.
Optional Object:
{
"type": "force_diagram",
"object": {
"type": "box",
"x": 200,
"y": 150,
"width": 60,
"height": 60,
"label": "5kg"
},
"arrows": [...]
}See src/types.ts for complete type definitions.
Examples
Circuit Diagram
const circuitDSL = {
type: "circuit",
width: 400,
height: 300,
components: [
{ type: "battery", x: 100, y: 150, label: "12V" },
{ type: "resistor", x: 200, y: 100, label: "R1" },
// ... more components
],
};Force Diagram (v0.2.0+ Format)
const forceDSL = {
type: "force_diagram", // NEW: snake_case
width: 400, // Optional
height: 300, // Optional
arrows: [
// NEW: compass-based arrows
{
direction: "down", // Instead of angle: -90
label: "মাধ্যাকর্ষণ",
magnitude: "100 N", // Can be number or string
unit: "N",
color: "#dc2626",
},
{
direction: "up",
label: "Normal Force",
magnitude: 100, // Number works too
unit: "N",
color: "#2563eb",
},
],
};Force Diagram (Old v0.1.x Format - Deprecated)
const forceDSL = {
type: "forceDiagram",
width: 400,
height: 300,
object: { type: "box", x: 200, y: 150, width: 60, height: 60 },
forces: [
{
originX: 200,
originY: 150,
magnitude: 50,
angle: -90,
label: "F₁",
color: "#e53e3e",
},
// ... more forces
],
};Testing
import { renderFigure } from '@xrn07/figure-renderer';
test('renders circuit diagram', () => {
const dsl = { type: 'circuit', ... };
const svg = renderFigure(dsl);
expect(svg).toMatchSnapshot();
});Bundle Size
- Minified: ~8KB
- Gzipped: ~3KB
- Tree-shakeable: Unused renderers can be excluded
Changelog
v0.2.2 (2026-06-XX)
Major Enhancement:
- 🎉 Truly Generic Package - Now supports ANY figure type without errors
- ✨ No more "unknown figure type" crashes
- ✨ Renders placeholder SVG for unregistered types (physics, chemistry, math, biology, etc.)
- ✨ Graceful degradation instead of errors
- 🐛 Fixed arrow overlap for multiple arrows in same direction
- ✨ Automatic arrow stacking with proper spacing
Improvements:
- 📦 Package now works out-of-the-box for any educational content
- 🎯 Better developer experience - no need to pre-register every type
v0.2.1 (2026-06-XX)
Enhancements:
- ✨ Accept objects directly in
parseFigureDSL()(not just JSON strings) - ✨ Support numeric magnitudes (e.g.,
magnitude: 100instead ofmagnitude: "100 N") - ✨ Automatic field normalization (e.g.,
alt_bn→alt) - ✨ Flexible magnitude parsing with automatic unit handling
- 📝 Updated tests to verify real API format compatibility
Bug Fixes:
- 🐛 Fixed parsing of real-world API responses with numeric values
v0.2.0 (2025-01-XX)
Breaking Changes:
- 🔴 Migrated from rigid enum types to generic string-based type system
- 🔴 Changed force diagram format from angle-based to compass directions
- 🔴 Renamed
forceDiagram→force_diagram - 🔴 Replaced
forcesarray witharrowsarray
New Features:
- ✨ Compass direction system (up, down, left, right, etc.)
- ✨ Optional dimensions with sensible defaults
- ✨ Bengali and multilingual support via
altfield - ✨ Renderer registry for custom figure types
- ✨ Props customization system
License
MIT
