mudlet-map-renderer 2.1.0 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +42 -0
  2. package/dist/{MapReader-mU_4JWv_.js → MapReader-Brzg-X7T.js} +1 -1
  3. package/dist/{MapReader-mU_4JWv_.js.map → MapReader-Brzg-X7T.js.map} +1 -1
  4. package/dist/assets/worker-DtQws85m.js.map +1 -0
  5. package/dist/binary.mjs +1 -1
  6. package/dist/export/flushSceneShapes.d.ts +1 -1
  7. package/dist/flushSceneShapes-Duhi0OQv.js +2937 -0
  8. package/dist/flushSceneShapes-Duhi0OQv.js.map +1 -0
  9. package/dist/hit/HitTester.d.ts +19 -1
  10. package/dist/index.d.ts +6 -2
  11. package/dist/index.mjs +2534 -4696
  12. package/dist/index.mjs.map +1 -1
  13. package/dist/labelPlacement.d.ts +132 -0
  14. package/dist/offscreen.mjs +355 -0
  15. package/dist/offscreen.mjs.map +1 -0
  16. package/dist/overlay/RippleEffect.d.ts +48 -0
  17. package/dist/overlay/SceneOverlay.d.ts +16 -3
  18. package/dist/rendering/KonvaRenderBackend.d.ts +4 -0
  19. package/dist/rendering/MapRenderer.d.ts +12 -3
  20. package/dist/rendering/offscreen/OffscreenCanvasBackend.d.ts +109 -0
  21. package/dist/rendering/offscreen/index.d.ts +11 -0
  22. package/dist/rendering/offscreen/protocol.d.ts +100 -0
  23. package/dist/rendering/offscreen/renderFrame.d.ts +28 -0
  24. package/dist/rendering/offscreen/serializeTransform.d.ts +20 -0
  25. package/dist/rendering/offscreen/worker.d.ts +15 -0
  26. package/dist/style/Style.d.ts +12 -0
  27. package/dist/style/index.d.ts +24 -1
  28. package/dist/style/shape/DarkModernStyle.d.ts +15 -0
  29. package/dist/style/shape/GraphPaperStyle.d.ts +16 -0
  30. package/dist/style/shape/StainedGlassStyle.d.ts +15 -0
  31. package/dist/style/shape/TopographicStyle.d.ts +14 -0
  32. package/dist/style/shape/TreasureMapStyle.d.ts +23 -0
  33. package/dist/style/shape/WatercolorStyle.d.ts +32 -0
  34. package/dist/style/shape/index.d.ts +7 -0
  35. package/dist/style/shape/paintMap.d.ts +8 -0
  36. package/package.json +6 -1
