mudlet-map-renderer 0.33.0-konva → 0.34.1-konva

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.
Files changed (34) hide show
  1. package/README.md +141 -21
  2. package/dist/ExitRenderer.d.ts +44 -0
  3. package/dist/ScenePipeline.d.ts +15 -2
  4. package/dist/Viewport.d.ts +12 -0
  5. package/dist/backend/DrawingBackend.d.ts +49 -6
  6. package/dist/export/CanvasExporter.d.ts +67 -0
  7. package/dist/export/Exporter.d.ts +57 -0
  8. package/dist/export/PngExporter.d.ts +23 -0
  9. package/dist/export/SvgExporter.d.ts +15 -0
  10. package/dist/export/canvasToBytes.d.ts +19 -0
  11. package/dist/index.cjs +4 -4
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.ts +19 -17
  14. package/dist/index.mjs +1184 -1109
  15. package/dist/index.mjs.map +1 -1
  16. package/dist/overlay/AmbientLightOverlay.d.ts +32 -0
  17. package/dist/overlay/LiveEffect.d.ts +33 -0
  18. package/dist/overlay/SceneOverlay.d.ts +63 -0
  19. package/dist/rendering/KonvaRenderBackend.d.ts +31 -9
  20. package/dist/rendering/MapRenderer.d.ts +64 -36
  21. package/dist/scene/AmbientLightStyle.d.ts +7 -2
  22. package/dist/{backend/BlueprintBackend.d.ts → style/BlueprintStyle.d.ts} +2 -12
  23. package/dist/{backend/IsometricBackend.d.ts → style/IsometricStyle.d.ts} +5 -6
  24. package/dist/{backend/NeonBackend.d.ts → style/NeonStyle.d.ts} +2 -12
  25. package/dist/{backend/ParchmentBackend.d.ts → style/ParchmentStyle.d.ts} +6 -16
  26. package/dist/{backend/SketchyBackend.d.ts → style/SketchyStyle.d.ts} +4 -13
  27. package/dist/style/index.d.ts +32 -0
  28. package/dist/types/Settings.d.ts +3 -17
  29. package/package.json +1 -1
  30. package/dist/HeadlessRenderer.d.ts +0 -64
  31. package/dist/MapRenderer.d.ts +0 -23
  32. package/dist/Renderer.d.ts +0 -56
  33. package/dist/rendering/SvgRenderBackend.d.ts +0 -21
  34. package/dist/types/OverlayPlugin.d.ts +0 -44
package/README.md CHANGED
@@ -186,37 +186,97 @@ For large maps, spatial culling hides off-screen rooms for better performance:
186
186
  renderer.setCullingMode('indexed');
187
187
  ```
188
188
 
189
- ### Export
189
+ ### Styles
190
+
191
+ A `Style` is a target-agnostic visual transformer. One style drives the
192
+ interactive canvas *and* every exporter — set it once and it applies to SVG,
193
+ PNG, and anything else you export.
190
194
 
191
195
  ```ts
192
- // SVG export
193
- const svg = renderer.exportSvg();
194
- const svgCentered = renderer.exportSvg({ roomId: 1234, padding: 5 });
196
+ import {
197
+ compose, identityStyle,
198
+ Parchment, Blueprint, Neon, Sketchy, Isometric,
199
+ } from 'mudlet-map-renderer';
200
+
201
+ // Single style
202
+ renderer.setStyle(Parchment);
203
+
204
+ // Compose a chain (left → right = inner → outer)
205
+ renderer.setStyle(compose(
206
+ Parchment,
207
+ Sketchy({ jitter: 0.012, color: '#4a3728' }),
208
+ Isometric({ rotation: 30, depth: 0.18 }),
209
+ ));
210
+
211
+ // Clear the current style
212
+ renderer.setStyle(identityStyle);
213
+ // or
214
+ renderer.clearStyle();
215
+ ```
216
+
217
+ Built-in styles:
218
+
219
+ | Style | Effect |
220
+ |---|---|
221
+ | `Parchment` | Warm sepia / aged-paper palette |
222
+ | `Blueprint` | White lines on deep blue |
223
+ | `Neon` | Glowing neon outlines on dark background |
224
+ | `Sketchy({ jitter, color })` | Hand-drawn pencil wobble |
225
+ | `Isometric({ rotation?, depth? })` | 2:1 iso projection with optional cubes |
226
+
227
+ Custom styles extend `BaseStyle<Inner>` and override only the draw calls they
228
+ transform — see the built-ins for examples.
229
+
230
+ ### Export
195
231
 
196
- // SVG with explicit overlays
197
- const svgWithOverlays = renderer.exportSvg({
232
+ Exporters are plug-ins: a new output format is a new `Exporter<T>` class,
233
+ not a new `MapRenderer` method.
234
+
235
+ ```ts
236
+ import {
237
+ SvgExporter, PngExporter, PngBlobExporter, CanvasExporter,
238
+ } from 'mudlet-map-renderer';
239
+
240
+ // SVG (string)
241
+ const svg = renderer.export(new SvgExporter());
242
+ const svgCentered = renderer.export(new SvgExporter({ roomId: 1234, padding: 5 }));
243
+ const svgTyped = renderer.export(new SvgExporter({
198
244
  overlays: {
199
245
  position: { roomId: 1234 },
200
246
  highlights: [{ roomId: 100, color: '#ff0000' }],
201
247
  paths: [{ locations: [101, 102, 103], color: '#00ff00' }],
202
248
  },
203
- });
249
+ }));
204
250
 
