@principal-ai/subsystems-react 0.24.8 → 0.24.10

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.
@@ -1,10 +1,11 @@
1
1
  /**
2
- * DiagnosticToggle — the subsystem graph's status/report chip.
2
+ * DiagnosticToggle — the subsystem graph's status + diagnostics-view toggle.
3
3
  *
4
4
  * A compact button that lives in the sidebar title row (beside the description
5
5
  * toggle). It carries the *status* of the graph's last verification pass in its
6
- * icon color + count and, on click, opens the full report (the host owns that
7
- * report; this component only surfaces state and calls `onOpen`).
6
+ * icon color + count, and toggles the sidebar between the diagnostics list and
7
+ * the normal files/walkthroughs view. The graph owns that view; this component
8
+ * only surfaces state and calls `onToggle`.
8
9
  *
9
10
  * Status → color (theme):
10
11
  * - `ok` → success (green)
@@ -19,6 +20,7 @@
19
20
  import { useState } from 'react';
20
21
  import { Activity, Loader2 } from 'lucide-react';
21
22
  import { useTheme } from '@principal-ade/industry-theme';
23
+ import { hexWithAlpha } from './nodes';
22
24
 
23
25
  export type SubsystemDiagnosticStatus = 'ok' | 'issues' | 'gaps' | 'unknown';
24
26
 
@@ -30,10 +32,12 @@ export interface SubsystemDiagnostic {
30
32
  stale?: boolean;
31
33
  /** A verification pass is running right now. */
32
34
  busy?: boolean;
35
+ /** True while the diagnostics list is showing (pressed state). */
36
+ active?: boolean;
33
37
  /** Tooltip / a11y label override; otherwise derived from `status`. */
34
38
  title?: string;
35
- /** Click — open the report. */
36
- onOpen?: () => void;
39
+ /** Click — toggle the diagnostics list. */
40
+ onToggle?: () => void;
37
41
  }
38
42
 
39
43
  /** Resolve the status accent from the active theme. */
@@ -66,8 +70,8 @@ export function diagnosticTitle(diagnostic: SubsystemDiagnostic): string {
66
70
  : 'Diagnostics: unconfirmed claims';
67
71
  else head = 'Diagnostics: not checked yet';
68
72
  if (busy) return `${head} — checking…`;
69
- if (stale) return `${head} (stale) — open for details`;
70
- return `${head} — open report`;
73
+ if (stale) return `${head} (stale) — toggle the list`;
74
+ return `${head} — toggle the list`;
71
75
  }
72
76
 
