mudlet-map-editor 0.1.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.
- package/README.md +77 -0
- package/dist-lib/App.d.ts +5 -0
- package/dist-lib/components/AreaManagerModal.d.ts +8 -0
- package/dist-lib/components/ContextMenu.d.ts +8 -0
- package/dist-lib/components/DropdownSelect.d.ts +15 -0
- package/dist-lib/components/EnvManagerModal.d.ts +8 -0
- package/dist-lib/components/EnvPicker.d.ts +13 -0
- package/dist-lib/components/HelpModal.d.ts +10 -0
- package/dist-lib/components/RoomPanel.d.ts +15 -0
- package/dist-lib/components/SessionsPanel.d.ts +1 -0
- package/dist-lib/components/SidePanel.d.ts +10 -0
- package/dist-lib/components/SwatchPalette.d.ts +6 -0
- package/dist-lib/components/Toolbar.d.ts +6 -0
- package/dist-lib/components/UrlLoadModal.d.ts +4 -0
- package/dist-lib/components/icons.d.ts +7 -0
- package/dist-lib/components/panelShared.d.ts +27 -0
- package/dist-lib/components/panels/CustomLinePanel.d.ts +20 -0
- package/dist-lib/components/panels/ExitPanel.d.ts +15 -0
- package/dist-lib/components/panels/HistoryPanel.d.ts +8 -0
- package/dist-lib/components/panels/LabelPanel.d.ts +13 -0
- package/dist-lib/components/panels/MapPanel.d.ts +8 -0
- package/dist-lib/editor/commands.d.ts +30 -0
- package/dist-lib/editor/coords.d.ts +6 -0
- package/dist-lib/editor/effects/ConnectHandlesEffect.d.ts +21 -0
- package/dist-lib/editor/effects/CustomLinePreviewEffect.d.ts +19 -0
- package/dist-lib/editor/effects/GridOverlayEffect.d.ts +27 -0
- package/dist-lib/editor/effects/HoverHaloEffect.d.ts +19 -0
- package/dist-lib/editor/effects/LabelHaloEffect.d.ts +25 -0
- package/dist-lib/editor/effects/MarqueeEffect.d.ts +11 -0
- package/dist-lib/editor/effects/RubberBandEffect.d.ts +17 -0
- package/dist-lib/editor/effects/SelectedLinkEffect.d.ts +17 -0
- package/dist-lib/editor/effects/SelectionHaloEffect.d.ts +20 -0
- package/dist-lib/editor/effects/SnapIndicatorEffect.d.ts +14 -0
- package/dist-lib/editor/hitTest.d.ts +100 -0
- package/dist-lib/editor/labelPixmap.d.ts +12 -0
- package/dist-lib/editor/loadFile.d.ts +2 -0
- package/dist-lib/editor/mapBytes.d.ts +1 -0
- package/dist-lib/editor/mapHelpers.d.ts +17 -0
- package/dist-lib/editor/plugin.d.ts +20 -0
- package/dist-lib/editor/pointerController.d.ts +2 -0
- package/dist-lib/editor/reader/EditorMapReader.d.ts +175 -0
- package/dist-lib/editor/scene.d.ts +22 -0
- package/dist-lib/editor/session.d.ts +19 -0
- package/dist-lib/editor/store.d.ts +98 -0
- package/dist-lib/editor/tools.d.ts +52 -0
- package/dist-lib/editor/types.d.ts +523 -0
- package/dist-lib/index.d.ts +5 -0
- package/dist-lib/index.js +11029 -0
- package/dist-lib/main.d.ts +1 -0
- package/dist-lib/mapIO.d.ts +6 -0
- package/dist-lib/platform.d.ts +2 -0
- package/dist-lib/shims/fs-stub.d.ts +7 -0
- package/dist-lib/styles.css +3476 -0
- package/package.json +45 -0
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type { MapRenderer } from 'mudlet-map-renderer';
|
|
2
|
+
import type { MudletMap } from '../mapIO';
|
|
3
|
+
import type { Direction, HitItem, LabelResizeHandle } from './types';
|
|
4
|
+
import type { EditorMapReader } from './reader/EditorMapReader';
|
|
5
|
+
/**
|
|
6
|
+
* Hit-test map labels. Labels are axis-aligned rects. Returns the topmost label
|
|
7
|
+
* (last in the list, rendered on top) whose render-space rect contains (mapX, mapY).
|
|
8
|
+
* MapData.Label stores X/Y in Mudlet space; render position is (X, -Y).
|
|
9
|
+
*/
|
|
10
|
+
export declare function labelAt(areaId: number, z: number, mapX: number, mapY: number, reader: EditorMapReader): {
|
|
11
|
+
id: number;
|
|
12
|
+
areaId: number;
|
|
13
|
+
} | null;
|
|
14
|
+
/**
|
|
15
|
+
* Like labelAt but returns ALL labels whose rect contains (mapX, mapY), in
|
|
16
|
+
* draw order (topmost — last in the list — is last in the returned array).
|
|
17
|
+
*/
|
|
18
|
+
export declare function allLabelsAt(areaId: number, z: number, mapX: number, mapY: number, reader: EditorMapReader): {
|
|
19
|
+
id: number;
|
|
20
|
+
areaId: number;
|
|
21
|
+
}[];
|
|
22
|
+
/**
|
|
23
|
+
* Returns ALL elements under (mapX, mapY) in visual-priority order:
|
|
24
|
+
* 1. All rooms occupying the same grid cell (stacked rooms)
|
|
25
|
+
* 2. All overlapping labels (topmost first)
|
|
26
|
+
* 3. Custom line (if any)
|
|
27
|
+
* 4. Exit (if any)
|
|
28
|
+
* Used for Alt+click cycling and the right-click disambiguate menu.
|
|
29
|
+
*/
|
|
30
|
+
export declare function allHitsAt(renderer: MapRenderer, map: MudletMap, areaId: number, z: number, mapX: number, mapY: number, roomSize: number, reader: EditorMapReader): HitItem[];
|
|
31
|
+
/**
|
|
32
|
+
* 8 handle positions relative to a label's padded selection rect.
|
|
33
|
+
* Returns the handle under the cursor, or null.
|
|
34
|
+
* `hitRadius` is the capture radius in map units (typically 8px worth of units).
|
|
35
|
+
*/
|
|
36
|
+
export declare function labelResizeHandleAt(bounds: {
|
|
37
|
+
x: number;
|
|
38
|
+
y: number;
|
|
39
|
+
w: number;
|
|
40
|
+
h: number;
|
|
41
|
+
}, mapX: number, mapY: number, hitRadius: number): LabelResizeHandle | null;
|
|
42
|
+
/** Does any room occupy the exact raw grid cell (x, y, z) on the given area? */
|
|
43
|
+
export declare function roomAtCell(map: MudletMap, areaId: number, x: number, y: number, z: number): import("mudlet-map-binary-reader").MudletRoom | null;
|
|
44
|
+
/**
|
|
45
|
+
* 8 handle offsets (signs relative to room centre, each scaled by roomSize/2).
|
|
46
|
+
* Used both for hit-testing and for rendering the ConnectHandlesEffect.
|
|
47
|
+
*/
|
|
48
|
+
export declare const HANDLE_OFFSETS: ReadonlyArray<readonly [number, number, Direction]>;
|
|
49
|
+
/**
|
|
50
|
+
* Given a cursor in render space and a room's render-space centre, return
|
|
51
|
+
* which handle direction the cursor is pointing at. Assumes the caller has
|
|
52
|
+
* already established the cursor is within the room's hit-test region — this
|
|
53
|
+
* function just maps the angular offset to one of 8 sectors, so there is
|
|
54
|
+
* never a "body" / null result and the direction transitions smoothly around
|
|
55
|
+
* the room.
|
|
56
|
+
*/
|
|
57
|
+
export declare function handleDirFor(cursor: {
|
|
58
|
+
x: number;
|
|
59
|
+
y: number;
|
|
60
|
+
}, roomCentre: {
|
|
61
|
+
x: number;
|
|
62
|
+
y: number;
|
|
63
|
+
}, _roomSize: number): Direction;
|
|
64
|
+
/**
|
|
65
|
+
* Hit-test an inter-room exit against the geometry the renderer actually
|
|
66
|
+
* drew. Walks every polyline segment of every drawn exit's `lines` and
|
|
67
|
+
* `arrows` so dashed lines, one-way arrows, cross-z stubs, and the
|
|
68
|
+
* renderer's suppression rules (e.g. both-sides-customLine two-ways) are
|
|
69
|
+
* honoured by construction — we never hover an exit that wasn't rendered.
|
|
70
|
+
*/
|
|
71
|
+
export declare function exitAt(renderer: MapRenderer, mapX: number, mapY: number, roomSize: number): {
|
|
72
|
+
fromId: number;
|
|
73
|
+
toId: number;
|
|
74
|
+
dir: Direction;
|
|
75
|
+
} | null;
|
|
76
|
+
/**
|
|
77
|
+
* Hit-test a custom line polyline against the geometry the renderer drew.
|
|
78
|
+
* `points` on each drawn entry already mirror what went to the canvas —
|
|
79
|
+
* including the prepended room-centre segment — so stroke style doesn't
|
|
80
|
+
* matter; we just walk segments.
|
|
81
|
+
*/
|
|
82
|
+
export declare function customLineAt(renderer: MapRenderer, mapX: number, mapY: number, roomSize: number): {
|
|
83
|
+
roomId: number;
|
|
84
|
+
exitName: string;
|
|
85
|
+
} | null;
|
|
86
|
+
/**
|
|
87
|
+
* Hit-test a line segment of a specific custom line. Returns the raw-points
|
|
88
|
+
* index at which a new waypoint should be inserted so it lands on the clicked
|
|
89
|
+
* segment (between waypoint `insertIndex - 1` and `insertIndex`). Waypoint
|
|
90
|
+
* handles are skipped — caller should check `customLinePointAt` first.
|
|
91
|
+
*/
|
|
92
|
+
export declare function customLineSegmentAt(renderer: MapRenderer, roomId: number, exitName: string, mapX: number, mapY: number, roomSize: number): {
|
|
93
|
+
insertIndex: number;
|
|
94
|
+
} | null;
|
|
95
|
+
/**
|
|
96
|
+
* Hit-test a waypoint handle on a specific custom line. Returns the 0-based
|
|
97
|
+
* index into the *raw* customLine.points array (which excludes the
|
|
98
|
+
* renderer-prepended room centre), or null.
|
|
99
|
+
*/
|
|
100
|
+
export declare function customLinePointAt(renderer: MapRenderer, roomId: number, exitName: string, mapX: number, mapY: number, roomSize: number): number | null;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { LabelSnapshot } from './types';
|
|
2
|
+
/**
|
|
3
|
+
* Render a label into an offscreen canvas sized to label.size (in map units
|
|
4
|
+
* × PX_PER_UNIT) and return a PNG data URL.
|
|
5
|
+
*
|
|
6
|
+
* The background fills the entire rect. Text is centered horizontally and
|
|
7
|
+
* vertically. Multi-line text is split on '\n'. Font decorations (underline,
|
|
8
|
+
* strikeout) are drawn manually so they work across all browsers.
|
|
9
|
+
*/
|
|
10
|
+
export declare function generateLabelPixmap(label: LabelSnapshot): string;
|
|
11
|
+
/** Convert a PNG data URL to a Buffer for storage in the binary map. */
|
|
12
|
+
export declare function dataUrlToBuffer(dataUrl: string): Uint8Array;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function getMapBytes(): Uint8Array | null;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { MudletMap, MudletRoom } from '../mapIO';
|
|
2
|
+
import type { Direction } from './types';
|
|
3
|
+
import { OPPOSITE } from './types';
|
|
4
|
+
export declare function nextAreaId(map: MudletMap): number;
|
|
5
|
+
export declare function nextRoomId(map: MudletMap): number;
|
|
6
|
+
export declare function createDefaultRoom(id: number, areaId: number, x: number, y: number, z: number): MudletRoom;
|
|
7
|
+
export declare function inferDirection(sx: number, sy: number, tx: number, ty: number): Direction;
|
|
8
|
+
export declare function getExit(room: MudletRoom, dir: Direction): number;
|
|
9
|
+
export declare function setExit(room: MudletRoom, dir: Direction, value: number): void;
|
|
10
|
+
/** Returns all incoming cardinal-exit references to `targetId` from any room in the map. */
|
|
11
|
+
export declare function findNeighborsPointingAt(map: MudletMap, targetId: number): Array<{
|
|
12
|
+
roomId: number;
|
|
13
|
+
dir: Direction;
|
|
14
|
+
}>;
|
|
15
|
+
/** Cardinals that use 2D map geometry (not up/down/in/out). */
|
|
16
|
+
export declare function is2DCardinal(dir: Direction): boolean;
|
|
17
|
+
export { OPPOSITE };
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ReactNode } from 'react';
|
|
2
|
+
import type { MudletMap } from '../mapIO';
|
|
3
|
+
import type { SwatchSet } from './types';
|
|
4
|
+
import type { SceneHandle } from './scene';
|
|
5
|
+
export interface SidebarTab {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
render(sceneRef: {
|
|
9
|
+
current: SceneHandle | null;
|
|
10
|
+
}): ReactNode;
|
|
11
|
+
}
|
|
12
|
+
export interface EditorPlugin {
|
|
13
|
+
onAppReady?(): Promise<void>;
|
|
14
|
+
onMapOpened?(map: MudletMap): void;
|
|
15
|
+
onMapClosed?(): void;
|
|
16
|
+
onMapSave?(bytes: Uint8Array): void;
|
|
17
|
+
renderOverlay?(): ReactNode;
|
|
18
|
+
sidebarTabs?(): SidebarTab[];
|
|
19
|
+
swatchSets?(): SwatchSet[];
|
|
20
|
+
}
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import type { MudletMap, MudletRoom, MudletColor } from '../../mapIO';
|
|
2
|
+
import type { LabelSnapshot } from '../types';
|
|
3
|
+
import { type Direction, type LabelFont } from '../types';
|
|
4
|
+
/** Editor-side Exit — mirrors the renderer's Exit type. */
|
|
5
|
+
export interface EditorExit {
|
|
6
|
+
a: number;
|
|
7
|
+
b: number;
|
|
8
|
+
aDir?: Direction;
|
|
9
|
+
bDir?: Direction;
|
|
10
|
+
zIndex: number[];
|
|
11
|
+
}
|
|
12
|
+
/** Live view over a raw MudletRoom. Y is flipped for render-space consumption. */
|
|
13
|
+
export interface LiveRoom {
|
|
14
|
+
readonly id: number;
|
|
15
|
+
x: number;
|
|
16
|
+
y: number;
|
|
17
|
+
z: number;
|
|
18
|
+
readonly area: number;
|
|
19
|
+
readonly name: string;
|
|
20
|
+
readonly weight: number;
|
|
21
|
+
readonly env: number | undefined;
|
|
22
|
+
readonly roomChar: string | undefined;
|
|
23
|
+
readonly userData: Record<string, string>;
|
|
24
|
+
readonly doors: Record<string, number>;
|
|
25
|
+
readonly isLocked: boolean;
|
|
26
|
+
readonly exitLocks: number[];
|
|
27
|
+
readonly stubs: number[];
|
|
28
|
+
readonly exitWeights: Record<string, number>;
|
|
29
|
+
readonly mSpecialExitLocks: number[];
|
|
30
|
+
readonly exits: Record<string, number>;
|
|
31
|
+
readonly specialExits: Record<string, number>;
|
|
32
|
+
readonly customLines: Record<string, any>;
|
|
33
|
+
readonly hash?: string;
|
|
34
|
+
/** Backing raw room (for direct mutation). */
|
|
35
|
+
readonly __raw: MudletRoom;
|
|
36
|
+
}
|
|
37
|
+
export declare class EditorPlane {
|
|
38
|
+
private rooms;
|
|
39
|
+
private labels;
|
|
40
|
+
constructor(rooms: LiveRoom[], labels: any[]);
|
|
41
|
+
getRooms(): LiveRoom[];
|
|
42
|
+
getLabels(): any[];
|
|
43
|
+
getBounds(): {
|
|
44
|
+
minX: number;
|
|
45
|
+
maxX: number;
|
|
46
|
+
minY: number;
|
|
47
|
+
maxY: number;
|
|
48
|
+
};
|
|
49
|
+
setRooms(rooms: LiveRoom[]): void;
|
|
50
|
+
setLabels(labels: any[]): void;
|
|
51
|
+
}
|
|
52
|
+
export declare class EditorArea {
|
|
53
|
+
private readonly areaId;
|
|
54
|
+
private readonly areaName;
|
|
55
|
+
private rooms;
|
|
56
|
+
private labels;
|
|
57
|
+
private planes;
|
|
58
|
+
private exits;
|
|
59
|
+
private version;
|
|
60
|
+
constructor(areaId: number, areaName: string, rooms: LiveRoom[], labels: any[]);
|
|
61
|
+
getAreaId(): number;
|
|
62
|
+
getAreaName(): string;
|
|
63
|
+
getVersion(): number;
|
|
64
|
+
markDirty(): void;
|
|
65
|
+
getPlane(z: number): EditorPlane;
|
|
66
|
+
getPlanes(): EditorPlane[];
|
|
67
|
+
getZLevels(): number[];
|
|
68
|
+
getRooms(): LiveRoom[];
|
|
69
|
+
getFullBounds(): {
|
|
70
|
+
minX: number;
|
|
71
|
+
maxX: number;
|
|
72
|
+
minY: number;
|
|
73
|
+
maxY: number;
|
|
74
|
+
};
|
|
75
|
+
getLinkExits(zIndex: number): EditorExit[];
|
|
76
|
+
setLabels(labels: any[]): void;
|
|
77
|
+
addRoomLive(room: LiveRoom): void;
|
|
78
|
+
removeRoomById(id: number): void;
|
|
79
|
+
removeRoomsById(ids: Set<number>): void;
|
|
80
|
+
rebuildPlanes(): void;
|
|
81
|
+
rebuildExits(): void;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Editor-side MapReader. Drop-in replacement for `mudlet-map-renderer`'s MapReader
|
|
85
|
+
* — exposes the same read surface the renderer needs, but:
|
|
86
|
+
* - Rooms are live getters over raw `MudletRoom` objects (no clone, no snapshot).
|
|
87
|
+
* - Y is flipped via a getter so mutations to raw.y propagate with correct sign.
|
|
88
|
+
* - Mutation methods are public: `moveRoom`, `setExit`, `addRoom`, `removeRoom`,
|
|
89
|
+
* `setRoomField`. Each updates the raw map and invalidates the relevant Area
|
|
90
|
+
* cache, so `renderer.refresh()` picks up the change.
|
|
91
|
+
*/
|
|
92
|
+
export declare class EditorMapReader {
|
|
93
|
+
private readonly raw;
|
|
94
|
+
private readonly rooms;
|
|
95
|
+
private readonly areas;
|
|
96
|
+
private readonly colors;
|
|
97
|
+
constructor(raw: MudletMap);
|
|
98
|
+
private toRendererLabel;
|
|
99
|
+
private syncRendererLabels;
|
|
100
|
+
getRoom(id: number): LiveRoom | undefined;
|
|
101
|
+
getArea(areaId: number): EditorArea | undefined;
|
|
102
|
+
getAreas(): EditorArea[];
|
|
103
|
+
getRooms(): LiveRoom[];
|
|
104
|
+
getExplorationArea(): undefined;
|
|
105
|
+
decorateWithExploration(): Set<number> | undefined;
|
|
106
|
+
getVisitedRooms(): Set<number> | undefined;
|
|
107
|
+
clearExplorationDecoration(): void;
|
|
108
|
+
isExplorationEnabled(): boolean;
|
|
109
|
+
setVisitedRooms(): Set<number>;
|
|
110
|
+
addVisitedRoom(): boolean;
|
|
111
|
+
addVisitedRooms(): number;
|
|
112
|
+
hasVisitedRoom(): boolean;
|
|
113
|
+
getColorValue(envId: number): string;
|
|
114
|
+
getSymbolColor(envId: number, opacity?: number): string;
|
|
115
|
+
/** Move a room. Coordinates are in RENDER space (same as what culling returns / cursor maps to). */
|
|
116
|
+
moveRoom(id: number, x: number, y: number, z: number): void;
|
|
117
|
+
/** Set a cardinal exit. `toId < 0` removes it. */
|
|
118
|
+
setExit(fromId: number, dir: Direction, toId: number): void;
|
|
119
|
+
setRoomField(id: number, field: 'name' | 'environment' | 'weight' | 'symbol', value: string | number): void;
|
|
120
|
+
setRoomLock(id: number, lock: boolean): void;
|
|
121
|
+
setUserDataEntry(id: number, key: string, value: string | null): void;
|
|
122
|
+
/** Add a raw room (expected `raw.rooms[id]` already set or not, we set it). */
|
|
123
|
+
addRoom(id: number, rawRoom: MudletRoom): void;
|
|
124
|
+
setSpecialExit(roomId: number, name: string, toId: number): void;
|
|
125
|
+
removeSpecialExit(roomId: number, name: string): void;
|
|
126
|
+
setDoor(roomId: number, dir: Direction, value: number): void;
|
|
127
|
+
setSpecialExitDoor(roomId: number, name: string, value: number): void;
|
|
128
|
+
setExitWeight(roomId: number, dir: Direction, value: number): void;
|
|
129
|
+
setSpecialExitWeight(roomId: number, name: string, value: number): void;
|
|
130
|
+
setExitLock(roomId: number, dir: Direction, lock: boolean): void;
|
|
131
|
+
setStub(roomId: number, dir: Direction, stub: boolean): void;
|
|
132
|
+
/** Move a single custom line waypoint. renderX/renderY are render-space (y-down). */
|
|
133
|
+
setCustomLinePoint(roomId: number, exitName: string, index: number, renderX: number, renderY: number): void;
|
|
134
|
+
setCustomLine(roomId: number, exitName: string, points: [number, number][], color: MudletColor, style: number, arrow: boolean): void;
|
|
135
|
+
removeCustomLine(roomId: number, exitName: string): void;
|
|
136
|
+
addArea(id: number, name: string): void;
|
|
137
|
+
removeArea(id: number): void;
|
|
138
|
+
moveRoomsToArea(roomIds: number[], fromAreaId: number, toAreaId: number): void;
|
|
139
|
+
renameArea(id: number, name: string): void;
|
|
140
|
+
setCustomEnvColor(envId: number, color: MudletColor | null): void;
|
|
141
|
+
getLabelSnapshot(areaId: number, labelId: number): LabelSnapshot | null;
|
|
142
|
+
addLabel(areaId: number, snapshot: LabelSnapshot): void;
|
|
143
|
+
removeLabel(areaId: number, labelId: number): void;
|
|
144
|
+
/** Move a label. renderX/renderY are render-space (y-down); stored as raw Mudlet (y-up). */
|
|
145
|
+
moveLabel(areaId: number, labelId: number, renderX: number, renderY: number): void;
|
|
146
|
+
setLabelText(areaId: number, labelId: number, text: string): void;
|
|
147
|
+
setLabelSize(areaId: number, labelId: number, width: number, height: number): void;
|
|
148
|
+
setLabelPixmap(areaId: number, labelId: number, dataUrl: string): void;
|
|
149
|
+
setLabelImageSrc(areaId: number, labelId: number, imageSrc: string | undefined): void;
|
|
150
|
+
setLabelFont(areaId: number, labelId: number, font: LabelFont): void;
|
|
151
|
+
setLabelOutlineColor(areaId: number, labelId: number, color: import('../../mapIO').MudletColor | undefined): void;
|
|
152
|
+
setLabelNoScaling(areaId: number, labelId: number, noScaling: boolean): void;
|
|
153
|
+
setLabelShowOnTop(areaId: number, labelId: number, showOnTop: boolean): void;
|
|
154
|
+
setLabelColors(areaId: number, labelId: number, fg: MudletColor, bg: MudletColor): void;
|
|
155
|
+
getAllEnvColors(): {
|
|
156
|
+
envId: number;
|
|
157
|
+
rgbValue: string;
|
|
158
|
+
}[];
|
|
159
|
+
/**
|
|
160
|
+
* Bulk-delete an entire area and its rooms. Assumes the caller has already
|
|
161
|
+
* severed cross-area incoming exits on raw rooms (same pattern as `deleteRoom`
|
|
162
|
+
* using `neighborEdits`). Rebuilds exit caches on `affectedOtherAreaIds` once
|
|
163
|
+
* at the end — cheap, vs. N× rebuild-all-areas from the per-room path.
|
|
164
|
+
*/
|
|
165
|
+
removeAreaWithRooms(areaId: number, roomIds: number[], affectedOtherAreaIds: number[]): void;
|
|
166
|
+
/** Restore an area + its rooms (for `deleteAreaWithRooms` undo). */
|
|
167
|
+
restoreAreaWithRooms(areaId: number, areaName: string, rooms: Array<{
|
|
168
|
+
id: number;
|
|
169
|
+
room: MudletRoom;
|
|
170
|
+
}>, affectedOtherAreaIds: number[]): void;
|
|
171
|
+
removeRoom(id: number): void;
|
|
172
|
+
/** Bulk-remove many rooms. Caller must have already severed neighbor exits in raw map.
|
|
173
|
+
* Does one rebuildPlanes/rebuildExits per affected area instead of one per room. */
|
|
174
|
+
removeRooms(ids: number[]): void;
|
|
175
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MapRenderer, type Settings } from 'mudlet-map-renderer';
|
|
2
|
+
import type { MudletMap } from '../mapIO';
|
|
3
|
+
import { EditorMapReader, type LiveRoom } from './reader/EditorMapReader';
|
|
4
|
+
export interface SceneHandle {
|
|
5
|
+
renderer: MapRenderer;
|
|
6
|
+
reader: EditorMapReader;
|
|
7
|
+
settings: Settings;
|
|
8
|
+
/** Render-space room (y flipped). Reads pass through to raw. */
|
|
9
|
+
getRenderRoom(id: number): LiveRoom | undefined;
|
|
10
|
+
/** Switch displayed area / z-level. Redraws and fits the viewport. */
|
|
11
|
+
setArea(areaId: number, z: number, insets?: {
|
|
12
|
+
top?: number;
|
|
13
|
+
right?: number;
|
|
14
|
+
bottom?: number;
|
|
15
|
+
left?: number;
|
|
16
|
+
}): void;
|
|
17
|
+
/** Switch displayed area / z-level and pan to a specific map-space point, keeping the current zoom. */
|
|
18
|
+
setAreaAt(areaId: number, z: number, mapX: number, mapY: number): void;
|
|
19
|
+
refresh(): void;
|
|
20
|
+
destroy(): void;
|
|
21
|
+
}
|
|
22
|
+
export declare function createScene(map: MudletMap, container: HTMLDivElement): SceneHandle;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type MudletMap } from '../mapIO';
|
|
2
|
+
import type { Command } from './types';
|
|
3
|
+
export interface SessionData {
|
|
4
|
+
id: string;
|
|
5
|
+
fileName: string;
|
|
6
|
+
mapBytes: ArrayBuffer;
|
|
7
|
+
imageSrcs: Record<string, string>;
|
|
8
|
+
undoStack: Command[];
|
|
9
|
+
currentAreaId: number | null;
|
|
10
|
+
currentZ: number;
|
|
11
|
+
savedAt: number;
|
|
12
|
+
roomCount: number;
|
|
13
|
+
}
|
|
14
|
+
export declare function saveSession(fileName: string, map: MudletMap, undoStack: Command[], currentAreaId: number | null, currentZ: number, existingId?: string): Promise<string>;
|
|
15
|
+
export declare function listSessions(): Promise<SessionData[]>;
|
|
16
|
+
export declare function clearAllSessions(): Promise<void>;
|
|
17
|
+
export declare function clearSession(id: string): Promise<void>;
|
|
18
|
+
/** Reconstruct a MudletMap from a SessionData, re-applying editor-only fields (imageSrc). */
|
|
19
|
+
export declare function restoreMapFromSession(session: SessionData): MudletMap;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import type { MudletMap } from '../mapIO';
|
|
2
|
+
import type { Command, HitItem, HoverTarget, LoadedMap, Pending, Selection, SwatchSet, ToolId } from './types';
|
|
3
|
+
interface UserSettings {
|
|
4
|
+
snapToGrid: boolean;
|
|
5
|
+
}
|
|
6
|
+
export declare function saveUserSettings(patch: Partial<UserSettings>): void;
|
|
7
|
+
export declare function saveSwatchState(sets: SwatchSet[], activeSetId: string | null, activeSwatchId: string | null): void;
|
|
8
|
+
export interface EditorState {
|
|
9
|
+
map: MudletMap | null;
|
|
10
|
+
loaded: LoadedMap | null;
|
|
11
|
+
currentAreaId: number | null;
|
|
12
|
+
currentZ: number;
|
|
13
|
+
activeTool: ToolId;
|
|
14
|
+
selection: Selection;
|
|
15
|
+
hover: HoverTarget;
|
|
16
|
+
pending: Pending;
|
|
17
|
+
snapToGrid: boolean;
|
|
18
|
+
gridStep: number;
|
|
19
|
+
/** When true (Space held), pointer input defers to the renderer's pan regardless of active tool. */
|
|
20
|
+
spaceHeld: boolean;
|
|
21
|
+
undo: Command[];
|
|
22
|
+
redo: Command[];
|
|
23
|
+
status: string;
|
|
24
|
+
/** Bumped on structural changes (room added/removed, area/z changed) that need a full rebuild. */
|
|
25
|
+
structureVersion: number;
|
|
26
|
+
/** Bumped on every mutation (coord, exits, props) to trigger React re-renders of panels. */
|
|
27
|
+
dataVersion: number;
|
|
28
|
+
/** Snapped cursor position in render-space, tracked by tools that show a snap indicator. */
|
|
29
|
+
snapCursor: {
|
|
30
|
+
x: number;
|
|
31
|
+
y: number;
|
|
32
|
+
} | null;
|
|
33
|
+
sidebarTab: string;
|
|
34
|
+
panelCollapsed: boolean;
|
|
35
|
+
contextMenu: ContextMenuState;
|
|
36
|
+
savedUndoLength: number;
|
|
37
|
+
/** When set, the next area/z navigation pans to this map-space point instead of fitting. Consumed and cleared by App. */
|
|
38
|
+
navigateTo: {
|
|
39
|
+
mapX: number;
|
|
40
|
+
mapY: number;
|
|
41
|
+
} | null;
|
|
42
|
+
/** When set, App pans to this map-space point without changing area/z. Consumed and cleared by App. */
|
|
43
|
+
panRequest: {
|
|
44
|
+
mapX: number;
|
|
45
|
+
mapY: number;
|
|
46
|
+
} | null;
|
|
47
|
+
/** Tracks the last Alt+click position (integer cell) and cycle index for overlapping-element cycling. */
|
|
48
|
+
hitCycle: {
|
|
49
|
+
x: number;
|
|
50
|
+
y: number;
|
|
51
|
+
index: number;
|
|
52
|
+
} | null;
|
|
53
|
+
/** When true, label resize preserves the aspect ratio at the start of the drag. */
|
|
54
|
+
labelAspectRatioLocked: boolean;
|
|
55
|
+
swatchSets: SwatchSet[];
|
|
56
|
+
pluginSwatchSets: SwatchSet[];
|
|
57
|
+
activeSwatchSetId: string | null;
|
|
58
|
+
activeSwatchId: string | null;
|
|
59
|
+
swatchPaletteOpen: boolean;
|
|
60
|
+
sessionId: string | null;
|
|
61
|
+
}
|
|
62
|
+
export type ContextMenuState = {
|
|
63
|
+
kind: 'customLinePoint';
|
|
64
|
+
roomId: number;
|
|
65
|
+
exitName: string;
|
|
66
|
+
pointIndex: number;
|
|
67
|
+
screenX: number;
|
|
68
|
+
screenY: number;
|
|
69
|
+
} | {
|
|
70
|
+
kind: 'room';
|
|
71
|
+
roomId: number;
|
|
72
|
+
screenX: number;
|
|
73
|
+
screenY: number;
|
|
74
|
+
} | {
|
|
75
|
+
kind: 'disambiguate';
|
|
76
|
+
hits: HitItem[];
|
|
77
|
+
screenX: number;
|
|
78
|
+
screenY: number;
|
|
79
|
+
} | {
|
|
80
|
+
kind: 'label';
|
|
81
|
+
areaId: number;
|
|
82
|
+
labelId: number;
|
|
83
|
+
screenX: number;
|
|
84
|
+
screenY: number;
|
|
85
|
+
} | null;
|
|
86
|
+
type Listener = (state: EditorState) => void;
|
|
87
|
+
declare class Store {
|
|
88
|
+
private state;
|
|
89
|
+
private listeners;
|
|
90
|
+
getState: () => EditorState;
|
|
91
|
+
setState: (patch: Partial<EditorState> | ((s: EditorState) => Partial<EditorState>)) => void;
|
|
92
|
+
subscribe: (listener: Listener) => (() => void);
|
|
93
|
+
bumpData: () => void;
|
|
94
|
+
bumpStructure: () => void;
|
|
95
|
+
}
|
|
96
|
+
export declare const store: Store;
|
|
97
|
+
export declare function useEditorState<T>(selector: (s: EditorState) => T): T;
|
|
98
|
+
export {};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { MapRenderer, Settings } from 'mudlet-map-renderer';
|
|
2
|
+
import type { HitItem, Selection, ToolId } from './types';
|
|
3
|
+
import type { SceneHandle } from './scene';
|
|
4
|
+
export interface ToolContext {
|
|
5
|
+
renderer: MapRenderer;
|
|
6
|
+
container: HTMLElement;
|
|
7
|
+
settings: Settings;
|
|
8
|
+
refresh: () => void;
|
|
9
|
+
scene: SceneHandle;
|
|
10
|
+
}
|
|
11
|
+
export interface Tool {
|
|
12
|
+
id: ToolId;
|
|
13
|
+
cursor?: string;
|
|
14
|
+
onPointerDown?(ev: PointerEvent, ctx: ToolContext): boolean;
|
|
15
|
+
onPointerMove?(ev: PointerEvent, ctx: ToolContext): boolean | void;
|
|
16
|
+
onPointerUp?(ev: PointerEvent, ctx: ToolContext): boolean | void;
|
|
17
|
+
onContextMenu?(ev: MouseEvent, ctx: ToolContext): boolean | void;
|
|
18
|
+
onCancel?(ctx: ToolContext): void;
|
|
19
|
+
}
|
|
20
|
+
type PickSpecialExitCb = (roomId: number) => void;
|
|
21
|
+
export declare function registerSpecialExitPickCb(cb: PickSpecialExitCb | null): void;
|
|
22
|
+
/**
|
|
23
|
+
* Combo tool: click a room to select it, drag to move it, click empty to clear selection.
|
|
24
|
+
* Drag vs click is detected naturally — a drag only "moves" when the snapped cursor cell
|
|
25
|
+
* differs from the room's current cell, so micro-jitter on a click doesn't trigger a move.
|
|
26
|
+
*/
|
|
27
|
+
export declare const selectTool: Tool;
|
|
28
|
+
/**
|
|
29
|
+
* Connect is a drag interaction. Pointer-down on a room picks the source; if
|
|
30
|
+
* the cursor is near one of the 8 handle positions (corners + midpoints) the
|
|
31
|
+
* source direction is fixed explicitly. Otherwise the direction is inferred
|
|
32
|
+
* from the target on drop.
|
|
33
|
+
*/
|
|
34
|
+
export declare const connectTool: Tool;
|
|
35
|
+
export declare const unlinkTool: Tool;
|
|
36
|
+
export declare const addRoomTool: Tool;
|
|
37
|
+
export declare const deleteTool: Tool;
|
|
38
|
+
export declare const panTool: Tool;
|
|
39
|
+
export declare const customLineTool: Tool;
|
|
40
|
+
/**
|
|
41
|
+
* Revert any raw mutations made during the in-progress draw. Safe to call even
|
|
42
|
+
* when no raw write happened yet (restores / removes based on previousSnapshot).
|
|
43
|
+
*/
|
|
44
|
+
export declare function restorePendingCustomLine(pending: import('./types').PendingCustomLine, scene: SceneHandle): void;
|
|
45
|
+
export declare function finishCustomLine(pending: import('./types').PendingCustomLine, ctx?: ToolContext): void;
|
|
46
|
+
export declare const addLabelTool: Tool;
|
|
47
|
+
export declare function getActiveSwatch(s: import('./store').EditorState): import('./types').Swatch | null;
|
|
48
|
+
export declare const paintTool: Tool;
|
|
49
|
+
export declare const TOOLS: Record<ToolId, Tool>;
|
|
50
|
+
export declare function hitToSelection(hit: HitItem): Selection;
|
|
51
|
+
export declare function hitStatusLabel(hit: HitItem): string;
|
|
52
|
+
export {};
|