@principal-ai/subsystems-react 0.21.0 → 0.22.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/graphify/consolidated.d.ts +5 -24
- package/dist/graphify/consolidated.d.ts.map +1 -1
- package/dist/graphify/index.d.ts +1 -1
- package/dist/graphify/index.d.ts.map +1 -1
- package/dist/graphify/index.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/pierre/PierreSnippetView.d.ts.map +1 -1
- package/dist/pierre/PierreSnippetView.js +4 -19
- package/dist/pierre/PierreSnippetView.js.map +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.d.ts.map +1 -1
- package/dist/pierre/PierreWalkthroughCodeView.js +20 -3
- package/dist/pierre/PierreWalkthroughCodeView.js.map +1 -1
- package/dist/pierre/constructColors.d.ts.map +1 -1
- package/dist/pierre/constructColors.js +0 -5
- package/dist/pierre/constructColors.js.map +1 -1
- package/dist/pierre/index.d.ts +1 -1
- package/dist/pierre/index.d.ts.map +1 -1
- package/dist/pierre/index.js +1 -1
- package/dist/pierre/index.js.map +1 -1
- package/dist/pierre/sliceSnippet.d.ts +6 -0
- package/dist/pierre/sliceSnippet.d.ts.map +1 -1
- package/dist/pierre/sliceSnippet.js +25 -0
- package/dist/pierre/sliceSnippet.js.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js +0 -3
- package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
- package/dist/subsystem/formatDeclaration.d.ts.map +1 -1
- package/dist/subsystem/formatDeclaration.js +0 -15
- package/dist/subsystem/formatDeclaration.js.map +1 -1
- package/dist/subsystem/model.d.ts +61 -22
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +186 -63
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts +3 -4
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +5 -6
- package/dist/subsystem/nodes.js.map +1 -1
- package/dist/utils/elkLayout.d.ts +6 -4
- package/dist/utils/elkLayout.d.ts.map +1 -1
- package/dist/utils/elkLayout.js +144 -54
- package/dist/utils/elkLayout.js.map +1 -1
- package/package.json +3 -3
- package/src/graphify/consolidated.ts +4 -26
- package/src/graphify/index.ts +0 -2
- package/src/index.ts +0 -2
- package/src/pierre/PierreSnippetView.tsx +4 -18
- package/src/pierre/PierreWalkthroughCodeView.tsx +39 -3
- package/src/pierre/constructColors.test.ts +1 -2
- package/src/pierre/constructColors.ts +0 -5
- package/src/pierre/index.ts +1 -1
- package/src/pierre/sliceSnippet.test.ts +44 -1
- package/src/pierre/sliceSnippet.ts +28 -0
- package/src/stories/Subsystem/ComponentDeclarationAudit.stories.tsx +0 -31
- package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +2 -46
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +9 -24
- package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +2 -2
- package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +287 -0
- package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +1 -1
- package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +6 -6
- package/src/stories/Subsystem/ComponentGraph/fixtures.ts +0 -3
- package/src/subsystem/formatDeclaration.ts +0 -19
- package/src/subsystem/model.test.ts +190 -9
- package/src/subsystem/model.ts +219 -73
- package/src/subsystem/nodes.tsx +6 -6
- package/src/utils/elkLayout.ts +148 -58
- package/dist/components/ConfigurationSelector.d.ts +0 -35
- package/dist/components/ConfigurationSelector.d.ts.map +0 -1
- package/dist/components/ConfigurationSelector.js +0 -63
- package/dist/components/ConfigurationSelector.js.map +0 -1
- package/dist/components/GraphRenderer.d.ts +0 -295
- package/dist/components/GraphRenderer.d.ts.map +0 -1
- package/dist/components/GraphRenderer.js +0 -2008
- package/dist/components/GraphRenderer.js.map +0 -1
- package/dist/components/MultiCanvasRenderer.d.ts +0 -75
- package/dist/components/MultiCanvasRenderer.d.ts.map +0 -1
- package/dist/components/MultiCanvasRenderer.js +0 -203
- package/dist/components/MultiCanvasRenderer.js.map +0 -1
- package/dist/components/NodeTooltip.d.ts +0 -24
- package/dist/components/NodeTooltip.d.ts.map +0 -1
- package/dist/components/NodeTooltip.js +0 -160
- package/dist/components/NodeTooltip.js.map +0 -1
- package/dist/components/SelectionSidebar.d.ts +0 -18
- package/dist/components/SelectionSidebar.d.ts.map +0 -1
- package/dist/components/SelectionSidebar.js +0 -190
- package/dist/components/SelectionSidebar.js.map +0 -1
- package/dist/components/SequenceDiagramRenderer.d.ts +0 -77
- package/dist/components/SequenceDiagramRenderer.d.ts.map +0 -1
- package/dist/components/SequenceDiagramRenderer.js +0 -604
- package/dist/components/SequenceDiagramRenderer.js.map +0 -1
- package/dist/components/WorkflowSequenceDiagram.d.ts +0 -63
- package/dist/components/WorkflowSequenceDiagram.d.ts.map +0 -1
- package/dist/components/WorkflowSequenceDiagram.js +0 -147
- package/dist/components/WorkflowSequenceDiagram.js.map +0 -1
- package/dist/components/dashboard/DashboardRenderer.d.ts +0 -10
- package/dist/components/dashboard/DashboardRenderer.d.ts.map +0 -1
- package/dist/components/dashboard/DashboardRenderer.js +0 -179
- package/dist/components/dashboard/DashboardRenderer.js.map +0 -1
- package/dist/components/dashboard/MetricPanel.d.ts +0 -10
- package/dist/components/dashboard/MetricPanel.d.ts.map +0 -1
- package/dist/components/dashboard/MetricPanel.js +0 -103
- package/dist/components/dashboard/MetricPanel.js.map +0 -1
- package/dist/components/dashboard/MockDataProvider.d.ts +0 -30
- package/dist/components/dashboard/MockDataProvider.d.ts.map +0 -1
- package/dist/components/dashboard/MockDataProvider.js +0 -270
- package/dist/components/dashboard/MockDataProvider.js.map +0 -1
- package/dist/components/dashboard/components/BarChart.d.ts +0 -10
- package/dist/components/dashboard/components/BarChart.d.ts.map +0 -1
- package/dist/components/dashboard/components/BarChart.js +0 -167
- package/dist/components/dashboard/components/BarChart.js.map +0 -1
- package/dist/components/dashboard/components/LineChart.d.ts +0 -10
- package/dist/components/dashboard/components/LineChart.d.ts.map +0 -1
- package/dist/components/dashboard/components/LineChart.js +0 -141
- package/dist/components/dashboard/components/LineChart.js.map +0 -1
- package/dist/components/dashboard/components/MetricCard.d.ts +0 -9
- package/dist/components/dashboard/components/MetricCard.d.ts.map +0 -1
- package/dist/components/dashboard/components/MetricCard.js +0 -163
- package/dist/components/dashboard/components/MetricCard.js.map +0 -1
- package/dist/components/dashboard/components/SourceLink.d.ts +0 -9
- package/dist/components/dashboard/components/SourceLink.d.ts.map +0 -1
- package/dist/components/dashboard/components/SourceLink.js +0 -39
- package/dist/components/dashboard/components/SourceLink.js.map +0 -1
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts +0 -9
- package/dist/components/dashboard/components/TimeRangeSelector.d.ts.map +0 -1
- package/dist/components/dashboard/components/TimeRangeSelector.js +0 -167
- package/dist/components/dashboard/components/TimeRangeSelector.js.map +0 -1
- package/dist/components/dashboard/components/index.d.ts +0 -6
- package/dist/components/dashboard/components/index.d.ts.map +0 -1
- package/dist/components/dashboard/components/index.js +0 -6
- package/dist/components/dashboard/components/index.js.map +0 -1
- package/dist/components/dashboard/index.d.ts +0 -6
- package/dist/components/dashboard/index.d.ts.map +0 -1
- package/dist/components/dashboard/index.js +0 -8
- package/dist/components/dashboard/index.js.map +0 -1
- package/dist/components/dashboard/types.d.ts +0 -74
- package/dist/components/dashboard/types.d.ts.map +0 -1
- package/dist/components/dashboard/types.js +0 -8
- package/dist/components/dashboard/types.js.map +0 -1
- package/dist/components/state-view/PipelineView.d.ts +0 -14
- package/dist/components/state-view/PipelineView.d.ts.map +0 -1
- package/dist/components/state-view/PipelineView.js +0 -195
- package/dist/components/state-view/PipelineView.js.map +0 -1
- package/dist/components/state-view/index.d.ts +0 -14
- package/dist/components/state-view/index.d.ts.map +0 -1
- package/dist/components/state-view/index.js +0 -12
- package/dist/components/state-view/index.js.map +0 -1
- package/dist/components/state-view/types.d.ts +0 -188
- package/dist/components/state-view/types.d.ts.map +0 -1
- package/dist/components/state-view/types.js +0 -10
- package/dist/components/state-view/types.js.map +0 -1
- package/dist/components/state-view/useStateView.d.ts +0 -32
- package/dist/components/state-view/useStateView.d.ts.map +0 -1
- package/dist/components/state-view/useStateView.js +0 -129
- package/dist/components/state-view/useStateView.js.map +0 -1
- package/dist/contexts/GraphEditContext.d.ts +0 -20
- package/dist/contexts/GraphEditContext.d.ts.map +0 -1
- package/dist/contexts/GraphEditContext.js +0 -8
- package/dist/contexts/GraphEditContext.js.map +0 -1
- package/dist/contexts/TooltipPortalContext.d.ts +0 -8
- package/dist/contexts/TooltipPortalContext.d.ts.map +0 -1
- package/dist/contexts/TooltipPortalContext.js +0 -8
- package/dist/contexts/TooltipPortalContext.js.map +0 -1
- package/dist/edges/CustomEdge.d.ts +0 -24
- package/dist/edges/CustomEdge.d.ts.map +0 -1
- package/dist/edges/CustomEdge.js +0 -222
- package/dist/edges/CustomEdge.js.map +0 -1
- package/dist/edges/GenericEdge.d.ts +0 -18
- package/dist/edges/GenericEdge.d.ts.map +0 -1
- package/dist/edges/GenericEdge.js +0 -10
- package/dist/edges/GenericEdge.js.map +0 -1
- package/dist/graphify/kind.d.ts +0 -23
- package/dist/graphify/kind.d.ts.map +0 -1
- package/dist/graphify/kind.js +0 -77
- package/dist/graphify/kind.js.map +0 -1
- package/dist/hooks/usePathBasedEvents.d.ts +0 -42
- package/dist/hooks/usePathBasedEvents.d.ts.map +0 -1
- package/dist/hooks/usePathBasedEvents.js +0 -119
- package/dist/hooks/usePathBasedEvents.js.map +0 -1
- package/dist/hooks/useSequenceLayout.d.ts +0 -186
- package/dist/hooks/useSequenceLayout.d.ts.map +0 -1
- package/dist/hooks/useSequenceLayout.js +0 -327
- package/dist/hooks/useSequenceLayout.js.map +0 -1
- package/dist/hooks/useUndoRedo.d.ts +0 -80
- package/dist/hooks/useUndoRedo.d.ts.map +0 -1
- package/dist/hooks/useUndoRedo.js +0 -70
- package/dist/hooks/useUndoRedo.js.map +0 -1
- package/dist/nodes/CustomNode.d.ts +0 -28
- package/dist/nodes/CustomNode.d.ts.map +0 -1
- package/dist/nodes/CustomNode.js +0 -820
- package/dist/nodes/CustomNode.js.map +0 -1
- package/dist/nodes/GenericNode.d.ts +0 -20
- package/dist/nodes/GenericNode.d.ts.map +0 -1
- package/dist/nodes/GenericNode.js +0 -20
- package/dist/nodes/GenericNode.js.map +0 -1
- package/dist/nodes/otel/OtelBoundaryNode.d.ts +0 -55
- package/dist/nodes/otel/OtelBoundaryNode.d.ts.map +0 -1
- package/dist/nodes/otel/OtelBoundaryNode.js +0 -81
- package/dist/nodes/otel/OtelBoundaryNode.js.map +0 -1
- package/dist/nodes/otel/OtelEventNode.d.ts +0 -59
- package/dist/nodes/otel/OtelEventNode.d.ts.map +0 -1
- package/dist/nodes/otel/OtelEventNode.js +0 -82
- package/dist/nodes/otel/OtelEventNode.js.map +0 -1
- package/dist/nodes/otel/OtelResourceNode.d.ts +0 -53
- package/dist/nodes/otel/OtelResourceNode.d.ts.map +0 -1
- package/dist/nodes/otel/OtelResourceNode.js +0 -105
- package/dist/nodes/otel/OtelResourceNode.js.map +0 -1
- package/dist/nodes/otel/OtelScopeNode.d.ts +0 -53
- package/dist/nodes/otel/OtelScopeNode.d.ts.map +0 -1
- package/dist/nodes/otel/OtelScopeNode.js +0 -81
- package/dist/nodes/otel/OtelScopeNode.js.map +0 -1
- package/dist/nodes/otel/OtelSpanConventionNode.d.ts +0 -61
- package/dist/nodes/otel/OtelSpanConventionNode.d.ts.map +0 -1
- package/dist/nodes/otel/OtelSpanConventionNode.js +0 -131
- package/dist/nodes/otel/OtelSpanConventionNode.js.map +0 -1
- package/dist/nodes/otel/index.d.ts +0 -17
- package/dist/nodes/otel/index.d.ts.map +0 -1
- package/dist/nodes/otel/index.js +0 -13
- package/dist/nodes/otel/index.js.map +0 -1
- package/dist/nodes/otel/shared/NodeBadges.d.ts +0 -10
- package/dist/nodes/otel/shared/NodeBadges.d.ts.map +0 -1
- package/dist/nodes/otel/shared/NodeBadges.js +0 -178
- package/dist/nodes/otel/shared/NodeBadges.js.map +0 -1
- package/dist/nodes/otel/shared/NodeContent.d.ts +0 -10
- package/dist/nodes/otel/shared/NodeContent.d.ts.map +0 -1
- package/dist/nodes/otel/shared/NodeContent.js +0 -96
- package/dist/nodes/otel/shared/NodeContent.js.map +0 -1
- package/dist/nodes/otel/shared/index.d.ts +0 -8
- package/dist/nodes/otel/shared/index.d.ts.map +0 -1
- package/dist/nodes/otel/shared/index.js +0 -8
- package/dist/nodes/otel/shared/index.js.map +0 -1
- package/dist/nodes/otel/shared/types.d.ts +0 -129
- package/dist/nodes/otel/shared/types.d.ts.map +0 -1
- package/dist/nodes/otel/shared/types.js +0 -5
- package/dist/nodes/otel/shared/types.js.map +0 -1
- package/dist/nodes/otel/shared/useNodeBehavior.d.ts +0 -42
- package/dist/nodes/otel/shared/useNodeBehavior.d.ts.map +0 -1
- package/dist/nodes/otel/shared/useNodeBehavior.js +0 -61
- package/dist/nodes/otel/shared/useNodeBehavior.js.map +0 -1
- package/dist/pierre/PierreThroughlineCodeView.d.ts +0 -22
- package/dist/pierre/PierreThroughlineCodeView.d.ts.map +0 -1
- package/dist/pierre/PierreThroughlineCodeView.js +0 -206
- package/dist/pierre/PierreThroughlineCodeView.js.map +0 -1
- package/dist/subsystem/SubsystemCarousel.d.ts +0 -36
- package/dist/subsystem/SubsystemCarousel.d.ts.map +0 -1
- package/dist/subsystem/SubsystemCarousel.js +0 -163
- package/dist/subsystem/SubsystemCarousel.js.map +0 -1
- package/dist/utils/animationMapping.d.ts +0 -53
- package/dist/utils/animationMapping.d.ts.map +0 -1
- package/dist/utils/animationMapping.js +0 -126
- package/dist/utils/animationMapping.js.map +0 -1
- package/dist/utils/canvasBounds.d.ts +0 -50
- package/dist/utils/canvasBounds.d.ts.map +0 -1
- package/dist/utils/canvasBounds.js +0 -88
- package/dist/utils/canvasBounds.js.map +0 -1
- package/dist/utils/graphConverter.d.ts +0 -18
- package/dist/utils/graphConverter.d.ts.map +0 -1
- package/dist/utils/graphConverter.js +0 -114
- package/dist/utils/graphConverter.js.map +0 -1
- package/dist/utils/iconResolver.d.ts +0 -29
- package/dist/utils/iconResolver.d.ts.map +0 -1
- package/dist/utils/iconResolver.js +0 -30
- package/dist/utils/iconResolver.js.map +0 -1
- package/dist/utils/orientationUtils.d.ts +0 -19
- package/dist/utils/orientationUtils.d.ts.map +0 -1
- package/dist/utils/orientationUtils.js +0 -62
- package/dist/utils/orientationUtils.js.map +0 -1
package/src/subsystem/model.ts
CHANGED
|
@@ -32,7 +32,6 @@ export type SubsystemComponentConstruct =
|
|
|
32
32
|
| 'interface'
|
|
33
33
|
| 'type_alias'
|
|
34
34
|
| 'enum'
|
|
35
|
-
| 'module'
|
|
36
35
|
| 'store'
|
|
37
36
|
| 'external'
|
|
38
37
|
| 'custom_entity';
|
|
@@ -92,9 +91,6 @@ export interface SubsystemDeclToken {
|
|
|
92
91
|
*/
|
|
93
92
|
export type SubsystemRelationType =
|
|
94
93
|
| 'imports'
|
|
95
|
-
| 'imports_from'
|
|
96
|
-
| 're_exports'
|
|
97
|
-
| 'defines'
|
|
98
94
|
| 'extends'
|
|
99
95
|
| 'inherits'
|
|
100
96
|
| 'implements'
|
|
@@ -190,11 +186,21 @@ export interface SubsystemComponent {
|
|
|
190
186
|
/**
|
|
191
187
|
* Runtime process membership — which deployment unit this node is a
|
|
192
188
|
* member of (e.g. `principal-studio/host`, `principal-studio/renderer`). Nodes
|
|
193
|
-
* sharing a `process` are drawn inside one boundary region
|
|
194
|
-
*
|
|
195
|
-
*
|
|
189
|
+
* sharing a `process` are drawn inside one boundary region; nodes without
|
|
190
|
+
* one sit outside every process boundary (external actors, services,
|
|
191
|
+
* libraries). Orthogonal to `module` (source-file frame).
|
|
196
192
|
*/
|
|
197
193
|
process?: string;
|
|
194
|
+
/**
|
|
195
|
+
* Source-module membership — which file/module this export belongs to
|
|
196
|
+
* (e.g. `src/session/transcript.ts`). Nodes sharing a `module` are drawn
|
|
197
|
+
* inside one boundary frame. Prefer this over inventing a module construct:
|
|
198
|
+
* each export keeps its real construct (`function` / `class` / …) and the
|
|
199
|
+
* file reads as a frame. Orthogonal to `process` (runtime deployment).
|
|
200
|
+
* When both are set, the module frame is the component's parent (finer
|
|
201
|
+
* grain); process framing still groups siblings that share a process.
|
|
202
|
+
*/
|
|
203
|
+
module?: string;
|
|
198
204
|
/** A symbol this component exposes / is (the node's identity). */
|
|
199
205
|
symbol?: string;
|
|
200
206
|
/**
|
|
@@ -354,26 +360,20 @@ export function deriveGraphEdges(doc: {
|
|
|
354
360
|
*
|
|
355
361
|
* `symbol` is the source of truth (fully-qualified code identity). The name
|
|
356
362
|
* is the symbol itself:
|
|
357
|
-
* - class/type/
|
|
358
|
-
* - method
|
|
359
|
-
* -
|
|
360
|
-
* a common-sense convention for whole-file modules, not a real TS name
|
|
361
|
-
* - otherwise no symbol → fall back to an existing name
|
|
363
|
+
* - class/type/function/... symbol → symbol (e.g. `SessionReader`)
|
|
364
|
+
* - method `Owner.method` → last segment (e.g. `SessionReader.normalize` → `normalize`)
|
|
365
|
+
* - otherwise no symbol → fall back to an existing name
|
|
362
366
|
*/
|
|
363
367
|
export function deriveNameFromSymbol(
|
|
364
368
|
symbol: string | undefined,
|
|
365
369
|
construct: SubsystemComponentConstruct,
|
|
366
370
|
existingName?: string,
|
|
367
|
-
|
|
371
|
+
_file?: string,
|
|
368
372
|
stereotype?: string,
|
|
369
373
|
): string {
|
|
370
374
|
let name: string | undefined;
|
|
371
375
|
if (symbol && symbol.trim()) {
|
|
372
376
|
name = symbol;
|
|
373
|
-
} else if (construct === 'module' && file) {
|
|
374
|
-
const base = file.split('/').pop() ?? '';
|
|
375
|
-
const clean = base.replace(/\.[^.]+$/, ''); // strip extension
|
|
376
|
-
if (clean) name = clean;
|
|
377
377
|
}
|
|
378
378
|
if (!name) name = existingName ?? 'untitled';
|
|
379
379
|
|
|
@@ -382,7 +382,7 @@ export function deriveNameFromSymbol(
|
|
|
382
382
|
// Executable constructs wear `()`; brace-bodied constructs (interface,
|
|
383
383
|
// type_alias, enum) wear ` {}`. Classes render bare — the construct badge
|
|
384
384
|
// already says "class".
|
|
385
|
-
// Everything else (class, store,
|
|
385
|
+
// Everything else (class, store, external) renders bare.
|
|
386
386
|
if (stereotype === 'component' && !name.startsWith('<')) {
|
|
387
387
|
return `<${name}>`;
|
|
388
388
|
}
|
|
@@ -431,11 +431,13 @@ export function formatPurl(purl: string): string {
|
|
|
431
431
|
export type SubsystemGraphNodeType = 'subsystem-component' | 'subsystem-group';
|
|
432
432
|
|
|
433
433
|
/**
|
|
434
|
-
* One
|
|
435
|
-
* Nodes without
|
|
434
|
+
* One boundary region — either a process (runtime deployment unit) or a
|
|
435
|
+
* module (source file). Nodes without that field sit outside those frames.
|
|
436
436
|
*/
|
|
437
437
|
export interface SubsystemProcessRegion {
|
|
438
|
-
/**
|
|
438
|
+
/** Discriminator — which field produced this region. */
|
|
439
|
+
kind: 'process' | 'module';
|
|
440
|
+
/** The `process` or `module` value (e.g. `principal-studio/host`, `src/a.ts`). */
|
|
439
441
|
key: string;
|
|
440
442
|
/** Display label for the boundary frame. */
|
|
441
443
|
label: string;
|
|
@@ -448,9 +450,21 @@ export function processGroupNodeId(processKey: string): string {
|
|
|
448
450
|
return `process:${processKey}`;
|
|
449
451
|
}
|
|
450
452
|
|
|
453
|
+
/** React Flow id for a module boundary group node. */
|
|
454
|
+
export function moduleGroupNodeId(moduleKey: string): string {
|
|
455
|
+
return `module:${moduleKey}`;
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
/** React Flow id for a boundary group of either kind. */
|
|
459
|
+
export function boundaryGroupNodeId(region: Pick<SubsystemProcessRegion, 'kind' | 'key'>): string {
|
|
460
|
+
return region.kind === 'module'
|
|
461
|
+
? moduleGroupNodeId(region.key)
|
|
462
|
+
: processGroupNodeId(region.key);
|
|
463
|
+
}
|
|
464
|
+
|
|
451
465
|
/**
|
|
452
|
-
* Derive boundary regions
|
|
453
|
-
*
|
|
466
|
+
* Derive process boundary regions — one per distinct non-empty `process`
|
|
467
|
+
* value, in first-appearance order.
|
|
454
468
|
*/
|
|
455
469
|
export function getSubsystemRegions(
|
|
456
470
|
doc: Pick<SubsystemModelDocument, 'components'>,
|
|
@@ -464,12 +478,100 @@ export function getSubsystemRegions(
|
|
|
464
478
|
byProcess.set(p, list);
|
|
465
479
|
}
|
|
466
480
|
return [...byProcess.entries()].map(([key, memberIds]) => ({
|
|
481
|
+
kind: 'process' as const,
|
|
467
482
|
key,
|
|
468
483
|
label: key,
|
|
469
484
|
memberIds,
|
|
470
485
|
}));
|
|
471
486
|
}
|
|
472
487
|
|
|
488
|
+
/**
|
|
489
|
+
* Derive module boundary regions — one per distinct non-empty `module`
|
|
490
|
+
* value, in first-appearance order. Label is the module path (file).
|
|
491
|
+
*/
|
|
492
|
+
export function getSubsystemModuleRegions(
|
|
493
|
+
doc: Pick<SubsystemModelDocument, 'components'>,
|
|
494
|
+
): SubsystemProcessRegion[] {
|
|
495
|
+
const byModule = new Map<string, string[]>();
|
|
496
|
+
for (const c of doc.components) {
|
|
497
|
+
const m = c.module?.trim();
|
|
498
|
+
if (!m) continue;
|
|
499
|
+
const list = byModule.get(m) ?? [];
|
|
500
|
+
list.push(c.id);
|
|
501
|
+
byModule.set(m, list);
|
|
502
|
+
}
|
|
503
|
+
return [...byModule.entries()].map(([key, memberIds]) => ({
|
|
504
|
+
kind: 'module' as const,
|
|
505
|
+
key,
|
|
506
|
+
label: key,
|
|
507
|
+
memberIds,
|
|
508
|
+
}));
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* One compound frame for ELK / React Flow — module frames may nest under a
|
|
513
|
+
* process frame via `parentId`.
|
|
514
|
+
*/
|
|
515
|
+
export interface BoundaryLayoutGroup {
|
|
516
|
+
id: string;
|
|
517
|
+
memberIds: string[];
|
|
518
|
+
/** When set, this group is a child of another boundary group (process). */
|
|
519
|
+
parentId?: string;
|
|
520
|
+
region: SubsystemProcessRegion;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
/**
|
|
524
|
+
* Build the process → module → leaf group tree for layout.
|
|
525
|
+
* Multi-member modules nest under a process when every member shares that
|
|
526
|
+
* process; process ELK children are nested module group ids plus any
|
|
527
|
+
* process members that are not inside a kept module frame.
|
|
528
|
+
*/
|
|
529
|
+
export function buildBoundaryLayoutGroups(
|
|
530
|
+
doc: Pick<SubsystemModelDocument, 'components'>,
|
|
531
|
+
): BoundaryLayoutGroup[] {
|
|
532
|
+
const byId = new Map(doc.components.map((c) => [c.id, c]));
|
|
533
|
+
const modules = getSubsystemModuleRegions(doc).filter((r) => r.memberIds.length >= 2);
|
|
534
|
+
const processes = getSubsystemRegions(doc).filter((r) => r.memberIds.length >= 2);
|
|
535
|
+
const keptProcessKeys = new Set(processes.map((r) => r.key));
|
|
536
|
+
|
|
537
|
+
const moduleGroups: BoundaryLayoutGroup[] = modules.map((r) => {
|
|
538
|
+
const processesOfMembers = new Set<string>();
|
|
539
|
+
for (const id of r.memberIds) {
|
|
540
|
+
const p = byId.get(id)?.process?.trim();
|
|
541
|
+
if (p) processesOfMembers.add(p);
|
|
542
|
+
}
|
|
543
|
+
let parentId: string | undefined;
|
|
544
|
+
if (processesOfMembers.size === 1) {
|
|
545
|
+
const p = [...processesOfMembers][0]!;
|
|
546
|
+
if (keptProcessKeys.has(p)) parentId = processGroupNodeId(p);
|
|
547
|
+
}
|
|
548
|
+
return {
|
|
549
|
+
id: moduleGroupNodeId(r.key),
|
|
550
|
+
memberIds: [...r.memberIds],
|
|
551
|
+
parentId,
|
|
552
|
+
region: r,
|
|
553
|
+
};
|
|
554
|
+
});
|
|
555
|
+
|
|
556
|
+
const processGroups: BoundaryLayoutGroup[] = processes.map((r) => {
|
|
557
|
+
const processId = processGroupNodeId(r.key);
|
|
558
|
+
const nestedModules = moduleGroups.filter((m) => m.parentId === processId);
|
|
559
|
+
const nestedModuleKeys = new Set(nestedModules.map((m) => m.region.key));
|
|
560
|
+
const directLeaves = r.memberIds.filter((id) => {
|
|
561
|
+
const mod = byId.get(id)?.module?.trim();
|
|
562
|
+
if (!mod) return true;
|
|
563
|
+
return !nestedModuleKeys.has(mod);
|
|
564
|
+
});
|
|
565
|
+
return {
|
|
566
|
+
id: processId,
|
|
567
|
+
memberIds: [...nestedModules.map((m) => m.id), ...directLeaves],
|
|
568
|
+
region: r,
|
|
569
|
+
};
|
|
570
|
+
});
|
|
571
|
+
|
|
572
|
+
return [...moduleGroups, ...processGroups];
|
|
573
|
+
}
|
|
574
|
+
|
|
473
575
|
export interface SubsystemGraphNodeData extends Record<string, unknown> {
|
|
474
576
|
component: SubsystemComponent;
|
|
475
577
|
/** Set while a file is open in the drawer: true if this node's component
|
|
@@ -509,9 +611,6 @@ export type SubsystemGraphEdge = Edge<SubsystemGraphEdgeData>;
|
|
|
509
611
|
|
|
510
612
|
export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
|
|
511
613
|
imports: '#0893d2', // blue
|
|
512
|
-
imports_from: '#5aa9e6', // light blue
|
|
513
|
-
re_exports: '#3aa5c9', // cyan-blue
|
|
514
|
-
defines: '#2e86ab', // steel blue
|
|
515
614
|
calls: '#4ec9b0', // teal
|
|
516
615
|
extends: '#b48ead', // purple
|
|
517
616
|
inherits: '#9b6fd0', // purple
|
|
@@ -531,9 +630,6 @@ export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
|
|
|
531
630
|
|
|
532
631
|
export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed' | 'dotted'> = {
|
|
533
632
|
imports: 'solid',
|
|
534
|
-
imports_from: 'solid',
|
|
535
|
-
re_exports: 'solid',
|
|
536
|
-
defines: 'solid',
|
|
537
633
|
calls: 'solid',
|
|
538
634
|
extends: 'dashed',
|
|
539
635
|
inherits: 'dashed',
|
|
@@ -555,9 +651,6 @@ export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed'
|
|
|
555
651
|
* directly verifiable" styling of edge labels. */
|
|
556
652
|
export const MECHANISM_DESCRIPTIONS: [SubsystemEdgeMechanism, string, boolean][] = [
|
|
557
653
|
['imports', 'import statement (code-level dependency)', true],
|
|
558
|
-
['imports_from', 'imported by (reverse dependency)', true],
|
|
559
|
-
['re_exports', 're-exports symbols from', true],
|
|
560
|
-
['defines', 'defines / declares symbol', true],
|
|
561
654
|
['calls', 'function/method call (call graph edge)', true],
|
|
562
655
|
['extends', 'class inheritance', true],
|
|
563
656
|
['inherits', 'class inheritance', true],
|
|
@@ -566,7 +659,7 @@ export const MECHANISM_DESCRIPTIONS: [SubsystemEdgeMechanism, string, boolean][]
|
|
|
566
659
|
['uses', 'general dependency (import, call, or reference)', false],
|
|
567
660
|
['method', 'structural: has method / member', true],
|
|
568
661
|
['references', 'type / symbol reference (not a call)', true],
|
|
569
|
-
['contains', '
|
|
662
|
+
['contains', 'UI composition: shell/view owns a child surface', false],
|
|
570
663
|
['feeds', 'data flow: output feeds into input', false],
|
|
571
664
|
['produces', 'data flow: produces / outputs', false],
|
|
572
665
|
['writes', 'state access: mutates retained state', true],
|
|
@@ -750,23 +843,23 @@ export function nodeMinWidthForBadges(component: {
|
|
|
750
843
|
|
|
751
844
|
/**
|
|
752
845
|
* Convert a subsystem graph document into React Flow nodes. Components that
|
|
753
|
-
* carry a `
|
|
754
|
-
* (`
|
|
755
|
-
*
|
|
756
|
-
*
|
|
846
|
+
* carry a `module` get a `parentId` pointing at their module frame
|
|
847
|
+
* (`module:<path>`); otherwise a `process` stamps `process:<process>`. Module
|
|
848
|
+
* frames may themselves nest under a process frame (stamped on the group node
|
|
849
|
+
* in `buildSubsystemGraph`). Nodes without either stay top-level. The initial
|
|
850
|
+
* grid clusters by `module ?? process ?? purl`; ELK then refines with compound
|
|
851
|
+
* layout.
|
|
757
852
|
*/
|
|
758
853
|
export function convertSubsystemToNodes(
|
|
759
854
|
doc: SubsystemModelDocument,
|
|
760
855
|
opts: { maxNodeWidth?: number } = {},
|
|
761
856
|
): SubsystemGraphNode[] {
|
|
762
857
|
const { maxNodeWidth } = opts;
|
|
763
|
-
//
|
|
764
|
-
//
|
|
765
|
-
// purl is code identity — nodes without a process (external actors,
|
|
766
|
-
// services, libraries) fall back to purl and sit outside every boundary.
|
|
858
|
+
// Cluster for the pre-ELK grid: module (source file) → process (runtime)
|
|
859
|
+
// → purl (package identity).
|
|
767
860
|
const byPkg = new Map<string, SubsystemComponent[]>();
|
|
768
861
|
for (const c of doc.components) {
|
|
769
|
-
const regionKey = c.process ?? c.purl;
|
|
862
|
+
const regionKey = c.module ?? c.process ?? c.purl;
|
|
770
863
|
const list = byPkg.get(regionKey) ?? [];
|
|
771
864
|
list.push(c);
|
|
772
865
|
byPkg.set(regionKey, list);
|
|
@@ -801,11 +894,17 @@ export function convertSubsystemToNodes(
|
|
|
801
894
|
const cssBorder = 4; // 2px border each side
|
|
802
895
|
const rawWidth = Math.max(cssMinWidth, textWidth + cssPadding + cssBorder);
|
|
803
896
|
const nodeWidth = Math.max(cssMinWidth, Math.min(cap, rawWidth));
|
|
897
|
+
const moduleKey = c.module?.trim();
|
|
804
898
|
const processKey = c.process?.trim();
|
|
899
|
+
const parentId = moduleKey
|
|
900
|
+
? moduleGroupNodeId(moduleKey)
|
|
901
|
+
: processKey
|
|
902
|
+
? processGroupNodeId(processKey)
|
|
903
|
+
: undefined;
|
|
805
904
|
nodes.push({
|
|
806
905
|
id: c.id,
|
|
807
906
|
type: 'subsystem-component',
|
|
808
|
-
...(
|
|
907
|
+
...(parentId ? { parentId } : {}),
|
|
809
908
|
position: { x: PAD + col * COL_W, y: cursorY + row * ROW_H },
|
|
810
909
|
width: nodeWidth,
|
|
811
910
|
height: 84,
|
|
@@ -818,20 +917,21 @@ export function convertSubsystemToNodes(
|
|
|
818
917
|
}
|
|
819
918
|
|
|
820
919
|
/**
|
|
821
|
-
* Convert boundary regions into React Flow parent (group) nodes.
|
|
822
|
-
*
|
|
920
|
+
* Convert boundary regions into React Flow parent (group) nodes. Module
|
|
921
|
+
* frames that share a process nest under that process via `parentId`.
|
|
823
922
|
* Positions/sizes are placeholders — ELK compound layout overwrites them.
|
|
824
923
|
*/
|
|
825
924
|
export function convertSubsystemToGroups(
|
|
826
925
|
doc: Pick<SubsystemModelDocument, 'components'>,
|
|
827
926
|
): SubsystemGraphNode[] {
|
|
828
|
-
return
|
|
829
|
-
id:
|
|
830
|
-
type: 'subsystem-group',
|
|
927
|
+
return buildBoundaryLayoutGroups(doc).map((g) => ({
|
|
928
|
+
id: g.id,
|
|
929
|
+
type: 'subsystem-group' as const,
|
|
831
930
|
position: { x: 0, y: 0 },
|
|
832
931
|
width: 400,
|
|
833
932
|
height: 300,
|
|
834
|
-
|
|
933
|
+
...(g.parentId ? { parentId: g.parentId } : {}),
|
|
934
|
+
data: { region: g.region },
|
|
835
935
|
}));
|
|
836
936
|
}
|
|
837
937
|
|
|
@@ -874,8 +974,8 @@ export function subsystemGraphLayoutKey(
|
|
|
874
974
|
doc: Pick<SubsystemModelDocument, 'components' | 'relations' | 'walkthroughs'>,
|
|
875
975
|
): string {
|
|
876
976
|
const components = doc.components
|
|
877
|
-
.map(({ id, purl, name, symbol, construct, file, purpose, process }) =>
|
|
878
|
-
[id, purl, name, symbol ?? '', construct, file, purpose ?? '', process ?? ''].join('\0'))
|
|
977
|
+
.map(({ id, purl, name, symbol, construct, file, purpose, process, module }) =>
|
|
978
|
+
[id, purl, name, symbol ?? '', construct, file, purpose ?? '', process ?? '', module ?? ''].join('\0'))
|
|
879
979
|
.sort()
|
|
880
980
|
.join('\n');
|
|
881
981
|
const edgeKey = deriveGraphEdges(doc)
|
|
@@ -901,15 +1001,25 @@ export async function buildSubsystemGraph(
|
|
|
901
1001
|
const { maxNodeWidth, showEdgeLabels, measuredWidths, measuredHeights } = opts;
|
|
902
1002
|
const nodes = convertSubsystemToNodes(doc, { maxNodeWidth });
|
|
903
1003
|
const edges = convertSubsystemToEdges(doc);
|
|
904
|
-
//
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
const
|
|
908
|
-
const
|
|
1004
|
+
// Nested boundary tree: process → module → leaves (when both fields set).
|
|
1005
|
+
const layoutGroups = buildBoundaryLayoutGroups(doc);
|
|
1006
|
+
const regions = layoutGroups.map((g) => g.region);
|
|
1007
|
+
const regionIds = new Set(layoutGroups.map((g) => g.id));
|
|
1008
|
+
const byId = new Map(doc.components.map((c) => [c.id, c]));
|
|
1009
|
+
|
|
1010
|
+
// Drop leaf parentIds that point at singleton / missing frames; fall back
|
|
1011
|
+
// from a dropped module frame to a kept process frame when possible.
|
|
909
1012
|
for (const n of nodes) {
|
|
910
1013
|
if (n.type !== 'subsystem-component') continue;
|
|
911
|
-
const
|
|
912
|
-
if (
|
|
1014
|
+
const parentId = (n as { parentId?: string }).parentId;
|
|
1015
|
+
if (!parentId) continue;
|
|
1016
|
+
if (regionIds.has(parentId)) continue;
|
|
1017
|
+
const comp = (n.data as SubsystemGraphNodeData).component;
|
|
1018
|
+
const processKey = comp?.process?.trim() || byId.get(n.id)?.process?.trim();
|
|
1019
|
+
const processId = processKey ? processGroupNodeId(processKey) : undefined;
|
|
1020
|
+
if (processId && regionIds.has(processId)) {
|
|
1021
|
+
(n as { parentId?: string }).parentId = processId;
|
|
1022
|
+
} else {
|
|
913
1023
|
delete (n as { parentId?: string }).parentId;
|
|
914
1024
|
}
|
|
915
1025
|
}
|
|
@@ -961,8 +1071,7 @@ export async function buildSubsystemGraph(
|
|
|
961
1071
|
}
|
|
962
1072
|
}
|
|
963
1073
|
|
|
964
|
-
// ELK auto-layout:
|
|
965
|
-
// process partitions as compound parents so boundaries shape the layout.
|
|
1074
|
+
// ELK auto-layout: nested compound parents (process → module → leaves).
|
|
966
1075
|
let placedNodes = nodes;
|
|
967
1076
|
let labelPositions = new Map<string, { x: number; y: number }>();
|
|
968
1077
|
let elkPathStrings = new Map<string, string>();
|
|
@@ -978,32 +1087,69 @@ export async function buildSubsystemGraph(
|
|
|
978
1087
|
interLayerSpacing: 120,
|
|
979
1088
|
preserveNodePositions: false,
|
|
980
1089
|
edgeLabels: showEdgeLabels === false ? { enabled: false } : { enabled: true, placement: 'CENTER' },
|
|
981
|
-
groups:
|
|
982
|
-
id:
|
|
983
|
-
memberIds:
|
|
1090
|
+
groups: layoutGroups.map((g) => ({
|
|
1091
|
+
id: g.id,
|
|
1092
|
+
memberIds: g.memberIds,
|
|
1093
|
+
parentId: g.parentId,
|
|
984
1094
|
})),
|
|
985
1095
|
});
|
|
986
|
-
const
|
|
987
|
-
|
|
988
|
-
|
|
1096
|
+
const builtGroupIds = new Set(result.groupBounds.keys());
|
|
1097
|
+
// Parents before children — process frames, then nested module frames.
|
|
1098
|
+
const processGroupNodes: SubsystemGraphNode[] = [];
|
|
1099
|
+
const moduleGroupNodes: SubsystemGraphNode[] = [];
|
|
1100
|
+
for (const g of layoutGroups) {
|
|
1101
|
+
const bounds = result.groupBounds.get(g.id);
|
|
1102
|
+
if (!bounds) continue;
|
|
1103
|
+
const parentId =
|
|
1104
|
+
g.parentId && builtGroupIds.has(g.parentId) ? g.parentId : undefined;
|
|
989
1105
|
const group: SubsystemGraphNode = {
|
|
990
|
-
id:
|
|
1106
|
+
id: g.id,
|
|
991
1107
|
type: 'subsystem-group',
|
|
992
1108
|
position: { x: bounds.x, y: bounds.y },
|
|
993
1109
|
width: Math.max(200, bounds.width),
|
|
994
1110
|
height: Math.max(160, bounds.height),
|
|
995
|
-
|
|
1111
|
+
...(parentId ? { parentId } : {}),
|
|
1112
|
+
data: { region: g.region },
|
|
996
1113
|
};
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1114
|
+
if (g.region.kind === 'process') processGroupNodes.push(group);
|
|
1115
|
+
else moduleGroupNodes.push(group);
|
|
1116
|
+
}
|
|
1117
|
+
// Clear leaf parentIds that point at groups ELK dropped.
|
|
1118
|
+
for (const n of result.nodes as SubsystemGraphNode[]) {
|
|
1119
|
+
if (n.type !== 'subsystem-component') continue;
|
|
1120
|
+
const parentId = (n as { parentId?: string }).parentId;
|
|
1121
|
+
if (parentId && !builtGroupIds.has(parentId)) {
|
|
1122
|
+
delete (n as { parentId?: string }).parentId;
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
placedNodes = [
|
|
1126
|
+
...processGroupNodes,
|
|
1127
|
+
...moduleGroupNodes,
|
|
1128
|
+
...(result.nodes as SubsystemGraphNode[]),
|
|
1129
|
+
];
|
|
1001
1130
|
labelPositions = result.edgeLabelPositions;
|
|
1002
1131
|
elkPathStrings = result.edgePaths;
|
|
1003
1132
|
elkPathPoints = result.edgePathPoints;
|
|
1004
1133
|
} catch (err) {
|
|
1005
|
-
// Fall back to the (unpositioned) grid if ELK is unavailable
|
|
1134
|
+
// Fall back to the (unpositioned) grid if ELK is unavailable — still
|
|
1135
|
+
// emit multi-member frames so parentId targets exist.
|
|
1006
1136
|
console.warn('[subsystem-graph] ELK layout failed, using manual positions:', err);
|
|
1137
|
+
const processGroupNodes: SubsystemGraphNode[] = [];
|
|
1138
|
+
const moduleGroupNodes: SubsystemGraphNode[] = [];
|
|
1139
|
+
for (const g of layoutGroups) {
|
|
1140
|
+
const group: SubsystemGraphNode = {
|
|
1141
|
+
id: g.id,
|
|
1142
|
+
type: 'subsystem-group',
|
|
1143
|
+
position: { x: 0, y: 0 },
|
|
1144
|
+
width: 400,
|
|
1145
|
+
height: 300,
|
|
1146
|
+
...(g.parentId ? { parentId: g.parentId } : {}),
|
|
1147
|
+
data: { region: g.region },
|
|
1148
|
+
};
|
|
1149
|
+
if (g.region.kind === 'process') processGroupNodes.push(group);
|
|
1150
|
+
else moduleGroupNodes.push(group);
|
|
1151
|
+
}
|
|
1152
|
+
placedNodes = [...processGroupNodes, ...moduleGroupNodes, ...nodes];
|
|
1007
1153
|
}
|
|
1008
1154
|
}
|
|
1009
1155
|
|
package/src/subsystem/nodes.tsx
CHANGED
|
@@ -43,7 +43,6 @@ export const CONSTRUCT_LABEL: Record<string, string> = {
|
|
|
43
43
|
interface: 'interface',
|
|
44
44
|
type_alias: 'type alias',
|
|
45
45
|
enum: 'enum',
|
|
46
|
-
module: 'module',
|
|
47
46
|
store: 'store',
|
|
48
47
|
external: 'external',
|
|
49
48
|
custom_entity: 'entity',
|
|
@@ -326,10 +325,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
|
|
|
326
325
|
}
|
|
327
326
|
|
|
328
327
|
/**
|
|
329
|
-
*
|
|
330
|
-
* via `parentId`; this draws the labeled container only
|
|
331
|
-
* selection).
|
|
332
|
-
* so each deployment unit reads as its own region.
|
|
328
|
+
* Boundary frame — a React Flow parent node for a process or module region.
|
|
329
|
+
* Members render inside via `parentId`; this draws the labeled container only
|
|
330
|
+
* (no handles, no selection). Border color derives from the region key.
|
|
333
331
|
*/
|
|
334
332
|
export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData, 'subsystem-group'>>) {
|
|
335
333
|
const { theme } = useTheme();
|
|
@@ -343,6 +341,8 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
|
|
|
343
341
|
const color = packageColor(region?.key ?? 'process');
|
|
344
342
|
const dimmed = data.dimmed === true;
|
|
345
343
|
const hidden = (data as { hidden?: boolean }).hidden === true;
|
|
344
|
+
const label =
|
|
345
|
+
region?.kind === 'module' ? `module · ${region.label}` : (region?.label ?? '');
|
|
346
346
|
|
|
347
347
|
if (!region) return null;
|
|
348
348
|
|
|
@@ -378,7 +378,7 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
|
|
|
378
378
|
whiteSpace: 'nowrap',
|
|
379
379
|
}}
|
|
380
380
|
>
|
|
381
|
-
{
|
|
381
|
+
{label}
|
|
382
382
|
</div>
|
|
383
383
|
</div>
|
|
384
384
|
);
|