mudlet-map-renderer 0.27.1-konva → 0.29.0-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 (49) hide show
  1. package/README.md +392 -0
  2. package/dist/CullingManager.d.ts +76 -0
  3. package/dist/ExitRenderer.d.ts +4 -20
  4. package/dist/GridRenderer.d.ts +16 -0
  5. package/dist/HeadlessRenderer.d.ts +39 -38
  6. package/dist/InteractionHandler.d.ts +53 -0
  7. package/dist/MapGraph.d.ts +28 -0
  8. package/dist/MapRenderer.d.ts +4 -13
  9. package/dist/MapState.d.ts +104 -0
  10. package/dist/PathData.d.ts +1 -1
  11. package/dist/PathFinder.d.ts +2 -7
  12. package/dist/Renderer.d.ts +36 -316
  13. package/dist/RoomShapeRenderer.d.ts +16 -0
  14. package/dist/ScenePipeline.d.ts +75 -0
  15. package/dist/SvgTypes.d.ts +21 -0
  16. package/dist/TypedEventEmitter.d.ts +17 -0
  17. package/dist/Viewport.d.ts +68 -0
  18. package/dist/backend/BlueprintBackend.d.ts +30 -0
  19. package/dist/backend/DrawingBackend.d.ts +100 -0
  20. package/dist/backend/IsometricBackend.d.ts +71 -0
  21. package/dist/backend/KonvaBackend.d.ts +37 -0
  22. package/dist/backend/ParchmentBackend.d.ts +43 -0
  23. package/dist/backend/SketchyBackend.d.ts +36 -0
  24. package/dist/backend/SvgBackend.d.ts +46 -0
  25. package/dist/index.cjs +5 -2
  26. package/dist/index.cjs.map +1 -1
  27. package/dist/index.d.ts +29 -9
  28. package/dist/index.mjs +3581 -2293
  29. package/dist/index.mjs.map +1 -1
  30. package/dist/rendering/KonvaRenderBackend.d.ts +88 -0
  31. package/dist/rendering/MapRenderer.d.ts +111 -0
  32. package/dist/rendering/SvgRenderBackend.d.ts +21 -0
  33. package/dist/scene/AmbientLightStyle.d.ts +24 -0
  34. package/dist/scene/GridStyle.d.ts +21 -0
  35. package/dist/scene/InnerExitStyle.d.ts +24 -0
  36. package/dist/scene/OverlayRenderer.d.ts +35 -0
  37. package/dist/scene/OverlayStyle.d.ts +43 -0
  38. package/dist/scene/RoomStyle.d.ts +32 -0
  39. package/dist/scene/SpecialExitStyle.d.ts +35 -0
  40. package/dist/scene/StubStyle.d.ts +13 -0
  41. package/dist/scene/WeatherOverlay.d.ts +59 -0
  42. package/dist/types/OverlayPlugin.d.ts +40 -0
  43. package/dist/types/Settings.d.ts +189 -0
  44. package/dist/utils/color.d.ts +4 -0
  45. package/dist/utils/textMeasure.d.ts +12 -1
  46. package/package.json +11 -3
  47. package/dist/CanvasExporter.d.ts +0 -76
  48. package/dist/PathRenderer.d.ts +0 -14
  49. package/dist/SvgExporter.d.ts +0 -51
