sysml-diagram 0.21.1 → 0.23.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/out/main.js CHANGED
@@ -161289,6 +161289,10 @@ function applyTransform(m, p) {
161289
161289
  m.r[6] * p[0] + m.r[7] * p[1] + m.r[8] * p[2] + m.t[2]
161290
161290
  ];
161291
161291
  }
161292
+ function isUnrotated(m) {
161293
+ const id2 = [1, 0, 0, 0, 1, 0, 0, 0, 1];
161294
+ return m.r.every((v, i) => Math.abs(v - id2[i]) < 1e-6);
161295
+ }
161292
161296
  function yawOf(m) {
161293
161297
  if (Math.abs(m.r[2]) > 1e-6 || Math.abs(m.r[5]) > 1e-6)
161294
161298
  return void 0;
@@ -162227,6 +162231,91 @@ var FeaturePathResolver = class {
162227
162231
  }
162228
162232
  };
162229
162233
 
162234
+ // ../language-server/out/src/platform/platform.js
162235
+ var current;
162236
+ function setPlatform(platform) {
162237
+ current = platform;
162238
+ }
162239
+ function getPlatform() {
162240
+ if (!current)
162241
+ throw new Error("SysML: no platform installed - the entry point must call setPlatform() first.");
162242
+ return current;
162243
+ }
162244
+ function hasPlatform() {
162245
+ return current !== void 0;
162246
+ }
162247
+
162248
+ // ../language-server/out/src/services/library-index-manager.js
162249
+ var SysmlIndexManager = class extends DefaultIndexManager {
162250
+ constructor(services) {
162251
+ super(services);
162252
+ }
162253
+ // REQ-075, REQ-383 — `libraryRoot` is a URI and the concrete file URIs come
162254
+ // from the platform, because the two hosts index the same library under
162255
+ // different schemes (`file:` on the desktop, `sysml-lib:` in a worker).
162256
+ loadPrecomputedLibraryIndex(index2, libraryRoot) {
162257
+ const platform = getPlatform();
162258
+ let symbolCount = 0;
162259
+ for (const file of index2.files) {
162260
+ const documentUri = platform.libraryUri(libraryRoot, file.path);
162261
+ const descriptions = file.symbols.map((symbol) => this.deserializeSymbol(symbol, documentUri));
162262
+ const uri = documentUri.toString();
162263
+ this.symbolIndex.set(uri, descriptions);
162264
+ this.symbolByTypeIndex.clear(uri);
162265
+ symbolCount += descriptions.length;
162266
+ }
162267
+ return symbolCount;
162268
+ }
162269
+ deserializeSymbol(symbol, documentUri) {
162270
+ return {
162271
+ name: symbol.name,
162272
+ type: symbol.type,
162273
+ path: symbol.path,
162274
+ documentUri,
162275
+ nameSegment: symbol.nameSegment,
162276
+ selectionSegment: symbol.selectionSegment,
162277
+ // REQ-068 — preserve declared visibility for wildcard re-export.
162278
+ ...symbol.isPrivate ? { isPrivate: true } : {},
162279
+ ...symbol.visibility ? { visibility: symbol.visibility } : {},
162280
+ // issue #152 — a re-exported alias is not owned nesting.
162281
+ ...symbol.derivedKind ? { derivedKind: symbol.derivedKind } : {},
162282
+ // REQ-242 — issue #103 — keeps type completion to definitions.
162283
+ ...symbol.isUsage ? { isUsage: true } : {}
162284
+ };
162285
+ }
162286
+ };
162287
+ function isSysmlIndexManager(value) {
162288
+ return typeof value.loadPrecomputedLibraryIndex === "function";
162289
+ }
162290
+ var libraryRoots = /* @__PURE__ */ new Set();
162291
+ var ROOT_SEPARATOR = "\0";
162292
+ function normalizeLibraryPath(p) {
162293
+ return p.replace(/\\/gu, "/").replace(/\/+$/u, "").toLowerCase();
162294
+ }
162295
+ function registerLibraryRoot(root4) {
162296
+ const uri = typeof root4 === "string" ? root4.length > 0 ? URI2.file(root4) : void 0 : root4;
162297
+ if (!uri)
162298
+ return;
162299
+ libraryRoots.add(`${uri.scheme}${ROOT_SEPARATOR}${normalizeLibraryPath(uri.path)}`);
162300
+ }
162301
+ function isInsideDir(fsPath, dir) {
162302
+ return fsPath === dir || fsPath.startsWith(`${dir}/`);
162303
+ }
162304
+ function isStandardLibraryUri(uri) {
162305
+ const fsPath = normalizeLibraryPath(uri.path);
162306
+ for (const entry of libraryRoots) {
162307
+ const separator = entry.indexOf(ROOT_SEPARATOR);
162308
+ if (entry.slice(0, separator) !== uri.scheme)
162309
+ continue;
162310
+ if (isInsideDir(fsPath, entry.slice(separator + 1)))
162311
+ return true;
162312
+ }
162313
+ return fsPath.split("/").some((segment) => segment === "sysml.library");
162314
+ }
162315
+ function isLibraryDocument(doc) {
162316
+ return doc.isLibraryDocument === true || isStandardLibraryUri(doc.uri);
162317
+ }
162318
+
162230
162319
  // ../language-server/out/src/services/namespace-kind.js
