@peter.prib/renderengine 0.15.1 → 0.15.2

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.
@@ -33564,6 +33564,67 @@
33564
33564
  }
33565
33565
  }
33566
33566
 
33567
+ /**
33568
+ * AddGroupToViewCommand — add a view-specific group to a view: its layout entry
33569
+ * is stored in view.folderLayout. Undo removes it.
33570
+ */
33571
+ class AddGroupToViewCommand extends Command {
33572
+ /**
33573
+ * @param {ArchimateView} view
33574
+ * @param {Object} [options]
33575
+ * @param {string} [options.id]
33576
+ * @param {string} [options.name]
33577
+ * @param {number} [options.x]
33578
+ * @param {number} [options.y]
33579
+ * @param {number} [options.width]
33580
+ * @param {number} [options.height]
33581
+ * @param {number} [options.zIndex]
33582
+ * @param {Object} [options.appearance]
33583
+ * @param {string} [options.parentId]
33584
+ */
33585
+ constructor(view, { id = `group-${Date.now().toString(36)}`, name = 'Group', x = 100, y = 100, width = 400, height = 140, zIndex = 0, appearance = {}, parentId = undefined } = {}) {
33586
+ super('Add group to view');
33587
+ this.view = view;
33588
+ this.groupId = id;
33589
+ this.entry = { x, y, width, height, name, zIndex, appearance, parentId };
33590
+ }
33591
+
33592
+ execute() {
33593
+ this.view.folderLayout[this.groupId] = { ...this.entry };
33594
+ }
33595
+
33596
+ undo() {
33597
+ delete this.view.folderLayout[this.groupId];
33598
+ }
33599
+ }
33600
+
33601
+ /**
33602
+ * RemoveGroupFromViewCommand — take a group off a view: its layout entry
33603
+ * goes, and what was drawn in it stays where it is (on the view, or in the
33604
+ * group around it). The model folder stays. Undo puts the same entry back.
33605
+ */
33606
+ class RemoveGroupFromViewCommand extends Command {
33607
+ /**
33608
+ * @param {ArchimateView} view
33609
+ * @param {string} folderId - The folder the group draws
33610
+ */
33611
+ constructor(view, folderId) {
33612
+ super('Remove group from view');
33613
+ this.view = view;
33614
+ this.folderId = folderId;
33615
+ this._entry = null;
33616
+ }
33617
+
33618
+ execute() {
33619
+ this._entry = this.view.folderLayout[this.folderId] ?? null;
33620
+ delete this.view.folderLayout[this.folderId];
33621
+ }
33622
+
33623
+ undo() {
33624
+ if (this._entry) this.view.folderLayout[this.folderId] = this._entry;
33625
+ }
33626
+ }
33627
+
33567
33628
  /**
33568
33629
  * Canonical, id-indexed home for ArchiMate model data (elements, relationships, views).
33569
33630
  * Replaces ad-hoc flat-array handling and fixes multi-view import flattening.
@@ -34717,27 +34778,65 @@
34717
34778
  }
34718
34779
 
34719
34780
  /**
34720
- * The folders drawn on this view as groups: those with bounds in its
34721
- * `folderLayout`, bounds applied, each after its parent. `parentId` is the
34722
- * nearest ancestor that is also drawn here (a group inside an undrawn
34723
- * folder sits on the view itself).
34781
+ * The folders drawn on this view as groups: both model repository folders
34782
+ * and view-specific groups with bounds in `folderLayout`, bounds applied,
34783
+ * each after its parent. `parentId` is the nearest ancestor also drawn here.
34724
34784
  */
