@principal-ai/subsystems-react 0.23.1 → 0.23.2

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.
@@ -20,6 +20,7 @@ import {
20
20
  MECHANISM_COLOR,
21
21
  MECHANISM_STYLE,
22
22
  PROPOSED_COLOR,
23
+ constructBadgeColor,
23
24
  constructBadgeLabel,
24
25
  rightBadgeLabel,
25
26
  rightBadgeColor,
@@ -88,6 +89,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
88
89
  // shows as the hover badge, not as color — for now; a role glyph/accent may
89
90
  // come later. A component-authored `color` override wins over the construct.
90
91
  const color = componentColor(c, resolvePierreSyntaxThemeName(mode));
92
+ // Left badge: framework brand (e.g. React cyan) when the label is a
93
+ // framework stereotype; otherwise the same construct color as the border.
94
+ const badgeColor = constructBadgeColor(c) ?? color;
91
95
  const [hover, setHover] = useState(false);
92
96
  const configuredMax = SUBSYSTEM_CALLBACKS.maxNodeWidth;
93
97
  const maxWidth = configuredMax ?? 300;
@@ -114,6 +118,10 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
114
118
  // Selection is stamped into data by the graph component — React Flow's own
115
119
  // `selected` never updates because the node stops click propagation.
116
120
  const isSelected = selected || (data.isSelected as boolean | undefined) === true;
121
+ // Externals are "outside" the system — square corners on the node + badges.
122
+ const isExternal = c.construct === 'external';
123
+ const nodeRadius = isExternal ? 0 : 8;
124
+ const badgeRadius = isExternal ? 0 : 4;
117
125
 
118
126
  return (
119
127
  <div
@@ -141,7 +149,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
141
149
  minWidth: badgeMinWidth,
142
150
  maxWidth,
143
151
  padding: '6px 10px',
144
- borderRadius: 8,
152
+ borderRadius: nodeRadius,
145
153
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
146
154
  // Selected / file-matched nodes get a thicker border. Proposed nodes
147
155
  // use a dashed goldenrod border; left construct badge keeps construct color.
@@ -157,11 +165,12 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
157
165
  >
158
166
  {/* Construct / stereotype badge — prefers framework stereotype so a
159
167
  React UI unit reads as "react · component" instead of "function".
160
- Persistent; pointer-events none so clicks pass through to the node. */}
168
+ Persistent; pointer-events none so clicks pass through to the node.
169
+ Framework badges use brand color; border stays construct-colored. */}
161
170
  <div
162
171
  style={{
163
172
  position: 'absolute',
164
- top: -9,
173
+ top: -11,
165
174
  left: BADGE_EDGE_INSET,
166
175
  zIndex: 1,
167
176
  fontFamily: theme.fonts.monospace,
@@ -170,11 +179,11 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
170
179
  textTransform: 'uppercase',
171
180
  lineHeight: '17px',
172
181
  whiteSpace: 'nowrap',
173
- color,
182
+ color: badgeColor,
174
183
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
175
- border: `1px solid ${color}`,
176
- borderRadius: 4,
177
- padding: '0 5px',
184
+ border: `2px solid ${badgeColor}`,
185
+ borderRadius: badgeRadius,
186
+ padding: '2px 8px',
178
187
  }}
179
188
  >
180
189
  {constructBadgeLabel(c)}
@@ -187,7 +196,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
187
196
  <div
188
197
  style={{
189
198
  position: 'absolute',
190
- top: -9,
199
+ top: -11,
191
200
  right: BADGE_EDGE_INSET,
192
201
  zIndex: 1,
193
202
  display: 'flex',
@@ -205,9 +214,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
205
214
  lineHeight: '17px',
206
215
  color: storageColor,
207
216
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
208
- border: `1px solid ${storageColor}`,
209
- borderRadius: 4,
210
- padding: '0 5px',
217
+ border: `2px solid ${storageColor}`,
218
+ borderRadius: badgeRadius,
219
+ padding: '2px 8px',
211
220
  }}
212
221
  >
213
222
  {storageLabel}
@@ -223,9 +232,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
223
232
  lineHeight: '17px',
224
233
  color: topRightColor,
225
234
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
226
- border: `1px solid ${topRightColor}`,
227
- borderRadius: 4,
228
- padding: '0 5px',
235
+ border: `2px solid ${topRightColor}`,
236
+ borderRadius: badgeRadius,
237
+ padding: '2px 8px',
229
238
  }}