162231
162320
  var KERML_CLASSIFIER_DECL_TYPES = /* @__PURE__ */ new Set([
162232
162321
  "AssociationDecl",
@@ -162363,10 +162452,7 @@ function boundText(b) {
162363
162452
  return String(b.intVal);
162364
162453
  return b.bound;
162365
162454
  }
162366
- function multText(node) {
162367
- if (!node)
162368
- return void 0;
162369
- const m = node.multiplicity;
162455
+ function multiplicityText(m) {
162370
162456
  if (!m)
162371
162457
  return void 0;
162372
162458
  const lo = boundText(m.lower);
@@ -162375,6 +162461,88 @@ function multText(node) {
162375
162461
  return void 0;
162376
162462
  return hi == null || hi === lo ? `[${lo}]` : `[${lo}..${hi}]`;
162377
162463
  }
162464
+ function connectionEndMultiplicity(node) {
162465
+ if (!node)
162466
+ return void 0;
162467
+ const n2 = node;
162468
+ const multiplicity = n2.innerMultiplicity ?? n2.multiplicity ?? n2.endMultiplicity;
162469
+ if (!multiplicity)
162470
+ return void 0;
162471
+ const lo = boundText(multiplicity.lower);
162472
+ const hi = boundText(multiplicity.upper);
162473
+ if (lo == null && hi == null)
162474
+ return void 0;
162475
+ return hi == null || hi === lo ? `[${lo}]` : `[${lo}..${hi}]`;
162476
+ }
162477
+ var END_PROPERTY_ADORNMENTS = /* @__PURE__ */ new Set(["abstract", "derived", "readonly", "ordered", "nonunique"]);
162478
+ var uniqueStrings = (values2) => [...new Set([...values2].filter((value) => !!value))];
162479
+ function formatConnectionEndAdornment(notation) {
162480
+ const adornment = [
162481
+ notation.direction,
162482
+ ...notation.properties ?? [],
162483
+ notation.subsets?.length ? `subsets ${notation.subsets.join(", ")}` : void 0,
162484
+ notation.redefines?.length ? `redefines ${notation.redefines.join(", ")}` : void 0
162485
+ ].filter((value) => !!value).join(" ");
162486
+ return adornment || void 0;
162487
+ }
162488
+ function mergeConnectionEndNotation(local, inherited) {
162489
+ const notation = {
162490
+ role: local.role ?? inherited?.role,
162491
+ multiplicity: local.multiplicity ?? inherited?.multiplicity,
162492
+ direction: local.direction ?? inherited?.direction,
162493
+ properties: uniqueStrings([...local.properties ?? [], ...inherited?.properties ?? []]),
162494
+ subsets: uniqueStrings([...local.subsets ?? [], ...inherited?.subsets ?? []]),
162495
+ redefines: uniqueStrings([...local.redefines ?? [], ...inherited?.redefines ?? []]),
162496
+ type: local.type ?? inherited?.type
162497
+ };
162498
+ notation.adornment = formatConnectionEndAdornment(notation);
162499
+ return notation;
162500
+ }
162501
+ function connectionEndNotationClaims(notation) {
162502
+ return uniqueStrings([
162503
+ notation.role,
162504
+ ...(notation.redefines ?? []).map(lastSeg)
162505
+ ]);
162506
+ }
162507
+ function connectionEndSpecializesRole(notation, role) {
162508
+ const wanted = lastSeg(role);
162509
+ return [...notation.subsets ?? [], ...notation.redefines ?? []].some((target) => lastSeg(target) === wanted);
162510
+ }
162511
+ function declaredConnectionEndNotation(node) {
162512
+ const n2 = node;
162513
+ const modifiers2 = [
162514
+ ...n2.modifiers ?? [],
162515
+ ...n2.postModifiers ?? [],
162516
+ ...n2.trailingQuals ?? [],
162517
+ ...n2.innerTrailingQuals ?? []
162518
+ ];
162519
+ const direction = modifiers2.find((value) => value === "in" || value === "out" || value === "inout");
162520
+ const properties = uniqueStrings(modifiers2.filter((value) => END_PROPERTY_ADORNMENTS.has(value)));
162521
+ const subsets = [];
162522
+ const redefines = [];
162523
+ for (const relationship of [...n2.relationships ?? [], ...n2.innerRelationships ?? []]) {
162524
+ const targets = relationship.targets ?? [];
162525
+ if (relationship.kind === ":>" || relationship.kind === "subsets") {
162526
+ subsets.push(...targets);
162527
+ } else if (relationship.kind === ":>>" || relationship.kind === "redefines") {
162528
+ redefines.push(...targets);
162529
+ }
162530
+ }
162531
+ return mergeConnectionEndNotation({
162532
+ role: n2.innerName ?? nameOf2(node) ?? redefines.map(lastSeg).find(Boolean),
162533
+ multiplicity: connectionEndMultiplicity(node),
162534
+ direction,
162535
+ properties,
162536
+ subsets: uniqueStrings(subsets),
162537
+ redefines: uniqueStrings(redefines),
162538
+ type: n2.innerTyping?.type?.$refText ?? n2.typing?.type?.$refText
162539
+ }, void 0);
162540
+ }
162541
+ function multText(node) {
162542
+ if (!node)
162543
+ return void 0;
162544
+ return multiplicityText(node.multiplicity);
162545
+ }
162378
162546
  var typeText = (node) => node.typing?.type?.$refText || void 0;
162379
162547
  var isConjugated = (node) => node.typing?.conjugate === true;
162380
162548
  var isAbstract = (node) => Array.isArray(node.modifiers) && node.modifiers.includes("abstract");
@@ -162505,6 +162673,18 @@ var modifiersOf = (node) => {
162505
162673
  const mods = node.modifiers;
162506
162674
  return Array.isArray(mods) ? mods : [];
162507
162675
  };
162676
+ var ANONYMOUS_INTERFACE_NAME = "(anonymous)";
162677
+ var isEndMember = (node) => node.$type === "EndDecl" || modifiersOf(node).includes("end");
162678
+ function endRowText(node) {
162679
+ const end = node;
162680
+ const name = nameOf2(node) ?? end.innerName;
162681
+ const type = typeText(node) ?? end.innerTyping?.type?.$refText;
162682
+ const mult = multText(node) ?? multiplicityText(end.innerMultiplicity) ?? multiplicityText(end.endMultiplicity);
162683
+ const head2 = `${name ?? ""}${type ? `${name ? " " : ""}: ${type}` : ""}`.trim();
162684
+ if (!head2)
162685
+ return void 0;
162686
+ return `${head2}${mult ? ` ${mult}` : ""}`;
162687
+ }
162508
162688
  var portionKindOf = (node) => modifiersOf(node).find((m) => m === "timeslice" || m === "snapshot");
162509
162689
  var isIndividualOccurrence = (node) => modifiersOf(node).includes("individual");
162510
162690
  var isOccurrenceModified = (node) => portionKindOf(node) !== void 0 || isIndividualOccurrence(node);
@@ -162768,17 +162948,40 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162768
162948
  else
162769
162949
  drawn.set(k, [target]);
162770
162950
  };
162771
- for (const n2 of model.nodes) {
162772
- claim(n2.source, { id: n2.id, parent: n2.parent, frame: n2.frame });
162951
+ const claimNode = (n2, definitionLayer2 = false) => {
162952
+ claim(n2.source, { id: n2.id, parent: n2.parent, frame: n2.frame, definitionLayer: definitionLayer2 });
162773
162953
  for (const port of n2.ports ?? []) {
162774
- claim(port.source, { id: port.id, parent: n2.parent, frame: n2.frame, port: true });
162954
+ if (port.meta?.connectionEndPin === true)
162955
+ continue;
162956
+ claim(port.source, {
162957
+ id: port.id,
162958
+ parent: n2.parent,
162959
+ frame: n2.frame,
162960
+ port: true,
162961
+ definitionLayer: definitionLayer2
162962
+ });
162775
162963
  }
162776
- }
162777
- for (const f of model.frames ?? []) {
162778
- claim(f.source, { id: f.id, frame: f.id });
162964
+ };
162965
+ const claimFrame = (f, definitionLayer2 = false) => {
162966
+ claim(f.source, { id: f.id, frame: f.id, definitionLayer: definitionLayer2 });
162779
162967
  for (const port of f.ports ?? []) {
162780
- claim(port.source, { id: port.id, frame: f.id, port: true });
162968
+ if (port.meta?.connectionEndPin === true)
162969
+ continue;
162970
+ claim(port.source, { id: port.id, frame: f.id, port: true, definitionLayer: definitionLayer2 });
162781
162971
  }
162972
+ };
162973
+ for (const n2 of model.nodes) {
162974
+ claimNode(n2);
162975
+ }
162976
+ for (const f of model.frames ?? []) {
162977
+ claimFrame(f);
162978
+ }
162979
+ const definitionLayer = model.layers?.definitions;
162980
+ for (const n2 of definitionLayer?.nodes ?? []) {
162981
+ claimNode(n2, true);
162982
+ }
162983
+ for (const f of definitionLayer?.frames ?? []) {
162984
+ claimFrame(f, true);
162782
162985
  }
162783
162986
  if (model.ivBoundary) {
162784
162987
  claim(model.ivBoundary.source, { id: model.ivBoundary.id, frame: model.ivBoundary.id });
@@ -162790,15 +162993,22 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162790
162993
  return model;
162791
162994
  const nodes = [...model.nodes];
162792
162995
  const edges = [...model.edges];
162996
+ const definitionNodes = [...definitionLayer?.nodes ?? []];
162997
+ const definitionEdges = [...definitionLayer?.edges ?? []];
162793
162998
  let noteCount = 0;
162794
- let edgeCount = edges.length;
162999
+ let edgeCount = [...edges, ...definitionEdges].reduce((next, edge) => {
163000
+ const match = /^e(\d+)$/u.exec(edge.id);
163001
+ return match ? Math.max(next, Number(match[1]) + 1) : next;
163002
+ }, 0);
162795
163003
  const note = (annotation) => {
162796
163004
  const targets = drawn.get(key(sourceOf2(annotation.owner, uri)) ?? "")?.filter((target) => annotation.keyword !== "doc" || docAsNote || target.port);
162797
163005
  if (!targets?.length)
162798
163006
  return;
162799
163007
  for (const target of targets) {
162800
163008
  const id2 = `__note_${noteCount++}__`;
162801
- nodes.push({
163009
+ const targetNodes = target.definitionLayer ? definitionNodes : nodes;
163010
+ const targetEdges = target.definitionLayer ? definitionEdges : edges;
163011
+ targetNodes.push({
162802
163012
  id: id2,
162803
163013
  name: annotation.label,
162804
163014
  keyword: annotation.keyword,
@@ -162811,7 +163021,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162811
163021
  ...target.frame ? { frame: target.frame } : {},
162812
163022
  source: sourceOf2(annotation.annotation, uri)
162813
163023
  });
162814
- edges.push({
163024
+ targetEdges.push({
162815
163025
  id: `e${edgeCount++}`,
162816
163026
  from: id2,
162817
163027
  to: target.id,
@@ -162829,7 +163039,23 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162829
163039
  note(annotation);
162830
163040
  }
162831
163041
  }
162832
- return noteCount ? { ...model, nodes, edges } : model;
163042
+ if (!noteCount)
163043
+ return model;
163044
+ return {
163045
+ ...model,
163046
+ nodes,
163047
+ edges,
163048
+ ...definitionLayer ? {
163049
+ layers: {
163050
+ ...model.layers,
163051
+ definitions: {
163052
+ ...definitionLayer,
163053
+ nodes: definitionNodes,
163054
+ edges: definitionEdges
163055
+ }
163056
+ }
163057
+ } : {}
163058
+ };
162833
163059
  }
162834
163060
  // REQ-224 — Detect the file/anchor-specific view kinds that will render
162835
163061
  // substantive diagram content. The extension uses this to limit the
@@ -163088,7 +163314,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163088
163314
  // and the feature it redefines. The latter can contribute its own body and its
163089
163315
  // type's complete inventory, so following only typing definitions turns an
163090
163316
  // anonymous `part :>> inheritedPart` into an empty leaf.
163091
- inheritedFeatureOwnersOf(node, index2) {
163317
+ inheritedFeatureOwnersOf(node, index2, resolving = /* @__PURE__ */ new Set()) {
163318
+ if (resolving.has(node))
163319
+ return [];
163320
+ resolving.add(node);
163092
163321
  const chain = [];
163093
163322
  const seen = /* @__PURE__ */ new Set([node]);
163094
163323
  const queue = [];
@@ -163101,22 +163330,26 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163101
163330
  };
163102
163331
  const addTargets = (owner) => {
163103
163332
  for (const target of featureInheritanceTargets(owner)) {
163104
- add(this.resolveFeatureInheritanceTarget(owner, target, index2));
163333
+ add(this.resolveFeatureInheritanceTarget(owner, target, index2, resolving));
163105
163334
  }
163106
163335
  };
163107
- if (node.isDef === true)
163108
- addTargets(node);
163109
- else {
163110
- add(this.resolveType(node, index2));
163111
- addTargets(node);
163112
- }
163113
- while (queue.length > 0) {
163114
- const owner = queue.shift();
163115
- if (owner.isDef !== true)
163116
- add(this.resolveType(owner, index2));
163117
- addTargets(owner);
163336
+ try {
163337
+ if (node.isDef === true)
163338
+ addTargets(node);
163339
+ else {
163340
+ add(this.resolveType(node, index2));
163341
+ addTargets(node);
163342
+ }
163343
+ while (queue.length > 0) {
163344
+ const owner = queue.shift();
163345
+ if (owner.isDef !== true)
163346
+ add(this.resolveType(owner, index2));
163347
+ addTargets(owner);
163348
+ }
163349
+ return chain;
163350
+ } finally {
163351
+ resolving.delete(node);
163118
163352
  }
163119
- return chain;
163120
163353
  }
163121
163354
  /** Declaration that an anonymous local redefinition specializes. Direct edits
163122
163355
  * in definition-sync mode address this feature, while child insertion still
@@ -163131,7 +163364,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163131
163364
  * names a sibling supplied by the containing usage or one of its types. A
163132
163365
  * global simple-name lookup can select the redeclaration itself or an
163133
163366
  * unrelated namesake, so the containing feature inventory is checked first. */
163134
- resolveFeatureInheritanceTarget(owner, target, index2) {
163367
+ resolveFeatureInheritanceTarget(owner, target, index2, resolving) {
163135
163368
  const path10 = this.resolvedFeatureInheritancePath(owner, target);
163136
163369
  if (path10.qualified) {
163137
163370
  return path10.target === owner ? void 0 : path10.target;
@@ -163140,7 +163373,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163140
163373
  const wanted = lastSeg(target);
163141
163374
  const container = owner.$container;
163142
163375
  if (container) {
163143
- for (const source of [container, ...this.inheritedFeatureOwnersOf(container, index2)]) {
163376
+ for (const source of [container, ...this.inheritedFeatureOwnersOf(container, index2, resolving)]) {
163144
163377
  const inherited = membersOf(source).find((member) => member !== owner && effectiveNameOf(member) === wanted);
163145
163378
  if (inherited)
163146
163379
  return inherited;
@@ -163649,8 +163882,27 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163649
163882
  hasGeometryObject(node, index2) {
163650
163883
  return this.collectGeometry(node, index2).length > 0;
163651
163884
  }
163885
+ // REQ-360 — Package overview
163886
+ /** Package overviews contain layouts, not inventories of unplaced shape types. */
163887
+ hasPlacedGeometryObject(node, index2) {
163888
+ return this.collectGeometry(node, index2).some((candidate) => candidate.geo !== void 0);
163889
+ }
163890
+ // REQ-360 — Package overview
163891
+ /** Collapse a wrapper whose only direct object owns the actual layout. */
163892
+ hasGeometryOverviewLayout(node, index2) {
163893
+ const placed = this.collectGeometry(node, index2).filter((candidate) => candidate.geo !== void 0);
163894
+ if (placed.length === 0)
163895
+ return false;
163896
+ const direct = placed.filter((candidate) => !candidate.name.includes("."));
163897
+ if (direct.length !== 1)
163898
+ return true;
163899
+ const prefix = `${direct[0].name}.`;
163900
+ return !placed.some((candidate) => candidate.name.startsWith(prefix));
163901
+ }
163652
163902
  hasRenderablePorts(node, index2) {
163653
- return [node, ...this.inheritedFeatureOwnersOf(node, index2)].some((source) => membersOf(source).some((m) => isPortDecl(m) && m.isDef !== true));
163903
+ if (membersOf(node).some((member) => isPortDecl(member) && member.isDef !== true))
163904
+ return true;
163905
+ return this.inheritedFeatureOwnersOf(node, index2).some((source) => membersOf(source).some((member) => isPortDecl(member) && member.isDef !== true && !this.isInheritedLibraryBackboneFeature(source, member, index2)));
163654
163906
  }
163655
163907
  // REQ-192, issue #233 — the named part usages a part shows when expanded: its
163656
163908
  // own and the ones it inherits, through its typing definition or a `:>`
@@ -163658,11 +163910,51 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163658
163910
  // The predicate form of `childUsagesOf`, so an anchor that only INHERITS
163659
163911
  // internals is recognised as having them.
163660
163912
  hasNestedPartUsages(node, index2) {
163661
- return [node, ...this.inheritedFeatureOwnersOf(node, index2)].some((source) => this.nestedUsages(source, isPartDecl).some((n2) => nameOf2(n2) !== void 0));
163913
+ return this.structuralChildUsagesOf(node, index2).some((usage) => nameOf2(usage) !== void 0);
163662
163914
  }
163663
163915
  nestedUsages(node, guard) {
163664
163916
  return membersOf(node).filter((m) => guard(m) && m.isDef !== true);
163665
163917
  }
163918
+ // REQ-192 - standard-library definitions contribute real inherited structure,
163919
+ // but their recursive backbone features are collecting roles, not additional
163920
+ // concrete children of every typed usage. For example, SpatialItem declares
163921
+ // `subSpatialParts : SpatialItem`, and Part declares `start : Part`. Expanding
163922
+ // either role as another occurrence recursively materializes the library model
163923
+ // instead of the user's composition. A local redefinition remains visible.
163924
+ isInheritedLibraryBackboneFeature(source, usage, index2) {
163925
+ if (!isLibraryDocument(ast_utils_exports.getDocument(usage)))
163926
+ return false;
163927
+ if (modifiersOf(usage).includes("abstract"))
163928
+ return true;
163929
+ const ownerType = source.isDef === true ? source : this.resolveType(source, index2);
163930
+ const usageType = this.resolveType(usage, index2);
163931
+ if (!ownerType || !usageType)
163932
+ return false;
163933
+ if (ownerType === usageType)
163934
+ return true;
163935
+ return this.inheritedFeatureOwnersOf(ownerType, index2).includes(usageType) || this.inheritedFeatureOwnersOf(usageType, index2).includes(ownerType);
163936
+ }
163937
+ /** The concrete structural children projected by IV, with the same nearest-name
163938
+ * fold used for other effective inventories. Authored subsets of an inherited
163939
+ * library collection stay visible; only the inherited recursive role is hidden. */
163940
+ structuralChildUsagesOf(part, index2) {
163941
+ const out = this.nestedUsages(part, isPartDecl);
163942
+ const have = new Set(out.map(effectiveNameOf).filter((name) => !!name));
163943
+ for (const source of this.inheritedFeatureOwnersOf(part, index2)) {
163944
+ for (const usage of this.nestedUsages(source, isPartDecl)) {
163945
+ if (this.isInheritedLibraryBackboneFeature(source, usage, index2))
163946
+ continue;
163947
+ const name = effectiveNameOf(usage);
163948
+ if (name !== void 0) {
163949
+ if (have.has(name))
163950
+ continue;
163951
+ have.add(name);
163952
+ }
163953
+ out.push(usage);
163954
+ }
163955
+ }
163956
+ return out;
163957
+ }
163666
163958
  // issue #109 — usages of kinds with NO dedicated usage view (constraint /
163667
163959
  // calc / occurrence / item / attribute). Nested in an element they surface as
163668
163960
  // owner compartments (compartmentsFor); declared as direct package members
@@ -163697,7 +163989,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163697
163989
  const all = [...ast_utils_exports.streamAllContents(scope)];
163698
163990
  const defNodes = all.filter((n2) => isDefinitionNode(n2) && nameOf2(n2));
163699
163991
  const packageOverview = isPackage(ctx.anchor) || isDocument(ctx.anchor);
163700
- const hasOwnedMemberNode = all.some((n2) => isPackage(n2) && !!nameOf2(n2) || isDefinitionNode(n2) && !!nameOf2(n2) || this.isViewlessUsage(n2) && this.isDirectPackageMember(n2) || isPartDecl(n2) && n2.isDef !== true && !!nameOf2(n2) && this.isDirectPackageMember(n2));
163992
+ const isDirectPackageInterfaceUsage = (n2) => isInterfaceDecl(n2) && n2.isDef !== true && this.isDirectPackageMember(n2);
163993
+ const isPackageInterfaceUsage = (n2) => {
163994
+ const usage = n2;
163995
+ const emptyPrefix = usage.target !== void 0 && usage.connect === void 0;
163996
+ return isDirectPackageInterfaceUsage(n2) && !emptyPrefix && !!nameOf2(n2);
163997
+ };
163998
+ const hasOwnedMemberNode = all.some((n2) => isPackage(n2) && !!nameOf2(n2) || isDefinitionNode(n2) && !!nameOf2(n2) || this.isViewlessUsage(n2) && this.isDirectPackageMember(n2) || isDirectPackageInterfaceUsage(n2) || isPartDecl(n2) && n2.isDef !== true && !!nameOf2(n2) && this.isDirectPackageMember(n2));
163701
163999
  const packageNodes = [
163702
164000
  ...packageOverview && hasOwnedMemberNode && isPackage(scope) && nameOf2(scope) ? [scope] : [],
163703
164001
  ...all.filter((p) => isPackage(p) && nameOf2(p))
@@ -163755,7 +164053,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163755
164053
  meta: { gvPackageMember: true }
163756
164054
  });
163757
164055
  }
163758
- const packageUsageNodes = [...viewlessUsageNodes, ...packagePartUsageNodes];
164056
+ const packageInterfaceUsageNodes = all.filter(isPackageInterfaceUsage);
164057
+ for (const u of packageInterfaceUsageNodes) {
164058
+ const def = this.resolveType(u, index2);
164059
+ pushNode(u, "box", false, { type: def && ids.has(def) ? void 0 : typeText(u) });
164060
+ }
164061
+ const packageUsageNodes = [...viewlessUsageNodes, ...packagePartUsageNodes, ...packageInterfaceUsageNodes];
163759
164062
  for (const p of packageNodes) {
163760
164063
  const pn = p;
163761
164064
  pushNode(p, "package", false, {
@@ -163853,6 +164156,77 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163853
164156
  }
163854
164157
  }
163855
164158
  }
164159
+ for (const definition of defNodes.filter(isConnectionDecl)) {
164160
+ const definitionId = ids.get(definition);
164161
+ if (!definitionId)
164162
+ continue;
164163
+ const effectiveEnds = this.connectionEndNotationsOf(definition, index2);
164164
+ const resolvedEnds = effectiveEnds.map((notation) => {
164165
+ const target = notation.type ? resolveDrawnByText(notation.type) : void 0;
164166
+ return target && ids.has(target) ? { id: ids.get(target), notation } : void 0;
164167
+ });
164168
+ if (resolvedEnds.length < 2 || resolvedEnds.some((end) => !end))
164169
+ continue;
164170
+ const completeEnds = resolvedEnds.filter((end) => !!end);
164171
+ const definitionNode = nodes.find((node) => node.id === definitionId);
164172
+ const hasElaboration = !!definitionNode?.compartments?.length || specializationFamily(definition).length > 0;
164173
+ if (definitionNode) {
164174
+ definitionNode.meta = {
164175
+ ...definitionNode.meta,
164176
+ connectionDefinitionGraphical: true,
164177
+ ...hasElaboration ? { connectionDefinitionElaboration: true } : {}
164178
+ };
164179
+ }
164180
+ const common = {
164181
+ kind: "connect",
164182
+ name: nameOf2(definition),
164183
+ isDef: true,
164184
+ label: `\xABconnection def\xBB ${nameOf2(definition) ?? ""}`.trim(),
164185
+ source: sourceOf2(definition, uri),
164186
+ meta: { connectionDefinitionGraphical: true },
164187
+ ...hasElaboration ? { elaboration: definitionId } : {}
164188
+ };
164189
+ if (completeEnds.length === 2) {
164190
+ edges.push({
164191
+ id: `e${e++}`,
164192
+ from: completeEnds[0].id,
164193
+ to: completeEnds[1].id,
164194
+ ...common,
164195
+ endRoleFrom: completeEnds[0].notation.role,
164196
+ endLabelFrom: completeEnds[0].notation.multiplicity,
164197
+ endAdornmentFrom: completeEnds[0].notation.adornment,
164198
+ endRoleTo: completeEnds[1].notation.role,
164199
+ endLabelTo: completeEnds[1].notation.multiplicity,
164200
+ endAdornmentTo: completeEnds[1].notation.adornment
164201
+ });
164202
+ } else {
164203
+ const hubId = `${definitionId}::__connection__`;
164204
+ nodes.push({
164205
+ id: hubId,
164206
+ name: nameOf2(definition) ?? "",
164207
+ keyword: "connection def",
164208
+ isDef: true,
164209
+ shape: "dot",
164210
+ ...packageOf2(definition),
164211
+ source: sourceOf2(definition, uri),
164212
+ meta: {
164213
+ connectionDefinitionGraphical: true,
164214
+ connectionDefinitionHub: true,
164215
+ naryConnectionHub: true
164216
+ }
164217
+ });
164218
+ completeEnds.forEach((end, indexOfEnd) => edges.push({
164219
+ id: `e${e++}`,
164220
+ from: hubId,
164221
+ to: end.id,
164222
+ ...common,
164223
+ ...indexOfEnd === 0 ? {} : { label: void 0, elaboration: void 0 },
164224
+ endRoleTo: end.notation.role,
164225
+ endLabelTo: end.notation.multiplicity,
164226
+ endAdornmentTo: end.notation.adornment
164227
+ }));
164228
+ }
164229
+ }
163856
164230
  for (const rs of all.filter((m) => m.$type === "StandaloneRelationshipDecl")) {
163857
164231
  const rn = rs;
163858
164232
  const from = resolveDrawnByText(String(rn.source ?? ""));
@@ -164269,6 +164643,270 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164269
164643
  const o = v;
164270
164644
  return this.featurePath(o.reference) ?? this.featurePath(o.path) ?? o.$cstNode?.text?.replace(/\[[^\]]*\]/g, "").trim();
164271
164645
  }
164646
+ connectorEndRole(v) {
164647
+ if (!v || typeof v !== "object")
164648
+ return void 0;
164649
+ const end = v;
164650
+ if (!end.reference)
164651
+ return void 0;
164652
+ const role = this.featurePath(end.path);
164653
+ return role ? lastSeg(role) : void 0;
164654
+ }
164655
+ // REQ-192, issue #111: connection and interface definitions may specialize
164656
+ // definitions that own their ends. Fold those ends nearest-first, just like
164657
+ // the rest of the effective IV inventory. A local end shadows an inherited
164658
+ // end by role or explicit redefinition and inherits notation it omits.
164659
+ connectionEndNotationsOf(owner, index2) {
164660
+ if (!owner)
164661
+ return [];
164662
+ const inheritedOwners = owner.isDef === true ? [] : this.inheritedFeatureOwnersOf(owner, index2);
164663
+ const definition = owner.isDef === true ? owner : this.resolveType(owner, index2) ?? inheritedOwners.find((candidate) => candidate.isDef === true && candidate.$type === owner.$type);
164664
+ if (!definition)
164665
+ return [];
164666
+ const selected2 = [];
164667
+ for (const source of [definition, ...this.inheritedFeatureOwnersOf(definition, index2)]) {
164668
+ for (const end of membersOf(source).filter(isEndMember)) {
164669
+ const notation = declaredConnectionEndNotation(end);
164670
+ const claims = new Set(connectionEndNotationClaims(notation));
164671
+ const existing = selected2.find((candidate) => [...claims].some((claim) => candidate.claims.has(claim)));
164672
+ if (existing) {
164673
+ existing.notation = mergeConnectionEndNotation(existing.notation, notation);
164674
+ for (const claim of connectionEndNotationClaims(existing.notation))
164675
+ existing.claims.add(claim);
164676
+ continue;
164677
+ }
164678
+ const inheritedRole = notation.role;
164679
+ const specialized = inheritedRole ? selected2.filter((candidate) => connectionEndSpecializesRole(candidate.notation, inheritedRole)) : [];
164680
+ if (specialized.length > 0) {
164681
+ for (const candidate of specialized) {
164682
+ candidate.notation = mergeConnectionEndNotation(candidate.notation, notation);
164683
+ for (const claim of connectionEndNotationClaims(candidate.notation))
164684
+ candidate.claims.add(claim);
164685
+ }
164686
+ continue;
164687
+ }
164688
+ selected2.push({ notation, claims });
164689
+ }
164690
+ }
164691
+ return selected2.map(({ notation }) => notation);
164692
+ }
164693
+ matchingConnectionEndNotation(notations, role, ordinal) {
164694
+ const wanted = role ? lastSeg(role) : void 0;
164695
+ if (wanted) {
164696
+ return notations.find((notation) => connectionEndNotationClaims(notation).some((claim) => lastSeg(claim) === wanted));
164697
+ }
164698
+ return notations[ordinal];
164699
+ }
164700
+ declaredConnectionEndBinding(node) {
164701
+ const n2 = node;
164702
+ for (const relationship of [...n2.relationships ?? [], ...n2.innerRelationships ?? []]) {
164703
+ if (relationship.kind !== "::>" && relationship.kind !== "references")
164704
+ continue;
164705
+ for (const [occurrence, target] of (relationship.targets ?? []).entries()) {
164706
+ if (!target)
164707
+ continue;
164708
+ return {
164709
+ path: target,
164710
+ binding: {
164711
+ host: relationship,
164712
+ property: "targets",
164713
+ occurrence
164714
+ }
164715
+ };
164716
+ }
164717
+ }
164718
+ return {};
164719
+ }
164720
+ connectorEndBinding(end) {
164721
+ if (!end || typeof end !== "object" || !("$type" in end))
164722
+ return void 0;
164723
+ const connectorEnd = end;
164724
+ if (connectorEnd.$type !== "ConnectorEnd")
164725
+ return void 0;
164726
+ return {
164727
+ host: connectorEnd,
164728
+ property: connectorEnd.reference ? "reference" : "path",
164729
+ occurrence: 0
164730
+ };
164731
+ }
164732
+ canonicalConnectionBindingTarget(target, seen = /* @__PURE__ */ new Set()) {
164733
+ if (!isAliasDecl(target))
164734
+ return target;
164735
+ if (seen.has(target))
164736
+ return void 0;
164737
+ seen.add(target);
164738
+ const resolution = this.annotationPathResolver(target).resolvePropertyPath(target, "target");
164739
+ if (resolution.unresolvedIndex !== void 0 || resolution.indeterminateIndex !== void 0)
164740
+ return void 0;
164741
+ const terminal = resolution.segments.at(-1);
164742
+ const aliasTarget = terminal?.target ?? this.annotationDescriptionNode(target, terminal?.description);
164743
+ return aliasTarget ? this.canonicalConnectionBindingTarget(aliasTarget, seen) : void 0;
164744
+ }
164745
+ explicitFeatureAncestorsOf(node, index2) {
164746
+ const ancestors = [];
164747
+ const seen = /* @__PURE__ */ new Set([node]);
164748
+ const queue = [node];
164749
+ while (queue.length > 0) {
164750
+ const current2 = queue.shift();
164751
+ for (const path10 of featureInheritanceTargets(current2)) {
164752
+ const target = this.resolveFeatureInheritanceTarget(current2, path10, index2, /* @__PURE__ */ new Set());
164753
+ if (!target || seen.has(target))
164754
+ continue;
164755
+ seen.add(target);
164756
+ ancestors.push(target);
164757
+ queue.push(target);
164758
+ }
164759
+ }
164760
+ return ancestors;
164761
+ }
164762
+ /** REQ-192: whether the declared path resolves to the exact rendered
164763
+ * occurrence path. The ordinary IV endpoint resolver remains forgiving for
164764
+ * issue #62; inherited visibility also checks every semantic receiver so a
164765
+ * path such as `outside.p` cannot borrow another visible `inside.p` merely
164766
+ * because both ports share one definition. */
164767
+ connectionUsageEndBindingMatches(end, renderedPath, ownerPath, index2) {
164768
+ if (!end.binding || !renderedPath || !ownerPath)
164769
+ return false;
164770
+ const resolution = this.annotationPathResolver(end.binding.host).resolvePropertyPath(end.binding.host, end.binding.property, end.binding.occurrence);
164771
+ if (resolution.unresolvedIndex !== void 0 || resolution.indeterminateIndex !== void 0)
164772
+ return false;
164773
+ const semanticPath = [];
164774
+ const resolver = this.annotationPathResolver(end.binding.host);
164775
+ let canonicalOwner;
164776
+ for (const segment of resolution.segments) {
164777
+ const target = canonicalOwner ? resolver.visibleMembers(canonicalOwner).find((candidate) => effectiveNameOf(candidate) === segment.text) : segment.target ?? this.annotationDescriptionNode(end.binding.host, segment.description);
164778
+ if (!target)
164779
+ return false;
164780
+ const canonical = this.canonicalConnectionBindingTarget(target);
164781
+ if (!canonical)
164782
+ return false;
164783
+ if (canonicalOwner || isAliasDecl(target))
164784
+ canonicalOwner = canonical;
164785
+ if (isPackage(canonical) || isDocument(canonical) || canonical.$type === "NamespaceDecl")
164786
+ continue;
164787
+ semanticPath.push(canonical);
164788
+ }
164789
+ if (semanticPath.length === 0)
164790
+ return false;
164791
+ const sameDeclaration = (candidate, expected) => {
164792
+ if (candidate === expected)
164793
+ return true;
164794
+ const candidateCst = candidate.$cstNode;
164795
+ const expectedCst = expected.$cstNode;
164796
+ if (!candidateCst || !expectedCst || candidate.$type !== expected.$type || candidateCst.offset !== expectedCst.offset || candidateCst.end !== expectedCst.end) {
164797
+ return false;
164798
+ }
164799
+ try {
164800
+ return ast_utils_exports.getDocument(candidate).uri.toString() === ast_utils_exports.getDocument(expected).uri.toString();
164801
+ } catch {
164802
+ return false;
164803
+ }
164804
+ };
164805
+ const matches = (candidatePath, allowRootRebinding) => candidatePath.length === semanticPath.length && candidatePath.every((candidate, at) => {
164806
+ const expected = semanticPath[at];
164807
+ if (sameDeclaration(candidate, expected) || this.explicitFeatureAncestorsOf(candidate, index2).some((ancestor) => sameDeclaration(ancestor, expected))) {
164808
+ return true;
164809
+ }
164810
+ return allowRootRebinding && at === 0 && this.inheritedFeatureOwnersOf(candidate, index2).some((ancestor) => sameDeclaration(ancestor, expected));
164811
+ });
164812
+ const relativePath = renderedPath.slice(ownerPath.length);
164813
+ const ownerRootedPath = renderedPath.slice(Math.max(0, ownerPath.length - 1));
164814
+ const result = matches(relativePath, false) || matches(ownerRootedPath, true) || matches(renderedPath, true) || semanticPath.length === 1 && matches(renderedPath.slice(-1), false);
164815
+ return result;
164816
+ }
164817
+ /** REQ-192: the ends written directly on one explicit connection usage.
164818
+ * This keeps the concrete binding path beside its notation so inheritance
164819
+ * can fold both pieces of the effective end together. */
164820
+ declaredConnectionUsageEndsOf(owner, index2) {
164821
+ const clause = owner.connect;
164822
+ if (clause) {
164823
+ const rawEnds = clause.ends?.length ? clause.ends : [clause.source, clause.target].filter((end) => end !== void 0);
164824
+ return rawEnds.map((end, ordinal) => ({
164825
+ notation: this.connectorEndNotation(owner, end, index2, ordinal),
164826
+ path: this.connectorEndPath(end),
164827
+ binding: this.connectorEndBinding(end),
164828
+ ordinal
164829
+ }));
164830
+ }
164831
+ const effective = this.connectionEndNotationsOf(owner, index2);
164832
+ return membersOf(owner).filter(isEndMember).map((end, ordinal) => {
164833
+ const local = declaredConnectionEndNotation(end);
164834
+ const declaredBinding = this.declaredConnectionEndBinding(end);
164835
+ return {
164836
+ notation: mergeConnectionEndNotation(local, this.matchingConnectionEndNotation(effective, local.role, ordinal)),
164837
+ ...declaredBinding,
164838
+ ordinal
164839
+ };
164840
+ });
164841
+ }
164842
+ /** REQ-192/195: an explicit connection or interface usage redefinition
164843
+ * inherits any end binding it does not replace. Sources are nearest first.
164844
+ * Role and redefinition claims identify corresponding ends, with source-local
164845
+ * ordinal as the fallback. */
164846
+ effectiveConnectionUsageEndsOf(owner, index2) {
164847
+ const declared = [];
164848
+ for (const source of [owner, ...this.inheritedFeatureOwnersOf(owner, index2)]) {
164849
+ if (source.$type !== owner.$type || !isConnectionDecl(source) && !isInterfaceDecl(source) || source.isDef === true)
164850
+ continue;
164851
+ for (const candidate of this.declaredConnectionUsageEndsOf(source, index2)) {
164852
+ const claims = connectionEndNotationClaims(candidate.notation).map(lastSeg);
164853
+ let existingIndex = claims.length > 0 ? declared.findIndex((current2) => connectionEndNotationClaims(current2.notation).map(lastSeg).some((claim) => claims.includes(claim))) : -1;
164854
+ if (existingIndex < 0) {
164855
+ const ordinalIndex = declared.findIndex((current2) => current2.ordinal === candidate.ordinal);
164856
+ const ordinalClaims = ordinalIndex >= 0 ? connectionEndNotationClaims(declared[ordinalIndex].notation).map(lastSeg) : [];
164857
+ if (claims.length === 0 || ordinalClaims.length === 0)
164858
+ existingIndex = ordinalIndex;
164859
+ }
164860
+ if (existingIndex < 0) {
164861
+ declared.push(candidate);
164862
+ continue;
164863
+ }
164864
+ const nearer = declared[existingIndex];
164865
+ const inheritsPath = nearer.path === void 0;
164866
+ declared[existingIndex] = {
164867
+ ...nearer,
164868
+ notation: mergeConnectionEndNotation(nearer.notation, candidate.notation),
164869
+ path: nearer.path ?? candidate.path,
164870
+ binding: inheritsPath ? candidate.binding : nearer.binding
164871
+ };
164872
+ }
164873
+ }
164874
+ const effective = this.connectionEndNotationsOf(owner, index2);
164875
+ const selectedDeclared = /* @__PURE__ */ new Set();
164876
+ const ends = effective.map((notation, ordinal) => {
164877
+ const claims = connectionEndNotationClaims(notation).map(lastSeg);
164878
+ let declaredIndex = declared.findIndex((candidate, candidateIndex) => !selectedDeclared.has(candidateIndex) && connectionEndNotationClaims(candidate.notation).map(lastSeg).some((claim) => claims.includes(claim)));
164879
+ if (declaredIndex < 0 && declared[ordinal] && !selectedDeclared.has(ordinal)) {
164880
+ const declaredClaims = connectionEndNotationClaims(declared[ordinal].notation).map(lastSeg);
164881
+ if (claims.length === 0 || declaredClaims.length === 0)
164882
+ declaredIndex = ordinal;
164883
+ }
164884
+ const concrete = declaredIndex >= 0 ? declared[declaredIndex] : void 0;
164885
+ if (declaredIndex >= 0)
164886
+ selectedDeclared.add(declaredIndex);
164887
+ return {
164888
+ notation: mergeConnectionEndNotation(concrete?.notation ?? {}, notation),
164889
+ path: concrete?.path,
164890
+ binding: concrete?.binding,
164891
+ ordinal
164892
+ };
164893
+ });
164894
+ for (const [declaredIndex, candidate] of declared.entries()) {
164895
+ if (selectedDeclared.has(declaredIndex))
164896
+ continue;
164897
+ ends.push({ ...candidate, ordinal: ends.length });
164898
+ }
164899
+ return ends;
164900
+ }
164901
+ connectorEndNotation(owner, rawEnd, index2, ordinal = 0) {
164902
+ const explicit = rawEnd && typeof rawEnd === "object" && "$type" in rawEnd ? connectionEndMultiplicity(rawEnd) : void 0;
164903
+ const role = this.connectorEndRole(rawEnd);
164904
+ const inherited = this.matchingConnectionEndNotation(this.connectionEndNotationsOf(owner, index2), role, ordinal);
164905
+ return mergeConnectionEndNotation({
164906
+ role,
164907
+ multiplicity: explicit ?? inherited?.multiplicity
164908
+ }, inherited);
164909
+ }
164272
164910
  // FlowStmt ends for both spec forms (OMG SysML 8.2.2.16): explicit
164273
164911
  // `from a to b`, and the keyword-less shorthand `flow a.x to b.y;` whose
164274
164912
  // source is parsed as name + dotted segments.
@@ -164302,7 +164940,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164302
164940
  const nodes = [];
164303
164941
  const edges = [];
164304
164942
  const frames = [];
164943
+ const primaryTarget = { nodes, edges, frames };
164944
+ const definitionTarget = { nodes: [], edges: [], frames: [] };
164305
164945
  const eRef = { n: 0 };
164946
+ const drawnPartIds = /* @__PURE__ */ new Set();
164947
+ const endpointProvenance = /* @__PURE__ */ new Map();
164948
+ const typedPartUsages = [];
164949
+ const definitionRootIds = /* @__PURE__ */ new Map();
164306
164950
  const enclosingPackage = (n2) => {
164307
164951
  let c = n2.$container;
164308
164952
  while (c && c !== scope) {
@@ -164325,22 +164969,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164325
164969
  pkgFrame.set(pkg, id2);
164326
164970
  return id2;
164327
164971
  };
164328
- const childUsagesOf = (part) => {
164329
- const out = this.nestedUsages(part, isPartDecl);
164330
- const have = new Set(out.map(effectiveNameOf).filter((x) => !!x));
164331
- for (const source of this.inheritedFeatureOwnersOf(part, index2)) {
164332
- for (const usage of this.nestedUsages(source, isPartDecl)) {
164333
- const nm = effectiveNameOf(usage);
164334
- if (nm !== void 0) {
164335
- if (have.has(nm))
164336
- continue;
164337
- have.add(nm);
164338
- }
164339
- out.push(usage);
164340
- }
164341
- }
164342
- return out;
164343
- };
164972
+ const childUsagesOf = (part) => this.structuralChildUsagesOf(part, index2);
164344
164973
  const effectiveMembersOf = (part) => {
164345
164974
  const effective = [];
164346
164975
  const claimedNames = /* @__PURE__ */ new Set();
@@ -164367,6 +164996,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164367
164996
  const actionPort = /* @__PURE__ */ new Map();
164368
164997
  const localNode = /* @__PURE__ */ new Map();
164369
164998
  const actionPinIds = /* @__PURE__ */ new Set();
164999
+ const structuralPortIds = /* @__PURE__ */ new Set();
164370
165000
  const addActionPort = (key, id2) => {
164371
165001
  if (!actionPort.has(key))
164372
165002
  actionPort.set(key, id2);
@@ -164384,6 +165014,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164384
165014
  localPort.set(`${id2}.${rel2}`, port.id);
164385
165015
  if (bare)
164386
165016
  localPort.set(rel2, port.id);
165017
+ structuralPortIds.add(port.id);
164387
165018
  }
164388
165019
  };
164389
165020
  for (const { usage, id: id2 } of childInfos)
@@ -164430,25 +165061,40 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164430
165061
  return actionPort.get(firstLast) ?? void 0;
164431
165062
  if (localPort.has(firstLast))
164432
165063
  return localPort.get(firstLast);
165064
+ const rootId = localNode.get(segs[0]);
165065
+ const nestedId = rootId ? `${rootId}::${segs.slice(1).join("::")}` : void 0;
165066
+ if (nestedId && drawnPartIds.has(nestedId))
165067
+ return nestedId;
164433
165068
  }
164434
165069
  const bare = bareName2.get(segs[segs.length - 1]);
164435
165070
  if (bare)
164436
165071
  return bare;
164437
165072
  return segs.length === 1 ? localNode.get(segs[0]) : void 0;
164438
165073
  };
164439
- return { resolve: resolve8, actionPinIds };
165074
+ return { resolve: resolve8, actionPinIds, structuralPortIds };
164440
165075
  };
164441
- const renderInstance = (part, instanceId, parentFrame, seenTypes, depth, inheritedLocalUsage, inheritedLocalPath = []) => {
165076
+ const renderInstance = (part, instanceId, parentFrame, seenTypes, depth, inheritedLocalUsage, inheritedLocalPath = [], target = primaryTarget, definitionLayerRoot = false, occurrenceInherited = false, parentOccurrencePath = []) => {
165077
+ drawnPartIds.add(instanceId);
165078
+ const occurrencePath = [...parentOccurrencePath, part];
165079
+ endpointProvenance.set(instanceId, occurrencePath);
164442
165080
  const typeDef = part.isDef === true ? void 0 : this.resolveType(part, index2) ?? this.inheritedFeatureOwnersOf(part, index2).find((owner) => owner.isDef === true);
165081
+ if (typeDef && isPartDecl(typeDef) && typeDef.isDef === true && part.isDef !== true) {
165082
+ typedPartUsages.push({ usage: part, id: instanceId, definition: typeDef });
165083
+ }
164443
165084
  const typeQ = typeDef ? qnameOf(typeDef) : void 0;
164444
165085
  const declarationId = nameOf2(part) ? qnameOf(part) || instanceId : instanceId;
164445
165086
  const projected = nameOf2(part) === void 0 || declarationId !== instanceId;
164446
165087
  const localUsage = part.isDef !== true && !projected ? part : inheritedLocalUsage;
164447
165088
  const localPath = part.isDef !== true && !projected ? [] : inheritedLocalPath;
164448
- const ports = this.portsOf(part, index2, uri, instanceId, localUsage).ports;
165089
+ const projectedPorts = this.portsOf(part, index2, uri, instanceId, localUsage);
165090
+ const ports = projectedPorts.ports;
165091
+ for (const [id2, origin] of projectedPorts.origins) {
165092
+ endpointProvenance.set(id2, [...occurrencePath, ...origin]);
165093
+ }
164449
165094
  const syncDeclaration = this.synchronizationDeclarationOf(part, index2);
164450
165095
  const editMeta = {
164451
165096
  ...ivEditMeta(part, instanceId, typeDef, localUsage, localPath, uri),
165097
+ ...definitionLayerRoot ? { ivPartDef: true } : {},
164452
165098
  ...syncDeclaration && syncDeclaration !== part ? {
164453
165099
  syncDeclarationId: qnameOf(syncDeclaration),
164454
165100
  syncDeclarationName: effectiveNameOf(syncDeclaration),
@@ -164456,7 +165102,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164456
165102
  } : {}
164457
165103
  };
164458
165104
  const children2 = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? childUsagesOf(part) : [];
164459
- const concretePerformPath = (statement, target) => {
165105
+ const concretePerformPath = (statement, target2) => {
164460
165106
  if (!this.isAnonymousBehaviorReference(statement)) {
164461
165107
  return qnameOf(statement) || nameOf2(statement) || "perform";
164462
165108
  }
@@ -164464,7 +165110,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164464
165110
  const featureStart = segments.findIndex((segment, at) => at > 0 && segment.separator === ".");
164465
165111
  if (featureStart > 0) {
164466
165112
  const writtenPath = segments.map((segment) => segment.text).join("::");
164467
- const targetName = qnameOf(target);
165113
+ const targetName = qnameOf(target2);
164468
165114
  if (targetName === writtenPath || targetName.endsWith(`::${writtenPath}`)) {
164469
165115
  return targetName;
164470
165116
  }
@@ -164483,7 +165129,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164483
165129
  return [qnameOf(root4), ...segments.slice(featureStart).map((segment) => segment.text)].join("::");
164484
165130
  }
164485
165131
  }
164486
- return qnameOf(target) || nameOf2(target) || segments.map((segment) => segment.text).join("::");
165132
+ return qnameOf(target2) || nameOf2(target2) || segments.map((segment) => segment.text).join("::");
164487
165133
  };
164488
165134
  const performedActions = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? effectiveMembersOf(part).filter((m) => m.$type === "PerformStmt" && !!nameOf2(m)).map((act) => {
164489
165135
  const pinSource = this.performTargetOf(act, index2) ?? act;
@@ -164523,7 +165169,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164523
165169
  });
164524
165170
  const emitOwnedActions = (frameOf) => {
164525
165171
  for (const { act, name, id: actId, pins, meta } of performedActionInfos) {
164526
- nodes.push({
165172
+ target.nodes.push({
164527
165173
  id: actId,
164528
165174
  name,
164529
165175
  keyword: keywordFor(act),
@@ -164539,7 +165185,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164539
165185
  }
164540
165186
  };
164541
165187
  if (children2.length > 0 || performedActions.length > 0) {
164542
- frames.push({
165188
+ target.frames.push({
164543
165189
  id: instanceId,
164544
165190
  label: effectiveNameOf(part) ?? lastSeg(instanceId),
164545
165191
  keyword: keywordFor(part),
@@ -164557,13 +165203,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164557
165203
  const nextSeen = typeQ ? /* @__PURE__ */ new Set([...seenTypes, typeQ]) : seenTypes;
164558
165204
  const childInfos = children2.map((cu) => ({ usage: cu, id: `${instanceId}::${effectiveNameOf(cu) ?? lastSeg(qnameOf(cu) || "")}` }));
164559
165205
  for (const { usage, id: id2 } of childInfos) {
164560
- renderInstance(usage, id2, instanceId, nextSeen, depth + 1, localUsage, [...localPath, effectiveNameOf(usage) ?? lastSeg(id2)]);
165206
+ renderInstance(usage, id2, instanceId, nextSeen, depth + 1, localUsage, [...localPath, effectiveNameOf(usage) ?? lastSeg(id2)], target, false, occurrenceInherited || !isAstDescendantOrSelf(usage, part), occurrencePath);
164561
165207
  }
164562
165208
  emitOwnedActions(instanceId);
164563
165209
  const resolver = childResolver(childInfos, { usage: part, id: instanceId }, performedActionInfos);
164564
- this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, children2, index2, uri, nodes, edges, eRef, instanceId, resolver.actionPinIds, part);
165210
+ this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, children2, index2, uri, target.nodes, target.edges, eRef, instanceId, resolver.actionPinIds, resolver.structuralPortIds, part, occurrenceInherited, endpointProvenance);
164565
165211
  } else {
164566
- nodes.push({
165212
+ target.nodes.push({
164567
165213
  id: instanceId,
164568
165214
  name: effectiveNameOf(part) ?? lastSeg(instanceId),
164569
165215
  keyword: keywordFor(part),
@@ -164578,7 +165224,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164578
165224
  meta: editMeta
164579
165225
  });
164580
165226
  const resolver = childResolver([], { usage: part, id: instanceId });
164581
- this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, [], index2, uri, nodes, edges, eRef, instanceId, resolver.actionPinIds, part);
165227
+ this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, [], index2, uri, target.nodes, target.edges, eRef, instanceId, resolver.actionPinIds, resolver.structuralPortIds, part, occurrenceInherited, endpointProvenance);
164582
165228
  }
164583
165229
  };
164584
165230
  const packageOfRoot = (root4) => enclosingPackage(root4) ?? scope;
@@ -164608,18 +165254,53 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164608
165254
  const rootInfos = [];
164609
165255
  for (const root4 of roots) {
164610
165256
  const id2 = rootOccurrenceIds.get(root4) ?? rootBaseId(root4);
164611
- renderInstance(root4, id2, opts.packageFrames ? ensurePkgFrame(enclosingPackage(root4)) : void 0, /* @__PURE__ */ new Set(), 0, root4.isDef === true ? void 0 : root4, []);
165257
+ const rootIsDefinition = root4.isDef === true;
165258
+ if (rootIsDefinition)
165259
+ definitionRootIds.set(root4, id2);
165260
+ renderInstance(root4, id2, opts.packageFrames ? ensurePkgFrame(enclosingPackage(root4)) : void 0, /* @__PURE__ */ new Set(), 0, root4.isDef === true ? void 0 : root4, [], primaryTarget, opts.packageFrames && rootIsDefinition);
164612
165261
  rootInfos.push({ usage: root4, id: id2, pkg: packageOfRoot(root4) });
164613
165262
  }
165263
+ for (let cursor = 0; cursor < typedPartUsages.length; cursor++) {
165264
+ const { definition } = typedPartUsages[cursor];
165265
+ if (definitionRootIds.has(definition))
165266
+ continue;
165267
+ const definitionId = qnameOf(definition) || nameOf2(definition);
165268
+ if (!definitionId)
165269
+ continue;
165270
+ definitionRootIds.set(definition, definitionId);
165271
+ renderInstance(definition, definitionId, void 0, /* @__PURE__ */ new Set(), 0, void 0, [], definitionTarget, true);
165272
+ }
165273
+ const linkedDefinitions = /* @__PURE__ */ new Set();
165274
+ for (const { id: id2, definition } of typedPartUsages) {
165275
+ const definitionId = definitionRootIds.get(definition);
165276
+ if (!definitionId || id2 === definitionId)
165277
+ continue;
165278
+ const key = `${id2}\0${definitionId}`;
165279
+ if (linkedDefinitions.has(key))
165280
+ continue;
165281
+ linkedDefinitions.add(key);
165282
+ definitionTarget.edges.push({
165283
+ id: `e${eRef.n++}`,
165284
+ from: id2,
165285
+ to: definitionId,
165286
+ kind: "definedBy",
165287
+ meta: { ivDefinedBy: true }
165288
+ });
165289
+ }
164614
165290
  if (opts.packageFrames) {
164615
165291
  for (const pkg of [scope, ...[...ast_utils_exports.streamAllContents(scope)].filter(isPackage)]) {
164616
165292
  const localInfos = rootInfos.filter((r) => r.pkg === pkg && r.usage.isDef !== true);
164617
165293
  const localRoots = localInfos.map((r) => r.usage);
164618
165294
  const resolver = childResolver(localInfos);
164619
- this.emitInterconnectEdges(membersOf(pkg), resolver.resolve, localRoots, index2, uri, nodes, edges, eRef, void 0, resolver.actionPinIds, pkg);
165295
+ this.emitInterconnectEdges(membersOf(pkg), resolver.resolve, localRoots, index2, uri, nodes, edges, eRef, void 0, resolver.actionPinIds, resolver.structuralPortIds, pkg, false, endpointProvenance);
164620
165296
  }
164621
165297
  }
164622
- return { nodes, edges, frames };
165298
+ const definitionLayer = definitionTarget.nodes.length > 0 || definitionTarget.edges.length > 0 || definitionTarget.frames.length > 0 ? {
165299
+ nodes: definitionTarget.nodes,
165300
+ edges: definitionTarget.edges,
165301
+ ...definitionTarget.frames.length ? { frames: definitionTarget.frames } : {}
165302
+ } : void 0;
165303
+ return { nodes, edges, frames, definitionLayer };
164623
165304
  }
164624
165305
  // REQ-192 — Interconnection View for a single part/part-def anchor: the anchor
164625
165306
  // is the (non-collapsible) container frame and its internals nest inside it,
@@ -164627,7 +165308,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164627
165308
  buildInterconnectionView(ctx) {
164628
165309
  const anchor = ctx.anchor;
164629
165310
  const anchorId = qnameOf(anchor) || nameOf2(anchor) || "__anchor__";
164630
- const { nodes, edges, frames } = this.renderIvRoots(ctx, [anchor], { packageFrames: false });
165311
+ const { nodes, edges, frames, definitionLayer } = this.renderIvRoots(ctx, [anchor], { packageFrames: false });
164631
165312
  if (anchor.isDef === true && frames.length === 0 && nodes.every((n2) => !n2.ports?.length && !n2.compartments?.length)) {
164632
165313
  return this.model("iv", anchor, [], []);
164633
165314
  }
@@ -164641,6 +165322,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164641
165322
  }
164642
165323
  const model = this.model("iv", anchor, nodes, edges);
164643
165324
  model.frames = frames;
165325
+ if (definitionLayer)
165326
+ model.layers = { definitions: definitionLayer };
164644
165327
  model.meta = { ...model.meta, unified: true, groupMode: "nested" };
164645
165328
  return model;
164646
165329
  }
@@ -164649,14 +165332,14 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164649
165332
  // single Interconnection View (called once over the anchor's whole subtree)
164650
165333
  // and the package overview (called once per container part, tagging any
164651
165334
  // synthetic n-ary dot with that container's frame id).
164652
- emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), memberOwner) {
165335
+ emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), structuralPortIds = /* @__PURE__ */ new Set(), memberOwner, memberOwnerInherited = false, endpointProvenance = /* @__PURE__ */ new Map()) {
164653
165336
  const memberList2 = [...members];
164654
165337
  const relationshipFields = (source) => {
164655
165338
  let directMember = source;
164656
165339
  while (directMember.$container && directMember.$container !== memberOwner) {
164657
165340
  directMember = directMember.$container;
164658
165341
  }
164659
- const inherited = !!memberOwner && directMember.$container !== memberOwner;
165342
+ const inherited = memberOwnerInherited || !!memberOwner && directMember.$container !== memberOwner;
164660
165343
  const meta = {
164661
165344
  ...inherited ? { ivInheritedRelation: true } : {},
164662
165345
  ...frameId ? { ivRelationshipOwnerId: frameId } : {}
@@ -164667,12 +165350,38 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164667
165350
  };
164668
165351
  };
164669
165352
  const supportsEndpoints = (kind, ids) => {
165353
+ if (kind === "interface") {
165354
+ return ids.length > 0 && ids.every((id2) => structuralPortIds.has(id2));
165355
+ }
164670
165356
  const pinCount = ids.filter((id2) => actionPinIds.has(id2)).length;
164671
165357
  if (pinCount === 0)
164672
165358
  return true;
164673
165359
  return ids.length === 2 && pinCount === 2 && (kind === "flow" || kind === "binding");
164674
165360
  };
164675
- const dot = (id2, name, src) => ({ id: id2, name, keyword: "connection", isDef: false, shape: "dot", frame: frameId, source: sourceOf2(src, uri) });
165361
+ const dot = (id2, name, src) => ({
165362
+ id: id2,
165363
+ name,
165364
+ keyword: "connection",
165365
+ isDef: false,
165366
+ shape: "dot",
165367
+ frame: frameId,
165368
+ source: sourceOf2(src, uri),
165369
+ meta: { naryConnectionHub: true }
165370
+ });
165371
+ const connectionUsageNodeId = (connection) => {
165372
+ const source = sourceOf2(connection, uri);
165373
+ const ownerId = frameId ?? (qnameOf(memberOwner ?? connection) || "iv");
165374
+ const semanticName = frameId ? effectiveNameOf(connection) : qnameOf(connection);
165375
+ const suffix = semanticName ?? (source ? `${source.range.start.line}_${source.range.start.character}` : `${eRef.n}`);
165376
+ return `${ownerId}::__connection_${suffix}`;
165377
+ };
165378
+ const interfaceUsageNodeId = (interfaceUsage) => {
165379
+ const source = sourceOf2(interfaceUsage, uri);
165380
+ const ownerId = frameId ?? (qnameOf(memberOwner ?? interfaceUsage) || "iv");
165381
+ const semanticName = frameId ? effectiveNameOf(interfaceUsage) : qnameOf(interfaceUsage);
165382
+ const suffix = semanticName ?? (source ? `${source.range.start.line}_${source.range.start.character}` : `${eRef.n}`);
165383
+ return `${ownerId}::__interface_${suffix}`;
165384
+ };
164676
165385
  const emitConnector = (rawEnds, src, label, name, type) => {
164677
165386
  const resolvedEnds = rawEnds.map((end) => ({ id: resolveEnd(this.connectorEndPath(end) ?? ""), end })).filter((x) => !!x.id);
164678
165387
  if (resolvedEnds.length < 2)
@@ -164680,6 +165389,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164680
165389
  if (!supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2)))
164681
165390
  return;
164682
165391
  if (resolvedEnds.length === 2) {
165392
+ const fromNotation = this.connectorEndNotation(src, resolvedEnds[0].end, index2, 0);
165393
+ const toNotation = this.connectorEndNotation(src, resolvedEnds[1].end, index2, 1);
164683
165394
  edges.push({
164684
165395
  id: `e${eRef.n++}`,
164685
165396
  from: resolvedEnds[0].id,
@@ -164689,15 +165400,20 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164689
165400
  type,
164690
165401
  isDef: false,
164691
165402
  label,
164692
- endLabelFrom: multText(resolvedEnds[0].end),
164693
- endLabelTo: multText(resolvedEnds[1].end),
165403
+ endRoleFrom: fromNotation.role,
165404
+ endLabelFrom: fromNotation.multiplicity,
165405
+ endAdornmentFrom: fromNotation.adornment,
165406
+ endRoleTo: toNotation.role,
165407
+ endLabelTo: toNotation.multiplicity,
165408
+ endAdornmentTo: toNotation.adornment,
164694
165409
  ...relationshipFields(src)
164695
165410
  });
164696
165411
  return;
164697
165412
  }
164698
165413
  const dotId = `__nary_${eRef.n}__`;
164699
165414
  nodes.push(dot(dotId, label ?? "", src));
164700
- for (const { id: endId, end } of resolvedEnds) {
165415
+ for (const [ordinal, { id: endId, end }] of resolvedEnds.entries()) {
165416
+ const notation = this.connectorEndNotation(src, end, index2, ordinal);
164701
165417
  edges.push({
164702
165418
  id: `e${eRef.n++}`,
164703
165419
  from: dotId,
@@ -164707,7 +165423,9 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164707
165423
  type,
164708
165424
  isDef: false,
164709
165425
  label,
164710
- endLabelTo: multText(end),
165426
+ endRoleTo: notation.role,
165427
+ endLabelTo: notation.multiplicity,
165428
+ endAdornmentTo: notation.adornment,
164711
165429
  ...relationshipFields(src)
164712
165430
  });
164713
165431
  }
@@ -164742,21 +165460,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164742
165460
  } else if (m.$type === "ConnectStmt") {
164743
165461
  const cs = m;
164744
165462
  if (cs.ends?.length) {
164745
- const resolvedEnds = cs.ends.map((p) => ({ id: resolveEnd(this.connectorEndPath(p) ?? ""), end: p })).filter((x) => !!x.id);
164746
- if (resolvedEnds.length >= 2 && supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2))) {
164747
- const dotId = `__nary_${eRef.n}__`;
164748
- nodes.push(dot(dotId, "", m));
164749
- for (const { id: endId, end } of resolvedEnds) {
164750
- edges.push({
164751
- id: `e${eRef.n++}`,
164752
- from: dotId,
164753
- to: endId,
164754
- kind: "connect",
164755
- endLabelTo: multText(end),
164756
- ...relationshipFields(m)
164757
- });
164758
- }
164759
- }
165463
+ emitConnector(cs.ends, m);
164760
165464
  } else {
164761
165465
  const srcEnd = m.source;
164762
165466
  const tgtEnd = m.target;
@@ -164764,6 +165468,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164764
165468
  const from = resolveEnd(srcPath);
164765
165469
  const to = resolveEnd(this.connectorEndPath(tgtEnd) ?? "");
164766
165470
  if (from && to && supportsEndpoints("connect", [from, to])) {
165471
+ const fromNotation = this.connectorEndNotation(m, srcEnd, index2, 0);
165472
+ const toNotation = this.connectorEndNotation(m, tgtEnd, index2, 1);
164767
165473
  edges.push({
164768
165474
  id: `e${eRef.n++}`,
164769
165475
  from,
@@ -164771,8 +165477,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164771
165477
  kind: "connect",
164772
165478
  label: this.flowItemLabel(srcPath, parts, index2),
164773
165479
  // REQ-179 — connector end multiplicities (`connect [1] a to [0..*] b;`)
164774
- endLabelFrom: multText(srcEnd),
164775
- endLabelTo: multText(tgtEnd),
165480
+ endRoleFrom: fromNotation.role,
165481
+ endLabelFrom: fromNotation.multiplicity,
165482
+ endAdornmentFrom: fromNotation.adornment,
165483
+ endRoleTo: toNotation.role,
165484
+ endLabelTo: toNotation.multiplicity,
165485
+ endAdornmentTo: toNotation.adornment,
164776
165486
  ...relationshipFields(m)
164777
165487
  });
164778
165488
  }
@@ -164818,74 +165528,192 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164818
165528
  });
164819
165529
  }
164820
165530
  } else if (isInterfaceDecl(m) && m.isDef !== true) {
164821
- const clause = m.connect;
164822
- if (clause) {
164823
- const from = resolveEnd(this.connectorEndPath(clause.source) ?? "");
164824
- const to = resolveEnd(this.connectorEndPath(clause.target) ?? "");
165531
+ const interfaceUsage = m;
165532
+ if (interfaceUsage.target !== void 0 && interfaceUsage.connect === void 0) {
165533
+ const sourcePath = [nameOf2(m) ?? "", ...interfaceUsage.srcSegs ?? []].filter(Boolean).join(".");
165534
+ const targetPath = this.featurePath(interfaceUsage.target) ?? "";
165535
+ const from = resolveEnd(sourcePath);
165536
+ const to = resolveEnd(targetPath);
164825
165537
  if (from && to && supportsEndpoints("interface", [from, to])) {
164826
- const name = nameOf2(m);
164827
- const type = typeText(m);
164828
- const label = ["\xABinterface\xBB", nameOf2(m) ?? "", typeText(m) ? `: ${typeText(m)}` : ""].filter(Boolean).join(" ").trim();
164829
165538
  edges.push({
164830
165539
  id: `e${eRef.n++}`,
164831
165540
  from,
164832
165541
  to,
164833
165542
  kind: "interface",
164834
- name,
164835
- type,
164836
165543
  isDef: false,
164837
- label,
164838
- endLabelFrom: multText(clause.source),
164839
- endLabelTo: multText(clause.target),
165544
+ label: "\xABinterface\xBB",
164840
165545
  ...relationshipFields(m)
164841
165546
  });
164842
165547
  }
165548
+ continue;
164843
165549
  }
164844
- } else if (isConnectionDecl(m) && m.isDef !== true) {
164845
- const clause = m.connect;
164846
- if (!clause)
165550
+ const name = effectiveNameOf(m);
165551
+ const inheritedType = this.inheritedFeatureOwnersOf(m, index2).filter((candidate) => isInterfaceDecl(candidate) && candidate.isDef !== true).map(typeText).find((candidate) => !!candidate);
165552
+ const type = typeText(m) ?? inheritedType;
165553
+ const usageEnds = this.effectiveConnectionUsageEndsOf(m, index2);
165554
+ const usageId = interfaceUsageNodeId(m);
165555
+ const usageSource = sourceOf2(m, uri);
165556
+ const relationship = relationshipFields(m);
165557
+ const resolvedUsageEnds = usageEnds.flatMap((end, ordinal) => {
165558
+ const targetId = end.path ? resolveEnd(end.path) : void 0;
165559
+ return targetId && supportsEndpoints("interface", [targetId]) ? [{ end, ordinal, targetId }] : [];
165560
+ });
165561
+ const inherited = relationship.meta?.ivInheritedRelation === true;
165562
+ const visibleUsageEnds = inherited ? resolvedUsageEnds.filter(({ end, targetId }) => this.connectionUsageEndBindingMatches(end, endpointProvenance.get(targetId), frameId ? endpointProvenance.get(frameId) : void 0, index2)) : resolvedUsageEnds;
165563
+ if (inherited && visibleUsageEnds.length === 0)
164847
165564
  continue;
164848
- const name = nameOf2(m);
164849
- const type = typeText(m);
164850
- const label = name ?? type;
164851
- if (clause.ends?.length) {
164852
- const resolvedEnds = clause.ends.map((p) => ({ id: resolveEnd(this.connectorEndPath(p) ?? ""), end: p })).filter((x) => !!x.id);
164853
- if (resolvedEnds.length >= 2 && supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2))) {
164854
- const dotId = `__nary_${eRef.n}__`;
164855
- nodes.push(dot(dotId, label ?? "", m));
164856
- for (const { id: endId, end } of resolvedEnds) {
164857
- edges.push({
164858
- id: `e${eRef.n++}`,
164859
- from: dotId,
164860
- to: endId,
164861
- kind: "connect",
164862
- name,
164863
- type,
164864
- isDef: false,
164865
- label,
164866
- endLabelTo: multText(end),
164867
- ...relationshipFields(m)
164868
- });
165565
+ const resolvedOrdinals = new Set(visibleUsageEnds.map(({ ordinal }) => ordinal));
165566
+ const ownerNode = frameId ? nodes.find((node) => node.id === frameId) : void 0;
165567
+ const usageFrame = ownerNode ? ownerNode.frame : frameId;
165568
+ const ports = usageEnds.map((end, ordinal) => {
165569
+ const role = end.notation.role;
165570
+ const direction = end.notation.direction === "in" || end.notation.direction === "out" || end.notation.direction === "inout" ? end.notation.direction : void 0;
165571
+ return {
165572
+ id: `${usageId}::__end_${ordinal}`,
165573
+ name: role ?? `end${ordinal + 1}`,
165574
+ type: end.notation.type,
165575
+ direction,
165576
+ isDef: false,
165577
+ pin: false,
165578
+ source: usageSource,
165579
+ meta: {
165580
+ // The shared renderer uses this presentation marker for
165581
+ // the outline-coloured dot and its single outward dock.
165582
+ connectionEndPin: true,
165583
+ interfaceEndPort: true,
165584
+ connectionUsageId: usageId,
165585
+ interfaceUsageId: usageId,
165586
+ connectionEndOrdinal: ordinal,
165587
+ ...role ? { connectionEndRole: role } : {},
165588
+ ...resolvedOrdinals.has(ordinal) ? { connectionEndBound: true } : {},
165589
+ ...end.notation.multiplicity ? { connectionEndMultiplicity: end.notation.multiplicity } : {},
165590
+ ...end.notation.adornment ? { connectionEndAdornment: end.notation.adornment } : {}
164869
165591
  }
165592
+ };
165593
+ });
165594
+ nodes.push({
165595
+ id: usageId,
165596
+ name: name ?? ANONYMOUS_INTERFACE_NAME,
165597
+ keyword: "interface",
165598
+ isDef: false,
165599
+ shape: "box",
165600
+ type,
165601
+ multiplicity: multText(m),
165602
+ ports,
165603
+ ...usageFrame ? { frame: usageFrame } : {},
165604
+ compartments: this.compartmentsFor(m, uri, index2),
165605
+ source: usageSource,
165606
+ meta: {
165607
+ ...relationship.meta ?? {},
165608
+ interfaceUsage: true,
165609
+ ...name ? { explicitRelationshipName: name } : {}
164870
165610
  }
164871
- } else {
164872
- const from = resolveEnd(this.connectorEndPath(clause.source) ?? "");
164873
- const to = resolveEnd(this.connectorEndPath(clause.target) ?? "");
164874
- if (from && to && supportsEndpoints("connect", [from, to])) {
164875
- edges.push({
164876
- id: `e${eRef.n++}`,
164877
- from,
164878
- to,
164879
- kind: "connect",
164880
- name,
164881
- type,
164882
- isDef: false,
164883
- label,
164884
- endLabelFrom: multText(clause.source),
164885
- endLabelTo: multText(clause.target),
164886
- ...relationshipFields(m)
164887
- });
165611
+ });
165612
+ for (const { end, ordinal, targetId } of visibleUsageEnds) {
165613
+ const role = end.notation.role;
165614
+ edges.push({
165615
+ id: `e${eRef.n++}`,
165616
+ from: ports[ordinal].id,
165617
+ to: targetId,
165618
+ kind: "interface",
165619
+ name,
165620
+ type,
165621
+ isDef: false,
165622
+ endRoleFrom: role,
165623
+ endLabelFrom: end.notation.multiplicity,
165624
+ endAdornmentFrom: end.notation.adornment,
165625
+ source: relationship.source,
165626
+ meta: {
165627
+ ...relationship.meta ?? {},
165628
+ interfaceUsageEnd: true,
165629
+ interfaceUsageId: usageId,
165630
+ connectionUsageId: usageId,
165631
+ connectionEndOrdinal: ordinal,
165632
+ ...role ? { connectionEndRole: role } : {}
165633
+ }
165634
+ });
165635
+ }
165636
+ } else if (isConnectionDecl(m) && m.isDef !== true) {
165637
+ const name = effectiveNameOf(m);
165638
+ const inheritedType = this.inheritedFeatureOwnersOf(m, index2).filter((candidate) => isConnectionDecl(candidate) && candidate.isDef !== true).map(typeText).find((candidate) => !!candidate);
165639
+ const type = typeText(m) ?? inheritedType;
165640
+ const usageEnds = this.effectiveConnectionUsageEndsOf(m, index2);
165641
+ const usageId = connectionUsageNodeId(m);
165642
+ const usageSource = sourceOf2(m, uri);
165643
+ const relationship = relationshipFields(m);
165644
+ const resolvedUsageEnds = usageEnds.flatMap((end, ordinal) => {
165645
+ const targetId = end.path ? resolveEnd(end.path) : void 0;
165646
+ return targetId && supportsEndpoints("connect", [targetId]) ? [{ end, ordinal, targetId }] : [];
165647
+ });
165648
+ const inherited = relationship.meta?.ivInheritedRelation === true;
165649
+ const visibleUsageEnds = inherited ? resolvedUsageEnds.filter(({ end, targetId }) => this.connectionUsageEndBindingMatches(end, endpointProvenance.get(targetId), frameId ? endpointProvenance.get(frameId) : void 0, index2)) : resolvedUsageEnds;
165650
+ if (inherited && visibleUsageEnds.length === 0) {
165651
+ continue;
165652
+ }
165653
+ const resolvedOrdinals = new Set(visibleUsageEnds.map(({ ordinal }) => ordinal));
165654
+ const ownerNode = frameId ? nodes.find((node) => node.id === frameId) : void 0;
165655
+ const usageFrame = ownerNode ? ownerNode.frame : frameId;
165656
+ const ports = usageEnds.map((end, ordinal) => {
165657
+ const role = end.notation.role;
165658
+ const direction = end.notation.direction === "in" || end.notation.direction === "out" || end.notation.direction === "inout" ? end.notation.direction : void 0;
165659
+ return {
165660
+ id: `${usageId}::__end_${ordinal}`,
165661
+ name: role ?? `end${ordinal + 1}`,
165662
+ type: end.notation.type,
165663
+ direction,
165664
+ isDef: false,
165665
+ pin: true,
165666
+ source: usageSource,
165667
+ meta: {
165668
+ connectionEndPin: true,
165669
+ connectionUsageId: usageId,
165670
+ connectionEndOrdinal: ordinal,
165671
+ ...role ? { connectionEndRole: role } : {},
165672
+ ...resolvedOrdinals.has(ordinal) ? { connectionEndBound: true } : {},
165673
+ ...end.notation.multiplicity ? { connectionEndMultiplicity: end.notation.multiplicity } : {},
165674
+ ...end.notation.adornment ? { connectionEndAdornment: end.notation.adornment } : {}
165675
+ }
165676
+ };
165677
+ });
165678
+ nodes.push({
165679
+ id: usageId,
165680
+ name: name ?? "connection",
165681
+ keyword: "connection",
165682
+ isDef: false,
165683
+ shape: "box",
165684
+ type,
165685
+ ports,
165686
+ ...usageFrame ? { frame: usageFrame } : {},
165687
+ compartments: this.compartmentsFor(m, uri, index2),
165688
+ source: usageSource,
165689
+ meta: {
165690
+ ...relationship.meta ?? {},
165691
+ connectionUsage: true,
165692
+ ...name ? { explicitRelationshipName: name } : {}
164888
165693
  }
165694
+ });
165695
+ for (const { end, ordinal, targetId } of visibleUsageEnds) {
165696
+ const role = end.notation.role;
165697
+ edges.push({
165698
+ id: `e${eRef.n++}`,
165699
+ from: ports[ordinal].id,
165700
+ to: targetId,
165701
+ kind: "connect",
165702
+ name,
165703
+ type,
165704
+ isDef: false,
165705
+ endRoleFrom: role,
165706
+ endLabelFrom: end.notation.multiplicity,
165707
+ endAdornmentFrom: end.notation.adornment,
165708
+ source: relationship.source,
165709
+ meta: {
165710
+ ...relationship.meta ?? {},
165711
+ connectionUsageEnd: true,
165712
+ connectionUsageId: usageId,
165713
+ connectionEndOrdinal: ordinal,
165714
+ ...role ? { connectionEndRole: role } : {}
165715
+ }
165716
+ });
164889
165717
  }
164890
165718
  }
164891
165719
  }
@@ -164936,9 +165764,11 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164936
165764
  if (roots.length === 0) {
164937
165765
  roots = all.filter((d) => isDefPart(d) && !insideAPart(d) && !insideAnOccurrence(d) && hasInternalParts(d)).sort(byName);
164938
165766
  }
164939
- const { nodes, edges, frames } = this.renderIvRoots(ctx, roots, { packageFrames: true });
165767
+ const { nodes, edges, frames, definitionLayer } = this.renderIvRoots(ctx, roots, { packageFrames: true });
164940
165768
  const model = this.model("iv", scope, nodes, edges);
164941
165769
  model.frames = frames;
165770
+ if (definitionLayer)
165771
+ model.layers = { definitions: definitionLayer };
164942
165772
  model.meta = { ...model.meta, overview: true, groupMode: "nested" };
164943
165773
  model.root = { ...model.root, keyword: "package", name: nameOf2(scope) ?? model.root.name };
164944
165774
  if (nodes.length === 0 && frames.length === 0) {
@@ -164950,7 +165780,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164950
165780
  // (non-package) single view of `kind`; each renders as one overview tile.
164951
165781
  collectOverviewAnchors(scope, kind, index2) {
164952
165782
  const all = [...ast_utils_exports.streamAllContents(scope)];
164953
- const isNaturalAnchor = (n2) => !isPackage(n2) && !isDocument(n2) && (this.hasAnchorContent(n2, kind, index2) || kind === "afv" && this.isAfvScaffoldAnchor(n2)) && (kind !== "sv" || this.hasSequenceInteraction(n2));
165783
+ const isNaturalAnchor = (n2) => !isPackage(n2) && !isDocument(n2) && ((kind === "gev" ? this.hasGeometryOverviewLayout(n2, index2) : this.hasAnchorContent(n2, kind, index2)) || kind === "afv" && this.isAfvScaffoldAnchor(n2)) && (kind !== "sv" || this.hasSequenceInteraction(n2));
164954
165784
  const naturalAnchors = all.filter(isNaturalAnchor);
164955
165785
  const hasNaturalDescendant = (candidate) => naturalAnchors.some((natural) => {
164956
165786
  for (let cur = natural.$container; cur && cur !== scope; cur = cur.$container) {
@@ -165011,7 +165841,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165011
165841
  const scopeQ = qnameOf(ctx.anchor);
165012
165842
  const collected = this.collectOverviewAnchors(ctx.anchor, kind, ctx.index).sort((a2, b) => (qnameOf(a2) || nameOf2(a2) || "").localeCompare(qnameOf(b) || nameOf2(b) || ""));
165013
165843
  const sequenceScaffold = kind === "sv" && this.nestedUsages(ctx.anchor, isPartDecl).some((n2) => nameOf2(n2) !== void 0);
165014
- const anchors = collected.length > 0 ? collected : kind === "gev" || sequenceScaffold ? [ctx.anchor] : [];
165844
+ const useScopeFallback = kind === "gev" ? this.hasPlacedGeometryObject(ctx.anchor, ctx.index) : sequenceScaffold;
165845
+ const anchors = collected.length > 0 ? collected : useScopeFallback ? [ctx.anchor] : [];
165015
165846
  const nodes = [];
165016
165847
  const edges = [];
165017
165848
  const frames = [];
@@ -167963,18 +168794,31 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
167963
168794
  geo.sizeZ = Math.abs(sizeZ);
167964
168795
  if (radius !== void 0)
167965
168796
  geo.radius = Math.abs(radius);
167966
- const yaw = state.fromFrame ? yawOf(state.world) : 0;
167967
- if (local.rot !== void 0)
168797
+ const localYaw = local.rot === void 0 ? void 0 : rotationTransform([0, 0, 1], local.rot);
168798
+ const orientedWorld = localYaw ? composeTransforms(state.world, localYaw) : state.world;
168799
+ const yaw = yawOf(orientedWorld);
168800
+ if (!state.fromFrame && local.rot !== void 0)
167968
168801
  geo.rot = local.rot;
167969
168802
  else if (yaw !== void 0 && yaw !== 0)
167970
168803
  geo.rot = yaw;
167971
- let approx = state.approx;
167972
- if (yaw === void 0)
167973
- approx ??= "the composed frame rotates this object out of the upright axis; its position is exact, its orientation is not depicted";
168804
+ if (state.fromFrame && !isUnrotated(orientedWorld)) {
168805
+ const r = orientedWorld.r.map(roundGeo);
168806
+ geo.orientation = [
168807
+ r[0],
168808
+ r[1],
168809
+ r[2],
168810
+ r[3],
168811
+ r[4],
168812
+ r[5],
168813
+ r[6],
168814
+ r[7],
168815
+ r[8]
168816
+ ];
168817
+ }
167974
168818
  if (state.fromFrame)
167975
168819
  geo.frame = true;
167976
- if (approx)
167977
- geo.approx = approx;
168820
+ if (state.approx)
168821
+ geo.approx = state.approx;
167978
168822
  return geo;
167979
168823
  }
167980
168824
  // issue #107 — a node's OWN placement relative to its parent frame: the
@@ -168067,7 +168911,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168067
168911
  return {
168068
168912
  present: true,
168069
168913
  transform: translationTransform(origin),
168070
- approx: reoriented ? "its frame declares basisDirections; the position is exact, the re-orientation is not depicted" : void 0,
168914
+ approx: reoriented ? "its frame declares basisDirections that this workspace cannot evaluate numerically" : void 0,
168071
168915
  unit,
168072
168916
  sourcePath
168073
168917
  };
@@ -168624,9 +169468,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168624
169468
  portsOf(node, index2, uri, ownerId, localUsage) {
168625
169469
  const ports = [];
168626
169470
  const map3 = /* @__PURE__ */ new Map();
169471
+ const origins = /* @__PURE__ */ new Map();
168627
169472
  const visibleNames = /* @__PURE__ */ new Set();
168628
169473
  const usageName = effectiveNameOf(node);
168629
- const makePort = (portNode, inheritedFromType, parentPortId, parentPath) => {
169474
+ const makePort = (portNode, inheritedFromType, parentPortId, parentPath, parentOrigin = []) => {
168630
169475
  const pn = effectiveNameOf(portNode);
168631
169476
  if (!pn)
168632
169477
  return void 0;
@@ -168711,7 +169556,9 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168711
169556
  if (usageName)
168712
169557
  map3.set(`${usageName}.${rel2}`, portId);
168713
169558
  map3.set(`${ownerId}.${rel2}`, portId);
168714
- return { port, portTypeDef, inheritedOwners };
169559
+ const origin = [...parentOrigin, portNode];
169560
+ origins.set(portId, origin);
169561
+ return { port, portTypeDef, inheritedOwners, origin };
168715
169562
  };
168716
169563
  const addNested = (portNode, made, inheritedFromType, path10, depth, seenTypes) => {
168717
169564
  if (depth > NESTED_PORT_MAX_DEPTH)
@@ -168722,18 +169569,20 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168722
169569
  const nestedSeen = /* @__PURE__ */ new Set();
168723
169570
  const nestedSources = [
168724
169571
  // declared in this port usage's body: as local as its parent is
168725
- ...membersOf(portNode).map((member) => ({ member, inherited: inheritedFromType })),
169572
+ ...membersOf(portNode).map((member) => ({ member, inherited: inheritedFromType, source: portNode })),
168726
169573
  // every effective owner contributes its body, nearest first
168727
- ...!repeatedType ? made.inheritedOwners.flatMap((owner) => membersOf(owner).map((member) => ({ member, inherited: true }))) : []
169574
+ ...!repeatedType ? made.inheritedOwners.flatMap((owner) => membersOf(owner).map((member) => ({ member, inherited: true, source: owner }))) : []
168728
169575
  ];
168729
- for (const { member, inherited } of nestedSources) {
169576
+ for (const { member, inherited, source } of nestedSources) {
168730
169577
  if (!isPortDecl(member) || member.isDef === true)
168731
169578
  continue;
169579
+ if (inherited && this.isInheritedLibraryBackboneFeature(source, member, index2))
169580
+ continue;
168732
169581
  const nn = effectiveNameOf(member);
168733
169582
  if (!nn || nestedSeen.has(nn))
168734
169583
  continue;
168735
169584
  nestedSeen.add(nn);
168736
- const child = makePort(member, inherited, made.port.id, path10);
169585
+ const child = makePort(member, inherited, made.port.id, path10, made.origin);
168737
169586
  if (!child)
168738
169587
  continue;
168739
169588
  ports.push(child.port);
@@ -168755,11 +169604,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168755
169604
  if (isPortDecl(m) && m.isDef !== true)
168756
169605
  addPort(m, false);
168757
169606
  for (const inherited of this.inheritedFeatureOwnersOf(node, index2)) {
168758
- for (const m of membersOf(inherited))
168759
- if (isPortDecl(m) && m.isDef !== true)
169607
+ for (const m of membersOf(inherited)) {
169608
+ if (isPortDecl(m) && m.isDef !== true && !this.isInheritedLibraryBackboneFeature(inherited, m, index2))
168760
169609
  addPort(m, true);
169610
+ }
168761
169611
  }
168762
- return { ports, map: map3 };
169612
+ return { ports, map: map3, origins };
168763
169613
  }
168764
169614
  // REQ-100, issue #132 — the effective directions of a port type's directed
168765
169615
  // features, following the specialization chain and flipping `in`↔`out` across
@@ -168915,6 +169765,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168915
169765
  } else if (subjectMandatory) {
168916
169766
  add("subject", [{ text: "\u26A0 none declared" }]);
168917
169767
  }
169768
+ add("ends", members.filter(isEndMember).map((m) => item(m, endRowText(m) ?? statementText(m))));
168918
169769
  add("attributes", [
168919
169770
  ...usages(isAttributeDecl).map((a2) => item(a2)),
168920
169771
  ...inheritedRows((member) => isAttributeDecl(member) && isOrdinaryMember(member))
@@ -168924,6 +169775,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168924
169775
  add("ports", usages(isPortDecl).map((m) => item(m)));
168925
169776
  add("parts", usages(isPartDecl).filter(notPortion).map((m) => item(m)));
168926
169777
  }
169778
+ add("connections", members.filter((member) => isConnectionDecl(member) && member.isDef !== true && isOrdinaryMember(member)).map((member) => item(member, this.featureText(member) || "(anonymous connection)")));
169779
+ add("interfaces", members.filter((m) => isInterfaceDecl(m) && m.isDef !== true && isOrdinaryMember(m)).map((m) => item(m, this.interfaceRowText(m))));
168927
169780
  add("features", members.filter((m) => m.$type === "FeatureShorthand" || m.$type === "FeatureRedefinitionShorthand").filter(isOrdinaryMember).filter((m) => !isOccurrenceModified(m)).map((m) => item(m, canonicalFeatureText(m))));
168928
169781
  const undirected = (guard) => usages(guard);
168929
169782
  add("items", undirected(isItemDecl).filter(notPortion).map((m) => item(m)));
@@ -169019,6 +169872,25 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
169019
169872
  add("satisfies", members.filter((m) => m.$type === "SatisfyStmt").map((m) => item(m, String(m.target ?? ""))));
169020
169873
  return out.length ? out : void 0;
169021
169874
  }
169875
+ // REQ-195 — Interface and binding guidance (`interfaces` row text)
169876
+ // issue #110 — the textual row (and node label) of an interface usage:
169877
+ // `name : Type connect a.p to b.q`. The connect ends are kept because this
169878
+ // row is the depiction a reader falls back to when the edge is not drawn, and
169879
+ // without them an anonymous typed usage would read as its type alone. The two
169880
+ // keyword-less shorthands (`interface a.p to b.q;` and an n-ary
169881
+ // `connect (a, b, c)`) are spelled the same way.
169882
+ interfaceRowText(node) {
169883
+ const n2 = node;
169884
+ const type = typeText(node);
169885
+ const mult = multText(node);
169886
+ const name = nameOf2(node) ?? ANONYMOUS_INTERFACE_NAME;
169887
+ const head2 = `${name}${type ? ` : ${type}` : ""}${mult ? ` ${mult}` : ""}`;
169888
+ const clause = n2.connect;
169889
+ const ends = clause?.ends?.length ? `connect (${clause.ends.map((end) => this.connectorEndPath(end) ?? "?").join(", ")})` : clause ? `connect ${this.connectorEndPath(clause.source) ?? "?"} to ${this.connectorEndPath(clause.target) ?? "?"}` : n2.target !== void 0 ? `${[nameOf2(node) ?? "", ...n2.srcSegs ?? []].filter(Boolean).join(".")} to ${this.featurePath(n2.target) ?? "?"}` : void 0;
169890
+ if (ends && !clause)
169891
+ return ends;
169892
+ return [head2, ends].filter(Boolean).join(" ");
169893
+ }
169022
169894
  refText(v) {
169023
169895
  if (typeof v === "string")
169024
169896
  return v;
@@ -169541,91 +170413,6 @@ function outlineGroupForType(astType) {
169541
170413
  return CATEGORY_TO_OUTLINE_GROUP[categoryForType(astType)] ?? "structure";
169542
170414
  }
169543
170415
 
169544
- // ../language-server/out/src/platform/platform.js
169545
- var current;
169546
- function setPlatform(platform) {
169547
- current = platform;
169548
- }
169549
- function getPlatform() {
169550
- if (!current)
169551
- throw new Error("SysML: no platform installed - the entry point must call setPlatform() first.");
169552
- return current;
169553
- }
169554
- function hasPlatform() {
169555
- return current !== void 0;
169556
- }
169557
-
169558
- // ../language-server/out/src/services/library-index-manager.js
169559
- var SysmlIndexManager = class extends DefaultIndexManager {
169560
- constructor(services) {
169561
- super(services);
169562
- }
169563
- // REQ-075, REQ-383 — `libraryRoot` is a URI and the concrete file URIs come
169564
- // from the platform, because the two hosts index the same library under
169565
- // different schemes (`file:` on the desktop, `sysml-lib:` in a worker).
169566
- loadPrecomputedLibraryIndex(index2, libraryRoot) {
169567
- const platform = getPlatform();
169568
- let symbolCount = 0;
169569
- for (const file of index2.files) {
169570
- const documentUri = platform.libraryUri(libraryRoot, file.path);
169571
- const descriptions = file.symbols.map((symbol) => this.deserializeSymbol(symbol, documentUri));
169572
- const uri = documentUri.toString();
169573
- this.symbolIndex.set(uri, descriptions);
169574
- this.symbolByTypeIndex.clear(uri);
169575
- symbolCount += descriptions.length;
169576
- }
169577
- return symbolCount;
169578
- }
169579
- deserializeSymbol(symbol, documentUri) {
169580
- return {
169581
- name: symbol.name,
169582
- type: symbol.type,
169583
- path: symbol.path,
169584
- documentUri,
169585
- nameSegment: symbol.nameSegment,
169586
- selectionSegment: symbol.selectionSegment,
169587
- // REQ-068 — preserve declared visibility for wildcard re-export.
169588
- ...symbol.isPrivate ? { isPrivate: true } : {},
169589
- ...symbol.visibility ? { visibility: symbol.visibility } : {},
169590
- // issue #152 — a re-exported alias is not owned nesting.
169591
- ...symbol.derivedKind ? { derivedKind: symbol.derivedKind } : {},
169592
- // REQ-242 — issue #103 — keeps type completion to definitions.
169593
- ...symbol.isUsage ? { isUsage: true } : {}
169594
- };
169595
- }
169596
- };
169597
- function isSysmlIndexManager(value) {
169598
- return typeof value.loadPrecomputedLibraryIndex === "function";
169599
- }
169600
- var libraryRoots = /* @__PURE__ */ new Set();
169601
- var ROOT_SEPARATOR = "\0";
169602
- function normalizeLibraryPath(p) {
169603
- return p.replace(/\\/gu, "/").replace(/\/+$/u, "").toLowerCase();
169604
- }
169605
- function registerLibraryRoot(root4) {
169606
- const uri = typeof root4 === "string" ? root4.length > 0 ? URI2.file(root4) : void 0 : root4;
169607
- if (!uri)
169608
- return;
169609
- libraryRoots.add(`${uri.scheme}${ROOT_SEPARATOR}${normalizeLibraryPath(uri.path)}`);
169610
- }
169611
- function isInsideDir(fsPath, dir) {
169612
- return fsPath === dir || fsPath.startsWith(`${dir}/`);
169613
- }
169614
- function isStandardLibraryUri(uri) {
169615
- const fsPath = normalizeLibraryPath(uri.path);
169616
- for (const entry of libraryRoots) {
169617
- const separator = entry.indexOf(ROOT_SEPARATOR);
169618
- if (entry.slice(0, separator) !== uri.scheme)
169619
- continue;
169620
- if (isInsideDir(fsPath, entry.slice(separator + 1)))
169621
- return true;
169622
- }
169623
- return fsPath.split("/").some((segment) => segment === "sysml.library");
169624
- }
169625
- function isLibraryDocument(doc) {
169626
- return doc.isLibraryDocument === true || isStandardLibraryUri(doc.uri);
169627
- }
169628
-
169629
170416
  // ../language-server/out/src/services/document-symbol-provider.js
169630
170417
  var SPECIALIZATION_KINDS2 = /* @__PURE__ */ new Set([":>", "specializes"]);
169631
170418
  var REDEFINITION_KINDS = /* @__PURE__ */ new Set([":>>", "redefines"]);
@@ -176474,7 +177261,7 @@ ${baseIndent}}`;
176474
177261
  return;
176475
177262
  const expected = interfaceEndTypes(definition);
176476
177263
  const connect = decl.connect;
176477
- const written = [connect?.source, connect?.target];
177264
+ const written = connect?.ends?.length ? connect.ends : [connect?.source, connect?.target];
176478
177265
  if (expected.length !== written.length)
176479
177266
  return;
176480
177267
  for (let position = 0; position < written.length; position += 1) {
@@ -182421,7 +183208,9 @@ var PORT_GLYPH_SIZE = 20;
182421
183208
  var PORT_GLYPH_HALF = PORT_GLYPH_SIZE / 2;
182422
183209
  var FEATURE_COMPARTMENT_CLEARANCE = PORT_GLYPH_HALF + 12;
182423
183210
  var ACTION_PIN_GLYPH_SIZE = PORT_GLYPH_SIZE;
183211
+ var CONNECTION_END_GLYPH_SIZE = 12;
182424
183212
  function endpointGlyphSize(endpoint) {
183213
+ if (endpoint.meta?.connectionEndPin === true) return CONNECTION_END_GLYPH_SIZE;
182425
183214
  return endpoint.pin ? ACTION_PIN_GLYPH_SIZE : PORT_GLYPH_SIZE;
182426
183215
  }
182427
183216
  var NESTED_PORT_GAP = 8;
@@ -182542,9 +183331,9 @@ function portDockSides(side) {
182542
183331
  }
182543
183332
  var PORT_MOVE_STRIP_ALONG = 24;
182544
183333
  var PORT_CONNECT_DOT_HIT = 10;
182545
- function portInteractionStrip(rect, side) {
183334
+ function portInteractionStrip(rect, side, singleOutwardHandle = false) {
182546
183335
  const vertical = side === "left" || side === "right";
182547
- const across = Math.max(1, rect.across - PORT_CONNECT_DOT_HIT);
183336
+ const across = singleOutwardHandle ? rect.across : Math.max(1, rect.across - PORT_CONNECT_DOT_HIT);
182548
183337
  const run = Math.max(PORT_MOVE_STRIP_ALONG, rect.along);
182549
183338
  return {
182550
183339
  cx: rect.cx,
@@ -182630,6 +183419,10 @@ function sideLength(side, w, h, topReserve = 0) {
182630
183419
  }
182631
183420
  var SENDACCEPT_NOTCH = 14;
182632
183421
  var CIRCULAR_CONTROL_RADII = {
183422
+ // REQ-192 - the n-ary connection hub is the same kind of fixed circular
183423
+ // canvas control as an AFV start node. Its wrapper, handles, saved size,
183424
+ // and painted outline therefore share one exact radius.
183425
+ dot: CONNECTION_END_GLYPH_SIZE / 2,
182633
183426
  initial: 6,
182634
183427
  final: 8,
182635
183428
  terminate: 8
@@ -182853,6 +183646,15 @@ function gvModeEdges(model, mode) {
182853
183646
  function isGvUsageNode(n2) {
182854
183647
  return n2.meta?.gvUsage === true;
182855
183648
  }
183649
+ function isConnectionDefinitionHub(n2) {
183650
+ return n2.meta?.connectionDefinitionHub === true;
183651
+ }
183652
+ function isGraphicalConnectionDefinitionCard(n2) {
183653
+ return n2.meta?.connectionDefinitionGraphical === true && n2.meta?.connectionDefinitionHub !== true && n2.meta?.connectionDefinitionElaboration !== true;
183654
+ }
183655
+ function isGraphicalConnectionDefinitionEdge(edge) {
183656
+ return edge.meta?.connectionDefinitionGraphical === true;
183657
+ }
182856
183658
  var GV_BOXED_USAGE_COMPARTMENTS = /* @__PURE__ */ new Set(["parts", "occurrences", "timeslices", "snapshots", "individuals"]);
182857
183659
  function stripBoxedUsageCompartments(nodes) {
182858
183660
  let changed = false;
@@ -182869,11 +183671,16 @@ function applyGvMode(model, mode) {
182869
183671
  if (model.kind !== "gv") return model;
182870
183672
  const edges = gvModeEdges(model, mode);
182871
183673
  if (mode === "group") {
182872
- const nodes2 = model.nodes.filter((n2) => !isGvUsageNode(n2));
183674
+ const nodes2 = model.nodes.filter((n2) => !isGvUsageNode(n2) && !isConnectionDefinitionHub(n2));
182873
183675
  return { ...model, nodes: nodes2, edges };
182874
183676
  }
182875
- const nodes = stripBoxedUsageCompartments(model.nodes);
182876
- return nodes === model.nodes && edges === model.edges ? model : { ...model, nodes, edges };
183677
+ const cardIsVisible = (node) => !isGraphicalConnectionDefinitionCard(node);
183678
+ const graphicalNodes = model.nodes.every(cardIsVisible) ? model.nodes : model.nodes.filter(cardIsVisible);
183679
+ const nodes = stripBoxedUsageCompartments(graphicalNodes);
183680
+ const nodeIds = new Set(nodes.map((node) => node.id));
183681
+ const edgeIsVisible = (edge) => nodeIds.has(edge.from) && nodeIds.has(edge.to);
183682
+ const visibleEdges = edges.every(edgeIsVisible) ? edges : edges.filter(edgeIsVisible);
183683
+ return nodes === model.nodes && visibleEdges === model.edges ? model : { ...model, nodes, edges: visibleEdges };
182877
183684
  }
182878
183685
  var GV_CATEGORIES = [
182879
183686
  { key: "package", label: "Packages" },
@@ -182893,6 +183700,7 @@ var GV_CATEGORIES = [
182893
183700
  ];
182894
183701
  function gvNodeCategory(node) {
182895
183702
  if (node.shape === "package") return "package";
183703
+ if (isConnectionDefinitionHub(node)) return "interface";
182896
183704
  if (node.shape === "annotation" || node.shape === "dot") return "annotation";
182897
183705
  const k = node.keyword.toLowerCase().trim().replace(/^(individual|timeslice|snapshot|parallel|variation|variant|abstract|ref|derived)(\s+|$)/g, "").replace(/\s+def$/, "").trim();
182898
183706
  if (k === "" || k === "def") return "item";
@@ -182934,13 +183742,13 @@ function withoutOrphanedAnnotations(original, nodes, edges) {
182934
183742
  function applyGvFilters(model, hidden) {
182935
183743
  if (model.kind !== "gv" || hidden.size === 0) return model;
182936
183744
  const nodes = model.nodes.filter((n2) => n2.shape === "package" || !hidden.has(gvNodeCategory(n2)));
182937
- if (nodes.length === model.nodes.length) return model;
182938
183745
  const keptIds = /* @__PURE__ */ new Set();
182939
183746
  for (const n2 of nodes) {
182940
183747
  keptIds.add(n2.id);
182941
183748
  for (const p of n2.ports ?? []) keptIds.add(p.id);
182942
183749
  }
182943
- const edges = model.edges.filter((e) => keptIds.has(e.from) && keptIds.has(e.to));
183750
+ const edges = model.edges.filter((e) => !(hidden.has("interface") && isGraphicalConnectionDefinitionEdge(e)) && keptIds.has(e.from) && keptIds.has(e.to));
183751
+ if (nodes.length === model.nodes.length && edges.length === model.edges.length) return model;
182944
183752
  return { ...model, nodes: withoutOrphanedAnnotations(model, nodes, edges), edges };
182945
183753
  }
182946
183754
  var PAD = 10;
@@ -183026,6 +183834,13 @@ function compartmentWidth(node) {
183026
183834
  return w;
183027
183835
  }
183028
183836
  function nodeForCanvas(node) {
183837
+ if (node.meta?.connectionUsage === true || node.meta?.interfaceUsage === true) {
183838
+ const compartments2 = node.compartments?.filter((compartment) => compartment.title === "doc");
183839
+ return {
183840
+ ...node,
183841
+ compartments: compartments2?.length ? compartments2 : void 0
183842
+ };
183843
+ }
183029
183844
  if (!/\bconstraint(?:\s+def)?$/u.test(node.keyword.trim().toLowerCase())) return node;
183030
183845
  const compartments = node.compartments?.filter((compartment) => compartment.title !== "expression");
183031
183846
  return {
@@ -183063,13 +183878,12 @@ function nodeSize(node, direction) {
183063
183878
  // connector attaching at the node edge meets the visible circle.
183064
183879
  case "initial":
183065
183880
  case "final":
183881
+ case "dot":
183066
183882
  return circularControlSize(node.shape);
183067
183883
  // OMG SysML v2.1 Part 1, Table 15: the terminate control node is an
183068
183884
  // unlabeled circled X, regardless of its optional semantic target.
183069
183885
  case "terminate":
183070
183886
  return circularControlSize(node.shape);
183071
- case "dot":
183072
- return { w: 14, h: 14 };
183073
183887
  case "decision":
183074
183888
  case "merge":
183075
183889
  return { w: Math.max(64, Math.ceil((twKind(node.name) + 8) / 0.7)), h: 48 };
@@ -183109,9 +183923,10 @@ function nodeSize(node, direction) {
183109
183923
  );
183110
183924
  const featureH = nodeFeatureCompartmentReserve(node);
183111
183925
  const compactContainer = node.meta?.internalsHidden === true;
183112
- const portRunHeight = portRows ? compactContainer ? portRows * 24 + 8 : 40 + portRows * 24 + 8 : 0;
183926
+ const connectionUsage = node.meta?.connectionUsage === true || node.meta?.interfaceUsage === true;
183927
+ const portRunHeight = connectionUsage ? portRows ? portRows * 18 + 6 : 0 : portRows ? compactContainer ? portRows * 24 + 8 : 40 + portRows * 24 + 8 : 0;
183113
183928
  const bodyH = Math.max(
183114
- BOX_H,
183929
+ connectionUsage ? 42 : BOX_H,
183115
183930
  featureH === 0 ? NODE_COMPARTMENT_TOP + compartmentHeight(node) : 0,
183116
183931
  portRunHeight,
183117
183932
  portedSideLength(ph, "left"),
@@ -183336,6 +184151,18 @@ function assignPortHandles(ports, overrides, ownerShape) {
183336
184151
  for (const parent of topHandles) placeChildren(parent);
183337
184152
  return [...topHandles, ...nestedHandles];
183338
184153
  }
184154
+ var CONNECTION_BLOCK_GEOMETRY_VERSION = 2;
184155
+ function connectionUsageHeightOverride(node, height, connectionBlockVersion) {
184156
+ if (height === void 0 || node?.meta?.connectionUsage !== true) return height;
184157
+ if ((connectionBlockVersion ?? 0) >= CONNECTION_BLOCK_GEOMETRY_VERSION) return height;
184158
+ const handles = assignPortHandles(node.ports, void 0, node.shape);
184159
+ const rows = Math.max(
184160
+ handles.filter((handle) => handle.side === "left").length,
184161
+ handles.filter((handle) => handle.side === "right").length
184162
+ );
184163
+ const oldAutomaticHeight = Math.max(BOX_H, rows ? rows * 24 + 48 : 0);
184164
+ return height === oldAutomaticHeight ? void 0 : height;
184165
+ }
183339
184166
  function sizeWithOverride(base, o, minimum = { w: 48, h: 24 }) {
183340
184167
  return {
183341
184168
  w: o?.w !== void 0 ? Math.max(minimum.w, o.w) : base.w,
@@ -183452,11 +184279,38 @@ function applyCaseDefs(model, show) {
183452
184279
  };
183453
184280
  }
183454
184281
  var BEHAVIOR_FILTER_DEFAULTS = {
183455
- afv: { parts: true, performers: true, ports: true, defs: true, definedBy: true, actions: true },
183456
- stv: { parts: false, performers: true, ports: true, defs: true, definedBy: true, actions: true },
184282
+ afv: {
184283
+ parts: true,
184284
+ performers: true,
184285
+ ports: true,
184286
+ defs: true,
184287
+ definedBy: true,
184288
+ actions: true,
184289
+ connectionBoxes: true,
184290
+ interfaceBoxes: true
184291
+ },
184292
+ stv: {
184293
+ parts: false,
184294
+ performers: true,
184295
+ ports: true,
184296
+ defs: true,
184297
+ definedBy: true,
184298
+ actions: true,
184299
+ connectionBoxes: true,
184300
+ interfaceBoxes: true
184301
+ },
183457
184302
  // The Interconnection View is a STRUCTURE view; explicitly performed actions
183458
184303
  // are an optional behavior overlay and therefore the first layer to drop.
183459
- iv: { parts: true, performers: true, ports: true, defs: true, definedBy: true, actions: true }
184304
+ iv: {
184305
+ parts: true,
184306
+ performers: true,
184307
+ ports: true,
184308
+ defs: true,
184309
+ definedBy: true,
184310
+ actions: true,
184311
+ connectionBoxes: true,
184312
+ interfaceBoxes: true
184313
+ }
183460
184314
  };
183461
184315
  function behaviorFilterDefaults(kind) {
183462
184316
  return BEHAVIOR_FILTER_DEFAULTS[kind === "stv" ? "stv" : kind === "iv" ? "iv" : "afv"];
@@ -183469,9 +184323,161 @@ function restoreBehaviorFilters(kind, saved) {
183469
184323
  return restored;
183470
184324
  }
183471
184325
  var FILTERED_KINDS = /* @__PURE__ */ new Set(["afv", "stv", "iv"]);
184326
+ function withVisibleDefinitionLayer(model) {
184327
+ const layer = model.layers?.definitions;
184328
+ if (!layer) return model;
184329
+ return {
184330
+ ...model,
184331
+ nodes: [...model.nodes, ...layer.nodes],
184332
+ edges: [...model.edges, ...layer.edges],
184333
+ frames: [...model.frames ?? [], ...layer.frames ?? []],
184334
+ layers: void 0
184335
+ };
184336
+ }
184337
+ function explicitRelationshipBindings(usage, model) {
184338
+ return (usage.ports ?? []).flatMap((port, fallbackOrdinal) => {
184339
+ const edge = model.edges.find((candidate) => candidate.from === port.id || candidate.to === port.id);
184340
+ if (!edge) return [];
184341
+ const pinAtFrom = edge.from === port.id;
184342
+ const ordinal = typeof port.meta?.connectionEndOrdinal === "number" ? port.meta.connectionEndOrdinal : fallbackOrdinal;
184343
+ const role = typeof port.meta?.connectionEndRole === "string" ? port.meta.connectionEndRole : pinAtFrom ? edge.endRoleFrom : edge.endRoleTo;
184344
+ return [{
184345
+ edge,
184346
+ externalId: pinAtFrom ? edge.to : edge.from,
184347
+ ordinal,
184348
+ role,
184349
+ multiplicity: pinAtFrom ? edge.endLabelFrom : edge.endLabelTo,
184350
+ adornment: pinAtFrom ? edge.endAdornmentFrom : edge.endAdornmentTo
184351
+ }];
184352
+ }).sort((left, right) => left.ordinal - right.ordinal);
184353
+ }
184354
+ function hideExplicitRelationshipBoxes(model, relationship) {
184355
+ if (model.kind !== "iv") return model;
184356
+ const usages = model.nodes.filter((node) => relationship === "connection" ? node.meta?.connectionUsage === true : node.meta?.interfaceUsage === true);
184357
+ if (usages.length === 0) return model;
184358
+ const usageIds = new Set(usages.map((usage) => usage.id));
184359
+ const annotatedUsageIds = /* @__PURE__ */ new Set();
184360
+ for (const edge of model.edges) {
184361
+ if (edge.kind !== "annotation") continue;
184362
+ if (usageIds.has(edge.from)) annotatedUsageIds.add(edge.from);
184363
+ if (usageIds.has(edge.to)) annotatedUsageIds.add(edge.to);
184364
+ }
184365
+ const projections = usages.map((usage) => ({
184366
+ usage,
184367
+ bindings: explicitRelationshipBindings(usage, model),
184368
+ endCount: usage.ports?.length ?? 0
184369
+ }));
184370
+ const retainedIds = new Set(projections.filter(({ usage, bindings, endCount }) => (nodeForCanvas(usage).compartments?.length ?? 0) > 0 || annotatedUsageIds.has(usage.id) || endCount < 2 || bindings.length !== endCount).map(({ usage }) => usage.id));
184371
+ const hiddenIds = /* @__PURE__ */ new Set();
184372
+ for (const { usage } of projections) {
184373
+ if (retainedIds.has(usage.id)) continue;
184374
+ hiddenIds.add(usage.id);
184375
+ for (const port of usage.ports ?? []) hiddenIds.add(port.id);
184376
+ }
184377
+ const edges = model.edges.filter((edge) => !hiddenIds.has(edge.from) && !hiddenIds.has(edge.to));
184378
+ const compactNodes = [];
184379
+ const compactEdges = [];
184380
+ for (const { usage, bindings, endCount } of projections) {
184381
+ if (retainedIds.has(usage.id)) continue;
184382
+ const sharedEdgeMeta = { ...bindings[0]?.edge.meta ?? {} };
184383
+ delete sharedEdgeMeta.connectionUsageEnd;
184384
+ delete sharedEdgeMeta.interfaceUsageEnd;
184385
+ delete sharedEdgeMeta.connectionEndOrdinal;
184386
+ delete sharedEdgeMeta.connectionEndRole;
184387
+ const semanticName = typeof usage.meta?.explicitRelationshipName === "string" ? usage.meta.explicitRelationshipName : bindings[0]?.edge.name;
184388
+ const semanticType = bindings[0]?.edge.type ?? usage.type;
184389
+ const label = relationship === "interface" ? ["\xABinterface\xBB", semanticName ?? "", semanticType ? `: ${semanticType}` : ""].filter(Boolean).join(" ").trim() : semanticName;
184390
+ const baseMeta = {
184391
+ ...usage.meta,
184392
+ ...sharedEdgeMeta,
184393
+ connectionUsageId: usage.id,
184394
+ connectionUsageSource: usage.source,
184395
+ ...relationship === "interface" ? { interfaceUsageId: usage.id } : {}
184396
+ };
184397
+ if (endCount <= 2) {
184398
+ if (bindings.length !== 2) continue;
184399
+ const [from, to] = bindings;
184400
+ compactEdges.push({
184401
+ id: from.edge.id,
184402
+ from: from.externalId,
184403
+ to: to.externalId,
184404
+ kind: relationship === "connection" ? "connect" : "interface",
184405
+ name: semanticName,
184406
+ type: semanticType,
184407
+ isDef: false,
184408
+ label,
184409
+ endRoleFrom: from.role,
184410
+ endLabelFrom: from.multiplicity,
184411
+ endAdornmentFrom: from.adornment,
184412
+ endRoleTo: to.role,
184413
+ endLabelTo: to.multiplicity,
184414
+ endAdornmentTo: to.adornment,
184415
+ source: usage.source ?? from.edge.source,
184416
+ meta: {
184417
+ ...baseMeta,
184418
+ ...relationship === "connection" ? { compactConnectionUsage: true } : { compactInterfaceUsage: true },
184419
+ connectionEndFromOrdinal: from.ordinal,
184420
+ connectionEndToOrdinal: to.ordinal,
184421
+ ...from.role ? { connectionEndFromRole: from.role } : {},
184422
+ ...to.role ? { connectionEndToRole: to.role } : {}
184423
+ }
184424
+ });
184425
+ continue;
184426
+ }
184427
+ const hubId = `${usage.id}::__compact_hub`;
184428
+ compactNodes.push({
184429
+ id: hubId,
184430
+ name: "",
184431
+ keyword: relationship,
184432
+ isDef: false,
184433
+ shape: "dot",
184434
+ ...usage.frame ? { frame: usage.frame } : {},
184435
+ source: usage.source,
184436
+ meta: {
184437
+ ...baseMeta,
184438
+ naryConnectionHub: true,
184439
+ ...relationship === "connection" ? { compactConnectionUsageHub: true } : { compactInterfaceUsageHub: true }
184440
+ }
184441
+ });
184442
+ for (const binding of bindings) {
184443
+ compactEdges.push({
184444
+ id: binding.edge.id,
184445
+ from: hubId,
184446
+ to: binding.externalId,
184447
+ kind: relationship === "connection" ? "connect" : "interface",
184448
+ name: semanticName,
184449
+ type: semanticType,
184450
+ isDef: false,
184451
+ label,
184452
+ endRoleTo: binding.role,
184453
+ endLabelTo: binding.multiplicity,
184454
+ endAdornmentTo: binding.adornment,
184455
+ source: usage.source ?? binding.edge.source,
184456
+ meta: {
184457
+ ...baseMeta,
184458
+ ...relationship === "connection" ? { compactConnectionUsageEnd: true } : { compactInterfaceUsageEnd: true },
184459
+ connectionEndOrdinal: binding.ordinal,
184460
+ ...binding.role ? { connectionEndRole: binding.role } : {}
184461
+ }
184462
+ });
184463
+ }
184464
+ }
184465
+ return {
184466
+ ...model,
184467
+ nodes: [...model.nodes.filter((node) => !hiddenIds.has(node.id)), ...compactNodes],
184468
+ edges: [...edges, ...compactEdges]
184469
+ };
184470
+ }
184471
+ function hideExplicitConnectionBoxes(model) {
184472
+ return hideExplicitRelationshipBoxes(model, "connection");
184473
+ }
184474
+ function hideExplicitInterfaceBoxes(model) {
184475
+ return hideExplicitRelationshipBoxes(model, "interface");
184476
+ }
183472
184477
  function applyBehaviorFilters(model, filters) {
183473
184478
  if (!FILTERED_KINDS.has(model.kind)) return model;
183474
- const frames = model.frames ?? [];
184479
+ const visibleModel = filters.defs ? withVisibleDefinitionLayer(model) : model;
184480
+ const frames = visibleModel.frames ?? [];
183475
184481
  const dropped = /* @__PURE__ */ new Set();
183476
184482
  const reparented = /* @__PURE__ */ new Set();
183477
184483
  for (const frame2 of frames) {
@@ -183480,12 +184486,15 @@ function applyBehaviorFilters(model, filters) {
183480
184486
  if (!filters.parts && isOwnerPartFrame || !filters.performers && isPerformerLane) {
183481
184487
  reparented.add(frame2.id);
183482
184488
  }
183483
- if (!filters.defs && frame2.meta?.behaviorDef === true) dropped.add(frame2.id);
184489
+ if (!filters.defs && (frame2.meta?.behaviorDef === true || frame2.meta?.ivPartDef === true)) dropped.add(frame2.id);
183484
184490
  }
183485
- const droppedNodes = new Set(model.nodes.filter((n2) => !filters.parts && n2.meta?.stvPart === true || !filters.defs && n2.meta?.behaviorDef === true || !filters.actions && n2.meta?.ivAction === true).map((n2) => n2.id));
184491
+ const droppedNodes = new Set(visibleModel.nodes.filter((n2) => !filters.parts && n2.meta?.stvPart === true || !filters.defs && (n2.meta?.behaviorDef === true || n2.meta?.ivPartDef === true) || !filters.actions && n2.meta?.ivAction === true).map((n2) => n2.id));
183486
184492
  const strippedPorts = !filters.ports && frames.some((f) => f.ports?.some((p) => p.meta?.afvPartPort === true));
183487
- const strippedDefinedBy = model.kind === "afv" && !filters.definedBy && model.edges.some((edge) => edge.kind === "definedBy");
183488
- if (dropped.size === 0 && reparented.size === 0 && droppedNodes.size === 0 && !strippedPorts && !strippedDefinedBy) return model;
184493
+ const strippedDefinedBy = !filters.definedBy && visibleModel.edges.some((edge) => edge.kind === "definedBy");
184494
+ if (dropped.size === 0 && reparented.size === 0 && droppedNodes.size === 0 && !strippedPorts && !strippedDefinedBy) {
184495
+ const connectionsProjected2 = filters.connectionBoxes ? visibleModel : hideExplicitConnectionBoxes(visibleModel);
184496
+ return filters.interfaceBoxes ? connectionsProjected2 : hideExplicitInterfaceBoxes(connectionsProjected2);
184497
+ }
183489
184498
  const byId = new Map(frames.map((f) => [f.id, f]));
183490
184499
  const insideDropped = (id2) => {
183491
184500
  const seen = /* @__PURE__ */ new Set();
@@ -183516,7 +184525,7 @@ function applyBehaviorFilters(model, filters) {
183516
184525
  const ports = keepPorts(f.ports);
183517
184526
  return parent === f.parent && ports === f.ports ? f : { ...f, parent, ports };
183518
184527
  });
183519
- const nodes = model.nodes.filter((n2) => !droppedNodes.has(n2.id) && !insideDropped(n2.frame) && !dropped.has(n2.frame ?? "")).map((n2) => {
184528
+ const nodes = visibleModel.nodes.filter((n2) => !droppedNodes.has(n2.id) && !insideDropped(n2.frame) && !dropped.has(n2.frame ?? "")).map((n2) => {
183520
184529
  const frame2 = visibleParent(n2.frame);
183521
184530
  return frame2 === n2.frame ? n2 : { ...n2, frame: frame2 };
183522
184531
  });
@@ -183526,8 +184535,15 @@ function applyBehaviorFilters(model, filters) {
183526
184535
  ...nextFrames.map((f) => f.id),
183527
184536
  ...nextFrames.flatMap((f) => (f.ports ?? []).map((p) => p.id))
183528
184537
  ]);
183529
- const edges = model.edges.filter((e) => !(model.kind === "afv" && !filters.definedBy && e.kind === "definedBy") && !hiddenPortIds.has(e.from) && !hiddenPortIds.has(e.to) && liveIds.has(e.from) && liveIds.has(e.to));
183530
- return { ...model, frames: nextFrames, nodes: withoutOrphanedAnnotations(model, nodes, edges), edges };
184538
+ const edges = visibleModel.edges.filter((e) => !(!filters.definedBy && e.kind === "definedBy") && !hiddenPortIds.has(e.from) && !hiddenPortIds.has(e.to) && liveIds.has(e.from) && liveIds.has(e.to));
184539
+ const filtered = {
184540
+ ...visibleModel,
184541
+ frames: nextFrames,
184542
+ nodes: withoutOrphanedAnnotations(visibleModel, nodes, edges),
184543
+ edges
184544
+ };
184545
+ const connectionsProjected = filters.connectionBoxes ? filtered : hideExplicitConnectionBoxes(filtered);
184546
+ return filters.interfaceBoxes ? connectionsProjected : hideExplicitInterfaceBoxes(connectionsProjected);
183531
184547
  }
183532
184548
  function isProxyPort(port) {
183533
184549
  return port.meta?.proxy === true;
@@ -183635,7 +184651,7 @@ function collapseIvModel(model, hiddenInternals) {
183635
184651
  const hidden = /* @__PURE__ */ new Set([...hiddenFrameIds, ...hiddenNodeIds, ...ownerOfHiddenPort.keys()]);
183636
184652
  const containerRootOf = (id2) => proxyHost.get(id2) ?? bodyDock.get(id2);
183637
184653
  const seenEdge = /* @__PURE__ */ new Set();
183638
- const edges = model.edges.filter((e) => {
184654
+ const edges = model.edges.filter((edge) => rootFor(typeof edge.meta?.ivRelationshipOwnerId === "string" ? edge.meta.ivRelationshipOwnerId : void 0) === void 0).filter((e) => {
183639
184655
  const fromRoot = containerRootOf(e.from);
183640
184656
  const toRoot = containerRootOf(e.to);
183641
184657
  if (fromRoot !== void 0 && fromRoot === toRoot) return false;
@@ -184013,15 +185029,24 @@ function modelToFlow(model, opts) {
184013
185029
  const visibleFeatureH = featureCompartmentBandHeight(canvasNode);
184014
185030
  const featureH = nodeFeatureCompartmentReserve(canvasNode);
184015
185031
  const base = isGeo ? { w: 24, h: 24 } : nodeSize(canvasNode, opts.direction);
185032
+ const requiresNaturalCompartmentHeight = visibleFeatureH > 0 || (canvasNode.meta?.connectionUsage === true || canvasNode.meta?.interfaceUsage === true) && !!canvasNode.compartments?.length;
184016
185033
  const sizeOverride = ov[n2.layoutKey ?? n2.id];
184017
185034
  const compactCollapsed = canvasNode.meta?.internalsHidden === true;
184018
- const presentationOverride = compactCollapsed && sizeOverride ? { ...sizeOverride, w: void 0, h: void 0 } : sizeOverride;
185035
+ const compactOverride = compactCollapsed && sizeOverride ? { ...sizeOverride, w: sizeOverride.collapsedW, h: sizeOverride.collapsedH } : sizeOverride;
185036
+ const presentationOverride = compactOverride ? {
185037
+ ...compactOverride,
185038
+ h: connectionUsageHeightOverride(
185039
+ canvasNode,
185040
+ compactOverride.h,
185041
+ compactOverride.connectionBlockVersion
185042
+ )
185043
+ } : compactOverride;
184019
185044
  const projectedSize = isCircularControlShape(canvasNode.shape) ? base : isForkJoinShape(canvasNode.shape) ? forkJoinSizeWithOverride(presentationOverride, opts.direction) : sizeWithOverride(
184020
185045
  base,
184021
185046
  presentationOverride,
184022
185047
  {
184023
185048
  w: canvasNode.meta?.containerControls === true || canvasNode.meta?.internalsHidden === true ? COLLAPSED_CONTAINER_MIN_WIDTH : 48,
184024
- h: visibleFeatureH > 0 ? base.h : 24
185049
+ h: requiresNaturalCompartmentHeight ? base.h : 24
184025
185050
  }
184026
185051
  );
184027
185052
  const compactPortHandles = compactCollapsed ? assignPortHandles(canvasNode.ports, opts.portOverrides, canvasNode.shape) : [];
@@ -184051,7 +185076,7 @@ function modelToFlow(model, opts) {
184051
185076
  w: size.w,
184052
185077
  h: size.h,
184053
185078
  featureCompartmentHeight: featureH,
184054
- featureCompartmentMinHeight: visibleFeatureH > 0 ? base.h : void 0,
185079
+ featureCompartmentMinHeight: requiresNaturalCompartmentHeight ? base.h : void 0,
184055
185080
  showPortLabels: opts.showPortLabels,
184056
185081
  showMult: opts.showMult,
184057
185082
  connectPointSpacing
@@ -184172,18 +185197,27 @@ function modelToFlow(model, opts) {
184172
185197
  const routeKey = edgeRouteKey(e, dup, persistedEndpoints);
184173
185198
  const savedAnchor = opts.edgeAnchors?.[routeKey];
184174
185199
  const settledGeometry = opts.edgeGeometry?.[routeKey];
184175
- const savedSourceHandle = fromPort ? void 0 : canonicalSideHandle(savedAnchor?.sourceHandle ?? settledGeometry?.sourceHandle);
184176
- const savedTargetHandle = toPort ? void 0 : canonicalSideHandle(savedAnchor?.targetHandle ?? settledGeometry?.targetHandle);
184177
- const savedSourcePortSide = parsePortAnchorHandleId(settledGeometry?.sourceHandle)?.side;
184178
- const savedTargetPortSide = parsePortAnchorHandleId(settledGeometry?.targetHandle)?.side;
185200
+ const explicitSourceHandle = fromPort ? void 0 : canonicalSideHandle(savedAnchor?.sourceHandle);
185201
+ const explicitTargetHandle = toPort ? void 0 : canonicalSideHandle(savedAnchor?.targetHandle);
185202
+ const savedSourceHandle = explicitSourceHandle ?? (fromPort ? void 0 : canonicalSideHandle(settledGeometry?.sourceHandle));
185203
+ const savedTargetHandle = explicitTargetHandle ?? (toPort ? void 0 : canonicalSideHandle(settledGeometry?.targetHandle));
185204
+ const portSideForHandle = (ownerId, portId, persistedHandle) => {
185205
+ const portHandle = nodeById.get(ownerId)?.data.ports?.find((handle) => handle.port.id === portId);
185206
+ return portHandle?.port.meta?.connectionEndPin === true ? portHandle.side : parsePortAnchorHandleId(persistedHandle)?.side;
185207
+ };
185208
+ const savedSourcePortSide = fromPort ? portSideForHandle(source, e.from, settledGeometry?.sourceHandle) : void 0;
185209
+ const savedTargetPortSide = toPort ? portSideForHandle(target, e.to, settledGeometry?.targetHandle) : void 0;
185210
+ const sourceHandle = fromPort ? savedSourcePortSide ? portAnchorHandleId(e.from, savedSourcePortSide) : e.from : savedSourceHandle;
185211
+ const targetHandle = toPort ? savedTargetPortSide ? portAnchorHandleId(e.to, savedTargetPortSide) : e.to : savedTargetHandle;
185212
+ const settledHandlesMatch = !settledGeometry || (settledGeometry.sourceHandle === void 0 || settledGeometry.sourceHandle === sourceHandle) && (settledGeometry.targetHandle === void 0 || settledGeometry.targetHandle === targetHandle);
184179
185213
  retainExplicitHandle(source, savedSourceHandle);
184180
185214
  retainExplicitHandle(target, savedTargetHandle);
184181
185215
  edges.push({
184182
185216
  id: e.id,
184183
185217
  source,
184184
185218
  target,
184185
- ...fromPort ? { sourceHandle: savedSourcePortSide ? portAnchorHandleId(e.from, savedSourcePortSide) : e.from } : savedSourceHandle ? { sourceHandle: savedSourceHandle } : {},
184186
- ...toPort ? { targetHandle: savedTargetPortSide ? portAnchorHandleId(e.to, savedTargetPortSide) : e.to } : savedTargetHandle ? { targetHandle: savedTargetHandle } : {},
185219
+ ...sourceHandle ? { sourceHandle } : {},
185220
+ ...targetHandle ? { targetHandle } : {},
184187
185221
  type: "sysml",
184188
185222
  data: {
184189
185223
  kind: model.kind,
@@ -184192,7 +185226,9 @@ function modelToFlow(model, opts) {
184192
185226
  showMult: opts.showMult,
184193
185227
  routeKey,
184194
185228
  route: opts.edgeRoutes?.[routeKey],
184195
- settledGeometry
185229
+ ...explicitSourceHandle ? { explicitSourceAnchor: true } : {},
185230
+ ...explicitTargetHandle ? { explicitTargetAnchor: true } : {},
185231
+ settledGeometry: settledHandlesMatch ? settledGeometry : void 0
184196
185232
  },
184197
185233
  selectable: true,
184198
185234
  reconnectable: true,
@@ -184284,7 +185320,7 @@ function clampDirection(kind, d) {
184284
185320
 
184285
185321
  // ../extension/src/webview/diagram/flow/geo-layout.ts
184286
185322
  var GEO_MARGIN_LEFT = 48;
184287
- var GEO_TOP_PAD = 28;
185323
+ var GEO_TOP_PAD = 40;
184288
185324
  var GEO_TARGET_W = 860;
184289
185325
  var GEO_TARGET_H = 520;
184290
185326
  var GEO_DEF_W = 400;
@@ -184299,33 +185335,110 @@ function geoProject(f, x, y, z2) {
184299
185335
  const isoY = (x + y) * f.sinA - z2;
184300
185336
  return [f.ox + (isoX - f.isoMinX) * f.scale, f.oy + (isoY - f.isoMinY) * f.scale];
184301
185337
  }
184302
- function geoExtentOf(node, def) {
185338
+ function geoWorldPoint(placement, local) {
185339
+ const [lx, ly, lz] = local;
185340
+ const orientation = placement.orientation;
185341
+ if (orientation) {
185342
+ return [
185343
+ placement.x + orientation[0] * lx + orientation[1] * ly + orientation[2] * lz,
185344
+ placement.y + orientation[3] * lx + orientation[4] * ly + orientation[5] * lz,
185345
+ (placement.z ?? 0) + orientation[6] * lx + orientation[7] * ly + orientation[8] * lz
185346
+ ];
185347
+ }
185348
+ if (placement.rot) {
185349
+ const angle = placement.rot * Math.PI / 180;
185350
+ const cos = Math.cos(angle);
185351
+ const sin = Math.sin(angle);
185352
+ return [
185353
+ placement.x + cos * lx - sin * ly,
185354
+ placement.y + sin * lx + cos * ly,
185355
+ (placement.z ?? 0) + lz
185356
+ ];
185357
+ }
185358
+ return [placement.x + lx, placement.y + ly, (placement.z ?? 0) + lz];
185359
+ }
185360
+ function geoHalfExtentsOf(node, def) {
184303
185361
  const g = node.geo;
184304
- const z2 = g.z ?? 0;
184305
185362
  switch (g.shape) {
184306
185363
  case "sphere": {
184307
185364
  const r = g.radius ?? def / 2;
184308
- return { hx: r, hy: r, zlo: z2 - r, zhi: z2 + r };
185365
+ return { hx: r, hy: r, hz: r };
184309
185366
  }
184310
185367
  case "cylinder":
184311
185368
  case "cone": {
184312
185369
  const r = g.radius ?? def / 2;
184313
- const h = g.sizeZ ?? def;
184314
- return { hx: r, hy: r, zlo: z2 - h / 2, zhi: z2 + h / 2 };
185370
+ return { hx: r, hy: r, hz: (g.sizeZ ?? def) / 2 };
184315
185371
  }
184316
185372
  case "pyramid":
184317
185373
  case "wedge":
184318
- case "box": {
184319
- const sx = g.sizeX ?? def;
184320
- const sy = g.sizeY ?? def;
184321
- const sz = g.sizeZ ?? def;
184322
- return { hx: sx / 2, hy: sy / 2, zlo: z2 - sz / 2, zhi: z2 + sz / 2 };
184323
- }
184324
- default: {
184325
- const sx = g.sizeX ?? GEO_DEF_W;
184326
- const sy = g.sizeY ?? GEO_DEF_H;
184327
- return { hx: sx / 2, hy: sy / 2, zlo: z2, zhi: z2 };
185374
+ case "box":
185375
+ return {
185376
+ hx: (g.sizeX ?? def) / 2,
185377
+ hy: (g.sizeY ?? def) / 2,
185378
+ hz: (g.sizeZ ?? def) / 2
185379
+ };
185380
+ default:
185381
+ return { hx: (g.sizeX ?? GEO_DEF_W) / 2, hy: (g.sizeY ?? GEO_DEF_H) / 2, hz: 0 };
185382
+ }
185383
+ }
185384
+ function geoShapePoints(node, def) {
185385
+ const g = node.geo;
185386
+ const { hx, hy, hz } = geoHalfExtentsOf(node, def);
185387
+ const transform2 = (points) => points.map((point) => geoWorldPoint(g, point));
185388
+ const ring = (z2) => Array.from({ length: 32 }, (_2, index2) => {
185389
+ const angle = index2 * Math.PI / 16;
185390
+ return [hx * Math.cos(angle), hy * Math.sin(angle), z2];
185391
+ });
185392
+ switch (g.shape) {
185393
+ case "sphere": {
185394
+ const radius = g.radius ?? def / 2;
185395
+ const cosA = Math.cos(Math.PI / 6);
185396
+ const sinA = Math.sin(Math.PI / 6);
185397
+ const norm = Math.hypot(cosA, cosA);
185398
+ const silhouette = Array.from({ length: 32 }, (_2, index2) => {
185399
+ const angle = index2 * Math.PI / 16;
185400
+ const alongX = [cosA / norm, -cosA / norm, 0];
185401
+ const alongY = [sinA / norm, sinA / norm, -1 / norm];
185402
+ const x = radius * (alongX[0] * Math.cos(angle) + alongY[0] * Math.sin(angle));
185403
+ const y = radius * (alongX[1] * Math.cos(angle) + alongY[1] * Math.sin(angle));
185404
+ const z2 = radius * (alongX[2] * Math.cos(angle) + alongY[2] * Math.sin(angle));
185405
+ return [g.x + x, g.y + y, (g.z ?? 0) + z2];
185406
+ });
185407
+ return [
185408
+ ...silhouette,
185409
+ [g.x - radius, g.y, g.z ?? 0],
185410
+ [g.x + radius, g.y, g.z ?? 0],
185411
+ [g.x, g.y - radius, g.z ?? 0],
185412
+ [g.x, g.y + radius, g.z ?? 0],
185413
+ [g.x, g.y, (g.z ?? 0) - radius],
185414
+ [g.x, g.y, (g.z ?? 0) + radius]
185415
+ ];
184328
185416
  }
185417
+ case "cylinder":
185418
+ return transform2([...ring(-hz), ...ring(hz)]);
185419
+ case "cone":
185420
+ return transform2([...ring(-hz), [0, 0, hz]]);
185421
+ case "pyramid":
185422
+ return transform2([
185423
+ [-hx, -hy, -hz],
185424
+ [hx, -hy, -hz],
185425
+ [hx, hy, -hz],
185426
+ [-hx, hy, -hz],
185427
+ [0, 0, hz]
185428
+ ]);
185429
+ case "wedge":
185430
+ return transform2([
185431
+ [-hx, -hy, -hz],
185432
+ [hx, -hy, -hz],
185433
+ [hx, hy, -hz],
185434
+ [-hx, hy, -hz],
185435
+ [0, -hy, hz],
185436
+ [0, hy, hz]
185437
+ ]);
185438
+ case "box":
185439
+ return transform2([-1, 1].flatMap((x) => [-1, 1].flatMap((y) => [-1, 1].map((z2) => [x * hx, y * hy, z2 * hz]))));
185440
+ default:
185441
+ return transform2([[-hx, -hy, 0], [hx, -hy, 0], [hx, hy, 0], [-hx, hy, 0]]);
184329
185442
  }
184330
185443
  }
184331
185444
  function makeGeoFrameNode(kind, w, h, geo) {
@@ -184405,26 +185518,30 @@ function layoutPlan(kind, placed, strip, axes, edges) {
184405
185518
  function layoutIso(kind, placed, strip, unit, edges) {
184406
185519
  const cosA = Math.cos(Math.PI / 6);
184407
185520
  const sinA = Math.sin(Math.PI / 6);
184408
- let pminX = 0, pmaxX = 0, pminY = 0, pmaxY = 0, pmaxZ = 0;
185521
+ let centreMinX = 0, centreMaxX = 0, centreMinY = 0, centreMaxY = 0;
184409
185522
  for (const n2 of placed) {
184410
185523
  const g = n2.data.node.geo;
184411
- pminX = Math.min(pminX, g.x);
184412
- pmaxX = Math.max(pmaxX, g.x);
184413
- pminY = Math.min(pminY, g.y);
184414
- pmaxY = Math.max(pmaxY, g.y);
184415
- pmaxZ = Math.max(pmaxZ, g.z ?? 0);
185524
+ centreMinX = Math.min(centreMinX, g.x);
185525
+ centreMaxX = Math.max(centreMaxX, g.x);
185526
+ centreMinY = Math.min(centreMinY, g.y);
185527
+ centreMaxY = Math.max(centreMaxY, g.y);
184416
185528
  }
184417
- const span = Math.max(pmaxX - pminX, pmaxY - pminY, 1);
185529
+ const span = Math.max(centreMaxX - centreMinX, centreMaxY - centreMinY, 1);
184418
185530
  const def = Math.max(span * 0.12, 1e-4);
185531
+ let pminX = 0, pmaxX = 0, pminY = 0, pmaxY = 0, pmaxZ = 0;
184419
185532
  let isoMinX = Infinity, isoMaxX = -Infinity, isoMinY = Infinity, isoMaxY = -Infinity;
184420
185533
  const iso = (x, y, z2) => [(x - y) * cosA, (x + y) * sinA - z2];
184421
- const exts = /* @__PURE__ */ new Map();
185534
+ const pointsByNode = /* @__PURE__ */ new Map();
184422
185535
  for (const n2 of placed) {
184423
- const e = geoExtentOf(n2.data.node, def);
184424
- exts.set(n2, e);
184425
- const g = n2.data.node.geo;
184426
- for (const dx of [-e.hx, e.hx]) for (const dy of [-e.hy, e.hy]) for (const z2 of [e.zlo, e.zhi]) {
184427
- const [ix, iy] = iso(g.x + dx, g.y + dy, z2);
185536
+ const points = geoShapePoints(n2.data.node, def);
185537
+ pointsByNode.set(n2, points);
185538
+ for (const [worldX, worldY, worldZ] of points) {
185539
+ pminX = Math.min(pminX, worldX);
185540
+ pmaxX = Math.max(pmaxX, worldX);
185541
+ pminY = Math.min(pminY, worldY);
185542
+ pmaxY = Math.max(pmaxY, worldY);
185543
+ pmaxZ = Math.max(pmaxZ, worldZ);
185544
+ const [ix, iy] = iso(worldX, worldY, worldZ);
184428
185545
  isoMinX = Math.min(isoMinX, ix);
184429
185546
  isoMaxX = Math.max(isoMaxX, ix);
184430
185547
  isoMinY = Math.min(isoMinY, iy);
@@ -184436,11 +185553,9 @@ function layoutIso(kind, placed, strip, unit, edges) {
184436
185553
  const oy = GEO_TOP_PAD + 6;
184437
185554
  const frame3d = { ox, oy, scale, cosA, sinA, isoMinX, isoMinY, minX: pminX, maxX: pmaxX, minY: pminY, maxY: pmaxY, maxZ: pmaxZ, unit, def };
184438
185555
  const outPlaced = placed.map((n2) => {
184439
- const e = exts.get(n2);
184440
- const g = n2.data.node.geo;
184441
185556
  let sxMin = Infinity, syMin = Infinity, sxMax = -Infinity, syMax = -Infinity;
184442
- for (const dx of [-e.hx, e.hx]) for (const dy of [-e.hy, e.hy]) for (const z2 of [e.zlo, e.zhi]) {
184443
- const [px, py] = geoProject(frame3d, g.x + dx, g.y + dy, z2);
185557
+ for (const [worldX, worldY, worldZ] of pointsByNode.get(n2)) {
185558
+ const [px, py] = geoProject(frame3d, worldX, worldY, worldZ);
184444
185559
  sxMin = Math.min(sxMin, px);
184445
185560
  syMin = Math.min(syMin, py);
184446
185561
  sxMax = Math.max(sxMax, px);
@@ -193979,7 +195094,7 @@ function buildEdgeBundles(candidates, obstacles = [], busPositions = {}) {
193979
195094
  const buckets = /* @__PURE__ */ new Map();
193980
195095
  const fanSizes = /* @__PURE__ */ new Map();
193981
195096
  const add = (edge, role) => {
193982
- if (edge.eligible === false) return;
195097
+ if (edge.eligible === false || edge.eligibleRole && edge.eligibleRole !== role) return;
193983
195098
  const pairKey = `${edge.source}\0${edge.target}\0${edge.className}`;
193984
195099
  if ((pairCounts.get(pairKey) ?? 0) > 1) return;
193985
195100
  const commonNodeId = role === "source" ? edge.source : edge.target;
@@ -194683,6 +195798,16 @@ async function elkLayout(elk, graph) {
194683
195798
  }
194684
195799
  }
194685
195800
  function overrideFor(node, overrides) {
195801
+ const stored = storedOverrideFor(node, overrides);
195802
+ if (!stored) return void 0;
195803
+ const compact2 = node.data.node?.meta?.internalsHidden === true || node.data.frame?.meta?.internalsHidden === true;
195804
+ const active = compact2 ? { ...stored, w: stored.collapsedW, h: stored.collapsedH } : stored;
195805
+ return {
195806
+ ...active,
195807
+ h: connectionUsageHeightOverride(node.data.node, active.h, active.connectionBlockVersion)
195808
+ };
195809
+ }
195810
+ function storedOverrideFor(node, overrides) {
194686
195811
  return overrides?.[node.data.layoutKey] ?? overrides?.[node.id];
194687
195812
  }
194688
195813
  var AFV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set([
@@ -195909,7 +197034,8 @@ function semanticPortHandle(node, handle) {
195909
197034
  function validConcretePortAnchor(node, handle) {
195910
197035
  const parsed = parsePortAnchorHandleId(handle);
195911
197036
  const ph = parsed ? portOnNode(node, parsed.portId) : void 0;
195912
- return !!ph && portConnectHandleIds(ph.port.id, ph.side).includes(handle);
197037
+ if (!ph) return false;
197038
+ return ph.port.meta?.connectionEndPin === true ? handle === portAnchorHandleId(ph.port.id, ph.side) : portConnectHandleIds(ph.port.id, ph.side).includes(handle);
195913
197039
  }
195914
197040
  function isStrictDescendant(nodeId, ancestorId, byId) {
195915
197041
  let cur = byId.get(nodeId)?.parentId;
@@ -195924,6 +197050,7 @@ function isStrictDescendant(nodeId, ancestorId, byId) {
195924
197050
  function inferredPortAnchorSide(node, other, portId, byId) {
195925
197051
  const ph = portOnNode(node, portId);
195926
197052
  if (!ph) return "right";
197053
+ if (ph.port.meta?.connectionEndPin === true) return ph.side;
195927
197054
  return other.id === node.id || isStrictDescendant(other.id, node.id, byId) ? OPPOSITE[ph.side] : ph.side;
195928
197055
  }
195929
197056
  function assignPortAnchorSides(nodes, edges) {
@@ -196014,6 +197141,38 @@ function nextSideAnchor(counts, offsetsCache, assignedOffsets, node, side, spaci
196014
197141
  }
196015
197142
  function assignEdgeSides(nodes, edges, topDown = false, connectPointSpacing = CONNECT_POINT_SPACING_DEFAULT, behaviorDirection) {
196016
197143
  const byId = new Map(nodes.map((n2) => [n2.id, n2]));
197144
+ const naryHub = (node) => node?.data.node?.shape === "dot" && node.data.node.meta?.naryConnectionHub === true;
197145
+ const explicitHubAnchors = /* @__PURE__ */ new Map();
197146
+ const retainExplicitHubAnchor = (nodeId, side, offset2) => {
197147
+ const current2 = explicitHubAnchors.get(nodeId) ?? {};
197148
+ const offsets = current2[side] ?? [];
197149
+ if (!offsets.includes(offset2)) current2[side] = [...offsets, offset2].sort((a2, b) => a2 - b);
197150
+ explicitHubAnchors.set(nodeId, current2);
197151
+ };
197152
+ for (const edge of edges) {
197153
+ const sourceAnchor = parseSideAnchorHandleId(edge.sourceHandle);
197154
+ if (sourceAnchor && naryHub(byId.get(edge.source))) {
197155
+ if (edge.data?.explicitSourceAnchor === true) {
197156
+ retainExplicitHubAnchor(edge.source, sourceAnchor.side, sourceAnchor.offset);
197157
+ } else {
197158
+ edge.sourceHandle = sideAnchorHandleId(sourceAnchor.side, 0.5);
197159
+ }
197160
+ }
197161
+ const targetAnchor = parseSideAnchorHandleId(edge.targetHandle);
197162
+ if (targetAnchor && naryHub(byId.get(edge.target))) {
197163
+ if (edge.data?.explicitTargetAnchor === true) {
197164
+ retainExplicitHubAnchor(edge.target, targetAnchor.side, targetAnchor.offset);
197165
+ } else {
197166
+ edge.targetHandle = sideAnchorHandleId(targetAnchor.side, 0.5);
197167
+ }
197168
+ }
197169
+ }
197170
+ for (const node of nodes) {
197171
+ const explicit = explicitHubAnchors.get(node.id);
197172
+ if (naryHub(node) && (node.data.explicitSideAnchors || explicit)) {
197173
+ node.data = { ...node.data, explicitSideAnchors: explicit };
197174
+ }
197175
+ }
196017
197176
  const sideCounts = /* @__PURE__ */ new Map();
196018
197177
  const offsetsCache = /* @__PURE__ */ new Map();
196019
197178
  const assignedOffsets = /* @__PURE__ */ new Map();
@@ -196033,7 +197192,7 @@ function assignEdgeSides(nodes, edges, topDown = false, connectPointSpacing = CO
196033
197192
  const requests = /* @__PURE__ */ new Map();
196034
197193
  const requestPreference = /* @__PURE__ */ new Map();
196035
197194
  const requestKey = (edge, role) => `${edge.id}|${role}`;
196036
- const anchor = (node, side, preferred) => nextSideAnchor(sideCounts, offsetsCache, assignedOffsets, node, side, connectPointSpacing, demand, preferred);
197195
+ const anchor = (node, side, preferred) => naryHub(node) ? sideAnchorHandleId(side, 0.5) : nextSideAnchor(sideCounts, offsetsCache, assignedOffsets, node, side, connectPointSpacing, demand, preferred);
196037
197196
  const diamondPreference = (node, side, dx, dy) => {
196038
197197
  const shape = node.data.node?.shape;
196039
197198
  if (shape !== "decision" && shape !== "merge") return void 0;
@@ -197027,6 +198186,50 @@ function routePointNearSource(points, source, target, distance2) {
197027
198186
  }
197028
198187
  return void 0;
197029
198188
  }
198189
+ function connectionEndLabelLines(edge, side, showMultiplicity) {
198190
+ const semanticRole = side === "from" ? edge.endRoleFrom : edge.endRoleTo;
198191
+ const multiplicity = side === "from" ? edge.endLabelFrom : edge.endLabelTo;
198192
+ let adornment = side === "from" ? edge.endAdornmentFrom : edge.endAdornmentTo;
198193
+ const namedPinEnd = side === "from" && (edge.meta?.connectionUsageEnd === true || edge.meta?.interfaceUsageEnd === true);
198194
+ if (namedPinEnd && semanticRole && adornment) {
198195
+ const impliedRedefinition = `redefines ${semanticRole}`;
198196
+ if (adornment === impliedRedefinition) {
198197
+ adornment = void 0;
198198
+ } else if (adornment.endsWith(` ${impliedRedefinition}`)) {
198199
+ adornment = adornment.slice(0, -(impliedRedefinition.length + 1)).trim() || void 0;
198200
+ }
198201
+ }
198202
+ const role = namedPinEnd ? void 0 : semanticRole;
198203
+ return [role, showMultiplicity ? multiplicity : void 0, adornment].filter((line2) => !!line2);
198204
+ }
198205
+ function connectionEndLabelPoint(points, source, target, side) {
198206
+ const reversed = side === "to" ? [...points ?? []].reverse() : points;
198207
+ const start2 = side === "to" ? target : source;
198208
+ const finish = side === "to" ? source : target;
198209
+ const near = routePointNearSource(reversed, start2, finish, 24);
198210
+ if (!near) return start2;
198211
+ return {
198212
+ x: markCoordinate(near.point.x - near.tangent.y * 12),
198213
+ y: markCoordinate(near.point.y + near.tangent.x * 12)
198214
+ };
198215
+ }
198216
+ function connectionElaborationGeometry(start2, target) {
198217
+ const cx = target.x + target.width / 2;
198218
+ const cy = target.y + target.height / 2;
198219
+ const dx = start2.x - cx;
198220
+ const dy = start2.y - cy;
198221
+ const scale = Math.max(
198222
+ Math.abs(dx) / Math.max(target.width / 2, 1),
198223
+ Math.abs(dy) / Math.max(target.height / 2, 1),
198224
+ 1
198225
+ );
198226
+ const end = { x: cx + dx / scale, y: cy + dy / scale };
198227
+ return {
198228
+ start: start2,
198229
+ target: end,
198230
+ path: `M ${markCoordinate(start2.x)} ${markCoordinate(start2.y)} L ${markCoordinate(end.x)} ${markCoordinate(end.y)}`
198231
+ };
198232
+ }
197030
198233
  function edgeLabelPoint(edge, centre, mark, points, source, target) {
197031
198234
  const branchLabel = edge.kind === "succession" && (edge.label === "else" || /^\[.*\]$/u.test(edge.label ?? ""));
197032
198235
  if (!branchLabel) return successionFlowLabelPoint(centre, mark);
@@ -197169,13 +198372,25 @@ function treeFacingDock(node, other, byId) {
197169
198372
  const normal = otherCentre.y >= centre.y ? DOCK_NORMAL.bottom : DOCK_NORMAL.top;
197170
198373
  return { point: { x: centre.x, y: centre.y + normal.y * h / 2 }, normal };
197171
198374
  }
198375
+ function naryFacingDock(node, other, byId) {
198376
+ const from = nodeCentre(node, byId);
198377
+ const to = nodeCentre(other, byId);
198378
+ const dx = to.x - from.x;
198379
+ const dy = to.y - from.y;
198380
+ const side = Math.abs(dx) >= Math.abs(dy) ? dx >= 0 ? "right" : "left" : dy >= 0 ? "bottom" : "top";
198381
+ return dockPoint(node, sideAnchorHandleId(side, 0.5), byId, from);
198382
+ }
198383
+ function isNaryConnectionHub(node) {
198384
+ return node?.data.node?.shape === "dot" && node.data.node.meta?.naryConnectionHub === true;
198385
+ }
197172
198386
  function bundleCandidateFor(edge, byId) {
197173
198387
  const semantic = edge.data?.edge;
197174
198388
  const sourceNode = byId.get(edge.source);
197175
198389
  const targetNode = byId.get(edge.target);
197176
198390
  if (!semantic || !sourceNode || !targetNode) return void 0;
197177
- const sourceFallback = treeFacingDock(sourceNode, targetNode, byId);
197178
- const targetFallback = treeFacingDock(targetNode, sourceNode, byId);
198391
+ const naryRole = semantic.kind === "connect" ? isNaryConnectionHub(sourceNode) ? "source" : isNaryConnectionHub(targetNode) ? "target" : void 0 : void 0;
198392
+ const sourceFallback = naryRole ? naryFacingDock(sourceNode, targetNode, byId) : treeFacingDock(sourceNode, targetNode, byId);
198393
+ const targetFallback = naryRole ? naryFacingDock(targetNode, sourceNode, byId) : treeFacingDock(targetNode, sourceNode, byId);
197179
198394
  const explicitSource = dockPoint(sourceNode, edge.sourceHandle, byId, sourceFallback.point);
197180
198395
  const explicitTarget = dockPoint(targetNode, edge.targetHandle, byId, targetFallback.point);
197181
198396
  const source = explicitSource.normal ? explicitSource : sourceFallback;
@@ -197198,7 +198413,8 @@ function bundleCandidateFor(edge, byId) {
197198
198413
  // remaining separate semantic edges. Once one connector owns manual
197199
198414
  // waypoints it deliberately leaves that automatic fan and keeps its
197200
198415
  // individually movable route.
197201
- eligible: edge.data?.kind === "gv" && GV_BUS_EDGE_KINDS.has(semantic.kind) && edge.source !== edge.target && !edge.data.route?.length && !isPortDocked(edge.sourceHandle) && !isPortDocked(edge.targetHandle)
198416
+ eligible: edge.source !== edge.target && !edge.data?.route?.length && (naryRole !== void 0 || edge.data?.kind === "gv" && GV_BUS_EDGE_KINDS.has(semantic.kind) && !isPortDocked(edge.sourceHandle) && !isPortDocked(edge.targetHandle)),
198417
+ ...naryRole ? { eligibleRole: naryRole } : {}
197202
198418
  };
197203
198419
  }
197204
198420
  function movingNodeIds(nodes, byId) {
@@ -197371,7 +198587,7 @@ function edgeBundles(nodes, edges, runtime = defaultBundleRuntime) {
197371
198587
  function projectedBundleMember(edge) {
197372
198588
  return edge.data?.bundleMember;
197373
198589
  }
197374
- function projectIndividualGvTreeRenderEdges(nodes, semanticEdges, runtime) {
198590
+ function projectAutomaticBusRenderEdges(nodes, semanticEdges, runtime) {
197375
198591
  const bundles = edgeBundles(nodes, semanticEdges, runtime);
197376
198592
  if (!bundles.size) return semanticEdges;
197377
198593
  return semanticEdges.map((edge) => {
@@ -197985,6 +199201,7 @@ body {
197985
199201
  /* REQ-186/188/189/199/206/210 \u2014 the dashed labelled families */
197986
199202
  .dlink.allocate, .dlink.frame, .dlink.derive, .dlink.causation, .dlink.import, .dlink.expose { stroke-dasharray: 4 3; }
197987
199203
  .dlink.annotation { stroke-dasharray: 2 3; }
199204
+ .dlink.elaboration { stroke-dasharray: 2 3; pointer-events: none; }
197988
199205
  .dlink.reply { stroke-dasharray: 4 3; } /* REQ-202 \u2014 sequence reply arrows */
197989
199206
  /* issue #84 \u2014 REQ-202: a message is dragged up/down to change WHEN it happens.
197990
199207
  The grip is the invisible hit band over the arrow (a row-resize cursor is the
@@ -198023,6 +199240,15 @@ body {
198023
199240
  * on rather than the flow colour (user direction 2026-08-09). Its rounded corners and its
198024
199241
  * direction arrow come from the shared port path in nodes.tsx. */
198025
199242
  .dnode-port.pin { fill: var(--element-fill); stroke: var(--element-outline); stroke-width: 1.2; }
199243
+ /* REQ-192 - explicit connection ends are outline-coloured endpoint dots, not
199244
+ * action-pin boxes. The dot itself is the persistent snap affordance. */
199245
+ .dconnection-end-dot { fill: var(--element-outline); stroke: var(--element-outline); stroke-width: 1.2; }
199246
+ .dconnection-end-dot.selected { fill: var(--accent); stroke: var(--accent); }
199247
+ .rf-node.selected .dnode-control.dot { fill: var(--accent); stroke: var(--accent); }
199248
+ .dconnection-end-dot.hovered, .dconnection-end-dot.connect-target {
199249
+ fill: var(--cyan); stroke: var(--cyan); filter: drop-shadow(0 0 3px var(--cyan));
199250
+ }
199251
+ .dconnection-end-dot.moving { fill: var(--accent); stroke: var(--accent); filter: drop-shadow(0 0 4px var(--accent)); }
198026
199252
  /* REQ-194 \u2014 the port direction ARROW: a shaft across the glyph with a solid head
198027
199253
  * at each directed end (--> out, <-- in, <-> inout), per OMG 8.2.3.12.
198028
199254
  * NOTE: no backticks in this file's CSS \u2014 STYLE is a template literal. */
@@ -198187,15 +199413,19 @@ body {
198187
199413
  /* REQ-205 \u2014 Geometry View 3D (isometric) shapes + ground frame */
198188
199414
  .dgeo3d-grid { stroke-width: 1; opacity: 0.35; }
198189
199415
  .dgeo3d-face { stroke: var(--element-outline); stroke-width: 1; stroke-linejoin: round; }
198190
- .dgeo3d-face.top { fill: var(--element-fill); fill-opacity: 0.56; }
198191
- .dgeo3d-face.side { fill: var(--element-fill); fill-opacity: 0.38; }
198192
- .dgeo3d-face.side2 { fill: var(--element-fill); fill-opacity: 0.24; }
199416
+ .dgeo3d-face.top { fill: var(--element-fill); fill-opacity: 0.42; }
199417
+ .dgeo3d-face.side { fill: var(--element-fill); fill-opacity: 0.28; }
199418
+ .dgeo3d-face.side2 { fill: var(--element-fill); fill-opacity: 0.18; }
198193
199419
  .dgeo3d-edge { stroke: var(--element-outline); stroke-width: 1; opacity: 0.7; }
198194
199420
  .dgeo3d-node { cursor: pointer; }
198195
199421
  .dgeo3d-node:hover .dgeo3d-face { fill-opacity: 0.42; }
198196
199422
  .dgeo3d-node.selected .dgeo3d-face { stroke: var(--accent); }
198197
199423
  .dgeo3d-node.selected .dgeo3d-face.top { fill: var(--accent); fill-opacity: 0.34; }
198198
- .dgeo3d-name { fill: var(--fg-0); font-family: var(--font-mono); font-size: 10px; }
199424
+ .dgeo3d-name {
199425
+ fill: var(--fg-0); stroke: var(--diagram-canvas); stroke-width: 3px;
199426
+ paint-order: stroke fill; stroke-linejoin: round;
199427
+ font-family: var(--font-mono); font-size: 10px;
199428
+ }
198199
199429
  /* REQ-205 \u2014 an object whose coordinate-frame transformation could not be fully
198200
199430
  decided is DRAWN and MARKED, never silently misplaced: its outline goes dashed
198201
199431
  and it carries a warning glyph whose tooltip names the reason. (The issue
@@ -198772,14 +200002,17 @@ svg.react-flow__connectionline { z-index: 1; }
198772
200002
  * Each 5px dot has a compact hit box and can start or end a connector. The chosen
198773
200003
  * handle is the chosen dock point, while the central body stays selectable. */
198774
200004
  .react-flow__handle.rf-handle-port { width: ${PORT_CONNECT_DOT_HIT}px; height: ${PORT_CONNECT_DOT_HIT}px; --rf-dot-size: 5px; z-index: 6; }
200005
+ .react-flow__handle.rf-handle-connection-end { width: 12px; height: 12px; z-index: 6; }
200006
+ .react-flow__handle.rf-handle-connection-end::after { display: none; }
198775
200007
  .react-flow__handle.rf-handle-side { width: ${BODY_SNAP_POINT_HIT_SIZE}px; height: ${BODY_SNAP_POINT_HIT_SIZE}px; --rf-dot-size: 5px; z-index: 2; }
198776
200008
  /* REQ-370: body snap points are hollow circles. Port and pin endpoints are
198777
200009
  * solid squares, so adjacent targets cannot be mistaken for each other. */
198778
200010
  .react-flow__handle.rf-handle-side::after { background: var(--bg-1); border: 1.5px solid var(--cyan); }
198779
200011
  .react-flow__handle.rf-handle-port::after { border-radius: 1px; }
198780
- /* The start/done centre stays available for normal node selection and dragging
198781
- * even though four 12px handle targets meet over these exact-size glyphs. The
198782
- * perimeter dots remain above the rest of the node and keep their full hit area. */
200012
+ /* The start, done, and n-ary hub centre stays available for normal node
200013
+ * selection and dragging even though four 12px handle targets meet over these
200014
+ * exact-size glyphs. The perimeter dots remain above the rest of the node and
200015
+ * keep their full hit area. */
198783
200016
  .rf-circular-control-hit {
198784
200017
  position: absolute; left: 50%; top: 50%; width: 50%; height: 50%;
198785
200018
  transform: translate(-50%, -50%); border-radius: 50%;
@@ -198793,6 +200026,7 @@ svg.react-flow__connectionline { z-index: 1; }
198793
200026
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-side.connectionindicator,
198794
200027
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-lifeline.connectionindicator { opacity: 0.42; }
198795
200028
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-port.connectionindicator { opacity: 0.42; }
200029
+ .react-flow.sysml-connecting .react-flow__handle.rf-handle-connection-end.connectionindicator { opacity: 0.42; }
198796
200030
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-port.active { opacity: 1; }
198797
200031
  /* The nearest semantically valid body target is stronger than the candidate
198798
200032
  * points. Port and pin targets use the highlighted endpoint glyph instead. */
@@ -198943,6 +200177,9 @@ svg.react-flow__connectionline { z-index: 1; }
198943
200177
  .rf-edge-label { position: absolute; pointer-events: none; font-family: var(--font-mono); font-size: 9px;
198944
200178
  color: var(--fg-0); background: transparent; border: 0; padding: 0; white-space: nowrap; }
198945
200179
  .rf-edge-label.mult { background: transparent; border: 0; color: var(--fg-2); padding: 0; }
200180
+ .rf-edge-label.connection-end { color: var(--fg-1); display: flex; flex-direction: column;
200181
+ line-height: 1.15; text-align: center; white-space: nowrap; }
200182
+ .rf-edge-label.connection-end span:nth-child(n+2) { color: var(--fg-2); }
198946
200183
 
198947
200184
  /* REQ-366/386 \u2014 two adjacent, independent container presentation controls.
198948
200185
  * +/\u2212 owns graphical internals; the outlined list owns textual feature
@@ -199025,6 +200262,11 @@ function labelExtent(text) {
199025
200262
  function labelSvg(text, x, y, cls) {
199026
200263
  return `<text class="${cls}" x="${Math.round(x)}" y="${Math.round(y)}" text-anchor="middle" dominant-baseline="central">${escapeXml(text)}</text>`;
199027
200264
  }
200265
+ function multilineLabelSvg(lines, x, y, cls) {
200266
+ const firstY = y - (lines.length - 1) * 5.5;
200267
+ const spans = lines.map((line2, index2) => `<tspan x="${Math.round(x)}" dy="${index2 === 0 ? 0 : 11}">${escapeXml(line2)}</tspan>`).join("");
200268
+ return `<text class="${cls}" x="${Math.round(x)}" y="${Math.round(firstY)}" text-anchor="middle" dominant-baseline="central">${spans}</text>`;
200269
+ }
199028
200270
  function buildDiagramSvg(options) {
199029
200271
  const { nodes, edges, lineStyle, nodeMarkup } = options;
199030
200272
  if (!nodes.length) return null;
@@ -199103,6 +200345,22 @@ function buildDiagramSvg(options) {
199103
200345
  const markerStart = style2.markerStart ? ` marker-start="${style2.markerStart}"` : "";
199104
200346
  const markerEnd = style2.markerEnd ? ` marker-end="${style2.markerEnd}"` : "";
199105
200347
  edgeParts.push(`<path class="${style2.className}" d="${geometry.path}"${markerStart}${markerEnd}/>`);
200348
+ if (semantic?.elaboration) {
200349
+ const targetNode = byId.get(semantic.elaboration);
200350
+ if (targetNode) {
200351
+ const position = absolutePos(targetNode.id, byId);
200352
+ const size = sizeOf(targetNode);
200353
+ const elaboration = connectionElaborationGeometry(geometry.label, {
200354
+ x: position.x,
200355
+ y: position.y,
200356
+ width: size.w,
200357
+ height: size.h
200358
+ });
200359
+ edgeParts.push(`<path class="dlink elaboration" d="${elaboration.path}"/>`);
200360
+ extendPoint(elaboration.start.x, elaboration.start.y, 2);
200361
+ extendPoint(elaboration.target.x, elaboration.target.y, 2);
200362
+ }
200363
+ }
199106
200364
  const sequencingMark = successionFlowMarkGeometry(
199107
200365
  style2,
199108
200366
  geometry.label,
@@ -199130,14 +200388,22 @@ function buildDiagramSvg(options) {
199130
200388
  labelParts.push(labelSvg(semantic.label, labelPoint.x, labelPoint.y, `elabel ${semantic.kind}`));
199131
200389
  extendLabel(semantic.label, labelPoint.x, labelPoint.y);
199132
200390
  }
199133
- if (edge.data?.showMult) {
199134
- if (semantic?.endLabelFrom) {
199135
- labelParts.push(labelSvg(semantic.endLabelFrom, geometry.source.x, geometry.source.y, "elabel mult"));
199136
- extendLabel(semantic.endLabelFrom, geometry.source.x, geometry.source.y);
199137
- }
199138
- if (semantic?.endLabelTo) {
199139
- labelParts.push(labelSvg(semantic.endLabelTo, geometry.target.x, geometry.target.y, "elabel mult"));
199140
- extendLabel(semantic.endLabelTo, geometry.target.x, geometry.target.y);
200391
+ if (semantic) {
200392
+ for (const side of ["from", "to"]) {
200393
+ const lines = connectionEndLabelLines(semantic, side, edge.data?.showMult === true);
200394
+ if (!lines.length) continue;
200395
+ const point = connectionEndLabelPoint(
200396
+ geometry.points ?? geometry.anchors,
200397
+ geometry.source,
200398
+ geometry.target,
200399
+ side
200400
+ );
200401
+ const role = side === "from" ? semantic.endRoleFrom : semantic.endRoleTo;
200402
+ const adornment = side === "from" ? semantic.endAdornmentFrom : semantic.endAdornmentTo;
200403
+ labelParts.push(!role && !adornment && lines.length === 1 ? labelSvg(lines[0], point.x, point.y, "elabel mult") : multilineLabelSvg(lines, point.x, point.y, "elabel connection-end"));
200404
+ const widest = lines.reduce((longest, line2) => line2.length > longest.length ? line2 : longest, "");
200405
+ extendLabel(widest, point.x, point.y);
200406
+ extendPoint(point.x, point.y, lines.length * 6);
199141
200407
  }
199142
200408
  }
199143
200409
  }
@@ -199606,6 +200872,7 @@ var TOOLBOX = {
199606
200872
  elements: [
199607
200873
  el("part", "part", "\u25A2"),
199608
200874
  el("port", "port", "\u25AB"),
200875
+ el("connection", "connection", "\u25AD"),
199609
200876
  el("attribute", "attribute", "\u2013"),
199610
200877
  el("item", "item", "\u25C7"),
199611
200878
  el("constraint", "constraint", "{}"),
@@ -199770,7 +201037,7 @@ var COMMON_USAGE_KINDS = [
199770
201037
  var CASE_CHILDREN = ["subject", "actor", "part", "attribute", "action", "item", "requirement"];
199771
201038
  var CHILD_KINDS = {
199772
201039
  package: ["package", ...DEFINITION_KINDS, ...COMMON_USAGE_KINDS],
199773
- part: ["part", "attribute", "port", "item", "constraint", "action", "state", "calc", "occurrence"],
201040
+ part: ["part", "attribute", "port", "item", "connection", "constraint", "action", "state", "calc", "occurrence"],
199774
201041
  item: ["attribute", "port", "part", "item"],
199775
201042
  // REQ-006/REQ-192 — a port owns the features that describe what crosses
199776
201043
  // its boundary. Keep the direction in the creation choice so an IV user
@@ -199875,6 +201142,7 @@ function isWritableRelationEndpoint(endpoint) {
199875
201142
  }
199876
201143
  function directRelationForHandle(viewKind, endpoint) {
199877
201144
  if (!endpoint || !isWritableRelationEndpoint(endpoint)) return void 0;
201145
+ if (viewKind === "iv" && (endpoint.connectionEnd || normalizedElementKeyword(endpoint.keyword).base === "connection end")) return "connectionEnd";
199878
201146
  return relationToolsForNode(endpoint.keyword, viewKind, endpoint.shape).find((tool) => tool.kind !== "terminate" && tool.kind !== "finish")?.kind;
199879
201147
  }
199880
201148
  var START_PSEUDOSTATE_BASES = /* @__PURE__ */ new Set(["start", "initial"]);
@@ -199965,6 +201233,65 @@ function relationToolsForNode(keyword, viewKind, shape) {
199965
201233
  var import_react7 = __toESM(require_react());
199966
201234
  var import_jsx_runtime3 = __toESM(require_jsx_runtime());
199967
201235
  var ptStr = (pts) => pts.map((p) => `${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(" ");
201236
+ var GEO_VIEW_DIRECTION = [1, 1, 1];
201237
+ function dot3(a2, b) {
201238
+ return a2[0] * b[0] + a2[1] * b[1] + a2[2] * b[2];
201239
+ }
201240
+ function cross3(a2, b) {
201241
+ return [
201242
+ a2[1] * b[2] - a2[2] * b[1],
201243
+ a2[2] * b[0] - a2[0] * b[2],
201244
+ a2[0] * b[1] - a2[1] * b[0]
201245
+ ];
201246
+ }
201247
+ function subtract3(a2, b) {
201248
+ return [a2[0] - b[0], a2[1] - b[1], a2[2] - b[2]];
201249
+ }
201250
+ function convexHull(points) {
201251
+ const sorted = [...points].sort((a2, b) => a2[0] - b[0] || a2[1] - b[1]);
201252
+ if (sorted.length <= 2) return sorted;
201253
+ const turn = (a2, b, c) => (b[0] - a2[0]) * (c[1] - a2[1]) - (b[1] - a2[1]) * (c[0] - a2[0]);
201254
+ const half = (input) => {
201255
+ const result = [];
201256
+ for (const point of input) {
201257
+ while (result.length >= 2 && turn(result.at(-2), result.at(-1), point) <= 0) result.pop();
201258
+ result.push(point);
201259
+ }
201260
+ return result;
201261
+ };
201262
+ const lower2 = half(sorted);
201263
+ const upper = half([...sorted].reverse());
201264
+ return [...lower2.slice(0, -1), ...upper.slice(0, -1)];
201265
+ }
201266
+ function polyhedronBody(node, f, localVertices, faces) {
201267
+ const g = node.geo;
201268
+ const center = geoWorldPoint(g, [0, 0, 0]);
201269
+ const world = localVertices.map((point) => geoWorldPoint(g, point));
201270
+ const visible = faces.flatMap((face, index2) => {
201271
+ let points = face.map((vertex) => world[vertex]);
201272
+ let normal = cross3(subtract3(points[1], points[0]), subtract3(points[2], points[0]));
201273
+ const faceCenter = [
201274
+ points.reduce((sum, point) => sum + point[0], 0) / points.length,
201275
+ points.reduce((sum, point) => sum + point[1], 0) / points.length,
201276
+ points.reduce((sum, point) => sum + point[2], 0) / points.length
201277
+ ];
201278
+ if (dot3(normal, subtract3(faceCenter, center)) < 0) {
201279
+ points = [...points].reverse();
201280
+ normal = [-normal[0], -normal[1], -normal[2]];
201281
+ }
201282
+ const normalLength = Math.hypot(normal[0], normal[1], normal[2]);
201283
+ if (normalLength === 0 || dot3(normal, GEO_VIEW_DIRECTION) / (normalLength * Math.sqrt(3)) <= 1e-9) return [];
201284
+ const absolute = normal.map(Math.abs);
201285
+ const faceClass = absolute[2] >= absolute[0] && absolute[2] >= absolute[1] && normal[2] > 0 ? "top" : absolute[0] >= absolute[1] ? "side2" : "side";
201286
+ return [{
201287
+ key: index2,
201288
+ faceClass,
201289
+ depth: points.reduce((sum, point) => sum + dot3(point, GEO_VIEW_DIRECTION), 0) / points.length,
201290
+ points: points.map((point) => geoProject(f, point[0], point[1], point[2]))
201291
+ }];
201292
+ }).sort((a2, b) => a2.depth - b.depth);
201293
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { children: visible.map((face) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: `dgeo3d-face ${face.faceClass}`, points: ptStr(face.points) }, face.key)) });
201294
+ }
199968
201295
  var GEO_HANDLE_POSITION = {
199969
201296
  top: Position3.Top,
199970
201297
  right: Position3.Right,
@@ -200087,15 +201414,16 @@ function IsoAxes({ f, marker }) {
200087
201414
  }
200088
201415
  function geoShapeBody(node, f) {
200089
201416
  const g = node.geo;
200090
- const e = geoExtentOf(node, f.def);
201417
+ const { hx, hy, hz } = geoHalfExtentsOf(node, f.def);
200091
201418
  const P = (x, y, z2) => geoProject(f, x, y, z2);
200092
- const { hx, hy, zlo, zhi } = e;
200093
- const cx = g.x;
200094
- const cy = g.y;
201419
+ const PL = (point) => {
201420
+ const world = geoWorldPoint(g, point);
201421
+ return P(world[0], world[1], world[2]);
201422
+ };
200095
201423
  switch (g.shape) {
200096
201424
  case "sphere": {
200097
- const c = P(cx, cy, (zlo + zhi) / 2);
200098
- const pts = [P(cx + hx, cy, e.zlo), P(cx - hx, cy, e.zlo), P(cx, cy + hy, e.zlo), P(cx, cy - hy, e.zlo), P(cx, cy, zhi), P(cx, cy, zlo)];
201425
+ const c = PL([0, 0, 0]);
201426
+ const pts = geoShapePoints(node, f.def).map((point) => P(point[0], point[1], point[2]));
200099
201427
  const r = Math.max(...pts.map((p) => Math.hypot(p[0] - c[0], p[1] - c[1])));
200100
201428
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200101
201429
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { className: "dgeo3d-face top", cx: c[0], cy: c[1], r }),
@@ -200104,64 +201432,79 @@ function geoShapeBody(node, f) {
200104
201432
  }
200105
201433
  case "cylinder":
200106
201434
  case "cone": {
200107
- const exr = (z2) => {
200108
- const c = P(cx, cy, z2);
200109
- const cardinals = [P(cx + hx, cy, z2), P(cx - hx, cy, z2), P(cx, cy + hy, z2), P(cx, cy - hy, z2)];
200110
- const rx = (Math.max(...cardinals.map((p) => p[0])) - Math.min(...cardinals.map((p) => p[0]))) / 2;
200111
- const ry = (Math.max(...cardinals.map((p) => p[1])) - Math.min(...cardinals.map((p) => p[1]))) / 2;
200112
- return { c, rx, ry };
200113
- };
200114
- const base = exr(zlo);
201435
+ const ring = (z2) => Array.from({ length: 32 }, (_2, index2) => {
201436
+ const angle = index2 * Math.PI / 16;
201437
+ return [hx * Math.cos(angle), hy * Math.sin(angle), z2];
201438
+ });
201439
+ const baseLocal = ring(-hz);
201440
+ const baseWorld = baseLocal.map((point) => geoWorldPoint(g, point));
201441
+ const base = baseWorld.map((point) => P(point[0], point[1], point[2]));
200115
201442
  if (g.shape === "cone") {
200116
- const apex = P(cx, cy, zhi);
201443
+ const apexWorld = geoWorldPoint(g, [0, 0, hz]);
201444
+ const apex = P(apexWorld[0], apexWorld[1], apexWorld[2]);
201445
+ const baseCenter = geoWorldPoint(g, [0, 0, -hz]);
201446
+ const baseVisible = dot3(baseCenter, GEO_VIEW_DIRECTION) > dot3(apexWorld, GEO_VIEW_DIRECTION);
200117
201447
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200118
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { className: "dgeo3d-face side", cx: base.c[0], cy: base.c[1], rx: base.rx, ry: base.ry }),
200119
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([[base.c[0] - base.rx, base.c[1]], apex, [base.c[0] + base.rx, base.c[1]]]) }),
200120
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { className: "dgeo3d-edge", cx: base.c[0], cy: base.c[1], rx: base.rx, ry: base.ry, fill: "none" })
201448
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr(convexHull([...base, apex])) }),
201449
+ baseVisible ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr(base) }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { className: "dgeo3d-edge", points: ptStr([...base, base[0]]), fill: "none", strokeDasharray: "3 3" })
200121
201450
  ] });
200122
201451
  }
200123
- const top = exr(zhi);
201452
+ const topLocal = ring(hz);
201453
+ const topWorld = topLocal.map((point) => geoWorldPoint(g, point));
201454
+ const top = topWorld.map((point) => P(point[0], point[1], point[2]));
201455
+ const baseDepth = dot3(geoWorldPoint(g, [0, 0, -hz]), GEO_VIEW_DIRECTION);
201456
+ const topDepth = dot3(geoWorldPoint(g, [0, 0, hz]), GEO_VIEW_DIRECTION);
201457
+ const near = topDepth >= baseDepth ? top : base;
201458
+ const far = topDepth >= baseDepth ? base : top;
200124
201459
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200125
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { className: "dgeo3d-edge", cx: base.c[0], cy: base.c[1], rx: base.rx, ry: base.ry, fill: "none", strokeDasharray: "3 3" }),
200126
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([[top.c[0] - top.rx, top.c[1]], [top.c[0] + top.rx, top.c[1]], [base.c[0] + base.rx, base.c[1]], [base.c[0] - base.rx, base.c[1]]]) }),
200127
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("ellipse", { className: "dgeo3d-face top", cx: top.c[0], cy: top.c[1], rx: top.rx, ry: top.ry })
201460
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr(convexHull([...base, ...top])) }),
201461
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { className: "dgeo3d-edge", points: ptStr([...far, far[0]]), fill: "none", strokeDasharray: "3 3" }),
201462
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr(near) })
200128
201463
  ] });