34725
34785
  get placedFolders() {
34726
- const placed = this.model.allFolders().filter(f => this.folderLayout[f.id]);
34727
- const drawn = new Set(placed.map(f => f.id));
34728
- return placed.map(folder => ({
34729
- ...folder,
34730
- ...this.folderLayout[folder.id],
34731
- parentId: this._nearestPlaced(folder.parentId, drawn),
34732
- }));
34786
+ const drawn = new Set(Object.keys(this.folderLayout));
34787
+ const modelFolders = this.model.allFolders();
34788
+ const modelFolderIds = new Set(modelFolders.map(f => f.id));
34789
+
34790
+ const result = [];
34791
+ for (const folder of modelFolders.filter(f => this.folderLayout[f.id])) {
34792
+ result.push({
34793
+ ...folder,
34794
+ ...this.folderLayout[folder.id],
34795
+ name: this.folderLayout[folder.id].name ?? folder.name,
34796
+ parentId: this._nearestPlaced(folder.parentId, drawn),
34797
+ });
34798
+ }
34799
+ for (const [id, layout] of Object.entries(this.folderLayout)) {
34800
+ if (!modelFolderIds.has(id)) {
34801
+ result.push({
34802
+ id,
34803
+ name: layout.name || 'Group',
34804
+ ...layout,
34805
+ parentId: layout.parentId && drawn.has(layout.parentId) ? layout.parentId : undefined,
34806
+ });
34807
+ }
34808
+ }
34809
+ return result;
34733
34810
  }
34734
34811
 
34735
- /** The group on this view an element is drawn in: its folder, or that folder's nearest drawn ancestor. */
34812
+ /** The group on this view an element is drawn in: its explicit view parent group, or its model folder's nearest drawn ancestor. */
34736
34813
  placedFolderOf(elementId) {
34737
- const drawn = new Set(this.model.allFolders().filter(f => this.folderLayout[f.id]).map(f => f.id));
34814
+ const inst = this.elementInstancesFor(elementId)[0];
34815
+ if (inst?.archi?.parent && this.folderLayout[inst.archi.parent]) {
34816
+ return inst.archi.parent;
34817
+ }
34818
+ if (inst?.parentId && this.folderLayout[inst.parentId]) {
34819
+ return inst.parentId;
34820
+ }
34821
+ const drawn = new Set(Object.keys(this.folderLayout));
34738
34822
  return this._nearestPlaced(this.model.getElementFolder(elementId), drawn);
34739
34823
  }
34740
34824
 
34825
+ /** Add a view-specific group to this view. One undo step (AddGroupToViewCommand). */
34826
+ addGroup(spec = {}) {
34827
+ const id = spec.id || `group-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 6)}`;
34828
+ const cmd = new AddGroupToViewCommand(this, { id, ...spec });
34829
+ this.history.execute(cmd);
34830
+ return this.folderLayout[id];
34831
+ }
34832
+
34833
+ /** Remove a group (view-specific or placed model folder) from this view. One undo step (RemoveGroupFromViewCommand). */
34834
+ removeGroup(groupId) {
34835
+ if (!this.folderLayout[groupId]) return false;
34836
+ this.history.execute(new RemoveGroupFromViewCommand(this, groupId));
34837
+ return true;
34838
+ }
34839
+
34741
34840
  /** @private */