73
77
  export interface SubsystemDiagnosticToggleProps extends SubsystemDiagnostic {
@@ -80,16 +84,22 @@ export function SubsystemDiagnosticToggle({
80
84
  issueCount,
81
85
  stale = false,
82
86
  busy = false,
87
+ active = false,
83
88
  size = 14,
84
- onOpen,
89
+ onToggle,
85
90
  ...rest
86
91
  }: SubsystemDiagnosticToggleProps) {
87
92
  const { theme } = useTheme();
88
93
  const [hover, setHover] = useState(false);
89
94
  const muted = theme.colors.textMuted ?? theme.colors.textSecondary;
90
95
  const color = diagnosticStatusColor(status, theme.colors, muted);
91
- const label = diagnosticTitle({ status, issueCount, stale, busy, ...rest });
96
+ const label = diagnosticTitle({ status, issueCount, stale, busy, active, ...rest });
92
97
  const showCount = issueCount != null && issueCount > 0;
98
+ const background = active
99
+ ? hexWithAlpha(color, hover ? 0.24 : 0.16)
100
+ : hover
101
+ ? theme.colors.border
102
+ : 'transparent';
93
103
 
94
104
  return (
95
105
  <>
@@ -98,12 +108,12 @@ export function SubsystemDiagnosticToggle({
98
108
  )}
99
109
  <button
100
110
  type="button"
101
- onClick={onOpen}
111
+ onClick={onToggle}
102
112
  onMouseEnter={() => setHover(true)}
103
113
  onMouseLeave={() => setHover(false)}
104
114
  title={label}
105
115
  aria-label={label}
106
- aria-haspopup="dialog"
116
+ aria-pressed={active}
107
117
  style={{
108
118
  display: 'inline-flex',
109
119
  alignItems: 'center',
@@ -112,7 +122,7 @@ export function SubsystemDiagnosticToggle({
112
122
  padding: showCount ? '0 7px 0 6px' : '0 5px',
113
123
  border: 'none',
114
124
  borderRadius: 4,
115
- background: hover ? theme.colors.border : 'transparent',
125
+ background,
116
126
  color,
117
127
  cursor: 'pointer',
118
128
  flexShrink: 0,
@@ -169,19 +169,20 @@ export interface SubsystemComponentGraphProps {
169
169
  sidebarAfterDescription?: ReactNode;
170
170
  /**
171
171
  * Diagnostics status chip in the sidebar title row (beside the description
172
- * toggle). Shows the last verification pass's state as an icon color + count;
173
- * `onOpen` opens the full report. Omit to hide the chip.
172
+ * toggle). Shows the last verification pass's state as an icon color + count,
173
+ * and toggles the sidebar between the diagnostics list and the normal
174
+ * files/walkthroughs view. Omit to hide the chip.
174
175
  */
175
176
  diagnostic?: SubsystemDiagnostic;
176
177
  /**
177
178
  * Verification issues for this graph (audit findings). When diagnostics are
178
- * active (see `showIssues`) the sidebar's bottom panel becomes the issue
179
- * list instead of the file tree / walkthroughs.
179
+ * active the sidebar's bottom panel becomes the issue list instead of the
180
+ * file tree / walkthroughs.
180
181
  */
181
182
  issues?: SubsystemIssue[];
182
183
  /**
183
- * Force the sidebar into diagnostics mode. Defaults to `true` whenever
184
- * `issues` is non-empty (and nothing else has been forced).
184
+ * Pin diagnostics mode (controlled). When omitted, the sidebar seeds
185
+ * diagnostics on iff `issues` is non-empty, and the title-row chip toggles it.
185
186
  */
186
187
  showIssues?: boolean;
187
188
  /** Click an issue — focus its target on the graph / open detail. */
@@ -965,10 +966,17 @@ function Inner({ components, relations, walkthroughs, onSelect, onEdgeSelect, me
965
966
  // own owner-avatar header. Clicking a header collapses that repo's tree.
966
967
  const repoGroups = useMemo(() => buildRepoGroups(components), [components]);
967
968
  const hasWalkthroughs = useMemo(() => (walkthroughs?.length ?? 0) > 0, [walkthroughs]);
968
- // Diagnostics mode: the bottom panel shows the issue list instead of the
969
- // file tree / walkthroughs. Explicit `showIssues` wins; otherwise any issues
970
- // present turn it on.
971
- const issuesActive = showIssues ?? (issues?.length ?? 0) > 0;
969
+ // Diagnostics view in the sidebar. Uncontrolled unless the host pins
970
+ // `showIssues`: the title-row chip toggles it, seeded from issues presence.
971
+ const [diagnosticsOpen, setDiagnosticsOpen] = useState<boolean>(
972
+ showIssues ?? (issues?.length ?? 0) > 0,
973
+ );
974
+ const controlledIssues = showIssues !== undefined;
975
+ const issuesActive = controlledIssues ? showIssues : diagnosticsOpen;
976
+ const toggleIssues = () => {
977
+ if (!controlledIssues) setDiagnosticsOpen((v) => !v);
978
+ diagnostic?.onToggle?.();
979
+ };
972
980
  const [collapsedRepos, setCollapsedRepos] = useState<Set<string>>(new Set());
973
981
  const toggleRepoCollapsed = useCallback((key: string) => {
974
982
  setCollapsedRepos((prev) => {
@@ -1512,7 +1520,13 @@ function Inner({ components, relations, walkthroughs, onSelect, onEdgeSelect, me
1512
1520
  marginLeft: title ? undefined : 'auto',
1513
1521
  }}
1514
1522
  >
1515
- {diagnostic && <SubsystemDiagnosticToggle {...diagnostic} />}
1523
+ {diagnostic && (
1524
+ <SubsystemDiagnosticToggle
1525
+ {...diagnostic}
1526
+ active={issuesActive}
1527
+ onToggle={toggleIssues}
1528
+ />
1529
+ )}
1516
1530
  {description && (
1517
1531
  <button
1518
1532
  type="button"