205
- // PNG export (data URL)
206
- const pngDataUrl = renderer.exportPng({ pixelRatio: 2 });
251
+ // PNG data URL
252
+ const pngUrl = renderer.export(new PngExporter({ pixelRatio: 2 }));
207
253
 
208
- // PNG export (Blob)
209
- const blob = await renderer.exportPngBlob({ pixelRatio: 2 });
254
+ // PNG as Blob
255
+ const blob = await renderer.export(new PngBlobExporter({ pixelRatio: 2 }));
210
256
 
211
- // Canvas export (specific region)
212
- const canvas = renderer.renderToCanvas({
257
+ // Headless PNG bytes at a specific size (portable — works in browser + Node)
258
+ const png = renderer.export(new PngBytesExporter({
213
259
  width: 1920,
214
260
  height: 1080,
215
261
  roomId: 1234,
216
262
  padding: 5,
217
- });
263
+ }));
264
+ // fs.writeFileSync('out.png', png!); // Node
265
+ // new Blob([png!], { type: 'image/png' }); // Browser
266
+
267
+ // Canvas handle (if you need to draw more on it, attach to DOM, etc.)
268
+ const canvas = renderer.export(new CanvasExporter({
269
+ width: 1920,
270
+ height: 1080,
271
+ }));
218
272
  ```
219
273
 
274
+ Style + export compose: the style currently applied with `setStyle` is passed
275
+ to every exporter, so the SVG, the PNG, and the on-screen canvas stay in sync.
276
+
277
+ Writing a new exporter — e.g. PDF — means shipping a class that implements
278
+ `Exporter<Uint8Array>`. No changes to `MapRenderer`.
279
+
220
280
  ### Headless rendering (no DOM)
221
281
 
222
282
  For server-side or offscreen rendering, omit the container argument:
@@ -225,10 +285,45 @@ For server-side or offscreen rendering, omit the container argument:
225
285
  const renderer = new MapRenderer(mapReader, createSettings());
226
286
 
227
287
  renderer.drawArea(42, 0);
228
- renderer.setPosition(1234);
288
+ renderer.state.positionRoomId = 1234; // mark player position without auto-centering
229
289
 
230
- const svg = renderer.exportSvg({ padding: 5 });
231
- const canvas = renderer.renderToCanvas({ width: 1920, height: 1080 });
290
+ const svg = renderer.export(new SvgExporter({ padding: 5 }));
291
+ const png = renderer.export(new PngBytesExporter({ width: 1920, height: 1080 }));
292
+ ```
293
+
294
+ ### Overlays
295
+
296
+ Two kinds, picked by what you need:
297
+
298
+ - **`SceneOverlay`** — target-agnostic, appears in every output (interactive
299
+ canvas + every exporter). Use for static scene content (badges, annotations).
300
+ - **`LiveEffect`** — interactive canvas only, receives a Konva layer and
301
+ viewport updates for animation. Skipped by exporters.
302
+
303
+ ```ts
304
+ import type { SceneOverlay, LiveEffect } from 'mudlet-map-renderer';
305
+
306
+ // Scene overlay — uses target-agnostic draw primitives
307
+ class BadgeOverlay implements SceneOverlay {
308
+ render(target, state, bounds) {
309
+ const g = target.createGroup(0, 0);
310
+ target.addCircle(g, { cx: 5, cy: 5, radius: 0.4, fill: '#ff0' });
311
+ return g;
312
+ }
313
+ }
314
+
315
+ renderer.addSceneOverlay('badge', new BadgeOverlay());
316
+ renderer.removeSceneOverlay('badge');
317
+
318
+ // Live effect — gets a Konva layer, interactive only
319
+ class Pulse implements LiveEffect {
320
+ attach(layer) { /* add Konva shapes */ }
321
+ updateViewport(bounds, scale) { /* react to pan/zoom */ }
322
+ destroy() { /* cleanup */ }
323
+ }
324
+
325
+ renderer.addLiveEffect('pulse', new Pulse());
326
+ renderer.removeLiveEffect('pulse');
232
327
  ```
