sysml-diagram 0.22.0 → 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",
@@ -162372,6 +162461,83 @@ function multiplicityText(m) {
162372
162461
  return void 0;
162373
162462
  return hi == null || hi === lo ? `[${lo}]` : `[${lo}..${hi}]`;
162374
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
+ }
162375
162541
  function multText(node) {
162376
162542
  if (!node)
162377
162543
  return void 0;
@@ -162782,17 +162948,40 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162782
162948
  else
162783
162949
  drawn.set(k, [target]);
162784
162950
  };
162785
- for (const n2 of model.nodes) {
162786
- 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 });
162787
162953
  for (const port of n2.ports ?? []) {
162788
- 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
+ });
162789
162963
  }
162790
- }
162791
- for (const f of model.frames ?? []) {
162792
- 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 });
162793
162967
  for (const port of f.ports ?? []) {
162794
- 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 });
162795
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);
162796
162985
  }
162797
162986
  if (model.ivBoundary) {
162798
162987
  claim(model.ivBoundary.source, { id: model.ivBoundary.id, frame: model.ivBoundary.id });
@@ -162804,15 +162993,22 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162804
162993
  return model;
162805
162994
  const nodes = [...model.nodes];
162806
162995
  const edges = [...model.edges];
162996
+ const definitionNodes = [...definitionLayer?.nodes ?? []];
162997
+ const definitionEdges = [...definitionLayer?.edges ?? []];
162807
162998
  let noteCount = 0;
162808
- 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);
162809
163003
  const note = (annotation) => {
162810
163004
  const targets = drawn.get(key(sourceOf2(annotation.owner, uri)) ?? "")?.filter((target) => annotation.keyword !== "doc" || docAsNote || target.port);
162811
163005
  if (!targets?.length)
162812
163006
  return;
162813
163007
  for (const target of targets) {
162814
163008
  const id2 = `__note_${noteCount++}__`;
162815
- nodes.push({
163009
+ const targetNodes = target.definitionLayer ? definitionNodes : nodes;
163010
+ const targetEdges = target.definitionLayer ? definitionEdges : edges;
163011
+ targetNodes.push({
162816
163012
  id: id2,
162817
163013
  name: annotation.label,
162818
163014
  keyword: annotation.keyword,
@@ -162825,7 +163021,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162825
163021
  ...target.frame ? { frame: target.frame } : {},
162826
163022
  source: sourceOf2(annotation.annotation, uri)
162827
163023
  });
162828
- edges.push({
163024
+ targetEdges.push({
162829
163025
  id: `e${edgeCount++}`,
162830
163026
  from: id2,
162831
163027
  to: target.id,
@@ -162843,7 +163039,23 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162843
163039
  note(annotation);
162844
163040
  }
162845
163041
  }
162846
- 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
+ };
162847
163059
  }
162848
163060
  // REQ-224 — Detect the file/anchor-specific view kinds that will render
162849
163061
  // substantive diagram content. The extension uses this to limit the
@@ -163670,8 +163882,27 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163670
163882
  hasGeometryObject(node, index2) {
163671
163883
  return this.collectGeometry(node, index2).length > 0;
163672
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
+ }
163673
163902
  hasRenderablePorts(node, index2) {
163674
- 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)));
163675
163906
  }
163676
163907
  // REQ-192, issue #233 — the named part usages a part shows when expanded: its
163677
163908
  // own and the ones it inherits, through its typing definition or a `:>`
@@ -163679,11 +163910,51 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163679
163910
  // The predicate form of `childUsagesOf`, so an anchor that only INHERITS
163680
163911
  // internals is recognised as having them.
163681
163912
  hasNestedPartUsages(node, index2) {
163682
- 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);
163683
163914
  }
163684
163915
  nestedUsages(node, guard) {
163685
163916
  return membersOf(node).filter((m) => guard(m) && m.isDef !== true);
163686
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
+ }
163687
163958
  // issue #109 — usages of kinds with NO dedicated usage view (constraint /
163688
163959
  // calc / occurrence / item / attribute). Nested in an element they surface as
163689
163960
  // owner compartments (compartmentsFor); declared as direct package members
@@ -163718,8 +163989,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163718
163989
  const all = [...ast_utils_exports.streamAllContents(scope)];
163719
163990
  const defNodes = all.filter((n2) => isDefinitionNode(n2) && nameOf2(n2));
163720
163991
  const packageOverview = isPackage(ctx.anchor) || isDocument(ctx.anchor);
163721
- const isPackageInterfaceUsage = (n2) => isInterfaceDecl(n2) && n2.isDef !== true && !!nameOf2(n2) && this.isDirectPackageMember(n2);
163722
- const hasOwnedMemberNode = all.some((n2) => isPackage(n2) && !!nameOf2(n2) || isDefinitionNode(n2) && !!nameOf2(n2) || this.isViewlessUsage(n2) && this.isDirectPackageMember(n2) || isPackageInterfaceUsage(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));
163723
163999
  const packageNodes = [
163724
164000
  ...packageOverview && hasOwnedMemberNode && isPackage(scope) && nameOf2(scope) ? [scope] : [],
163725
164001
  ...all.filter((p) => isPackage(p) && nameOf2(p))
@@ -163880,6 +164156,77 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163880
164156
  }
163881
164157
  }
163882
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
+ }
163883
164230
  for (const rs of all.filter((m) => m.$type === "StandaloneRelationshipDecl")) {
163884
164231
  const rn = rs;
163885
164232
  const from = resolveDrawnByText(String(rn.source ?? ""));
@@ -164296,6 +164643,270 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164296
164643
  const o = v;
164297
164644
  return this.featurePath(o.reference) ?? this.featurePath(o.path) ?? o.$cstNode?.text?.replace(/\[[^\]]*\]/g, "").trim();
164298
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
+ }
164299
164910
  // FlowStmt ends for both spec forms (OMG SysML 8.2.2.16): explicit
164300
164911
  // `from a to b`, and the keyword-less shorthand `flow a.x to b.y;` whose
164301
164912
  // source is parsed as name + dotted segments.
@@ -164329,7 +164940,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164329
164940
  const nodes = [];
164330
164941
  const edges = [];
164331
164942
  const frames = [];
164943
+ const primaryTarget = { nodes, edges, frames };
164944
+ const definitionTarget = { nodes: [], edges: [], frames: [] };
164332
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();
164333
164950
  const enclosingPackage = (n2) => {
164334
164951
  let c = n2.$container;
164335
164952
  while (c && c !== scope) {
@@ -164352,22 +164969,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164352
164969
  pkgFrame.set(pkg, id2);
164353
164970
  return id2;
164354
164971
  };
164355
- const childUsagesOf = (part) => {
164356
- const out = this.nestedUsages(part, isPartDecl);
164357
- const have = new Set(out.map(effectiveNameOf).filter((x) => !!x));
164358
- for (const source of this.inheritedFeatureOwnersOf(part, index2)) {
164359
- for (const usage of this.nestedUsages(source, isPartDecl)) {
164360
- const nm = effectiveNameOf(usage);
164361
- if (nm !== void 0) {
164362
- if (have.has(nm))
164363
- continue;
164364
- have.add(nm);
164365
- }
164366
- out.push(usage);
164367
- }
164368
- }
164369
- return out;
164370
- };
164972
+ const childUsagesOf = (part) => this.structuralChildUsagesOf(part, index2);
164371
164973
  const effectiveMembersOf = (part) => {
164372
164974
  const effective = [];
164373
164975
  const claimedNames = /* @__PURE__ */ new Set();
@@ -164394,6 +164996,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164394
164996
  const actionPort = /* @__PURE__ */ new Map();
164395
164997
  const localNode = /* @__PURE__ */ new Map();
164396
164998
  const actionPinIds = /* @__PURE__ */ new Set();
164999
+ const structuralPortIds = /* @__PURE__ */ new Set();
164397
165000
  const addActionPort = (key, id2) => {
164398
165001
  if (!actionPort.has(key))
164399
165002
  actionPort.set(key, id2);
@@ -164411,6 +165014,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164411
165014
  localPort.set(`${id2}.${rel2}`, port.id);
164412
165015
  if (bare)
164413
165016
  localPort.set(rel2, port.id);
165017
+ structuralPortIds.add(port.id);
164414
165018
  }
164415
165019
  };
164416
165020
  for (const { usage, id: id2 } of childInfos)
@@ -164457,25 +165061,40 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164457
165061
  return actionPort.get(firstLast) ?? void 0;
164458
165062
  if (localPort.has(firstLast))
164459
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;
164460
165068
  }
164461
165069
  const bare = bareName2.get(segs[segs.length - 1]);
164462
165070
  if (bare)
164463
165071
  return bare;
164464
165072
  return segs.length === 1 ? localNode.get(segs[0]) : void 0;
164465
165073
  };
164466
- return { resolve: resolve8, actionPinIds };
165074
+ return { resolve: resolve8, actionPinIds, structuralPortIds };
164467
165075
  };
164468
- 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);
164469
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
+ }
164470
165084
  const typeQ = typeDef ? qnameOf(typeDef) : void 0;
164471
165085
  const declarationId = nameOf2(part) ? qnameOf(part) || instanceId : instanceId;
164472
165086
  const projected = nameOf2(part) === void 0 || declarationId !== instanceId;
164473
165087
  const localUsage = part.isDef !== true && !projected ? part : inheritedLocalUsage;
164474
165088
  const localPath = part.isDef !== true && !projected ? [] : inheritedLocalPath;
164475
- 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
+ }
164476
165094
  const syncDeclaration = this.synchronizationDeclarationOf(part, index2);
164477
165095
  const editMeta = {
164478
165096
  ...ivEditMeta(part, instanceId, typeDef, localUsage, localPath, uri),
165097
+ ...definitionLayerRoot ? { ivPartDef: true } : {},
164479
165098
  ...syncDeclaration && syncDeclaration !== part ? {
164480
165099
  syncDeclarationId: qnameOf(syncDeclaration),
164481
165100
  syncDeclarationName: effectiveNameOf(syncDeclaration),
@@ -164483,7 +165102,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164483
165102
  } : {}
164484
165103
  };
164485
165104
  const children2 = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? childUsagesOf(part) : [];
164486
- const concretePerformPath = (statement, target) => {
165105
+ const concretePerformPath = (statement, target2) => {
164487
165106
  if (!this.isAnonymousBehaviorReference(statement)) {
164488
165107
  return qnameOf(statement) || nameOf2(statement) || "perform";
164489
165108
  }
@@ -164491,7 +165110,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164491
165110
  const featureStart = segments.findIndex((segment, at) => at > 0 && segment.separator === ".");
164492
165111
  if (featureStart > 0) {
164493
165112
  const writtenPath = segments.map((segment) => segment.text).join("::");
164494
- const targetName = qnameOf(target);
165113
+ const targetName = qnameOf(target2);
164495
165114
  if (targetName === writtenPath || targetName.endsWith(`::${writtenPath}`)) {
164496
165115
  return targetName;
164497
165116
  }
@@ -164510,7 +165129,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164510
165129
  return [qnameOf(root4), ...segments.slice(featureStart).map((segment) => segment.text)].join("::");
164511
165130
  }
164512
165131
  }
164513
- return qnameOf(target) || nameOf2(target) || segments.map((segment) => segment.text).join("::");
165132
+ return qnameOf(target2) || nameOf2(target2) || segments.map((segment) => segment.text).join("::");
164514
165133
  };
