@a9n-shoji/rvw 0.4.3 → 0.5.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/CHANGELOG.md +22 -0
- package/README.md +4 -1
- package/dist/cli.mjs +699 -3
- package/dist/cli.mjs.map +4 -4
- package/dist/web/assets/StructureViewer-RiN3cqvU.js +5 -0
- package/dist/web/assets/{index-B0xRoLW2.css → index-CUAdTtdU.css} +1 -1
- package/dist/web/assets/index-D0LsMDeu.js +11 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/skills/rvw-structure/SKILL.md +27 -2
- package/skills/rvw-structure/references/structure-authoring.md +33 -4
- package/dist/web/assets/StructureViewer-EKDvAQ2a.js +0 -5
- package/dist/web/assets/index-DC6BgBp4.js +0 -11
package/dist/cli.mjs
CHANGED
|
@@ -20295,7 +20295,7 @@ import path5 from "node:path";
|
|
|
20295
20295
|
import { TextDecoder as TextDecoder2 } from "node:util";
|
|
20296
20296
|
|
|
20297
20297
|
// src/shared/constants.ts
|
|
20298
|
-
var APP_VERSION = "0.
|
|
20298
|
+
var APP_VERSION = "0.5.0";
|
|
20299
20299
|
var PROTOCOL_VERSION = 4;
|
|
20300
20300
|
var VIEWER_ID_HEADER = "x-rvw-viewer-id";
|
|
20301
20301
|
var VIEWER_OPEN_LEASE_HEADER = "x-rvw-viewer-open-lease";
|
|
@@ -43805,6 +43805,691 @@ function createRuntime(options = {}) {
|
|
|
43805
43805
|
return { database, git, github, service, close: () => database.close() };
|
|
43806
43806
|
}
|
|
43807
43807
|
|
|
43808
|
+
// src/domain/structure-projection.ts
|
|
43809
|
+
var STRUCTURE_NODE_WIDTH = 228;
|
|
43810
|
+
var STRUCTURE_NODE_HEIGHT = 112;
|
|
43811
|
+
function stableCompare(left, right) {
|
|
43812
|
+
return left === right ? 0 : left < right ? -1 : 1;
|
|
43813
|
+
}
|
|
43814
|
+
function compareLinks([leftA, leftB], [rightA, rightB]) {
|
|
43815
|
+
const first = stableCompare(leftA, rightA);
|
|
43816
|
+
return first === 0 ? stableCompare(leftB, rightB) : first;
|
|
43817
|
+
}
|
|
43818
|
+
function simpleStructureTopology(structure) {
|
|
43819
|
+
const neighborSets = new Map(structure.nodes.map((node2) => [node2.id, /* @__PURE__ */ new Set()]));
|
|
43820
|
+
const pairNodes = /* @__PURE__ */ new Map();
|
|
43821
|
+
const pairDirections = /* @__PURE__ */ new Map();
|
|
43822
|
+
for (const edge of structure.edges) {
|
|
43823
|
+
if (edge.from === edge.to || !neighborSets.has(edge.from) || !neighborSets.has(edge.to)) {
|
|
43824
|
+
continue;
|
|
43825
|
+
}
|
|
43826
|
+
neighborSets.get(edge.from)?.add(edge.to);
|
|
43827
|
+
neighborSets.get(edge.to)?.add(edge.from);
|
|
43828
|
+
const ordered = [edge.from, edge.to].sort(stableCompare);
|
|
43829
|
+
const pairKey = JSON.stringify(ordered);
|
|
43830
|
+
pairNodes.set(pairKey, ordered);
|
|
43831
|
+
const directions = pairDirections.get(pairKey) ?? {
|
|
43832
|
+
hasUndirected: false,
|
|
43833
|
+
directions: /* @__PURE__ */ new Map()
|
|
43834
|
+
};
|
|
43835
|
+
if (edge.directed) {
|
|
43836
|
+
directions.directions.set(JSON.stringify([edge.from, edge.to]), [edge.from, edge.to]);
|
|
43837
|
+
} else {
|
|
43838
|
+
directions.hasUndirected = true;
|
|
43839
|
+
}
|
|
43840
|
+
pairDirections.set(pairKey, directions);
|
|
43841
|
+
}
|
|
43842
|
+
return {
|
|
43843
|
+
neighbors: new Map(
|
|
43844
|
+
[...neighborSets].map(([nodeId, neighbors]) => [nodeId, [...neighbors].sort(stableCompare)])
|
|
43845
|
+
),
|
|
43846
|
+
links: [...pairNodes.values()].sort(compareLinks),
|
|
43847
|
+
directionalLinks: [...pairDirections.values()].flatMap(
|
|
43848
|
+
({ hasUndirected, directions }) => !hasUndirected && directions.size === 1 ? [...directions.values()] : []
|
|
43849
|
+
).sort(compareLinks)
|
|
43850
|
+
};
|
|
43851
|
+
}
|
|
43852
|
+
function topologyComponents(topology) {
|
|
43853
|
+
const components = [];
|
|
43854
|
+
const assigned = /* @__PURE__ */ new Set();
|
|
43855
|
+
for (const first of [...topology.neighbors.keys()].sort(stableCompare)) {
|
|
43856
|
+
if (assigned.has(first)) continue;
|
|
43857
|
+
const component = [];
|
|
43858
|
+
const queue = [first];
|
|
43859
|
+
assigned.add(first);
|
|
43860
|
+
for (let index2 = 0; index2 < queue.length; index2 += 1) {
|
|
43861
|
+
const current = queue[index2];
|
|
43862
|
+
component.push(current);
|
|
43863
|
+
for (const neighbor of topology.neighbors.get(current) ?? []) {
|
|
43864
|
+
if (assigned.has(neighbor)) continue;
|
|
43865
|
+
assigned.add(neighbor);
|
|
43866
|
+
queue.push(neighbor);
|
|
43867
|
+
}
|
|
43868
|
+
}
|
|
43869
|
+
component.sort(stableCompare);
|
|
43870
|
+
components.push(component);
|
|
43871
|
+
}
|
|
43872
|
+
return components.sort(
|
|
43873
|
+
(left, right) => right.length - left.length || stableCompare(left[0], right[0])
|
|
43874
|
+
);
|
|
43875
|
+
}
|
|
43876
|
+
function topologyRoot(nodeIds, topology) {
|
|
43877
|
+
const neighborDegree = (nodeId) => (topology.neighbors.get(nodeId) ?? []).reduce(
|
|
43878
|
+
(total, neighbor) => total + (topology.neighbors.get(neighbor)?.length ?? 0),
|
|
43879
|
+
0
|
|
43880
|
+
);
|
|
43881
|
+
return [...nodeIds].sort(
|
|
43882
|
+
(left, right) => (topology.neighbors.get(right)?.length ?? 0) - (topology.neighbors.get(left)?.length ?? 0) || neighborDegree(right) - neighborDegree(left) || stableCompare(left, right)
|
|
43883
|
+
)[0];
|
|
43884
|
+
}
|
|
43885
|
+
function countNonForwardLinks(ranks, directionalLinks) {
|
|
43886
|
+
return directionalLinks.reduce((count, [from, to]) => {
|
|
43887
|
+
const fromRank = ranks.get(from);
|
|
43888
|
+
const toRank = ranks.get(to);
|
|
43889
|
+
return count + (fromRank !== void 0 && toRank !== void 0 && fromRank >= toRank ? 1 : 0);
|
|
43890
|
+
}, 0);
|
|
43891
|
+
}
|
|
43892
|
+
function normalizedRankIndexes(ranks) {
|
|
43893
|
+
return new Map(
|
|
43894
|
+
[...new Set(ranks.values())].sort((left, right) => left - right).map((rank, index2) => [rank, index2])
|
|
43895
|
+
);
|
|
43896
|
+
}
|
|
43897
|
+
function totalDirectionalSpan(ranks, directionalLinks) {
|
|
43898
|
+
const indexes = normalizedRankIndexes(ranks);
|
|
43899
|
+
return directionalLinks.reduce((total, [from, to]) => {
|
|
43900
|
+
const fromRank = ranks.get(from);
|
|
43901
|
+
const toRank = ranks.get(to);
|
|
43902
|
+
if (fromRank === void 0 || toRank === void 0) return total;
|
|
43903
|
+
return total + Math.abs(indexes.get(fromRank) - indexes.get(toRank));
|
|
43904
|
+
}, 0);
|
|
43905
|
+
}
|
|
43906
|
+
function compareNumberArrays(left, right) {
|
|
43907
|
+
for (let index2 = 0; index2 < Math.max(left.length, right.length); index2 += 1) {
|
|
43908
|
+
const difference = (left[index2] ?? 0) - (right[index2] ?? 0);
|
|
43909
|
+
if (difference !== 0) return difference;
|
|
43910
|
+
}
|
|
43911
|
+
return 0;
|
|
43912
|
+
}
|
|
43913
|
+
function canonicalTopologyStructuralColors(nodeIds, topology, originNodeId) {
|
|
43914
|
+
const nodeSet = new Set(nodeIds);
|
|
43915
|
+
const incomingNeighbors = new Map(nodeIds.map((nodeId) => [nodeId, /* @__PURE__ */ new Set()]));
|
|
43916
|
+
const outgoingNeighbors = new Map(nodeIds.map((nodeId) => [nodeId, /* @__PURE__ */ new Set()]));
|
|
43917
|
+
for (const [from, to] of topology.directionalLinks) {
|
|
43918
|
+
if (!nodeSet.has(from) || !nodeSet.has(to)) continue;
|
|
43919
|
+
outgoingNeighbors.get(from)?.add(to);
|
|
43920
|
+
incomingNeighbors.get(to)?.add(from);
|
|
43921
|
+
}
|
|
43922
|
+
const ambiguousNeighbors = new Map(
|
|
43923
|
+
nodeIds.map((nodeId) => {
|
|
43924
|
+
const directionalNeighbors = /* @__PURE__ */ new Set([
|
|
43925
|
+
...incomingNeighbors.get(nodeId),
|
|
43926
|
+
...outgoingNeighbors.get(nodeId)
|
|
43927
|
+
]);
|
|
43928
|
+
return [
|
|
43929
|
+
nodeId,
|
|
43930
|
+
new Set(
|
|
43931
|
+
(topology.neighbors.get(nodeId) ?? []).filter(
|
|
43932
|
+
(neighbor) => nodeSet.has(neighbor) && !directionalNeighbors.has(neighbor)
|
|
43933
|
+
)
|
|
43934
|
+
)
|
|
43935
|
+
];
|
|
43936
|
+
})
|
|
43937
|
+
);
|
|
43938
|
+
const assignColors = (descriptors) => {
|
|
43939
|
+
const colorByDescriptor = new Map(
|
|
43940
|
+
[...new Set(descriptors.values())].sort(stableCompare).map((descriptor, index2) => [descriptor, index2])
|
|
43941
|
+
);
|
|
43942
|
+
return new Map(
|
|
43943
|
+
nodeIds.map((nodeId) => [nodeId, colorByDescriptor.get(descriptors.get(nodeId))])
|
|
43944
|
+
);
|
|
43945
|
+
};
|
|
43946
|
+
let colors = assignColors(
|
|
43947
|
+
new Map(
|
|
43948
|
+
nodeIds.map((nodeId) => [
|
|
43949
|
+
nodeId,
|
|
43950
|
+
JSON.stringify([
|
|
43951
|
+
nodeId === originNodeId ? 1 : 0,
|
|
43952
|
+
incomingNeighbors.get(nodeId).size,
|
|
43953
|
+
outgoingNeighbors.get(nodeId).size,
|
|
43954
|
+
ambiguousNeighbors.get(nodeId).size
|
|
43955
|
+
])
|
|
43956
|
+
])
|
|
43957
|
+
)
|
|
43958
|
+
);
|
|
43959
|
+
for (let pass = 0; pass < nodeIds.length; pass += 1) {
|
|
43960
|
+
const next2 = assignColors(
|
|
43961
|
+
new Map(
|
|
43962
|
+
nodeIds.map((nodeId) => [
|
|
43963
|
+
nodeId,
|
|
43964
|
+
JSON.stringify([
|
|
43965
|
+
colors.get(nodeId),
|
|
43966
|
+
[...incomingNeighbors.get(nodeId)].map((neighbor) => colors.get(neighbor)).sort((left, right) => left - right),
|
|
43967
|
+
[...outgoingNeighbors.get(nodeId)].map((neighbor) => colors.get(neighbor)).sort((left, right) => left - right),
|
|
43968
|
+
[...ambiguousNeighbors.get(nodeId)].map((neighbor) => colors.get(neighbor)).sort((left, right) => left - right)
|
|
43969
|
+
])
|
|
43970
|
+
])
|
|
43971
|
+
)
|
|
43972
|
+
);
|
|
43973
|
+
if (nodeIds.every((nodeId) => next2.get(nodeId) === colors.get(nodeId))) break;
|
|
43974
|
+
colors = next2;
|
|
43975
|
+
}
|
|
43976
|
+
return colors;
|
|
43977
|
+
}
|
|
43978
|
+
function chooseRankProposal(nodeId, proposals, ranks, directionalLinks) {
|
|
43979
|
+
const scored = [...proposals].map((rank) => {
|
|
43980
|
+
const candidate = new Map(ranks).set(nodeId, rank);
|
|
43981
|
+
return {
|
|
43982
|
+
rank,
|
|
43983
|
+
nonForward: countNonForwardLinks(candidate, directionalLinks),
|
|
43984
|
+
occupiedColumns: new Set(candidate.values()).size,
|
|
43985
|
+
span: totalDirectionalSpan(candidate, directionalLinks)
|
|
43986
|
+
};
|
|
43987
|
+
});
|
|
43988
|
+
scored.sort(
|
|
43989
|
+
(left, right) => left.nonForward - right.nonForward || left.occupiedColumns - right.occupiedColumns || left.span - right.span || Math.abs(left.rank) - Math.abs(right.rank) || left.rank - right.rank
|
|
43990
|
+
);
|
|
43991
|
+
return scored[0].rank;
|
|
43992
|
+
}
|
|
43993
|
+
function applyForwardWaves(nodeSet, topology, ranks) {
|
|
43994
|
+
for (let pass = 0; pass < nodeSet.size; pass += 1) {
|
|
43995
|
+
const proposals = /* @__PURE__ */ new Map();
|
|
43996
|
+
for (const [from, to] of topology.directionalLinks) {
|
|
43997
|
+
if (!nodeSet.has(from) || !nodeSet.has(to) || ranks.has(to)) continue;
|
|
43998
|
+
const fromRank = ranks.get(from);
|
|
43999
|
+
if (fromRank === void 0) continue;
|
|
44000
|
+
const targetProposals = proposals.get(to) ?? /* @__PURE__ */ new Set();
|
|
44001
|
+
targetProposals.add(fromRank + 1);
|
|
44002
|
+
proposals.set(to, targetProposals);
|
|
44003
|
+
}
|
|
44004
|
+
if (proposals.size === 0) break;
|
|
44005
|
+
const accepted = [...proposals].map(
|
|
44006
|
+
([nodeId, nodeProposals]) => [
|
|
44007
|
+
nodeId,
|
|
44008
|
+
chooseRankProposal(nodeId, nodeProposals, ranks, topology.directionalLinks)
|
|
44009
|
+
]
|
|
44010
|
+
).sort(([left], [right]) => stableCompare(left, right));
|
|
44011
|
+
for (const [nodeId, rank] of accepted) ranks.set(nodeId, rank);
|
|
44012
|
+
}
|
|
44013
|
+
}
|
|
44014
|
+
function stronglyConnectedComponents(nodeIds, directionalLinks) {
|
|
44015
|
+
const nodeSet = new Set(nodeIds);
|
|
44016
|
+
const outgoing = new Map(nodeIds.map((nodeId) => [nodeId, /* @__PURE__ */ new Set()]));
|
|
44017
|
+
for (const [from, to] of directionalLinks) {
|
|
44018
|
+
if (nodeSet.has(from) && nodeSet.has(to)) outgoing.get(from)?.add(to);
|
|
44019
|
+
}
|
|
44020
|
+
const indexes = /* @__PURE__ */ new Map();
|
|
44021
|
+
const lowLinks = /* @__PURE__ */ new Map();
|
|
44022
|
+
const stack = [];
|
|
44023
|
+
const onStack = /* @__PURE__ */ new Set();
|
|
44024
|
+
const components = [];
|
|
44025
|
+
let nextIndex = 0;
|
|
44026
|
+
const visit = (nodeId) => {
|
|
44027
|
+
const nodeIndex = nextIndex;
|
|
44028
|
+
nextIndex += 1;
|
|
44029
|
+
indexes.set(nodeId, nodeIndex);
|
|
44030
|
+
lowLinks.set(nodeId, nodeIndex);
|
|
44031
|
+
stack.push(nodeId);
|
|
44032
|
+
onStack.add(nodeId);
|
|
44033
|
+
for (const neighbor of [...outgoing.get(nodeId) ?? []].sort(stableCompare)) {
|
|
44034
|
+
if (!indexes.has(neighbor)) {
|
|
44035
|
+
visit(neighbor);
|
|
44036
|
+
lowLinks.set(nodeId, Math.min(lowLinks.get(nodeId), lowLinks.get(neighbor)));
|
|
44037
|
+
} else if (onStack.has(neighbor)) {
|
|
44038
|
+
lowLinks.set(nodeId, Math.min(lowLinks.get(nodeId), indexes.get(neighbor)));
|
|
44039
|
+
}
|
|
44040
|
+
}
|
|
44041
|
+
if (lowLinks.get(nodeId) !== indexes.get(nodeId)) return;
|
|
44042
|
+
const component = [];
|
|
44043
|
+
for (; ; ) {
|
|
44044
|
+
const member = stack.pop();
|
|
44045
|
+
onStack.delete(member);
|
|
44046
|
+
component.push(member);
|
|
44047
|
+
if (member === nodeId) break;
|
|
44048
|
+
}
|
|
44049
|
+
component.sort(stableCompare);
|
|
44050
|
+
components.push(component);
|
|
44051
|
+
};
|
|
44052
|
+
for (const nodeId of [...nodeIds].sort(stableCompare)) {
|
|
44053
|
+
if (!indexes.has(nodeId)) visit(nodeId);
|
|
44054
|
+
}
|
|
44055
|
+
return components;
|
|
44056
|
+
}
|
|
44057
|
+
function internalComponentRanks(nodeIds, directionalLinks, structuralColors, preferredAnchor, originIncomingBoundaryNodeIds, incomingBoundaryNodeIds, outgoingBoundaryNodeIds, attachmentBoundaryNodeIds) {
|
|
44058
|
+
if (nodeIds.length === 1) return /* @__PURE__ */ new Map([[nodeIds[0], 0]]);
|
|
44059
|
+
const nodeSet = new Set(nodeIds);
|
|
44060
|
+
const internalLinks = directionalLinks.filter(
|
|
44061
|
+
([from, to]) => nodeSet.has(from) && nodeSet.has(to)
|
|
44062
|
+
);
|
|
44063
|
+
const neighborSets = new Map(nodeIds.map((nodeId) => [nodeId, /* @__PURE__ */ new Set()]));
|
|
44064
|
+
for (const [from, to] of internalLinks) {
|
|
44065
|
+
neighborSets.get(from)?.add(to);
|
|
44066
|
+
neighborSets.get(to)?.add(from);
|
|
44067
|
+
}
|
|
44068
|
+
const topology = {
|
|
44069
|
+
neighbors: new Map(
|
|
44070
|
+
[...neighborSets].map(([nodeId, neighbors]) => [nodeId, [...neighbors].sort(stableCompare)])
|
|
44071
|
+
),
|
|
44072
|
+
links: internalLinks,
|
|
44073
|
+
directionalLinks: internalLinks
|
|
44074
|
+
};
|
|
44075
|
+
const ranksFromAnchor = (anchor) => {
|
|
44076
|
+
const rawRanks = /* @__PURE__ */ new Map([[anchor, 0]]);
|
|
44077
|
+
applyForwardWaves(nodeSet, topology, rawRanks);
|
|
44078
|
+
const rankIndexes = normalizedRankIndexes(rawRanks);
|
|
44079
|
+
return new Map(nodeIds.map((nodeId) => [nodeId, rankIndexes.get(rawRanks.get(nodeId)) ?? 0]));
|
|
44080
|
+
};
|
|
44081
|
+
if (preferredAnchor && nodeSet.has(preferredAnchor)) return ranksFromAnchor(preferredAnchor);
|
|
44082
|
+
const candidates = nodeIds.map((anchor) => {
|
|
44083
|
+
const ranks = ranksFromAnchor(anchor);
|
|
44084
|
+
const maxRank = Math.max(...ranks.values());
|
|
44085
|
+
const rankLoadProfile = Array.from(
|
|
44086
|
+
{ length: maxRank + 1 },
|
|
44087
|
+
(_, rank) => nodeIds.filter((nodeId) => ranks.get(nodeId) === rank).length
|
|
44088
|
+
);
|
|
44089
|
+
const rankStructuralProfile = rankLoadProfile.flatMap((_, rank) => [
|
|
44090
|
+
-1,
|
|
44091
|
+
...nodeIds.filter((nodeId) => ranks.get(nodeId) === rank).map((nodeId) => structuralColors.get(nodeId)).sort((left, right) => left - right)
|
|
44092
|
+
]);
|
|
44093
|
+
return {
|
|
44094
|
+
anchor,
|
|
44095
|
+
ranks,
|
|
44096
|
+
nonForward: countNonForwardLinks(ranks, internalLinks),
|
|
44097
|
+
originIncomingBoundaryRanks: originIncomingBoundaryNodeIds.map((nodeId) => ranks.get(nodeId)).sort((left, right) => right - left),
|
|
44098
|
+
incomingBoundaryRanks: incomingBoundaryNodeIds.map((nodeId) => ranks.get(nodeId)).sort((left, right) => right - left),
|
|
44099
|
+
outgoingBoundaryDistances: outgoingBoundaryNodeIds.map((nodeId) => maxRank - ranks.get(nodeId)).sort((left, right) => right - left),
|
|
44100
|
+
attachmentBoundaryRanks: attachmentBoundaryNodeIds.map((nodeId) => ranks.get(nodeId)).sort((left, right) => right - left),
|
|
44101
|
+
width: new Set(ranks.values()).size,
|
|
44102
|
+
span: totalDirectionalSpan(ranks, internalLinks),
|
|
44103
|
+
rankLoadProfile,
|
|
44104
|
+
rankStructuralProfile,
|
|
44105
|
+
anchorStructuralColor: structuralColors.get(anchor)
|
|
44106
|
+
};
|
|
44107
|
+
});
|
|
44108
|
+
candidates.sort(
|
|
44109
|
+
(left, right) => left.nonForward - right.nonForward || compareNumberArrays(left.originIncomingBoundaryRanks, right.originIncomingBoundaryRanks) || compareNumberArrays(left.incomingBoundaryRanks, right.incomingBoundaryRanks) || compareNumberArrays(left.outgoingBoundaryDistances, right.outgoingBoundaryDistances) || compareNumberArrays(left.attachmentBoundaryRanks, right.attachmentBoundaryRanks) || left.width - right.width || left.span - right.span || compareNumberArrays(left.rankLoadProfile, right.rankLoadProfile) || compareNumberArrays(left.rankStructuralProfile, right.rankStructuralProfile) || left.anchorStructuralColor - right.anchorStructuralColor || stableCompare(left.anchor, right.anchor)
|
|
44110
|
+
);
|
|
44111
|
+
return candidates[0].ranks;
|
|
44112
|
+
}
|
|
44113
|
+
function directionalCondensationRanks(nodeIds, topology, originNodeId) {
|
|
44114
|
+
const nodeSet = new Set(nodeIds);
|
|
44115
|
+
const directionalLinks = topology.directionalLinks.filter(
|
|
44116
|
+
([from, to]) => nodeSet.has(from) && nodeSet.has(to)
|
|
44117
|
+
);
|
|
44118
|
+
const structuralColors = canonicalTopologyStructuralColors(nodeIds, topology, originNodeId);
|
|
44119
|
+
const components = stronglyConnectedComponents(nodeIds, directionalLinks);
|
|
44120
|
+
const componentByNodeId = /* @__PURE__ */ new Map();
|
|
44121
|
+
components.forEach((component, componentIndex) => {
|
|
44122
|
+
for (const nodeId of component) componentByNodeId.set(nodeId, componentIndex);
|
|
44123
|
+
});
|
|
44124
|
+
const outgoing = new Map(components.map((_, index2) => [index2, /* @__PURE__ */ new Set()]));
|
|
44125
|
+
const incoming = new Map(components.map((_, index2) => [index2, /* @__PURE__ */ new Set()]));
|
|
44126
|
+
for (const [from, to] of directionalLinks) {
|
|
44127
|
+
const fromComponent = componentByNodeId.get(from);
|
|
44128
|
+
const toComponent = componentByNodeId.get(to);
|
|
44129
|
+
if (fromComponent === toComponent) continue;
|
|
44130
|
+
outgoing.get(fromComponent).add(toComponent);
|
|
44131
|
+
incoming.get(toComponent).add(fromComponent);
|
|
44132
|
+
}
|
|
44133
|
+
const originComponent = componentByNodeId.get(originNodeId);
|
|
44134
|
+
const directionalGroups = [];
|
|
44135
|
+
const directionalGroupByComponent = /* @__PURE__ */ new Map();
|
|
44136
|
+
for (let first = 0; first < components.length; first += 1) {
|
|
44137
|
+
if (directionalGroupByComponent.has(first)) continue;
|
|
44138
|
+
const groupIndex = directionalGroups.length;
|
|
44139
|
+
const group = [];
|
|
44140
|
+
const queue = [first];
|
|
44141
|
+
directionalGroupByComponent.set(first, groupIndex);
|
|
44142
|
+
for (let index2 = 0; index2 < queue.length; index2 += 1) {
|
|
44143
|
+
const current = queue[index2];
|
|
44144
|
+
group.push(current);
|
|
44145
|
+
for (const neighbor of [...outgoing.get(current), ...incoming.get(current)]) {
|
|
44146
|
+
if (directionalGroupByComponent.has(neighbor)) continue;
|
|
44147
|
+
directionalGroupByComponent.set(neighbor, groupIndex);
|
|
44148
|
+
queue.push(neighbor);
|
|
44149
|
+
}
|
|
44150
|
+
}
|
|
44151
|
+
directionalGroups.push(group);
|
|
44152
|
+
}
|
|
44153
|
+
const directionalGroupByNodeId = /* @__PURE__ */ new Map();
|
|
44154
|
+
for (const [nodeId, componentIndex] of componentByNodeId) {
|
|
44155
|
+
directionalGroupByNodeId.set(nodeId, directionalGroupByComponent.get(componentIndex));
|
|
44156
|
+
}
|
|
44157
|
+
const attachmentBoundaryNodeIds = new Map(
|
|
44158
|
+
components.map((_, componentIndex) => [componentIndex, []])
|
|
44159
|
+
);
|
|
44160
|
+
for (const [left, right] of topology.links) {
|
|
44161
|
+
if (directionalGroupByNodeId.get(left) === directionalGroupByNodeId.get(right)) continue;
|
|
44162
|
+
attachmentBoundaryNodeIds.get(componentByNodeId.get(left)).push(left);
|
|
44163
|
+
attachmentBoundaryNodeIds.get(componentByNodeId.get(right)).push(right);
|
|
44164
|
+
}
|
|
44165
|
+
const compareComponents = (left, right) => stableCompare(components[left][0], components[right][0]);
|
|
44166
|
+
const localRanks = /* @__PURE__ */ new Map();
|
|
44167
|
+
const componentWidths = /* @__PURE__ */ new Map();
|
|
44168
|
+
for (let componentIndex = 0; componentIndex < components.length; componentIndex += 1) {
|
|
44169
|
+
const component = components[componentIndex];
|
|
44170
|
+
const ranks2 = internalComponentRanks(
|
|
44171
|
+
component,
|
|
44172
|
+
directionalLinks,
|
|
44173
|
+
structuralColors,
|
|
44174
|
+
component.includes(originNodeId) ? originNodeId : null,
|
|
44175
|
+
directionalLinks.flatMap(
|
|
44176
|
+
([from, to]) => componentByNodeId.get(from) === originComponent && componentByNodeId.get(to) === componentIndex && componentIndex !== originComponent ? [to] : []
|
|
44177
|
+
),
|
|
44178
|
+
directionalLinks.flatMap(
|
|
44179
|
+
([from, to]) => componentByNodeId.get(to) === componentIndex && componentByNodeId.get(from) !== componentIndex ? [to] : []
|
|
44180
|
+
),
|
|
44181
|
+
directionalLinks.flatMap(
|
|
44182
|
+
([from, to]) => componentByNodeId.get(from) === componentIndex && componentByNodeId.get(to) !== componentIndex ? [from] : []
|
|
44183
|
+
),
|
|
44184
|
+
attachmentBoundaryNodeIds.get(componentIndex)
|
|
44185
|
+
);
|
|
44186
|
+
localRanks.set(componentIndex, ranks2);
|
|
44187
|
+
componentWidths.set(componentIndex, Math.max(...ranks2.values()) + 1);
|
|
44188
|
+
}
|
|
44189
|
+
const remainingIncoming = new Map(
|
|
44190
|
+
components.map((_, componentIndex) => [componentIndex, incoming.get(componentIndex).size])
|
|
44191
|
+
);
|
|
44192
|
+
const componentStarts = /* @__PURE__ */ new Map();
|
|
44193
|
+
const ready = components.map((_, componentIndex) => componentIndex).filter((componentIndex) => remainingIncoming.get(componentIndex) === 0).sort(compareComponents);
|
|
44194
|
+
for (const componentIndex of ready) componentStarts.set(componentIndex, 0);
|
|
44195
|
+
while (ready.length > 0) {
|
|
44196
|
+
const current = ready.shift();
|
|
44197
|
+
for (const target of [...outgoing.get(current)].sort(compareComponents)) {
|
|
44198
|
+
componentStarts.set(
|
|
44199
|
+
target,
|
|
44200
|
+
Math.max(
|
|
44201
|
+
componentStarts.get(target) ?? 0,
|
|
44202
|
+
componentStarts.get(current) + componentWidths.get(current)
|
|
44203
|
+
)
|
|
44204
|
+
);
|
|
44205
|
+
const unresolved = remainingIncoming.get(target) - 1;
|
|
44206
|
+
remainingIncoming.set(target, unresolved);
|
|
44207
|
+
if (unresolved === 0) {
|
|
44208
|
+
ready.push(target);
|
|
44209
|
+
ready.sort(compareComponents);
|
|
44210
|
+
}
|
|
44211
|
+
}
|
|
44212
|
+
}
|
|
44213
|
+
const unshiftedRanks = /* @__PURE__ */ new Map();
|
|
44214
|
+
for (let componentIndex = 0; componentIndex < components.length; componentIndex += 1) {
|
|
44215
|
+
for (const [nodeId, localRank] of localRanks.get(componentIndex)) {
|
|
44216
|
+
unshiftedRanks.set(nodeId, componentStarts.get(componentIndex) + localRank);
|
|
44217
|
+
}
|
|
44218
|
+
}
|
|
44219
|
+
const originGroup = directionalGroupByComponent.get(originComponent);
|
|
44220
|
+
const groupOffsets = /* @__PURE__ */ new Map([[originGroup, -unshiftedRanks.get(originNodeId)]]);
|
|
44221
|
+
while (groupOffsets.size < directionalGroups.length) {
|
|
44222
|
+
const proposals = /* @__PURE__ */ new Map();
|
|
44223
|
+
for (const [left, right] of topology.links) {
|
|
44224
|
+
const leftGroup = directionalGroupByNodeId.get(left);
|
|
44225
|
+
const rightGroup = directionalGroupByNodeId.get(right);
|
|
44226
|
+
if (leftGroup === rightGroup) continue;
|
|
44227
|
+
const leftOffset = groupOffsets.get(leftGroup);
|
|
44228
|
+
const rightOffset = groupOffsets.get(rightGroup);
|
|
44229
|
+
if (leftOffset !== void 0 && rightOffset === void 0) {
|
|
44230
|
+
const offsets = proposals.get(rightGroup) ?? /* @__PURE__ */ new Set();
|
|
44231
|
+
offsets.add(unshiftedRanks.get(left) + leftOffset + 1 - unshiftedRanks.get(right));
|
|
44232
|
+
proposals.set(rightGroup, offsets);
|
|
44233
|
+
}
|
|
44234
|
+
if (leftOffset === void 0 && rightOffset !== void 0) {
|
|
44235
|
+
const offsets = proposals.get(leftGroup) ?? /* @__PURE__ */ new Set();
|
|
44236
|
+
offsets.add(unshiftedRanks.get(right) + rightOffset + 1 - unshiftedRanks.get(left));
|
|
44237
|
+
proposals.set(leftGroup, offsets);
|
|
44238
|
+
}
|
|
44239
|
+
}
|
|
44240
|
+
if (proposals.size === 0) break;
|
|
44241
|
+
const knownGroupOffsets = new Map(groupOffsets);
|
|
44242
|
+
const rankedValues = [...unshiftedRanks].flatMap(([nodeId, rank]) => {
|
|
44243
|
+
const offset = knownGroupOffsets.get(directionalGroupByNodeId.get(nodeId));
|
|
44244
|
+
return offset === void 0 ? [] : [rank + offset];
|
|
44245
|
+
});
|
|
44246
|
+
const acceptedOffsets = /* @__PURE__ */ new Map();
|
|
44247
|
+
for (const [groupIndex, proposedOffsets] of proposals) {
|
|
44248
|
+
const candidates = [...proposedOffsets].map((offset) => {
|
|
44249
|
+
const crossGroupSpan = topology.links.reduce((total, [left, right]) => {
|
|
44250
|
+
const leftGroup = directionalGroupByNodeId.get(left);
|
|
44251
|
+
const rightGroup = directionalGroupByNodeId.get(right);
|
|
44252
|
+
const leftOffset = leftGroup === groupIndex ? offset : knownGroupOffsets.get(leftGroup);
|
|
44253
|
+
const rightOffset = rightGroup === groupIndex ? offset : knownGroupOffsets.get(rightGroup);
|
|
44254
|
+
if (leftOffset === void 0 || rightOffset === void 0) return total;
|
|
44255
|
+
return total + Math.abs(
|
|
44256
|
+
unshiftedRanks.get(left) + leftOffset - unshiftedRanks.get(right) - rightOffset
|
|
44257
|
+
);
|
|
44258
|
+
}, 0);
|
|
44259
|
+
const addedRanks = directionalGroups[groupIndex].flatMap(
|
|
44260
|
+
(componentIndex) => components[componentIndex].map((nodeId) => unshiftedRanks.get(nodeId) + offset)
|
|
44261
|
+
);
|
|
44262
|
+
return {
|
|
44263
|
+
offset,
|
|
44264
|
+
crossGroupSpan,
|
|
44265
|
+
occupiedColumns: (/* @__PURE__ */ new Set([...rankedValues, ...addedRanks])).size
|
|
44266
|
+
};
|
|
44267
|
+
});
|
|
44268
|
+
candidates.sort(
|
|
44269
|
+
(left, right) => left.crossGroupSpan - right.crossGroupSpan || left.occupiedColumns - right.occupiedColumns || Math.abs(left.offset) - Math.abs(right.offset) || left.offset - right.offset
|
|
44270
|
+
);
|
|
44271
|
+
acceptedOffsets.set(groupIndex, candidates[0].offset);
|
|
44272
|
+
}
|
|
44273
|
+
for (const [groupIndex, offset] of acceptedOffsets) groupOffsets.set(groupIndex, offset);
|
|
44274
|
+
}
|
|
44275
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
44276
|
+
for (const [nodeId, rank] of unshiftedRanks) {
|
|
44277
|
+
const groupOffset = groupOffsets.get(directionalGroupByNodeId.get(nodeId));
|
|
44278
|
+
ranks.set(nodeId, rank + (groupOffset ?? 0));
|
|
44279
|
+
}
|
|
44280
|
+
return ranks;
|
|
44281
|
+
}
|
|
44282
|
+
function componentRanks(nodeIds, topology, entrypointId) {
|
|
44283
|
+
const nodeSet = new Set(nodeIds);
|
|
44284
|
+
const entrypoint = entrypointId && nodeSet.has(entrypointId) ? entrypointId : null;
|
|
44285
|
+
if (!entrypoint) {
|
|
44286
|
+
const root3 = topologyRoot(nodeIds, topology);
|
|
44287
|
+
const ranks2 = /* @__PURE__ */ new Map();
|
|
44288
|
+
ranks2.set(root3, 0);
|
|
44289
|
+
const queue = [root3];
|
|
44290
|
+
for (let index2 = 0; index2 < queue.length; index2 += 1) {
|
|
44291
|
+
const current = queue[index2];
|
|
44292
|
+
for (const neighbor of topology.neighbors.get(current) ?? []) {
|
|
44293
|
+
if (!nodeSet.has(neighbor) || ranks2.has(neighbor)) continue;
|
|
44294
|
+
ranks2.set(neighbor, ranks2.get(current) + 1);
|
|
44295
|
+
queue.push(neighbor);
|
|
44296
|
+
}
|
|
44297
|
+
}
|
|
44298
|
+
return ranks2;
|
|
44299
|
+
}
|
|
44300
|
+
const ranks = directionalCondensationRanks(nodeIds, topology, entrypoint);
|
|
44301
|
+
const rankedQueue = [...ranks].sort(
|
|
44302
|
+
([leftId, leftRank], [rightId, rightRank]) => leftRank - rightRank || stableCompare(leftId, rightId)
|
|
44303
|
+
).map(([nodeId]) => nodeId);
|
|
44304
|
+
for (let index2 = 0; index2 < rankedQueue.length; index2 += 1) {
|
|
44305
|
+
const current = rankedQueue[index2];
|
|
44306
|
+
for (const neighbor of topology.neighbors.get(current) ?? []) {
|
|
44307
|
+
if (!nodeSet.has(neighbor) || ranks.has(neighbor)) continue;
|
|
44308
|
+
ranks.set(neighbor, ranks.get(current) + 1);
|
|
44309
|
+
rankedQueue.push(neighbor);
|
|
44310
|
+
}
|
|
44311
|
+
}
|
|
44312
|
+
return ranks;
|
|
44313
|
+
}
|
|
44314
|
+
function orderRankGroups(groups, topology) {
|
|
44315
|
+
const rankValues = [...groups.keys()].sort((left, right) => left - right);
|
|
44316
|
+
const result = new Map(
|
|
44317
|
+
rankValues.map((rank) => [rank, [...groups.get(rank)].sort(stableCompare)])
|
|
44318
|
+
);
|
|
44319
|
+
const sortAgainst = (rank, adjacentRank) => {
|
|
44320
|
+
const nodes = result.get(rank);
|
|
44321
|
+
const adjacent = result.get(adjacentRank);
|
|
44322
|
+
const adjacentOrder = new Map(adjacent.map((nodeId, index2) => [nodeId, index2]));
|
|
44323
|
+
const barycenter = (nodeId) => {
|
|
44324
|
+
const indexes = (topology.neighbors.get(nodeId) ?? []).flatMap((neighbor) => {
|
|
44325
|
+
const index2 = adjacentOrder.get(neighbor);
|
|
44326
|
+
return index2 === void 0 ? [] : [index2];
|
|
44327
|
+
});
|
|
44328
|
+
return indexes.length === 0 ? null : indexes.reduce((total, index2) => total + index2, 0) / indexes.length;
|
|
44329
|
+
};
|
|
44330
|
+
nodes.sort((left, right) => {
|
|
44331
|
+
const leftCenter = barycenter(left);
|
|
44332
|
+
const rightCenter = barycenter(right);
|
|
44333
|
+
if (leftCenter === null && rightCenter === null) return stableCompare(left, right);
|
|
44334
|
+
if (leftCenter === null) return 1;
|
|
44335
|
+
if (rightCenter === null) return -1;
|
|
44336
|
+
return leftCenter - rightCenter || stableCompare(left, right);
|
|
44337
|
+
});
|
|
44338
|
+
};
|
|
44339
|
+
for (let pass = 0; pass < 6; pass += 1) {
|
|
44340
|
+
for (let index2 = 1; index2 < rankValues.length; index2 += 1) {
|
|
44341
|
+
sortAgainst(rankValues[index2], rankValues[index2 - 1]);
|
|
44342
|
+
}
|
|
44343
|
+
for (let index2 = rankValues.length - 2; index2 >= 0; index2 -= 1) {
|
|
44344
|
+
sortAgainst(rankValues[index2], rankValues[index2 + 1]);
|
|
44345
|
+
}
|
|
44346
|
+
}
|
|
44347
|
+
return result;
|
|
44348
|
+
}
|
|
44349
|
+
function layoutTopologyComponent(nodeIds, topology, entrypointId) {
|
|
44350
|
+
const ranks = componentRanks(nodeIds, topology, entrypointId);
|
|
44351
|
+
const rawGroups = /* @__PURE__ */ new Map();
|
|
44352
|
+
for (const nodeId of nodeIds) {
|
|
44353
|
+
const rank = ranks.get(nodeId) ?? 0;
|
|
44354
|
+
const group = rawGroups.get(rank) ?? [];
|
|
44355
|
+
group.push(nodeId);
|
|
44356
|
+
rawGroups.set(rank, group);
|
|
44357
|
+
}
|
|
44358
|
+
const groups = orderRankGroups(rawGroups, topology);
|
|
44359
|
+
const rankValues = [...groups.keys()].sort((left, right) => left - right);
|
|
44360
|
+
const rankStride = STRUCTURE_NODE_WIDTH + 192;
|
|
44361
|
+
const rowStride = STRUCTURE_NODE_HEIGHT + 72;
|
|
44362
|
+
const maxRows = Math.max(...[...groups.values()].map((nodes) => nodes.length));
|
|
44363
|
+
const centerY = (maxRows - 1) * rowStride / 2;
|
|
44364
|
+
const positions = /* @__PURE__ */ new Map();
|
|
44365
|
+
rankValues.forEach((rank, rankIndex) => {
|
|
44366
|
+
const group = groups.get(rank);
|
|
44367
|
+
const top = centerY - (group.length - 1) * rowStride / 2;
|
|
44368
|
+
group.forEach((nodeId, rowIndex) => {
|
|
44369
|
+
positions.set(nodeId, { x: rankIndex * rankStride, y: top + rowIndex * rowStride });
|
|
44370
|
+
});
|
|
44371
|
+
});
|
|
44372
|
+
return {
|
|
44373
|
+
nodeIds,
|
|
44374
|
+
positions,
|
|
44375
|
+
ranks,
|
|
44376
|
+
width: (rankValues.length - 1) * rankStride + STRUCTURE_NODE_WIDTH,
|
|
44377
|
+
height: (maxRows - 1) * rowStride + STRUCTURE_NODE_HEIGHT
|
|
44378
|
+
};
|
|
44379
|
+
}
|
|
44380
|
+
function projectStructure(structure) {
|
|
44381
|
+
const topology = simpleStructureTopology(structure);
|
|
44382
|
+
if (structure.nodes.length === 0) {
|
|
44383
|
+
const diagnostics2 = {
|
|
44384
|
+
columnCount: 0,
|
|
44385
|
+
rowsPerColumn: [],
|
|
44386
|
+
maxRows: 0,
|
|
44387
|
+
directionalLinkCount: topology.directionalLinks.length,
|
|
44388
|
+
nonForwardDirectionalLinkCount: 0,
|
|
44389
|
+
nonForwardDirectionalLinkRatio: 0,
|
|
44390
|
+
originOutgoingDirectionalLinkCount: 0
|
|
44391
|
+
};
|
|
44392
|
+
return {
|
|
44393
|
+
positionsByNodeId: /* @__PURE__ */ new Map(),
|
|
44394
|
+
rankByNodeId: /* @__PURE__ */ new Map(),
|
|
44395
|
+
columnIndexByNodeId: /* @__PURE__ */ new Map(),
|
|
44396
|
+
columns: [],
|
|
44397
|
+
directionalLinks: topology.directionalLinks,
|
|
44398
|
+
diagnostics: diagnostics2
|
|
44399
|
+
};
|
|
44400
|
+
}
|
|
44401
|
+
const componentGap = 180;
|
|
44402
|
+
const outerPadding = 64;
|
|
44403
|
+
const components = topologyComponents(topology).map(
|
|
44404
|
+
(nodeIds) => layoutTopologyComponent(
|
|
44405
|
+
nodeIds,
|
|
44406
|
+
topology,
|
|
44407
|
+
nodeIds.includes(structure.originNodeId) ? structure.originNodeId : null
|
|
44408
|
+
)
|
|
44409
|
+
);
|
|
44410
|
+
const totalArea = components.reduce(
|
|
44411
|
+
(sum, component) => sum + (component.width + componentGap) * (component.height + componentGap),
|
|
44412
|
+
0
|
|
44413
|
+
);
|
|
44414
|
+
const targetRowWidth = Math.max(960, Math.sqrt(totalArea) * 1.45);
|
|
44415
|
+
const positions = /* @__PURE__ */ new Map();
|
|
44416
|
+
const ranks = /* @__PURE__ */ new Map();
|
|
44417
|
+
let cursorX = outerPadding;
|
|
44418
|
+
let cursorY = outerPadding;
|
|
44419
|
+
let rowHeight = 0;
|
|
44420
|
+
for (const component of components) {
|
|
44421
|
+
if (cursorX > outerPadding && cursorX + component.width > targetRowWidth) {
|
|
44422
|
+
cursorX = outerPadding;
|
|
44423
|
+
cursorY += rowHeight + componentGap;
|
|
44424
|
+
rowHeight = 0;
|
|
44425
|
+
}
|
|
44426
|
+
for (const nodeId of component.nodeIds) {
|
|
44427
|
+
const point5 = component.positions.get(nodeId);
|
|
44428
|
+
positions.set(nodeId, { x: cursorX + point5.x, y: cursorY + point5.y });
|
|
44429
|
+
ranks.set(nodeId, component.ranks.get(nodeId) ?? 0);
|
|
44430
|
+
}
|
|
44431
|
+
cursorX += component.width + componentGap;
|
|
44432
|
+
rowHeight = Math.max(rowHeight, component.height);
|
|
44433
|
+
}
|
|
44434
|
+
const xValues = [...new Set([...positions.values()].map(({ x }) => x))].sort(
|
|
44435
|
+
(left, right) => left - right
|
|
44436
|
+
);
|
|
44437
|
+
const columnIndexByX = new Map(xValues.map((x, index2) => [x, index2]));
|
|
44438
|
+
const columns = xValues.map(
|
|
44439
|
+
(x) => [...positions].filter(([, point5]) => point5.x === x).sort(([, left], [, right]) => left.y - right.y).map(([nodeId]) => nodeId)
|
|
44440
|
+
);
|
|
44441
|
+
const columnIndexByNodeId = new Map(
|
|
44442
|
+
[...positions].map(([nodeId, point5]) => [nodeId, columnIndexByX.get(point5.x)])
|
|
44443
|
+
);
|
|
44444
|
+
const nonForwardDirectionalLinkCount = topology.directionalLinks.reduce(
|
|
44445
|
+
(count, [from, to]) => count + (columnIndexByNodeId.has(from) && columnIndexByNodeId.has(to) && columnIndexByNodeId.get(from) >= columnIndexByNodeId.get(to) ? 1 : 0),
|
|
44446
|
+
0
|
|
44447
|
+
);
|
|
44448
|
+
const directionalLinkCount = topology.directionalLinks.length;
|
|
44449
|
+
const rowsPerColumn = columns.map((column) => column.length);
|
|
44450
|
+
const diagnostics = {
|
|
44451
|
+
columnCount: columns.length,
|
|
44452
|
+
rowsPerColumn,
|
|
44453
|
+
maxRows: Math.max(0, ...rowsPerColumn),
|
|
44454
|
+
directionalLinkCount,
|
|
44455
|
+
nonForwardDirectionalLinkCount,
|
|
44456
|
+
nonForwardDirectionalLinkRatio: directionalLinkCount === 0 ? 0 : nonForwardDirectionalLinkCount / directionalLinkCount,
|
|
44457
|
+
originOutgoingDirectionalLinkCount: topology.directionalLinks.filter(
|
|
44458
|
+
([from]) => from === structure.originNodeId
|
|
44459
|
+
).length
|
|
44460
|
+
};
|
|
44461
|
+
return {
|
|
44462
|
+
positionsByNodeId: positions,
|
|
44463
|
+
rankByNodeId: ranks,
|
|
44464
|
+
columnIndexByNodeId,
|
|
44465
|
+
columns,
|
|
44466
|
+
directionalLinks: topology.directionalLinks,
|
|
44467
|
+
diagnostics
|
|
44468
|
+
};
|
|
44469
|
+
}
|
|
44470
|
+
function structureAuthoringWarnings(diagnostics) {
|
|
44471
|
+
const warnings = [];
|
|
44472
|
+
if (diagnostics.originOutgoingDirectionalLinkCount === 0) {
|
|
44473
|
+
warnings.push({
|
|
44474
|
+
code: "STRUCTURE_ORIGIN_NO_OUTGOING_DIRECTIONAL_RELATION",
|
|
44475
|
+
message: "origin has no outgoing unambiguous directed relation; verify that it is the factual code entrypoint for this behavior. A terminal or intermediate origin may still be valid. Do not change factual relation direction solely to remove this warning."
|
|
44476
|
+
});
|
|
44477
|
+
}
|
|
44478
|
+
if (diagnostics.maxRows >= 8) {
|
|
44479
|
+
warnings.push({
|
|
44480
|
+
code: "STRUCTURE_LAYOUT_MAX_ROWS_HIGH",
|
|
44481
|
+
message: "the initial projection has a tall column; reconsider the factual origin, node granularity, overlapping or nested claims, whether multiple behaviors are mixed, and the subject boundary."
|
|
44482
|
+
});
|
|
44483
|
+
}
|
|
44484
|
+
if (diagnostics.nonForwardDirectionalLinkRatio >= 0.25) {
|
|
44485
|
+
warnings.push({
|
|
44486
|
+
code: "STRUCTURE_LAYOUT_NON_FORWARD_DIRECTIONAL_LINK_RATIO_HIGH",
|
|
44487
|
+
message: "many unambiguous directed relations are non-forward in the initial projection; reconsider the factual origin, node granularity, behavior boundary, and subject boundary. A factual graph may remain above this threshold. Do not change edge direction solely to improve the score."
|
|
44488
|
+
});
|
|
44489
|
+
}
|
|
44490
|
+
return warnings;
|
|
44491
|
+
}
|
|
44492
|
+
|
|
43808
44493
|
// src/infrastructure/skills/skill-installer.ts
|
|
43809
44494
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
43810
44495
|
import {
|
|
@@ -50080,6 +50765,10 @@ function writeJson(value) {
|
|
|
50080
50765
|
process.stdout.write(`${JSON.stringify(value)}
|
|
50081
50766
|
`);
|
|
50082
50767
|
}
|
|
50768
|
+
function structureAuthoringFeedback(structure) {
|
|
50769
|
+
const layout = projectStructure(structure).diagnostics;
|
|
50770
|
+
return { layout, warnings: structureAuthoringWarnings(layout) };
|
|
50771
|
+
}
|
|
50083
50772
|
function writeJsonSequence(value) {
|
|
50084
50773
|
process.stdout.write(`${JSON.stringify(value)}
|
|
50085
50774
|
`);
|
|
@@ -50687,6 +51376,7 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
|
|
|
50687
51376
|
"pullRequest.sync",
|
|
50688
51377
|
"structure.list",
|
|
50689
51378
|
"structure.read",
|
|
51379
|
+
"structure.preview",
|
|
50690
51380
|
"structure.publish",
|
|
50691
51381
|
"structure.update",
|
|
50692
51382
|
"structure.delete",
|
|
@@ -50825,6 +51515,10 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
|
|
|
50825
51515
|
});
|
|
50826
51516
|
const comment3 = program2.command("comment").description("\u4FDD\u5B58\u6E08\u307F\u30B3\u30E1\u30F3\u30C8\u3092\u64CD\u4F5C");
|
|
50827
51517
|
const structure = program2.command("structure").description("source anchor\u4ED8\u304DStructure\u3092\u7BA1\u7406");
|
|
51518
|
+
structure.command("preview").requiredOption("--stdin", "stdin\u304B\u3089JSON\u3092\u8AAD\u3080").requiredOption("--json", "JSON\u3067\u51FA\u529B").description("Structure content\u3092\u4FDD\u5B58\u305B\u305Acanonical initial layout\u3067\u691C\u67FB").action(async () => {
|
|
51519
|
+
const content3 = structureContentInputSchema.parse(await readStdinJson());
|
|
51520
|
+
writeJson({ ok: true, ...structureAuthoringFeedback(content3) });
|
|
51521
|
+
});
|
|
50828
51522
|
structure.command("publish").requiredOption("--stdin", "stdin\u304B\u3089JSON\u3092\u8AAD\u3080").requiredOption("--json", "JSON\u3067\u51FA\u529B").description("Structure\u3092\u767B\u9332\uFF08viewer\u306F\u958B\u304B\u305Anavigation\u3082\u5909\u66F4\u3057\u306A\u3044\uFF09").action(async () => {
|
|
50829
51523
|
const input = structurePublishInputSchema.parse(await readStdinJson());
|
|
50830
51524
|
const published = await callService(
|
|
@@ -50832,7 +51526,8 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
|
|
|
50832
51526
|
input,
|
|
50833
51527
|
async () => await getRuntime().service.publishStructure(input)
|
|
50834
51528
|
);
|
|
50835
|
-
|
|
51529
|
+
const warnings = structureAuthoringFeedback(published).warnings;
|
|
51530
|
+
writeJson({ ok: true, structure: published, ...warnings.length > 0 ? { warnings } : {} });
|
|
50836
51531
|
});
|
|
50837
51532
|
structure.command("get").argument("<structure-uri>").requiredOption("--json", "JSON\u3067\u51FA\u529B").description("Structure\u306E\u73FE\u5728\u5185\u5BB9\u3092\u53D6\u5F97").action(async (uri) => {
|
|
50838
51533
|
const result = await callService(
|
|
@@ -50857,7 +51552,8 @@ function createProgram(runtimeFactory = defaultRuntimeFactory) {
|
|
|
50857
51552
|
{ uri, content: content3 },
|
|
50858
51553
|
async () => await getRuntime().service.updateStructure(uri, content3)
|
|
50859
51554
|
);
|
|
50860
|
-
|
|
51555
|
+
const warnings = structureAuthoringFeedback(updated).warnings;
|
|
51556
|
+
writeJson({ ok: true, structure: updated, ...warnings.length > 0 ? { warnings } : {} });
|
|
50861
51557
|
});
|
|
50862
51558
|
structure.command("delete").argument("<structure-uri>").option("--yes", "\u4E0D\u53EF\u9006\u306A\u524A\u9664\u3092\u78BA\u8A8D").option("--expected-updated-at <timestamp>", "preview\u3067\u53D6\u5F97\u3057\u305FupdatedAt").requiredOption("--json", "JSON\u3067\u51FA\u529B").description("Structure\u3092\u524A\u9664").action(
|
|
50863
51559
|
async (uri, options) => {
|