233
328
 
234
329
  ### Cleanup
@@ -360,16 +455,41 @@ MudletMapReader.exportJson(map, 'map.json');
360
455
  | `setZoom(zoom)` | Set zoom level |
361
456
  | `zoomToCenter(zoom)` | Zoom keeping center fixed |
362
457
  | `fitArea()` | Fit the full area in view |
363
- | `exportSvg(options?)` | Export as SVG string |
364
- | `exportPng(options?)` | Export as PNG data URL |
365
- | `exportPngBlob(options?)` | Export as PNG Blob |
366
- | `renderToCanvas(options)` | Export a region to canvas |
458
+ | `setStyle(style)` | Apply a visual style (interactive + exporters) |
459
+ | `clearStyle()` | Remove the current style |
460
+ | `export(exporter)` | Run an `Exporter<T>` and return its output |
461
+ | `addSceneOverlay(id, overlay)` | Add a target-agnostic overlay |
462
+ | `removeSceneOverlay(id)` | Remove a scene overlay |
463
+ | `addLiveEffect(id, effect)` | Add an interactive-only animated effect |
464
+ | `removeLiveEffect(id)` | Remove a live effect |
367
465
  | `refresh()` | Force a full re-render |
368
466
  | `on(event, handler)` | Subscribe to an event |
369
467
  | `off(event, handler)` | Unsubscribe from an event |
370
468
  | `setCullingMode(mode)` | Set culling strategy |
371
469
  | `destroy()` | Release all resources |
372
470
 
471
+ ### Exporters
472
+
473
+ | Exporter | Output |
474
+ |---|---|
475
+ | `SvgExporter({ roomId?, padding?, overlays? })` | `string` — SVG document |
476
+ | `PngExporter({ pixelRatio? })` | `string` — PNG data URL (current viewport) |
477
+ | `PngBlobExporter({ pixelRatio? })` | `Promise<Blob>` — PNG Blob (browser only) |
478
+ | `PngBytesExporter({ width, height, roomId?, padding?, overlays?, mimeType?, quality? })` | `Uint8Array` — headless PNG/JPEG bytes; portable (browser + Node) |
479
+ | `CanvasExporter({ width, height, roomId?, padding?, overlays? })` | `ExportCanvas` — canvas handle; reframes to fit |
480
+
481
+ ### Styles
482
+
483
+ | Style | Constructor / Usage |
484
+ |---|---|
485
+ | `Parchment` | `setStyle(Parchment)` |
486
+ | `Blueprint` | `setStyle(Blueprint)` |
487
+ | `Neon` | `setStyle(Neon)` |
488
+ | `Sketchy(opts)` | `setStyle(Sketchy({ jitter, color }))` |
489
+ | `Isometric(opts)` | `setStyle(Isometric({ rotation?, depth? }))` |
490
+ | `compose(...)` | Chain multiple styles into one |
491
+ | `identityStyle` | Pass-through; equivalent to `clearStyle()` |
492
+
373
493
  ### `PathFinder`
374
494
 
375
495
  | Method | Description |
@@ -36,6 +36,18 @@ export type ExitDrawData = {
36
36
  };
37
37
  /** Set when this exit leads to a room in a different area (cross-area exit). */
38
38
  targetRoomId?: number;
39
+ /** Source room center in map coords — used to compute label direction. */
40
+ from?: {
41
+ x: number;
42
+ y: number;
43
+ };
44
+ /** Arrow tip / far endpoint in map coords — anchor for placed labels. */
45
+ tip?: {
46
+ x: number;
47
+ y: number;
48
+ };
49
+ /** Stroke colour of the rendered arrow — used to colour area-exit labels. */
50
+ arrowColor?: string;
39
51
  };
