@modern-ant/model-diagram 0.2.5

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 (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +396 -0
  3. package/THIRD_PARTY_NOTICES.md +32 -0
  4. package/dist/events/emitter.d.ts +8 -0
  5. package/dist/events/emitter.d.ts.map +1 -0
  6. package/dist/events/emitter.js +30 -0
  7. package/dist/events/emitter.js.map +1 -0
  8. package/dist/events/types.d.ts +94 -0
  9. package/dist/events/types.d.ts.map +1 -0
  10. package/dist/events/types.js +2 -0
  11. package/dist/events/types.js.map +1 -0
  12. package/dist/index.d.ts +8 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +4 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/jointjs/adapter.d.ts +75 -0
  17. package/dist/jointjs/adapter.d.ts.map +1 -0
  18. package/dist/jointjs/adapter.js +905 -0
  19. package/dist/jointjs/adapter.js.map +1 -0
  20. package/dist/layout/elk-layout.d.ts +15 -0
  21. package/dist/layout/elk-layout.d.ts.map +1 -0
  22. package/dist/layout/elk-layout.js +112 -0
  23. package/dist/layout/elk-layout.js.map +1 -0
  24. package/dist/model/types.d.ts +45 -0
  25. package/dist/model/types.d.ts.map +1 -0
  26. package/dist/model/types.js +2 -0
  27. package/dist/model/types.js.map +1 -0
  28. package/dist/model/validation.d.ts +8 -0
  29. package/dist/model/validation.d.ts.map +1 -0
  30. package/dist/model/validation.js +95 -0
  31. package/dist/model/validation.js.map +1 -0
  32. package/dist/rendering/diagram.d.ts +96 -0
  33. package/dist/rendering/diagram.d.ts.map +1 -0
  34. package/dist/rendering/diagram.js +733 -0
  35. package/dist/rendering/diagram.js.map +1 -0
  36. package/dist/rendering/geometry.d.ts +20 -0
  37. package/dist/rendering/geometry.d.ts.map +1 -0
  38. package/dist/rendering/geometry.js +42 -0
  39. package/dist/rendering/geometry.js.map +1 -0
  40. package/dist/rendering/store.d.ts +30 -0
  41. package/dist/rendering/store.d.ts.map +1 -0
  42. package/dist/rendering/store.js +231 -0
  43. package/dist/rendering/store.js.map +1 -0
  44. package/dist/routing/relationship-style.d.ts +15 -0
  45. package/dist/routing/relationship-style.d.ts.map +1 -0
  46. package/dist/routing/relationship-style.js +37 -0
  47. package/dist/routing/relationship-style.js.map +1 -0
  48. package/package.json +58 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 @modern-ant/model-diagram contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,396 @@
1
+ # @modern-ant/model-diagram
2
+
3
+ A framework-neutral TypeScript library for rendering and interactively
4
+ arranging portable UML-style model diagrams in a browser. It accepts resolved
5
+ JavaScript objects, keeps layout changes in memory, and emits semantic events
6
+ that a host can translate into its own persistence format.
7
+
8
+ The library renders and edits diagram layout in memory and emits semantic
9
+ events. It does not persist YAML or project files.
10
+
11
+ ## Responsibility boundary
12
+
13
+ ```text
14
+ YAML or another host format
15
+ -> host parser/resolver
16
+ -> portable DiagramModel + DiagramLayout
17
+ -> @modern-ant/model-diagram
18
+ -> interactive diagram
19
+ -> semantic events
20
+ -> host persistence
21
+ ```
22
+
23
+ This package does not read or write files, parse YAML, manage Git or locks,
24
+ authenticate users, decide permissions, or depend on a particular editor or
25
+ UI framework. JointJS is kept behind the internal
26
+ `src/jointjs` adapter and no JointJS model, view, cell, vertex, or SVG identity
27
+ appears in the public API.
28
+
29
+ ## Installation
30
+
31
+ ```sh
32
+ npm install @modern-ant/model-diagram
33
+ ```
34
+
35
+ Before publication, use the repository directly through an npm workspace or a
36
+ local file dependency:
37
+
38
+ ```json
39
+ {
40
+ "dependencies": {
41
+ "@modern-ant/model-diagram": "file:../model-diagram"
42
+ }
43
+ }
44
+ ```
45
+
46
+ The package is ESM-first and includes TypeScript declarations and source maps.
47
+ The host must give the container a non-zero width and height. The host remains
48
+ consumer-owned: its CSS controls its dimensions, and the package sizes a
49
+ library-owned child rendering surface to the host's current bounds. A
50
+ `ResizeObserver` keeps that internal surface synchronized when flex, grid,
51
+ block, or resizable-panel layout changes the host size.
52
+
53
+ ## Minimal usage
54
+
55
+ ```ts
56
+ import {
57
+ createDiagram,
58
+ type DiagramLayout,
59
+ type DiagramModel,
60
+ } from "@modern-ant/model-diagram";
61
+
62
+ const model: DiagramModel = {
63
+ classes: [
64
+ {
65
+ id: "parcel",
66
+ name: "Parcel",
67
+ stereotype: "featureType",
68
+ note: "A registered spatial unit.",
69
+ attributes: [
70
+ { name: "id", type: "UUID", required: true },
71
+ { name: "area", type: "Measure", multiplicity: "0..1" },
72
+ ],
73
+ },
74
+ { id: "right", name: "Right" },
75
+ ],
76
+ relationships: [
77
+ {
78
+ id: "parcel-right",
79
+ from: "parcel",
80
+ to: "right",
81
+ type: "association",
82
+ label: "is subject to",
83
+ role: "parcel / applicable right",
84
+ fromMultiplicity: "1",
85
+ toMultiplicity: "0..*",
86
+ },
87
+ ],
88
+ };
89
+
90
+ const storedLayout: DiagramLayout = {
91
+ classes: { parcel: { x: 300, y: 180 } },
92
+ relationships: {
93
+ "parcel-right": { waypoints: [{ x: 540, y: 260 }] },
94
+ },
95
+ };
96
+
97
+ const diagram = createDiagram(document.querySelector("#diagram")!, {
98
+ model,
99
+ layout: storedLayout,
100
+ editable: true,
101
+ });
102
+
103
+ const unsubscribe = diagram.on("class-position-changed", (event) => {
104
+ // The host decides how and when this becomes a YAML/file update.
105
+ console.log(event.classId, event.x, event.y);
106
+ });
107
+
108
+ await diagram.whenReady();
109
+ diagram.fitToContent();
110
+
111
+ // Later:
112
+ unsubscribe();
113
+ diagram.destroy();
114
+ ```
115
+
116
+ ## Input model
117
+
118
+ ```ts
119
+ interface DiagramModel {
120
+ classes: DiagramClass[];
121
+ relationships: DiagramRelationship[];
122
+ }
123
+
124
+ interface DiagramClass {
125
+ id: string;
126
+ name: string;
127
+ note?: string;
128
+ stereotype?: string;
129
+ attributes?: DiagramAttribute[];
130
+ }
131
+
132
+ interface DiagramAttribute {
133
+ name: string;
134
+ type?: string;
135
+ required?: boolean;
136
+ multiplicity?: string;
137
+ description?: string;
138
+ }
139
+
140
+ interface DiagramRelationship {
141
+ id: string;
142
+ from: string;
143
+ to: string;
144
+ type:
145
+ | "association"
146
+ | "aggregation"
147
+ | "composition"
148
+ | "inheritance"
149
+ | "dependency";
150
+ label?: string;
151
+ role?: string;
152
+ fromMultiplicity?: string;
153
+ toMultiplicity?: string;
154
+ }
155
+ ```
156
+
157
+ Class and relationship IDs must be unique. Relationship endpoints must refer
158
+ to existing class IDs. `createDiagram`, `setModel`, and `setLayout` fail with a
159
+ `DiagramValidationError` containing all detected issues when their input is
160
+ invalid.
161
+
162
+ `note` contains explanatory class text rendered in a connected note element.
163
+ `label` is short display text placed near the relationship midpoint and remains
164
+ distinct from `role`, relationship type, and endpoint multiplicities. Empty or
165
+ whitespace-only relationship labels are normalized as absent.
166
+
167
+ Class attributes render in input order. A filled dot marks required attributes
168
+ and an open dot marks optional attributes. Association, hollow aggregation
169
+ diamond, filled composition diamond, hollow inheritance triangle, and dashed
170
+ dependency arrow styles remain visually distinct. Multiplicities and roles are
171
+ rendered as labels on the relationship.
172
+
173
+ ## Portable layout
174
+
175
+ ```ts
176
+ interface DiagramLayout {
177
+ classes?: Record<string, Position>;
178
+ notes?: Record<string, Position>;
179
+ relationships?: Record<string, RelationshipLayout>;
180
+ }
181
+
182
+ interface Position {
183
+ x: number;
184
+ y: number;
185
+ }
186
+
187
+ interface RelationshipLayout {
188
+ waypoints?: Position[];
189
+ }
190
+ ```
191
+
192
+ All positions are diagram-space coordinates. Class and note positions are
193
+ element centers. Relationship waypoints are ordered intermediate points only;
194
+ attachment points are calculated at the class boundaries and are never part
195
+ of persisted layout.
196
+
197
+ - A class without stored coordinates is placed by ELK during initial layout.
198
+ - A note without stored coordinates follows its class at a default offset.
199
+ - A stored note position makes that note independent of later class movement.
200
+ - A relationship without waypoints uses automatic Manhattan routing.
201
+ - A relationship with waypoints preserves their order and uses those points
202
+ while its endpoint segments continue to follow moved classes.
203
+
204
+ `diagram.getLayout()` returns current class centers, manual note centers, and
205
+ non-empty waypoint lists. Layout is owned per diagram instance, so the same
206
+ class ID can have different positions in multiple diagrams.
207
+
208
+ ## Events
209
+
210
+ Subscribe with `diagram.on(type, listener)`. It returns an idempotent
211
+ unsubscribe function. Every event is a plain object with semantic IDs only.
212
+
213
+ | Event | Meaning |
214
+ | --- | --- |
215
+ | `selection-changed` | Class, relationship, note, waypoint, or empty selection |
216
+ | `class-position-preview` | High-frequency class drag preview |
217
+ | `class-position-changed` | Class drag committed on pointer release |
218
+ | `note-position-preview` | High-frequency note drag preview |
219
+ | `note-position-changed` | Note drag committed on pointer release |
220
+ | `relationship-waypoint-added` | Ordered intermediate point inserted |
221
+ | `relationship-waypoint-preview` | High-frequency waypoint drag preview |
222
+ | `relationship-waypoint-changed` | Waypoint drag committed |
223
+ | `relationship-waypoint-removed` | Intermediate point removed |
224
+ | `relationship-route-reset` | All manual waypoints removed |
225
+ | `relationship-changed` | Committed relationship label changes |
226
+ | `auto-layout-completed` | ELK result applied in memory and available to the host |
227
+ | `history-changed` | Current actionable `canUndo` and `canRedo` availability |
228
+
229
+ Preview events are transient. Hosts should normally persist only committed
230
+ events. A waypoint selection has this renderer-neutral shape:
231
+
232
+ ```ts
233
+ {
234
+ kind: "relationship-waypoint",
235
+ relationshipId: "parcel-right",
236
+ index: 0,
237
+ }
238
+ ```
239
+
240
+ ## Editing and waypoint interaction
241
+
242
+ In editable mode, classes and notes are draggable. Moving a class
243
+ keeps all relationship endpoints and note connectors attached. A note follows
244
+ its class until the user moves the note; its committed position then becomes
245
+ manual and independent.
246
+
247
+ Select a relationship to show its waypoint handles. Double-click a relationship
248
+ segment to insert a waypoint, drag a handle to move it, and double-click a
249
+ handle to remove it. Delete or Backspace also removes a selected waypoint. For
250
+ touch interfaces and explicit host controls, use the semantic command methods:
251
+
252
+ ```ts
253
+ diagram.addRelationshipWaypoint(id, { x: 640, y: 300 }, 1);
254
+ diagram.moveRelationshipWaypoint(id, 1, { x: 680, y: 320 });
255
+ diagram.removeRelationshipWaypoint(id, 1);
256
+ diagram.resetRelationshipRoute(id);
257
+ diagram.updateRelationship(id, {
258
+ label: "owns",
259
+ });
260
+ ```
261
+
262
+ Host property editors can call `updateRelationship`. These commands emit
263
+ committed semantic events for hosts to persist and throw while the diagram is
264
+ read-only. `relationship-changed` contains the relationship ID and only the
265
+ normalized fields changed by the command. Pass `undefined`, an empty string, or
266
+ whitespace-only text to remove a label; the emitted `changes` object retains
267
+ that changed key with the value `undefined`.
268
+
269
+ ## Undo and redo
270
+
271
+ Each diagram has independent, in-memory semantic history:
272
+
273
+ ```ts
274
+ if (diagram.canUndo()) diagram.undo();
275
+ if (diagram.canRedo()) diagram.redo();
276
+ diagram.clearHistory();
277
+ ```
278
+
279
+ Class moves, note moves, relationship label changes, waypoint additions, moves
280
+ and removals, route resets, and explicit automatic layout are
281
+ undoable. Pointer-move previews never enter history; one completed drag creates
282
+ one entry. Undo and redo emit the normal
283
+ committed semantic event for the resulting state, followed by
284
+ `history-changed`. They never emit preview events, fit the viewport, or reset
285
+ zoom, pan, or valid selection.
286
+
287
+ Auto-layout is one atomic history entry regardless of how many classes move.
288
+ Its undo and redo each restore all affected class positions before emitting one
289
+ `auto-layout-completed` event containing the complete resulting layout. Undoing
290
+ a route reset restores the full route before emitting an ordered
291
+ `relationship-waypoint-added` event for each restored point; redoing it emits
292
+ `relationship-route-reset`.
293
+
294
+ Undoing and then committing a new edit clears the redo stack. `setModel()` and
295
+ `setLayout()` are authoritative external replacements and clear both stacks so
296
+ old entries cannot apply to refreshed state. History is not persistence,
297
+ revision control, YAML state, or Git state, and `destroy()` discards it.
298
+
299
+ In read-only mode, `canUndo()` and `canRedo()` both return `false`, while
300
+ `undo()` and `redo()` throw the same `Diagram is read-only` error as other edit
301
+ commands. The stacks are retained, so their availability returns if editable
302
+ mode is restored. `clearHistory()` remains available because it does not mutate
303
+ the diagram's semantic state.
304
+
305
+ ## Automatic layout
306
+
307
+ ELK's layered algorithm calculates initial centers whenever at least one class
308
+ has no stored position. Stored class positions remain authoritative during that
309
+ initial pass; missing classes are placed into non-overlapping available space
310
+ where practical.
311
+
312
+ ```ts
313
+ const calculated = await diagram.autoLayout({
314
+ direction: "RIGHT",
315
+ nodeSpacing: 70,
316
+ layerSpacing: 130,
317
+ });
318
+ ```
319
+
320
+ An explicit `autoLayout()` rearranges all classes by default. Pass
321
+ `preserveStoredPositions: true` to keep current positions and fill around them.
322
+ The method returns and emits the resulting portable layout and records one
323
+ atomic history transaction. It never persists that result. Automatic layout
324
+ during initial creation or an authoritative `setModel()`/`setLayout()` refresh
325
+ does not create history.
326
+
327
+ ## Viewport
328
+
329
+ Drag blank space to pan and use the wheel to zoom around the pointer. The host
330
+ can also call `fitToContent()`, `setZoom()`, `zoomIn()`, `zoomOut()`, or
331
+ `panBy()`. The SVG viewport uses transforms over diagram-space coordinates,
332
+ not a fixed drawing boundary, so classes, notes, labels, markers, and waypoints
333
+ can be moved to negative or far-positive positions and recovered with
334
+ fit-to-content.
335
+
336
+ ## Read-only mode and updates
337
+
338
+ ```ts
339
+ diagram.setEditable(false); // pan, zoom, and selection remain enabled
340
+ diagram.setLayout(nextLayout);
341
+ diagram.setModel(nextModel);
342
+ const currentModel = diagram.getModel(); // detached semantic snapshot
343
+ ```
344
+
345
+ `setLayout` updates the existing JointJS cells rather than recreating the
346
+ renderer. `setModel` reconciles elements by semantic ID and preserves current
347
+ positions and waypoints for IDs that remain stable. Both calls clear local
348
+ undo/redo history.
349
+
350
+ Call `destroy()` when removing a diagram. It is safe to call more than once and
351
+ disconnects resize observation, native listeners, JointJS views, graph state,
352
+ and event subscriptions.
353
+
354
+ ## Host integration responsibilities
355
+
356
+ A host application must:
357
+
358
+ 1. Parse and resolve its source format into `DiagramModel` and `DiagramLayout`.
359
+ 2. Decide whether editing is permitted and call `setEditable` accordingly.
360
+ 3. Debounce or otherwise handle preview events if it displays live state.
361
+ 4. Translate committed semantic events into its own YAML or other persistence
362
+ operations.
363
+ 5. Handle revisions, conflicts, locks, files, Git, authentication, and errors.
364
+ 6. Dispose the instance when its owning view unmounts.
365
+
366
+ ## Browser requirements
367
+
368
+ The runtime targets modern evergreen browsers with ESM, SVG, Pointer Events,
369
+ `ResizeObserver`, and standard DOM geometry APIs. It has no server-side
370
+ rendering contract. React hosts should create and destroy the imperative
371
+ instance from their component lifecycle.
372
+
373
+ ## Development demo
374
+
375
+ ```sh
376
+ npm install
377
+ npm run demo
378
+ ```
379
+
380
+ The demo contains all five relationship types, labels and multiplicities,
381
+ notes, automatic layout, drag and waypoint editing, viewport controls, a
382
+ read-only toggle, and a semantic event log. It is excluded from the runtime
383
+ package.
384
+
385
+ ## Licensing
386
+
387
+ This project is licensed under the [MIT License](./LICENSE).
388
+
389
+ Runtime dependencies and their separately applicable licenses are documented
390
+ in [THIRD_PARTY_NOTICES.md](./THIRD_PARTY_NOTICES.md). JointJS Community
391
+ Edition is consumed as an unmodified external dependency under MPL-2.0. ELK.js
392
+ is consumed as an unmodified external dependency under its EPL-2.0 option.
393
+
394
+ Modern ANT and associated marks are trademarks of Modern ANT SARL. The MIT
395
+ license applies to the software and does not grant rights to use Modern ANT
396
+ trademarks.
@@ -0,0 +1,32 @@
1
+ # Third-party notices
2
+
3
+ `@modern-ant/model-diagram` is MIT-licensed. It consumes the following
4
+ unmodified runtime dependencies as separately installed npm packages. Their
5
+ licenses apply to those packages; the project's MIT license does not replace
6
+ them.
7
+
8
+ ## JointJS Community Edition
9
+
10
+ - Package: `@joint/core` 4.3.3
11
+ - License: Mozilla Public License 2.0 (`MPL-2.0`)
12
+ - Source: <https://github.com/clientIO/joint>
13
+ - License text: <https://github.com/clientIO/joint/blob/master/LICENSE>
14
+
15
+ JointJS is an external dependency and implementation detail. This repository
16
+ does not copy, vendor, or modify JointJS source.
17
+
18
+ ## ELK.js
19
+
20
+ - Package: `elkjs` 0.12.0
21
+ - License expression: Eclipse Public License 2.0 OR GNU General Public License
22
+ 3.0 or later (`EPL-2.0 OR GPL-3.0-or-later`)
23
+ - This project consumes the package under the EPL-2.0 option.
24
+ - Source: <https://github.com/kieler/elkjs>
25
+ - License text: <https://github.com/kieler/elkjs/blob/master/LICENSE.md>
26
+
27
+ ELK.js is an external dependency and is not modified in this repository.
28
+
29
+ Versions above are the versions verified when this notice was written. The
30
+ package manifest permits compatible updates, so distributors should re-check
31
+ the installed versions and their included license files when preparing a
32
+ release.
@@ -0,0 +1,8 @@
1
+ import type { DiagramEventListener, DiagramEventMap, DiagramEventType, Unsubscribe } from "./types.js";
2
+ export declare class TypedEventEmitter {
3
+ private readonly listeners;
4
+ on<K extends DiagramEventType>(type: K, listener: DiagramEventListener<K>): Unsubscribe;
5
+ emit<K extends DiagramEventType>(type: K, event: DiagramEventMap[K]): void;
6
+ clear(): void;
7
+ }
8
+ //# sourceMappingURL=emitter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.d.ts","sourceRoot":"","sources":["../../src/events/emitter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,WAAW,EACZ,MAAM,YAAY,CAAC;AAEpB,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAGtB;IAEJ,EAAE,CAAC,CAAC,SAAS,gBAAgB,EAC3B,IAAI,EAAE,CAAC,EACP,QAAQ,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC,WAAW;IAoBd,IAAI,CAAC,CAAC,SAAS,gBAAgB,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,IAAI;IAK1E,KAAK,IAAI,IAAI;CAGd"}
@@ -0,0 +1,30 @@
1
+ export class TypedEventEmitter {
2
+ listeners = new Map();
3
+ on(type, listener) {
4
+ let listenersForType = this.listeners.get(type);
5
+ if (!listenersForType) {
6
+ listenersForType = new Set();
7
+ this.listeners.set(type, listenersForType);
8
+ }
9
+ const erasedListener = listener;
10
+ listenersForType.add(erasedListener);
11
+ let subscribed = true;
12
+ return () => {
13
+ if (!subscribed)
14
+ return;
15
+ subscribed = false;
16
+ listenersForType?.delete(erasedListener);
17
+ if (listenersForType?.size === 0)
18
+ this.listeners.delete(type);
19
+ };
20
+ }
21
+ emit(type, event) {
22
+ const snapshot = [...(this.listeners.get(type) ?? [])];
23
+ for (const listener of snapshot)
24
+ listener(event);
25
+ }
26
+ clear() {
27
+ this.listeners.clear();
28
+ }
29
+ }
30
+ //# sourceMappingURL=emitter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"emitter.js","sourceRoot":"","sources":["../../src/events/emitter.ts"],"names":[],"mappings":"AAOA,MAAM,OAAO,iBAAiB;IACX,SAAS,GAAG,IAAI,GAAG,EAGjC,CAAC;IAEJ,EAAE,CACA,IAAO,EACP,QAAiC;QAEjC,IAAI,gBAAgB,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChD,IAAI,CAAC,gBAAgB,EAAE,CAAC;YACtB,gBAAgB,GAAG,IAAI,GAAG,EAAE,CAAC;YAC7B,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,EAAE,gBAAgB,CAAC,CAAC;QAC7C,CAAC;QACD,MAAM,cAAc,GAAG,QAEd,CAAC;QACV,gBAAgB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;QAErC,IAAI,UAAU,GAAG,IAAI,CAAC;QACtB,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,UAAU;gBAAE,OAAO;YACxB,UAAU,GAAG,KAAK,CAAC;YACnB,gBAAgB,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC;YACzC,IAAI,gBAAgB,EAAE,IAAI,KAAK,CAAC;gBAAE,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;QAChE,CAAC,CAAC;IACJ,CAAC;IAED,IAAI,CAA6B,IAAO,EAAE,KAAyB;QACjE,MAAM,QAAQ,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC;QACvD,KAAK,MAAM,QAAQ,IAAI,QAAQ;YAAE,QAAQ,CAAC,KAAK,CAAC,CAAC;IACnD,CAAC;IAED,KAAK;QACH,IAAI,CAAC,SAAS,CAAC,KAAK,EAAE,CAAC;IACzB,CAAC;CACF","sourcesContent":["import type {\n DiagramEventListener,\n DiagramEventMap,\n DiagramEventType,\n Unsubscribe,\n} from \"./types.js\";\n\nexport class TypedEventEmitter {\n private readonly listeners = new Map<\n DiagramEventType,\n Set<(event: DiagramEventMap[DiagramEventType]) => void>\n >();\n\n on<K extends DiagramEventType>(\n type: K,\n listener: DiagramEventListener<K>,\n ): Unsubscribe {\n let listenersForType = this.listeners.get(type);\n if (!listenersForType) {\n listenersForType = new Set();\n this.listeners.set(type, listenersForType);\n }\n const erasedListener = listener as (\n event: DiagramEventMap[DiagramEventType],\n ) => void;\n listenersForType.add(erasedListener);\n\n let subscribed = true;\n return () => {\n if (!subscribed) return;\n subscribed = false;\n listenersForType?.delete(erasedListener);\n if (listenersForType?.size === 0) this.listeners.delete(type);\n };\n }\n\n emit<K extends DiagramEventType>(type: K, event: DiagramEventMap[K]): void {\n const snapshot = [...(this.listeners.get(type) ?? [])];\n for (const listener of snapshot) listener(event);\n }\n\n clear(): void {\n this.listeners.clear();\n }\n}\n"]}
@@ -0,0 +1,94 @@
1
+ import type { DiagramLayout, DiagramRelationshipChanges } from "../model/types.js";
2
+ export type DiagramSelection = {
3
+ kind: "class";
4
+ id: string;
5
+ } | {
6
+ kind: "relationship";
7
+ id: string;
8
+ } | {
9
+ kind: "note";
10
+ id: string;
11
+ } | {
12
+ kind: "relationship-waypoint";
13
+ relationshipId: string;
14
+ index: number;
15
+ };
16
+ export interface DiagramEventMap {
17
+ "selection-changed": {
18
+ type: "selection-changed";
19
+ selection: DiagramSelection | null;
20
+ };
21
+ "class-position-preview": {
22
+ type: "class-position-preview";
23
+ classId: string;
24
+ x: number;
25
+ y: number;
26
+ };
27
+ "class-position-changed": {
28
+ type: "class-position-changed";
29
+ classId: string;
30
+ x: number;
31
+ y: number;
32
+ };
33
+ "note-position-preview": {
34
+ type: "note-position-preview";
35
+ classId: string;
36
+ x: number;
37
+ y: number;
38
+ };
39
+ "note-position-changed": {
40
+ type: "note-position-changed";
41
+ classId: string;
42
+ x: number;
43
+ y: number;
44
+ };
45
+ "relationship-waypoint-added": {
46
+ type: "relationship-waypoint-added";
47
+ relationshipId: string;
48
+ index: number;
49
+ x: number;
50
+ y: number;
51
+ };
52
+ "relationship-waypoint-preview": {
53
+ type: "relationship-waypoint-preview";
54
+ relationshipId: string;
55
+ index: number;
56
+ x: number;
57
+ y: number;
58
+ };
59
+ "relationship-waypoint-changed": {
60
+ type: "relationship-waypoint-changed";
61
+ relationshipId: string;
62
+ index: number;
63
+ x: number;
64
+ y: number;
65
+ };
66
+ "relationship-waypoint-removed": {
67
+ type: "relationship-waypoint-removed";
68
+ relationshipId: string;
69
+ index: number;
70
+ };
71
+ "relationship-route-reset": {
72
+ type: "relationship-route-reset";
73
+ relationshipId: string;
74
+ };
75
+ "relationship-changed": {
76
+ type: "relationship-changed";
77
+ relationshipId: string;
78
+ changes: DiagramRelationshipChanges;
79
+ };
80
+ "auto-layout-completed": {
81
+ type: "auto-layout-completed";
82
+ layout: DiagramLayout;
83
+ };
84
+ "history-changed": {
85
+ type: "history-changed";
86
+ canUndo: boolean;
87
+ canRedo: boolean;
88
+ };
89
+ }
90
+ export type DiagramEventType = keyof DiagramEventMap;
91
+ export type DiagramEvent = DiagramEventMap[DiagramEventType];
92
+ export type DiagramEventListener<K extends DiagramEventType> = (event: DiagramEventMap[K]) => void;
93
+ export type Unsubscribe = () => void;
94
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/events/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/events/types.ts"],"names":[],"mappings":"","sourcesContent":["import type {\n DiagramLayout,\n DiagramRelationshipChanges,\n} from \"../model/types.js\";\n\nexport type DiagramSelection =\n | { kind: \"class\"; id: string }\n | { kind: \"relationship\"; id: string }\n | { kind: \"note\"; id: string }\n | {\n kind: \"relationship-waypoint\";\n relationshipId: string;\n index: number;\n };\n\nexport interface DiagramEventMap {\n \"selection-changed\": {\n type: \"selection-changed\";\n selection: DiagramSelection | null;\n };\n \"class-position-preview\": {\n type: \"class-position-preview\";\n classId: string;\n x: number;\n y: number;\n };\n \"class-position-changed\": {\n type: \"class-position-changed\";\n classId: string;\n x: number;\n y: number;\n };\n \"note-position-preview\": {\n type: \"note-position-preview\";\n classId: string;\n x: number;\n y: number;\n };\n \"note-position-changed\": {\n type: \"note-position-changed\";\n classId: string;\n x: number;\n y: number;\n };\n \"relationship-waypoint-added\": {\n type: \"relationship-waypoint-added\";\n relationshipId: string;\n index: number;\n x: number;\n y: number;\n };\n \"relationship-waypoint-preview\": {\n type: \"relationship-waypoint-preview\";\n relationshipId: string;\n index: number;\n x: number;\n y: number;\n };\n \"relationship-waypoint-changed\": {\n type: \"relationship-waypoint-changed\";\n relationshipId: string;\n index: number;\n x: number;\n y: number;\n };\n \"relationship-waypoint-removed\": {\n type: \"relationship-waypoint-removed\";\n relationshipId: string;\n index: number;\n };\n \"relationship-route-reset\": {\n type: \"relationship-route-reset\";\n relationshipId: string;\n };\n \"relationship-changed\": {\n type: \"relationship-changed\";\n relationshipId: string;\n changes: DiagramRelationshipChanges;\n };\n \"auto-layout-completed\": {\n type: \"auto-layout-completed\";\n layout: DiagramLayout;\n };\n \"history-changed\": {\n type: \"history-changed\";\n canUndo: boolean;\n canRedo: boolean;\n };\n}\n\nexport type DiagramEventType = keyof DiagramEventMap;\nexport type DiagramEvent = DiagramEventMap[DiagramEventType];\nexport type DiagramEventListener<K extends DiagramEventType> = (\n event: DiagramEventMap[K],\n) => void;\nexport type Unsubscribe = () => void;\n"]}
@@ -0,0 +1,8 @@
1
+ export { createDiagram, ModelDiagram } from "./rendering/diagram.js";
2
+ export type { CreateDiagramOptions, Diagram, } from "./rendering/diagram.js";
3
+ export type { AutoLayoutOptions, LayoutEngine, } from "./layout/elk-layout.js";
4
+ export { DiagramValidationError } from "./model/validation.js";
5
+ export { validateDiagramLayout, validateDiagramModel, } from "./model/validation.js";
6
+ export type { DiagramAttribute, DiagramClass, DiagramLayout, DiagramModel, DiagramRelationship, DiagramRelationshipChanges, Position, RelationshipLayout, RelationshipType, } from "./model/types.js";
7
+ export type { DiagramEvent, DiagramEventListener, DiagramEventMap, DiagramEventType, DiagramSelection, Unsubscribe, } from "./events/types.js";
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACrE,YAAY,EACV,oBAAoB,EACpB,OAAO,GACR,MAAM,wBAAwB,CAAC;AAChC,YAAY,EACV,iBAAiB,EACjB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAChC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EACV,gBAAgB,EAChB,YAAY,EACZ,aAAa,EACb,YAAY,EACZ,mBAAmB,EACnB,0BAA0B,EAC1B,QAAQ,EACR,kBAAkB,EAClB,gBAAgB,GACjB,MAAM,kBAAkB,CAAC;AAC1B,YAAY,EACV,YAAY,EACZ,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,WAAW,GACZ,MAAM,mBAAmB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,4 @@
1
+ export { createDiagram, ModelDiagram } from "./rendering/diagram.js";
2
+ export { DiagramValidationError } from "./model/validation.js";
3
+ export { validateDiagramLayout, validateDiagramModel, } from "./model/validation.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AASrE,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EACL,qBAAqB,EACrB,oBAAoB,GACrB,MAAM,uBAAuB,CAAC","sourcesContent":["export { createDiagram, ModelDiagram } from \"./rendering/diagram.js\";\nexport type {\n CreateDiagramOptions,\n Diagram,\n} from \"./rendering/diagram.js\";\nexport type {\n AutoLayoutOptions,\n LayoutEngine,\n} from \"./layout/elk-layout.js\";\nexport { DiagramValidationError } from \"./model/validation.js\";\nexport {\n validateDiagramLayout,\n validateDiagramModel,\n} from \"./model/validation.js\";\nexport type {\n DiagramAttribute,\n DiagramClass,\n DiagramLayout,\n DiagramModel,\n DiagramRelationship,\n DiagramRelationshipChanges,\n Position,\n RelationshipLayout,\n RelationshipType,\n} from \"./model/types.js\";\nexport type {\n DiagramEvent,\n DiagramEventListener,\n DiagramEventMap,\n DiagramEventType,\n DiagramSelection,\n Unsubscribe,\n} from \"./events/types.js\";\n"]}