@ankhorage/zora 20.1.6 → 20.2.1
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 +12 -0
- package/README.md +1 -1
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.d.ts +13 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.js +71 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.d.ts +87 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.js +132 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/GraphView.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/bindGraphEvents.d.ts +7 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/bindGraphEvents.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/bindGraphEvents.js +59 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/bindGraphEvents.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.d.ts +7 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js +32 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphController.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.d.ts +22 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.js +41 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.d.ts +30 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.js +222 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.d.ts +5 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.js +51 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.d.ts +5 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.js +18 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.d.ts +5 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.js +14 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.d.ts +14 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.js +34 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.d.ts +3 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.js +10 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.js.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.d.ts +5 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.d.ts.map +1 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.js +12 -0
- package/dist/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.js.map +1 -0
- package/dist/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.d.ts.map +1 -1
- package/dist/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.js +25 -10
- package/dist/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.js.map +1 -1
- package/package.json +6 -4
- package/src/features/graph-view/adapters/inbound/web-artifact/GraphNodeOverlay.tsx +142 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/GraphView.tsx +293 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/bindGraphEvents.ts +102 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/createGraphController.ts +38 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/createGraphResizeObserver.ts +80 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.ts +301 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.test.ts +49 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.ts +72 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts +20 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.ts +20 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.ts +53 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.ts +13 -0
- package/src/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.ts +19 -0
- package/src/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.test.ts +47 -31
- package/src/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.ts +47 -11
- package/src/types/cytoscape-elk.d.ts +4 -0
- package/web-dist/graph-view/GraphView.d.ts +87 -0
- package/web-dist/graph-view/GraphView.js +123308 -0
- package/web-dist/graph-view/artifact.json +7 -0
- package/web-dist/tree-view/artifact.json +7 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type CytoscapeOptions } from 'cytoscape';
|
|
2
|
+
import type { GraphViewCallbacks, GraphViewEdge, GraphViewElementEventType, GraphViewLayoutName, GraphViewNode, GraphViewRenderedNode, GraphViewSize, GraphViewStyleRule } from './GraphView';
|
|
3
|
+
type GraphContainer = NonNullable<CytoscapeOptions['container']>;
|
|
4
|
+
type RenderedNodeListener = (nodes: readonly GraphViewRenderedNode[]) => void;
|
|
5
|
+
interface GraphRuntimeUpdate {
|
|
6
|
+
readonly edges: readonly GraphViewEdge[];
|
|
7
|
+
readonly fitPadding?: number;
|
|
8
|
+
readonly layout?: GraphViewLayoutName;
|
|
9
|
+
readonly layoutOptions?: Readonly<Record<string, unknown>>;
|
|
10
|
+
readonly maxZoom?: number;
|
|
11
|
+
readonly minZoom?: number;
|
|
12
|
+
readonly nodes: readonly GraphViewNode[];
|
|
13
|
+
readonly richNodeRendering: boolean;
|
|
14
|
+
readonly spacingFactor?: number;
|
|
15
|
+
readonly styleRules?: readonly GraphViewStyleRule[];
|
|
16
|
+
}
|
|
17
|
+
export interface GraphRuntime {
|
|
18
|
+
destroy(): void;
|
|
19
|
+
handleOverlayNodeEvent(id: string, type: GraphViewElementEventType): void;
|
|
20
|
+
setNodeSize(id: string, size: GraphViewSize): void;
|
|
21
|
+
setSelectedNodeIds(nodeIds: readonly string[] | undefined): void;
|
|
22
|
+
subscribeRenderedNodes(listener: RenderedNodeListener): () => void;
|
|
23
|
+
update(input: GraphRuntimeUpdate): void;
|
|
24
|
+
}
|
|
25
|
+
/*** Create one Cytoscape runtime that exclusively owns layout, fit, resize, and disposal. */
|
|
26
|
+
export declare function createGraphRuntime(container: GraphContainer, callbacksRef: {
|
|
27
|
+
current: GraphViewCallbacks;
|
|
28
|
+
}): GraphRuntime;
|
|
29
|
+
export {};
|
|
30
|
+
//# sourceMappingURL=createGraphRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGraphRuntime.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.ts"],"names":[],"mappings":"AAAA,OAAkB,EAAa,KAAK,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAOxE,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,yBAAyB,EACzB,mBAAmB,EACnB,aAAa,EACb,qBAAqB,EACrB,aAAa,EACb,kBAAkB,EACnB,MAAM,aAAa,CAAC;AAQrB,KAAK,cAAc,GAAG,WAAW,CAAC,gBAAgB,CAAC,WAAW,CAAC,CAAC,CAAC;AACjE,KAAK,oBAAoB,GAAG,CAAC,KAAK,EAAE,SAAS,qBAAqB,EAAE,KAAK,IAAI,CAAC;AAE9E,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,MAAM,CAAC,EAAE,mBAAmB,CAAC;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,SAAS,aAAa,EAAE,CAAC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,OAAO,CAAC;IACpC,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,kBAAkB,EAAE,CAAC;CACrD;AAED,MAAM,WAAW,YAAY;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,sBAAsB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,yBAAyB,GAAG,IAAI,CAAC;IAC1E,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,aAAa,GAAG,IAAI,CAAC;IACnD,kBAAkB,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,GAAG,IAAI,CAAC;IACjE,sBAAsB,CAAC,QAAQ,EAAE,oBAAoB,GAAG,MAAM,IAAI,CAAC;IACnE,MAAM,CAAC,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAAC;CACzC;AAoBD,6FAA6F;AAC7F,wBAAgB,kBAAkB,CAChC,SAAS,EAAE,cAAc,EACzB,YAAY,EAAE;IAAE,OAAO,EAAE,kBAAkB,CAAA;CAAE,GAC5C,YAAY,CAmBd"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import cytoscape, {} from 'cytoscape';
|
|
2
|
+
import elk from 'cytoscape-elk';
|
|
3
|
+
import { bindGraphEvents } from './bindGraphEvents';
|
|
4
|
+
import { createGraphController } from './createGraphController';
|
|
5
|
+
import { createGraphResizeObserver } from './createGraphResizeObserver';
|
|
6
|
+
import { fitGraphViewport } from './fitGraphViewport';
|
|
7
|
+
import { readGraphRenderedNodes } from './readGraphRenderedNodes';
|
|
8
|
+
import { runGraphLayout } from './runGraphLayout';
|
|
9
|
+
import { scheduleGraphFrame } from './scheduleGraphFrame';
|
|
10
|
+
import { syncGraphElements } from './syncGraphElements';
|
|
11
|
+
cytoscape.use(elk);
|
|
12
|
+
/*** Create one Cytoscape runtime that exclusively owns layout, fit, resize, and disposal. */
|
|
13
|
+
export function createGraphRuntime(container, callbacksRef) {
|
|
14
|
+
const cy = cytoscape({
|
|
15
|
+
container,
|
|
16
|
+
elements: [],
|
|
17
|
+
minZoom: 0.05,
|
|
18
|
+
maxZoom: 2,
|
|
19
|
+
selectionType: 'additive',
|
|
20
|
+
userPanningEnabled: true,
|
|
21
|
+
});
|
|
22
|
+
const state = createRuntimeState(cy, container, callbacksRef);
|
|
23
|
+
return {
|
|
24
|
+
destroy: () => destroyRuntime(state),
|
|
25
|
+
handleOverlayNodeEvent: (id, type) => handleOverlayNodeEvent(state, id, type),
|
|
26
|
+
setNodeSize: (id, size) => setNodeSize(state, id, size),
|
|
27
|
+
setSelectedNodeIds: (nodeIds) => setSelectedNodeIds(state, nodeIds),
|
|
28
|
+
subscribeRenderedNodes: (listener) => subscribeRenderedNodes(state, listener),
|
|
29
|
+
update: (input) => updateRuntime(state, input),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
/*** Build the mutable state shared by the single graph-runtime owner. */
|
|
33
|
+
function createRuntimeState(cy, container, callbacksRef) {
|
|
34
|
+
const fitPaddingRef = { current: 50 };
|
|
35
|
+
const controller = createGraphController(cy, fitPaddingRef);
|
|
36
|
+
const layoutRunningRef = { current: false };
|
|
37
|
+
const readyRef = { current: false };
|
|
38
|
+
const renderedNodeListeners = new Set();
|
|
39
|
+
const stateBase = {
|
|
40
|
+
callbacksRef,
|
|
41
|
+
controller,
|
|
42
|
+
cy,
|
|
43
|
+
fitPaddingRef,
|
|
44
|
+
generationRef: { current: 0 },
|
|
45
|
+
hoveredNodeIds: new Set(),
|
|
46
|
+
latestUpdateRef: { current: null },
|
|
47
|
+
layoutRef: { current: null },
|
|
48
|
+
layoutRunningRef,
|
|
49
|
+
nodeSizes: new Map(),
|
|
50
|
+
readyRef,
|
|
51
|
+
relayoutScheduledRef: { current: false },
|
|
52
|
+
renderedNodeListeners,
|
|
53
|
+
};
|
|
54
|
+
const unbindEvents = bindGraphEvents(cy, callbacksRef, controller, () => emitRenderedNodes(stateBase));
|
|
55
|
+
const resizeObserver = createGraphResizeObserver({
|
|
56
|
+
container,
|
|
57
|
+
cy,
|
|
58
|
+
fitPaddingRef,
|
|
59
|
+
layoutRunningRef,
|
|
60
|
+
onViewportSettled: () => emitRenderedNodes(stateBase),
|
|
61
|
+
readyRef,
|
|
62
|
+
});
|
|
63
|
+
return { ...stateBase, resizeObserver, unbindEvents };
|
|
64
|
+
}
|
|
65
|
+
/*** Apply one graph update and start exactly one new layout generation. */
|
|
66
|
+
function updateRuntime(state, input) {
|
|
67
|
+
if (state.cy.destroyed())
|
|
68
|
+
return;
|
|
69
|
+
stopCurrentLayout(state);
|
|
70
|
+
state.latestUpdateRef.current = input;
|
|
71
|
+
state.fitPaddingRef.current = input.fitPadding ?? 50;
|
|
72
|
+
state.cy.minZoom(input.minZoom ?? 0.05);
|
|
73
|
+
state.cy.maxZoom(input.maxZoom ?? 2);
|
|
74
|
+
syncGraphElements(state.cy, input.nodes, input.edges);
|
|
75
|
+
applyGraphStyles(state.cy, input.styleRules, input.richNodeRendering);
|
|
76
|
+
applyKnownNodeSizes(state);
|
|
77
|
+
startCurrentLayout(state, input);
|
|
78
|
+
}
|
|
79
|
+
/*** Start a generation-safe layout whose completion is the only normal automatic fit trigger. */
|
|
80
|
+
function startCurrentLayout(state, input) {
|
|
81
|
+
const generation = state.generationRef.current + 1;
|
|
82
|
+
state.generationRef.current = generation;
|
|
83
|
+
state.layoutRunningRef.current = true;
|
|
84
|
+
state.cy.resize();
|
|
85
|
+
state.layoutRef.current = runGraphLayout(state.cy, {
|
|
86
|
+
layout: input.layout ?? 'concentric',
|
|
87
|
+
layoutOptions: input.layoutOptions,
|
|
88
|
+
spacingFactor: input.spacingFactor ?? 1,
|
|
89
|
+
}, () => completeCurrentLayout(state, generation));
|
|
90
|
+
}
|
|
91
|
+
/*** Settle only the latest layout generation and fit once from node bounds. */
|
|
92
|
+
function completeCurrentLayout(state, generation) {
|
|
93
|
+
state.layoutRef.current = null;
|
|
94
|
+
scheduleGraphFrame(() => {
|
|
95
|
+
if (state.cy.destroyed() || generation !== state.generationRef.current)
|
|
96
|
+
return;
|
|
97
|
+
state.cy.resize();
|
|
98
|
+
state.layoutRunningRef.current = false;
|
|
99
|
+
if (!hasUsableViewport(state.cy))
|
|
100
|
+
return;
|
|
101
|
+
fitGraphViewport(state.cy, { padding: state.fitPaddingRef.current });
|
|
102
|
+
emitRenderedNodes(state);
|
|
103
|
+
if (!state.readyRef.current) {
|
|
104
|
+
state.readyRef.current = true;
|
|
105
|
+
state.callbacksRef.current.onReady?.(state.controller);
|
|
106
|
+
}
|
|
107
|
+
state.callbacksRef.current.onLayoutComplete?.(state.controller);
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
/*** Stop the current layout and invalidate every stale completion callback. */
|
|
111
|
+
function stopCurrentLayout(state) {
|
|
112
|
+
state.generationRef.current += 1;
|
|
113
|
+
state.layoutRunningRef.current = false;
|
|
114
|
+
state.layoutRef.current?.stop();
|
|
115
|
+
state.layoutRef.current = null;
|
|
116
|
+
}
|
|
117
|
+
/*** Apply consumer styles and hide native node paint only when React rich nodes are active. */
|
|
118
|
+
function applyGraphStyles(cy, rules, richNodeRendering) {
|
|
119
|
+
const styles = [...(rules ?? [])];
|
|
120
|
+
if (richNodeRendering) {
|
|
121
|
+
styles.push({
|
|
122
|
+
selector: 'node',
|
|
123
|
+
style: {
|
|
124
|
+
'background-opacity': 0,
|
|
125
|
+
'border-width': 0,
|
|
126
|
+
label: '',
|
|
127
|
+
'overlay-opacity': 0,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
if (styles.length > 0)
|
|
132
|
+
cy.style(styles).update();
|
|
133
|
+
}
|
|
134
|
+
/*** Synchronize controlled selection without rerunning layout. */
|
|
135
|
+
function setSelectedNodeIds(state, nodeIds) {
|
|
136
|
+
if (state.cy.destroyed() || nodeIds === undefined)
|
|
137
|
+
return;
|
|
138
|
+
const selectedIds = new Set(nodeIds);
|
|
139
|
+
state.cy.nodes().forEach((node) => {
|
|
140
|
+
if (selectedIds.has(node.id()))
|
|
141
|
+
node.select();
|
|
142
|
+
else
|
|
143
|
+
node.unselect();
|
|
144
|
+
});
|
|
145
|
+
emitRenderedNodes(state);
|
|
146
|
+
}
|
|
147
|
+
/*** Persist one rich-node size and schedule at most one relayout for changed geometry. */
|
|
148
|
+
function setNodeSize(state, id, size) {
|
|
149
|
+
const previous = state.nodeSizes.get(id);
|
|
150
|
+
if (previous?.width === size.width && previous.height === size.height)
|
|
151
|
+
return;
|
|
152
|
+
state.nodeSizes.set(id, size);
|
|
153
|
+
applyNodeSize(state.cy, id, size);
|
|
154
|
+
scheduleMeasuredNodeRelayout(state);
|
|
155
|
+
}
|
|
156
|
+
/*** Apply every known rich-node size after Cytoscape elements are replaced. */
|
|
157
|
+
function applyKnownNodeSizes(state) {
|
|
158
|
+
for (const [id, size] of state.nodeSizes)
|
|
159
|
+
applyNodeSize(state.cy, id, size);
|
|
160
|
+
}
|
|
161
|
+
/*** Apply one measured model-space size as a Cytoscape per-node style bypass. */
|
|
162
|
+
function applyNodeSize(cy, id, size) {
|
|
163
|
+
const node = cy.getElementById(id);
|
|
164
|
+
if (node.empty())
|
|
165
|
+
return;
|
|
166
|
+
node.style({ height: size.height, width: size.width });
|
|
167
|
+
}
|
|
168
|
+
/*** Coalesce DOM measurements into one follow-up layout generation. */
|
|
169
|
+
function scheduleMeasuredNodeRelayout(state) {
|
|
170
|
+
if (state.relayoutScheduledRef.current)
|
|
171
|
+
return;
|
|
172
|
+
state.relayoutScheduledRef.current = true;
|
|
173
|
+
scheduleGraphFrame(() => {
|
|
174
|
+
state.relayoutScheduledRef.current = false;
|
|
175
|
+
const input = state.latestUpdateRef.current;
|
|
176
|
+
if (input === null || state.cy.destroyed())
|
|
177
|
+
return;
|
|
178
|
+
stopCurrentLayout(state);
|
|
179
|
+
startCurrentLayout(state, input);
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
/*** Subscribe one React overlay to rendered node state without exposing the Cytoscape core. */
|
|
183
|
+
function subscribeRenderedNodes(state, listener) {
|
|
184
|
+
state.renderedNodeListeners.add(listener);
|
|
185
|
+
listener(readGraphRenderedNodes(state.cy, state.hoveredNodeIds));
|
|
186
|
+
return () => {
|
|
187
|
+
state.renderedNodeListeners.delete(listener);
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
/*** Publish current rendered node positions only to active rich-node subscribers. */
|
|
191
|
+
function emitRenderedNodes(state) {
|
|
192
|
+
if (state.renderedNodeListeners.size === 0)
|
|
193
|
+
return;
|
|
194
|
+
const nodes = readGraphRenderedNodes(state.cy, state.hoveredNodeIds);
|
|
195
|
+
for (const listener of state.renderedNodeListeners)
|
|
196
|
+
listener(nodes);
|
|
197
|
+
}
|
|
198
|
+
/*** Translate one rich-overlay event back into normal graph selection and callbacks. */
|
|
199
|
+
function handleOverlayNodeEvent(state, id, type) {
|
|
200
|
+
if (type === 'pointer-enter')
|
|
201
|
+
state.hoveredNodeIds.add(id);
|
|
202
|
+
if (type === 'pointer-leave')
|
|
203
|
+
state.hoveredNodeIds.delete(id);
|
|
204
|
+
if (type === 'press')
|
|
205
|
+
state.cy.getElementById(id).select();
|
|
206
|
+
state.callbacksRef.current.onNodeEvent?.({ id, type });
|
|
207
|
+
emitRenderedNodes(state);
|
|
208
|
+
}
|
|
209
|
+
/*** Return whether Cytoscape currently has dimensions suitable for fitting. */
|
|
210
|
+
function hasUsableViewport(cy) {
|
|
211
|
+
return cy.width() > 0 && cy.height() > 0;
|
|
212
|
+
}
|
|
213
|
+
/*** Dispose all runtime resources in the reverse order they were acquired. */
|
|
214
|
+
function destroyRuntime(state) {
|
|
215
|
+
state.resizeObserver?.disconnect();
|
|
216
|
+
stopCurrentLayout(state);
|
|
217
|
+
state.unbindEvents();
|
|
218
|
+
state.renderedNodeListeners.clear();
|
|
219
|
+
if (!state.cy.destroyed())
|
|
220
|
+
state.cy.destroy();
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=createGraphRuntime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createGraphRuntime.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createGraphRuntime.ts"],"names":[],"mappings":"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","sourcesContent":["import cytoscape, { type Core, type CytoscapeOptions } from 'cytoscape';\nimport elk from 'cytoscape-elk';\n\nimport { bindGraphEvents } from './bindGraphEvents';\nimport { createGraphController } from './createGraphController';\nimport { createGraphResizeObserver, type GraphResizeObserver } from './createGraphResizeObserver';\nimport { fitGraphViewport } from './fitGraphViewport';\nimport type {\n GraphViewCallbacks,\n GraphViewEdge,\n GraphViewElementEventType,\n GraphViewLayoutName,\n GraphViewNode,\n GraphViewRenderedNode,\n GraphViewSize,\n GraphViewStyleRule,\n} from './GraphView';\nimport { readGraphRenderedNodes } from './readGraphRenderedNodes';\nimport { runGraphLayout } from './runGraphLayout';\nimport { scheduleGraphFrame } from './scheduleGraphFrame';\nimport { syncGraphElements } from './syncGraphElements';\n\ncytoscape.use(elk as cytoscape.Ext);\n\ntype GraphContainer = NonNullable<CytoscapeOptions['container']>;\ntype RenderedNodeListener = (nodes: readonly GraphViewRenderedNode[]) => void;\n\ninterface GraphRuntimeUpdate {\n readonly edges: readonly GraphViewEdge[];\n readonly fitPadding?: number;\n readonly layout?: GraphViewLayoutName;\n readonly layoutOptions?: Readonly<Record<string, unknown>>;\n readonly maxZoom?: number;\n readonly minZoom?: number;\n readonly nodes: readonly GraphViewNode[];\n readonly richNodeRendering: boolean;\n readonly spacingFactor?: number;\n readonly styleRules?: readonly GraphViewStyleRule[];\n}\n\nexport interface GraphRuntime {\n destroy(): void;\n handleOverlayNodeEvent(id: string, type: GraphViewElementEventType): void;\n setNodeSize(id: string, size: GraphViewSize): void;\n setSelectedNodeIds(nodeIds: readonly string[] | undefined): void;\n subscribeRenderedNodes(listener: RenderedNodeListener): () => void;\n update(input: GraphRuntimeUpdate): void;\n}\n\ninterface GraphRuntimeState {\n readonly callbacksRef: { current: GraphViewCallbacks };\n readonly controller: ReturnType<typeof createGraphController>;\n readonly cy: Core;\n readonly fitPaddingRef: { current: number };\n readonly generationRef: { current: number };\n readonly hoveredNodeIds: Set<string>;\n readonly latestUpdateRef: { current: GraphRuntimeUpdate | null };\n readonly layoutRef: { current: ReturnType<typeof runGraphLayout> | null };\n readonly layoutRunningRef: { current: boolean };\n readonly nodeSizes: Map<string, GraphViewSize>;\n readonly readyRef: { current: boolean };\n readonly relayoutScheduledRef: { current: boolean };\n readonly renderedNodeListeners: Set<RenderedNodeListener>;\n readonly resizeObserver: GraphResizeObserver | null;\n readonly unbindEvents: () => void;\n}\n\n/*** Create one Cytoscape runtime that exclusively owns layout, fit, resize, and disposal. */\nexport function createGraphRuntime(\n container: GraphContainer,\n callbacksRef: { current: GraphViewCallbacks },\n): GraphRuntime {\n const cy = cytoscape({\n container,\n elements: [],\n minZoom: 0.05,\n maxZoom: 2,\n selectionType: 'additive',\n userPanningEnabled: true,\n });\n const state = createRuntimeState(cy, container, callbacksRef);\n\n return {\n destroy: () => destroyRuntime(state),\n handleOverlayNodeEvent: (id, type) => handleOverlayNodeEvent(state, id, type),\n setNodeSize: (id, size) => setNodeSize(state, id, size),\n setSelectedNodeIds: (nodeIds) => setSelectedNodeIds(state, nodeIds),\n subscribeRenderedNodes: (listener) => subscribeRenderedNodes(state, listener),\n update: (input) => updateRuntime(state, input),\n };\n}\n\n/*** Build the mutable state shared by the single graph-runtime owner. */\nfunction createRuntimeState(\n cy: Core,\n container: GraphContainer,\n callbacksRef: { current: GraphViewCallbacks },\n): GraphRuntimeState {\n const fitPaddingRef = { current: 50 };\n const controller = createGraphController(cy, fitPaddingRef);\n const layoutRunningRef = { current: false };\n const readyRef = { current: false };\n const renderedNodeListeners = new Set<RenderedNodeListener>();\n const stateBase = {\n callbacksRef,\n controller,\n cy,\n fitPaddingRef,\n generationRef: { current: 0 },\n hoveredNodeIds: new Set<string>(),\n latestUpdateRef: { current: null as GraphRuntimeUpdate | null },\n layoutRef: { current: null as ReturnType<typeof runGraphLayout> | null },\n layoutRunningRef,\n nodeSizes: new Map<string, GraphViewSize>(),\n readyRef,\n relayoutScheduledRef: { current: false },\n renderedNodeListeners,\n };\n const unbindEvents = bindGraphEvents(cy, callbacksRef, controller, () =>\n emitRenderedNodes(stateBase),\n );\n const resizeObserver = createGraphResizeObserver({\n container,\n cy,\n fitPaddingRef,\n layoutRunningRef,\n onViewportSettled: () => emitRenderedNodes(stateBase),\n readyRef,\n });\n\n return { ...stateBase, resizeObserver, unbindEvents };\n}\n\n/*** Apply one graph update and start exactly one new layout generation. */\nfunction updateRuntime(state: GraphRuntimeState, input: GraphRuntimeUpdate) {\n if (state.cy.destroyed()) return;\n stopCurrentLayout(state);\n state.latestUpdateRef.current = input;\n state.fitPaddingRef.current = input.fitPadding ?? 50;\n state.cy.minZoom(input.minZoom ?? 0.05);\n state.cy.maxZoom(input.maxZoom ?? 2);\n syncGraphElements(state.cy, input.nodes, input.edges);\n applyGraphStyles(state.cy, input.styleRules, input.richNodeRendering);\n applyKnownNodeSizes(state);\n startCurrentLayout(state, input);\n}\n\n/*** Start a generation-safe layout whose completion is the only normal automatic fit trigger. */\nfunction startCurrentLayout(state: GraphRuntimeState, input: GraphRuntimeUpdate) {\n const generation = state.generationRef.current + 1;\n state.generationRef.current = generation;\n state.layoutRunningRef.current = true;\n state.cy.resize();\n state.layoutRef.current = runGraphLayout(\n state.cy,\n {\n layout: input.layout ?? 'concentric',\n layoutOptions: input.layoutOptions,\n spacingFactor: input.spacingFactor ?? 1,\n },\n () => completeCurrentLayout(state, generation),\n );\n}\n\n/*** Settle only the latest layout generation and fit once from node bounds. */\nfunction completeCurrentLayout(state: GraphRuntimeState, generation: number) {\n state.layoutRef.current = null;\n scheduleGraphFrame(() => {\n if (state.cy.destroyed() || generation !== state.generationRef.current) return;\n state.cy.resize();\n state.layoutRunningRef.current = false;\n if (!hasUsableViewport(state.cy)) return;\n fitGraphViewport(state.cy, { padding: state.fitPaddingRef.current });\n emitRenderedNodes(state);\n\n if (!state.readyRef.current) {\n state.readyRef.current = true;\n state.callbacksRef.current.onReady?.(state.controller);\n }\n state.callbacksRef.current.onLayoutComplete?.(state.controller);\n });\n}\n\n/*** Stop the current layout and invalidate every stale completion callback. */\nfunction stopCurrentLayout(state: GraphRuntimeState) {\n state.generationRef.current += 1;\n state.layoutRunningRef.current = false;\n state.layoutRef.current?.stop();\n state.layoutRef.current = null;\n}\n\n/*** Apply consumer styles and hide native node paint only when React rich nodes are active. */\nfunction applyGraphStyles(\n cy: Core,\n rules: readonly GraphViewStyleRule[] | undefined,\n richNodeRendering: boolean,\n) {\n const styles = [...(rules ?? [])];\n if (richNodeRendering) {\n styles.push({\n selector: 'node',\n style: {\n 'background-opacity': 0,\n 'border-width': 0,\n label: '',\n 'overlay-opacity': 0,\n },\n });\n }\n if (styles.length > 0) cy.style(styles).update();\n}\n\n/*** Synchronize controlled selection without rerunning layout. */\nfunction setSelectedNodeIds(state: GraphRuntimeState, nodeIds: readonly string[] | undefined) {\n if (state.cy.destroyed() || nodeIds === undefined) return;\n const selectedIds = new Set(nodeIds);\n state.cy.nodes().forEach((node) => {\n if (selectedIds.has(node.id())) node.select();\n else node.unselect();\n });\n emitRenderedNodes(state);\n}\n\n/*** Persist one rich-node size and schedule at most one relayout for changed geometry. */\nfunction setNodeSize(state: GraphRuntimeState, id: string, size: GraphViewSize) {\n const previous = state.nodeSizes.get(id);\n if (previous?.width === size.width && previous.height === size.height) return;\n state.nodeSizes.set(id, size);\n applyNodeSize(state.cy, id, size);\n scheduleMeasuredNodeRelayout(state);\n}\n\n/*** Apply every known rich-node size after Cytoscape elements are replaced. */\nfunction applyKnownNodeSizes(state: GraphRuntimeState) {\n for (const [id, size] of state.nodeSizes) applyNodeSize(state.cy, id, size);\n}\n\n/*** Apply one measured model-space size as a Cytoscape per-node style bypass. */\nfunction applyNodeSize(cy: Core, id: string, size: GraphViewSize) {\n const node = cy.getElementById(id);\n if (node.empty()) return;\n node.style({ height: size.height, width: size.width });\n}\n\n/*** Coalesce DOM measurements into one follow-up layout generation. */\nfunction scheduleMeasuredNodeRelayout(state: GraphRuntimeState) {\n if (state.relayoutScheduledRef.current) return;\n state.relayoutScheduledRef.current = true;\n scheduleGraphFrame(() => {\n state.relayoutScheduledRef.current = false;\n const input = state.latestUpdateRef.current;\n if (input === null || state.cy.destroyed()) return;\n stopCurrentLayout(state);\n startCurrentLayout(state, input);\n });\n}\n\n/*** Subscribe one React overlay to rendered node state without exposing the Cytoscape core. */\nfunction subscribeRenderedNodes(state: GraphRuntimeState, listener: RenderedNodeListener) {\n state.renderedNodeListeners.add(listener);\n listener(readGraphRenderedNodes(state.cy, state.hoveredNodeIds));\n return () => {\n state.renderedNodeListeners.delete(listener);\n };\n}\n\n/*** Publish current rendered node positions only to active rich-node subscribers. */\nfunction emitRenderedNodes(\n state: Pick<GraphRuntimeState, 'cy' | 'hoveredNodeIds' | 'renderedNodeListeners'>,\n) {\n if (state.renderedNodeListeners.size === 0) return;\n const nodes = readGraphRenderedNodes(state.cy, state.hoveredNodeIds);\n for (const listener of state.renderedNodeListeners) listener(nodes);\n}\n\n/*** Translate one rich-overlay event back into normal graph selection and callbacks. */\nfunction handleOverlayNodeEvent(\n state: GraphRuntimeState,\n id: string,\n type: GraphViewElementEventType,\n) {\n if (type === 'pointer-enter') state.hoveredNodeIds.add(id);\n if (type === 'pointer-leave') state.hoveredNodeIds.delete(id);\n if (type === 'press') state.cy.getElementById(id).select();\n state.callbacksRef.current.onNodeEvent?.({ id, type });\n emitRenderedNodes(state);\n}\n\n/*** Return whether Cytoscape currently has dimensions suitable for fitting. */\nfunction hasUsableViewport(cy: Core): boolean {\n return cy.width() > 0 && cy.height() > 0;\n}\n\n/*** Dispose all runtime resources in the reverse order they were acquired. */\nfunction destroyRuntime(state: GraphRuntimeState) {\n state.resizeObserver?.disconnect();\n stopCurrentLayout(state);\n state.unbindEvents();\n state.renderedNodeListeners.clear();\n if (!state.cy.destroyed()) state.cy.destroy();\n}\n"]}
|
package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { ElementDefinition } from 'cytoscape';
|
|
2
|
+
import type { GraphViewEdge, GraphViewNode } from './GraphView';
|
|
3
|
+
/*** Convert plain graph data into renderable Cytoscape elements without impossible compound edges. */
|
|
4
|
+
export declare function createRenderableGraphElements(nodes: readonly GraphViewNode[], edges: readonly GraphViewEdge[]): ElementDefinition[];
|
|
5
|
+
//# sourceMappingURL=createRenderableGraphElements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRenderableGraphElements.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAEnD,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhE,sGAAsG;AACtG,wBAAgB,6BAA6B,CAC3C,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,KAAK,EAAE,SAAS,aAAa,EAAE,GAC9B,iBAAiB,EAAE,CAoBrB"}
|
package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/*** Convert plain graph data into renderable Cytoscape elements without impossible compound edges. */
|
|
2
|
+
export function createRenderableGraphElements(nodes, edges) {
|
|
3
|
+
const nodeIds = new Set(nodes.map((node) => node.id));
|
|
4
|
+
const parentById = new Map(nodes.flatMap((node) => (node.parentId ? [[node.id, node.parentId]] : [])));
|
|
5
|
+
const nodeElements = nodes.map((node) => ({
|
|
6
|
+
group: 'nodes',
|
|
7
|
+
classes: node.classes,
|
|
8
|
+
data: {
|
|
9
|
+
...(node.data ?? {}),
|
|
10
|
+
id: node.id,
|
|
11
|
+
label: node.label ?? node.id,
|
|
12
|
+
...(node.parentId ? { parent: node.parentId } : {}),
|
|
13
|
+
},
|
|
14
|
+
}));
|
|
15
|
+
const edgeElements = edges.flatMap((edge, index) => createEdgeElement(edge, index, nodeIds, parentById));
|
|
16
|
+
return [...nodeElements, ...edgeElements];
|
|
17
|
+
}
|
|
18
|
+
/*** Build one edge unless one endpoint owns the other in the compound hierarchy. */
|
|
19
|
+
function createEdgeElement(edge, index, nodeIds, parentById) {
|
|
20
|
+
if (!nodeIds.has(edge.source) || !nodeIds.has(edge.target))
|
|
21
|
+
return [];
|
|
22
|
+
if (isAncestor(edge.source, edge.target, parentById))
|
|
23
|
+
return [];
|
|
24
|
+
if (isAncestor(edge.target, edge.source, parentById))
|
|
25
|
+
return [];
|
|
26
|
+
return [
|
|
27
|
+
{
|
|
28
|
+
group: 'edges',
|
|
29
|
+
classes: edge.classes,
|
|
30
|
+
data: {
|
|
31
|
+
...(edge.data ?? {}),
|
|
32
|
+
id: edge.id ?? `${edge.source}->${edge.target}:${index}`,
|
|
33
|
+
source: edge.source,
|
|
34
|
+
target: edge.target,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
}
|
|
39
|
+
/*** Return whether a node appears in another node's compound parent chain. */
|
|
40
|
+
function isAncestor(possibleAncestor, nodeId, parentById) {
|
|
41
|
+
const visited = new Set();
|
|
42
|
+
let current = parentById.get(nodeId);
|
|
43
|
+
while (current !== undefined && !visited.has(current)) {
|
|
44
|
+
if (current === possibleAncestor)
|
|
45
|
+
return true;
|
|
46
|
+
visited.add(current);
|
|
47
|
+
current = parentById.get(current);
|
|
48
|
+
}
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=createRenderableGraphElements.js.map
|
package/dist/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"createRenderableGraphElements.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/createRenderableGraphElements.ts"],"names":[],"mappings":"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","sourcesContent":["import type { ElementDefinition } from 'cytoscape';\n\nimport type { GraphViewEdge, GraphViewNode } from './GraphView';\n\n/*** Convert plain graph data into renderable Cytoscape elements without impossible compound edges. */\nexport function createRenderableGraphElements(\n nodes: readonly GraphViewNode[],\n edges: readonly GraphViewEdge[],\n): ElementDefinition[] {\n const nodeIds = new Set(nodes.map((node) => node.id));\n const parentById = new Map(\n nodes.flatMap((node) => (node.parentId ? [[node.id, node.parentId] as const] : [])),\n );\n const nodeElements: ElementDefinition[] = nodes.map((node) => ({\n group: 'nodes',\n classes: node.classes,\n data: {\n ...(node.data ?? {}),\n id: node.id,\n label: node.label ?? node.id,\n ...(node.parentId ? { parent: node.parentId } : {}),\n },\n }));\n const edgeElements = edges.flatMap((edge, index) =>\n createEdgeElement(edge, index, nodeIds, parentById),\n );\n\n return [...nodeElements, ...edgeElements];\n}\n\n/*** Build one edge unless one endpoint owns the other in the compound hierarchy. */\nfunction createEdgeElement(\n edge: GraphViewEdge,\n index: number,\n nodeIds: ReadonlySet<string>,\n parentById: ReadonlyMap<string, string>,\n): ElementDefinition[] {\n if (!nodeIds.has(edge.source) || !nodeIds.has(edge.target)) return [];\n if (isAncestor(edge.source, edge.target, parentById)) return [];\n if (isAncestor(edge.target, edge.source, parentById)) return [];\n\n return [\n {\n group: 'edges',\n classes: edge.classes,\n data: {\n ...(edge.data ?? {}),\n id: edge.id ?? `${edge.source}->${edge.target}:${index}`,\n source: edge.source,\n target: edge.target,\n },\n },\n ];\n}\n\n/*** Return whether a node appears in another node's compound parent chain. */\nfunction isAncestor(\n possibleAncestor: string,\n nodeId: string,\n parentById: ReadonlyMap<string, string>,\n): boolean {\n const visited = new Set<string>();\n let current = parentById.get(nodeId);\n\n while (current !== undefined && !visited.has(current)) {\n if (current === possibleAncestor) return true;\n visited.add(current);\n current = parentById.get(current);\n }\n\n return false;\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Core } from 'cytoscape';
|
|
2
|
+
import type { GraphViewFitOptions } from './GraphView';
|
|
3
|
+
/*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */
|
|
4
|
+
export declare function fitGraphViewport(cy: Core, options?: GraphViewFitOptions): boolean;
|
|
5
|
+
//# sourceMappingURL=fitGraphViewport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fitGraphViewport.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,2FAA2F;AAC3F,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,IAAI,EAAE,OAAO,GAAE,mBAAwB,GAAG,OAAO,CAOrF"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */
|
|
2
|
+
export function fitGraphViewport(cy, options = {}) {
|
|
3
|
+
if (cy.destroyed())
|
|
4
|
+
return false;
|
|
5
|
+
const nodes = getFitNodes(cy, options.nodeIds);
|
|
6
|
+
if (nodes.empty())
|
|
7
|
+
return false;
|
|
8
|
+
cy.fit(nodes, options.padding ?? 50);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
/*** Resolve an optional node-id target without ever adding edges to the fit collection. */
|
|
12
|
+
function getFitNodes(cy, nodeIds) {
|
|
13
|
+
if (nodeIds === undefined || nodeIds.length === 0)
|
|
14
|
+
return cy.nodes();
|
|
15
|
+
const requestedIds = new Set(nodeIds);
|
|
16
|
+
return cy.nodes().filter((node) => requestedIds.has(node.id()));
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=fitGraphViewport.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fitGraphViewport.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/fitGraphViewport.ts"],"names":[],"mappings":"AAIA,2FAA2F;AAC3F,MAAM,UAAU,gBAAgB,CAAC,EAAQ,EAAE,UAA+B,EAAE;IAC1E,IAAI,EAAE,CAAC,SAAS,EAAE;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,KAAK,GAAG,WAAW,CAAC,EAAE,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC;IAC/C,IAAI,KAAK,CAAC,KAAK,EAAE;QAAE,OAAO,KAAK,CAAC;IAEhC,EAAE,CAAC,GAAG,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;IACrC,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2FAA2F;AAC3F,SAAS,WAAW,CAAC,EAAQ,EAAE,OAAsC;IACnE,IAAI,OAAO,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC;IACrE,MAAM,YAAY,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACtC,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,YAAY,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC;AAClE,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type { GraphViewFitOptions } from './GraphView';\n\n/*** Fit the requested visible nodes and never include edge geometry in viewport bounds. */\nexport function fitGraphViewport(cy: Core, options: GraphViewFitOptions = {}): boolean {\n if (cy.destroyed()) return false;\n const nodes = getFitNodes(cy, options.nodeIds);\n if (nodes.empty()) return false;\n\n cy.fit(nodes, options.padding ?? 50);\n return true;\n}\n\n/*** Resolve an optional node-id target without ever adding edges to the fit collection. */\nfunction getFitNodes(cy: Core, nodeIds: readonly string[] | undefined) {\n if (nodeIds === undefined || nodeIds.length === 0) return cy.nodes();\n const requestedIds = new Set(nodeIds);\n return cy.nodes().filter((node) => requestedIds.has(node.id()));\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Core } from 'cytoscape';
|
|
2
|
+
import type { GraphViewRenderedNode } from './GraphView';
|
|
3
|
+
/*** Read React-overlay node state from Cytoscape's authoritative rendered positions. */
|
|
4
|
+
export declare function readGraphRenderedNodes(cy: Core, hoveredNodeIds: ReadonlySet<string>): readonly GraphViewRenderedNode[];
|
|
5
|
+
//# sourceMappingURL=readGraphRenderedNodes.d.ts.map
|
package/dist/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.d.ts.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readGraphRenderedNodes.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,wFAAwF;AACxF,wBAAgB,sBAAsB,CACpC,EAAE,EAAE,IAAI,EACR,cAAc,EAAE,WAAW,CAAC,MAAM,CAAC,GAClC,SAAS,qBAAqB,EAAE,CAWlC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/*** Read React-overlay node state from Cytoscape's authoritative rendered positions. */
|
|
2
|
+
export function readGraphRenderedNodes(cy, hoveredNodeIds) {
|
|
3
|
+
if (cy.destroyed())
|
|
4
|
+
return [];
|
|
5
|
+
const zoom = cy.zoom();
|
|
6
|
+
return cy.nodes().map((node) => ({
|
|
7
|
+
hovered: hoveredNodeIds.has(node.id()),
|
|
8
|
+
id: node.id(),
|
|
9
|
+
position: node.renderedPosition(),
|
|
10
|
+
selected: node.selected(),
|
|
11
|
+
zoom,
|
|
12
|
+
}));
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=readGraphRenderedNodes.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"readGraphRenderedNodes.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/readGraphRenderedNodes.ts"],"names":[],"mappings":"AAIA,wFAAwF;AACxF,MAAM,UAAU,sBAAsB,CACpC,EAAQ,EACR,cAAmC;IAEnC,IAAI,EAAE,CAAC,SAAS,EAAE;QAAE,OAAO,EAAE,CAAC;IAC9B,MAAM,IAAI,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC;IAEvB,OAAO,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC/B,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACtC,EAAE,EAAE,IAAI,CAAC,EAAE,EAAE;QACb,QAAQ,EAAE,IAAI,CAAC,gBAAgB,EAAE;QACjC,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE;QACzB,IAAI;KACL,CAAC,CAAC,CAAC;AACN,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport type { GraphViewRenderedNode } from './GraphView';\n\n/*** Read React-overlay node state from Cytoscape's authoritative rendered positions. */\nexport function readGraphRenderedNodes(\n cy: Core,\n hoveredNodeIds: ReadonlySet<string>,\n): readonly GraphViewRenderedNode[] {\n if (cy.destroyed()) return [];\n const zoom = cy.zoom();\n\n return cy.nodes().map((node) => ({\n hovered: hoveredNodeIds.has(node.id()),\n id: node.id(),\n position: node.renderedPosition(),\n selected: node.selected(),\n zoom,\n }));\n}\n"]}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { Core } from 'cytoscape';
|
|
2
|
+
import type { GraphViewLayoutName } from './GraphView';
|
|
3
|
+
interface RunGraphLayoutInput {
|
|
4
|
+
readonly layout: GraphViewLayoutName;
|
|
5
|
+
readonly layoutOptions?: Readonly<Record<string, unknown>>;
|
|
6
|
+
readonly spacingFactor: number;
|
|
7
|
+
}
|
|
8
|
+
interface GraphLayoutSession {
|
|
9
|
+
stop(): void;
|
|
10
|
+
}
|
|
11
|
+
/*** Run one Cytoscape layout with automatic fitting disabled and explicit stop ownership. */
|
|
12
|
+
export declare function runGraphLayout(cy: Core, input: RunGraphLayoutInput, onComplete: () => void): GraphLayoutSession;
|
|
13
|
+
export {};
|
|
14
|
+
//# sourceMappingURL=runGraphLayout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runGraphLayout.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAA0B,MAAM,WAAW,CAAC;AAE9D,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAEvD,UAAU,mBAAmB;IAC3B,QAAQ,CAAC,MAAM,EAAE,mBAAmB,CAAC;IACrC,QAAQ,CAAC,aAAa,CAAC,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IAC3D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,UAAU,kBAAkB;IAC1B,IAAI,IAAI,IAAI,CAAC;CACd;AAED,6FAA6F;AAC7F,wBAAgB,cAAc,CAC5B,EAAE,EAAE,IAAI,EACR,KAAK,EAAE,mBAAmB,EAC1B,UAAU,EAAE,MAAM,IAAI,GACrB,kBAAkB,CAWpB"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*** Run one Cytoscape layout with automatic fitting disabled and explicit stop ownership. */
|
|
2
|
+
export function runGraphLayout(cy, input, onComplete) {
|
|
3
|
+
const layout = cy.layout(createLayoutOptions(input));
|
|
4
|
+
const handleStop = () => onComplete();
|
|
5
|
+
cy.one('layoutstop', handleStop);
|
|
6
|
+
layout.run();
|
|
7
|
+
return {
|
|
8
|
+
stop() {
|
|
9
|
+
stopGraphLayout(cy, layout, handleStop);
|
|
10
|
+
},
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/*** Build layout options while reserving fit ownership for the graph runtime. */
|
|
14
|
+
function createLayoutOptions(input) {
|
|
15
|
+
return {
|
|
16
|
+
...(input.layoutOptions ?? {}),
|
|
17
|
+
name: input.layout,
|
|
18
|
+
spacingFactor: input.spacingFactor,
|
|
19
|
+
nodeDimensionsIncludeLabels: true,
|
|
20
|
+
fit: false,
|
|
21
|
+
animate: false,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
/*** Stop one layout without allowing its stale layout-stop callback to escape. */
|
|
25
|
+
function stopGraphLayout(cy, layout, handleStop) {
|
|
26
|
+
cy.off('layoutstop', handleStop);
|
|
27
|
+
try {
|
|
28
|
+
layout.stop();
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Cytoscape may already have disposed a superseded layout.
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=runGraphLayout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runGraphLayout.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/runGraphLayout.ts"],"names":[],"mappings":"AAcA,6FAA6F;AAC7F,MAAM,UAAU,cAAc,CAC5B,EAAQ,EACR,KAA0B,EAC1B,UAAsB;IAEtB,MAAM,MAAM,GAAG,EAAE,CAAC,MAAM,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;IACrD,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,UAAU,EAAE,CAAC;IACtC,EAAE,CAAC,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACjC,MAAM,CAAC,GAAG,EAAE,CAAC;IAEb,OAAO;QACL,IAAI;YACF,eAAe,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;QAC1C,CAAC;KACF,CAAC;AACJ,CAAC;AAED,iFAAiF;AACjF,SAAS,mBAAmB,CAAC,KAA0B;IACrD,OAAO;QACL,GAAG,CAAC,KAAK,CAAC,aAAa,IAAI,EAAE,CAAC;QAC9B,IAAI,EAAE,KAAK,CAAC,MAAM;QAClB,aAAa,EAAE,KAAK,CAAC,aAAa;QAClC,2BAA2B,EAAE,IAAI;QACjC,GAAG,EAAE,KAAK;QACV,OAAO,EAAE,KAAK;KACf,CAAC;AACJ,CAAC;AAED,kFAAkF;AAClF,SAAS,eAAe,CAAC,EAAQ,EAAE,MAAe,EAAE,UAAsB;IACxE,EAAE,CAAC,GAAG,CAAC,YAAY,EAAE,UAAU,CAAC,CAAC;IACjC,IAAI,CAAC;QACH,MAAM,CAAC,IAAI,EAAE,CAAC;IAChB,CAAC;IAAC,MAAM,CAAC;QACP,2DAA2D;IAC7D,CAAC;AACH,CAAC","sourcesContent":["import type { Core, LayoutOptions, Layouts } from 'cytoscape';\n\nimport type { GraphViewLayoutName } from './GraphView';\n\ninterface RunGraphLayoutInput {\n readonly layout: GraphViewLayoutName;\n readonly layoutOptions?: Readonly<Record<string, unknown>>;\n readonly spacingFactor: number;\n}\n\ninterface GraphLayoutSession {\n stop(): void;\n}\n\n/*** Run one Cytoscape layout with automatic fitting disabled and explicit stop ownership. */\nexport function runGraphLayout(\n cy: Core,\n input: RunGraphLayoutInput,\n onComplete: () => void,\n): GraphLayoutSession {\n const layout = cy.layout(createLayoutOptions(input));\n const handleStop = () => onComplete();\n cy.one('layoutstop', handleStop);\n layout.run();\n\n return {\n stop() {\n stopGraphLayout(cy, layout, handleStop);\n },\n };\n}\n\n/*** Build layout options while reserving fit ownership for the graph runtime. */\nfunction createLayoutOptions(input: RunGraphLayoutInput): LayoutOptions {\n return {\n ...(input.layoutOptions ?? {}),\n name: input.layout,\n spacingFactor: input.spacingFactor,\n nodeDimensionsIncludeLabels: true,\n fit: false,\n animate: false,\n };\n}\n\n/*** Stop one layout without allowing its stale layout-stop callback to escape. */\nfunction stopGraphLayout(cy: Core, layout: Layouts, handleStop: () => void) {\n cy.off('layoutstop', handleStop);\n try {\n layout.stop();\n } catch {\n // Cytoscape may already have disposed a superseded layout.\n }\n}\n"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduleGraphFrame.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,MAAM,IAAI,QAWtD"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*** Schedule one graph-runtime callback after browser paint with a non-browser fallback. */
|
|
2
|
+
export function scheduleGraphFrame(callback) {
|
|
3
|
+
const frame = globalThis.requestAnimationFrame;
|
|
4
|
+
if (frame) {
|
|
5
|
+
frame(callback);
|
|
6
|
+
return;
|
|
7
|
+
}
|
|
8
|
+
setTimeout(callback, 0);
|
|
9
|
+
}
|
|
10
|
+
//# sourceMappingURL=scheduleGraphFrame.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scheduleGraphFrame.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/scheduleGraphFrame.ts"],"names":[],"mappings":"AAAA,4FAA4F;AAC5F,MAAM,UAAU,kBAAkB,CAAC,QAAoB;IACrD,MAAM,KAAK,GACT,UAGD,CAAC,qBAAqB,CAAC;IACxB,IAAI,KAAK,EAAE,CAAC;QACV,KAAK,CAAC,QAAQ,CAAC,CAAC;QAChB,OAAO;IACT,CAAC;IACD,UAAU,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC;AAC1B,CAAC","sourcesContent":["/*** Schedule one graph-runtime callback after browser paint with a non-browser fallback. */\nexport function scheduleGraphFrame(callback: () => void) {\n const frame = (\n globalThis as unknown as {\n requestAnimationFrame?: (scheduled: () => void) => number;\n }\n ).requestAnimationFrame;\n if (frame) {\n frame(callback);\n return;\n }\n setTimeout(callback, 0);\n}\n"]}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Core } from 'cytoscape';
|
|
2
|
+
import type { GraphViewEdge, GraphViewNode } from './GraphView';
|
|
3
|
+
/*** Replace Cytoscape elements from the current plain graph projection in one batch. */
|
|
4
|
+
export declare function syncGraphElements(cy: Core, nodes: readonly GraphViewNode[], edges: readonly GraphViewEdge[]): void;
|
|
5
|
+
//# sourceMappingURL=syncGraphElements.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncGraphElements.d.ts","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,KAAK,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAEhE,wFAAwF;AACxF,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,IAAI,EACR,KAAK,EAAE,SAAS,aAAa,EAAE,EAC/B,KAAK,EAAE,SAAS,aAAa,EAAE,QAShC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { createRenderableGraphElements } from './createRenderableGraphElements';
|
|
2
|
+
/*** Replace Cytoscape elements from the current plain graph projection in one batch. */
|
|
3
|
+
export function syncGraphElements(cy, nodes, edges) {
|
|
4
|
+
if (cy.destroyed())
|
|
5
|
+
return;
|
|
6
|
+
const elements = createRenderableGraphElements(nodes, edges);
|
|
7
|
+
cy.batch(() => {
|
|
8
|
+
cy.elements().remove();
|
|
9
|
+
cy.add(elements);
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=syncGraphElements.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"syncGraphElements.js","sourceRoot":"","sources":["../../../../../../src/features/graph-view/adapters/inbound/web-artifact/syncGraphElements.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,6BAA6B,EAAE,MAAM,iCAAiC,CAAC;AAGhF,wFAAwF;AACxF,MAAM,UAAU,iBAAiB,CAC/B,EAAQ,EACR,KAA+B,EAC/B,KAA+B;IAE/B,IAAI,EAAE,CAAC,SAAS,EAAE;QAAE,OAAO;IAE3B,MAAM,QAAQ,GAAG,6BAA6B,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IAC7D,EAAE,CAAC,KAAK,CAAC,GAAG,EAAE;QACZ,EAAE,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,CAAC;QACvB,EAAE,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { Core } from 'cytoscape';\n\nimport { createRenderableGraphElements } from './createRenderableGraphElements';\nimport type { GraphViewEdge, GraphViewNode } from './GraphView';\n\n/*** Replace Cytoscape elements from the current plain graph projection in one batch. */\nexport function syncGraphElements(\n cy: Core,\n nodes: readonly GraphViewNode[],\n edges: readonly GraphViewEdge[],\n) {\n if (cy.destroyed()) return;\n\n const elements = createRenderableGraphElements(nodes, edges);\n cy.batch(() => {\n cy.elements().remove();\n cy.add(elements);\n });\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"materializeWebComponentArtifactAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,MAAM,WAAW,oCAAoC;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qCAAqC;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;CAC1B;AAED,8FAA8F;AAC9F,wBAAsB,oCAAoC,CACxD,KAAK,EAAE,oCAAoC,EAC3C,UAAU,EAAE,kCAAkC,GAC7C,OAAO,CAAC,qCAAqC,CAAC,
|
|
1
|
+
{"version":3,"file":"materializeWebComponentArtifactAsync.d.ts","sourceRoot":"","sources":["../../../../../src/features/web-component-artifact/application/use-cases/materializeWebComponentArtifactAsync.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kCAAkC,EAAE,MAAM,sDAAsD,CAAC;AAE/G,MAAM,WAAW,oCAAoC;IACnD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,WAAW,qCAAqC;IACpD,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAC;IACzC,QAAQ,CAAC,eAAe,EAAE,MAAM,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,KAAK,CAAC;CAC1B;AAED,8FAA8F;AAC9F,wBAAsB,oCAAoC,CACxD,KAAK,EAAE,oCAAoC,EAC3C,UAAU,EAAE,kCAAkC,GAC7C,OAAO,CAAC,qCAAqC,CAAC,CA4ChD"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/*** Materialize one replaceable web-only component artifact from the published ZORA build. */
|
|
2
2
|
export async function materializeWebComponentArtifactAsync(input, fileSystem) {
|
|
3
|
-
const
|
|
4
|
-
const
|
|
3
|
+
const sourceDirectory = fileSystem.joinPath(input.packageRoot, 'web-dist', input.component);
|
|
4
|
+
const artifact = await readWebArtifactDefinitionAsync(sourceDirectory, input.component, fileSystem);
|
|
5
5
|
await fileSystem.ensureDirectoryAsync(input.outputDirectory);
|
|
6
6
|
const createdFiles = await Promise.all(artifact.files.map(async (fileName) => {
|
|
7
7
|
const sourcePath = fileSystem.joinPath(sourceDirectory, fileName);
|
|
@@ -25,15 +25,30 @@ export async function materializeWebComponentArtifactAsync(input, fileSystem) {
|
|
|
25
25
|
platform: 'web',
|
|
26
26
|
};
|
|
27
27
|
}
|
|
28
|
-
/***
|
|
29
|
-
function
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
28
|
+
/*** Read one packaged artifact manifest so new web components require no CLI branching. */
|
|
29
|
+
async function readWebArtifactDefinitionAsync(sourceDirectory, component, fileSystem) {
|
|
30
|
+
const manifestPath = fileSystem.joinPath(sourceDirectory, 'artifact.json');
|
|
31
|
+
const manifestSource = await readWebArtifactManifestSourceAsync(manifestPath, component, fileSystem);
|
|
32
|
+
const parsed = JSON.parse(manifestSource);
|
|
33
|
+
if (parsed.component !== component ||
|
|
34
|
+
!Array.isArray(parsed.files) ||
|
|
35
|
+
parsed.files.length === 0 ||
|
|
36
|
+
!parsed.files.every((fileName) => typeof fileName === 'string' && fileName.length > 0)) {
|
|
37
|
+
throw new Error(`Invalid ZORA web artifact manifest: ${component}`);
|
|
38
|
+
}
|
|
39
|
+
return {
|
|
40
|
+
component,
|
|
41
|
+
files: parsed.files,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
/*** Read an artifact manifest and translate missing packages into the CLI-facing error. */
|
|
45
|
+
async function readWebArtifactManifestSourceAsync(manifestPath, component, fileSystem) {
|
|
46
|
+
try {
|
|
47
|
+
return await fileSystem.readTextFileAsync(manifestPath);
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
throw new Error(`Unsupported ZORA web component: ${component}`);
|
|
35
51
|
}
|
|
36
|
-
throw new Error(`Unsupported ZORA web component: ${component}`);
|
|
37
52
|
}
|
|
38
53
|
/*** Add a generated-file marker without changing the compiled artifact semantics. */
|
|
39
54
|
function createGeneratedSource(packageVersion, source) {
|