164515
165134
  const performedActions = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? effectiveMembersOf(part).filter((m) => m.$type === "PerformStmt" && !!nameOf2(m)).map((act) => {
164516
165135
  const pinSource = this.performTargetOf(act, index2) ?? act;
@@ -164550,7 +165169,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164550
165169
  });
164551
165170
  const emitOwnedActions = (frameOf) => {
164552
165171
  for (const { act, name, id: actId, pins, meta } of performedActionInfos) {
164553
- nodes.push({
165172
+ target.nodes.push({
164554
165173
  id: actId,
164555
165174
  name,
164556
165175
  keyword: keywordFor(act),
@@ -164566,7 +165185,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164566
165185
  }
164567
165186
  };
164568
165187
  if (children2.length > 0 || performedActions.length > 0) {
164569
- frames.push({
165188
+ target.frames.push({
164570
165189
  id: instanceId,
164571
165190
  label: effectiveNameOf(part) ?? lastSeg(instanceId),
164572
165191
  keyword: keywordFor(part),
@@ -164584,13 +165203,13 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164584
165203
  const nextSeen = typeQ ? /* @__PURE__ */ new Set([...seenTypes, typeQ]) : seenTypes;
164585
165204
  const childInfos = children2.map((cu) => ({ usage: cu, id: `${instanceId}::${effectiveNameOf(cu) ?? lastSeg(qnameOf(cu) || "")}` }));
164586
165205
  for (const { usage, id: id2 } of childInfos) {
164587
- 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);
164588
165207
  }
164589
165208
  emitOwnedActions(instanceId);
164590
165209
  const resolver = childResolver(childInfos, { usage: part, id: instanceId }, performedActionInfos);
164591
- 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);
164592
165211
  } else {
164593
- nodes.push({
165212
+ target.nodes.push({
164594
165213
  id: instanceId,
164595
165214
  name: effectiveNameOf(part) ?? lastSeg(instanceId),
164596
165215
  keyword: keywordFor(part),
@@ -164605,7 +165224,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164605
165224
  meta: editMeta
164606
165225
  });
164607
165226
  const resolver = childResolver([], { usage: part, id: instanceId });
164608
- 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);
164609
165228
  }
164610
165229
  };
164611
165230
  const packageOfRoot = (root4) => enclosingPackage(root4) ?? scope;
@@ -164635,18 +165254,53 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164635
165254
  const rootInfos = [];
164636
165255
  for (const root4 of roots) {
164637
165256
  const id2 = rootOccurrenceIds.get(root4) ?? rootBaseId(root4);
164638
- 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);
164639
165261
  rootInfos.push({ usage: root4, id: id2, pkg: packageOfRoot(root4) });
164640
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
+ }
164641
165290
  if (opts.packageFrames) {
164642
165291
  for (const pkg of [scope, ...[...ast_utils_exports.streamAllContents(scope)].filter(isPackage)]) {
164643
165292
  const localInfos = rootInfos.filter((r) => r.pkg === pkg && r.usage.isDef !== true);
164644
165293
  const localRoots = localInfos.map((r) => r.usage);
164645
165294
  const resolver = childResolver(localInfos);
164646
- 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);
164647
165296
  }
164648
165297
  }
164649
- 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 };
164650
165304
  }
164651
165305
  // REQ-192 — Interconnection View for a single part/part-def anchor: the anchor
164652
165306
  // is the (non-collapsible) container frame and its internals nest inside it,
@@ -164654,7 +165308,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164654
165308
  buildInterconnectionView(ctx) {
164655
165309
  const anchor = ctx.anchor;
164656
165310
  const anchorId = qnameOf(anchor) || nameOf2(anchor) || "__anchor__";
164657
- const { nodes, edges, frames } = this.renderIvRoots(ctx, [anchor], { packageFrames: false });
165311
+ const { nodes, edges, frames, definitionLayer } = this.renderIvRoots(ctx, [anchor], { packageFrames: false });
164658
165312
  if (anchor.isDef === true && frames.length === 0 && nodes.every((n2) => !n2.ports?.length && !n2.compartments?.length)) {
164659
165313
  return this.model("iv", anchor, [], []);
164660
165314
  }
@@ -164668,6 +165322,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164668
165322
  }
164669
165323
  const model = this.model("iv", anchor, nodes, edges);
164670
165324
  model.frames = frames;
165325
+ if (definitionLayer)
165326
+ model.layers = { definitions: definitionLayer };
164671
165327
  model.meta = { ...model.meta, unified: true, groupMode: "nested" };
164672
165328
  return model;
164673
165329
  }
@@ -164676,14 +165332,14 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164676
165332
  // single Interconnection View (called once over the anchor's whole subtree)
164677
165333
  // and the package overview (called once per container part, tagging any
164678
165334
  // synthetic n-ary dot with that container's frame id).
164679
- 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()) {
164680
165336
  const memberList2 = [...members];
164681
165337
  const relationshipFields = (source) => {
164682
165338
  let directMember = source;
164683
165339
  while (directMember.$container && directMember.$container !== memberOwner) {
164684
165340
  directMember = directMember.$container;
164685
165341
  }
164686
- const inherited = !!memberOwner && directMember.$container !== memberOwner;
165342
+ const inherited = memberOwnerInherited || !!memberOwner && directMember.$container !== memberOwner;
164687
165343
  const meta = {
164688
165344
  ...inherited ? { ivInheritedRelation: true } : {},
164689
165345
  ...frameId ? { ivRelationshipOwnerId: frameId } : {}
@@ -164694,12 +165350,38 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164694
165350
  };
164695
165351
  };
164696
165352
  const supportsEndpoints = (kind, ids) => {
165353
+ if (kind === "interface") {
165354
+ return ids.length > 0 && ids.every((id2) => structuralPortIds.has(id2));
165355
+ }
164697
165356
  const pinCount = ids.filter((id2) => actionPinIds.has(id2)).length;
164698
165357
  if (pinCount === 0)
164699
165358
  return true;
164700
165359
  return ids.length === 2 && pinCount === 2 && (kind === "flow" || kind === "binding");
164701
165360
  };
164702
- 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
+ };
164703
165385
  const emitConnector = (rawEnds, src, label, name, type) => {
164704
165386
  const resolvedEnds = rawEnds.map((end) => ({ id: resolveEnd(this.connectorEndPath(end) ?? ""), end })).filter((x) => !!x.id);
164705
165387
  if (resolvedEnds.length < 2)
@@ -164707,6 +165389,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164707
165389
  if (!supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2)))
164708
165390
  return;
164709
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);
164710
165394
  edges.push({
164711
165395
  id: `e${eRef.n++}`,
164712
165396
  from: resolvedEnds[0].id,
@@ -164716,15 +165400,20 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164716
165400
  type,
164717
165401
  isDef: false,
164718
165402
  label,
164719
- endLabelFrom: multText(resolvedEnds[0].end),
164720
- 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,
164721
165409
  ...relationshipFields(src)
164722
165410
  });
164723
165411
  return;
164724
165412
  }
164725
165413
  const dotId = `__nary_${eRef.n}__`;
164726
165414
  nodes.push(dot(dotId, label ?? "", src));
164727
- 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);
164728
165417
  edges.push({
164729
165418
  id: `e${eRef.n++}`,
164730
165419
  from: dotId,
@@ -164734,7 +165423,9 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164734
165423
  type,
164735
165424
  isDef: false,
164736
165425
  label,
164737
- endLabelTo: multText(end),
165426
+ endRoleTo: notation.role,
165427
+ endLabelTo: notation.multiplicity,
165428
+ endAdornmentTo: notation.adornment,
164738
165429
  ...relationshipFields(src)
164739
165430
  });
164740
165431
  }
@@ -164769,21 +165460,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164769
165460
  } else if (m.$type === "ConnectStmt") {
164770
165461
  const cs = m;
164771
165462
  if (cs.ends?.length) {
164772
- const resolvedEnds = cs.ends.map((p) => ({ id: resolveEnd(this.connectorEndPath(p) ?? ""), end: p })).filter((x) => !!x.id);
164773
- if (resolvedEnds.length >= 2 && supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2))) {
164774
- const dotId = `__nary_${eRef.n}__`;
164775
- nodes.push(dot(dotId, "", m));
164776
- for (const { id: endId, end } of resolvedEnds) {
164777
- edges.push({
164778
- id: `e${eRef.n++}`,
164779
- from: dotId,
164780
- to: endId,
164781
- kind: "connect",
164782
- endLabelTo: multText(end),
164783
- ...relationshipFields(m)
164784
- });
164785
- }
164786
- }
165463
+ emitConnector(cs.ends, m);
164787
165464
  } else {
164788
165465
  const srcEnd = m.source;
164789
165466
  const tgtEnd = m.target;
@@ -164791,6 +165468,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164791
165468
  const from = resolveEnd(srcPath);
164792
165469
  const to = resolveEnd(this.connectorEndPath(tgtEnd) ?? "");
164793
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);
164794
165473
  edges.push({
164795
165474
  id: `e${eRef.n++}`,
164796
165475
  from,
@@ -164798,8 +165477,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164798
165477
  kind: "connect",
164799
165478
  label: this.flowItemLabel(srcPath, parts, index2),
164800
165479
  // REQ-179 — connector end multiplicities (`connect [1] a to [0..*] b;`)
164801
- endLabelFrom: multText(srcEnd),
164802
- 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,
164803
165486
  ...relationshipFields(m)
164804
165487
  });
164805
165488
  }
@@ -164845,108 +165528,192 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164845
165528
  });
164846
165529
  }
164847
165530
  } else if (isInterfaceDecl(m) && m.isDef !== true) {
164848
- const clause = m.connect;
164849
- if (clause) {
164850
- const rawEnds = clause.ends?.length ? clause.ends : [clause.source, clause.target];
164851
- const resolvedEnds = rawEnds.map((end) => ({ id: resolveEnd(this.connectorEndPath(end) ?? ""), end })).filter((x) => !!x.id);
164852
- const name = nameOf2(m);
164853
- const type = typeText(m);
164854
- const label = ["\xABinterface\xBB", nameOf2(m) ?? "", typeText(m) ? `: ${typeText(m)}` : ""].filter(Boolean).join(" ").trim();
164855
- const docks = resolvedEnds.length === rawEnds.length && supportsEndpoints("interface", resolvedEnds.map(({ id: id2 }) => id2));
164856
- if (docks && resolvedEnds.length === 2) {
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);
165537
+ if (from && to && supportsEndpoints("interface", [from, to])) {
164857
165538
  edges.push({
164858
165539
  id: `e${eRef.n++}`,
164859
- from: resolvedEnds[0].id,
164860
- to: resolvedEnds[1].id,
165540
+ from,
165541
+ to,
164861
165542
  kind: "interface",
164862
- name,
164863
- type,
164864
165543
  isDef: false,
164865
- label,
164866
- endLabelFrom: multText(resolvedEnds[0].end),
164867
- endLabelTo: multText(resolvedEnds[1].end),
165544
+ label: "\xABinterface\xBB",
164868
165545
  ...relationshipFields(m)
164869
165546
  });
164870
- } else if (docks && resolvedEnds.length > 2) {
164871
- const dotId = `__nary_${eRef.n}__`;
164872
- nodes.push(dot(dotId, label, m));
164873
- for (const { id: endId, end } of resolvedEnds) {
164874
- edges.push({
164875
- id: `e${eRef.n++}`,
164876
- from: dotId,
164877
- to: endId,
164878
- kind: "interface",
164879
- name,
164880
- type,
164881
- isDef: false,
164882
- label,
164883
- endLabelTo: multText(end),
164884
- ...relationshipFields(m)
164885
- });
165547
+ }
165548
+ continue;
165549
+ }
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)
165564
+ continue;
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 } : {}
164886
165591
  }
