@principal-ai/subsystems-react 0.20.5 → 0.20.6
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/construct.d.ts +23 -0
- package/dist/graphify/construct.d.ts.map +1 -0
- package/dist/graphify/construct.js +77 -0
- package/dist/graphify/construct.js.map +1 -0
- package/dist/graphify/index.d.ts +2 -2
- package/dist/graphify/index.d.ts.map +1 -1
- package/dist/graphify/index.js +1 -1
- package/dist/graphify/index.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/subsystem/ComponentDeclaration.js +1 -1
- package/dist/subsystem/ComponentDeclaration.js.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
- package/dist/subsystem/SubsystemComponentGraph.js +23 -2
- package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
- package/dist/subsystem/model.d.ts +24 -7
- package/dist/subsystem/model.d.ts.map +1 -1
- package/dist/subsystem/model.js +25 -2
- package/dist/subsystem/model.js.map +1 -1
- package/dist/subsystem/nodes.d.ts.map +1 -1
- package/dist/subsystem/nodes.js +18 -10
- package/dist/subsystem/nodes.js.map +1 -1
- package/package.json +1 -1
- package/src/graphify/{kind.test.ts → construct.test.ts} +20 -20
- package/src/graphify/{kind.ts → construct.ts} +17 -17
- package/src/graphify/index.ts +5 -2
- package/src/index.ts +7 -4
- package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +2 -6
- package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +254 -0
- package/src/subsystem/ComponentDeclaration.tsx +1 -1
- package/src/subsystem/SubsystemComponentGraph.tsx +23 -2
- package/src/subsystem/model.test.ts +22 -0
- package/src/subsystem/model.ts +38 -9
- package/src/subsystem/nodes.tsx +21 -14
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
buildSubsystemGraph,
|
|
9
9
|
deriveNameFromSymbol,
|
|
10
10
|
constructBadgeLabel,
|
|
11
|
+
rightBadgeLabel,
|
|
11
12
|
nodeMinWidthForBadges,
|
|
12
13
|
formatPurl,
|
|
13
14
|
packageColor,
|
|
@@ -156,6 +157,27 @@ describe('subsystem graph model', () => {
|
|
|
156
157
|
role: 'entry',
|
|
157
158
|
});
|
|
158
159
|
expect(withRole).toBeGreaterThan(stereotype);
|
|
160
|
+
|
|
161
|
+
const withProposed = nodeMinWidthForBadges({
|
|
162
|
+
construct: 'function',
|
|
163
|
+
proposed: true,
|
|
164
|
+
});
|
|
165
|
+
expect(withProposed).toBeGreaterThan(plain);
|
|
166
|
+
|
|
167
|
+
const withBoth = nodeMinWidthForBadges({
|
|
168
|
+
construct: 'function',
|
|
169
|
+
role: 'entry',
|
|
170
|
+
proposed: true,
|
|
171
|
+
});
|
|
172
|
+
// proposed-only label is shorter than entry · proposed; still wider than plain
|
|
173
|
+
expect(withBoth).toBe(withProposed);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
test('rightBadgeLabel prefers proposed over role', () => {
|
|
177
|
+
expect(rightBadgeLabel({})).toBeNull();
|
|
178
|
+
expect(rightBadgeLabel({ proposed: true })).toBe('proposed');
|
|
179
|
+
expect(rightBadgeLabel({ role: 'entry' })).toBe('entry');
|
|
180
|
+
expect(rightBadgeLabel({ role: 'service', proposed: true })).toBe('proposed');
|
|
159
181
|
});
|
|
160
182
|
|
|
161
183
|
test('deriveNameFromSymbol falls back to file basename for modules', () => {
|
package/src/subsystem/model.ts
CHANGED
|
@@ -126,6 +126,13 @@ export interface SubsystemComponent {
|
|
|
126
126
|
purl: string;
|
|
127
127
|
/** One-line purpose shown on the node. */
|
|
128
128
|
purpose?: string;
|
|
129
|
+
/**
|
|
130
|
+
* Design / migration placeholder — participates in edges and flows but is
|
|
131
|
+
* not a live source declaration yet. Verification skips source checks until
|
|
132
|
+
* promoted (`proposed` cleared, `file` + `symbol` filled). Orthogonal to
|
|
133
|
+
* `construct` (intended shape) and `role` (topology).
|
|
134
|
+
*/
|
|
135
|
+
proposed?: boolean;
|
|
129
136
|
/**
|
|
130
137
|
* Semantic role — where the node sits in the topology (boundary element,
|
|
131
138
|
* external system), orthogonal to `construct` (what it is). Drives the
|
|
@@ -179,13 +186,6 @@ export interface SubsystemComponent {
|
|
|
179
186
|
* letting ELK guess the order.
|
|
180
187
|
*/
|
|
181
188
|
layer?: number;
|
|
182
|
-
/**
|
|
183
|
-
* How this session *occupied* the component — `edited` (modified it) vs
|
|
184
|
-
* `analyzed` (read deeply / built an understanding) vs `referenced`
|
|
185
|
-
* (touched in passing). Drives clustering: edits strengthen a subsystem,
|
|
186
|
-
* but a read-only investigation can still form one around an analyzed seam.
|
|
187
|
-
*/
|
|
188
|
-
capture?: 'edited' | 'analyzed' | 'referenced';
|
|
189
189
|
/**
|
|
190
190
|
* Graphify drill-down detail for this component, when the facet is anchored
|
|
191
191
|
* to a graphify node. Discriminated by kind (class/function/type/module/store/
|
|
@@ -525,6 +525,33 @@ export const ROLE_LABEL: Record<SubsystemComponentRole, string> = {
|
|
|
525
525
|
service: 'service',
|
|
526
526
|
};
|
|
527
527
|
|
|
528
|
+
/** Right-badge accent for `proposed: true` (no role, or combined with role). */
|
|
529
|
+
export const PROPOSED_COLOR = '#b8860b'; // darkgoldenrod — not-yet-in-source
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Top-right badge label: proposed wins over role when both are set
|
|
533
|
+
* (proposed is the temporary exception to scan for; role stays on the model).
|
|
534
|
+
* Returns null when neither applies.
|
|
535
|
+
*/
|
|
536
|
+
export function rightBadgeLabel(component: {
|
|
537
|
+
role?: SubsystemComponentRole;
|
|
538
|
+
proposed?: boolean;
|
|
539
|
+
}): string | null {
|
|
540
|
+
if (component.proposed) return 'proposed';
|
|
541
|
+
if (component.role != null) return ROLE_LABEL[component.role];
|
|
542
|
+
return null;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/** Color for the top-right badge (role and/or proposed). */
|
|
546
|
+
export function rightBadgeColor(component: {
|
|
547
|
+
role?: SubsystemComponentRole;
|
|
548
|
+
proposed?: boolean;
|
|
549
|
+
}): string | null {
|
|
550
|
+
if (component.proposed) return PROPOSED_COLOR;
|
|
551
|
+
if (component.role != null) return ROLE_COLOR[component.role];
|
|
552
|
+
return null;
|
|
553
|
+
}
|
|
554
|
+
|
|
528
555
|
/**
|
|
529
556
|
* Primary badge text for a node: prefer framework stereotype over the
|
|
530
557
|
* language construct so a React UI unit reads as "component" / "hook"
|
|
@@ -576,13 +603,15 @@ export function nodeMinWidthForBadges(component: {
|
|
|
576
603
|
framework?: string;
|
|
577
604
|
stereotype?: string;
|
|
578
605
|
role?: SubsystemComponentRole;
|
|
606
|
+
proposed?: boolean;
|
|
579
607
|
entityKind?: string;
|
|
580
608
|
}): number {
|
|
581
609
|
const left = estimateBadgeLabelWidth(constructBadgeLabel(component));
|
|
582
|
-
|
|
610
|
+
const rightLabel = rightBadgeLabel(component);
|
|
611
|
+
if (rightLabel == null) {
|
|
583
612
|
return Math.max(NODE_CSS_MIN_WIDTH, BADGE_EDGE_INSET + left + BADGE_EDGE_INSET);
|
|
584
613
|
}
|
|
585
|
-
const right = estimateBadgeLabelWidth(
|
|
614
|
+
const right = estimateBadgeLabelWidth(rightLabel);
|
|
586
615
|
return Math.max(
|
|
587
616
|
NODE_CSS_MIN_WIDTH,
|
|
588
617
|
BADGE_EDGE_INSET + left + BADGE_PAIR_GAP + right + BADGE_EDGE_INSET,
|
package/src/subsystem/nodes.tsx
CHANGED
|
@@ -19,9 +19,10 @@ import { useTheme } from '@principal-ade/industry-theme';
|
|
|
19
19
|
import {
|
|
20
20
|
MECHANISM_COLOR,
|
|
21
21
|
MECHANISM_STYLE,
|
|
22
|
-
|
|
23
|
-
ROLE_LABEL,
|
|
22
|
+
PROPOSED_COLOR,
|
|
24
23
|
constructBadgeLabel,
|
|
24
|
+
rightBadgeLabel,
|
|
25
|
+
rightBadgeColor,
|
|
25
26
|
deriveNameFromSymbol,
|
|
26
27
|
BADGE_EDGE_INSET,
|
|
27
28
|
nodeMinWidthForBadges,
|
|
@@ -92,11 +93,20 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
|
|
|
92
93
|
// `symbol` is the source of truth; `name` is derived from it consistently.
|
|
93
94
|
const displayName = deriveNameFromSymbol(c.symbol, c.construct, c.name, c.file, c.stereotype);
|
|
94
95
|
// Top badges are absolutely positioned — widen the node so they nowrap
|
|
95
|
-
// instead of wrapping, including when construct + role
|
|
96
|
+
// instead of wrapping, including when construct + role/proposed share the top.
|
|
96
97
|
const badgeMinWidth = nodeMinWidthForBadges(c);
|
|
98
|
+
const topRightLabel = rightBadgeLabel(c);
|
|
99
|
+
const topRightColor = rightBadgeColor(c);
|
|
97
100
|
// Set while a file is open in the drawer: true → spotlight, false → dim,
|
|
98
101
|
// absent (no file open) → neutral.
|
|
99
102
|
const fileMatch = data.fileMatch as boolean | undefined;
|
|
103
|
+
// Border: proposed uses the goldenrod accent (dashed); construct color stays
|
|
104
|
+
// on the left badge. File-open spotlight still wins with primary.
|
|
105
|
+
const borderColor = fileMatch
|
|
106
|
+
? theme.colors.primary
|
|
107
|
+
: c.proposed
|
|
108
|
+
? PROPOSED_COLOR
|
|
109
|
+
: color;
|
|
100
110
|
// Selection is stamped into data by the graph component — React Flow's own
|
|
101
111
|
// `selected` never updates because the node stops click propagation.
|
|
102
112
|
const isSelected = selected || (data.isSelected as boolean | undefined) === true;
|
|
@@ -129,10 +139,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
|
|
|
129
139
|
padding: '6px 10px',
|
|
130
140
|
borderRadius: 8,
|
|
131
141
|
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
132
|
-
// Selected / file-matched nodes get a thicker border
|
|
133
|
-
//
|
|
134
|
-
|
|
135
|
-
border: `${isSelected || fileMatch ? 4 : 2}px solid ${fileMatch ? theme.colors.primary : color}`,
|
|
142
|
+
// Selected / file-matched nodes get a thicker border. Proposed nodes
|
|
143
|
+
// use a dashed goldenrod border; left construct badge keeps construct color.
|
|
144
|
+
border: `${isSelected || fileMatch ? 4 : 2}px ${c.proposed ? 'dashed' : 'solid'} ${borderColor}`,
|
|
136
145
|
boxShadow: fileMatch
|
|
137
146
|
? `0 1px 4px rgba(0,0,0,0.25), 0 0 12px ${theme.colors.primary}55`
|
|
138
147
|
: '0 1px 4px rgba(0,0,0,0.25)',
|
|
@@ -167,10 +176,8 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
|
|
|
167
176
|
{constructBadgeLabel(c)}
|
|
168
177
|
</div>
|
|
169
178
|
|
|
170
|
-
{/*
|
|
171
|
-
|
|
172
|
-
border stays construct-colored. */}
|
|
173
|
-
{c.role != null && (
|
|
179
|
+
{/* Right badge — proposed wins over role when both are set. */}
|
|
180
|
+
{topRightLabel != null && topRightColor != null && (
|
|
174
181
|
<div
|
|
175
182
|
style={{
|
|
176
183
|
position: 'absolute',
|
|
@@ -183,14 +190,14 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
|
|
|
183
190
|
textTransform: 'uppercase',
|
|
184
191
|
lineHeight: '17px',
|
|
185
192
|
whiteSpace: 'nowrap',
|
|
186
|
-
color:
|
|
193
|
+
color: topRightColor,
|
|
187
194
|
background: theme.colors.backgroundSecondary ?? theme.colors.background,
|
|
188
|
-
border: `1px solid ${
|
|
195
|
+
border: `1px solid ${topRightColor}`,
|
|
189
196
|
borderRadius: 4,
|
|
190
197
|
padding: '0 5px',
|
|
191
198
|
}}
|
|
192
199
|
>
|
|
193
|
-
{
|
|
200
|
+
{topRightLabel}
|
|
194
201
|
</div>
|
|
195
202
|
)}
|
|
196
203
|
|