34742
34841
  _nearestPlaced(folderId, drawn) {
34743
34842
  for (let id = folderId; id; id = this.model.getFolder(id)?.parentId) {
@@ -35871,6 +35970,28 @@
35871
35970
  clearWaypoints() {
35872
35971
  this.waypoints = [];
35873
35972
  }
35973
+
35974
+ /** The edge as plain JSON: its endpoints, label, waypoints, side pins and style overrides. */
35975
+ toJSON() {
35976
+ return {
35977
+ id: this.id,
35978
+ sourceId: this.sourceId,
35979
+ targetId: this.targetId,
35980
+ label: this.label,
35981
+ waypoints: this.waypoints.map(w => ({ x: w.x, y: w.y })),
35982
+ sourceSide: this.sourceSide,
35983
+ targetSide: this.targetSide,
35984
+ visible: this.visible,
35985
+ ...this.styleOverrides(),
35986
+ };
35987
+ }
35988
+
35989
+ /** Reconstruct an Edge from plain JSON. */
35990
+ static fromJSON(json) {
35991
+ if (!json) throw new TypeError('Edge.fromJSON requires a JSON object');
35992
+ const { id, sourceId, targetId, ...rest } = json;
35993
+ return new Edge(id, sourceId, targetId, rest);
35994
+ }
35874
35995
  }
35875
35996
 
35876
35997
  /** Style keys the line's Archi type bits decide (dashes, arrows). */
@@ -44232,7 +44353,8 @@
44232
44353
  execute() {
44233
44354
  const { instance, parent } = this;
44234
44355
  const { parent: _was, ...rest } = instance.archi ?? {};
44235
- instance.archi = parent ? { ...rest, parent: parent.visualId } : (Object.keys(rest).length ? rest : null);
44356
+ const parentId = parent ? (parent.visualId ?? parent.id) : null;
44357
+ instance.archi = parentId ? { ...rest, parent: parentId } : (Object.keys(rest).length ? rest : null);
44236
44358
  if (parent && (instance.zIndex ?? 0) <= (parent.zIndex ?? 0)) instance.zIndex = (parent.zIndex ?? 0) + 1;
44237
44359
  }
44238
44360
 
@@ -44357,8 +44479,11 @@
44357
44479
  return this.view.model.getFolder(this.id);
44358
44480
  }
44359
44481
 
44360
- get name() { return this.folder?.name ?? 'Group'; }
44361
- set name(value) { if (this.folder) this.folder.name = value || 'Group'; }
44482
+ get name() { return this.folder?.name ?? this.layout?.name ?? 'Group'; }
44483
+ set name(value) {
44484
+ if (this.folder) this.folder.name = value || 'Group';
44485
+ if (this.layout) this.layout.name = value || 'Group';
44486
+ }
44362
44487
 
44363
44488
  // The layout keeps the top-left corner; a template is placed by its centre.
44364
44489
  get x() { return this.layout.x + this.width / 2; }
@@ -44446,33 +44571,6 @@
44446
44571
  }
44447
44572
  }
44448
44573
 
44449
- /**
44450
- * RemoveGroupFromViewCommand — take a group off a view: its layout entry
44451
- * goes, and what was drawn in it stays where it is (on the view, or in the
44452
- * group around it). The model folder stays. Undo puts the same entry back.
44453
- */
44454
- class RemoveGroupFromViewCommand extends Command {
44455
- /**
44456
- * @param {ArchimateView} view
44457
- * @param {string} folderId - The folder the group draws
44458
- */
44459
- constructor(view, folderId) {
44460
- super('Remove group from view');
44461
- this.view = view;
44462
- this.folderId = folderId;
44463
- this._entry = null;
44464
- }
44465
-
44466
- execute() {
44467
- this._entry = this.view.folderLayout[this.folderId] ?? null;
44468
- delete this.view.folderLayout[this.folderId];
44469
- }
44470
-
44471
- undo() {
44472
- if (this._entry) this.view.folderLayout[this.folderId] = this._entry;
44473
- }
44474
- }
44475
-
44476
44574
  /**
44477
44575
  * ElementFilterRule — a filter's result on an ArchiMate view: shapes of the
44478
44576
  * model elements it kept are drawn as usual, every other element dimmed.
@@ -46085,15 +46183,28 @@
46085
46183
  return element ? treeLayerOf(element) : 'other';
46086
46184
  }
46087
46185
 
46088
- /** An element's box holds any box but a group; on a canvas, a block holds boxes. */
46186
+ /** An element's box holds any box; a group holds any box or nested group; on a canvas, a block holds boxes. */
46089
46187
  canHold(parentId, childId) {
46188
+ if (!parentId || !childId || parentId === childId) return false;
46090
46189
  const parent = this.templates.get(parentId), child = this.templates.get(childId);
46091
- if (!parent?.instance || !child?.instance || parent instanceof ViewGroupTemplate || child instanceof ViewGroupTemplate) return false;
46190
+ if (!parent || !child) return false;
46191
+ if (child instanceof ViewGroupTemplate && parent instanceof ViewGroupTemplate) {
46192
+ return !this.isAncestor(childId, parentId);
46193
+ }
46194
+ if (parent instanceof ViewGroupTemplate) {
46195
+ return !this.isAncestor(childId, parentId);
46196
+ }
46197
+ if (child instanceof ViewGroupTemplate) return false;
46198
+ if (!parent.instance || !child.instance) return false;
46092
46199
  return !!this._elementOf(parentId) || parent.instance.content?.archiVisualType === 'canvas-model-block';
46093
46200
  }