200129
201464
  }
200130
201465
  case "pyramid": {
200131
- const apex = P(cx, cy, zhi);
200132
- const bxp = P(cx + hx, cy - hy, zlo);
200133
- const bxp2 = P(cx + hx, cy + hy, zlo);
200134
- const byp = P(cx - hx, cy + hy, zlo);
200135
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200136
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([bxp, bxp2, apex]) }),
200137
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([byp, bxp2, apex]) }),
200138
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polyline", { className: "dgeo3d-edge", points: ptStr([P(cx - hx, cy - hy, zlo), bxp, bxp2, byp, P(cx - hx, cy - hy, zlo)]), fill: "none" })
200139
- ] });
201466
+ const vertices = [
201467
+ [-hx, -hy, -hz],
201468
+ [hx, -hy, -hz],
201469
+ [hx, hy, -hz],
201470
+ [-hx, hy, -hz],
201471
+ [0, 0, hz]
201472
+ ];
201473
+ return polyhedronBody(node, f, vertices, [[0, 3, 2, 1], [0, 1, 4], [1, 2, 4], [2, 3, 4], [3, 0, 4]]);
200140
201474
  }
200141
201475
  case "wedge": {
200142
- const rf = P(cx, cy - hy, zhi);
200143
- const rb = P(cx, cy + hy, zhi);
200144
- const blr = P(cx + hx, cy - hy, zlo);
200145
- const bur = P(cx + hx, cy + hy, zlo);
200146
- const bll = P(cx - hx, cy - hy, zlo);
200147
- const bul = P(cx - hx, cy + hy, zlo);
200148
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200149
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([blr, bur, rb, rf]) }),
200150
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr([bll, bul, rb, rf]) }),
200151
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([bul, bur, rb]) })
200152
- ] });
201476
+ const vertices = [
201477
+ [-hx, -hy, -hz],
201478
+ [hx, -hy, -hz],
201479
+ [hx, hy, -hz],
201480
+ [-hx, hy, -hz],
201481
+ [0, -hy, hz],
201482
+ [0, hy, hz]
201483
+ ];
201484
+ return polyhedronBody(node, f, vertices, [[0, 3, 2, 1], [0, 1, 4], [1, 2, 5, 4], [2, 3, 5], [3, 0, 4, 5]]);
200153
201485
  }
