sysml-diagram 0.21.0 → 0.22.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
@@ -161733,9 +161733,16 @@ function nameOf(node) {
161733
161733
  const name = node?.name;
161734
161734
  if (typeof name === "string" && name.length > 0)
161735
161735
  return name;
161736
- if (node?.$type === "FeatureRedefinitionShorthand") {
161737
- const leading = node.preRelationships?.[0]?.targets?.[0];
161738
- return leading ? lastSegment(leading) : void 0;
161736
+ const relational = node;
161737
+ for (const relationship of [
161738
+ ...relational?.preRelationships ?? [],
161739
+ ...relational?.relationships ?? []
161740
+ ]) {
161741
+ if (relationship.kind !== ":>>" && relationship.kind !== "redefines")
161742
+ continue;
161743
+ const target = relationship.targets?.[0];
161744
+ if (target)
161745
+ return lastSegment(target);
161739
161746
  }
161740
161747
  if (node?.$type === "FeatureShorthand") {
161741
161748
  const text = node.$cstNode?.text;
@@ -161752,9 +161759,16 @@ function nameOf(node) {
161752
161759
  return void 0;
161753
161760
  }
161754
161761
  function unwrap(node) {
161755
- if (node.$type !== "PrefixMetadataMember")
161756
- return node;
161757
- return node.element ?? node;
161762
+ let current2 = node;
161763
+ const seen = /* @__PURE__ */ new Set();
161764
+ while ((current2.$type === "PrefixMetadataMember" || current2.$type === "MemberPrefixDecl") && !seen.has(current2)) {
161765
+ seen.add(current2);
161766
+ const element = current2.element;
161767
+ if (!element)
161768
+ break;
161769
+ current2 = element;
161770
+ }
161771
+ return current2;
161758
161772
  }
161759
161773
  function directNamedChildren(node) {
161760
161774
  const result = [];
@@ -161785,12 +161799,32 @@ function isSelfReference(candidate, node) {
161785
161799
  function lastSegment(path10) {
161786
161800
  return path10.split(/::|\./u).pop() ?? path10;
161787
161801
  }
161802
+ function simpleRelationName(path10) {
161803
+ let quoted = false;
161804
+ for (let index2 = 0; index2 < path10.length; index2 += 1) {
161805
+ const char = path10[index2];
161806
+ if (quoted) {
161807
+ if (char === "\\")
161808
+ index2 += 1;
161809
+ else if (char === "'")
161810
+ quoted = false;
161811
+ continue;
161812
+ }
161813
+ if (char === "'")
161814
+ quoted = true;
161815
+ else if (char === "." || char === ":" && path10[index2 + 1] === ":")
161816
+ return void 0;
161817
+ }
161818
+ const withoutMultiplicity = path10.replace(/\s*\[[^\]]*\]\s*$/u, "").trim();
161819
+ return withoutMultiplicity ? canonicalEscapedName(withoutMultiplicity) : void 0;
161820
+ }
161788
161821
  var FeaturePathResolver = class {
161789
161822
  indexManager;
161790
161823
  descriptions;
161791
161824
  documents;
161792
161825
  locator;
161793
161826
  snapshots = /* @__PURE__ */ new WeakMap();
161827
+ resolvingSpecializations = /* @__PURE__ */ new Set();
161794
161828
  constructor(services) {
161795
161829
  this.indexManager = services?.shared.workspace.IndexManager;
161796
161830
  this.descriptions = services?.workspace.AstNodeDescriptionProvider;
@@ -161893,6 +161927,14 @@ var FeaturePathResolver = class {
161893
161927
  }
161894
161928
  return result;
161895
161929
  }
161930
+ /** Semantic owners reached through typing and specialization. This is used
161931
+ * when an anonymous redefinition has no keyword from which to recover its
161932
+ * feature kind. */
161933
+ // REQ-364: recover the semantic kind of an anonymous local redefinition.
161934
+ inheritedOwners(node) {
161935
+ const root4 = ast_utils_exports.getDocument(node).parseResult.value;
161936
+ return this.typedAndSpecializedOwners(node, this.snapshot(root4));
161937
+ }
161896
161938
  segmentsOf(node) {
161897
161939
  const cst = node.$cstNode;
161898
161940
  if (!cst)
@@ -162038,20 +162080,45 @@ var FeaturePathResolver = class {
162038
162080
  return typed;
162039
162081
  }
162040
162082
  specializationTargets(node, snapshot) {
162083
+ if (this.resolvingSpecializations.has(node))
162084
+ return [];
162085
+ this.resolvingSpecializations.add(node);
162041
162086
  const relNode = node;
162042
162087
  const result = [];
162043
- for (const relation of [...relNode.preRelationships ?? [], ...relNode.relationships ?? []]) {
162044
- if (!relation.kind || !SPECIALIZATION_KINDS.has(relation.kind))
162045
- continue;
162046
- for (const target of relation.targets ?? []) {
162047
- const description = this.pickDescription(snapshot.byName.get(target)) ?? this.pickDescription(snapshot.byName.get(lastSegment(target)));
162048
- const resolved = this.nodeOf(description);
162049
- if (resolved)
162050
- result.push(resolved);
162088
+ try {
162089
+ for (const relation of [...relNode.preRelationships ?? [], ...relNode.relationships ?? []]) {
162090
+ if (!relation.kind || !SPECIALIZATION_KINDS.has(relation.kind))
162091
+ continue;
162092
+ for (const target of relation.targets ?? []) {
162093
+ const simple = simpleRelationName(target);
162094
+ const scoped = simple ? this.scopedSpecializationTarget(node, simple, snapshot) : void 0;
162095
+ const description = scoped ? void 0 : this.pickDescription(snapshot.byName.get(target), node) ?? this.pickDescription(snapshot.byName.get(lastSegment(target)), node);
162096
+ const resolved = scoped ?? this.nodeOf(description);
162097
+ if (resolved)
162098
+ result.push(resolved);
162099
+ }
162051
162100
  }
162101
+ } finally {
162102
+ this.resolvingSpecializations.delete(node);
162052
162103
  }
162053
162104
  return result;
162054
162105
  }
162106
+ /** A simple feature specialization is resolved in the visible inventory of
162107
+ * its lexical owner. Only an absent scoped match may fall back to the global
162108
+ * index, which prevents an unrelated same-named feature from winning. */
162109
+ scopedSpecializationTarget(node, name, snapshot) {
162110
+ for (let scope = node.$container; scope; scope = scope.$container) {
162111
+ const local = directNamedChildren(scope).find((candidate) => candidate !== node && nameOf(candidate) === name);
162112
+ if (local)
162113
+ return local;
162114
+ for (const owner of this.typedAndSpecializedOwners(scope, snapshot)) {
162115
+ const inherited = directNamedChildren(owner).find((candidate) => candidate !== node && nameOf(candidate) === name);
162116
+ if (inherited)
162117
+ return inherited;
162118
+ }
162119
+ }
162120
+ return void 0;
162121
+ }
162055
162122
  memberInventoryKnown(node, snapshot) {
162056
162123
  const record = node;
162057
162124
  if (record.isDef === true || node.$type === "ClassDecl" || node.$type === "StructDecl" || node.$type === "EnumDecl" || node.$type === "DatatypeDecl" || node.$type === "FunctionDecl" || node.$type === "PredicateDecl") {
@@ -162243,14 +162310,21 @@ function sourceOf2(node, uri) {
162243
162310
  return { uri, range };
162244
162311
  }
162245
162312
  }
162313
+ function isAstDescendantOrSelf(node, ancestor) {
162314
+ for (let current2 = node; current2; current2 = current2.$container) {
162315
+ if (current2 === ancestor)
162316
+ return true;
162317
+ }
162318
+ return false;
162319
+ }
162246
162320
  function ivEditMeta(part, instanceId, definition, localUsage, localPath, uri) {
162247
- const declarationId = qnameOf(part) || instanceId;
162321
+ const declarationId = nameOf2(part) ? qnameOf(part) || instanceId : instanceId;
162248
162322
  const definitionId = definition ? qnameOf(definition) : void 0;
162249
162323
  const localUsageId = localUsage ? qnameOf(localUsage) : void 0;
162250
162324
  return {
162251
162325
  instanceId,
162252
162326
  declarationId,
162253
- declarationName: nameOf2(part),
162327
+ declarationName: effectiveNameOf(part),
162254
162328
  declarationSource: sourceOf2(part, uri),
162255
162329
  definitionId,
162256
162330
  definitionName: definition ? nameOf2(definition) : void 0,
@@ -162259,7 +162333,10 @@ function ivEditMeta(part, instanceId, definition, localUsage, localPath, uri) {
162259
162333
  localUsageName: localUsage ? nameOf2(localUsage) : void 0,
162260
162334
  localUsageSource: localUsage ? sourceOf2(localUsage, uri) : void 0,
162261
162335
  localUsagePath: [...localPath],
162262
- expandedFromDefinition: declarationId !== instanceId
162336
+ expandedFromDefinition: declarationId !== instanceId,
162337
+ // A named declaration remains local even when an anonymous `:>>`
162338
+ // wrapper removes its name from the rendered occurrence path.
162339
+ localDeclaration: !!nameOf2(part) && !!localUsage && isAstDescendantOrSelf(part, localUsage)
162263
162340
  };
162264
162341
  }
162265
162342
  var nameOf2 = (node) => {
@@ -162286,10 +162363,7 @@ function boundText(b) {
162286
162363
  return String(b.intVal);
162287
162364
  return b.bound;
162288
162365
  }
162289
- function multText(node) {
162290
- if (!node)
162291
- return void 0;
162292
- const m = node.multiplicity;
162366
+ function multiplicityText(m) {
162293
162367
  if (!m)
162294
162368
  return void 0;
162295
162369
  const lo = boundText(m.lower);
@@ -162298,6 +162372,11 @@ function multText(node) {
162298
162372
  return void 0;
162299
162373
  return hi == null || hi === lo ? `[${lo}]` : `[${lo}..${hi}]`;
162300
162374
  }
162375
+ function multText(node) {
162376
+ if (!node)
162377
+ return void 0;
162378
+ return multiplicityText(node.multiplicity);
162379
+ }
162301
162380
  var typeText = (node) => node.typing?.type?.$refText || void 0;
162302
162381
  var isConjugated = (node) => node.typing?.conjugate === true;
162303
162382
  var isAbstract = (node) => Array.isArray(node.modifiers) && node.modifiers.includes("abstract");
@@ -162428,6 +162507,18 @@ var modifiersOf = (node) => {
162428
162507
  const mods = node.modifiers;
162429
162508
  return Array.isArray(mods) ? mods : [];
162430
162509
  };
162510
+ var ANONYMOUS_INTERFACE_NAME = "(anonymous)";
162511
+ var isEndMember = (node) => node.$type === "EndDecl" || modifiersOf(node).includes("end");
162512
+ function endRowText(node) {
162513
+ const end = node;
162514
+ const name = nameOf2(node) ?? end.innerName;
162515
+ const type = typeText(node) ?? end.innerTyping?.type?.$refText;
162516
+ const mult = multText(node) ?? multiplicityText(end.innerMultiplicity) ?? multiplicityText(end.endMultiplicity);
162517
+ const head2 = `${name ?? ""}${type ? `${name ? " " : ""}: ${type}` : ""}`.trim();
162518
+ if (!head2)
162519
+ return void 0;
162520
+ return `${head2}${mult ? ` ${mult}` : ""}`;
162521
+ }
162431
162522
  var portionKindOf = (node) => modifiersOf(node).find((m) => m === "timeslice" || m === "snapshot");
162432
162523
  var isIndividualOccurrence = (node) => modifiersOf(node).includes("individual");
162433
162524
  var isOccurrenceModified = (node) => portionKindOf(node) !== void 0 || isIndividualOccurrence(node);
@@ -162466,6 +162557,16 @@ function specializationTargets(node) {
162466
162557
  }
162467
162558
  return out;
162468
162559
  }
162560
+ function featureInheritanceTargets(node) {
162561
+ const n2 = node;
162562
+ const out = [];
162563
+ for (const rel2 of [...n2.preRelationships ?? [], ...n2.relationships ?? []]) {
162564
+ if (rel2.kind === ":>" || rel2.kind === ":>>" || rel2.kind === "specializes" || rel2.kind === "subsets" || rel2.kind === "redefines") {
162565
+ out.push(...rel2.targets ?? []);
162566
+ }
162567
+ }
162568
+ return out;
162569
+ }
162469
162570
  function specializationFamily(node) {
162470
162571
  const n2 = node;
162471
162572
  const isDef = n2.isDef === true;
@@ -162562,9 +162663,11 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162562
162663
  if (hit)
162563
162664
  cache.models.delete(cacheKey);
162564
162665
  const model = this.computeDiagramModel(document2, root4, rootSymbol, kind, preferFileOverview, gridPreset, matrixRelationship);
162666
+ const externalRoots = this.externalRootsOf(model, document2.uri.toString());
162667
+ model.dependencies = [document2.uri.toString(), ...externalRoots.keys()];
162565
162668
  cache.models.set(cacheKey, {
162566
162669
  model,
162567
- externalRoots: this.externalRootsOf(model, document2.uri.toString()),
162670
+ externalRoots,
162568
162671
  indexEpoch: this.indexEpoch
162569
162672
  });
162570
162673
  return model;
@@ -162586,21 +162689,24 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162586
162689
  if (source?.uri && source.uri !== anchorUri)
162587
162690
  uris.add(source.uri);
162588
162691
  };
162589
- for (const node of model.nodes) {
162590
- add(node.source);
162591
- for (const port of node.ports ?? [])
162592
- add(port.source);
162593
- }
162594
- for (const frame2 of model.frames ?? []) {
162595
- add(frame2.source);
162596
- for (const port of frame2.ports ?? [])
162597
- add(port.source);
162598
- }
162599
- add(model.ivBoundary?.source);
162600
- for (const port of model.ivBoundary?.ports ?? [])
162601
- add(port.source);
162602
- for (const edge of model.edges)
162603
- add(edge.source);
162692
+ const seen = /* @__PURE__ */ new Set();
162693
+ const addSources = (value) => {
162694
+ if (!value || typeof value !== "object" || seen.has(value))
162695
+ return;
162696
+ seen.add(value);
162697
+ if (Array.isArray(value)) {
162698
+ for (const item of value)
162699
+ addSources(item);
162700
+ return;
162701
+ }
162702
+ const record = value;
162703
+ if (typeof record.uri === "string" && record.range && typeof record.range === "object") {
162704
+ add(record);
162705
+ }
162706
+ for (const nested of Object.values(record))
162707
+ addSources(nested);
162708
+ };
162709
+ addSources(model);
162604
162710
  return new Map([...uris].map((uri) => [uri, this.externalDocumentRoot(uri)]));
162605
162711
  }
162606
162712
  computeDiagramModel(document2, root4, rootSymbol, kind, preferFileOverview, gridPreset = "requirements", matrixRelationship = "allocation") {
@@ -162926,7 +163032,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162926
163032
  if (local)
162927
163033
  return local;
162928
163034
  const declaringIndex = this.localIndex(this.documentRootOf(scope));
162929
- for (const inherited of this.inheritedTypesOf(scope, declaringIndex)) {
163035
+ for (const inherited of this.inheritedFeatureOwnersOf(scope, declaringIndex)) {
162930
163036
  const inheritedMember = membersOf(inherited).find((member) => nameOf2(member) === unrooted);
162931
163037
  if (inheritedMember)
162932
163038
  return inheritedMember;
@@ -162991,6 +163097,105 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
162991
163097
  }
162992
163098
  return chain;
162993
163099
  }
163100
+ // REQ-061/REQ-192/REQ-364 - the effective feature owners inherited by a
163101
+ // definition or usage. A redefining usage specializes both its explicit type
163102
+ // and the feature it redefines. The latter can contribute its own body and its
163103
+ // type's complete inventory, so following only typing definitions turns an
163104
+ // anonymous `part :>> inheritedPart` into an empty leaf.
163105
+ inheritedFeatureOwnersOf(node, index2, resolving = /* @__PURE__ */ new Set()) {
163106
+ if (resolving.has(node))
163107
+ return [];
163108
+ resolving.add(node);
163109
+ const chain = [];
163110
+ const seen = /* @__PURE__ */ new Set([node]);
163111
+ const queue = [];
163112
+ const add = (candidate) => {
163113
+ if (!candidate || seen.has(candidate))
163114
+ return;
163115
+ seen.add(candidate);
163116
+ chain.push(candidate);
163117
+ queue.push(candidate);
163118
+ };
163119
+ const addTargets = (owner) => {
163120
+ for (const target of featureInheritanceTargets(owner)) {
163121
+ add(this.resolveFeatureInheritanceTarget(owner, target, index2, resolving));
163122
+ }
163123
+ };
163124
+ try {
163125
+ if (node.isDef === true)
163126
+ addTargets(node);
163127
+ else {
163128
+ add(this.resolveType(node, index2));
163129
+ addTargets(node);
163130
+ }
163131
+ while (queue.length > 0) {
163132
+ const owner = queue.shift();
163133
+ if (owner.isDef !== true)
163134
+ add(this.resolveType(owner, index2));
163135
+ addTargets(owner);
163136
+ }
163137
+ return chain;
163138
+ } finally {
163139
+ resolving.delete(node);
163140
+ }
163141
+ }
163142
+ /** Declaration that an anonymous local redefinition specializes. Direct edits
163143
+ * in definition-sync mode address this feature, while child insertion still
163144
+ * addresses the feature's typing definition. */
163145
+ synchronizationDeclarationOf(node, index2) {
163146
+ if (nameOf2(node))
163147
+ return node;
163148
+ const wanted = effectiveNameOf(node);
163149
+ return this.inheritedFeatureOwnersOf(node, index2).find((candidate) => candidate.isDef !== true && effectiveNameOf(candidate) === wanted && (isPartDecl(node) ? isPartDecl(candidate) : isPortDecl(node) ? isPortDecl(candidate) : true));
163150
+ }
163151
+ /** Resolve a usage specialization in its feature scope. A simple `:>> x`
163152
+ * names a sibling supplied by the containing usage or one of its types. A
163153
+ * global simple-name lookup can select the redeclaration itself or an
163154
+ * unrelated namesake, so the containing feature inventory is checked first. */
163155
+ resolveFeatureInheritanceTarget(owner, target, index2, resolving) {
163156
+ const path10 = this.resolvedFeatureInheritancePath(owner, target);
163157
+ if (path10.qualified) {
163158
+ return path10.target === owner ? void 0 : path10.target;
163159
+ }
163160
+ if (owner.isDef !== true) {
163161
+ const wanted = lastSeg(target);
163162
+ const container = owner.$container;
163163
+ if (container) {
163164
+ for (const source of [container, ...this.inheritedFeatureOwnersOf(container, index2, resolving)]) {
163165
+ const inherited = membersOf(source).find((member) => member !== owner && effectiveNameOf(member) === wanted);
163166
+ if (inherited)
163167
+ return inherited;
163168
+ }
163169
+ }
163170
+ }
163171
+ const resolved = path10.target ?? this.resolveSpecializationTarget(owner, target, index2);
163172
+ return resolved === owner ? void 0 : resolved;
163173
+ }
163174
+ /** Resolve a relationship target through the shared feature path service.
163175
+ * Qualified paths must retain every segment because a last-segment lookup can
163176
+ * select an unrelated sibling with the same name. */
163177
+ resolvedFeatureInheritancePath(owner, target) {
163178
+ const relationships = [
163179
+ ...owner.preRelationships ?? [],
163180
+ ...owner.relationships ?? []
163181
+ ];
163182
+ const resolver = this.annotationPathResolver(owner);
163183
+ for (const relationship of relationships) {
163184
+ if (!relationship.kind || ![":>", ":>>", "specializes", "subsets", "redefines"].includes(relationship.kind))
163185
+ continue;
163186
+ for (const [occurrence, written] of (relationship.targets ?? []).entries()) {
163187
+ if (written !== target)
163188
+ continue;
163189
+ const resolution = resolver.resolvePropertyPath(relationship, "targets", occurrence);
163190
+ const segment = resolution.segments.at(-1);
163191
+ return {
163192
+ qualified: resolution.segments.length > 1,
163193
+ target: segment?.target ?? this.annotationDescriptionNode(owner, segment?.description)
163194
+ };
163195
+ }
163196
+ }
163197
+ return { qualified: /::|\./u.test(target) };
163198
+ }
162994
163199
  // issue #233 (PR #239 review) — one supertype path, resolved. A specialization
162995
163200
  // target is path TEXT in this grammar, not a cross-reference, so there is no
162996
163201
  // linked node to prefer the way `resolveType` prefers `typing.type.ref`; the
@@ -163466,7 +163671,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163466
163671
  return this.collectGeometry(node, index2).length > 0;
163467
163672
  }
163468
163673
  hasRenderablePorts(node, index2) {
163469
- return [node, ...this.inheritedTypesOf(node, index2)].some((source) => membersOf(source).some((m) => isPortDecl(m) && m.isDef !== true));
163674
+ return [node, ...this.inheritedFeatureOwnersOf(node, index2)].some((source) => membersOf(source).some((m) => isPortDecl(m) && m.isDef !== true));
163470
163675
  }
163471
163676
  // REQ-192, issue #233 — the named part usages a part shows when expanded: its
163472
163677
  // own and the ones it inherits, through its typing definition or a `:>`
@@ -163474,7 +163679,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163474
163679
  // The predicate form of `childUsagesOf`, so an anchor that only INHERITS
163475
163680
  // internals is recognised as having them.
163476
163681
  hasNestedPartUsages(node, index2) {
163477
- return [node, ...this.inheritedTypesOf(node, index2)].some((source) => this.nestedUsages(source, isPartDecl).some((n2) => nameOf2(n2) !== void 0));
163682
+ return [node, ...this.inheritedFeatureOwnersOf(node, index2)].some((source) => this.nestedUsages(source, isPartDecl).some((n2) => nameOf2(n2) !== void 0));
163478
163683
  }
163479
163684
  nestedUsages(node, guard) {
163480
163685
  return membersOf(node).filter((m) => guard(m) && m.isDef !== true);
@@ -163513,7 +163718,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163513
163718
  const all = [...ast_utils_exports.streamAllContents(scope)];
163514
163719
  const defNodes = all.filter((n2) => isDefinitionNode(n2) && nameOf2(n2));
163515
163720
  const packageOverview = isPackage(ctx.anchor) || isDocument(ctx.anchor);
163516
- const hasOwnedMemberNode = all.some((n2) => isPackage(n2) && !!nameOf2(n2) || isDefinitionNode(n2) && !!nameOf2(n2) || this.isViewlessUsage(n2) && this.isDirectPackageMember(n2) || isPartDecl(n2) && n2.isDef !== true && !!nameOf2(n2) && this.isDirectPackageMember(n2));
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));
163517
163723
  const packageNodes = [
163518
163724
  ...packageOverview && hasOwnedMemberNode && isPackage(scope) && nameOf2(scope) ? [scope] : [],
163519
163725
  ...all.filter((p) => isPackage(p) && nameOf2(p))
@@ -163571,7 +163777,12 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163571
163777
  meta: { gvPackageMember: true }
163572
163778
  });
163573
163779
  }
163574
- const packageUsageNodes = [...viewlessUsageNodes, ...packagePartUsageNodes];
163780
+ const packageInterfaceUsageNodes = all.filter(isPackageInterfaceUsage);
163781
+ for (const u of packageInterfaceUsageNodes) {
163782
+ const def = this.resolveType(u, index2);
163783
+ pushNode(u, "box", false, { type: def && ids.has(def) ? void 0 : typeText(u) });
163784
+ }
163785
+ const packageUsageNodes = [...viewlessUsageNodes, ...packagePartUsageNodes, ...packageInterfaceUsageNodes];
163575
163786
  for (const p of packageNodes) {
163576
163787
  const pn = p;
163577
163788
  pushNode(p, "package", false, {
@@ -164144,7 +164355,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164144
164355
  const childUsagesOf = (part) => {
164145
164356
  const out = this.nestedUsages(part, isPartDecl);
164146
164357
  const have = new Set(out.map(effectiveNameOf).filter((x) => !!x));
164147
- for (const source of this.inheritedTypesOf(part, index2)) {
164358
+ for (const source of this.inheritedFeatureOwnersOf(part, index2)) {
164148
164359
  for (const usage of this.nestedUsages(source, isPartDecl)) {
164149
164360
  const nm = effectiveNameOf(usage);
164150
164361
  if (nm !== void 0) {
@@ -164157,7 +164368,27 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164157
164368
  }
164158
164369
  return out;
164159
164370
  };
164160
- const effectiveMembersOf = (part) => [part, ...this.inheritedTypesOf(part, index2)].flatMap((source) => membersOf(source));
164371
+ const effectiveMembersOf = (part) => {
164372
+ const effective = [];
164373
+ const claimedNames = /* @__PURE__ */ new Set();
164374
+ for (const source of [part, ...this.inheritedFeatureOwnersOf(part, index2)]) {
164375
+ for (const member of membersOf(source)) {
164376
+ const relationship = member;
164377
+ const emptyPrefixFlow = member.$type === "FlowStmt" && relationship.target !== void 0 && relationship.source === void 0;
164378
+ const emptyPrefixInterface = isInterfaceDecl(member) && relationship.target !== void 0 && relationship.connect === void 0;
164379
+ const namedRelationship = isConnectorDecl(member) || isConnectionDecl(member) || isInterfaceDecl(member) && !emptyPrefixInterface || member.$type === "BindingDecl" || member.$type === "FlowStmt" && !emptyPrefixFlow;
164380
+ if (namedRelationship) {
164381
+ const names = [nameOf2(member), redefinedNameOf(member)].filter((name) => name !== void 0);
164382
+ if (names.some((name) => claimedNames.has(name)))
164383
+ continue;
164384
+ for (const name of names)
164385
+ claimedNames.add(name);
164386
+ }
164387
+ effective.push(member);
164388
+ }
164389
+ }
164390
+ return effective;
164391
+ };
164161
164392
  const childResolver = (childInfos, self2, actionInfos = []) => {
164162
164393
  const localPort = /* @__PURE__ */ new Map();
164163
164394
  const actionPort = /* @__PURE__ */ new Map();
@@ -164235,14 +164466,22 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164235
164466
  return { resolve: resolve8, actionPinIds };
164236
164467
  };
164237
164468
  const renderInstance = (part, instanceId, parentFrame, seenTypes, depth, inheritedLocalUsage, inheritedLocalPath = []) => {
164238
- const ports = this.portsOf(part, index2, uri, instanceId).ports;
164239
- const typeDef = part.isDef === true ? void 0 : this.resolveType(part, index2);
164469
+ const typeDef = part.isDef === true ? void 0 : this.resolveType(part, index2) ?? this.inheritedFeatureOwnersOf(part, index2).find((owner) => owner.isDef === true);
164240
164470
  const typeQ = typeDef ? qnameOf(typeDef) : void 0;
164241
- const declarationId = qnameOf(part) || instanceId;
164242
- const projected = declarationId !== instanceId;
164471
+ const declarationId = nameOf2(part) ? qnameOf(part) || instanceId : instanceId;
164472
+ const projected = nameOf2(part) === void 0 || declarationId !== instanceId;
164243
164473
  const localUsage = part.isDef !== true && !projected ? part : inheritedLocalUsage;
164244
164474
  const localPath = part.isDef !== true && !projected ? [] : inheritedLocalPath;
164245
- const editMeta = ivEditMeta(part, instanceId, typeDef, localUsage, localPath, uri);
164475
+ const ports = this.portsOf(part, index2, uri, instanceId, localUsage).ports;
164476
+ const syncDeclaration = this.synchronizationDeclarationOf(part, index2);
164477
+ const editMeta = {
164478
+ ...ivEditMeta(part, instanceId, typeDef, localUsage, localPath, uri),
164479
+ ...syncDeclaration && syncDeclaration !== part ? {
164480
+ syncDeclarationId: qnameOf(syncDeclaration),
164481
+ syncDeclarationName: effectiveNameOf(syncDeclaration),
164482
+ syncDeclarationSource: sourceOf2(syncDeclaration, uri)
164483
+ } : {}
164484
+ };
164246
164485
  const children2 = depth < 8 && !(typeQ !== void 0 && seenTypes.has(typeQ)) ? childUsagesOf(part) : [];
164247
164486
  const concretePerformPath = (statement, target) => {
164248
164487
  if (!this.isAnonymousBehaviorReference(statement)) {
@@ -164286,16 +164525,31 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164286
164525
  }).filter((entry, i, list) => list.findIndex((other) => other.key === entry.key) === i) : [];
164287
164526
  const performedActionInfos = performedActions.map(({ act, pinSource, pathSegments, name, key }) => {
164288
164527
  const id2 = `${instanceId}::__perform_${key}`;
164528
+ const inheritedFromDefinition = !!localUsage && !isAstDescendantOrSelf(act, localUsage);
164529
+ const syncDeclaration2 = this.synchronizationDeclarationOf(act, index2);
164289
164530
  return {
164290
164531
  act,
164291
164532
  name,
164292
164533
  id: id2,
164293
164534
  pathSegments,
164294
- pins: this.actionPinPorts(pinSource, id2, index2, uri)
164535
+ pins: this.actionPinPorts(pinSource, id2, index2, uri),
164536
+ meta: {
164537
+ ...ivEditMeta(act, id2, void 0, localUsage, [...localPath, name], uri),
164538
+ ivAction: true,
164539
+ // A performed action projected from a typed part belongs
164540
+ // to the shared definition. Local IV edits cannot safely
164541
+ // materialize an action redefinition yet.
164542
+ ivInheritedAction: inheritedFromDefinition,
164543
+ ...syncDeclaration2 && syncDeclaration2 !== act ? {
164544
+ syncDeclarationId: qnameOf(syncDeclaration2),
164545
+ syncDeclarationName: effectiveNameOf(syncDeclaration2),
164546
+ syncDeclarationSource: sourceOf2(syncDeclaration2, uri)
164547
+ } : {}
164548
+ }
164295
164549
  };
164296
164550
  });
164297
164551
  const emitOwnedActions = (frameOf) => {
164298
- for (const { act, name, id: actId, pins } of performedActionInfos) {
164552
+ for (const { act, name, id: actId, pins, meta } of performedActionInfos) {
164299
164553
  nodes.push({
164300
164554
  id: actId,
164301
164555
  name,
@@ -164307,7 +164561,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164307
164561
  ports: pins,
164308
164562
  frame: frameOf,
164309
164563
  source: sourceOf2(act, uri),
164310
- meta: { ivAction: true }
164564
+ meta
164311
164565
  });
164312
164566
  }
164313
164567
  };
@@ -164334,7 +164588,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164334
164588
  }
164335
164589
  emitOwnedActions(instanceId);
164336
164590
  const resolver = childResolver(childInfos, { usage: part, id: instanceId }, performedActionInfos);
164337
- this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, children2, index2, uri, nodes, edges, eRef, instanceId, resolver.actionPinIds);
164591
+ this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, children2, index2, uri, nodes, edges, eRef, instanceId, resolver.actionPinIds, part);
164338
164592
  } else {
164339
164593
  nodes.push({
164340
164594
  id: instanceId,
@@ -164350,12 +164604,37 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164350
164604
  source: sourceOf2(part, uri),
164351
164605
  meta: editMeta
164352
164606
  });
164607
+ const resolver = childResolver([], { usage: part, id: instanceId });
164608
+ this.emitInterconnectEdges(effectiveMembersOf(part), resolver.resolve, [], index2, uri, nodes, edges, eRef, instanceId, resolver.actionPinIds, part);
164353
164609
  }
164354
164610
  };
164355
164611
  const packageOfRoot = (root4) => enclosingPackage(root4) ?? scope;
164612
+ const rootBaseId = (root4) => qnameOf(root4) || nameOf2(root4) || `__root_${root4.$cstNode?.offset ?? 0}__`;
164613
+ const rootsByBaseId = /* @__PURE__ */ new Map();
164614
+ for (const root4 of roots) {
164615
+ const base = rootBaseId(root4);
164616
+ rootsByBaseId.set(base, [...rootsByBaseId.get(base) ?? [], root4]);
164617
+ }
164618
+ const rootOccurrenceIds = /* @__PURE__ */ new Map();
164619
+ for (const [base, sameNameRoots] of rootsByBaseId) {
164620
+ if (sameNameRoots.length === 1) {
164621
+ rootOccurrenceIds.set(sameNameRoots[0], base);
164622
+ continue;
164623
+ }
164624
+ const discriminatorCounts = /* @__PURE__ */ new Map();
164625
+ const inSourceOrder = [...sameNameRoots].sort((a2, b) => (a2.$cstNode?.offset ?? 0) - (b.$cstNode?.offset ?? 0));
164626
+ for (const root4 of inSourceOrder) {
164627
+ const semanticIdentity = [typeText(root4), ...featureInheritanceTargets(root4)].filter((part) => !!part).join("_") || root4.$type;
164628
+ const discriminator = semanticIdentity.replace(/[^A-Za-z0-9_]+/gu, "_").replace(/^_+|_+$/gu, "") || "part";
164629
+ const occurrence = discriminatorCounts.get(discriminator) ?? 0;
164630
+ discriminatorCounts.set(discriminator, occurrence + 1);
164631
+ const suffix = occurrence === 0 ? discriminator : `${discriminator}_${occurrence + 1}`;
164632
+ rootOccurrenceIds.set(root4, `${base}::__occurrence_${suffix}`);
164633
+ }
164634
+ }
164356
164635
  const rootInfos = [];
164357
164636
  for (const root4 of roots) {
164358
- const id2 = qnameOf(root4) || nameOf2(root4) || `__root_${nodes.length + frames.length}__`;
164637
+ const id2 = rootOccurrenceIds.get(root4) ?? rootBaseId(root4);
164359
164638
  renderInstance(root4, id2, opts.packageFrames ? ensurePkgFrame(enclosingPackage(root4)) : void 0, /* @__PURE__ */ new Set(), 0, root4.isDef === true ? void 0 : root4, []);
164360
164639
  rootInfos.push({ usage: root4, id: id2, pkg: packageOfRoot(root4) });
164361
164640
  }
@@ -164364,7 +164643,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164364
164643
  const localInfos = rootInfos.filter((r) => r.pkg === pkg && r.usage.isDef !== true);
164365
164644
  const localRoots = localInfos.map((r) => r.usage);
164366
164645
  const resolver = childResolver(localInfos);
164367
- this.emitInterconnectEdges(membersOf(pkg), resolver.resolve, localRoots, index2, uri, nodes, edges, eRef, void 0, resolver.actionPinIds);
164646
+ this.emitInterconnectEdges(membersOf(pkg), resolver.resolve, localRoots, index2, uri, nodes, edges, eRef, void 0, resolver.actionPinIds, pkg);
164368
164647
  }
164369
164648
  }
164370
164649
  return { nodes, edges, frames };
@@ -164397,8 +164676,23 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164397
164676
  // single Interconnection View (called once over the anchor's whole subtree)
164398
164677
  // and the package overview (called once per container part, tagging any
164399
164678
  // synthetic n-ary dot with that container's frame id).
164400
- emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set()) {
164679
+ emitInterconnectEdges(members, resolveEnd, parts, index2, uri, nodes, edges, eRef, frameId, actionPinIds = /* @__PURE__ */ new Set(), memberOwner) {
164401
164680
  const memberList2 = [...members];
164681
+ const relationshipFields = (source) => {
164682
+ let directMember = source;
164683
+ while (directMember.$container && directMember.$container !== memberOwner) {
164684
+ directMember = directMember.$container;
164685
+ }
164686
+ const inherited = !!memberOwner && directMember.$container !== memberOwner;
164687
+ const meta = {
164688
+ ...inherited ? { ivInheritedRelation: true } : {},
164689
+ ...frameId ? { ivRelationshipOwnerId: frameId } : {}
164690
+ };
164691
+ return {
164692
+ source: sourceOf2(source, uri),
164693
+ ...Object.keys(meta).length > 0 ? { meta } : {}
164694
+ };
164695
+ };
164402
164696
  const supportsEndpoints = (kind, ids) => {
164403
164697
  const pinCount = ids.filter((id2) => actionPinIds.has(id2)).length;
164404
164698
  if (pinCount === 0)
@@ -164424,7 +164718,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164424
164718
  label,
164425
164719
  endLabelFrom: multText(resolvedEnds[0].end),
164426
164720
  endLabelTo: multText(resolvedEnds[1].end),
164427
- source: sourceOf2(src, uri)
164721
+ ...relationshipFields(src)
164428
164722
  });
164429
164723
  return;
164430
164724
  }
@@ -164441,7 +164735,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164441
164735
  isDef: false,
164442
164736
  label,
164443
164737
  endLabelTo: multText(end),
164444
- source: sourceOf2(src, uri)
164738
+ ...relationshipFields(src)
164445
164739
  });
164446
164740
  }
164447
164741
  };
@@ -164486,7 +164780,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164486
164780
  to: endId,
164487
164781
  kind: "connect",
164488
164782
  endLabelTo: multText(end),
164489
- source: sourceOf2(m, uri)
164783
+ ...relationshipFields(m)
164490
164784
  });
164491
164785
  }
164492
164786
  }