40
52
  export default class ExitRenderer {
41
53
  private mapReader;
@@ -53,6 +65,29 @@ export default class ExitRenderer {
53
65
  /**
54
66
  * Returns hit-zone bounds for special exits (custom lines) that lead to rooms in another area.
55
67
  */
68
+ /**
69
+ * Hit zones for cross-area inner exits (up/down/in/out). These are drawn as
70
+ * triangles inside the room, not through the exit pipeline, so they need
71
+ * their own plumbing for area-exit labelling and clickability.
72
+ */
73
+ getInnerExitAreaTargets(room: MapData.Room): {
74
+ bounds: {
75
+ x: number;
76
+ y: number;
77
+ width: number;
78
+ height: number;
79
+ };
80
+ targetRoomId: number;
81
+ from: {
82
+ x: number;
83
+ y: number;
84
+ };
85
+ tip: {
86
+ x: number;
87
+ y: number;
88
+ };
89
+ arrowColor: string;
90
+ }[];
56
91
  getSpecialExitAreaTargets(room: MapData.Room): {
57
92
  bounds: {
58
93
  x: number;
@@ -61,5 +96,14 @@ export default class ExitRenderer {
61
96
  height: number;
62
97
  };
63
98
  targetRoomId: number;
99
+ from: {
100
+ x: number;
101
+ y: number;
102
+ };
103
+ tip: {
104
+ x: number;
105
+ y: number;
106
+ };
107
+ arrowColor: string;
64
108
  }[];
65
109
  }
@@ -24,6 +24,18 @@ export type StandaloneExitEntry = {
24
24
  export type AreaExitHitZone = {
25
25
  bounds: Bounds;
26
26
  targetRoomId: number;
27
+ /** Source room center — used to compute arrow direction for labels. Optional for back-compat. */
28
+ from?: {
29
+ x: number;
30
+ y: number;
31
+ };
32
+ /** Arrow tip / far endpoint — anchor for placed labels. Optional for back-compat. */
33
+ tip?: {
34
+ x: number;
35
+ y: number;
36
+ };
37
+ /** Stroke colour of the rendered arrow — used to colour area-exit labels. */
38
+ arrowColor?: string;
27
39
  };
28
40
  export type SceneBuildResult = {
29
41
  roomNodes: Map<number, RoomNodeEntry>;
@@ -34,8 +46,8 @@ export type SceneBuildResult = {
34
46
  * Backend-agnostic scene composition pipeline.
35
47
  * Drives a DrawingBackend + LayerNode to render the full map scene.
36
48
  *
37
- * Both KonvaRenderBackend and SvgRenderBackend use this same pipeline
38
- * with their respective DrawingBackend implementations.
49
+ * Both the interactive KonvaRenderBackend and exporters (SvgExporter,
50
+ * CanvasExporter, …) drive this pipeline with their respective DrawingBackend.
39
51
  */
40
52
  export declare class ScenePipeline {
41
53
  private readonly mapReader;
@@ -70,6 +82,7 @@ export declare class ScenePipeline {
70
82
  renderExitData(data: ExitDrawData): GroupNode;
71
83
  private renderArrow;
72
84
  private renderLabels;
85
+ private renderAreaExitLabels;
73
86
  private renderAreaName;
74
87
  }
75
88
  export {};
@@ -52,6 +52,18 @@ export declare class Viewport {
52
52
  * Center on a map coordinate, with optional animation.
53
53
  */
54
54
  panToMapPointAnimated(x: number, y: number, instant: boolean): void;
55
+ /**
56
+ * Compute the zoom level that would fit the given map bounds in the current
57
+ * viewport (with the same padding/insets as {@link fitToMapBounds}). Useful
58
+ * for updating `minZoom` to lock zoom-out to an area without changing the
59
+ * current zoom or position.
60
+ */
61
+ computeFitZoom(minX: number, maxX: number, minY: number, maxY: number, insets?: {
62
+ top?: number;
63
+ right?: number;
64
+ bottom?: number;
65
+ left?: number;
66
+ }): number;
55
67
  /**
56
68
  * Fit the viewport to show the given map bounds with padding.
57
69
  * Optional `insets` (screen pixels) reserve space at each edge — content
@@ -1,9 +1,12 @@
1
1
  /**
2
- * Abstract interface for creating visual nodes.
3
- * Implement this to swap the rendering engine (Konva, PixiJS, raw Canvas2D, etc.).
2
+ * Low-level drawing primitives. A {@link DrawingBackend} is a thin abstraction
3
+ * over per-node draw calls. Leaf implementations target a specific rendering
4
+ * engine (Konva, raw Canvas2D, SVG strings, …); decorator implementations
5
+ * ({@link BaseStyle}) wrap another backend and transform the calls.
4
6
  *
5
- * GroupNode is the opaque handle returned by createGroup(). The culling system
6
- * and renderer use it for visibility toggling, positioning, and cleanup.
7
+ * End users should rarely touch this directly prefer {@link Style} factories
8
+ * (Parchment, Sketchy(…), …) with {@link MapRenderer.setStyle}, and
9
+ * {@link Exporter} implementations with {@link MapRenderer.export}.
7
10
  */
8
11
  export interface GroupNode {
9
12
  setVisible(visible: boolean): void;
@@ -119,11 +122,51 @@ export interface DrawingBackend {
119
122
  };
120
123
  /**
121
124
  * Map-space → render-space transform. Identity for flat backends; non-identity
122
- * for backends that warp coordinates (e.g. {@link IsometricBackend}).
125
+ * for styles that warp coordinates (e.g. `IsometricStyle`).
123
126
  * Decorators delegate to their inner backend.
124
- * MapRenderer auto-applies this to culling and grid rendering when the backend is set.
125
127
  */
126
128
  getTransform(): CoordFn;
127
129
  /** Inverse of {@link getTransform}. */
128
130
  getInverseTransform(): CoordFn;
129
131
  }
132
+ /**
133
+ * Abstract base for style (decorator) backends. Forwards every
134
+ * {@link DrawingBackend} method to `this.inner` by default; subclasses override
135
+ * only the methods they transform. Generic over the wrapped inner type so
136
+ * tooling preserves specific types through chains where useful.
137
+ */
138
+ export declare abstract class BaseStyle<Inner extends DrawingBackend = DrawingBackend> implements DrawingBackend {
139
+ protected readonly inner: Inner;
140
+ constructor(inner: Inner);
141
+ createGroup(x: number, y: number): GroupNode;
142
+ addRect(parent: GroupNode, config: RectConfig): void;
143
+ addCircle(parent: GroupNode, config: CircleConfig): void;
144
+ addLine(parent: GroupNode, config: LineConfig): void;
145
+ addPolygon(parent: GroupNode, config: PolygonConfig): void;
146
+ addText(parent: GroupNode, config: TextConfig): void;
147
+ addImage(parent: GroupNode, config: ImageConfig): void;
148
+ supportsBatchExitRendering(): boolean;
149
+ getExitDepthOffset(): {
150
+ x: number;
151
+ y: number;
152
+ };
153
+ getTransform(): CoordFn;
154
+ getInverseTransform(): CoordFn;
155
+ }
156
+ /**
157
+ * A {@link Style} is a target-agnostic transformer: given a {@link DrawingBackend}
158
+ * it returns a decorated one. The same style drives interactive canvas, SVG
159
+ * export, and any future target.
160
+ *
161
+ * Compose via {@link compose}; built-in styles live in `src/style`.
162
+ */
163
+ export type Style = (target: DrawingBackend) => DrawingBackend;
164
+ /** Identity style — passes the target through unchanged. Useful as a default. */
165
+ export declare const identityStyle: Style;
166
+ /**
167
+ * Compose a chain of {@link Style}s into a single Style.
168
+ *
169
+ * `compose(Parchment, Sketchy)` wraps with Parchment first, then Sketchy —
170
+ * Sketchy is the outermost decorator, i.e. its methods run first during rendering.
171
+ */
172
+ export declare function compose(...styles: Style[]): Style;
@@ -0,0 +1,67 @@
1
+ import { Exporter, ExportContext, ExportCanvas } from './Exporter';
2
+ export interface CanvasExportOptions {
3
+ /** Width of the output image in pixels. */
4
+ width: number;
5
+ /** Height of the output image in pixels. */
6
+ height: number;
7
+ /** Room ID to center the export on. If omitted, exports the full area. */
8
+ roomId?: number;
9
+ /** Padding in map units around the exported region. Default: 3 */
10
+ padding?: number;
11
+ /** Overlays to render over the scene (position marker, highlights, paths). */
12
+ overlays?: {
13
+ position?: {
14
+ roomId: number;
15
+ };
16
+ highlights?: Array<{
17
+ roomId: number;
18
+ color: string;
19
+ }>;
20
+ paths?: Array<{
21
+ locations: number[];
22
+ color: string;
23
+ }>;
24
+ };
25
+ }
26
+ /**
27
+ * Renders the current scene into a canvas at the requested width/height,
28
+ * reframing the viewport to fit the area (or a specific room) with padding.
29
+ *
30
+ * The returned {@link ExportCanvas} is the node-canvas-compatible object
31
+ * produced by Konva. Serialize with `.toBuffer('image/png')` in Node or
32
+ * `.toDataURL('image/png')` / `.toBlob(cb)` in the browser.
33
+ *
34
+ * Unlike {@link PngExporter} (which rasterizes the current on-screen viewport),
35
+ * `CanvasExporter` is the headless/programmatic path.
36
+ */
37
+ export declare class CanvasExporter implements Exporter<ExportCanvas | undefined> {
38
+ private readonly options;
39
+ constructor(options: CanvasExportOptions);
40
+ render({ backend }: ExportContext): ExportCanvas | undefined;
41
+ }
42
+ export interface PngBytesExportOptions extends CanvasExportOptions {
43
+ /** MIME type to encode. Defaults to `'image/png'`. */
44
+ mimeType?: string;
45
+ /** Encoder quality (0..1). Only used for lossy formats like `'image/jpeg'`. */
46
+ quality?: number;
47
+ }
48
+ /**
49
+ * Headless PNG/JPEG bytes at a specific width × height.
50
+ *
51
+ * Composes {@link CanvasExporter} with a portable `toDataURL` → `Uint8Array`
52
+ * decode, so callers get bytes directly without touching a canvas or casting
53
+ * to platform-specific types:
54
+ *
55
+ * ```ts
56
+ * const png = renderer.export(new PngBytesExporter({ width: 1920, height: 1080 }));
57
+ * fs.writeFileSync('out.png', png!); // Node
58
+ * new Blob([png!], { type: 'image/png' }); // Browser
59
+ * ```
60
+ *
61
+ * For JPEG: `new PngBytesExporter({ width, height, mimeType: 'image/jpeg', quality: 0.9 })`.
62
+ */
63
+ export declare class PngBytesExporter implements Exporter<Uint8Array | undefined> {
64
+ private readonly options;
65
+ constructor(options: PngBytesExportOptions);
66
+ render(context: ExportContext): Uint8Array | undefined;
67
+ }
@@ -0,0 +1,57 @@
1
+ import { MapState } from '../MapState';
2
+ import { Style } from '../backend/DrawingBackend';
3
+ import { SceneOverlay } from '../overlay/SceneOverlay';
4
+ import { InteractiveBackend } from '../rendering/MapRenderer';
5
+ /**
6
+ * Context handed to every {@link Exporter} by {@link MapRenderer.export}.
7
+ * Exporters pull whatever they need — state + style for SVG, backend for
8
+ * canvas/PNG rasterization — so callers never have to wire `renderer.backend`
9
+ * (or any other internal) into an exporter themselves.
10
+ */
11
+ export interface ExportContext {
12
+ readonly state: MapState;
13
+ readonly backend: InteractiveBackend;
14
+ readonly style: Style;
15
+ readonly sceneOverlays: Iterable<SceneOverlay>;
16
+ }
17
+ /**
18
+ * Pluggable output format. An {@link Exporter} consumes an {@link ExportContext}
19
+ * and returns an output of type `T` (a string, a Blob promise, a canvas, bytes…).
20
+ *
21
+ * Adding a new output format means implementing `Exporter<T>`. `MapRenderer`'s
22
+ * surface does not change — users just pass the new exporter to `renderer.export`.
23
+ *
24
+ * ```ts
25
+ * renderer.export(new SvgExporter({ padding: 5 })); // string
26
+ * renderer.export(new PngExporter({ pixelRatio: 2 })); // string (data URL)
27
+ * renderer.export(new CanvasExporter({ width, height })); // ExportCanvas
28
+ * ```
29
+ */
30
+ export interface Exporter<T> {
31
+ render(context: ExportContext): T;
32
+ }
33
+ /**
34
+ * Canvas returned by {@link InteractiveBackend.toCanvas} and
35
+ * {@link CanvasExporter}. Describes the portable surface common to the
36
+ * browser `HTMLCanvasElement` and the `canvas` package's Node-side Canvas:
37
+ *
38
+ * - Portable: `width`, `height`, `getContext`, `toDataURL`.
39
+ * - Browser: `toBlob(cb)` (optional here; use when serializing to a Blob).
40
+ *
41
+ * Platform-specific serializers (e.g. node-canvas's `toBuffer`) are intentionally
42
+ * not part of this interface. Cast when you need them:
43
+ *
44
+ * ```ts
45
+ * import type { Canvas } from 'canvas';
46
+ * const canvas = renderer.export(new CanvasExporter({width, height})) as unknown as Canvas;
47
+ * const png = canvas.toBuffer('image/png');
48
+ * ```
49
+ */
50
+ export interface ExportCanvas {
51
+ readonly width: number;
52
+ readonly height: number;
53
+ getContext(contextId: '2d', options?: any): CanvasRenderingContext2D | null;
54
+ toDataURL(type?: string, quality?: any): string;
55
+ /** Browser only; undefined in Node. */
56
+ toBlob?(callback: (blob: Blob | null) => void, type?: string, quality?: any): void;
57
+ }
@@ -0,0 +1,23 @@
1
+ import { Exporter, ExportContext } from './Exporter';
2
+ export interface PngExportOptions {
3
+ pixelRatio?: number;
4
+ }
5
+ /**
6
+ * Exports the current interactive canvas as a PNG data URL. Rasterizes the
7
+ * live Konva stage — whatever style the renderer currently has applied is
8
+ * what gets captured.
9
+ *
10
+ * For re-framing with an explicit width/height (e.g. server-side rendering),
11
+ * use {@link CanvasExporter} and serialize its output yourself.
12
+ */
13
+ export declare class PngExporter implements Exporter<string | undefined> {
14
+ private readonly options;
15
+ constructor(options?: PngExportOptions);
16
+ render({ backend }: ExportContext): string | undefined;
17
+ }
18
+ /** Companion to {@link PngExporter} that returns a Blob instead of a data URL. */
19
+ export declare class PngBlobExporter implements Exporter<Promise<Blob> | undefined> {
20
+ private readonly options;
21
+ constructor(options?: PngExportOptions);
22
+ render({ backend }: ExportContext): Promise<Blob> | undefined;
23
+ }
@@ -0,0 +1,15 @@
1
+ import { SvgExportOptions } from '../SvgTypes';
2
+ import { Exporter, ExportContext } from './Exporter';
3
+ /**
4
+ * Renders the current scene as an SVG string through the shared
5
+ * {@link ScenePipeline}. The context's style is applied so styled exports
6
+ * match the on-screen canvas; {@link SceneOverlay}s from the context are
7
+ * rendered into the SVG alongside the scene. `LiveEffect`s are intentionally
8
+ * ignored by design.
9
+ */
10
+ export declare class SvgExporter implements Exporter<string | undefined> {
11
+ private readonly options;
12
+ constructor(options?: SvgExportOptions);
13
+ render({ state, style, sceneOverlays }: ExportContext): string | undefined;
14
+ private renderBuiltInOverlays;
15
+ }
@@ -0,0 +1,19 @@
1
+ import { ExportCanvas } from './Exporter';
2
+ /**
3
+ * Serialize an {@link ExportCanvas} to raw bytes, synchronously, using the
4
+ * portable `toDataURL` API. Works identically in the browser and in Node.
5
+ *
6
+ * ```ts
7
+ * const canvas = renderer.export(new CanvasExporter({ width, height }));
8
+ * const png = canvasToBytes(canvas); // Uint8Array
9
+ * fs.writeFileSync('out.png', png); // Node
10
+ * // or: new Blob([png], { type: 'image/png' }) — browser
11
+ * ```
12
+ *
13
+ * @param canvas Canvas produced by {@link CanvasExporter} or the
14
+ * interactive backend's `exportCanvas`.
15
+ * @param mimeType Output format. Defaults to `'image/png'`. Pass `'image/jpeg'`
16
+ * + `quality` (0..1) for JPEG.
17
+ * @param quality JPEG quality (ignored for PNG).
18
+ */
19
+ export declare function canvasToBytes(canvas: ExportCanvas, mimeType?: string, quality?: number): Uint8Array;