200154
201486
  case "box": {
200155
- const t = (sx, sy) => P(cx + sx * hx, cy + sy * hy, zhi);
200156
- const b = (sx, sy) => P(cx + sx * hx, cy + sy * hy, zlo);
200157
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200158
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([t(1, -1), t(1, 1), b(1, 1), b(1, -1)]) }),
200159
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([t(-1, 1), t(1, 1), b(1, 1), b(-1, 1)]) }),
200160
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr([t(-1, -1), t(1, -1), t(1, 1), t(-1, 1)]) })
200161
- ] });
201487
+ const vertices = [
201488
+ [-hx, -hy, -hz],
201489
+ [hx, -hy, -hz],
201490
+ [hx, hy, -hz],
201491
+ [-hx, hy, -hz],
201492
+ [-hx, -hy, hz],
201493
+ [hx, -hy, hz],
201494
+ [hx, hy, hz],
201495
+ [-hx, hy, hz]
201496
+ ];
201497
+ return polyhedronBody(node, f, vertices, [
201498
+ [0, 3, 2, 1],
201499
+ [4, 5, 6, 7],
201500
+ [0, 1, 5, 4],
201501
+ [1, 2, 6, 5],
201502
+ [2, 3, 7, 6],
201503
+ [3, 0, 4, 7]
201504
+ ]);
200162
201505
  }