46094
46201
 
46095
- /** An element dropped on another nests there when a relationship a nesting stands for (NESTING_KINDS) is allowed between them. */
46202
+ /** An element dropped on another nests there when a relationship a nesting stands for (NESTING_KINDS) is allowed between them, or when dropped into a group. */
46096
46203
  nestsOnDrop(parentId, childId) {
46204
+ const parent = this.templates.get(parentId);
46205
+ if (parent instanceof ViewGroupTemplate) {
46206
+ return this.canHold(parentId, childId);
46207
+ }
46097
46208
  if (!this._elementOf(parentId) || !this._elementOf(childId)) return false;
46098
46209
  return this.allowedKinds(parentId, childId).some(kind => NESTING_KINDS.includes(kind));
46099
46210
  }
@@ -46101,13 +46212,16 @@
46101
46212
  nestCommand(childId, parentId) {
46102
46213
  const child = this.templates.get(childId)?.instance;
46103
46214
  if (!child) return null;
46104
- return new NestInstanceCommand(child, parentId ? this.templates.get(parentId)?.instance ?? null : null);
46215
+ const parentTemplate = parentId ? this.templates.get(parentId) : null;
46216
+ const parentTarget = parentTemplate?.instance ?? (parentTemplate instanceof ViewGroupTemplate ? parentTemplate : null);
46217
+ return new NestInstanceCommand(child, parentTarget);
46105
46218
  }
46106
46219
 
46107
- /** After nesting an element in another: offer the relationship the nesting stands for (nestingKinds). */
46220
+ /** After nesting an element in another: offer the relationship the nesting stands for (nestingKinds). Groups have no relationship. */
46108
46221
  nested(changes) {
46109
46222
  let chain = Promise.resolve();
46110
46223
  for (const { childId, parentId } of changes) {
46224
+ if (this.templates.get(parentId) instanceof ViewGroupTemplate) continue;
46111
46225
  chain = chain.then(() => this.offerNestingRelationship(parentId, childId)); // taken out: nothing to offer
46112
46226
  }
46113
46227
  return chain;
@@ -49746,64 +49860,159 @@
49746
49860
  new DrawioFileFormat(),
49747
49861
  ]);
49748
49862
 