164887
- } else if (resolvedEnds.length < rawEnds.length) {
164888
- nodes.push({
164889
- id: `__interface_${eRef.n++}__`,
164890
- // The box carries its type in its own `type` field, so
164891
- // naming an anonymous usage after that type would read
164892
- // as `FuelInterface : FuelInterface`.
164893
- name: nameOf2(m) ?? ANONYMOUS_INTERFACE_NAME,
164894
- keyword: "interface",
164895
- isDef: false,
164896
- shape: "box",
164897
- type: typeText(m),
164898
- multiplicity: multText(m),
164899
- frame: frameId,
164900
- compartments: this.interfaceFallbackCompartments(m, uri, index2),
164901
- source: sourceOf2(m, uri)
164902
- });
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 } : {}
164903
165610
  }
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
+ });
164904
165635
  }
164905
165636
  } else if (isConnectionDecl(m) && m.isDef !== true) {
164906
- const clause = m.connect;
164907
- if (!clause)
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) {
164908
165651
  continue;
164909
- const name = nameOf2(m);
164910
- const type = typeText(m);
164911
- const label = name ?? type;
164912
- if (clause.ends?.length) {
164913
- const resolvedEnds = clause.ends.map((p) => ({ id: resolveEnd(this.connectorEndPath(p) ?? ""), end: p })).filter((x) => !!x.id);
164914
- if (resolvedEnds.length >= 2 && supportsEndpoints("connect", resolvedEnds.map(({ id: id2 }) => id2))) {
164915
- const dotId = `__nary_${eRef.n}__`;
164916
- nodes.push(dot(dotId, label ?? "", m));
164917
- for (const { id: endId, end } of resolvedEnds) {
164918
- edges.push({
164919
- id: `e${eRef.n++}`,
164920
- from: dotId,
164921
- to: endId,
164922
- kind: "connect",
164923
- name,
164924
- type,
164925
- isDef: false,
164926
- label,
164927
- endLabelTo: multText(end),
164928
- ...relationshipFields(m)
164929
- });
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 } : {}
164930
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 } : {}
164931
165693
  }
164932
- } else {
164933
- const from = resolveEnd(this.connectorEndPath(clause.source) ?? "");
164934
- const to = resolveEnd(this.connectorEndPath(clause.target) ?? "");
164935
- if (from && to && supportsEndpoints("connect", [from, to])) {
164936
- edges.push({
164937
- id: `e${eRef.n++}`,
164938
- from,
164939
- to,
164940
- kind: "connect",
164941
- name,
164942
- type,
164943
- isDef: false,
164944
- label,
164945
- endLabelFrom: multText(clause.source),
164946
- endLabelTo: multText(clause.target),
164947
- ...relationshipFields(m)
164948
- });
164949
- }
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
+ });
164950
165717
  }
164951
165718
  }
164952
165719
  }
@@ -164997,9 +165764,11 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164997
165764
  if (roots.length === 0) {
164998
165765
  roots = all.filter((d) => isDefPart(d) && !insideAPart(d) && !insideAnOccurrence(d) && hasInternalParts(d)).sort(byName);
164999
165766
  }
165000
- const { nodes, edges, frames } = this.renderIvRoots(ctx, roots, { packageFrames: true });
165767
+ const { nodes, edges, frames, definitionLayer } = this.renderIvRoots(ctx, roots, { packageFrames: true });
165001
165768
  const model = this.model("iv", scope, nodes, edges);
165002
165769
  model.frames = frames;
165770
+ if (definitionLayer)
165771
+ model.layers = { definitions: definitionLayer };
165003
165772
  model.meta = { ...model.meta, overview: true, groupMode: "nested" };
165004
165773
  model.root = { ...model.root, keyword: "package", name: nameOf2(scope) ?? model.root.name };
165005
165774
  if (nodes.length === 0 && frames.length === 0) {
@@ -165011,7 +165780,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165011
165780
  // (non-package) single view of `kind`; each renders as one overview tile.
165012
165781
  collectOverviewAnchors(scope, kind, index2) {
165013
165782
  const all = [...ast_utils_exports.streamAllContents(scope)];
165014
- 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));
165015
165784
  const naturalAnchors = all.filter(isNaturalAnchor);
165016
165785
  const hasNaturalDescendant = (candidate) => naturalAnchors.some((natural) => {
165017
165786
  for (let cur = natural.$container; cur && cur !== scope; cur = cur.$container) {
@@ -165072,7 +165841,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
165072
165841
  const scopeQ = qnameOf(ctx.anchor);
165073
165842
  const collected = this.collectOverviewAnchors(ctx.anchor, kind, ctx.index).sort((a2, b) => (qnameOf(a2) || nameOf2(a2) || "").localeCompare(qnameOf(b) || nameOf2(b) || ""));
165074
165843
  const sequenceScaffold = kind === "sv" && this.nestedUsages(ctx.anchor, isPartDecl).some((n2) => nameOf2(n2) !== void 0);
165075
- 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] : [];
165076
165846
  const nodes = [];
165077
165847
  const edges = [];
165078
165848
  const frames = [];
@@ -168024,18 +168794,31 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168024
168794
  geo.sizeZ = Math.abs(sizeZ);
168025
168795
  if (radius !== void 0)
168026
168796
  geo.radius = Math.abs(radius);
168027
- const yaw = state.fromFrame ? yawOf(state.world) : 0;
168028
- 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)
168029
168801
  geo.rot = local.rot;
168030
168802
  else if (yaw !== void 0 && yaw !== 0)
168031
168803
  geo.rot = yaw;
168032
- let approx = state.approx;
168033
- if (yaw === void 0)
168034
- 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
+ }
168035
168818
  if (state.fromFrame)
168036
168819
  geo.frame = true;
168037
- if (approx)
168038
- geo.approx = approx;
168820
+ if (state.approx)
168821
+ geo.approx = state.approx;
168039
168822
  return geo;
168040
168823
  }
168041
168824
  // issue #107 — a node's OWN placement relative to its parent frame: the
@@ -168128,7 +168911,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168128
168911
  return {
168129
168912
  present: true,
168130
168913
  transform: translationTransform(origin),
168131
- 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,
168132
168915
  unit,
168133
168916
  sourcePath
168134
168917
  };
@@ -168685,9 +169468,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168685
169468
  portsOf(node, index2, uri, ownerId, localUsage) {
168686
169469
  const ports = [];
168687
169470
  const map3 = /* @__PURE__ */ new Map();
169471
+ const origins = /* @__PURE__ */ new Map();
168688
169472
  const visibleNames = /* @__PURE__ */ new Set();
168689
169473
  const usageName = effectiveNameOf(node);
168690
- const makePort = (portNode, inheritedFromType, parentPortId, parentPath) => {
169474
+ const makePort = (portNode, inheritedFromType, parentPortId, parentPath, parentOrigin = []) => {
168691
169475
  const pn = effectiveNameOf(portNode);
168692
169476
  if (!pn)
168693
169477
  return void 0;
@@ -168772,7 +169556,9 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168772
169556
  if (usageName)
168773
169557
  map3.set(`${usageName}.${rel2}`, portId);
168774
169558
  map3.set(`${ownerId}.${rel2}`, portId);
168775
- return { port, portTypeDef, inheritedOwners };
169559
+ const origin = [...parentOrigin, portNode];
169560
+ origins.set(portId, origin);
169561
+ return { port, portTypeDef, inheritedOwners, origin };
168776
169562
  };
168777
169563
  const addNested = (portNode, made, inheritedFromType, path10, depth, seenTypes) => {
168778
169564
  if (depth > NESTED_PORT_MAX_DEPTH)
@@ -168783,18 +169569,20 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168783
169569
  const nestedSeen = /* @__PURE__ */ new Set();
168784
169570
  const nestedSources = [
168785
169571
  // declared in this port usage's body: as local as its parent is
168786
- ...membersOf(portNode).map((member) => ({ member, inherited: inheritedFromType })),
169572
+ ...membersOf(portNode).map((member) => ({ member, inherited: inheritedFromType, source: portNode })),
168787
169573
  // every effective owner contributes its body, nearest first
168788
- ...!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 }))) : []
168789
169575
  ];
168790
- for (const { member, inherited } of nestedSources) {
169576
+ for (const { member, inherited, source } of nestedSources) {
168791
169577
  if (!isPortDecl(member) || member.isDef === true)
168792
169578
  continue;
169579
+ if (inherited && this.isInheritedLibraryBackboneFeature(source, member, index2))
169580
+ continue;
168793
169581
  const nn = effectiveNameOf(member);
168794
169582
  if (!nn || nestedSeen.has(nn))
168795
169583
  continue;
168796
169584
  nestedSeen.add(nn);
168797
- const child = makePort(member, inherited, made.port.id, path10);
169585
+ const child = makePort(member, inherited, made.port.id, path10, made.origin);
168798
169586
  if (!child)
168799
169587
  continue;
168800
169588
  ports.push(child.port);
@@ -168816,11 +169604,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168816
169604
  if (isPortDecl(m) && m.isDef !== true)
168817
169605
  addPort(m, false);
168818
169606
  for (const inherited of this.inheritedFeatureOwnersOf(node, index2)) {
168819
- for (const m of membersOf(inherited))
168820
- 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))
168821
169609
  addPort(m, true);
169610
+ }
168822
169611
  }
168823
- return { ports, map: map3 };
169612
+ return { ports, map: map3, origins };
168824
169613
  }
168825
169614
  // REQ-100, issue #132 — the effective directions of a port type's directed
168826
169615
  // features, following the specialization chain and flipping `in`↔`out` across
@@ -168986,6 +169775,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168986
169775
  add("ports", usages(isPortDecl).map((m) => item(m)));
168987
169776
  add("parts", usages(isPartDecl).filter(notPortion).map((m) => item(m)));
168988
169777
  }
169778
+ add("connections", members.filter((member) => isConnectionDecl(member) && member.isDef !== true && isOrdinaryMember(member)).map((member) => item(member, this.featureText(member) || "(anonymous connection)")));
168989
169779
  add("interfaces", members.filter((m) => isInterfaceDecl(m) && m.isDef !== true && isOrdinaryMember(m)).map((m) => item(m, this.interfaceRowText(m))));
168990
169780
  add("features", members.filter((m) => m.$type === "FeatureShorthand" || m.$type === "FeatureRedefinitionShorthand").filter(isOrdinaryMember).filter((m) => !isOccurrenceModified(m)).map((m) => item(m, canonicalFeatureText(m))));
168991
169781
  const undirected = (guard) => usages(guard);
@@ -169101,25 +169891,6 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
169101
169891
  return ends;
169102
169892
  return [head2, ends].filter(Boolean).join(" ");
169103
169893
  }