200163
201506
  default: {
200164
- const q = [P(cx - hx, cy - hy, zlo), P(cx + hx, cy - hy, zlo), P(cx + hx, cy + hy, zlo), P(cx - hx, cy + hy, zlo)];
201507
+ const q = [PL([-hx, -hy, 0]), PL([hx, -hy, 0]), PL([hx, hy, 0]), PL([-hx, hy, 0])];
200165
201508
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr(q) });
200166
201509
  }
200167
201510
  }
@@ -200184,6 +201527,7 @@ function GeoShapeNode({ id: id2, data, selected: selected2 }) {
200184
201527
  const dim = useInteraction(ctx.store, (s) => s.matchedIds ? !s.matchedIds.has(id2) : false);
200185
201528
  const origin = data.origin;
200186
201529
  const iso = data.geo3d && node.geo && origin;
201530
+ const displayName = node.name.split(/::|\./u).filter(Boolean).at(-1) ?? node.name;
200187
201531
  const approx = node.geo?.approx;
200188
201532
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
200189
201533
  "div",
@@ -200199,7 +201543,10 @@ function GeoShapeNode({ id: id2, data, selected: selected2 }) {
200199
201543
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "rf-node-svg", width: w, height: h, style: { overflow: "visible" }, children: [
200200
201544
  iso ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
200201
201545
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { className: `dgeo3d-node${selected2 ? " selected" : ""}`, transform: `translate(${-origin.x},${-origin.y})`, children: geoShapeBody(node, data.geo3d) }),
200202
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { className: "dgeo3d-name", x: w / 2, y: -3, textAnchor: "middle", children: node.name })
201546
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("text", { className: "dgeo3d-name", x: w / 2, y: -3, textAnchor: "middle", children: [
201547
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("title", { children: node.name }),
201548
+ displayName
201549
+ ] })
200203
201550
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
200204
201551
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("rect", { className: `dnode-rect${selected2 ? " selected" : ""}`, x: 0, y: 0, width: w, height: h, rx: node.isDef ? 0 : 12 }),
200205
201552
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { className: "dnode-label", x: w / 2, y: h / 2 + 3, textAnchor: "middle", children: node.name })
@@ -200420,7 +201767,7 @@ function usePortDrag(containerRef, w, h, topReserve, onPortMove, onPortPreview,
200420
201767
  };