49749
- /**
49750
- * DiagramView — one diagram (tab) of a DiagramModel: its shapes, lines,
49751
- * diagram-level line style and its own undo history (from DiagramEditorState).
49752
- *
49753
- * A DiagramEditor is bound directly to `templates`/`edges`, so edits land here
49754
- * with nothing to save back, and the history survives the editor being rebuilt.
49755
- */
49756
- class DiagramView extends DiagramEditorState {
49757
- /**
49758
- * @param {Object} options
49759
- * @param {string} options.id
49760
- * @param {string} options.title
49761
- * @param {Map<string, Template>} [options.templates]
49762
- * @param {Map<string, Edge>} [options.edges]
49763
- * @param {Object} [options.edgeStyle] - Diagram-level line style defaults
49764
- * @param {Object} [options.historyOptions] - Passed to CommandHistory (e.g. onChange)
49765
- * @param {string} [options.documentation]
49766
- */
49767
- constructor({ id, title, templates = new Map(), edges = new Map(), edgeStyle = {}, historyOptions = {}, documentation = undefined, arrangement = null }) {
49768
- super({ historyOptions, arrangement });
49769
- this.historyOptions = historyOptions;
49770
- this.id = id;
49771
- this.title = title;
49772
- this.documentation = documentation;
49773
- this.templates = templates;
49774
- this.edges = edges;
49775
- this.edgeStyle = edgeStyle;
49776
- }
49777
-
49778
- /** The shapes are what stack. */
49779
- layerItems() {
49780
- return [...this.templates.values()];
49781
- }
49782
-
49783
- /** The diagram as plain JSON: its shapes (by id), lines, line style and documentation. */
49784
- toJSON() {
49785
- return {
49786
- title: this.title,
49787
- documentation: this.documentation,
49788
- templates: [...this.templates].map(([id, t]) => ({ id, type: t.constructor.name, name: t.name, data: t.serialize() })),
49789
- edges: [...this.edges.values()].map(e => JSON.parse(JSON.stringify(e))),
49790
- edgeStyle: JSON.parse(JSON.stringify(this.edgeStyle ?? {})),
49791
- };
49792
- }
49793
-
49794
- /** An independent copy: shapes, lines, style and documentation are copied; history starts empty (same options). */
49795
- copy(id, title, historyOptions = this.historyOptions) {
49796
- return new DiagramView({
49797
- id,
49798
- title,
49799
- templates: new Map([...this.templates].map(([tid, t]) => [tid, new t.constructor(tid, t.name, t.serialize())])),
49800
- edges: new Map([...this.edges].map(([eid, e]) => [eid, e.clone()])),
49801
- edgeStyle: { ...this.edgeStyle },
49802
- documentation: this.documentation,
49803
- historyOptions,
49804
- arrangement: this.arrangement.copy(), // template ids are kept
49805
- });
49806
- }
49863
+ const TEMPLATE_CLASSES = Object.freeze({
49864
+ 'rectangle': RectangleTemplate,
49865
+ 'rounded-rectangle': RoundedRectangleTemplate,
49866
+ 'stadium': StadiumTemplate,
49867
+ 'predefined-process': PredefinedProcessTemplate,
49868
+ 'class': ClassTemplate,
49869
+ 'component': ComponentTemplate,
49870
+ 'swimlane': SwimlaneTemplate,
49871
+ 'circle': CircleTemplate,
49872
+ 'final-state': FinalStateTemplate,
49873
+ 'ellipse': EllipseTemplate,
49874
+ 'diamond': DiamondTemplate,
49875
+ 'triangle': TriangleTemplate,
49876
+ 'pentagon': PentagonTemplate,
49877
+ 'hexagon': HexagonTemplate,
49878
+ 'octagon': OctagonTemplate,
49879
+ 'star': StarTemplate,
49880
+ 'parallelogram': ParallelogramTemplate,
49881
+ 'trapezoid': TrapezoidTemplate,
49882
+ 'manual-input': ManualInputTemplate,
49883
+ 'cylinder': CylinderTemplate,
49884
+ 'document': DocumentTemplate,
49885
+ 'note': NoteTemplate,
49886
+ 'cloud': CloudTemplate,
49887
+ 'actor': ActorTemplate,
49888
+ 'package': PackageTemplate,
49889
+ 'box': BoxTemplate,
49890
+ 'data-store': DataStoreTemplate,
49891
+ 'lifeline': LifelineTemplate,
49892
+ });
49893
+
49894
+ const CLASS_TO_KEY = new Map(Object.entries(TEMPLATE_CLASSES).map(([k, c]) => [c, k]));
49895
+
49896
+ // Assign static templateType to each class
49897
+ for (const [type, cls] of Object.entries(TEMPLATE_CLASSES)) {
49898
+ cls.templateType = type;
49899
+ }
49900
+
49901
+ /** Returns the stable serialized type key for a template instance or template class. */
49902
+ function templateTypeKey(templateOrClass) {
49903
+ if (!templateOrClass) return 'rectangle';
49904
+ const c = typeof templateOrClass === 'function' ? templateOrClass : templateOrClass.constructor;
49905
+ if (CLASS_TO_KEY.has(c)) return CLASS_TO_KEY.get(c);
49906
+ if (Object.hasOwn(c, 'templateType')) return c.templateType;
49907
+ const name = c.name || '';
49908
+ const withoutSuffix = name.replace(/Template$/, '');
49909
+ const kebab = withoutSuffix.replace(/([a-z0-9])([A-Z])/g, '$1-$2').toLowerCase();
49910
+ return TEMPLATE_CLASSES[kebab] ? kebab : 'rectangle';
49911
+ }
49912
+
49913
+ /** Resolves a template class by its stable type key or class name, defaulting to RectangleTemplate. */
49914
+ function templateClassFor(key) {
49915
+ if (!key) return RectangleTemplate;
49916
+ if (TEMPLATE_CLASSES[key]) return TEMPLATE_CLASSES[key];
49917
+ const lower = String(key).toLowerCase();
49918
+ if (TEMPLATE_CLASSES[lower]) return TEMPLATE_CLASSES[lower];
49919
+ for (const [k, c] of Object.entries(TEMPLATE_CLASSES)) {
49920
+ if (c.name === key || c.name?.toLowerCase() === lower) return c;
49921
+ }
49922
+ return RectangleTemplate;
49923
+ }
49924
+
49925
+ /**
49926
+ * DiagramView — one diagram (tab) of a DiagramModel: its shapes, lines,
49927
+ * diagram-level line style and its own undo history (from DiagramEditorState).
49928
+ *
49929
+ * A DiagramEditor is bound directly to `templates`/`edges`, so edits land here
49930
+ * with nothing to save back, and the history survives the editor being rebuilt.
49931
+ */
49932
+ class DiagramView extends DiagramEditorState {
49933
+ /**
49934
+ * @param {Object} options
49935
+ * @param {string} options.id
49936
+ * @param {string} options.title
49937
+ * @param {Map<string, Template>} [options.templates]
49938
+ * @param {Map<string, Edge>} [options.edges]
49939
+ * @param {Object} [options.edgeStyle] - Diagram-level line style defaults
49940
+ * @param {Object} [options.historyOptions] - Passed to CommandHistory (e.g. onChange)
49941
+ * @param {string} [options.documentation]
49942
+ * @param {Arrangement} [options.arrangement]
49943
+ */
49944
+ constructor({ id, title, templates = new Map(), edges = new Map(), edgeStyle = {}, historyOptions = {}, documentation = undefined, arrangement = null }) {
49945
+ super({ historyOptions, arrangement });
49946
+ this.historyOptions = historyOptions;
49947
+ this.id = id;
49948
+ this.title = title;
49949
+ this.documentation = documentation;
49950
+ this.templates = templates;
49951
+ this.edges = edges;
49952
+ this.edgeStyle = edgeStyle;
49953
+ }
49954
+
49955
+ /** The shapes are what stack. */
49956
+ layerItems() {
49957
+ return [...this.templates.values()];
49958
+ }
49959
+
49960
+ /** The diagram as plain JSON: its shapes (by id and stable type), lines, line style and documentation. */
49961
+ toJSON() {
49962
+ return {
49963
+ id: this.id,
49964
+ title: this.title,
49965
+ documentation: this.documentation,
49966
+ templates: [...this.templates].map(([id, t]) => ({
49967
+ id,
49968
+ type: templateTypeKey(t),
49969
+ name: t.name,
49970
+ data: t.serialize(),
49971
+ })),
49972
+ edges: [...this.edges.values()].map(e => (typeof e.toJSON === 'function' ? e.toJSON() : JSON.parse(JSON.stringify(e)))),
49973
+ edgeStyle: JSON.parse(JSON.stringify(this.edgeStyle ?? {})),
49974
+ ...(this.arrangement && { arrangement: this.arrangement.toJSON() }),
49975
+ };
49976
+ }
49977
+
49978
+ /** Reconstruct a DiagramView from plain JSON. */
49979
+ static fromJSON(json, historyOptions = {}) {
49980
+ if (!json) throw new TypeError('DiagramView.fromJSON requires a JSON object');
49981
+ const templates = new Map();
49982
+ for (const t of (json.templates ?? [])) {
49983
+ const Class = templateClassFor(t.type);
49984
+ templates.set(t.id, new Class(t.id, t.name, t.data ?? {}));
49985
+ }
49986
+ const edges = new Map();
49987
+ for (const e of (json.edges ?? [])) {
49988
+ edges.set(e.id, Edge.fromJSON(e));
49989
+ }
49990
+ const arrangement = json.arrangement ? Arrangement.fromJSON(json.arrangement) : null;
49991
+ return new DiagramView({
49992
+ id: json.id,
49993
+ title: json.title,
49994
+ templates,
49995
+ edges,
49996
+ edgeStyle: json.edgeStyle ?? {},
49997
+ documentation: json.documentation,
49998
+ historyOptions,
49999
+ arrangement,
50000
+ });
50001
+ }
50002
+
50003
+ /** An independent copy: shapes, lines, style and documentation are copied; history starts empty (same options). */
50004
+ copy(id, title, historyOptions = this.historyOptions) {
50005
+ return new DiagramView({
50006
+ id,
50007
+ title,
50008
+ templates: new Map([...this.templates].map(([tid, t]) => [tid, new t.constructor(tid, t.name, t.serialize())])),
50009
+ edges: new Map([...this.edges].map(([eid, e]) => [eid, e.clone()])),
50010
+ edgeStyle: { ...this.edgeStyle },
50011
+ documentation: this.documentation,
50012
+ historyOptions,
50013
+ arrangement: this.arrangement.copy(), // template ids are kept
50014
+ });
50015
+ }
49807
50016
  }
49808
50017
 
49809
50018
  /**
@@ -49861,6 +50070,75 @@
49861
50070
  setTemplateType(templateType) {
49862
50071
  this.templateType = templateType;
49863
50072
  }
50073
+
50074
+ /** The diagram model as plain JSON: its name, templateType, line style defaults, active view and views. */
50075
+ toJSON() {
50076
+ return {
50077
+ version: 1,
50078
+ name: this.name,
50079
+ templateType: this.templateType,
50080
+ edgeStyle: JSON.parse(JSON.stringify(this.edgeStyle ?? {})),
50081
+ currentViewId: this.currentViewId,
50082
+ views: this.views().map(v => v.toJSON()),
50083
+ };
50084
+ }
50085
+
50086
+ /** Reconstruct a DiagramModel from plain JSON. */
50087
+ static fromJSON(json, historyOptions = {}) {
50088
+ if (!json) throw new TypeError('DiagramModel.fromJSON requires a JSON object');
50089
+ const model = new DiagramModel({
50090
+ name: json.name ?? '',
50091
+ templateType: json.templateType ?? 'generic',
50092
+ edgeStyle: json.edgeStyle ?? {},
50093
+ historyOptions,
50094
+ });
50095
+ const views = (json.views ?? []).map(vData => DiagramView.fromJSON(vData, model.historyOptions));
50096
+ for (const view of views) {
50097
+ model._insertView(model._adopt(view), { current: view.id === json.currentViewId });
50098
+ }
50099
+ if (json.currentViewId && model._views.has(json.currentViewId)) {
50100
+ model.currentViewId = json.currentViewId;
50101
+ } else if (model._views.size > 0 && !model.currentViewId) {
50102
+ model.currentViewId = model._views.keys().next().value;
50103
+ }
50104
+ const maxNum = Math.max(0, ...model.views().map(v => {
50105
+ const m = String(v.id).match(/\d+$/);
50106
+ return m ? parseInt(m[0], 10) : 0;
50107
+ }));
50108
+ model._nextId = maxNum + 1;
50109
+ model.markSaved();
50110
+ return model;
50111
+ }
50112
+ }
50113
+
50114
+ /**
50115
+ * DiagramJsonFileFormat — a diagram model as a file (`.diagram.json` or `.json`):
50116
+ * its name, palette category, line style defaults, views with templates, edges,
50117
+ * waypoints and arrangements (DiagramModel#toJSON), read back by DiagramModel.fromJSON.
50118
+ */
50119
+ class DiagramJsonFileFormat extends ModelFileFormat {
50120
+ get label() { return '.diagram.json'; }
50121
+ get accept() { return '.diagram.json,.json'; }
50122
+ get fileName() { return 'diagrams.diagram.json'; }
50123
+ get featureName() { return 'DiagramJson'; }
50124
+
50125
+ /** JSON with a diagram model's shape: templateType and views array, or views array of diagrams. */
50126
+ recognizes(text) {
50127
+ try {
50128
+ const json = JSON.parse(text);
50129
+ return !!json && typeof json === 'object' && (Array.isArray(json.views) || typeof json.templateType === 'string');
50130
+ } catch {
50131
+ return false;
50132
+ }
50133
+ }
50134
+
50135
+ export(model) {
50136
+ return { data: JSON.stringify(model.toJSON(), null, 2), mimeType: 'application/json' };
50137
+ }
50138
+
50139
+ import(text) {
50140
+ return DiagramModel.fromJSON(JSON.parse(text));
50141
+ }
49864
50142
  }