169104
- // REQ-195 — Interface and binding guidance (usage-node fallback body)
169105
- // issue #110 — the body of the `«interface»` usage node the IV draws when the
169106
- // connect ends do not dock on the canvas. The written end paths ARE the
169107
- // information the missing edge would have carried, so they read as the node's
169108
- // `ends` compartment (OMG SysML 8.2.3.14), after any end members it declares.
169109
- interfaceFallbackCompartments(node, uri, index2) {
169110
- const clause = node.connect;
169111
- const paths = clause?.ends?.length ? clause.ends.map((end) => this.connectorEndPath(end) ?? "?") : clause ? [this.connectorEndPath(clause.source) ?? "?", this.connectorEndPath(clause.target) ?? "?"] : [];
169112
- const compartments = [...this.compartmentsFor(node, uri, index2) ?? []];
169113
- if (!paths.length)
169114
- return compartments.length ? compartments : void 0;
169115
- const items = paths.map((text) => ({ text }));
169116
- const existing = compartments.find((compartment) => compartment.title === "ends");
169117
- if (existing)
169118
- existing.items = [...existing.items, ...items];
169119
- else
169120
- compartments.push({ title: "ends", items });
169121
- return compartments;
169122
- }
169123
169894
  refText(v) {
169124
169895
  if (typeof v === "string")
169125
169896
  return v;
@@ -169642,91 +170413,6 @@ function outlineGroupForType(astType) {
169642
170413
  return CATEGORY_TO_OUTLINE_GROUP[categoryForType(astType)] ?? "structure";
169643
170414
  }
169644
170415
 
169645
- // ../language-server/out/src/platform/platform.js
169646
- var current;
169647
- function setPlatform(platform) {
169648
- current = platform;
169649
- }
169650
- function getPlatform() {
169651
- if (!current)
169652
- throw new Error("SysML: no platform installed - the entry point must call setPlatform() first.");
169653
- return current;
169654
- }
169655
- function hasPlatform() {
169656
- return current !== void 0;
169657
- }
169658
-
169659
- // ../language-server/out/src/services/library-index-manager.js
169660
- var SysmlIndexManager = class extends DefaultIndexManager {
169661
- constructor(services) {
169662
- super(services);
169663
- }
169664
- // REQ-075, REQ-383 — `libraryRoot` is a URI and the concrete file URIs come
169665
- // from the platform, because the two hosts index the same library under
169666
- // different schemes (`file:` on the desktop, `sysml-lib:` in a worker).
169667
- loadPrecomputedLibraryIndex(index2, libraryRoot) {
169668
- const platform = getPlatform();
169669
- let symbolCount = 0;
169670
- for (const file of index2.files) {
169671
- const documentUri = platform.libraryUri(libraryRoot, file.path);
169672
- const descriptions = file.symbols.map((symbol) => this.deserializeSymbol(symbol, documentUri));
169673
- const uri = documentUri.toString();
169674
- this.symbolIndex.set(uri, descriptions);
169675
- this.symbolByTypeIndex.clear(uri);
169676
- symbolCount += descriptions.length;
169677
- }
169678
- return symbolCount;
169679
- }
169680
- deserializeSymbol(symbol, documentUri) {
169681
- return {
169682
- name: symbol.name,
169683
- type: symbol.type,
169684
- path: symbol.path,
169685
- documentUri,
169686
- nameSegment: symbol.nameSegment,
169687
- selectionSegment: symbol.selectionSegment,
169688
- // REQ-068 — preserve declared visibility for wildcard re-export.
169689
- ...symbol.isPrivate ? { isPrivate: true } : {},
169690
- ...symbol.visibility ? { visibility: symbol.visibility } : {},
169691
- // issue #152 — a re-exported alias is not owned nesting.
169692
- ...symbol.derivedKind ? { derivedKind: symbol.derivedKind } : {},
169693
- // REQ-242 — issue #103 — keeps type completion to definitions.
169694
- ...symbol.isUsage ? { isUsage: true } : {}
169695
- };
169696
- }
169697
- };
169698
- function isSysmlIndexManager(value) {
169699
- return typeof value.loadPrecomputedLibraryIndex === "function";
169700
- }
169701
- var libraryRoots = /* @__PURE__ */ new Set();
169702
- var ROOT_SEPARATOR = "\0";
169703
- function normalizeLibraryPath(p) {
169704
- return p.replace(/\\/gu, "/").replace(/\/+$/u, "").toLowerCase();
169705
- }
169706
- function registerLibraryRoot(root4) {
169707
- const uri = typeof root4 === "string" ? root4.length > 0 ? URI2.file(root4) : void 0 : root4;
169708
- if (!uri)
169709
- return;
169710
- libraryRoots.add(`${uri.scheme}${ROOT_SEPARATOR}${normalizeLibraryPath(uri.path)}`);
169711
- }
169712
- function isInsideDir(fsPath, dir) {
169713
- return fsPath === dir || fsPath.startsWith(`${dir}/`);
169714
- }
169715
- function isStandardLibraryUri(uri) {
169716
- const fsPath = normalizeLibraryPath(uri.path);
169717
- for (const entry of libraryRoots) {
169718
- const separator = entry.indexOf(ROOT_SEPARATOR);
169719
- if (entry.slice(0, separator) !== uri.scheme)
169720
- continue;
169721
- if (isInsideDir(fsPath, entry.slice(separator + 1)))
169722
- return true;
169723
- }
169724
- return fsPath.split("/").some((segment) => segment === "sysml.library");
169725
- }
169726
- function isLibraryDocument(doc) {
169727
- return doc.isLibraryDocument === true || isStandardLibraryUri(doc.uri);
169728
- }
169729
-
169730
170416
  // ../language-server/out/src/services/document-symbol-provider.js
169731
170417
  var SPECIALIZATION_KINDS2 = /* @__PURE__ */ new Set([":>", "specializes"]);
169732
170418
  var REDEFINITION_KINDS = /* @__PURE__ */ new Set([":>>", "redefines"]);
@@ -176575,7 +177261,7 @@ ${baseIndent}}`;
176575
177261
  return;
176576
177262
  const expected = interfaceEndTypes(definition);
176577
177263
  const connect = decl.connect;
176578
- const written = [connect?.source, connect?.target];
177264
+ const written = connect?.ends?.length ? connect.ends : [connect?.source, connect?.target];
176579
177265
  if (expected.length !== written.length)
176580
177266
  return;
176581
177267
  for (let position = 0; position < written.length; position += 1) {
@@ -182522,7 +183208,9 @@ var PORT_GLYPH_SIZE = 20;
182522
183208
  var PORT_GLYPH_HALF = PORT_GLYPH_SIZE / 2;
182523
183209
  var FEATURE_COMPARTMENT_CLEARANCE = PORT_GLYPH_HALF + 12;
182524
183210
  var ACTION_PIN_GLYPH_SIZE = PORT_GLYPH_SIZE;
183211
+ var CONNECTION_END_GLYPH_SIZE = 12;
182525
183212
  function endpointGlyphSize(endpoint) {
183213
+ if (endpoint.meta?.connectionEndPin === true) return CONNECTION_END_GLYPH_SIZE;
182526
183214
  return endpoint.pin ? ACTION_PIN_GLYPH_SIZE : PORT_GLYPH_SIZE;
182527
183215
  }
182528
183216
  var NESTED_PORT_GAP = 8;
@@ -182643,9 +183331,9 @@ function portDockSides(side) {
182643
183331
  }
182644
183332
  var PORT_MOVE_STRIP_ALONG = 24;
182645
183333
  var PORT_CONNECT_DOT_HIT = 10;
182646
- function portInteractionStrip(rect, side) {
183334
+ function portInteractionStrip(rect, side, singleOutwardHandle = false) {
182647
183335
  const vertical = side === "left" || side === "right";
182648
- 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);
182649
183337
  const run = Math.max(PORT_MOVE_STRIP_ALONG, rect.along);
182650
183338
  return {
182651
183339
  cx: rect.cx,
@@ -182731,6 +183419,10 @@ function sideLength(side, w, h, topReserve = 0) {
182731
183419
  }
182732
183420
  var SENDACCEPT_NOTCH = 14;
182733
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,
182734
183426
  initial: 6,
182735
183427
  final: 8,
182736
183428
  terminate: 8
@@ -182954,6 +183646,15 @@ function gvModeEdges(model, mode) {
182954
183646
  function isGvUsageNode(n2) {
182955
183647
  return n2.meta?.gvUsage === true;
182956
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
+ }
182957
183658
  var GV_BOXED_USAGE_COMPARTMENTS = /* @__PURE__ */ new Set(["parts", "occurrences", "timeslices", "snapshots", "individuals"]);
182958
183659
  function stripBoxedUsageCompartments(nodes) {
182959
183660
  let changed = false;
@@ -182970,11 +183671,16 @@ function applyGvMode(model, mode) {
182970
183671
  if (model.kind !== "gv") return model;
182971
183672
  const edges = gvModeEdges(model, mode);
182972
183673
  if (mode === "group") {
182973
- const nodes2 = model.nodes.filter((n2) => !isGvUsageNode(n2));
183674
+ const nodes2 = model.nodes.filter((n2) => !isGvUsageNode(n2) && !isConnectionDefinitionHub(n2));
182974
183675
  return { ...model, nodes: nodes2, edges };
182975
183676
  }
182976
- const nodes = stripBoxedUsageCompartments(model.nodes);
182977
- 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 };
182978
183684
  }
182979
183685
  var GV_CATEGORIES = [
182980
183686
  { key: "package", label: "Packages" },
@@ -182994,6 +183700,7 @@ var GV_CATEGORIES = [
182994
183700
  ];
182995
183701
  function gvNodeCategory(node) {
182996
183702
  if (node.shape === "package") return "package";
183703
+ if (isConnectionDefinitionHub(node)) return "interface";
182997
183704
  if (node.shape === "annotation" || node.shape === "dot") return "annotation";
182998
183705
  const k = node.keyword.toLowerCase().trim().replace(/^(individual|timeslice|snapshot|parallel|variation|variant|abstract|ref|derived)(\s+|$)/g, "").replace(/\s+def$/, "").trim();
182999
183706
  if (k === "" || k === "def") return "item";
@@ -183035,13 +183742,13 @@ function withoutOrphanedAnnotations(original, nodes, edges) {
183035
183742
  function applyGvFilters(model, hidden) {
183036
183743
  if (model.kind !== "gv" || hidden.size === 0) return model;
183037
183744
  const nodes = model.nodes.filter((n2) => n2.shape === "package" || !hidden.has(gvNodeCategory(n2)));
183038
- if (nodes.length === model.nodes.length) return model;
183039
183745
  const keptIds = /* @__PURE__ */ new Set();
183040
183746
  for (const n2 of nodes) {
183041
183747
  keptIds.add(n2.id);
183042
183748
  for (const p of n2.ports ?? []) keptIds.add(p.id);
183043
183749
  }
183044
- 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;
183045
183752
  return { ...model, nodes: withoutOrphanedAnnotations(model, nodes, edges), edges };
183046
183753
  }
183047
183754
  var PAD = 10;
@@ -183127,6 +183834,13 @@ function compartmentWidth(node) {
183127
183834
  return w;
183128
183835
  }
183129
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
+ }
183130
183844
  if (!/\bconstraint(?:\s+def)?$/u.test(node.keyword.trim().toLowerCase())) return node;
183131
183845
  const compartments = node.compartments?.filter((compartment) => compartment.title !== "expression");
183132
183846
  return {
@@ -183164,13 +183878,12 @@ function nodeSize(node, direction) {
183164
183878
  // connector attaching at the node edge meets the visible circle.
183165
183879
  case "initial":
183166
183880
  case "final":
183881
+ case "dot":
183167
183882
  return circularControlSize(node.shape);
183168
183883
  // OMG SysML v2.1 Part 1, Table 15: the terminate control node is an
183169
183884
  // unlabeled circled X, regardless of its optional semantic target.
183170
183885
  case "terminate":
183171
183886
  return circularControlSize(node.shape);
183172
- case "dot":
183173
- return { w: 14, h: 14 };
183174
183887
  case "decision":
183175
183888
  case "merge":
183176
183889
  return { w: Math.max(64, Math.ceil((twKind(node.name) + 8) / 0.7)), h: 48 };
@@ -183210,9 +183923,10 @@ function nodeSize(node, direction) {
183210
183923
  );
183211
183924
  const featureH = nodeFeatureCompartmentReserve(node);
183212
183925
  const compactContainer = node.meta?.internalsHidden === true;
183213
- 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;
183214
183928
  const bodyH = Math.max(
183215
- BOX_H,
183929
+ connectionUsage ? 42 : BOX_H,
183216
183930
  featureH === 0 ? NODE_COMPARTMENT_TOP + compartmentHeight(node) : 0,
183217
183931
  portRunHeight,
183218
183932
  portedSideLength(ph, "left"),
@@ -183437,6 +184151,18 @@ function assignPortHandles(ports, overrides, ownerShape) {
183437
184151
  for (const parent of topHandles) placeChildren(parent);
183438
184152
  return [...topHandles, ...nestedHandles];
183439
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
+ }
183440
184166
  function sizeWithOverride(base, o, minimum = { w: 48, h: 24 }) {
183441
184167
  return {
183442
184168
  w: o?.w !== void 0 ? Math.max(minimum.w, o.w) : base.w,
@@ -183553,11 +184279,38 @@ function applyCaseDefs(model, show) {
183553
184279
  };
183554
184280
  }
183555
184281
  var BEHAVIOR_FILTER_DEFAULTS = {
183556
- afv: { parts: true, performers: true, ports: true, defs: true, definedBy: true, actions: true },
183557
- 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
+ },
183558
184302
  // The Interconnection View is a STRUCTURE view; explicitly performed actions
183559
184303
  // are an optional behavior overlay and therefore the first layer to drop.
183560
- 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
+ }
183561
184314
  };
183562
184315
  function behaviorFilterDefaults(kind) {
183563
184316
  return BEHAVIOR_FILTER_DEFAULTS[kind === "stv" ? "stv" : kind === "iv" ? "iv" : "afv"];
@@ -183570,9 +184323,161 @@ function restoreBehaviorFilters(kind, saved) {
183570
184323
  return restored;
183571
184324
  }
183572
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
+ }
183573
184477
  function applyBehaviorFilters(model, filters) {
183574
184478
  if (!FILTERED_KINDS.has(model.kind)) return model;
183575
- const frames = model.frames ?? [];
184479
+ const visibleModel = filters.defs ? withVisibleDefinitionLayer(model) : model;
184480
+ const frames = visibleModel.frames ?? [];
183576
184481
  const dropped = /* @__PURE__ */ new Set();
183577
184482
  const reparented = /* @__PURE__ */ new Set();
183578
184483
  for (const frame2 of frames) {
@@ -183581,12 +184486,15 @@ function applyBehaviorFilters(model, filters) {
183581
184486
  if (!filters.parts && isOwnerPartFrame || !filters.performers && isPerformerLane) {
183582
184487
  reparented.add(frame2.id);
183583
184488
  }
183584
- 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);
183585
184490
  }
183586
- 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));
183587
184492
  const strippedPorts = !filters.ports && frames.some((f) => f.ports?.some((p) => p.meta?.afvPartPort === true));
183588
- const strippedDefinedBy = model.kind === "afv" && !filters.definedBy && model.edges.some((edge) => edge.kind === "definedBy");
183589
- 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
+ }
183590
184498
  const byId = new Map(frames.map((f) => [f.id, f]));
183591
184499
  const insideDropped = (id2) => {
183592
184500
  const seen = /* @__PURE__ */ new Set();
@@ -183617,7 +184525,7 @@ function applyBehaviorFilters(model, filters) {
183617
184525
  const ports = keepPorts(f.ports);
183618
184526
  return parent === f.parent && ports === f.ports ? f : { ...f, parent, ports };
183619
184527
  });
183620
- 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) => {
183621
184529
  const frame2 = visibleParent(n2.frame);
183622
184530
  return frame2 === n2.frame ? n2 : { ...n2, frame: frame2 };
183623
184531
  });
@@ -183627,8 +184535,15 @@ function applyBehaviorFilters(model, filters) {
183627
184535
  ...nextFrames.map((f) => f.id),
183628
184536
  ...nextFrames.flatMap((f) => (f.ports ?? []).map((p) => p.id))
183629
184537
  ]);
183630
- 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));
183631
- 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);
183632
184547
  }
183633
184548
  function isProxyPort(port) {
183634
184549
  return port.meta?.proxy === true;
@@ -183736,7 +184651,7 @@ function collapseIvModel(model, hiddenInternals) {
183736
184651
  const hidden = /* @__PURE__ */ new Set([...hiddenFrameIds, ...hiddenNodeIds, ...ownerOfHiddenPort.keys()]);
183737
184652
  const containerRootOf = (id2) => proxyHost.get(id2) ?? bodyDock.get(id2);
183738
184653
  const seenEdge = /* @__PURE__ */ new Set();
183739
- 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) => {
183740
184655
  const fromRoot = containerRootOf(e.from);
183741
184656
  const toRoot = containerRootOf(e.to);
183742
184657
  if (fromRoot !== void 0 && fromRoot === toRoot) return false;
@@ -184114,15 +185029,24 @@ function modelToFlow(model, opts) {
184114
185029
  const visibleFeatureH = featureCompartmentBandHeight(canvasNode);
184115
185030
  const featureH = nodeFeatureCompartmentReserve(canvasNode);
184116
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;
184117
185033
  const sizeOverride = ov[n2.layoutKey ?? n2.id];
184118
185034
  const compactCollapsed = canvasNode.meta?.internalsHidden === true;
184119
- 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;
184120
185044
  const projectedSize = isCircularControlShape(canvasNode.shape) ? base : isForkJoinShape(canvasNode.shape) ? forkJoinSizeWithOverride(presentationOverride, opts.direction) : sizeWithOverride(
184121
185045
  base,
184122
185046
  presentationOverride,
184123
185047
  {
184124
185048
  w: canvasNode.meta?.containerControls === true || canvasNode.meta?.internalsHidden === true ? COLLAPSED_CONTAINER_MIN_WIDTH : 48,
184125
- h: visibleFeatureH > 0 ? base.h : 24
185049
+ h: requiresNaturalCompartmentHeight ? base.h : 24
184126
185050
  }
184127
185051
  );
184128
185052
  const compactPortHandles = compactCollapsed ? assignPortHandles(canvasNode.ports, opts.portOverrides, canvasNode.shape) : [];
@@ -184152,7 +185076,7 @@ function modelToFlow(model, opts) {
184152
185076
  w: size.w,
184153
185077
  h: size.h,
184154
185078
  featureCompartmentHeight: featureH,
184155
- featureCompartmentMinHeight: visibleFeatureH > 0 ? base.h : void 0,
185079
+ featureCompartmentMinHeight: requiresNaturalCompartmentHeight ? base.h : void 0,
184156
185080
  showPortLabels: opts.showPortLabels,
184157
185081
  showMult: opts.showMult,
184158
185082
  connectPointSpacing
@@ -184273,18 +185197,27 @@ function modelToFlow(model, opts) {
184273
185197
  const routeKey = edgeRouteKey(e, dup, persistedEndpoints);
184274
185198
  const savedAnchor = opts.edgeAnchors?.[routeKey];
184275
185199
  const settledGeometry = opts.edgeGeometry?.[routeKey];
184276
- const savedSourceHandle = fromPort ? void 0 : canonicalSideHandle(savedAnchor?.sourceHandle ?? settledGeometry?.sourceHandle);
184277
- const savedTargetHandle = toPort ? void 0 : canonicalSideHandle(savedAnchor?.targetHandle ?? settledGeometry?.targetHandle);
184278
- const savedSourcePortSide = parsePortAnchorHandleId(settledGeometry?.sourceHandle)?.side;
184279
- 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);
184280
185213
  retainExplicitHandle(source, savedSourceHandle);
184281
185214
  retainExplicitHandle(target, savedTargetHandle);
184282
185215
  edges.push({
184283
185216
  id: e.id,
184284
185217
  source,
184285
185218
  target,
184286
- ...fromPort ? { sourceHandle: savedSourcePortSide ? portAnchorHandleId(e.from, savedSourcePortSide) : e.from } : savedSourceHandle ? { sourceHandle: savedSourceHandle } : {},
184287
- ...toPort ? { targetHandle: savedTargetPortSide ? portAnchorHandleId(e.to, savedTargetPortSide) : e.to } : savedTargetHandle ? { targetHandle: savedTargetHandle } : {},
185219
+ ...sourceHandle ? { sourceHandle } : {},
185220
+ ...targetHandle ? { targetHandle } : {},
184288
185221
  type: "sysml",
184289
185222
  data: {
184290
185223
  kind: model.kind,
@@ -184293,7 +185226,9 @@ function modelToFlow(model, opts) {
184293
185226
  showMult: opts.showMult,
184294
185227
  routeKey,
184295
185228
  route: opts.edgeRoutes?.[routeKey],
184296
- settledGeometry
185229
+ ...explicitSourceHandle ? { explicitSourceAnchor: true } : {},
185230
+ ...explicitTargetHandle ? { explicitTargetAnchor: true } : {},
185231
+ settledGeometry: settledHandlesMatch ? settledGeometry : void 0
184297
185232
  },
184298
185233
  selectable: true,
184299
185234
  reconnectable: true,
@@ -184385,7 +185320,7 @@ function clampDirection(kind, d) {
184385
185320
 
184386
185321
  // ../extension/src/webview/diagram/flow/geo-layout.ts
184387
185322
  var GEO_MARGIN_LEFT = 48;
184388
- var GEO_TOP_PAD = 28;
185323
+ var GEO_TOP_PAD = 40;
184389
185324
  var GEO_TARGET_W = 860;
184390
185325
  var GEO_TARGET_H = 520;
184391
185326
  var GEO_DEF_W = 400;
@@ -184400,33 +185335,110 @@ function geoProject(f, x, y, z2) {
184400
185335
  const isoY = (x + y) * f.sinA - z2;
184401
185336
  return [f.ox + (isoX - f.isoMinX) * f.scale, f.oy + (isoY - f.isoMinY) * f.scale];
184402
185337
  }
184403
- 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) {
184404
185361
  const g = node.geo;
184405
- const z2 = g.z ?? 0;
184406
185362
  switch (g.shape) {
184407
185363
  case "sphere": {
184408
185364
  const r = g.radius ?? def / 2;
184409
- return { hx: r, hy: r, zlo: z2 - r, zhi: z2 + r };
185365
+ return { hx: r, hy: r, hz: r };
184410
185366
  }
184411
185367
  case "cylinder":
184412
185368
  case "cone": {
184413
185369
  const r = g.radius ?? def / 2;
184414
- const h = g.sizeZ ?? def;
184415
- return { hx: r, hy: r, zlo: z2 - h / 2, zhi: z2 + h / 2 };
185370
+ return { hx: r, hy: r, hz: (g.sizeZ ?? def) / 2 };
184416
185371
  }
184417
185372
  case "pyramid":
184418
185373
  case "wedge":
184419
- case "box": {
184420
- const sx = g.sizeX ?? def;
184421
- const sy = g.sizeY ?? def;
184422
- const sz = g.sizeZ ?? def;
184423
- return { hx: sx / 2, hy: sy / 2, zlo: z2 - sz / 2, zhi: z2 + sz / 2 };
184424
- }
184425
- default: {
184426
- const sx = g.sizeX ?? GEO_DEF_W;
184427
- const sy = g.sizeY ?? GEO_DEF_H;
184428
- 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
+ ];
184429
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]]);
184430
185442
  }
184431
185443
  }
184432
185444
  function makeGeoFrameNode(kind, w, h, geo) {
@@ -184506,26 +185518,30 @@ function layoutPlan(kind, placed, strip, axes, edges) {
184506
185518
  function layoutIso(kind, placed, strip, unit, edges) {
184507
185519
  const cosA = Math.cos(Math.PI / 6);
184508
185520
  const sinA = Math.sin(Math.PI / 6);
184509
- let pminX = 0, pmaxX = 0, pminY = 0, pmaxY = 0, pmaxZ = 0;
185521
+ let centreMinX = 0, centreMaxX = 0, centreMinY = 0, centreMaxY = 0;
184510
185522
  for (const n2 of placed) {
184511
185523
  const g = n2.data.node.geo;
184512
- pminX = Math.min(pminX, g.x);
184513
- pmaxX = Math.max(pmaxX, g.x);
184514
- pminY = Math.min(pminY, g.y);
184515
- pmaxY = Math.max(pmaxY, g.y);
184516
- 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);
184517
185528
  }
184518
- const span = Math.max(pmaxX - pminX, pmaxY - pminY, 1);
185529
+ const span = Math.max(centreMaxX - centreMinX, centreMaxY - centreMinY, 1);
184519
185530
  const def = Math.max(span * 0.12, 1e-4);
185531
+ let pminX = 0, pmaxX = 0, pminY = 0, pmaxY = 0, pmaxZ = 0;
184520
185532
  let isoMinX = Infinity, isoMaxX = -Infinity, isoMinY = Infinity, isoMaxY = -Infinity;
184521
185533
  const iso = (x, y, z2) => [(x - y) * cosA, (x + y) * sinA - z2];
184522
- const exts = /* @__PURE__ */ new Map();
185534
+ const pointsByNode = /* @__PURE__ */ new Map();
184523
185535
  for (const n2 of placed) {
184524
- const e = geoExtentOf(n2.data.node, def);
184525
- exts.set(n2, e);
184526
- const g = n2.data.node.geo;
184527
- for (const dx of [-e.hx, e.hx]) for (const dy of [-e.hy, e.hy]) for (const z2 of [e.zlo, e.zhi]) {
184528
- 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);
184529
185545
  isoMinX = Math.min(isoMinX, ix);
184530
185546
  isoMaxX = Math.max(isoMaxX, ix);
184531
185547
  isoMinY = Math.min(isoMinY, iy);
@@ -184537,11 +185553,9 @@ function layoutIso(kind, placed, strip, unit, edges) {
184537
185553
  const oy = GEO_TOP_PAD + 6;
184538
185554
  const frame3d = { ox, oy, scale, cosA, sinA, isoMinX, isoMinY, minX: pminX, maxX: pmaxX, minY: pminY, maxY: pmaxY, maxZ: pmaxZ, unit, def };
184539
185555
  const outPlaced = placed.map((n2) => {
184540
- const e = exts.get(n2);
184541
- const g = n2.data.node.geo;
184542
185556
  let sxMin = Infinity, syMin = Infinity, sxMax = -Infinity, syMax = -Infinity;
184543
- for (const dx of [-e.hx, e.hx]) for (const dy of [-e.hy, e.hy]) for (const z2 of [e.zlo, e.zhi]) {
184544
- 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);
184545
185559
  sxMin = Math.min(sxMin, px);
184546
185560
  syMin = Math.min(syMin, py);
184547
185561
  sxMax = Math.max(sxMax, px);
@@ -194080,7 +195094,7 @@ function buildEdgeBundles(candidates, obstacles = [], busPositions = {}) {
194080
195094
  const buckets = /* @__PURE__ */ new Map();
194081
195095
  const fanSizes = /* @__PURE__ */ new Map();
194082
195096
  const add = (edge, role) => {
194083
- if (edge.eligible === false) return;
195097
+ if (edge.eligible === false || edge.eligibleRole && edge.eligibleRole !== role) return;
194084
195098
  const pairKey = `${edge.source}\0${edge.target}\0${edge.className}`;
194085
195099
  if ((pairCounts.get(pairKey) ?? 0) > 1) return;
194086
195100
  const commonNodeId = role === "source" ? edge.source : edge.target;
@@ -194784,6 +195798,16 @@ async function elkLayout(elk, graph) {
194784
195798
  }
194785
195799
  }
194786
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) {
194787
195811
  return overrides?.[node.data.layoutKey] ?? overrides?.[node.id];
194788
195812
  }
194789
195813
  var AFV_LAYOUT_EDGE_KINDS = /* @__PURE__ */ new Set([
@@ -196010,7 +197034,8 @@ function semanticPortHandle(node, handle) {
196010
197034
  function validConcretePortAnchor(node, handle) {
196011
197035
  const parsed = parsePortAnchorHandleId(handle);
196012
197036
  const ph = parsed ? portOnNode(node, parsed.portId) : void 0;
196013
- 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);
196014
197039
  }
196015
197040
  function isStrictDescendant(nodeId, ancestorId, byId) {
196016
197041
  let cur = byId.get(nodeId)?.parentId;
@@ -196025,6 +197050,7 @@ function isStrictDescendant(nodeId, ancestorId, byId) {
196025
197050
  function inferredPortAnchorSide(node, other, portId, byId) {
196026
197051
  const ph = portOnNode(node, portId);
196027
197052
  if (!ph) return "right";
197053
+ if (ph.port.meta?.connectionEndPin === true) return ph.side;
196028
197054
  return other.id === node.id || isStrictDescendant(other.id, node.id, byId) ? OPPOSITE[ph.side] : ph.side;
196029
197055
  }
196030
197056
  function assignPortAnchorSides(nodes, edges) {
@@ -196115,6 +197141,38 @@ function nextSideAnchor(counts, offsetsCache, assignedOffsets, node, side, spaci
196115
197141
  }
196116
197142
  function assignEdgeSides(nodes, edges, topDown = false, connectPointSpacing = CONNECT_POINT_SPACING_DEFAULT, behaviorDirection) {
196117
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
+ }
196118
197176
  const sideCounts = /* @__PURE__ */ new Map();
196119
197177
  const offsetsCache = /* @__PURE__ */ new Map();
196120
197178
  const assignedOffsets = /* @__PURE__ */ new Map();
@@ -196134,7 +197192,7 @@ function assignEdgeSides(nodes, edges, topDown = false, connectPointSpacing = CO
196134
197192
  const requests = /* @__PURE__ */ new Map();
196135
197193
  const requestPreference = /* @__PURE__ */ new Map();
196136
197194
  const requestKey = (edge, role) => `${edge.id}|${role}`;
196137
- 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);
196138
197196
  const diamondPreference = (node, side, dx, dy) => {
196139
197197
  const shape = node.data.node?.shape;
196140
197198
  if (shape !== "decision" && shape !== "merge") return void 0;
@@ -197128,6 +198186,50 @@ function routePointNearSource(points, source, target, distance2) {
197128
198186
  }
197129
198187
  return void 0;
197130
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
+ }
197131
198233
  function edgeLabelPoint(edge, centre, mark, points, source, target) {
197132
198234
  const branchLabel = edge.kind === "succession" && (edge.label === "else" || /^\[.*\]$/u.test(edge.label ?? ""));
197133
198235
  if (!branchLabel) return successionFlowLabelPoint(centre, mark);
@@ -197270,13 +198372,25 @@ function treeFacingDock(node, other, byId) {
197270
198372
  const normal = otherCentre.y >= centre.y ? DOCK_NORMAL.bottom : DOCK_NORMAL.top;
197271
198373
  return { point: { x: centre.x, y: centre.y + normal.y * h / 2 }, normal };
197272
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
+ }
197273
198386
  function bundleCandidateFor(edge, byId) {
197274
198387
  const semantic = edge.data?.edge;
197275
198388
  const sourceNode = byId.get(edge.source);
197276
198389
  const targetNode = byId.get(edge.target);
197277
198390
  if (!semantic || !sourceNode || !targetNode) return void 0;
197278
- const sourceFallback = treeFacingDock(sourceNode, targetNode, byId);
197279
- 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);
197280
198394
  const explicitSource = dockPoint(sourceNode, edge.sourceHandle, byId, sourceFallback.point);
197281
198395
  const explicitTarget = dockPoint(targetNode, edge.targetHandle, byId, targetFallback.point);
197282
198396
  const source = explicitSource.normal ? explicitSource : sourceFallback;
@@ -197299,7 +198413,8 @@ function bundleCandidateFor(edge, byId) {
197299
198413
  // remaining separate semantic edges. Once one connector owns manual
197300
198414
  // waypoints it deliberately leaves that automatic fan and keeps its
197301
198415
  // individually movable route.
197302
- 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 } : {}
197303
198418
  };
197304
198419
  }
197305
198420
  function movingNodeIds(nodes, byId) {
@@ -197472,7 +198587,7 @@ function edgeBundles(nodes, edges, runtime = defaultBundleRuntime) {
197472
198587
  function projectedBundleMember(edge) {
197473
198588
  return edge.data?.bundleMember;
197474
198589
  }
197475
- function projectIndividualGvTreeRenderEdges(nodes, semanticEdges, runtime) {
198590
+ function projectAutomaticBusRenderEdges(nodes, semanticEdges, runtime) {
197476
198591
  const bundles = edgeBundles(nodes, semanticEdges, runtime);
197477
198592
  if (!bundles.size) return semanticEdges;
197478
198593
  return semanticEdges.map((edge) => {
@@ -198086,6 +199201,7 @@ body {
198086
199201
  /* REQ-186/188/189/199/206/210 \u2014 the dashed labelled families */
198087
199202
  .dlink.allocate, .dlink.frame, .dlink.derive, .dlink.causation, .dlink.import, .dlink.expose { stroke-dasharray: 4 3; }
198088
199203
  .dlink.annotation { stroke-dasharray: 2 3; }
199204
+ .dlink.elaboration { stroke-dasharray: 2 3; pointer-events: none; }
198089
199205
  .dlink.reply { stroke-dasharray: 4 3; } /* REQ-202 \u2014 sequence reply arrows */
198090
199206
  /* issue #84 \u2014 REQ-202: a message is dragged up/down to change WHEN it happens.
198091
199207
  The grip is the invisible hit band over the arrow (a row-resize cursor is the
@@ -198124,6 +199240,15 @@ body {
198124
199240
  * on rather than the flow colour (user direction 2026-08-09). Its rounded corners and its
198125
199241
  * direction arrow come from the shared port path in nodes.tsx. */
198126
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)); }
198127
199252
  /* REQ-194 \u2014 the port direction ARROW: a shaft across the glyph with a solid head
198128
199253
  * at each directed end (--> out, <-- in, <-> inout), per OMG 8.2.3.12.
198129
199254
  * NOTE: no backticks in this file's CSS \u2014 STYLE is a template literal. */
@@ -198288,15 +199413,19 @@ body {
198288
199413
  /* REQ-205 \u2014 Geometry View 3D (isometric) shapes + ground frame */
198289
199414
  .dgeo3d-grid { stroke-width: 1; opacity: 0.35; }
198290
199415
  .dgeo3d-face { stroke: var(--element-outline); stroke-width: 1; stroke-linejoin: round; }
198291
- .dgeo3d-face.top { fill: var(--element-fill); fill-opacity: 0.56; }
198292
- .dgeo3d-face.side { fill: var(--element-fill); fill-opacity: 0.38; }
198293
- .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; }
198294
199419
  .dgeo3d-edge { stroke: var(--element-outline); stroke-width: 1; opacity: 0.7; }
198295
199420
  .dgeo3d-node { cursor: pointer; }
198296
199421
  .dgeo3d-node:hover .dgeo3d-face { fill-opacity: 0.42; }
198297
199422
  .dgeo3d-node.selected .dgeo3d-face { stroke: var(--accent); }
198298
199423
  .dgeo3d-node.selected .dgeo3d-face.top { fill: var(--accent); fill-opacity: 0.34; }
198299
- .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
+ }
198300
199429
  /* REQ-205 \u2014 an object whose coordinate-frame transformation could not be fully
198301
199430
  decided is DRAWN and MARKED, never silently misplaced: its outline goes dashed
198302
199431
  and it carries a warning glyph whose tooltip names the reason. (The issue
@@ -198873,14 +200002,17 @@ svg.react-flow__connectionline { z-index: 1; }
198873
200002
  * Each 5px dot has a compact hit box and can start or end a connector. The chosen
198874
200003
  * handle is the chosen dock point, while the central body stays selectable. */
198875
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; }
198876
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; }
198877
200008
  /* REQ-370: body snap points are hollow circles. Port and pin endpoints are
198878
200009
  * solid squares, so adjacent targets cannot be mistaken for each other. */
198879
200010
  .react-flow__handle.rf-handle-side::after { background: var(--bg-1); border: 1.5px solid var(--cyan); }
198880
200011
  .react-flow__handle.rf-handle-port::after { border-radius: 1px; }
198881
- /* The start/done centre stays available for normal node selection and dragging
198882
- * even though four 12px handle targets meet over these exact-size glyphs. The
198883
- * 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. */
198884
200016
  .rf-circular-control-hit {
198885
200017
  position: absolute; left: 50%; top: 50%; width: 50%; height: 50%;
198886
200018
  transform: translate(-50%, -50%); border-radius: 50%;
@@ -198894,6 +200026,7 @@ svg.react-flow__connectionline { z-index: 1; }
198894
200026
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-side.connectionindicator,
198895
200027
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-lifeline.connectionindicator { opacity: 0.42; }
198896
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; }
198897
200030
  .react-flow.sysml-connecting .react-flow__handle.rf-handle-port.active { opacity: 1; }
198898
200031
  /* The nearest semantically valid body target is stronger than the candidate
198899
200032
  * points. Port and pin targets use the highlighted endpoint glyph instead. */
@@ -199044,6 +200177,9 @@ svg.react-flow__connectionline { z-index: 1; }
199044
200177
  .rf-edge-label { position: absolute; pointer-events: none; font-family: var(--font-mono); font-size: 9px;
199045
200178
  color: var(--fg-0); background: transparent; border: 0; padding: 0; white-space: nowrap; }
199046
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); }
199047
200183
 
199048
200184
  /* REQ-366/386 \u2014 two adjacent, independent container presentation controls.
199049
200185
  * +/\u2212 owns graphical internals; the outlined list owns textual feature
@@ -199126,6 +200262,11 @@ function labelExtent(text) {
199126
200262
  function labelSvg(text, x, y, cls) {
199127
200263
  return `<text class="${cls}" x="${Math.round(x)}" y="${Math.round(y)}" text-anchor="middle" dominant-baseline="central">${escapeXml(text)}</text>`;
199128
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
+ }
199129
200270
  function buildDiagramSvg(options) {
199130
200271
  const { nodes, edges, lineStyle, nodeMarkup } = options;
199131
200272
  if (!nodes.length) return null;
@@ -199204,6 +200345,22 @@ function buildDiagramSvg(options) {
199204
200345
  const markerStart = style2.markerStart ? ` marker-start="${style2.markerStart}"` : "";
199205
200346
  const markerEnd = style2.markerEnd ? ` marker-end="${style2.markerEnd}"` : "";
199206
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
+ }
199207
200364
  const sequencingMark = successionFlowMarkGeometry(
199208
200365
  style2,
199209
200366
  geometry.label,
@@ -199231,14 +200388,22 @@ function buildDiagramSvg(options) {
199231
200388
  labelParts.push(labelSvg(semantic.label, labelPoint.x, labelPoint.y, `elabel ${semantic.kind}`));
199232
200389
  extendLabel(semantic.label, labelPoint.x, labelPoint.y);
199233
200390
  }
199234
- if (edge.data?.showMult) {
199235
- if (semantic?.endLabelFrom) {
199236
- labelParts.push(labelSvg(semantic.endLabelFrom, geometry.source.x, geometry.source.y, "elabel mult"));
199237
- extendLabel(semantic.endLabelFrom, geometry.source.x, geometry.source.y);
199238
- }
199239
- if (semantic?.endLabelTo) {
199240
- labelParts.push(labelSvg(semantic.endLabelTo, geometry.target.x, geometry.target.y, "elabel mult"));
199241
- 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);
199242
200407
  }
199243
200408
  }
199244
200409
  }
@@ -199707,6 +200872,7 @@ var TOOLBOX = {
199707
200872
  elements: [
199708
200873
  el("part", "part", "\u25A2"),
199709
200874
  el("port", "port", "\u25AB"),
200875
+ el("connection", "connection", "\u25AD"),
199710
200876
  el("attribute", "attribute", "\u2013"),
199711
200877
  el("item", "item", "\u25C7"),
199712
200878
  el("constraint", "constraint", "{}"),
@@ -199871,7 +201037,7 @@ var COMMON_USAGE_KINDS = [
199871
201037
  var CASE_CHILDREN = ["subject", "actor", "part", "attribute", "action", "item", "requirement"];
199872
201038
  var CHILD_KINDS = {
199873
201039
  package: ["package", ...DEFINITION_KINDS, ...COMMON_USAGE_KINDS],
199874
- part: ["part", "attribute", "port", "item", "constraint", "action", "state", "calc", "occurrence"],
201040
+ part: ["part", "attribute", "port", "item", "connection", "constraint", "action", "state", "calc", "occurrence"],
199875
201041
  item: ["attribute", "port", "part", "item"],
199876
201042
  // REQ-006/REQ-192 — a port owns the features that describe what crosses
199877
201043
  // its boundary. Keep the direction in the creation choice so an IV user
@@ -199976,6 +201142,7 @@ function isWritableRelationEndpoint(endpoint) {
199976
201142
  }
199977
201143
  function directRelationForHandle(viewKind, endpoint) {
199978
201144
  if (!endpoint || !isWritableRelationEndpoint(endpoint)) return void 0;
201145
+ if (viewKind === "iv" && (endpoint.connectionEnd || normalizedElementKeyword(endpoint.keyword).base === "connection end")) return "connectionEnd";
199979
201146
  return relationToolsForNode(endpoint.keyword, viewKind, endpoint.shape).find((tool) => tool.kind !== "terminate" && tool.kind !== "finish")?.kind;
199980
201147
  }
199981
201148
  var START_PSEUDOSTATE_BASES = /* @__PURE__ */ new Set(["start", "initial"]);
@@ -200066,6 +201233,65 @@ function relationToolsForNode(keyword, viewKind, shape) {
200066
201233
  var import_react7 = __toESM(require_react());
200067
201234
  var import_jsx_runtime3 = __toESM(require_jsx_runtime());
200068
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
+ }
200069
201295
  var GEO_HANDLE_POSITION = {
200070
201296
  top: Position3.Top,
200071
201297
  right: Position3.Right,
@@ -200188,15 +201414,16 @@ function IsoAxes({ f, marker }) {
200188
201414
  }
200189
201415
  function geoShapeBody(node, f) {
200190
201416
  const g = node.geo;
200191
- const e = geoExtentOf(node, f.def);
201417
+ const { hx, hy, hz } = geoHalfExtentsOf(node, f.def);
200192
201418
  const P = (x, y, z2) => geoProject(f, x, y, z2);
200193
- const { hx, hy, zlo, zhi } = e;
200194
- const cx = g.x;
200195
- const cy = g.y;
201419
+ const PL = (point) => {
201420
+ const world = geoWorldPoint(g, point);
201421
+ return P(world[0], world[1], world[2]);
201422
+ };
200196
201423
  switch (g.shape) {
200197
201424
  case "sphere": {
200198
- const c = P(cx, cy, (zlo + zhi) / 2);
200199
- 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]));
200200
201427
  const r = Math.max(...pts.map((p) => Math.hypot(p[0] - c[0], p[1] - c[1])));
200201
201428
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200202
201429
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("circle", { className: "dgeo3d-face top", cx: c[0], cy: c[1], r }),
@@ -200205,64 +201432,79 @@ function geoShapeBody(node, f) {
200205
201432
  }
200206
201433
  case "cylinder":
200207
201434
  case "cone": {
200208
- const exr = (z2) => {
200209
- const c = P(cx, cy, z2);
200210
- const cardinals = [P(cx + hx, cy, z2), P(cx - hx, cy, z2), P(cx, cy + hy, z2), P(cx, cy - hy, z2)];
200211
- const rx = (Math.max(...cardinals.map((p) => p[0])) - Math.min(...cardinals.map((p) => p[0]))) / 2;
200212
- const ry = (Math.max(...cardinals.map((p) => p[1])) - Math.min(...cardinals.map((p) => p[1]))) / 2;
200213
- return { c, rx, ry };
200214
- };
200215
- 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]));
200216
201442
  if (g.shape === "cone") {
200217
- 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);
200218
201447
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200219
- /* @__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 }),
200220
- /* @__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]]]) }),
200221
- /* @__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" })
200222
201450
  ] });
200223
201451
  }
200224
- 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;
200225
201459
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200226
- /* @__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" }),
200227
- /* @__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]]]) }),
200228
- /* @__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) })
200229
201463
  ] });
200230
201464
  }
200231
201465
  case "pyramid": {
200232
- const apex = P(cx, cy, zhi);
200233
- const bxp = P(cx + hx, cy - hy, zlo);
200234
- const bxp2 = P(cx + hx, cy + hy, zlo);
200235
- const byp = P(cx - hx, cy + hy, zlo);
200236
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200237
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([bxp, bxp2, apex]) }),
200238
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([byp, bxp2, apex]) }),
200239
- /* @__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" })
200240
- ] });
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]]);
200241
201474
  }
200242
201475
  case "wedge": {
200243
- const rf = P(cx, cy - hy, zhi);
200244
- const rb = P(cx, cy + hy, zhi);
200245
- const blr = P(cx + hx, cy - hy, zlo);
200246
- const bur = P(cx + hx, cy + hy, zlo);
200247
- const bll = P(cx - hx, cy - hy, zlo);
200248
- const bul = P(cx - hx, cy + hy, zlo);
200249
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200250
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side2", points: ptStr([blr, bur, rb, rf]) }),
200251
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr([bll, bul, rb, rf]) }),
200252
- /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face side", points: ptStr([bul, bur, rb]) })
200253
- ] });
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]]);
200254
201485
  }
200255
201486
  case "box": {
200256
- const t = (sx, sy) => P(cx + sx * hx, cy + sy * hy, zhi);
200257
- const b = (sx, sy) => P(cx + sx * hx, cy + sy * hy, zlo);
200258
- return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("g", { children: [
200259
- /* @__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)]) }),
200260
- /* @__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)]) }),
200261
- /* @__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)]) })
200262
- ] });
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
+ ]);
200263
201505
  }
200264
201506
  default: {
200265
- 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])];
200266
201508
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("polygon", { className: "dgeo3d-face top", points: ptStr(q) });
200267
201509
  }
200268
201510
  }
@@ -200285,6 +201527,7 @@ function GeoShapeNode({ id: id2, data, selected: selected2 }) {
200285
201527
  const dim = useInteraction(ctx.store, (s) => s.matchedIds ? !s.matchedIds.has(id2) : false);
200286
201528
  const origin = data.origin;
200287
201529
  const iso = data.geo3d && node.geo && origin;
201530
+ const displayName = node.name.split(/::|\./u).filter(Boolean).at(-1) ?? node.name;
200288
201531
  const approx = node.geo?.approx;
200289
201532
  return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
200290
201533
  "div",
@@ -200300,7 +201543,10 @@ function GeoShapeNode({ id: id2, data, selected: selected2 }) {
200300
201543
  /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("svg", { className: "rf-node-svg", width: w, height: h, style: { overflow: "visible" }, children: [
200301
201544
  iso ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
200302
201545
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("g", { className: `dgeo3d-node${selected2 ? " selected" : ""}`, transform: `translate(${-origin.x},${-origin.y})`, children: geoShapeBody(node, data.geo3d) }),
200303
- /* @__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
+ ] })
200304
201550
  ] }) : /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
200305
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 }),
200306
201552
  /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("text", { className: "dnode-label", x: w / 2, y: h / 2 + 3, textAnchor: "middle", children: node.name })
@@ -200521,7 +201767,7 @@ function usePortDrag(containerRef, w, h, topReserve, onPortMove, onPortPreview,
200521
201767
  };
200522
201768
  return { dragId, onPortPointerDown };
200523
201769
  }
200524
- 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 }) {
200525
201771
  const dimensions = typeof size === "number" ? { width: size, height: size } : size;
200526
201772
  const style2 = dimensions ? { left: x, top: y, ...dimensions, transform: "translate(-50%,-50%)" } : { left: x, top: y, transform: "translate(-50%,-50%)" };
200527
201773
  const enabled = connectable && !occluded;
@@ -200537,7 +201783,8 @@ function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, sta
200537
201783
  className: cls,
200538
201784
  isConnectable: enabled,
200539
201785
  isConnectableStart: false,
200540
- isConnectableEnd: enabled
201786
+ isConnectableEnd: enabled,
201787
+ onContextMenu
200541
201788
  }
200542
201789
  ),
200543
201790
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
@@ -200550,7 +201797,8 @@ function DualHandle({ id: id2, side, x, y, kind, active, connectable = true, sta
200550
201797
  className: cls,
200551
201798
  isConnectable: enabled,
200552
201799
  isConnectableStart: enabled && startable,
200553
- isConnectableEnd: enabled
201800
+ isConnectableEnd: enabled,
201801
+ onContextMenu
200554
201802
  }
200555
201803
  )
200556
201804
  ] });
@@ -200892,7 +202140,15 @@ function AnnotationBody({ node, w, h }) {
200892
202140
  }
200893
202141
  function DotBody({ node, w, h }) {
200894
202142
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { children: [
200895
- /* @__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
+ ),
200896
202152
  node.name ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("text", { className: "dnode-kind", x: w / 2 + 9, y: h / 2 + 4, children: node.name }) : ""
200897
202153
  ] });
200898
202154
  }
@@ -200961,6 +202217,8 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200961
202217
  };
200962
202218
  })() : void 0;
200963
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" : ""}`;
200964
202222
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
200965
202223
  "g",