200421
201768
  return { dragId, onPortPointerDown };
200422
201769
  }
200423
- function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, startable = false, size, occluded = false }) {
201770
+ function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, startable = false, size, occluded = false, onContextMenu }) {
200424
201771
  const dimensions = typeof size === "number" ? { width: size, height: size } : size;
200425
201772
  const style2 = dimensions ? { left: x, top: y, ...dimensions, transform: "translate(-50%,-50%)" } : { left: x, top: y, transform: "translate(-50%,-50%)" };
200426
201773
  const enabled = connectable && !occluded;
@@ -200436,7 +201783,8 @@ function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, sta
200436
201783
  className: cls,
200437
201784
  isConnectable: enabled,
200438
201785
  isConnectableStart: false,
200439
- isConnectableEnd: enabled
201786
+ isConnectableEnd: enabled,
201787
+ onContextMenu
200440
201788
  }
200441
201789
  ),
200442
201790
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -200449,7 +201797,8 @@ function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, sta
200449
201797
  className: cls,
200450
201798
  isConnectable: enabled,
200451
201799
  isConnectableStart: enabled && startable,
200452
- isConnectableEnd: enabled
201800
+ isConnectableEnd: enabled,
201801
+ onContextMenu
200453
201802
  }
200454
201803
  )
200455
201804
  ] });