@@ -43,6 +43,18 @@ export interface SceneOverlayContext {
43
43
  * ```
44
44
  */
45
45
  export interface SceneOverlay {
46
+ /**
47
+ * Optional. When `true`, {@link render} returns shapes already in
48
+ * **rendered/scene space** (i.e. post-Style projection), so the active
49
+ * {@link Style} transform is skipped for this overlay. Use this for
50
+ * overlays that visualise data the renderer has already projected — e.g.
51
+ * a hit-area debug overlay built from {@link HitTester} geometry, which is
52
+ * stored in rendered space. Without it, coordinate-warping styles
53
+ * (Isometric) would project the geometry a second time, offsetting it.
54
+ *
55
+ * Defaults to `false`: shapes are world-space and pass through the Style.
56
+ */
57
+ readonly sceneSpace?: boolean;
46
58
  /**
47
59
  * Optional. Called once when the overlay is registered with an interactive
48
60
  * renderer. Subscribe to events here and call `ctx.invalidate()` when the
@@ -60,9 +72,10 @@ export interface SceneOverlay {
60
72
  * Contribute geometry to the scene. Called on register, on invalidate, and
61
73
  * by every exporter.
62
74
  *
63
- * @returns one or more world-space {@link Shape}s, or `void` to emit
64
- * nothing this frame. Shapes carry their own {@link Shape.layer} hint;
65
- * leaving it unset routes them to the overlay layer.
75
+ * @returns one or more world-space {@link Shape}s (or rendered-space when
76
+ * {@link sceneSpace} is `true`), or `void` to emit nothing this frame.
77
+ * Shapes carry their own {@link Shape.layer} hint; leaving it unset
78
+ * routes them to the overlay layer.
66
79
  */
67
80
  render(state: MapState, bounds: ViewportBounds): Shape | Shape[] | void;
68
81
  }
@@ -60,6 +60,7 @@ export declare class KonvaRenderBackend implements InteractiveBackend {
60
60
  private destroyed;
61
61
  private _coordinateTransform;
62
62
  private coordinateInverse;
63
+ private coordinateLayerOffset;
63
64
  get coordinateTransform(): CoordFn;
64
65
  private liveEffects;
65
66
  private sceneOverlays;
@@ -102,6 +103,9 @@ export declare class KonvaRenderBackend implements InteractiveBackend {
102
103
  * relationship between input shape and tracked handle, so callers that cache
103
104
  * the return value (position marker, scene overlays, highlights, current-room
104
105
  * overlay, paths) can destroy the entire expansion through that one handle.
106
+ *
107
+ * `bypassStyle` skips the Style transform entirely — used for overlays whose
108
+ * geometry is already in rendered space (e.g. hit-area debug visualisation).
105
109
  */
106
110
  private addStyledShape;
107
111
  /**
@@ -54,6 +54,14 @@ export interface InteractiveBackend {
54
54
  getDrawnSpecialExits(): readonly DrawnSpecialExitEntry[];
55
55
  /** Companion to {@link getDrawnExits} for one-way stub indicators. */
56
56
  getDrawnStubs(): readonly DrawnStubEntry[];
57
+ /**
58
+ * Register an interactive-only animated effect. Optional: backends that
59
+ * cannot host animated effects omit it (the facade then no-ops). Both the
60
+ * Konva and OffscreenCanvas backends implement it.
61
+ */
62
+ addLiveEffect?(id: string, effect: LiveEffect): void;
63
+ /** Companion to {@link addLiveEffect}. */
64
+ removeLiveEffect?(id: string): void;
57
65
  destroy(): void;
58
66
  }
59
67
  /**
@@ -145,9 +153,10 @@ export declare class MapRenderer {
145
153
  addSceneOverlay(id: string, overlay: SceneOverlay): void;
146
154
  removeSceneOverlay(id: string): void;
147
155
  /**
148
- * Register an interactive-only animated effect. No-ops when running with a
149
- * non-Konva backend. Does not appear in SVG/PNG exports — use
150
- * {@link addSceneOverlay} for overlays that must appear in exports.
156
+ * Register an interactive-only animated effect. Supported by the Konva and
157
+ * OffscreenCanvas backends; no-ops on backends that don't host effects.
158
+ * Does not appear in SVG/PNG exports — use {@link addSceneOverlay} for
159
+ * overlays that must appear in exports.
151
160
  *
152
161
  * ```ts
153
162
  * renderer.addLiveEffect('rain', new RainEffect());
@@ -0,0 +1,109 @@
1
+ import { InteractiveBackend } from '../MapRenderer';
2
+ import { MapState } from '../../MapState';
3
+ import { RendererEventMap } from '../../types/Settings';
4
+ import { Camera } from '../../camera/Camera';
5
+ import { CullingManager } from '../../CullingManager';
6
+ import { HitTester } from '../../hit/HitTester';
7
+ import { TypedEventEmitter } from '../../TypedEventEmitter';
8
+ import { DrawnExitEntry, DrawnSpecialExitEntry, DrawnStubEntry } from '../../ScenePipeline';
9
+ import { Style } from '../../style/Style';
10
+ import { CoordFn } from '../../coord/CoordFn';
11
+ import { SceneOverlay } from '../../overlay/SceneOverlay';
12
+ import { LiveEffect } from '../../overlay/LiveEffect';
13
+ import { ExportCanvas } from '../../export/Exporter';
14
+ import { MainToWorkerMessage, WorkerToMainMessage } from './protocol';
15
+ /** Transport the backend talks to. A real `Worker` satisfies this directly. */
16
+ export interface WorkerTransport {
17
+ postMessage(message: MainToWorkerMessage, transfer?: Transferable[]): void;
18
+ addEventListener?(type: "message", handler: (ev: {
19
+ data: WorkerToMainMessage;
20
+ }) => void): void;
21
+ onmessage?: ((ev: {
22
+ data: WorkerToMainMessage;
23
+ }) => void) | null;
24
+ terminate?(): void;
25
+ }
26
+ export interface OffscreenBackendOptions {
27
+ /** Pre-created transport (used by the inline-worker factory and by tests). */
28
+ transport?: WorkerTransport;
29
+ /** Lazily create the transport. Ignored when {@link transport} is provided. */
30
+ createTransport?: () => WorkerTransport;
31
+ /** Override the device pixel ratio (tests / forcing 1x). */
32
+ devicePixelRatio?: number;
33
+ }
34
+ export declare class OffscreenCanvasBackend implements InteractiveBackend {
35
+ readonly camera: Camera;
36
+ readonly culling: CullingManager;
37
+ readonly events: TypedEventEmitter<RendererEventMap>;
38
+ readonly hitTester: HitTester;
39
+ private readonly state;
40
+ private readonly container?;
41
+ private readonly transport?;
42
+ private readonly pipeline;
43
+ private readonly dpr;
44
+ private currentStyle;
45
+ private _coordinateTransform;
46
+ private coordinateInverse;
47
+ private coordinateLayerOffset;
48
+ private lastResult?;
49
+ private lastHitShapes;
50
+ private boundsByShape;
51
+ private interactionHandler?;
52
+ private cameraChangeHandler?;
53
+ private readonly sceneOverlays;
54
+ private readonly viewportSubscribers;
55
+ private readonly liveEffects;
56
+ private liveEffectStage?;
57
+ private liveEffectLayer?;
58
+ private liveEffectHost?;
59
+ private readonly pendingExports;
60
+ private exportSeq;
61
+ private destroyed;
62
+ constructor(state: MapState, container?: HTMLDivElement, options?: OffscreenBackendOptions);
63
+ get coordinateTransform(): CoordFn;
64
+ private post;
65
+ private postSettings;
66
+ private postCamera;
67
+ private postScene;
68
+ private postOverlays;
69
+ private styleContext;
70
+ private styleOne;
71
+ private styleShapes;
72
+ /** Style a scene layer, carrying each shape's world-space cull bounds through. */
73
+ private styleLayer;
74
+ refresh(): void;
75
+ private buildBoundsMap;
76
+ /** Port of {@link KonvaRenderBackend.updateCurrentRoomOverlay} returning shapes. */
77
+ private buildCurrentRoomOverlayShapes;
78
+ setStyle(style: Style): void;
79
+ private applyStyleTransforms;
80
+ private onCameraChange;
81
+ private subscribeToState;
82
+ private onPositionChanged;
83
+ updateBackground(): void;
84
+ addSceneOverlay(id: string, overlay: SceneOverlay): void;
85
+ removeSceneOverlay(id: string): void;
86
+ getSceneOverlays(): Iterable<SceneOverlay>;
87
+ private createOverlayContext;
88
+ private ensureEffectStage;
89
+ /** Mirror the camera transform onto the effect stage so effects align with the map. */
90
+ private syncEffectStage;
91
+ addLiveEffect(id: string, effect: LiveEffect): void;
92
+ removeLiveEffect(id: string): void;
93
+ getDrawnExits(): readonly DrawnExitEntry[];
94
+ getDrawnSpecialExits(): readonly DrawnSpecialExitEntry[];
95
+ getDrawnStubs(): readonly DrawnStubEntry[];
96
+ /**
97
+ * Not supported with the worker backend — the live canvas is owned by the
98
+ * worker and cannot be read synchronously here. Use the headless
99
+ * `CanvasExporter` / `PngBytesExporter` (they rebuild from state), or the
100
+ * async {@link captureViewport} for a live snapshot.
101
+ */
102
+ exportCanvas(): ExportCanvas | undefined;
103
+ /** Async live-viewport snapshot via a worker round-trip. */
104
+ captureViewport(options?: {
105
+ pixelRatio?: number;
106
+ }): Promise<ImageBitmap>;
107
+ private handleWorkerMessage;
108
+ destroy(): void;
109
+ }
@@ -0,0 +1,11 @@
1
+ import { MapState } from '../../MapState';
2
+ import { InteractiveBackend } from '../MapRenderer';
3
+ import { OffscreenBackendOptions } from './OffscreenCanvasBackend';
4
+ export { OffscreenCanvasBackend } from './OffscreenCanvasBackend';
5
+ export type { OffscreenBackendOptions, WorkerTransport } from './OffscreenCanvasBackend';
6
+ export type { MainToWorkerMessage, WorkerToMainMessage, SerializableTransform } from './protocol';
7
+ /**
8
+ * Build a backend factory for {@link MapRenderer}'s `backendFactory` parameter.
9
+ * Pass the same `container` you give `MapRenderer`.
10
+ */
11
+ export declare function createOffscreenBackend(container?: HTMLDivElement, options?: OffscreenBackendOptions): (state: MapState) => InteractiveBackend;
@@ -0,0 +1,100 @@
1
+ import { Shape } from '../../scene/Shape';
2
+ import { Settings, ViewportBounds } from '../../types/Settings';
3
+ /** World-space axis-aligned bounds used for culling. */
4
+ export interface Bounds {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ }
10
+ /**
11
+ * Flattened coordinate transform. `Style.worldToScene` / `sceneToWorld` are
12
+ * closures and cannot cross `postMessage`; we sample them into affine matrices
13
+ * on the main thread. `'identity'` is the fast path for every flat style.
14
+ *
15
+ * Affine matrices follow the DOM/Canvas convention `[a, b, c, d, e, f]`:
16
+ * `x' = a·x + c·y + e`, `y' = b·x + d·y + f`.
17
+ */
18
+ export type SerializableTransform = {
19
+ kind: "identity";
20
+ } | {
21
+ kind: "affine";
22
+ /** World → scene (for culling-bounds projection). */
23
+ forward: AffineMatrix;
24
+ /** Scene → world (for grid Cartesian-bounds computation). */
25
+ inverse: AffineMatrix;
26
+ };
27
+ export type AffineMatrix = [number, number, number, number, number, number];
28
+ /** Camera transform needed to project world coordinates into render space. */
29
+ export interface RenderCamera {
30
+ scale: number;
31
+ offsetX: number;
32
+ offsetY: number;
33
+ }
34
+ /** One scene layer: styled shapes plus per-shape world-space cull bounds. */
35
+ export interface LayerPayload {
36
+ /** Already-styled, scene-space shapes (identity style → world-space). */
37
+ shapes: Shape[];
38
+ /**
39
+ * Per-shape world-space cull bounds, index-aligned with {@link shapes}.
40
+ * `null` marks an unmanaged pass-through (area name, noScaling label) that
41
+ * is always drawn. Shapes fanned out by a style share their parent bounds.
42
+ */
43
+ bounds: Array<Bounds | null>;
44
+ }
45
+ export interface InitMessage {
46
+ type: "init";
47
+ canvas: OffscreenCanvas;
48
+ width: number;
49
+ height: number;
50
+ /** Device pixel ratio for crisp rendering on HiDPI displays. */
51
+ dpr: number;
52
+ settings: Settings;
53
+ }
54
+ export interface SettingsMessage {
55
+ type: "settings";
56
+ settings: Settings;
57
+ }
58
+ export interface SceneMessage {
59
+ type: "scene";
60
+ link: LayerPayload;
61
+ room: LayerPayload;
62
+ topLabel: Shape[];
63
+ transform: SerializableTransform;
64
+ }
65
+ export interface OverlaysMessage {
66
+ type: "overlays";
67
+ /** Already-styled overlay shapes (current-room overlay + built-in overlays). */
68
+ shapes: Shape[];
69
+ }
70
+ export interface CameraMessage {
71
+ type: "camera";
72
+ camera: RenderCamera;
73
+ viewport: ViewportBounds;
74
+ width: number;
75
+ height: number;
76
+ }
77
+ export interface ResizeMessage {
78
+ type: "resize";
79
+ width: number;
80
+ height: number;
81
+ dpr: number;
82
+ }
83
+ export interface ExportMessage {
84
+ type: "export";
85
+ requestId: number;
86
+ pixelRatio: number;
87
+ }
88
+ export interface DestroyMessage {
89
+ type: "destroy";
90
+ }
91
+ export type MainToWorkerMessage = InitMessage | SettingsMessage | SceneMessage | OverlaysMessage | CameraMessage | ResizeMessage | ExportMessage | DestroyMessage;
92
+ export interface ReadyMessage {
93
+ type: "ready";
94
+ }
95
+ export interface ExportResultMessage {
96
+ type: "export";
97
+ requestId: number;
98
+ bitmap: ImageBitmap;
99
+ }
100
+ export type WorkerToMainMessage = ReadyMessage | ExportResultMessage;
@@ -0,0 +1,28 @@
1
+ import { Settings, ViewportBounds } from '../../types/Settings';
2
+ import { Shape } from '../../scene/Shape';
3
+ import { ImageFactory } from '../../render/CanvasRenderer';
4
+ import { LayerPayload, RenderCamera, SerializableTransform } from './protocol';
5
+ export interface FrameScene {
6
+ link: LayerPayload;
7
+ room: LayerPayload;
8
+ topLabel: Shape[];
9
+ transform: SerializableTransform;
10
+ }
11
+ export interface FrameInput {
12
+ scene: FrameScene | null;
13
+ overlays: Shape[];
14
+ camera: RenderCamera;
15
+ viewport: ViewportBounds;
16
+ settings: Settings;
17
+ /** Logical (CSS) pixel dimensions; HiDPI scaling is applied by the caller. */
18
+ width: number;
19
+ height: number;
20
+ /**
21
+ * Resolves an image shape's `src` to a drawable source. In the worker this
22
+ * returns a cached {@link ImageBitmap} (or `null` while it decodes), so
23
+ * data-URL labels and the ambient-light vignette render off-thread.
24
+ */
25
+ imageFactory?: ImageFactory;
26
+ }
27
+ /** Render one frame of the scene + overlays onto `ctx`. */
28
+ export declare function renderFrame(ctx: CanvasRenderingContext2D, input: FrameInput): void;
@@ -0,0 +1,20 @@
1
+ import { Style } from '../../style/Style';
2
+ import { SerializableTransform } from './protocol';
3
+ type TransformFn = (x: number, y: number) => {
4
+ x: number;
5
+ y: number;
6
+ };
7
+ /** Build the flattened transform for a style. Identity styles short-circuit. */
8
+ export declare function serializeTransform(style: Style): SerializableTransform;
9
+ /**
10
+ * World → scene projector for the worker's cull-bounds test. Returns
11
+ * `undefined` for the identity transform so callers can skip projection.
12
+ */
13
+ export declare function forwardFn(transform: SerializableTransform): TransformFn | undefined;
14
+ /**
15
+ * Scene → world projector for the worker's grid Cartesian-bounds computation.
16
+ * Returns `undefined` for the identity transform (grid layout treats absent
17
+ * inverse as identity).
18
+ */
19
+ export declare function inverseFn(transform: SerializableTransform): TransformFn | undefined;
20
+ export {};
@@ -0,0 +1,15 @@
1
+ /**
2
+ * OffscreenCanvas worker entry.
3
+ *
4
+ * Thin dispatcher around {@link renderFrame}: owns the transferred
5
+ * `OffscreenCanvas`, stores the latest scene / overlays / camera / settings,
6
+ * and coalesces redraws onto one animation frame (only the latest camera
7
+ * matters, so queued messages are harmless). Resizing a transferred canvas can
8
+ * only happen here, so size/DPR changes are applied worker-side.
9
+ *
10
+ * This module is bundled standalone (see vite.config.ts). It must import ONLY
11
+ * worker-safe code — never Konva, `textMeasure`, or anything touching
12
+ * `document`. A stray such import would surface as a runtime error in the
13
+ * worker and a Konva chunk in the build.
14
+ */
15
+ export {};
@@ -28,6 +28,18 @@ export interface Style {
28
28
  x: number;
29
29
  y: number;
30
30
  };
31
+ /**
32
+ * Optional extra scene-space offset that {@link transform} applies to a
33
+ * given layer's groups but {@link worldToScene} does not capture. Isometric
34
+ * lowers the `link` layer by the cube depth so connectors meet the cube
35
+ * base; without mirroring that here, {@link HitTester} would place exit hit
36
+ * zones a cube-height above the drawn connectors. Returns `{x:0, y:0}` for
37
+ * layers it does not shift.
38
+ */
39
+ sceneLayerOffset?(layer: Shape["layer"]): {
40
+ x: number;
41
+ y: number;
42
+ };
31
43
  }