230
239
  >
231
240
  {topRightLabel}
@@ -241,7 +250,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
241
250
  <div
242
251
  style={{
243
252
  position: 'absolute',
244
- bottom: -13,
253
+ bottom: -14,
245
254
  left: '50%',
246
255
  transform: 'translateX(-50%)',
247
256
  zIndex: 1,
@@ -254,9 +263,9 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
254
263
  // borders and badges, not the file name
255
264
  color: theme.colors.text ?? theme.colors.textSecondary,
256
265
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
257
- border: `1px solid ${color}`,
258
- borderRadius: 4,
259
- padding: '0 5px',
266
+ border: `2px solid ${color}`,
267
+ borderRadius: badgeRadius,
268
+ padding: '2px 8px',
260
269
  cursor: 'pointer',
261
270
  }}
262
271
  onClick={(e) => {
@@ -341,8 +350,21 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
341
350
  const color = packageColor(region?.key ?? 'process');
342
351
  const dimmed = data.dimmed === true;
343
352
  const hidden = (data as { hidden?: boolean }).hidden === true;
344
- const label =
345
- region?.kind === 'module' ? `module · ${region.label}` : (region?.label ?? '');
353
+ // Label is the region identity alone (path / process key / owner/name).
354
+ // Kind is carried by frame chrome (square = process; rounded = module/package)
355
+ // — don't prefix `module ·` / `package ·` or the path reads twice.
356
+ const label = region?.label ?? '';
357
+ // Process frames match external nodes — square corners (outside the soft
358
+ // component language). Module / package frames keep the rounded look.
359
+ const isProcess = region?.kind === 'process';
360
+ const frameRadius = isProcess ? 0 : 12;
361
+ const badgeRadius = isProcess ? 0 : 4;
362
+ // Processes are deployment units — solid frame. Modules/packages stay dashed
363
+ // until selected.
364
+ const frameStyle = isProcess || selected ? 'solid' : 'dashed';
365
+ // Process keeps a tinted fill; module / package bodies stay transparent so
366
+ // nested frames don't stack washes.
367
+ const frameFill = isProcess ? `${color}14` : 'transparent';
346
368
 
347
369
  if (!region) return null;
348
370
 
@@ -353,9 +375,9 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
353
375
  width: width ?? 400,
354
376
  height: height ?? 300,
355
377
  boxSizing: 'border-box',
356
- borderRadius: 12,
357
- border: `2px ${selected ? 'solid' : 'dashed'} ${color}`,
358
- background: `${color}14`,
378
+ borderRadius: frameRadius,
379
+ border: `2px ${frameStyle} ${color}`,
380
+ background: frameFill,
359
381
  opacity: hidden ? 0 : dimmed ? 0.35 : 1,
360
382
  transition: 'opacity 150ms ease',
361
383
  pointerEvents: 'none',
@@ -364,8 +386,14 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
364
386
  <div
365
387
  style={{
366
388
  position: 'absolute',
367
- top: -19,
368
- left: 12,
389
+ // Process: badge fill starts with the process fill (inside the
390
+ // frame border); no top badge border so widths don't fight.
391
+ // Module/package sit astride the top edge like component badges.
392
+ // Always set left/transform/borderTop explicitly — React Flow reuses
393
+ // group DOM nodes and `undefined` does not clear a prior value.
394
+ top: isProcess ? 0 : -19,
395
+ left: isProcess ? '50%' : 12,
396
+ transform: isProcess ? 'translateX(-50%)' : 'none',
369
397
  fontFamily: theme.fonts.monospace,
370
398
  fontSize: theme.fontSizes[3],
371
399
  fontWeight: 700,
@@ -373,7 +401,8 @@ export function SubsystemGroupNode(props: NodeProps<Node<SubsystemGroupNodeData,
373
401
  color,
374
402
  background: theme.colors.backgroundSecondary ?? theme.colors.background,
375
403
  border: `1px solid ${color}`,
376
- borderRadius: 4,
404
+ borderTopWidth: isProcess ? 0 : 1,
405
+ borderRadius: badgeRadius,
377
406
  padding: '1px 7px',
378
407
  whiteSpace: 'nowrap',
379
408
  }}