@@ -200791,7 +202140,15 @@ function AnnotationBody({ node, w, h }) {
200791
202140
  }
200792
202141
  function DotBody({ node, w, h }) {
200793
202142
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
200794
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { cx: w / 2, cy: h / 2, r: 5, fill: "var(--fg-2, #7e8aa1)" }),
202143
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
202144
+ "circle",
202145
+ {
202146
+ className: "dnode-control dot dconnection-end-dot",
202147
+ cx: w / 2,
202148
+ cy: h / 2,
202149
+ r: circularControlRadius("dot")
202150
+ }
202151
+ ),
200795
202152
  node.name ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { className: "dnode-kind", x: w / 2 + 9, y: h / 2 + 4, children: node.name }) : ""
200796
202153
  ] });
200797
202154
  }
@@ -200860,6 +202217,8 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200860
202217
  };
200861
202218
  })() : void 0;
200862
202219
  const proxy = rect.proxy;
202220
+ const connectionEnd = ph.port.meta?.connectionEndPin === true;
202221
+ const glyphClass = `${selected2 ? " selected" : ""}${hovered ? " hovered" : ""}${moving ? " moving" : ""}${connectTargetSide ? " connect-target" : ""}`;
200863
202222
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
200864
202223
  "g",
200865
202224
  {
@@ -200889,10 +202248,10 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200889
202248
  }