32
44
  /** Identity style — passes shapes through unchanged. */
33
45
  export declare const identityStyle: Style;
@@ -2,6 +2,8 @@ import { Style } from './Style';
2
2
  import { SketchyOptions } from './shape/SketchyStyle';
3
3
  import { IsometricOptions, IsometricRotation } from './shape/IsometricStyle';
4
4
  import { GradientRoomsOptions } from './shape/GradientRoomsStyle';
5
+ import { WatercolorOptions } from './shape/WatercolorStyle';
6
+ import { treasureMapDecorations } from './shape/TreasureMapStyle';
5
7
  export { compose, identityStyle } from './Style';
6
8
  export type { Style, StyleContext } from './Style';
7
9
  export { applyStyleToShapes } from './applyStyle';
@@ -25,4 +27,25 @@ export declare const SciFi: Style;
25
27
  * gradient stops are recoloured per stop.
26
28
  */
27
29
  export declare function GradientRooms(options?: GradientRoomsOptions): Style;
28
- export type { SketchyOptions, IsometricOptions, IsometricRotation, GradientRoomsOptions };
30
+ /** Stained-glass aesthetic saturated panes framed by fat near-black leading. */
31
+ export declare const StainedGlass: Style;
32
+ /** Old-school graph-paper dungeon — pale rooms inked in navy over blue grid. */
33
+ export declare const GraphPaper: Style;
34
+ /** Topographic relief — earthy rooms with concentric contour rings. */
35
+ export declare const Topographic: Style;
36
+ /** Hand-painted watercolour — translucent edge-bled washes that pool on overlap. */
37
+ export declare function Watercolor(options?: WatercolorOptions): Style;
38
+ /** Flat dark "modern UI" theme — muted dark rooms with subtle elevation shadows. */
39
+ export declare const DarkModern: Style;
40
+ /**
41
+ * Aged treasure-map palette — weathered-paper rooms inked in faded brown.
42
+ * Pair with {@link treasureMapDecorations} (a scene overlay) for the compass
43
+ * rose and double border frame:
44
+ * ```ts
45
+ * renderer.setStyle(TreasureMap);
46
+ * renderer.addSceneOverlay('treasure-decor', treasureMapDecorations());
47
+ * ```
48
+ */
49
+ export declare const TreasureMap: Style;
50
+ export { treasureMapDecorations };
51
+ export type { SketchyOptions, IsometricOptions, IsometricRotation, GradientRoomsOptions, WatercolorOptions };
@@ -0,0 +1,15 @@
1
+ import { Style } from '../Style';
2
+ /**
3
+ * Flat dark "modern UI" aesthetic as a {@link Style}.
4
+ *
5
+ * - Room fills are flattened into a muted dark palette (hue kept, saturation
6
+ * capped, lightness compressed) so the map reads as a calm dark dashboard.
7
+ * - Room outlines become a subtle translucent light border.
8
+ * - Each room body casts a soft offset drop-shadow (emitted **before** the
9
+ * body, like Neon's glow, so it renders underneath) for a sense of
10
+ * elevation.
11
+ * - Exit lines become a muted grey-blue; text is off-white.
12
+ * - Images and groups pass through unchanged (the caller walks group
13
+ * children separately).
14
+ */
15
+ export declare const darkModernShapeStyle: Style;
@@ -0,0 +1,16 @@
1
+ import { Style } from '../Style';
2
+ /**
3
+ * Old-school graph-paper dungeon aesthetic as a {@link Style} — the classic
4
+ * hand-drawn D&D look: pale rooms inked in navy over blue grid paper.
5
+ *
6
+ * - Fills map to a pale slate-blue → near-white range by luminance, so rooms
7
+ * stay light and the blue grid reads through. Gradient stops recolour per stop.
8
+ * - Strokes become fattened navy ink.
9
+ * - Exit lines become navy ink (kept thin where the source was thin).
10
+ * - Text uses navy ink.
11
+ * - Images and groups pass through unchanged.
12
+ *
13
+ * Pair with a cream/white background and a light-blue grid in settings for the
14
+ * full graph-paper effect.
15
+ */
16
+ export declare const graphPaperShapeStyle: Style;
@@ -0,0 +1,15 @@
1
+ import { Style } from '../Style';
2
+ /**
3
+ * Stained-glass aesthetic as a {@link Style} — jewel-toned panes separated by
4
+ * thick near-black leading, glowing on a dark backdrop.
5
+ *
6
+ * - Fills are pushed to high saturation in a mid-lightness window so rooms read
7
+ * as coloured glass; grey rooms stay neutral (frosted panes). Gradient fills
8
+ * recolour per stop.
9
+ * - Strokes become fat near-black leading; filled panes gain leading even when
10
+ * the source had none.
11
+ * - Exit lines become slim leading too, tying the panes together.
12
+ * - Text uses pale glass-white for legibility over the dark cames.
13
+ * - Images and groups pass through unchanged.
14
+ */
15
+ export declare const stainedGlassShapeStyle: Style;
@@ -0,0 +1,14 @@
1
+ import { Style } from '../Style';
2
+ /**
3
+ * Topographic / contour-map aesthetic as a {@link Style}. Rooms get an earthy
4
+ * elevation palette and a set of concentric inset "contour" rings, so each room
5
+ * reads like a hill on a shaded relief map.
6
+ *
7
+ * - Rect / circle / polygon: emit the earth-toned base shape, then up to
8
+ * {@link RINGS} stroke-only rings inset toward the centre. Rings collapse and
9
+ * are skipped for rooms too small to hold them.
10
+ * - Exit lines become muted brown paths.
11
+ * - Text uses dark sepia.
12
+ * - Images and groups pass through unchanged.
13
+ */
14
+ export declare const topographicShapeStyle: Style;
@@ -0,0 +1,23 @@
1
+ import { Style } from '../Style';
2
+ import { SceneOverlay } from '../../overlay/SceneOverlay';
3
+ /**
4
+ * Aged treasure-map palette as a {@link Style}.
5
+ *
6
+ * Recolours rooms onto a warm weathered-paper ramp and inks every line in
7
+ * faded brown. For the decorative compass rose and double border frame, add
8
+ * {@link treasureMapDecorations} as a scene overlay alongside this style.
9
+ *
10
+ * ```ts
11
+ * renderer.setStyle(TreasureMap);
12
+ * renderer.addSceneOverlay('treasure-decor', treasureMapDecorations());
13
+ * ```
14
+ */
15
+ export declare const treasureMapShapeStyle: Style;
16
+ /**
17
+ * Decorative scene overlay for the treasure-map look: a double-line border
18
+ * frame inset from the viewport and a compass rose in the top-right corner.
19
+ *
20
+ * Emitted in world space and pinned to the current viewport, so it tracks the
21
+ * visible region as the user pans/zooms and appears in every export path.
22
+ */
23
+ export declare function treasureMapDecorations(): SceneOverlay;
@@ -0,0 +1,32 @@
1
+ import { Style } from '../Style';
2
+ export interface WatercolorOptions {
3
+ /**
4
+ * Edge-bleed amount in map units (perpendicular wobble of pane edges).
5
+ * Default 0.05.
6
+ */
7
+ bleed?: number;
8
+ /**
9
+ * Translucent washes stacked per filled shape. More layers = deeper, more
10
+ * uneven colour where they overlap. Default 2 (clamped to 1..4).
11
+ */
12
+ layers?: number;
13
+ /** Per-wash alpha multiplier so stacked washes build up colour. Default 0.4. */
14
+ alpha?: number;
15
+ }
16
+ /**
17
+ * Hand-painted watercolour as a {@link Style}. Each filled shape becomes a
18
+ * stack of translucent, edge-wobbled washes that bleed and deepen where they
19
+ * overlap — soft and organic, with no crisp ink outline.
20
+ *
21
+ * - Rect / circle / polygon fills → N wobbly low-alpha polygon washes (seeded,
22
+ * so re-renders are identical). Hard strokes are dropped; the soft edges come
23
+ * from the overlapping washes.
24
+ * - Lines (exits) keep their colour but lose alpha so they read as a thin wet
25
+ * brushstroke under the rooms.
26
+ * - Text is repainted in muted ink.
27
+ * - Images and groups pass through unchanged.
28
+ *
29
+ * Unfilled stroked shapes (e.g. exit arrowheads) fall back to a single faint
30
+ * wobbled outline rather than vanishing.
31
+ */
32
+ export declare function watercolorShapeStyle(options?: WatercolorOptions): Style;
@@ -25,3 +25,10 @@ export { constructionShapeStyle } from './ConstructionStyle';
25
25
  export { scifiShapeStyle } from './SciFiStyle';
