mudlet-map-editor 0.13.0 → 0.14.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 CHANGED
@@ -58,6 +58,12 @@ npm run build # Type-check and bundle for production
58
58
  npm run preview # Preview the production build
59
59
  ```
60
60
 
61
+ ## Extending with plugins
62
+
63
+ Drop a file at `src/plugins/<name>/index.ts` with a default export implementing `EditorPlugin` and it is picked up automatically at build time. Plugins can add sidebar tabs, room panel sections, swatch presets, map check warnings, and lifecycle hooks (map open/close/save, app ready, custom overlay UI).
64
+
65
+ See [docs/plugins.md](docs/plugins.md) for the full interface reference and examples.
66
+
61
67
  ## Tech Stack
62
68
 
63
69
  - **React 19** + **TypeScript**
@@ -2,6 +2,16 @@ import type { ReactNode } from 'react';
2
2
  import type { MudletMap, MudletRoom } from '../mapIO';
3
3
  import type { SwatchSet } from './types';
4
4
  import type { SceneHandle } from './scene';
5
+ export interface PluginCheckResult {
6
+ /** Stable identifier for this warning instance; used to namespace ack keys. */
7
+ id: string;
8
+ /** Bold title shown in the warnings list. */
9
+ message: string;
10
+ /** Optional secondary description. */
11
+ detail?: string;
12
+ /** If set, the "Go" button navigates to this room. */
13
+ roomId?: number;
14
+ }
5
15
  export interface SidebarTab {
6
16
  id: string;
7
17
  label: string;
@@ -22,6 +32,8 @@ export interface RoomPanelSection {
22
32
  render(props: RoomSectionProps): ReactNode;
23
33
  }
24
34
  export interface EditorPlugin {
35
+ /** Stable identifier used to namespace plugin warning ack keys. Defaults to array index if omitted. */
36
+ id?: string;
25
37
  onAppReady?(): Promise<void>;
26
38
  onMapOpened?(map: MudletMap): void;
27
39
  onMapClosed?(): void;
@@ -31,4 +43,8 @@ export interface EditorPlugin {
31
43
  swatchSets?(): SwatchSet[];
32
44
  /** Contribute additional sections rendered at the bottom of the room selection panel. */
33
45
  roomPanelSections?(): RoomPanelSection[];
46
+ /** Return custom map warnings. Called whenever built-in warnings are recomputed. */
47
+ mapChecks?(map: MudletMap, sceneRef: {
48
+ current: SceneHandle | null;
49
+ }): PluginCheckResult[];
34
50
  }
@@ -37,6 +37,13 @@ export type MapWarning = {
37
37
  dir: string;
38
38
  targetId: number;
39
39
  areaName: string;
40
+ } | {
41
+ kind: 'plugin';
42
+ pluginId: string;
43
+ id: string;
44
+ message: string;
45
+ detail?: string;
46
+ roomId?: number;
40
47
  };
41
48
  export declare function warningKey(w: MapWarning): string;
42
49
  export declare function collectWarnings(sceneRef: {
@@ -1,6 +1,6 @@
1
1
  import './styles.css';
2
2
  export { default as App } from './App';
3
- export type { EditorPlugin, SidebarTab, RoomPanelSection, RoomSectionProps } from './editor/plugin';
3
+ export type { EditorPlugin, SidebarTab, RoomPanelSection, RoomSectionProps, PluginCheckResult } from './editor/plugin';
4
4
  export type { SwatchSet, Swatch } from './editor/types';
5
5
  export { loadUrlIntoStore } from './editor/loadFile';
6
6
  export { getMapBytes } from './editor/mapBytes';
package/dist-lib/index.js CHANGED
@@ -3166,6 +3166,7 @@ function Ke(e) {
3166
3166
  case "danglingExit": return `danglingExit:${e.roomId}:${e.dir}`;
3167
3167
  case "duplicateCoord": return `duplicateCoord:${e.areaId}:${e.x}:${e.y}:${e.z}`;
3168
3168
  case "coordMismatch": return `coordMismatch:${e.roomId}:${e.dir}`;
3169
+ case "plugin": return `plugin:${e.pluginId}:${e.id}`;
3169
3170
  }
3170
3171
  }
3171
3172
  var qe = [
@@ -3456,6 +3457,13 @@ function Qe({ sceneRef: e }) {
3456
3457
  ]
3457
3458
  })]
3458
3459
  });
3460
+ if (e.kind === "plugin") return /* @__PURE__ */ h("span", {
3461
+ className: "warning-text",
3462
+ children: [/* @__PURE__ */ m("strong", { children: e.message }), e.detail && /* @__PURE__ */ m("span", {
3463
+ className: "warning-detail",
3464
+ children: e.detail
3465
+ })]
3466
+ });
3459
3467
  }
3460
3468
  function x(e) {
3461
3469
  return e.kind === "zeroSizeLabel" ? /* @__PURE__ */ m("button", {
@@ -3468,7 +3476,7 @@ function Qe({ sceneRef: e }) {
3468
3476
  className: "warning-go-btn",
3469
3477
  onClick: () => Ze(e.roomIds[0]),
3470
3478
  children: "Go"
3471
- }) : /* @__PURE__ */ m("button", {
3479
+ }) : e.kind === "plugin" && e.roomId == null ? null : /* @__PURE__ */ m("button", {
3472
3480
  type: "button",
3473
3481
  className: "warning-go-btn",
3474
3482
  onClick: () => Ze(e.roomId),
@@ -13727,11 +13735,20 @@ function hi({ plugins: e = [], title: n = "Mudlet Map Editor" }) {
13727
13735
  N.setState({ warnings: [] });
13728
13736
  return;
13729
13737
  }
13730
- N.setState({ warnings: Ye(c, l) });
13738
+ let t = Ye(c, l), n = e.flatMap((e, t) => {
13739
+ let n = e.mapChecks?.(l, c) ?? [], r = e.id ?? String(t);
13740
+ return n.map((e) => ({
13741
+ kind: "plugin",
13742
+ pluginId: r,
13743
+ ...e
13744
+ }));
13745
+ });
13746
+ N.setState({ warnings: [...t, ...n] });
13731
13747
  }, [
13732
13748
  l,
13733
13749
  P((e) => e.undo.length),
13734
- P((e) => e.warningAckVersion)
13750
+ P((e) => e.warningAckVersion),
13751
+ e
13735
13752
  ]), i(() => {
13736
13753
  if (!l || !r.current) return;
13737
13754
  let e = ii(l, r.current);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mudlet-map-editor",
3
- "version": "0.13.0",
3
+ "version": "0.14.0",
4
4
  "type": "module",
5
5
  "description": "Mudlet Map Editor",
6
6
  "keywords": [