@@ -164506,7 +164800,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164506
164800
  // REQ-179 — connector end multiplicities (`connect [1] a to [0..*] b;`)
164507
164801
  endLabelFrom: multText(srcEnd),
164508
164802
  endLabelTo: multText(tgtEnd),
164509
- source: sourceOf2(m, uri)
164803
+ ...relationshipFields(m)
164510
164804
  });
164511
164805
  }
164512
164806
  }
@@ -164524,7 +164818,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164524
164818
  to,
164525
164819
  kind: "flow",
164526
164820
  label: this.flowPayloadText(fm) ?? typeText(m) ?? nameOf2(m),
164527
- source: sourceOf2(m, uri)
164821
+ ...relationshipFields(m)
164528
164822
  });
164529
164823
  }
164530
164824
  } else if (m.$type === "BindStmt" || m.$type === "BindingConnectorStmt" || m.$type === "BindingDecl") {
@@ -164547,29 +164841,63 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164547
164841
  // of its own (issue #120 review).
164548
164842
  endLabelFrom: multText(leftEnd) ?? multText(bn),
164549
164843
  endLabelTo: multText(rightEnd),
164550
- source: sourceOf2(m, uri)
164844
+ ...relationshipFields(m)
164551
164845
  });
164552
164846
  }
164553
164847
  } else if (isInterfaceDecl(m) && m.isDef !== true) {
164554
164848
  const clause = m.connect;
164555
164849
  if (clause) {
164556
- const from = resolveEnd(this.connectorEndPath(clause.source) ?? "");
164557
- const to = resolveEnd(this.connectorEndPath(clause.target) ?? "");
164558
- if (from && to && supportsEndpoints("interface", [from, to])) {
164559
- const name = nameOf2(m);
164560
- const type = typeText(m);
164561
- const label = ["\xABinterface\xBB", nameOf2(m) ?? "", typeText(m) ? `: ${typeText(m)}` : ""].filter(Boolean).join(" ").trim();
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) {
164562
164857
  edges.push({
164563
164858
  id: `e${eRef.n++}`,
164564
- from,
164565
- to,
164859
+ from: resolvedEnds[0].id,
164860
+ to: resolvedEnds[1].id,
164566
164861
  kind: "interface",
164567
164862
  name,
164568
164863
  type,
164569
164864
  isDef: false,
164570
164865
  label,
164571
- endLabelFrom: multText(clause.source),
164572
- endLabelTo: multText(clause.target),
164866
+ endLabelFrom: multText(resolvedEnds[0].end),
164867
+ endLabelTo: multText(resolvedEnds[1].end),
164868
+ ...relationshipFields(m)
164869
+ });
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
+ });
164886
+ }
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),
164573
164901
  source: sourceOf2(m, uri)