package/README.md ADDED
@@ -0,0 +1,392 @@
1
+ # mudlet-map-renderer
2
+
3
+ A rendering library for [Mudlet](https://www.mudlet.org/) map data. Takes Mudlet's JSON map format and renders interactive, zoomable maps using Konva, with SVG and PNG export support.
4
+
5
+ ![Nuln area rendered with default settings](docs/screenshot.png)
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ yarn add mudlet-map-renderer
11
+ ```
12
+
13
+ ## Quick start
14
+
15
+ ```ts
16
+ import { MudletMapReader } from 'mudlet-map-binary-reader';
17
+ import { MapRenderer, MapReader, createSettings } from 'mudlet-map-renderer';
18
+
19
+ // Read a Mudlet binary map file and export renderer-compatible data
20
+ const map = MudletMapReader.read('map.dat');
21
+ const { mapData, colors } = MudletMapReader.export(map);
22
+
23
+ const mapReader = new MapReader(mapData, colors);
24
+
25
+ // Create an interactive renderer attached to a DOM element
26
+ const container = document.getElementById('map') as HTMLDivElement;
27
+ const renderer = new MapRenderer(mapReader, createSettings(), container);
28
+
29
+ // Display an area (area ID, z-level)
30
+ renderer.drawArea(42, 0);
31
+
32
+ // Show the player position (centers the viewport on the room)
33
+ renderer.setPosition(1234);
34
+ ```
35
+
36
+ ## Usage
37
+
38
+ ### Settings
39
+
40
+ Create a settings object with `createSettings()` and customize before passing to the renderer:
41
+
42
+ ```ts
43
+ import { createSettings } from 'mudlet-map-renderer';
44
+
45
+ const settings = createSettings();
46
+ settings.roomSize = 0.8;
47
+ settings.roomShape = 'circle'; // 'rectangle' | 'circle' | 'roundedRectangle'
48
+ settings.backgroundColor = '#1a1a2e';
49
+ settings.lineColor = 'rgb(200, 220, 255)';
50
+ settings.gridEnabled = true;
51
+ settings.emboss = true;
52
+ settings.areaName = true;
53
+
54
+ // Player position marker
55
+ settings.playerMarker.strokeColor = '#ff6600';
56
+ settings.playerMarker.fillColor = '#ff6600';
57
+ settings.playerMarker.fillAlpha = 0.3;
58
+ settings.playerMarker.matchRoomShape = true;
59
+
60
+ const renderer = new MapRenderer(mapReader, settings, container);
61
+ ```
62
+
63
+ Settings are a shared mutable object. To change settings after the renderer is already running, modify the properties and call `refresh()`:
64
+
65
+ ```ts
66
+ // Change appearance at runtime
67
+ renderer.settings.roomShape = 'circle';
68
+ renderer.settings.emboss = true;
69
+ renderer.refresh();
70
+
71
+ // Background color has its own update method (CSS-only, no scene rebuild)
72
+ renderer.settings.backgroundColor = '#1a1a2e';
73
+ renderer.updateBackground();
74
+ ```
75
+
76
+ ### Navigation
77
+
78
+ ```ts
79
+ // Switch to a different area and z-level
80
+ renderer.drawArea(areaId, zIndex);
81
+
82
+ // Move player position (auto-switches area if needed)
83
+ renderer.setPosition(roomId);
84
+
85
+ // Move player without centering the viewport
86
+ renderer.setPosition(roomId, false);
87
+
88
+ // Update the marker without triggering area switch logic
89
+ renderer.updatePositionMarker(roomId);
90
+
91
+ // Center viewport on a room (with animation)
92
+ renderer.centerOn(roomId);
93
+
94
+ // Center instantly (no animation)
95
+ renderer.centerOn(roomId, true);
96
+
97
+ // Remove the position marker
98
+ renderer.clearPosition();
99
+ ```
100
+
101
+ ### Highlights and paths
102
+
103
+ ```ts
104
+ // Highlight rooms with a color
105
+ renderer.renderHighlight(roomId, '#ff0000');
106
+ renderer.renderHighlight(otherRoomId, 'rgba(0, 255, 0, 0.5)');
107
+
108
+ // Remove a specific highlight
109
+ renderer.removeHighlight(roomId);
110
+
111
+ // Check if a room is highlighted
112
+ if (renderer.hasHighlight(roomId)) { /* ... */ }
113
+
114
+ // Clear all highlights
115
+ renderer.clearHighlights();
116
+
117
+ // Render a path through a list of room IDs
118
+ renderer.renderPath([101, 102, 103, 104], '#66E64D');
119
+
120
+ // Clear all paths
121
+ renderer.clearPaths();
122
+ ```
123
+
124
+ ### Viewport control
125
+
126
+ ```ts
127
+ // Zoom
128
+ renderer.setZoom(1.5);
129
+ renderer.zoomToCenter(2.0);
130
+ console.log(renderer.getZoom());
131
+
132
+ // Fit the entire area in view
133
+ renderer.fitArea();
134
+
135
+ // Get current viewport bounds (in map coordinates)
136
+ const bounds = renderer.getViewportBounds();
137
+ // { minX, maxX, minY, maxY }
138
+
139
+ // Control resize behavior
140
+ renderer.centerOnResize = true; // re-center on window resize
141
+ renderer.minZoom = 0.1;
142
+ ```
143
+
144
+ ### Events
145
+
146
+ ```ts
147
+ renderer.on('roomclick', ({ roomId, position }) => {
148
+ console.log(`Clicked room ${roomId} at (${position.x}, ${position.y})`);
149
+ });
150
+
151
+ renderer.on('roomcontextmenu', ({ roomId, position }) => {
152
+ // Right-click or long-press on a room
153
+ });
154
+
155
+ renderer.on('areaexitclick', ({ targetRoomId, position }) => {
156
+ // Clicked an area exit — navigate to the target room's area
157
+ renderer.setPosition(targetRoomId);
158
+ });
159
+
160
+ renderer.on('mapclick', () => {
161
+ // Clicked on empty space
162
+ });
163
+
164
+ renderer.on('zoom', ({ zoom }) => {
165
+ console.log(`Zoom level: ${zoom}`);
166
+ });
167
+
168
+ renderer.on('pan', (bounds) => {
169
+ // Viewport moved
170
+ });
171
+
172
+ // Remove a listener
173
+ const handler = ({ roomId }) => { /* ... */ };
174
+ renderer.on('roomclick', handler);
175
+ renderer.off('roomclick', handler);
176
+ ```
177
+
178
+ ### Culling
179
+
180
+ For large maps, spatial culling hides off-screen rooms for better performance:
181
+
182
+ ```ts
183
+ // "indexed" (default) — bucket-based spatial index, best for large maps
184
+ // "basic" — simple bounds check per room
185
+ // "none" — render everything (useful for debugging)
186
+ renderer.setCullingMode('indexed');
187
+ ```
188
+
189
+ ### Export
190
+
191
+ ```ts
192
+ // SVG export
193
+ const svg = renderer.exportSvg();
194
+ const svgCentered = renderer.exportSvg({ roomId: 1234, padding: 5 });
195
+
196
+ // SVG with explicit overlays
197
+ const svgWithOverlays = renderer.exportSvg({
198
+ overlays: {
199
+ position: { roomId: 1234 },
200
+ highlights: [{ roomId: 100, color: '#ff0000' }],
201
+ paths: [{ locations: [101, 102, 103], color: '#00ff00' }],
202
+ },
203
+ });
204
+
205
+ // PNG export (data URL)
206
+ const pngDataUrl = renderer.exportPng({ pixelRatio: 2 });
207
+
208
+ // PNG export (Blob)
209
+ const blob = await renderer.exportPngBlob({ pixelRatio: 2 });
210
+
211
+ // Canvas export (specific region)
212
+ const canvas = renderer.renderToCanvas({
213
+ width: 1920,
214
+ height: 1080,
215
+ roomId: 1234,
216
+ padding: 5,
217
+ });
218
+ ```
219
+
220
+ ### Headless rendering (no DOM)
221
+
222
+ For server-side or offscreen rendering, omit the container argument:
223
+
224
+ ```ts
225
+ const renderer = new MapRenderer(mapReader, createSettings());
226
+
227
+ renderer.drawArea(42, 0);
228
+ renderer.setPosition(1234);
229
+
230
+ const svg = renderer.exportSvg({ padding: 5 });
231
+ const canvas = renderer.renderToCanvas({ width: 1920, height: 1080 });
232
+ ```
233
+
234
+ ### Cleanup
235
+
236
+ Call `destroy()` when you're done with a renderer to release all resources — DOM event listeners, Konva stages, and internal subscriptions:
237
+
238
+ ```ts
239
+ const renderer = new MapRenderer(mapReader, settings, container);
240
+
241
+ // ... use the renderer ...
242
+
243
+ // Tear down completely
244
+ renderer.destroy();
245
+ ```
246
+
247
+ This is important in SPAs and frameworks like React where components mount and unmount. Without calling `destroy()`, event listeners on `window` and the container element will leak.
248
+
249
+ #### React example
250
+
251
+ ```tsx
252
+ import { useEffect, useRef } from 'react';
253
+ import { MapRenderer, MapReader, createSettings } from 'mudlet-map-renderer';
254
+
255
+ function MudletMap({ mapData, envData, areaId, roomId }) {
256
+ const containerRef = useRef<HTMLDivElement>(null);
257
+
258
+ useEffect(() => {
259
+ if (!containerRef.current) return;
260
+
261
+ const mapReader = new MapReader(mapData, envData);
262
+ const renderer = new MapRenderer(mapReader, createSettings(), containerRef.current);
263
+
264
+ renderer.drawArea(areaId, 0);
265
+ if (roomId) renderer.setPosition(roomId);
266
+
267
+ return () => {
268
+ renderer.destroy();
269
+ };
270
+ }, [mapData, envData, areaId, roomId]);
271
+
272
+ return <div ref={containerRef} style={{ width: '100%', height: '100%' }} />;
273
+ }
274
+ ```
275
+
276
+ ## Pathfinding
277
+
278
+ ```ts
279
+ import { MapReader, PathFinder, MapGraph, computePathData } from 'mudlet-map-renderer';
280
+
281
+ const mapReader = new MapReader(mapData, envData);
282
+ const pathFinder = new PathFinder(mapReader);
283
+
284
+ // Find shortest path between two rooms (Dijkstra or A*)
285
+ const roomIds = pathFinder.findPath(startRoomId, endRoomId, 'astar');
286
+
287
+ if (roomIds) {
288
+ // Render the path on the map
289
+ renderer.renderPath(roomIds, '#66E64D');
290
+
291
+ // Or compute detailed path geometry for custom rendering
292
+ const pathData = computePathData(mapReader, renderer.settings, roomIds, '#66E64D');
293
+ }
294
+ ```
295
+
296
+ ## Exploration mode
297
+
298
+ Show only rooms the player has visited (fog of war):
299
+
300
+ ```ts
301
+ const mapReader = new MapReader(mapData, envData);
302
+
303
+ // Enable exploration with a set of visited room IDs
304
+ const visitedRooms = mapReader.decorateWithExploration(new Set([100, 101, 102, 200]));
305
+
306
+ // Add newly visited rooms as the player explores
307
+ visitedRooms.add(103);
308
+
309
+ // Areas automatically filter to only show visited rooms
310
+ const renderer = new MapRenderer(mapReader, createSettings(), container);
311
+ renderer.drawArea(areaId, 0);
312
+
313
+ // Disable exploration mode (show all rooms again)
314
+ mapReader.clearExplorationDecoration();
315
+ renderer.refresh();
316
+ ```
317
+
318
+ ## Map data format
319
+
320
+ The renderer expects data produced by [mudlet-map-binary-reader](https://github.com/Delwing/node-mudlet-map-binary-reader), which reads Mudlet's binary `map.dat` format and exports renderer-compatible structures:
321
+
322
+ ```ts
323
+ import { MudletMapReader } from 'mudlet-map-binary-reader';
324
+
325
+ const map = MudletMapReader.read('map.dat');
326
+ const { mapData, colors } = MudletMapReader.export(map);
327
+ // mapData: MapData.Map (Area[])
328
+ // colors: MapData.Env[]
329
+ ```
330
+
331
+ `MapReader` accepts these two values directly:
332
+
333
+ ```ts
334
+ const mapReader = new MapReader(mapData, colors);
335
+ ```
336
+
337
+ The binary reader can also write exported data to files for static use:
338
+
339
+ ```ts
340
+ MudletMapReader.export(map, 'output'); // writes .js files
341
+ MudletMapReader.exportJson(map, 'map.json');
342
+ ```
343
+
344
+ ## API reference
345
+
346
+ ### `MapRenderer`
347
+
348
+ | Method | Description |
349
+ |--------|-------------|
350
+ | `drawArea(id, zIndex)` | Display an area at a z-level |
351
+ | `setPosition(roomId, center?)` | Set player position (auto-switches area) |
352
+ | `updatePositionMarker(roomId)` | Update marker without area switch |
353
+ | `clearPosition()` | Remove position marker |
354
+ | `centerOn(roomId, instant?)` | Center viewport on a room |
355
+ | `renderHighlight(roomId, color)` | Highlight a room |
356
+ | `removeHighlight(roomId)` | Remove a highlight |
357
+ | `clearHighlights()` | Clear all highlights |
358
+ | `renderPath(roomIds, color?)` | Draw a path |
359
+ | `clearPaths()` | Clear all paths |
360
+ | `setZoom(zoom)` | Set zoom level |
361
+ | `zoomToCenter(zoom)` | Zoom keeping center fixed |
362
+ | `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 |
367
+ | `refresh()` | Force a full re-render |
368
+ | `on(event, handler)` | Subscribe to an event |
369
+ | `off(event, handler)` | Unsubscribe from an event |
370
+ | `setCullingMode(mode)` | Set culling strategy |
371
+ | `destroy()` | Release all resources |
372
+
373
+ ### `PathFinder`
374
+
375
+ | Method | Description |
376
+ |--------|-------------|
377
+ | `findPath(from, to, algorithm?)` | Find shortest path (`'dijkstra'` or `'astar'`) |
378
+
379
+ ### `MapReader`
380
+
381
+ | Method | Description |
382
+ |--------|-------------|
383
+ | `getArea(areaId)` | Get an area by ID |
384
+ | `getAreas()` | Get all areas |
385
+ | `getRoom(roomId)` | Get a room by ID |
386
+ | `getRooms()` | Get all rooms |
387
+ | `decorateWithExploration(visited?)` | Enable fog of war |
388
+ | `clearExplorationDecoration()` | Disable fog of war |
389
+
390
+ ## License
391
+
392
+ MIT
@@ -0,0 +1,76 @@
1
+ import { Settings } from './types/Settings';
2
+ import { GroupNode, LayerNode } from './backend/DrawingBackend';
3
+ export type CoordinateTransform = (x: number, y: number) => {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ export type RoomNodeEntry = {
8
+ room: MapData.Room;
9
+ group: GroupNode;
10
+ };
11
+ type Bounds = {
12
+ x: number;
13
+ y: number;
14
+ width: number;
15
+ height: number;
16
+ };
17
+ export type StandaloneExitEntry = {
18
+ group: GroupNode;
19
+ bounds: Bounds;
20
+ targetRoomId?: number;
21
+ };
22
+ export type StageInfo = {
23
+ scaleX(): number;
24
+ position(): {
25
+ x: number;
26
+ y: number;
27
+ };
28
+ width(): number;
29
+ height(): number;
30
+ };
31
+ /**
32
+ * Manages spatial indexing and viewport culling for rooms and exits.
33
+ * Toggles node visibility based on what's in the viewport.
34
+ * No direct Konva dependency.
35
+ */
36
+ export declare class CullingManager {
37
+ private readonly stageInfo;
38
+ private readonly roomLayer;
39
+ private readonly linkLayer;
40
+ private readonly settings;
41
+ roomNodes: Map<number, RoomNodeEntry>;
42
+ standaloneExitNodes: StandaloneExitEntry[];
43
+ spatialBucketSize: number;
44
+ private roomSpatialIndex;
45
+ private exitSpatialIndex;
46
+ private visibleRooms;
47
+ private visibleStandaloneExitNodes;
48
+ private bufferRoomSet;
49
+ private bufferExitSet;
50
+ private bufferRoomCandidates;
51
+ private bufferExitCandidates;
52
+ private standaloneExitBoundsRoomSize?;
53
+ private cullingScheduled;
54
+ private perfMonitor;
55
+ private coordinateTransform;
56
+ constructor(stageInfo: StageInfo, roomLayer: LayerNode, linkLayer: LayerNode, settings: Settings);
57
+ setCoordinateTransform(fn: CoordinateTransform | null): void;
58
+ private transformPoint;
59
+ private transformBounds;
60
+ computeBucketSize(): void;
61
+ clear(): void;
62
+ private getBucketKey;
63
+ private forEachBucket;
64
+ addRoomToSpatialIndex(entry: RoomNodeEntry): void;
65
+ addStandaloneExitToSpatialIndex(entry: StandaloneExitEntry): void;
66
+ findRoomAtMapPoint(mapX: number, mapY: number): MapData.Room | null;
67
+ markExitBoundsStale(): void;
68
+ setExitBoundsRoomSize(): void;
69
+ private collectRoomCandidates;
70
+ private collectStandaloneExitCandidates;
71
+ private refreshStandaloneExitBoundsIfNeeded;
72
+ scheduleCulling(): void;
73
+ updateCulling(): void;
74
+ private cullExits;
75
+ }
76
+ export {};
@@ -1,6 +1,6 @@
1
1
  import { default as Exit } from './reader/Exit';
2
2
  import { default as MapReader } from './reader/MapReader';
3
- import { Settings, Renderer } from './Renderer';
3
+ import { Settings } from './types/Settings';
4
4
  export type ExitDrawLine = {
5
5
  points: number[];
6
6
  stroke: string;
@@ -39,23 +39,17 @@ export type ExitDrawData = {
39
39
  };
40
40
  export default class ExitRenderer {
41
41
  private mapReader;
42
- private mapRenderer;
43
42
  private readonly settings;
44
- constructor(mapReader: MapReader, mapRenderer: Renderer | null, settings: Settings);
43
+ constructor(mapReader: MapReader, settings: Settings);
45
44
  /**
46
- * Get the edge point of a room based on its shape
45
+ * Get the edge point of a room based on its shape.
46
+ * The inset accounts for the inner border so exit lines reach the visible room edge.
47
47
  */
48
48
  private getRoomEdgePoint;
49
49
  renderData(exit: Exit, zIndex: number): ExitDrawData | undefined;
50
50
  renderDataWithColor(exit: Exit, color: string, zIndex: number): ExitDrawData | undefined;
51
51
  private renderTwoWayExitData;
52
52
  private renderOneWayExitData;
53
- render(exit: Exit, zIndex: number): import('konva/lib/Group').Group | import('konva/lib/shapes/Arrow').Arrow | undefined;
54
- renderWithColor(exit: Exit, color: string, zIndex: number): import('konva/lib/Group').Group | import('konva/lib/shapes/Arrow').Arrow | undefined;
55
- private renderTwoWayExit;
56
- private renderOneWayExit;
57
- renderAreaExit(room: MapData.Room, dir: MapData.direction, color?: string): import('konva/lib/shapes/Arrow').Arrow;
58
- renderSpecialExits(room: MapData.Room, overrideColor?: string): (import('konva/lib/shapes/Arrow').Arrow | import('konva/lib/shapes/Rect').Rect | import('konva/lib/shapes/Line').Line<import('konva/lib/shapes/Line').LineConfig>)[];
59
53
  /**
60
54
  * Returns hit-zone bounds for special exits (custom lines) that lead to rooms in another area.
61
55
  */
@@ -68,14 +62,4 @@ export default class ExitRenderer {
68
62
  };
69
63
  targetRoomId: number;
70
64
  }[];
71
- renderStubs(room: MapData.Room, color?: string): import('konva/lib/shapes/Line').Line<{
72
- points: number[];
73
- stroke: string;
74
- strokeWidth: number;
75
- perfectDrawEnabled: false;
76
- listening: false;
77
- }>[];
78
- private getSymbolColor;
79
- renderInnerExits(room: MapData.Room): import('konva/lib/Group').Group[];
80
- renderDoor(points: number[], type: 1 | 2 | 3): import('konva/lib/shapes/Rect').Rect;
81
65
  }
@@ -0,0 +1,16 @@
1
+ import { Settings, ViewportBounds } from './types/Settings';
2
+ import { DrawingBackend, LayerNode } from './backend/DrawingBackend';
3
+ /**
4
+ * Renders and caches the background grid on a dedicated layer.
5
+ * Grid lines are only recreated when the visible bounds change enough
6
+ * to cross a grid-line boundary. No direct Konva dependency.
7
+ */
8
+ export declare class GridRenderer {
9
+ private readonly layer;
10
+ private readonly settings;
11
+ private readonly backend;
12
+ private cachedBounds;
13
+ constructor(layer: LayerNode, settings: Settings, backend: DrawingBackend);
14
+ invalidateCache(): void;
15
+ render(viewportBounds: ViewportBounds): void;
16
+ }
@@ -1,41 +1,50 @@
1
1
  import { default as MapReader } from './reader/MapReader';
2
2
  import { default as Area } from './reader/Area';
3
- import { Settings } from './Renderer';
4
- import { MapRenderer } from './MapRenderer';
5
- import { SvgExportOptions } from './SvgExporter';
6
- import { CanvasExportOptions, CanvasExportOverlays } from './CanvasExporter';
3
+ import { Settings } from './types/Settings';
4
+ import { MapRenderer as MapRendererInterface } from './MapRenderer';
5
+ import { SvgExportOptions } from './SvgTypes';
6
+ export type CanvasExportOptions = {
7
+ /** Width of the output image in pixels. */
8
+ width: number;
9
+ /** Height of the output image in pixels. */
10
+ height: number;
11
+ /** Room ID to center the export on. If omitted, exports the full area. */
12
+ roomId?: number;
13
+ /** Padding in map units around the exported region. Default: 3 */
14
+ padding?: number;
15
+ };
16
+ export type CanvasExportOverlays = {
17
+ position?: {
18
+ roomId: number;
19
+ };
20
+ highlights?: Array<{
21
+ roomId: number;
22
+ color: string;
23
+ }>;
24
+ paths?: Array<{
25
+ locations: number[];
26
+ color: string;
27
+ }>;
28
+ };
7
29
  /**
8
- * A headless (no DOM / no Konva) renderer for server-side and backend use.
9
- * Provides the same stateful API as the browser Renderer for area display,
10
- * position tracking, path rendering, and highlights — then exports to SVG or
11
- * renders to a caller-provided Canvas2D context for PNG output.
30
+ * Backward-compatible headless renderer.
31
+ * Delegates to the unified MapRenderer in headless mode (no container).
12
32
  *
13
- * Usage:
33
+ * @deprecated Use MapRenderer directly (omit the container argument for headless mode):
14
34
  * ```
15
- * const renderer = new HeadlessRenderer(mapReader, settings);
35
+ * import { MapRenderer, createSettings } from 'mudlet-map-renderer';
36
+ *
37
+ * const renderer = new MapRenderer(mapReader, settings);
16
38
  * renderer.drawArea(areaId, 0);
17
39
  * renderer.setPosition(playerRoomId);
18
- * renderer.renderPath([roomA, roomB, roomC], '#ff0000');
19
40
  *
20
- * // SVG export (always available)
21
41
  * const svg = renderer.exportSvg({ padding: 5 });
22
- *
23
- * // PNG export (Node.js with 'canvas' package)
24
- * import { createCanvas } from 'canvas';
25
- * const canvas = createCanvas(1920, 1080);
26
- * renderer.renderToCanvas(canvas.getContext('2d'), { width: 1920, height: 1080 });
27
- * fs.writeFileSync('map.png', canvas.toBuffer('image/png'));
42
+ * const canvas = renderer.renderToCanvas({ width: 1920, height: 1080 });
28
43
  * ```
29
44
  */
30
- export declare class HeadlessRenderer implements MapRenderer {
31
- private readonly mapReader;
32
- readonly settings: Settings;
33
- private currentArea?;
34
- private currentAreaInstance?;
35
- private currentZIndex?;
36
- private positionRoomId?;
37
- private highlights;
38
- private paths;
45
+ export declare class HeadlessRenderer implements MapRendererInterface {
46
+ private readonly renderer;
47
+ get settings(): Settings;
39
48
  constructor(mapReader: MapReader, settings?: Settings);
40
49
  drawArea(id: number, zIndex: number): void;
41
50
  getCurrentArea(): Area | undefined;
@@ -47,17 +56,9 @@ export declare class HeadlessRenderer implements MapRenderer {
47
56
  removeHighlight(roomId: number): void;
48
57
  hasHighlight(roomId: number): boolean;
49
58
  clearHighlights(): void;
50
- /**
51
- * Export the current area as an SVG string.
52
- */
53
59
  exportSvg(options?: SvgExportOptions): string | undefined;
54
- /**
55
- * Render the current area to a Canvas2D context (for PNG export).
56
- * The caller provides the canvas and context — works with `canvas` npm package (Node.js)
57
- * or OffscreenCanvas (browser).
58
- */
59
- renderToCanvas(ctx: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D, options: Omit<CanvasExportOptions, 'overlays'> & {
60
+ renderToCanvas(options: CanvasExportOptions & {
60
61
  overlays?: CanvasExportOverlays;
61
- }): void;
62
- private buildOverlays;
62
+ }): any;
63
+ destroy(): void;
63
64
  }
@@ -0,0 +1,53 @@
1
+ import { Settings, RendererEventMap } from './types/Settings';
2
+ import { TypedEventEmitter } from './TypedEventEmitter';
3
+ import { Viewport } from './Viewport';
4
+ import { MapState } from './MapState';
5
+ type Bounds = {
6
+ x: number;
7
+ y: number;
8
+ width: number;
9
+ height: number;
10
+ };
11
+ type AreaExitHitZone = {
12
+ bounds: Bounds;
13
+ targetRoomId: number;
14
+ };
15
+ export type HitTestCallbacks = {
16
+ clientToMapPoint: (clientX: number, clientY: number) => {
17
+ x: number;
18
+ y: number;
19
+ } | null;
20
+ findRoomAtPoint: (mapX: number, mapY: number) => MapData.Room | null;
21
+ getAreaExitHitZones: () => AreaExitHitZone[];
22
+ };
23
+ /**
24
+ * Handles all DOM interaction on the map container:
25
+ * - Viewport: drag (mouse + touch), wheel zoom, pinch zoom, resize
26
+ * - Map: hover cursor, click, right-click, long-press, area exit clicks
27
+ *
28
+ * No Konva dependency — works with any rendering backend.
29
+ */
30
+ export declare class InteractionHandler {
31
+ private readonly container;
32
+ private readonly settings;
33
+ private readonly viewport;
34
+ private readonly state;
35
+ private readonly hitTest;
36
+ private readonly events;
37
+ private lastPinchDistance?;
38
+ private readonly cleanupFns;
39
+ private destroyed;
40
+ constructor(container: HTMLDivElement, viewport: Viewport, state: MapState, settings: Settings, hitTest: HitTestCallbacks, events: TypedEventEmitter<RendererEventMap>);
41
+ destroy(): void;
42
+ private listen;
43
+ private initViewportEvents;
44
+ private handlePinch;
45
+ private initMapEvents;
46
+ private findRoomAtClientPoint;
47
+ private findAreaExitAtClientPoint;
48
+ private emitRoomClickEvent;
49
+ private emitRoomContextEvent;
50
+ private emitAreaExitClickEvent;
51
+ private emitMapClickEvent;
52
+ }
53
+ export {};