200890
202249
  );
200891
202250
  })() : null,
200892
- /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
202251
+ connectionEnd ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { className: `dconnection-end-dot${glyphClass}`, cx: x, cy: y, r: half }) : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
200893
202252
  "rect",
200894
202253
  {
200895
- className: `dnode-port${ph.port.pin ? " pin" : ""}${ph.port.ref ? " ref" : ""}${rect.elongated ? " host" : ""}${proxy ? " proxy" : ""}${selected2 ? " selected" : ""}${hovered ? " hovered" : ""}${moving ? " moving" : ""}${connectTargetSide ? " connect-target" : ""}`,
202254
+ className: `dnode-port${ph.port.pin ? " pin" : ""}${ph.port.ref ? " ref" : ""}${rect.elongated ? " host" : ""}${proxy ? " proxy" : ""}${glyphClass}`,
200896
202255
  x: rect.x,
200897
202256
  y: rect.y,
200898
202257
  width: rect.width,
@@ -200900,12 +202259,12 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200900
202259
  rx: ph.port.isDef ? 0 : PORT_CORNER_RADIUS
200901
202260
  }
200902
202261
  ),
200903
- d ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
202262
+ d && !connectionEnd ? /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(import_jsx_runtime4.Fragment, { children: [
200904
202263
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir", d: arrowShaft() }),
200905
202264
  d === "out" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(1) }) : "",
200906
202265
  d === "in" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(-1) }) : ""
200907
202266
  ] }) : "",
200908
- selected2 && connectStart && !connectTargetSide && !proxy ? (() => {
202267
+ selected2 && connectStart && !connectTargetSide && !proxy && !connectionEnd ? (() => {
200909
202268
  const px = x + v.x * (half + PORT_PLUS_GAP);
200910
202269
  const py = y + v.y * (half + PORT_PLUS_GAP);
200911
202270
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { className: "dport-plus", children: [
@@ -200913,7 +202272,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200913
202272
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: px, y1: py - PORT_PLUS_HALF, x2: px, y2: py + PORT_PLUS_HALF })
200914
202273
  ] });
200915
202274
  })() : "",
200916
- connectTargetSide ? (() => {
202275
+ connectTargetSide && !connectionEnd ? (() => {
200917
202276
  const at2 = portDockAt(rect, ph.side, connectTargetSide);
200918
202277
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { className: "dport-anchor", cx: at2.x, cy: at2.y, r: 1.7 });
200919
202278
  })() : "",
@@ -201169,7 +202528,7 @@ function NodeBody({ node, data, selectedPortId, hoveredPortId, movingPortId, con
201169
202528
  hovered: hoveredPortId === ph.port.id,
201170
202529
  moving: movingPortId === ph.port.id,
201171
202530
  connectTargetSide: connectTargetPortId === ph.port.id ? connectTargetPortSide : void 0,
201172
- connectStart: showPortConnectStart && (data.kind === "iv" ? ph.port.pin !== true : data.kind === "afv"),
202531
+ connectStart: showPortConnectStart && (data.kind === "iv" ? ph.port.pin !== true || ph.port.meta?.connectionEndPin === true : data.kind === "afv"),
201173
202532
  onSelect: onPortSelect,
201174
202533
  onContext: onPortContext
201175
202534
  },
@@ -201221,15 +202580,16 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201221
202580
  shape: node.shape,
201222
202581
  source: node.source
201223
202582
  });
201224
- const nodeSideStartable = directRelation !== void 0 && data.kind !== "sv" && node.shape !== "lifeline" && !isPackageEndpoint(node.shape, node.keyword);
202583
+ const connectionUsage = node.meta?.connectionUsage === true || node.meta?.interfaceUsage === true;
202584
+ const nodeSideStartable = !connectionUsage && directRelation !== void 0 && data.kind !== "sv" && node.shape !== "lifeline" && !isPackageEndpoint(node.shape, node.keyword);
201225
202585
  const selectedPortId = useInteraction(ctx.store, (s) => typeof s.selectedId === "string" && myPortIds.includes(s.selectedId) ? s.selectedId : void 0);
201226
202586
  const dropClass = useDropState(ctx.store, id2);
201227
202587
  const [hoveredPortId, setHoveredPortId] = (0, import_react9.useState)(void 0);
201228
202588
  const [sidePointsHovered, setSidePointsHovered] = (0, import_react9.useState)(false);
201229
202589
  const isPackageNode = isPackageEndpoint(node.shape, node.keyword);
201230
- const sideConnectable = data.kind !== "sv" && !isPackageNode;
202590
+ const sideConnectable = data.kind !== "sv" && !isPackageNode && !connectionUsage;
201231
202591
  const perimeterControl = keepsPerimeterSidePointsMounted(node.shape);
201232
- const sideOffsets = SIDES.map((side) => mountedNodeSidePointOffsets(
202592
+ const sideOffsets = connectionUsage ? SIDES.map(() => []) : SIDES.map((side) => mountedNodeSidePointOffsets(
201233
202593
  data,
201234
202594
  side,
201235
202595
  sideConnectable,
@@ -201352,17 +202712,39 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201352
202712
  );
201353
202713
  });
201354
202714
  }),
201355
- node.shape === "initial" || node.shape === "final" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rf-circular-control-hit", "aria-hidden": "true" }) : null,
202715
+ node.shape === "initial" || node.shape === "final" || node.shape === "dot" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rf-circular-control-hit", "aria-hidden": "true" }) : null,
201356
202716
  ports.flatMap((ph) => {
201357
202717
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201358
202718
  const proxied = isProxyPort(ph.port);
201359
202719
  const startable = !proxied && directRelationForHandle(data.kind, {
201360
202720
  id: ph.port.id,
201361
202721
  name: ph.port.name,
201362
- keyword: ph.port.pin ? "pin" : "port",
202722
+ keyword: ph.port.meta?.interfaceEndPort === true ? "interface end" : ph.port.meta?.connectionEndPin === true ? "connection end" : ph.port.pin ? "pin" : "port",
201363
202723
  shape: ph.port.pin ? "pin" : "port",
201364
202724
  source: ph.port.source
201365
202725
  }) !== void 0;
202726
+ if (ph.port.meta?.connectionEndPin === true) {
202727
+ const point = portDockAt(rect, ph.side, ph.side);
202728
+ return [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
202729
+ DualHandle,
202730
+ {
202731
+ id: portAnchorHandleId(ph.port.id, ph.side),
202732
+ side: ph.side,
202733
+ x: point.x,
202734
+ y: point.y,
202735
+ kind: "connection-end",
202736
+ connectable: !proxied,
202737
+ active: !proxied && (ph.port.id === hoveredPortId || ph.port.id === selectedPortId || ph.port.id === connectTargetPortId),
202738
+ startable,
202739
+ onContextMenu: (e) => {
202740
+ e.preventDefault();
202741
+ e.stopPropagation();
202742
+ ctx.onContextNode(ph.port.id, e.clientX, e.clientY);
202743
+ }
202744
+ },
202745
+ `${ph.port.id}-end`
202746
+ )];
202747
+ }
201366
202748
  const [outerId, innerId] = portConnectHandleIds(ph.port.id, ph.side);
201367
202749
  const outer = portDockAt(rect, ph.side, ph.side);
201368
202750
  const innerSide = OPPOSITE_DOCK_SIDE[ph.side];
@@ -201403,7 +202785,7 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201403
202785
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201404
202786
  const nestedChild = ph.port.parentPort !== void 0;
201405
202787
  const proxyPort = isProxyPort(ph.port);
201406
- const strip = portInteractionStrip(rect, ph.side);
202788
+ const strip = portInteractionStrip(rect, ph.side, ph.port.meta?.connectionEndPin === true);
201407
202789
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
201408
202790
  "div",
201409
202791
  {
@@ -201774,7 +203156,7 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201774
203156
  hovered: hoveredPortId === ph.port.id,
201775
203157
  moving: dragId === ph.port.id,
201776
203158
  connectTargetSide: connectTargetPortId === ph.port.id ? connectTargetPortSide : void 0,
201777
- connectStart: data.kind === "iv" ? ph.port.pin !== true : data.kind === "afv",
203159
+ connectStart: data.kind === "iv" ? ph.port.pin !== true || ph.port.meta?.connectionEndPin === true : data.kind === "afv",
201778
203160
  onSelect: ctx.onPortMove ? void 0 : portSource,
201779
203161
  onContext: ctx.onContextNode
201780
203162
  },
@@ -201847,10 +203229,32 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201847
203229
  const startable = !proxied && directRelationForHandle(data.kind, {
201848
203230
  id: ph.port.id,
201849
203231
  name: ph.port.name,
201850
- keyword: ph.port.pin ? "pin" : "port",
203232
+ keyword: ph.port.meta?.interfaceEndPort === true ? "interface end" : ph.port.meta?.connectionEndPin === true ? "connection end" : ph.port.pin ? "pin" : "port",
201851
203233
  shape: ph.port.pin ? "pin" : "port",
201852
203234
  source: ph.port.source
201853
203235
  }) !== void 0;
203236
+ if (ph.port.meta?.connectionEndPin === true) {
203237
+ const point = portDockAt(rect, ph.side, ph.side);
203238
+ return [/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
203239
+ DualHandle,
203240
+ {
203241
+ id: portAnchorHandleId(ph.port.id, ph.side),
203242
+ side: ph.side,
203243
+ x: point.x,
203244
+ y: point.y,
203245
+ kind: "connection-end",
203246
+ connectable: !proxied,
203247
+ active: !proxied && (ph.port.id === hoveredPortId || ph.port.id === selectedPortId || ph.port.id === connectTargetPortId),
203248
+ startable,
203249
+ onContextMenu: (e) => {
203250
+ e.preventDefault();
203251
+ e.stopPropagation();
203252
+ ctx.onContextNode(ph.port.id, e.clientX, e.clientY);
203253
+ }
203254
+ },
203255
+ `${ph.port.id}-end`
203256
+ )];
203257
+ }
201854
203258
  const [outerId, innerId] = portConnectHandleIds(ph.port.id, ph.side);
201855
203259
  const outer = portDockAt(rect, ph.side, ph.side);
201856
203260
  const innerSide = OPPOSITE_DOCK_SIDE[ph.side];
@@ -201891,7 +203295,7 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201891
203295
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201892
203296
  const nestedChild = ph.port.parentPort !== void 0;
201893
203297
  const proxyPort = isProxyPort(ph.port);
201894
- const strip = portInteractionStrip(rect, ph.side);
203298
+ const strip = portInteractionStrip(rect, ph.side, ph.port.meta?.connectionEndPin === true);
201895
203299
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
201896
203300
  "div",
201897
203301
  {
@@ -202222,7 +203626,7 @@ async function renderDiagramSvg(document2, provider, options) {
202222
203626
  overrides: config.overrides,
202223
203627
  connectPointSpacing: config.connectPointSpacing
202224
203628
  });
202225
- const renderEdges = kind === "gv" && config.gvMode === "tree" && config.lineStyle === "orthogonal" ? projectIndividualGvTreeRenderEdges(laid.nodes, laid.edges, createEdgeBundleRuntime()) : laid.edges;
203629
+ const renderEdges = config.lineStyle === "orthogonal" ? projectAutomaticBusRenderEdges(laid.nodes, laid.edges, createEdgeBundleRuntime()) : laid.edges;
202226
203630
  const indexOf2 = new Map(laid.nodes.map((node, i) => [node.id, i]));
202227
203631
  return buildDiagramSvg({
202228
203632
  nodes: laid.nodes,
@@ -202278,7 +203682,7 @@ async function runExport(command) {
202278
203682
  }
202279
203683
 
202280
203684
  // src/main.ts
202281
- var VERSION2 = true ? "0.21.1" : "dev";
203685
+ var VERSION2 = true ? "0.23.0" : "dev";
202282
203686
  function display(file) {
202283
203687
  const rel2 = path9.relative(process.cwd(), file);
202284
203688
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;