26
26
  export { gradientRoomsStyle } from './GradientRoomsStyle';
27
27
  export type { GradientRoomsOptions } from './GradientRoomsStyle';
28
+ export { stainedGlassShapeStyle } from './StainedGlassStyle';
29
+ export { graphPaperShapeStyle } from './GraphPaperStyle';
30
+ export { topographicShapeStyle } from './TopographicStyle';
31
+ export { watercolorShapeStyle } from './WatercolorStyle';
32
+ export type { WatercolorOptions } from './WatercolorStyle';
33
+ export { darkModernShapeStyle } from './DarkModernStyle';
34
+ export { treasureMapShapeStyle, treasureMapDecorations } from './TreasureMapStyle';
@@ -23,3 +23,11 @@ export declare function parseRgb(color: string): ParsedRgb | null;
23
23
  export declare function formatRgb(r: number, g: number, b: number, a?: number): string;
24
24
  /** Perceived luminance in [0, 1] using ITU-R BT.601 weights. */
25
25
  export declare function luminance(c: ParsedRgb): number;
26
+ /**
27
+ * Convert 0..255 RGB to HSL with hue in degrees and saturation/lightness in
28
+ * [0, 1]. Achromatic colours report hue 0. Shared by the newer styles
29
+ * (StainedGlass, …); Neon and SciFi keep their own local copies.
30
+ */
31
+ export declare function rgbToHsl(r: number, g: number, b: number): [number, number, number];
32
+ /** Build an `rgb(...)` / `rgba(...)` string from HSL (hue degrees, s/l in [0,1]). */
33
+ export declare function hslToRgbString(h: number, s: number, l: number, a?: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mudlet-map-renderer",
3
- "version": "2.1.0",
3
+ "version": "2.3.0",
4
4
  "license": "MIT",
5
5
  "repository": {
6
6
  "url": "https://github.com/Delwing/mudlet-map-renderer"
@@ -17,6 +17,10 @@
17
17
  "./binary": {
18
18
  "types": "./dist/binary/index.d.ts",
19
19
  "import": "./dist/binary.mjs"
20
+ },
21
+ "./offscreen": {
22
+ "types": "./dist/rendering/offscreen/index.d.ts",
23
+ "import": "./dist/offscreen.mjs"
20
24
  }
21
25
  },
22
26
  "sideEffects": false,
@@ -25,6 +29,7 @@
25
29
  "build": "tsc -p tsconfig.json && vite build",
26
30
  "preview": "vite preview",
27
31
  "demo:dev": "vite --config vite.demo.config.ts",
32
+ "bench": "vite --config vite.bench.config.ts",
28
33
  "demo:build": "vite build --config vite.demo.config.ts",
29
34
  "demo:preview": "vite preview --config vite.demo.config.ts",
30
35
  "demo:headless": "tsx --tsconfig demo/tsconfig.json demo/headless.ts",