49865
50143
 
49866
50144
  /**
@@ -51233,7 +51511,8 @@
51233
51511
  }
51234
51512
 
51235
51513
  /**
51236
- * ArchimateDiagramEditor — ArchiMate-specific diagram editor extending DiagramEditor base.
51514
+ * ArchimateDiagramEditor — legacy GraphDiagram-based ArchiMate editor extending DiagramEditor.
51515
+ * @deprecated Use ArchimateCanvasEditor (or ArchimateCanvasPart) for ArchiMate editing. RenderEngine.archimateDiagram() remains for read-only charts.
51237
51516
  *
51238
51517
  * Encapsulates: ArchiMate model, tree navigator, diagram tabs, rendering, controls wiring.
51239
51518
  * Inherits: common rendering, drag/drop, toolbar, tree, filtering from DiagramEditor.
@@ -65434,6 +65713,7 @@ ${cells.map(c => ` <Cell${isHeader ? ' ss:StyleID="Header"' : ''}><Data ss:T
65434
65713
  exports.DerivedRelationships = DerivedRelationships;
65435
65714
  exports.DiagramEditor = DiagramEditor;
65436
65715
  exports.DiagramEditorState = DiagramEditorState;
65716
+ exports.DiagramJsonFileFormat = DiagramJsonFileFormat;
65437
65717
  exports.DiagramModel = DiagramModel;
65438
65718
  exports.DiagramTabs = DiagramTabs;
65439
65719
  exports.DiagramView = DiagramView;
@@ -65610,6 +65890,7 @@ ${cells.map(c => ` <Cell${isHeader ? ' ss:StyleID="Header"' : ''}><Data ss:T
65610
65890
  exports.StructuredType = StructuredType;
65611
65891
  exports.SurfaceChart3D = SurfaceChart3D;
65612
65892
  exports.SwimlaneTemplate = SwimlaneTemplate;
65893
+ exports.TEMPLATE_CLASSES = TEMPLATE_CLASSES;
65613
65894
  exports.TRACKERS = TRACKERS;
65614
65895
  exports.TRACKER_APIS = TRACKER_APIS;
65615
65896
  exports.TREE_LAYERS = TREE_LAYERS;
@@ -65740,6 +66021,8 @@ ${cells.map(c => ` <Cell${isHeader ? ' ss:StyleID="Header"' : ''}><Data ss:T
65740
66021
  exports.stackingOrder = stackingOrder;
65741
66022
  exports.strongerEmphasis = strongerEmphasis;
65742
66023
  exports.svgMarkupToPngBlob = svgMarkupToPngBlob;
66024
+ exports.templateClassFor = templateClassFor;
66025
+ exports.templateTypeKey = templateTypeKey;
65743
66026
  exports.toArchiExport = toArchiExport;
65744
66027
  exports.toArchiFile = toArchiFile;
65745
66028
  exports.toArchimateModel = toArchimateModel;