164574
164902
  });
164575
164903
  }
@@ -164597,7 +164925,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164597
164925
  isDef: false,
164598
164926
  label,
164599
164927
  endLabelTo: multText(end),
164600
- source: sourceOf2(m, uri)
164928
+ ...relationshipFields(m)
164601
164929
  });
164602
164930
  }
164603
164931
  }
@@ -164616,7 +164944,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164616
164944
  label,
164617
164945
  endLabelFrom: multText(clause.source),
164618
164946
  endLabelTo: multText(clause.target),
164619
- source: sourceOf2(m, uri)
164947
+ ...relationshipFields(m)
164620
164948
  });
164621
164949
  }
164622
164950
  }
@@ -164637,7 +164965,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
164637
164965
  const all = [...ast_utils_exports.streamAllContents(scope)];
164638
164966
  const isUsagePart = (n2) => isPartDecl(n2) && n2.isDef !== true && nameOf2(n2) !== void 0;
164639
164967
  const isDefPart = (n2) => isPartDecl(n2) && n2.isDef === true && nameOf2(n2) !== void 0;
164640
- const hasInternalParts = (d) => [d, ...this.inheritedTypesOf(d, index2)].some((source) => this.nestedUsages(source, isPartDecl).length > 0);
164968
+ const hasInternalParts = (d) => [d, ...this.inheritedFeatureOwnersOf(d, index2)].some((source) => this.nestedUsages(source, isPartDecl).length > 0);
164641
164969
  const insideAPart = (n2) => {
164642
164970
  let c = n2.$container;
164643
164971
  while (c && c !== scope) {
@@ -168354,7 +168682,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168354
168682
  }
168355
168683
  // REQ-175, REQ-193 — collect a node's ports (own + inherited from its type
168356
168684
  // def) as stubs, plus a "usageName.portName" → portId resolution map.
168357
- portsOf(node, index2, uri, ownerId) {
168685
+ portsOf(node, index2, uri, ownerId, localUsage) {
168358
168686
  const ports = [];
168359
168687
  const map3 = /* @__PURE__ */ new Map();
168360
168688
  const visibleNames = /* @__PURE__ */ new Set();
@@ -168366,23 +168694,50 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168366
168694
  const portId = parentPortId ? `${parentPortId}.${pn}` : `${ownerId}.${pn}`;
168367
168695
  const rel2 = parentPath ? `${parentPath}.${pn}` : pn;
168368
168696
  let direction = directionOf(portNode);
168369
- const portTypeDef = this.resolveType(portNode, index2);
168697
+ const inheritedOwners = this.inheritedFeatureOwnersOf(portNode, index2);
168698
+ const directPortTypeDef = this.resolveType(portNode, index2);
168699
+ const inheritedPortUsages = inheritedOwners.filter((owner) => isPortDecl(owner) && owner.isDef !== true);
168700
+ const inheritedTyping = inheritedPortUsages.map((owner) => ({ owner, type: this.resolveType(owner, index2) })).find((entry) => entry.type !== void 0);
168701
+ const portTypeDef = directPortTypeDef ?? inheritedTyping?.type ?? inheritedOwners.find((owner) => isPortDecl(owner) && owner.isDef === true);
168702
+ const inheritedDirection = inheritedPortUsages.map(directionOf).find((candidate) => candidate !== void 0);
168703
+ direction ??= inheritedDirection;
168704
+ const referenceTraitOf = (candidate) => {
168705
+ const modifiers2 = candidate.modifiers ?? [];
168706
+ if (modifiers2.includes("ref"))
168707
+ return true;
168708
+ if (modifiers2.includes("composite"))
168709
+ return false;
168710
+ return void 0;
168711
+ };
168712
+ const inheritedReference = inheritedPortUsages.map(referenceTraitOf).find((candidate) => candidate !== void 0);
168713
+ const reference = referenceTraitOf(portNode) ?? inheritedReference;
168714
+ const hasDirectTyping = portNode.typing !== void 0;
168715
+ const effectiveConjugated = hasDirectTyping ? isConjugated(portNode) : inheritedTyping ? isConjugated(inheritedTyping.owner) : false;
168370
168716
  if (!direction) {
168371
168717
  const conjugated = isConjugated(portNode);
168372
- const dirs = /* @__PURE__ */ new Set([
168373
- ...this.ownDirectedFeatureDirections(portNode, conjugated),
168374
- ...portTypeDef ? this.effectivePortDirections(portTypeDef, index2, conjugated) : []
168375
- ]);
168718
+ const dirs = this.ownDirectedFeatureDirections(portNode, conjugated);
168719
+ for (const inheritedUsage of inheritedPortUsages) {
168720
+ const inheritedConjugated = conjugated !== isConjugated(inheritedUsage);
168721
+ for (const inheritedDirection2 of this.ownDirectedFeatureDirections(inheritedUsage, inheritedConjugated)) {
168722
+ dirs.add(inheritedDirection2);
168723
+ }
168724
+ }
168725
+ const typeConjugated = directPortTypeDef ? conjugated : inheritedTyping ? conjugated !== isConjugated(inheritedTyping.owner) : conjugated;
168726
+ if (portTypeDef) {
168727
+ for (const inheritedDirection2 of this.effectivePortDirections(portTypeDef, index2, typeConjugated)) {
168728
+ dirs.add(inheritedDirection2);
168729
+ }
168730
+ }
168376
168731
  direction = dirs.size === 1 ? [...dirs][0] : dirs.size > 1 ? "inout" : void 0;
168377
168732
  }
168378
168733
  const port = {
168379
168734
  id: portId,
168380
168735
  name: pn,
168381
- type: typeText(portNode),
168736
+ type: typeText(portNode) ?? (inheritedTyping ? typeText(inheritedTyping.owner) : void 0),
168382
168737
  direction,
168383
- conjugated: isConjugated(portNode) || void 0,
168738
+ conjugated: effectiveConjugated || void 0,
168384
168739
  // REQ-194 — reference ports render with a dashed outline
168385
- ref: (portNode.modifiers ?? []).includes("ref") || void 0,
168740
+ ref: reference || void 0,
168386
168741
  isDef: portNode.isDef === true,
168387
168742
  // issue #108 — a nested port carries its parent's id so the webview
168388
168743
  // stacks it on the parent glyph and docks connectors on it.
@@ -168397,18 +168752,27 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168397
168752
  // projection origin to choose a direct edit or a usage-local
168398
168753
  // `port :>> name { ... }` materialization.
168399
168754
  meta: {
168400
- declarationId: qnameOf(portNode) || portId,
168755
+ declarationId: nameOf2(portNode) ? qnameOf(portNode) || portId : portId,
168401
168756
  declarationName: pn,
168402
168757
  inheritedFromType,
168758
+ localDeclaration: !inheritedFromType && !!nameOf2(portNode) && !!localUsage && isAstDescendantOrSelf(portNode, localUsage),
168403
168759
  definitionId: portTypeDef ? qnameOf(portTypeDef) : void 0,
168404
168760
  definitionName: portTypeDef ? nameOf2(portTypeDef) : void 0,
168405
- definitionSource: portTypeDef ? sourceOf2(portTypeDef, uri) : void 0
168761
+ definitionSource: portTypeDef ? sourceOf2(portTypeDef, uri) : void 0,
168762
+ ...(() => {
168763
+ const syncDeclaration = this.synchronizationDeclarationOf(portNode, index2);
168764
+ return syncDeclaration && syncDeclaration !== portNode ? {
168765
+ syncDeclarationId: qnameOf(syncDeclaration),
168766
+ syncDeclarationName: effectiveNameOf(syncDeclaration),
168767
+ syncDeclarationSource: sourceOf2(syncDeclaration, uri)
168768
+ } : {};
168769
+ })()
168406
168770
  }
168407
168771
  };
168408
168772
  if (usageName)
168409
168773
  map3.set(`${usageName}.${rel2}`, portId);
168410
168774
  map3.set(`${ownerId}.${rel2}`, portId);
168411
- return { port, portTypeDef };
168775
+ return { port, portTypeDef, inheritedOwners };
168412
168776
  };
168413
168777
  const addNested = (portNode, made, inheritedFromType, path10, depth, seenTypes) => {
168414
168778
  if (depth > NESTED_PORT_MAX_DEPTH)
@@ -168420,8 +168784,8 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168420
168784
  const nestedSources = [
168421
168785
  // declared in this port usage's body: as local as its parent is
168422
168786
  ...membersOf(portNode).map((member) => ({ member, inherited: inheritedFromType })),
168423
- // declared in the port's type: always a projection of that type
168424
- ...made.portTypeDef && !repeatedType ? membersOf(made.portTypeDef).map((member) => ({ member, inherited: true })) : []
168787
+ // every effective owner contributes its body, nearest first
168788
+ ...!repeatedType ? made.inheritedOwners.flatMap((owner) => membersOf(owner).map((member) => ({ member, inherited: true }))) : []
168425
168789
  ];
168426
168790
  for (const { member, inherited } of nestedSources) {
168427
168791
  if (!isPortDecl(member) || member.isDef === true)
@@ -168451,7 +168815,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168451
168815
  for (const m of membersOf(node))
168452
168816
  if (isPortDecl(m) && m.isDef !== true)
168453
168817
  addPort(m, false);
168454
- for (const inherited of this.inheritedTypesOf(node, index2)) {
168818
+ for (const inherited of this.inheritedFeatureOwnersOf(node, index2)) {
168455
168819
  for (const m of membersOf(inherited))
168456
168820
  if (isPortDecl(m) && m.isDef !== true)
168457
168821
  addPort(m, true);
@@ -168554,20 +168918,20 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168554
168918
  inheritedItems(node, index2, uri) {
168555
168919
  if (node.isDef === true)
168556
168920
  return [];
168557
- const def = this.resolveType(node, index2);
168558
- if (!def)
168559
- return [];
168560
- const localRedefined = /* @__PURE__ */ new Set();
168561
- for (const m of membersOf(node)) {
168562
- for (const rel2 of [...m.preRelationships ?? [], ...m.relationships ?? []]) {
168563
- if (rel2.kind === ":>>" || rel2.kind === "redefines") {
168564
- for (const t of rel2.targets ?? [])
168565
- localRedefined.add(lastSeg(t));
168566
- }
168921
+ const hiddenNames = new Set(membersOf(node).map(effectiveNameOf).filter((name) => name !== void 0));
168922
+ const inherited = [];
168923
+ for (const owner of this.inheritedFeatureOwnersOf(node, index2)) {
168924
+ for (const member of membersOf(owner)) {
168925
+ const name = effectiveNameOf(member);
168926
+ if (!name || hiddenNames.has(name))
168927
+ continue;
168928
+ hiddenNames.add(name);
168929
+ if (!isAttributeDecl(member) || member.isDef === true)
168930
+ continue;
168931
+ inherited.push({ member, source: sourceOf2(member, uri) });
168567
168932
  }
168568
168933
  }
168569
- const localNames = new Set(membersOf(node).map((m) => nameOf2(m)).filter(Boolean));
168570
- return membersOf(def).filter((m) => isAttributeDecl(m) && m.isDef !== true && nameOf2(m)).filter((m) => !localRedefined.has(nameOf2(m)) && !localNames.has(nameOf2(m))).map((member) => ({ member, source: sourceOf2(member, uri) }));
168934
+ return inherited;
168571
168935
  }
168572
168936
  compartmentsFor(node, uri, index2, featureRows = false, includeInherited = true) {
168573
168937
  const members = membersOf(node);
@@ -168612,6 +168976,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168612
168976
  } else if (subjectMandatory) {
168613
168977
  add("subject", [{ text: "\u26A0 none declared" }]);
168614
168978
  }
168979
+ add("ends", members.filter(isEndMember).map((m) => item(m, endRowText(m) ?? statementText(m))));
168615
168980
  add("attributes", [
168616
168981
  ...usages(isAttributeDecl).map((a2) => item(a2)),
168617
168982
  ...inheritedRows((member) => isAttributeDecl(member) && isOrdinaryMember(member))
@@ -168621,6 +168986,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168621
168986
  add("ports", usages(isPortDecl).map((m) => item(m)));
168622
168987
  add("parts", usages(isPartDecl).filter(notPortion).map((m) => item(m)));
168623
168988
  }
168989
+ add("interfaces", members.filter((m) => isInterfaceDecl(m) && m.isDef !== true && isOrdinaryMember(m)).map((m) => item(m, this.interfaceRowText(m))));
168624
168990
  add("features", members.filter((m) => m.$type === "FeatureShorthand" || m.$type === "FeatureRedefinitionShorthand").filter(isOrdinaryMember).filter((m) => !isOccurrenceModified(m)).map((m) => item(m, canonicalFeatureText(m))));
168625
168991
  const undirected = (guard) => usages(guard);
168626
168992
  add("items", undirected(isItemDecl).filter(notPortion).map((m) => item(m)));
@@ -168716,6 +169082,44 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
168716
169082
  add("satisfies", members.filter((m) => m.$type === "SatisfyStmt").map((m) => item(m, String(m.target ?? ""))));
168717
169083
  return out.length ? out : void 0;
168718
169084
  }
169085
+ // REQ-195 — Interface and binding guidance (`interfaces` row text)
169086
+ // issue #110 — the textual row (and node label) of an interface usage:
169087
+ // `name : Type connect a.p to b.q`. The connect ends are kept because this
169088
+ // row is the depiction a reader falls back to when the edge is not drawn, and
169089
+ // without them an anonymous typed usage would read as its type alone. The two
169090
+ // keyword-less shorthands (`interface a.p to b.q;` and an n-ary
169091
+ // `connect (a, b, c)`) are spelled the same way.
169092
+ interfaceRowText(node) {
169093
+ const n2 = node;
169094
+ const type = typeText(node);
169095
+ const mult = multText(node);
169096
+ const name = nameOf2(node) ?? ANONYMOUS_INTERFACE_NAME;
169097
+ const head2 = `${name}${type ? ` : ${type}` : ""}${mult ? ` ${mult}` : ""}`;
169098
+ const clause = n2.connect;
169099
+ const ends = clause?.ends?.length ? `connect (${clause.ends.map((end) => this.connectorEndPath(end) ?? "?").join(", ")})` : clause ? `connect ${this.connectorEndPath(clause.source) ?? "?"} to ${this.connectorEndPath(clause.target) ?? "?"}` : n2.target !== void 0 ? `${[nameOf2(node) ?? "", ...n2.srcSegs ?? []].filter(Boolean).join(".")} to ${this.featurePath(n2.target) ?? "?"}` : void 0;
169100
+ if (ends && !clause)
169101
+ return ends;
169102
+ return [head2, ends].filter(Boolean).join(" ");
169103
+ }
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
+ }
168719
169123
  refText(v) {
168720
169124
  if (typeof v === "string")
168721
169125
  return v;
@@ -182204,7 +182608,8 @@ function portGlyphRect(g, w, h, topReserve = 0) {
182204
182608
  y: cy - height / 2,
182205
182609
  width,
182206
182610
  height,
182207
- elongated: across > natural
182611
+ elongated: across > natural,
182612
+ proxy: g.port.meta?.proxy === true
182208
182613
  };
182209
182614
  }
182210
182615
  function hostMarkAcrossShift(rect) {
@@ -182212,7 +182617,7 @@ function hostMarkAcrossShift(rect) {
182212
182617
  return -(rect.across / 2 - NESTED_PORT_HOST_CLEAR / 2);
182213
182618
  }
182214
182619
  function portDockAlongShift(rect) {
182215
- if (!rect.elongated) return 0;
182620
+ if (!rect.elongated || rect.proxy) return 0;
182216
182621
  return rect.along / 2 - NESTED_PORT_END_PAD / 2;
182217
182622
  }
182218
182623
  function portDockAt(rect, side, dockSide) {
@@ -182906,8 +183311,8 @@ function collapseNestedPorts(model, expanded2) {
182906
183311
  }).filter((e) => {
182907
183312
  if (e.from === e.to) return false;
182908
183313
  const key = `${e.from}\0${e.to}\0${e.kind}\0${e.label ?? ""}`;
182909
- if (seenEdge.has(key)) return false;
182910
- seenEdge.add(key);
183314
+ if (seenEdge.has(`${key}:${e.id}`)) return false;
183315
+ seenEdge.add(`${key}:${e.id}`);
182911
183316
  return true;
182912
183317
  });
182913
183318
  return {
@@ -182956,11 +183361,17 @@ function assignPortHandles(ports, overrides, ownerShape) {
182956
183361
  label + reserved
182957
183362
  ),
182958
183363
  across: portGlyphAcross(kidMetrics.map((k) => k.across))
183364
+ } : isProxyPort(port) ? {
183365
+ along: Math.max(
183366
+ natural,
183367
+ tw(`${port.conjugated ? "~" : ""}${port.name}`, 9) + 2 * NESTED_PORT_END_PAD + (port.direction ? NESTED_PORT_CHEVRON_RUN : 0)
183368
+ ),
183369
+ across: portGlyphAcross()
182959
183370
  } : { along: natural + reserved, across: natural };
182960
183371
  metrics.set(port.id, m);
182961
183372
  return m;
182962
183373
  };
182963
- const alongOf = (port) => hasToggle(port) ? metricsOf(port).along : void 0;
183374
+ const alongOf = (port) => hasToggle(port) || isProxyPort(port) ? metricsOf(port).along : void 0;
182964
183375
  const place = (list, side) => {
182965
183376
  const n2 = list.length;
182966
183377
  const alongs = list.map(alongOf);
@@ -183219,6 +183630,50 @@ function applyBehaviorFilters(model, filters) {
183219
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));
183220
183631
  return { ...model, frames: nextFrames, nodes: withoutOrphanedAnnotations(model, nodes, edges), edges };
183221
183632
  }
183633
+ function isProxyPort(port) {
183634
+ return port.meta?.proxy === true;
183635
+ }
183636
+ var PROXY_LAYOUT_PREFIX = "proxy:";
183637
+ function proxyPortFor(port, path10, hostId) {
183638
+ const stand = {
183639
+ ...port,
183640
+ name: path10,
183641
+ layoutKey: `${PROXY_LAYOUT_PREFIX}${hostId}:${port.layoutKey ?? port.id}`,
183642
+ meta: { ...port.meta, proxy: true, proxyFor: port.id, proxyPath: path10, proxyName: port.name }
183643
+ };
183644
+ delete stand.parentPort;
183645
+ return stand;
183646
+ }
183647
+ function proxyPathOf(port, ownerId, hostId, containerLabel, containerOf, portById) {
183648
+ const segments = [port.name];
183649
+ const seenPorts = /* @__PURE__ */ new Set([port.id]);
183650
+ for (let cur = port.parentPort; cur !== void 0 && !seenPorts.has(cur); cur = portById.get(cur)?.parentPort) {
183651
+ seenPorts.add(cur);
183652
+ const parent = portById.get(cur);
183653
+ if (!parent) break;
183654
+ segments.unshift(parent.name);
183655
+ }
183656
+ const seen = /* @__PURE__ */ new Set();
183657
+ for (let cur = ownerId; cur !== void 0 && cur !== hostId && !seen.has(cur); cur = containerOf(cur)) {
183658
+ seen.add(cur);
183659
+ const label = containerLabel(cur);
183660
+ if (label) segments.unshift(label);
183661
+ }
183662
+ return segments.join(".");
183663
+ }
183664
+ function proxyPortsByHost(referenced, hostOf, ownerOf, containerLabel, containerOf, portById) {
183665
+ const byHost = /* @__PURE__ */ new Map();
183666
+ for (const portId of referenced) {
183667
+ const host = hostOf.get(portId);
183668
+ const entry = ownerOf.get(portId);
183669
+ if (host === void 0 || entry === void 0) continue;
183670
+ const path10 = proxyPathOf(entry.port, entry.owner, host, containerLabel, containerOf, portById);
183671
+ const list = byHost.get(host);
183672
+ if (list) list.push(proxyPortFor(entry.port, path10, host));
183673
+ else byHost.set(host, [proxyPortFor(entry.port, path10, host)]);
183674
+ }
183675
+ return byHost;
183676
+ }
183222
183677
  function collapseIvModel(model, hiddenInternals) {
183223
183678
  const frames = model.frames ?? [];
183224
183679
  if (!frames.length) return model;
@@ -183248,12 +183703,72 @@ function collapseIvModel(model, hiddenInternals) {
183248
183703
  };
183249
183704
  const hiddenFrameIds = new Set(frames.filter((f) => belongsToCollapsedRoot(f.id, false)).map((f) => f.id));
183250
183705
  const hiddenNodeIds = new Set(model.nodes.filter((n2) => belongsToCollapsedRoot(n2.frame ?? n2.parent, true)).map((n2) => n2.id));
183251
- const hiddenEndpoints = /* @__PURE__ */ new Set([...hiddenFrameIds, ...hiddenNodeIds]);
183252
- for (const f of frames) if (hiddenFrameIds.has(f.id)) for (const p of f.ports ?? []) hiddenEndpoints.add(p.id);
183253
- for (const n2 of model.nodes) if (hiddenNodeIds.has(n2.id)) for (const p of n2.ports ?? []) hiddenEndpoints.add(p.id);
183706
+ const nodeById = new Map(model.nodes.map((n2) => [n2.id, n2]));
183707
+ const containerOf = (id2) => {
183708
+ const frame2 = frameById.get(id2);
183709
+ if (frame2) return frame2.parent;
183710
+ const node = nodeById.get(id2);
183711
+ return node ? node.frame ?? node.parent : void 0;
183712
+ };
183713
+ const containerLabel = (id2) => frameById.get(id2)?.label ?? nodeById.get(id2)?.name;
183714
+ const rootFor = (id2) => {
183715
+ const seen = /* @__PURE__ */ new Set();
183716
+ for (let cur = id2; cur !== void 0 && !seen.has(cur); cur = containerOf(cur)) {
183717
+ if (roots.has(cur)) return cur;
183718
+ seen.add(cur);
183719
+ }
183720
+ return void 0;
183721
+ };
183722
+ const ownerOfHiddenPort = /* @__PURE__ */ new Map();
183723
+ for (const f of frames) if (hiddenFrameIds.has(f.id)) for (const p of f.ports ?? []) ownerOfHiddenPort.set(p.id, { port: p, owner: f.id });
183724
+ for (const n2 of model.nodes) if (hiddenNodeIds.has(n2.id)) for (const p of n2.ports ?? []) ownerOfHiddenPort.set(p.id, { port: p, owner: n2.id });
183725
+ const hiddenPortById = new Map([...ownerOfHiddenPort].map(([id2, entry]) => [id2, entry.port]));
183726
+ const proxyHost = /* @__PURE__ */ new Map();
183727
+ for (const [portId, entry] of ownerOfHiddenPort) {
183728
+ const root4 = rootFor(entry.owner);
183729
+ if (root4 !== void 0) proxyHost.set(portId, root4);
183730
+ }
183731
+ const bodyDock = /* @__PURE__ */ new Map();
183732
+ for (const id2 of [...hiddenFrameIds, ...hiddenNodeIds]) {
183733
+ const root4 = rootFor(id2);
183734
+ if (root4 !== void 0) bodyDock.set(id2, root4);
183735
+ }
183736
+ const hidden = /* @__PURE__ */ new Set([...hiddenFrameIds, ...hiddenNodeIds, ...ownerOfHiddenPort.keys()]);
183737
+ const containerRootOf = (id2) => proxyHost.get(id2) ?? bodyDock.get(id2);
183738
+ const seenEdge = /* @__PURE__ */ new Set();
183739
+ const edges = model.edges.filter((e) => {
183740
+ const fromRoot = containerRootOf(e.from);
183741
+ const toRoot = containerRootOf(e.to);
183742
+ if (fromRoot !== void 0 && fromRoot === toRoot) return false;
183743
+ return !(hidden.has(e.from) && fromRoot === void 0) && !(hidden.has(e.to) && toRoot === void 0);
183744
+ }).map((e) => {
183745
+ const from = bodyDock.get(e.from) ?? e.from;
183746
+ const to = bodyDock.get(e.to) ?? e.to;
183747
+ return from === e.from && to === e.to ? e : { ...e, from, to };
183748
+ }).filter((e) => {
183749
+ if (e.from === e.to) return false;
183750
+ const key = `${e.from}\0${e.to}\0${e.kind}\0${e.label ?? ""}`;
183751
+ if (seenEdge.has(`${key}:${e.id}`)) return false;
183752
+ seenEdge.add(`${key}:${e.id}`);
183753
+ return true;
183754
+ });
183755
+ const referenced = /* @__PURE__ */ new Set();
183756
+ for (const e of edges) {
183757
+ if (proxyHost.has(e.from)) referenced.add(e.from);
183758
+ if (proxyHost.has(e.to)) referenced.add(e.to);
183759
+ }
183760
+ const proxies = proxyPortsByHost(
183761
+ referenced,
183762
+ proxyHost,
183763
+ ownerOfHiddenPort,
183764
+ containerLabel,
183765
+ containerOf,
183766
+ hiddenPortById
183767
+ );
183254
183768
  const collapsedNodes = [...roots].flatMap((id2) => {
183255
183769
  const f = frameById.get(id2);
183256
183770
  if (!f) return [];
183771
+ const stand = proxies.get(id2) ?? [];
183257
183772
  return [{
183258
183773
  id: f.id,
183259
183774
  layoutKey: f.layoutKey,
@@ -183263,7 +183778,7 @@ function collapseIvModel(model, hiddenInternals) {
183263
183778
  shape: "box",
183264
183779
  type: f.type,
183265
183780
  multiplicity: f.multiplicity,
183266
- ports: f.ports,
183781
+ ports: stand.length ? [...f.ports ?? [], ...stand] : f.ports,
183267
183782
  compartments: f.compartments,
183268
183783
  frame: f.parent,
183269
183784
  source: f.source,
@@ -183274,7 +183789,7 @@ function collapseIvModel(model, hiddenInternals) {
183274
183789
  ...model,
183275
183790
  nodes: [...model.nodes.filter((n2) => !hiddenNodeIds.has(n2.id)), ...collapsedNodes],
183276
183791
  frames: frames.filter((f) => !roots.has(f.id) && !hiddenFrameIds.has(f.id)),
183277
- edges: model.edges.filter((e) => !hiddenEndpoints.has(e.from) && !hiddenEndpoints.has(e.to))
183792
+ edges
183278
183793
  };
183279
183794
  }
183280
183795
  function isCollapsibleActionFrame(frame2) {
@@ -183356,13 +183871,18 @@ function collapseActionFrames(model, hiddenInternals, isHost = isCollapsibleActi
183356
183871
  };
183357
183872
  const roots = new Set([...requested].filter((id2) => rootOf(frameById.get(id2)?.parent) === void 0));
183358
183873
  const dockOf = /* @__PURE__ */ new Map();
183874
+ const proxyHost = /* @__PURE__ */ new Map();
183875
+ const ownerOfHiddenPort = /* @__PURE__ */ new Map();
183359
183876
  const hiddenFrameIds = /* @__PURE__ */ new Set();
183360
183877
  for (const f of frames) {
183361
183878
  const root4 = roots.has(f.id) ? rootOf(f.parent) : rootOf(f.id);
183362
183879
  if (root4 === void 0) continue;
183363
183880
  hiddenFrameIds.add(f.id);
183364
183881
  dockOf.set(f.id, root4);
183365
- for (const p of f.ports ?? []) dockOf.set(p.id, root4);
183882
+ for (const p of f.ports ?? []) {
183883
+ proxyHost.set(p.id, root4);
183884
+ ownerOfHiddenPort.set(p.id, { port: p, owner: f.id });
183885
+ }
183366
183886
  }
183367
183887
  const hiddenNodeIds = /* @__PURE__ */ new Set();
183368
183888
  for (const n2 of model.nodes) {
@@ -183370,15 +183890,59 @@ function collapseActionFrames(model, hiddenInternals, isHost = isCollapsibleActi
183370
183890
  if (root4 === void 0) continue;
183371
183891
  hiddenNodeIds.add(n2.id);
183372
183892
  dockOf.set(n2.id, root4);
183373
- for (const p of n2.ports ?? []) dockOf.set(p.id, root4);
183893
+ for (const p of n2.ports ?? []) {
183894
+ proxyHost.set(p.id, root4);
183895
+ ownerOfHiddenPort.set(p.id, { port: p, owner: n2.id });
183896
+ }
183374
183897
  }
183375
183898
  for (const id2 of roots) {
183376
183899
  dockOf.delete(id2);
183377
- for (const p of frameById.get(id2)?.ports ?? []) dockOf.delete(p.id);
183900
+ for (const p of frameById.get(id2)?.ports ?? []) {
183901
+ proxyHost.delete(p.id);
183902
+ ownerOfHiddenPort.delete(p.id);
183903
+ }
183378
183904
  }
183905
+ const nodeById = new Map(model.nodes.map((n2) => [n2.id, n2]));
183906
+ const containerOf = (id2) => {
183907
+ const frame2 = frameById.get(id2);
183908
+ if (frame2) return frame2.parent;
183909
+ return nodeById.get(id2)?.frame;
183910
+ };
183911
+ const containerLabel = (id2) => frameById.get(id2)?.label ?? nodeById.get(id2)?.name;
183912
+ const hiddenPortById = new Map([...ownerOfHiddenPort].map(([id2, entry]) => [id2, entry.port]));
183913
+ const containerRootOf = (id2) => proxyHost.get(id2) ?? dockOf.get(id2);
183914
+ const seenEdge = /* @__PURE__ */ new Set();
183915
+ const edges = model.edges.filter((e) => {
183916
+ const fromRoot = containerRootOf(e.from);
183917
+ return fromRoot === void 0 || fromRoot !== containerRootOf(e.to);
183918
+ }).map((e) => {
183919
+ const from = dockOf.get(e.from) ?? e.from;
183920
+ const to = dockOf.get(e.to) ?? e.to;
183921
+ return from === e.from && to === e.to ? e : { ...e, from, to };
183922
+ }).filter((e) => {
183923
+ if (e.from === e.to) return false;
183924
+ const key = `${e.from}\0${e.to}\0${e.kind}\0${e.label ?? ""}`;
183925
+ if (seenEdge.has(`${key}:${e.id}`)) return false;
183926
+ seenEdge.add(`${key}:${e.id}`);
183927
+ return true;
183928
+ });
183929
+ const referenced = /* @__PURE__ */ new Set();
183930
+ for (const e of edges) {
183931
+ if (proxyHost.has(e.from)) referenced.add(e.from);
183932
+ if (proxyHost.has(e.to)) referenced.add(e.to);
183933
+ }
183934
+ const proxies = proxyPortsByHost(
183935
+ referenced,
183936
+ proxyHost,
183937
+ ownerOfHiddenPort,
183938
+ containerLabel,
183939
+ containerOf,
183940
+ hiddenPortById
183941
+ );
183379
183942
  const collapsedNodes = [...roots].flatMap((id2) => {
183380
183943
  const f = frameById.get(id2);
183381
183944
  if (!f) return [];
183945
+ const stand = proxies.get(id2) ?? [];
183382
183946
  return [{
183383
183947
  id: f.id,
183384
183948
  layoutKey: f.layoutKey,
@@ -183388,25 +183952,13 @@ function collapseActionFrames(model, hiddenInternals, isHost = isCollapsibleActi
183388
183952
  shape: collapsedShape,
183389
183953
  type: f.type,
183390
183954
  multiplicity: f.multiplicity,
183391
- ports: f.ports,
183955
+ ports: stand.length ? [...f.ports ?? [], ...stand] : f.ports,
183392
183956
  compartments: f.compartments,
183393
183957
  frame: f.parent,
183394
183958
  source: f.source,
183395
183959
  meta: { ...f.meta, internalsHidden: true }
183396
183960
  }];
183397
183961
  });
183398
- const seenEdge = /* @__PURE__ */ new Set();
183399
- const edges = model.edges.map((e) => {
183400
- const from = dockOf.get(e.from) ?? e.from;
183401
- const to = dockOf.get(e.to) ?? e.to;
183402
- return from === e.from && to === e.to ? e : { ...e, from, to };
183403
- }).filter((e) => {
183404
- if (e.from === e.to) return false;
183405
- const key = `${e.from} ${e.to} ${e.kind} ${e.label ?? ""}`;
183406
- if (seenEdge.has(key)) return false;
183407
- seenEdge.add(key);
183408
- return true;
183409
- });
183410
183962
  return {
183411
183963
  ...model,
183412
183964
  nodes: [...model.nodes.filter((n2) => !hiddenNodeIds.has(n2.id)), ...collapsedNodes],
@@ -183564,7 +184116,7 @@ function modelToFlow(model, opts) {
183564
184116
  const base = isGeo ? { w: 24, h: 24 } : nodeSize(canvasNode, opts.direction);
183565
184117
  const sizeOverride = ov[n2.layoutKey ?? n2.id];
183566
184118
  const compactCollapsed = canvasNode.meta?.internalsHidden === true;
183567
- const presentationOverride = compactCollapsed && sizeOverride ? { ...sizeOverride, h: void 0 } : sizeOverride;
184119
+ const presentationOverride = compactCollapsed && sizeOverride ? { ...sizeOverride, w: void 0, h: void 0 } : sizeOverride;
183568
184120
  const projectedSize = isCircularControlShape(canvasNode.shape) ? base : isForkJoinShape(canvasNode.shape) ? forkJoinSizeWithOverride(presentationOverride, opts.direction) : sizeWithOverride(
183569
184121
  base,
183570
184122
  presentationOverride,
@@ -183573,7 +184125,20 @@ function modelToFlow(model, opts) {
183573
184125
  h: visibleFeatureH > 0 ? base.h : 24
183574
184126
  }
183575
184127
  );
183576
- const size = model.kind === "gv" && canvasNode.shape !== "package" ? { ...projectedSize, w: Math.min(projectedSize.w, GV_NODE_MAX_WIDTH) } : projectedSize;
184128
+ const compactPortHandles = compactCollapsed ? assignPortHandles(canvasNode.ports, opts.portOverrides, canvasNode.shape) : [];
184129
+ const compactPortWidth = Math.max(
184130
+ portedSideLength(compactPortHandles, "top"),
184131
+ portedSideLength(compactPortHandles, "bottom")
184132
+ );
184133
+ const compactPortHeight = Math.max(
184134
+ portedSideLength(compactPortHandles, "left"),
184135
+ portedSideLength(compactPortHandles, "right")
184136
+ );
184137
+ const size = model.kind === "gv" && canvasNode.shape !== "package" ? { ...projectedSize, w: Math.min(projectedSize.w, GV_NODE_MAX_WIDTH) } : compactCollapsed ? {
184138
+ ...projectedSize,
184139
+ w: Math.max(compactPortWidth, Math.min(projectedSize.w, GV_NODE_MAX_WIDTH)),
184140
+ h: Math.max(compactPortHeight, projectedSize.h)
184141
+ } : projectedSize;
183577
184142
  const parentId = n2.frame ?? (model.kind === "gv" && n2.parent && gvPackageIds.has(n2.parent) ? n2.parent : void 0) ?? (boundary ? boundary.id : void 0);
183578
184143
  nodes.push({
183579
184144
  id: n2.id,
@@ -197549,6 +198114,12 @@ body {
197549
198114
 
197550
198115
  /* REQ-194 \u2014 port variants: dashed reference ports, direction chevrons */
197551
198116
  .dnode-port.ref { stroke-dasharray: 2 2; }
198117
+ /* REQ-400 \u2014 a PROXY stands in for a port inside collapsed contents.
198118
+ * It is drawn hollow with a fine dotted outline, so it reads as a place a
198119
+ * connector docks rather than as an element that is really on the boundary. Its
198120
+ * dot pattern is deliberately finer than the reference port's dash. */
198121
+ .dnode-port.proxy { fill: none; stroke-dasharray: 1 2; stroke-width: 1.2; }
198122
+ .dnode-port.proxy.selected { fill: var(--accent); fill-opacity: 0.35; }
197552
198123
  /* REQ-376 \u2014 an action pin takes the OUTLINE and explicit fill of the action it sits
197553
198124
  * on rather than the flow colour (user direction 2026-08-09). Its rounded corners and its
197554
198125
  * direction arrow come from the shared port path in nodes.tsx. */
@@ -198534,6 +199105,21 @@ function sizeOf(node) {
198534
199105
  h: node.measured?.height ?? node.height ?? node.data.h
198535
199106
  };
198536
199107
  }
199108
+ function exportedNodeColorClass(node) {
199109
+ const frame2 = node.data.frame;
199110
+ if (frame2) {
199111
+ return diagramElementColorClass(diagramElementColorTypeForFrame(frame2.keyword, {
199112
+ boundary: frame2.meta?.boundaryBox === true,
199113
+ exhibitor: frame2.meta?.exhibitBoundary === true,
199114
+ loop: frame2.meta?.loop === true,
199115
+ parallel: frame2.meta?.parallel === true,
199116
+ performer: frame2.meta?.performerLane === true,
199117
+ structured: frame2.meta?.structuredControl === true
199118
+ }));
199119
+ }
199120
+ const semantic = node.data.node;
199121
+ return diagramElementColorClass(semantic ? diagramElementColorTypeForNode(semantic.shape, semantic.keyword) : "default");
199122
+ }
198537
199123
  function labelExtent(text) {
198538
199124
  return { w: Math.max(8, text.length * 6.2) / 2, h: 7 };
198539
199125
  }
@@ -198569,7 +199155,7 @@ function buildDiagramSvg(options) {
198569
199155
  extend2(p.x + background.x, p.y + background.y, background.width, background.height);
198570
199156
  }
198571
199157
  const body = nodeMarkup(node);
198572
- if (body) nodeParts.push(`<g transform="translate(${Math.round(p.x)},${Math.round(p.y)})">${body}</g>`);
199158
+ if (body) nodeParts.push(`<g transform="translate(${Math.round(p.x)},${Math.round(p.y)})" class="${exportedNodeColorClass(node)}">${body}</g>`);
198573
199159
  }
198574
199160
  if (!isFinite(minX)) return null;
198575
199161
  const edgeParts = [];
@@ -198952,6 +199538,7 @@ function normalizeSideCar(value) {
198952
199538
  const next = {};
198953
199539
  if (state.mode !== void 0) next.mode = state.mode;
198954
199540
  if (state.zoom !== void 0) next.zoom = state.zoom;
199541
+ if (state.showGrid !== void 0) next.showGrid = state.showGrid;
198955
199542
  const dir = sanitizeDirection(state.direction);
198956
199543
  if (dir !== void 0) next.direction = dir;
198957
199544
  if (state.hiddenInternals !== void 0) next.hiddenInternals = state.hiddenInternals;
@@ -200373,6 +200960,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200373
200960
  len
200374
200961
  };
200375
200962
  })() : void 0;
200963
+ const proxy = rect.proxy;
200376
200964
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
200377
200965
  "g",
200378
200966
  {
@@ -200382,7 +200970,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200382
200970
  e.stopPropagation();
200383
200971
  onSelect(ph.port.id);
200384
200972
  } : void 0,
200385
- onContextMenu: onContext ? (e) => {
200973
+ onContextMenu: onContext && !proxy ? (e) => {
200386
200974
  e.preventDefault();
200387
200975
  e.stopPropagation();
200388
200976
  onContext(ph.port.id, e.clientX, e.clientY);
@@ -200405,7 +200993,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200405
200993
  /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
200406
200994
  "rect",
200407
200995
  {
200408
- className: `dnode-port${ph.port.pin ? " pin" : ""}${ph.port.ref ? " ref" : ""}${rect.elongated ? " host" : ""}${selected2 ? " selected" : ""}${hovered ? " hovered" : ""}${moving ? " moving" : ""}${connectTargetSide ? " connect-target" : ""}`,
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" : ""}`,
200409
200997
  x: rect.x,
200410
200998
  y: rect.y,
200411
200999
  width: rect.width,
@@ -200418,7 +201006,7 @@ function PortGlyph({ ph, w, h, topReserve = 0, showLabels, selected: selected2,
200418
201006
  d === "out" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(1) }) : "",
200419
201007
  d === "in" || d === "inout" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("path", { className: "dport-dir head", d: arrowHead(-1) }) : ""
200420
201008
  ] }) : "",
200421
- selected2 && connectStart && !connectTargetSide ? (() => {
201009
+ selected2 && connectStart && !connectTargetSide && !proxy ? (() => {
200422
201010
  const px = x + v.x * (half + PORT_PLUS_GAP);
200423
201011
  const py = y + v.y * (half + PORT_PLUS_GAP);
200424
201012
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("g", { className: "dport-plus", children: [
@@ -200868,7 +201456,8 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
200868
201456
  node.shape === "initial" || node.shape === "final" ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "rf-circular-control-hit", "aria-hidden": "true" }) : null,
200869
201457
  ports.flatMap((ph) => {
200870
201458
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
200871
- const startable = directRelationForHandle(data.kind, {
201459
+ const proxied = isProxyPort(ph.port);
201460
+ const startable = !proxied && directRelationForHandle(data.kind, {
200872
201461
  id: ph.port.id,
200873
201462
  name: ph.port.name,
200874
201463
  keyword: ph.port.pin ? "pin" : "port",
@@ -200889,7 +201478,8 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
200889
201478
  x: outer.x,
200890
201479
  y: outer.y,
200891
201480
  kind: "port",
200892
- active: revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === ph.side,
201481
+ connectable: !proxied,
201482
+ active: !proxied && (revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === ph.side),
200893
201483
  startable
200894
201484
  },
200895
201485
  `${ph.port.id}-out`
@@ -200902,7 +201492,8 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
200902
201492
  x: inner.x,
200903
201493
  y: inner.y,
200904
201494
  kind: "port",
200905
- active: revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === innerSide,
201495
+ connectable: !proxied,
201496
+ active: !proxied && (revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === innerSide),
200906
201497
  startable
200907
201498
  },
200908
201499
  `${ph.port.id}-in`
@@ -200912,17 +201503,21 @@ function SysmlNode({ id: id2, data, draggable, selected: selected2, width, heigh
200912
201503
  ctx.onPortMove ? ports.map((ph) => {
200913
201504
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
200914
201505
  const nestedChild = ph.port.parentPort !== void 0;
201506
+ const proxyPort = isProxyPort(ph.port);
200915
201507
  const strip = portInteractionStrip(rect, ph.side);
200916
201508
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
200917
201509
  "div",
200918
201510
  {
200919
201511
  className: "port-drag-hit nodrag",
200920
201512
  style: { position: "absolute", left: strip.cx, top: strip.cy, width: strip.w, height: strip.h, transform: "translate(-50%,-50%)" },
200921
- title: nestedChild ? `${ph.port.name} - click to select; drag a dot to connect (moves with its parent port)` : `${ph.port.name} - drag the centre to move along the edge; drag a dot to connect; click to select`,
201513
+ title: proxyPort ? `${ph.port.name} - a proxy for a port inside the collapsed contents; drag the centre to move it, click to select the port it stands for` : nestedChild ? `${ph.port.name} - click to select; drag a dot to connect (moves with its parent port)` : `${ph.port.name} - drag the centre to move along the edge; drag a dot to connect; click to select`,
200922
201514
  onPointerEnter: () => setHoveredPortId(ph.port.id),
200923
201515
  onPointerLeave: () => setHoveredPortId((cur) => cur === ph.port.id ? void 0 : cur),
200924
201516
  onPointerDown: (e) => onPortPointerDown(ph.port.id, e, { side: ph.side, offset: ph.offset }, !nestedChild),
200925
- onContextMenu: (e) => {
201517
+ onContextMenu: proxyPort ? (e) => {
201518
+ e.preventDefault();
201519
+ e.stopPropagation();
201520
+ } : (e) => {
200926
201521
  e.preventDefault();
200927
201522
  e.stopPropagation();
200928
201523
  ctx.onContextNode(ph.port.id, e.clientX, e.clientY);
@@ -201349,7 +201944,8 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201349
201944
  }),
201350
201945
  showPorts ? ports.flatMap((ph) => {
201351
201946
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201352
- const startable = directRelationForHandle(data.kind, {
201947
+ const proxied = isProxyPort(ph.port);
201948
+ const startable = !proxied && directRelationForHandle(data.kind, {
201353
201949
  id: ph.port.id,
201354
201950
  name: ph.port.name,
201355
201951
  keyword: ph.port.pin ? "pin" : "port",
@@ -201370,7 +201966,8 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201370
201966
  x: outer.x,
201371
201967
  y: outer.y,
201372
201968
  kind: "port",
201373
- active: revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === ph.side,
201969
+ connectable: !proxied,
201970
+ active: !proxied && (revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === ph.side),
201374
201971
  startable
201375
201972
  },
201376
201973
  `${ph.port.id}-out`
@@ -201383,7 +201980,8 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201383
201980
  x: inner.x,
201384
201981
  y: inner.y,
201385
201982
  kind: "port",
201386
- active: revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === innerSide,
201983
+ connectable: !proxied,
201984
+ active: !proxied && (revealBoth || ph.port.id === connectTargetPortId && connectTargetPortSide === innerSide),
201387
201985
  startable
201388
201986
  },
201389
201987
  `${ph.port.id}-in`
@@ -201393,17 +201991,21 @@ function FrameNode({ id: id2, data, selected: selected2, width, height }) {
201393
201991
  ctx.onPortMove ? ports.map((ph) => {
201394
201992
  const rect = portGlyphRect(ph, w, h, featureCompartmentHeight);
201395
201993
  const nestedChild = ph.port.parentPort !== void 0;
201994
+ const proxyPort = isProxyPort(ph.port);
201396
201995
  const strip = portInteractionStrip(rect, ph.side);
201397
201996
  return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
201398
201997
  "div",
201399
201998
  {
201400
201999
  className: "port-drag-hit nodrag",
201401
202000
  style: { position: "absolute", left: strip.cx, top: strip.cy, width: strip.w, height: strip.h, transform: "translate(-50%,-50%)" },
201402
- title: nestedChild ? `${ph.port.name} - click to select; drag a dot to connect (moves with its parent port)` : `${ph.port.name} - drag the centre to move along the edge; drag a dot to connect; click to select`,
202001
+ title: proxyPort ? `${ph.port.name} - a proxy for a port inside the collapsed contents; drag the centre to move it, click to select the port it stands for` : nestedChild ? `${ph.port.name} - click to select; drag a dot to connect (moves with its parent port)` : `${ph.port.name} - drag the centre to move along the edge; drag a dot to connect; click to select`,
201403
202002
  onPointerEnter: () => setHoveredPortId(ph.port.id),
201404
202003
  onPointerLeave: () => setHoveredPortId((cur) => cur === ph.port.id ? void 0 : cur),
201405
202004
  onPointerDown: (e) => onPortPointerDown(ph.port.id, e, { side: ph.side, offset: ph.offset }, !nestedChild),
201406
- onContextMenu: (e) => {
202005
+ onContextMenu: proxyPort ? (e) => {
202006
+ e.preventDefault();
202007
+ e.stopPropagation();
202008
+ } : (e) => {
201407
202009
  e.preventDefault();
201408
202010
  e.stopPropagation();
201409
202011
  ctx.onContextNode(ph.port.id, e.clientX, e.clientY);
@@ -201777,7 +202379,7 @@ async function runExport(command) {
201777
202379
  }
201778
202380
 
201779
202381
  // src/main.ts
201780
- var VERSION2 = true ? "0.21.0" : "dev";
202382
+ var VERSION2 = true ? "0.22.0" : "dev";
201781
202383
  function display(file) {
201782
202384
  const rel2 = path9.relative(process.cwd(), file);
201783
202385
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;