200966
202224
  {
@@ -200990,10 +202248,10 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200990
202248
  }
200991
202249
  );
200992
202250
  })() : null,
200993
- /* @__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)(
200994
202252
  "rect",
200995
202253
  {
200996
- 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}`,
200997
202255
  x: rect.x,
200998
202256
  y: rect.y,
200999
202257
  width: rect.width,
@@ -201001,12 +202259,12 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
201001
202259
  rx: ph.port.isDef ? 0 : PORT_CORNER_RADIUS
201002
202260
  }
201003
202261
  ),
201004
- 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: [
201005
202263
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir", d: arrowShaft() }),
201006
202264
  d === "out" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(1) }) : "",
201007
202265
  d === "in" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(-1) }) : ""
201008
202266
  ] }) : "",
201009
- selected2 && connectStart && !connectTargetSide && !proxy ? (() => {
202267
+ selected2 && connectStart && !connectTargetSide && !proxy && !connectionEnd ? (() => {
201010
202268
  const px = x + v.x * (half + PORT_PLUS_GAP);
201011
202269
  const py = y + v.y * (half + PORT_PLUS_GAP);
201012
202270
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { className: "dport-plus", children: [
@@ -201014,7 +202272,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
201014
202272
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("line", { x1: px, y1: py - PORT_PLUS_HALF, x2: px, y2: py + PORT_PLUS_HALF })
201015
202273
  ] });
201016
202274
  })() : "",
201017
- connectTargetSide ? (() => {
202275
+ connectTargetSide && !connectionEnd ? (() => {
201018
202276
  const at2 = portDockAt(rect, ph.side, connectTargetSide);
201019
202277
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("circle", { className: "dport-anchor", cx: at2.x, cy: at2.y, r: 1.7 });
201020
202278
  })() : "",
@@ -201270,7 +202528,7 @@ function NodeBody({ node, data, selectedPortId, hoveredPortId, movingPortId, con
201270
202528
  hovered: hoveredPortId === ph.port.id,
201271
202529
  moving: movingPortId === ph.port.id,
201272
202530
  connectTargetSide: connectTargetPortId === ph.port.id ? connectTargetPortSide : void 0,
201273
- 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"),
201274
202532
  onSelect: onPortSelect,
201275
202533
  onContext: onPortContext
201276
202534
  },
@@ -201322,15 +202580,16 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201322
202580
  shape: node.shape,
201323
202581
  source: node.source
201324
202582
  });
201325
- 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);
201326
202585
  const selectedPortId = useInteraction(ctx.store, (s) => typeof s.selectedId === "string" && myPortIds.includes(s.selectedId) ? s.selectedId : void 0);
201327
202586
  const dropClass = useDropState(ctx.store, id2);
201328
202587
  const [hoveredPortId, setHoveredPortId] = (0, import_react9.useState)(void 0);
201329
202588
  const [sidePointsHovered, setSidePointsHovered] = (0, import_react9.useState)(false);
201330
202589
  const isPackageNode = isPackageEndpoint(node.shape, node.keyword);
201331
- const sideConnectable = data.kind !== "sv" && !isPackageNode;
202590
+ const sideConnectable = data.kind !== "sv" && !isPackageNode && !connectionUsage;
201332
202591
  const perimeterControl = keepsPerimeterSidePointsMounted(node.shape);
201333
- const sideOffsets = SIDES.map((side) => mountedNodeSidePointOffsets(
202592
+ const sideOffsets = connectionUsage ? SIDES.map(() => []) : SIDES.map((side) => mountedNodeSidePointOffsets(
201334
202593
  data,
201335
202594
  side,
201336
202595
  sideConnectable,
@@ -201453,17 +202712,39 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201453
202712
  );
201454
202713
  });
201455
202714
  }),
201456
- 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,
201457
202716
  ports.flatMap((ph) => {
201458
202717
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201459
202718
  const proxied = isProxyPort(ph.port);
201460
202719
  const startable = !proxied && directRelationForHandle(data.kind, {
201461
202720
  id: ph.port.id,
201462
202721
  name: ph.port.name,
201463
- 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",
201464
202723
  shape: ph.port.pin ? "pin" : "port",
201465
202724
  source: ph.port.source
201466
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
+ }
201467
202748
  const [outerId, innerId] = portConnectHandleIds(ph.port.id, ph.side);
201468
202749
  const outer = portDockAt(rect, ph.side, ph.side);
201469
202750
  const innerSide = OPPOSITE_DOCK_SIDE[ph.side];
@@ -201504,7 +202785,7 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
201504
202785
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201505
202786
  const nestedChild = ph.port.parentPort !== void 0;
201506
202787
  const proxyPort = isProxyPort(ph.port);
201507
- const strip = portInteractionStrip(rect, ph.side);
202788
+ const strip = portInteractionStrip(rect, ph.side, ph.port.meta?.connectionEndPin === true);
201508
202789
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
201509
202790
  "div",
201510
202791
  {
@@ -201875,7 +203156,7 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201875
203156
  hovered: hoveredPortId === ph.port.id,
201876
203157
  moving: dragId === ph.port.id,
201877
203158
  connectTargetSide: connectTargetPortId === ph.port.id ? connectTargetPortSide : void 0,
201878
- 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",
201879
203160
  onSelect: ctx.onPortMove ? void 0 : portSource,
201880
203161
  onContext: ctx.onContextNode
201881
203162
  },
@@ -201948,10 +203229,32 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201948
203229
  const startable = !proxied && directRelationForHandle(data.kind, {
201949
203230
  id: ph.port.id,
201950
203231
  name: ph.port.name,
201951
- 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",
201952
203233
  shape: ph.port.pin ? "pin" : "port",
201953
203234
  source: ph.port.source
201954
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
+ }
201955
203258
  const [outerId, innerId] = portConnectHandleIds(ph.port.id, ph.side);
201956
203259
  const outer = portDockAt(rect, ph.side, ph.side);
201957
203260
  const innerSide = OPPOSITE_DOCK_SIDE[ph.side];
@@ -201992,7 +203295,7 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201992
203295
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201993
203296
  const nestedChild = ph.port.parentPort !== void 0;
201994
203297
  const proxyPort = isProxyPort(ph.port);
201995
- const strip = portInteractionStrip(rect, ph.side);
203298
+ const strip = portInteractionStrip(rect, ph.side, ph.port.meta?.connectionEndPin === true);
201996
203299
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
201997
203300
  "div",
201998
203301
  {
@@ -202323,7 +203626,7 @@ async function renderDiagramSvg(document2, provider, options) {
202323
203626
  overrides: config.overrides,
202324
203627
  connectPointSpacing: config.connectPointSpacing
202325
203628
  });
202326
- 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;
202327
203630
  const indexOf2 = new Map(laid.nodes.map((node, i) => [node.id, i]));
202328
203631
  return buildDiagramSvg({
202329
203632
  nodes: laid.nodes,
@@ -202379,7 +203682,7 @@ async function runExport(command) {
202379
203682
  }
202380
203683
 
202381
203684
  // src/main.ts
202382
- var VERSION2 = true ? "0.22.0" : "dev";
203685
+ var VERSION2 = true ? "0.23.0" : "dev";
202383
203686
  function display(file) {
202384
203687
  const rel2 = path9.relative(process.cwd(), file);
202385
203688
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;