@burdenoff/microfe-vibecontrols 2026.521.3 → 2026.522.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.
Files changed (105) hide show
  1. package/dist/VibeControlsRoot.js +86 -78
  2. package/dist/VibeControlsRoot.js.map +1 -1
  3. package/dist/VibeControlsRoutes.js +168 -125
  4. package/dist/VibeControlsRoutes.js.map +1 -1
  5. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js +174 -0
  6. package/dist/components/agent-manager/AgentManagerBookmarksDrawer.js.map +1 -0
  7. package/dist/components/agent-manager/AgentManagerChatPane.js +149 -94
  8. package/dist/components/agent-manager/AgentManagerChatPane.js.map +1 -1
  9. package/dist/components/agent-manager/AgentManagerMessageItem.js +52 -41
  10. package/dist/components/agent-manager/AgentManagerMessageItem.js.map +1 -1
  11. package/dist/components/agent-manager/AgentManagerMessageList.js +22 -15
  12. package/dist/components/agent-manager/AgentManagerMessageList.js.map +1 -1
  13. package/dist/components/agents/OpenInEditorMenu.js +241 -0
  14. package/dist/components/agents/OpenInEditorMenu.js.map +1 -0
  15. package/dist/components/agents/VibeOpenInEditorMenu.js +20 -0
  16. package/dist/components/agents/VibeOpenInEditorMenu.js.map +1 -0
  17. package/dist/components/agents/idePresets.js +148 -0
  18. package/dist/components/agents/idePresets.js.map +1 -0
  19. package/dist/components/agents/index.js +3 -0
  20. package/dist/components/ai/AIPlanPanel.js +332 -0
  21. package/dist/components/ai/AIPlanPanel.js.map +1 -0
  22. package/dist/components/analytics/index.js +2 -2
  23. package/dist/components/assistant/AssistantMessageItem.js +55 -47
  24. package/dist/components/assistant/AssistantMessageItem.js.map +1 -1
  25. package/dist/components/audit/AuditExportButton.js +87 -41
  26. package/dist/components/audit/AuditExportButton.js.map +1 -1
  27. package/dist/components/gitops/CIDurationChart.js +11 -11
  28. package/dist/components/security/SecurityEmptyState.js +33 -0
  29. package/dist/components/security/SecurityEmptyState.js.map +1 -0
  30. package/dist/components/security/SecurityEvidenceList.js +42 -0
  31. package/dist/components/security/SecurityEvidenceList.js.map +1 -0
  32. package/dist/components/security/SecurityFindingsTable.js +201 -0
  33. package/dist/components/security/SecurityFindingsTable.js.map +1 -0
  34. package/dist/components/security/SecurityHeaderStrip.js +67 -0
  35. package/dist/components/security/SecurityHeaderStrip.js.map +1 -0
  36. package/dist/components/security/SecurityLifecycleStepper.js +66 -0
  37. package/dist/components/security/SecurityLifecycleStepper.js.map +1 -0
  38. package/dist/components/security/SecurityProviderDrawer.js +112 -0
  39. package/dist/components/security/SecurityProviderDrawer.js.map +1 -0
  40. package/dist/components/security/SecurityTabPanel.js +105 -0
  41. package/dist/components/security/SecurityTabPanel.js.map +1 -0
  42. package/dist/components/security/gql.js +128 -0
  43. package/dist/components/security/gql.js.map +1 -0
  44. package/dist/components/security/index.js +8 -0
  45. package/dist/components/security/types.js +80 -0
  46. package/dist/components/security/types.js.map +1 -0
  47. package/dist/components/settings/IdePreferencesSection.js +66 -0
  48. package/dist/components/settings/IdePreferencesSection.js.map +1 -0
  49. package/dist/components/shared/PageSkeleton.js +88 -0
  50. package/dist/components/shared/PageSkeleton.js.map +1 -0
  51. package/dist/constants/pluginCatalog.js +20 -8
  52. package/dist/constants/pluginCatalog.js.map +1 -1
  53. package/dist/generated/wspace-operations.js +1350 -1262
  54. package/dist/generated/wspace-operations.js.map +1 -1
  55. package/dist/generated/wspace-types.js.map +1 -1
  56. package/dist/hooks/useToggleAgentBookmark.js +32 -0
  57. package/dist/hooks/useToggleAgentBookmark.js.map +1 -0
  58. package/dist/index.js +10 -10
  59. package/dist/pages/AgentDetailsPage.js +25 -25
  60. package/dist/pages/AgentDetailsPage.js.map +1 -1
  61. package/dist/pages/AgentsPage.js +326 -327
  62. package/dist/pages/AgentsPage.js.map +1 -1
  63. package/dist/pages/AnalyticsPage.js +425 -420
  64. package/dist/pages/AnalyticsPage.js.map +1 -1
  65. package/dist/pages/CatalogDetailPage.js +258 -249
  66. package/dist/pages/CatalogDetailPage.js.map +1 -1
  67. package/dist/pages/CatalogPage.js +22 -22
  68. package/dist/pages/CatalogPage.js.map +1 -1
  69. package/dist/pages/ComponentDetailPage.js +30 -30
  70. package/dist/pages/ComponentDetailPage.js.map +1 -1
  71. package/dist/pages/ComponentsPage.js +23 -23
  72. package/dist/pages/ComponentsPage.js.map +1 -1
  73. package/dist/pages/LogsPage.js +107 -0
  74. package/dist/pages/LogsPage.js.map +1 -0
  75. package/dist/pages/NoteDetailsPage.js +12 -12
  76. package/dist/pages/NoteDetailsPage.js.map +1 -1
  77. package/dist/pages/SessionDetailsPage.js +12 -12
  78. package/dist/pages/SessionDetailsPage.js.map +1 -1
  79. package/dist/pages/SessionsPage.js +158 -136
  80. package/dist/pages/SessionsPage.js.map +1 -1
  81. package/dist/pages/SettingsPage.js +60 -52
  82. package/dist/pages/SettingsPage.js.map +1 -1
  83. package/dist/pages/TargetDetailsPage.js +18 -18
  84. package/dist/pages/TargetDetailsPage.js.map +1 -1
  85. package/dist/pages/TargetsPage.js +328 -331
  86. package/dist/pages/TargetsPage.js.map +1 -1
  87. package/dist/pages/TemplateDetailPage.js +17 -17
  88. package/dist/pages/TemplateDetailPage.js.map +1 -1
  89. package/dist/pages/TemplatesPage.js +17 -17
  90. package/dist/pages/TemplatesPage.js.map +1 -1
  91. package/dist/pages/VibeDetailsPage.js +552 -535
  92. package/dist/pages/VibeDetailsPage.js.map +1 -1
  93. package/dist/pages/VibesPage.js +321 -296
  94. package/dist/pages/VibesPage.js.map +1 -1
  95. package/dist/pages/ai/AIBookmarksPage.js +184 -0
  96. package/dist/pages/ai/AIBookmarksPage.js.map +1 -0
  97. package/dist/pages/ai/AIPage.js +138 -109
  98. package/dist/pages/ai/AIPage.js.map +1 -1
  99. package/dist/services/planApi.js +18 -0
  100. package/dist/services/planApi.js.map +1 -0
  101. package/dist/store/vibeControlsStore.js +20 -2
  102. package/dist/store/vibeControlsStore.js.map +1 -1
  103. package/dist/translations/en.js +30 -1
  104. package/dist/translations/en.js.map +1 -1
  105. package/package.json +4 -4
@@ -0,0 +1,201 @@
1
+ import { CREATE_SECURITY_EXCEPTION_MUTATION as e, SECURITY_FINDINGS_QUERY as t, UPDATE_FINDING_STATUS_MUTATION as n } from "./gql.js";
2
+ import { useState as r } from "react";
3
+ import { X as i } from "lucide-react";
4
+ import { useMutation as a, useQuery as o } from "@apollo/client/react";
5
+ import { jsx as s, jsxs as c } from "react/jsx-runtime";
6
+ //#region src/components/security/SecurityFindingsTable.tsx
7
+ var l = {
8
+ CRITICAL: "bg-status-error-bg-subtle text-status-error-text",
9
+ HIGH: "bg-status-warning-bg-subtle text-status-warning-text",
10
+ MEDIUM: "bg-status-warning-bg-subtle text-status-warning-text opacity-80",
11
+ LOW: "bg-bg-sunken text-text-secondary",
12
+ INFO: "bg-bg-sunken text-text-tertiary"
13
+ };
14
+ function u({ vibeId: u }) {
15
+ let { data: f, loading: p, refetch: m } = o(t, { variables: {
16
+ filter: { vibeId: u },
17
+ pagination: { limit: 100 }
18
+ } }), [h] = a(n), [g] = a(e), [_, v] = r(null), y = f?.securityFindings.items ?? [];
19
+ return /* @__PURE__ */ c("div", {
20
+ "data-testid": "security-findings-table",
21
+ className: "space-y-2",
22
+ children: [
23
+ /* @__PURE__ */ c("div", {
24
+ className: "flex items-center justify-between",
25
+ children: [/* @__PURE__ */ s("h4", {
26
+ className: "text-sm font-semibold text-text-primary",
27
+ children: "Findings"
28
+ }), /* @__PURE__ */ s("span", {
29
+ className: "text-xs text-text-secondary",
30
+ children: p ? "Loading…" : `${f?.securityFindings.total ?? 0} total`
31
+ })]
32
+ }),
33
+ /* @__PURE__ */ s("div", {
34
+ className: "overflow-hidden rounded-md border border-border-default",
35
+ children: /* @__PURE__ */ c("table", {
36
+ className: "w-full text-sm",
37
+ children: [/* @__PURE__ */ s("thead", {
38
+ className: "bg-bg-sunken text-text-secondary",
39
+ children: /* @__PURE__ */ c("tr", { children: [
40
+ /* @__PURE__ */ s("th", {
41
+ className: "px-3 py-2 text-left text-xs",
42
+ children: "Severity"
43
+ }),
44
+ /* @__PURE__ */ s("th", {
45
+ className: "px-3 py-2 text-left text-xs",
46
+ children: "Title"
47
+ }),
48
+ /* @__PURE__ */ s("th", {
49
+ className: "px-3 py-2 text-left text-xs",
50
+ children: "File / Package"
51
+ }),
52
+ /* @__PURE__ */ s("th", {
53
+ className: "px-3 py-2 text-left text-xs",
54
+ children: "Status"
55
+ })
56
+ ] })
57
+ }), /* @__PURE__ */ c("tbody", { children: [y.length === 0 && /* @__PURE__ */ s("tr", { children: /* @__PURE__ */ s("td", {
58
+ colSpan: 4,
59
+ className: "px-3 py-6 text-center text-text-secondary text-xs",
60
+ children: "No findings yet. Run a scan to populate this table."
61
+ }) }), y.map((e) => /* @__PURE__ */ c("tr", {
62
+ "data-finding-id": e.id,
63
+ "data-severity": e.severity.toLowerCase(),
64
+ "data-status": e.status.toLowerCase(),
65
+ className: "border-t border-border-subtle hover:bg-bg-sunken cursor-pointer",
66
+ onClick: () => v(e),
67
+ children: [
68
+ /* @__PURE__ */ s("td", {
69
+ className: "px-3 py-2",
70
+ children: /* @__PURE__ */ s("span", {
71
+ className: `px-2 py-0.5 rounded text-xs font-semibold ${l[e.severity]}`,
72
+ children: e.severity
73
+ })
74
+ }),
75
+ /* @__PURE__ */ s("td", {
76
+ className: "px-3 py-2 text-text-primary truncate max-w-[24rem]",
77
+ children: e.title
78
+ }),
79
+ /* @__PURE__ */ s("td", {
80
+ className: "px-3 py-2 text-text-secondary text-xs",
81
+ children: e.packageName ? /* @__PURE__ */ c("span", { children: [
82
+ e.packageName,
83
+ "@",
84
+ e.packageVersion ?? "?"
85
+ ] }) : /* @__PURE__ */ c("span", { children: [e.file, e.line ? `:${e.line}` : ""] })
86
+ }),
87
+ /* @__PURE__ */ s("td", {
88
+ className: "px-3 py-2 text-text-secondary text-xs",
89
+ children: e.status
90
+ })
91
+ ]
92
+ }, e.id))] })]
93
+ })
94
+ }),
95
+ _ && /* @__PURE__ */ c("aside", {
96
+ "data-testid": "security-finding-drawer",
97
+ className: "fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-elevated border-l border-border-default shadow-lg z-50 overflow-y-auto",
98
+ children: [/* @__PURE__ */ c("div", {
99
+ className: "flex items-center justify-between px-4 py-3 border-b border-border-subtle",
100
+ children: [/* @__PURE__ */ s("h3", {
101
+ className: "text-sm font-semibold text-text-primary",
102
+ children: _.title
103
+ }), /* @__PURE__ */ s("button", {
104
+ type: "button",
105
+ "aria-label": "Close drawer",
106
+ onClick: () => v(null),
107
+ className: "text-text-secondary hover:text-text-primary",
108
+ children: /* @__PURE__ */ s(i, { className: "h-4 w-4" })
109
+ })]
110
+ }), /* @__PURE__ */ c("div", {
111
+ className: "p-4 space-y-3 text-sm",
112
+ children: [
113
+ /* @__PURE__ */ s(d, {
114
+ label: "Rule",
115
+ value: _.ruleId
116
+ }),
117
+ /* @__PURE__ */ s(d, {
118
+ label: "Severity",
119
+ value: _.severity
120
+ }),
121
+ /* @__PURE__ */ s(d, {
122
+ label: "Category",
123
+ value: _.category
124
+ }),
125
+ /* @__PURE__ */ s(d, {
126
+ label: "Status",
127
+ value: _.status
128
+ }),
129
+ _.file && /* @__PURE__ */ s(d, {
130
+ label: "File",
131
+ value: `${_.file}:${_.line ?? "?"}`
132
+ }),
133
+ _.packageName && /* @__PURE__ */ s(d, {
134
+ label: "Package",
135
+ value: `${_.packageName}@${_.packageVersion ?? "?"}`
136
+ }),
137
+ _.fixedVersion && /* @__PURE__ */ s(d, {
138
+ label: "Fixed in",
139
+ value: _.fixedVersion
140
+ }),
141
+ _.redactedSample && /* @__PURE__ */ s(d, {
142
+ label: "Redacted sample",
143
+ value: _.redactedSample
144
+ }),
145
+ _.remediation && /* @__PURE__ */ c("div", { children: [/* @__PURE__ */ s("div", {
146
+ className: "text-xs uppercase text-text-tertiary mb-1",
147
+ children: "Remediation"
148
+ }), /* @__PURE__ */ s("div", {
149
+ className: "text-sm text-text-primary",
150
+ children: _.remediation
151
+ })] }),
152
+ /* @__PURE__ */ c("div", {
153
+ className: "flex gap-2 pt-3 border-t border-border-subtle",
154
+ children: [/* @__PURE__ */ s("button", {
155
+ type: "button",
156
+ "data-testid": "security-finding-action-accept-risk",
157
+ onClick: async () => {
158
+ await g({ variables: { input: {
159
+ findingFingerprint: _.fingerprint,
160
+ vibeId: u,
161
+ reason: "Accepted via Security tab",
162
+ expiresAt: new Date(Date.now() + 720 * 60 * 60 * 1e3).toISOString()
163
+ } } }), v(null), await m();
164
+ },
165
+ className: "flex-1 px-3 py-2 rounded-md border border-border-default text-text-primary hover:bg-bg-sunken text-xs",
166
+ children: "Accept risk (30d)"
167
+ }), /* @__PURE__ */ s("button", {
168
+ type: "button",
169
+ "data-testid": "security-finding-action-mark-fp",
170
+ onClick: async () => {
171
+ await h({ variables: {
172
+ id: _.id,
173
+ status: "FALSE_POSITIVE"
174
+ } }), v(null), await m();
175
+ },
176
+ className: "flex-1 px-3 py-2 rounded-md border border-border-default text-text-primary hover:bg-bg-sunken text-xs",
177
+ children: "Mark false positive"
178
+ })]
179
+ })
180
+ ]
181
+ })]
182
+ })
183
+ ]
184
+ });
185
+ }
186
+ function d({ label: e, value: t }) {
187
+ return /* @__PURE__ */ c("div", {
188
+ className: "flex justify-between gap-2 border-b border-border-subtle pb-1",
189
+ children: [/* @__PURE__ */ s("span", {
190
+ className: "text-xs uppercase text-text-tertiary",
191
+ children: e
192
+ }), /* @__PURE__ */ s("span", {
193
+ className: "text-text-primary text-xs text-right truncate",
194
+ children: t
195
+ })]
196
+ });
197
+ }
198
+ //#endregion
199
+ export { u as SecurityFindingsTable };
200
+
201
+ //# sourceMappingURL=SecurityFindingsTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityFindingsTable.js","names":[],"sources":["../../../src/components/security/SecurityFindingsTable.tsx"],"sourcesContent":["import { useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { X } from 'lucide-react';\n\nimport {\n CREATE_SECURITY_EXCEPTION_MUTATION,\n SECURITY_FINDINGS_QUERY,\n UPDATE_FINDING_STATUS_MUTATION,\n} from './gql';\nimport type { SecurityFindingRow, SecuritySeverity } from './types';\n\ninterface FindingsResp {\n securityFindings: { items: SecurityFindingRow[]; total: number; hasMore: boolean };\n}\n\nconst SEVERITY_BADGE: Record<SecuritySeverity, string> = {\n CRITICAL: 'bg-status-error-bg-subtle text-status-error-text',\n HIGH: 'bg-status-warning-bg-subtle text-status-warning-text',\n MEDIUM: 'bg-status-warning-bg-subtle text-status-warning-text opacity-80',\n LOW: 'bg-bg-sunken text-text-secondary',\n INFO: 'bg-bg-sunken text-text-tertiary',\n};\n\nexport function SecurityFindingsTable({ vibeId }: { vibeId: string }): React.ReactElement {\n const { data, loading, refetch } = useQuery<FindingsResp>(SECURITY_FINDINGS_QUERY, {\n variables: { filter: { vibeId }, pagination: { limit: 100 } },\n });\n const [updateStatus] = useMutation(UPDATE_FINDING_STATUS_MUTATION);\n const [createException] = useMutation(CREATE_SECURITY_EXCEPTION_MUTATION);\n const [selected, setSelected] = useState<SecurityFindingRow | null>(null);\n\n const items = data?.securityFindings.items ?? [];\n\n return (\n <div data-testid=\"security-findings-table\" className=\"space-y-2\">\n <div className=\"flex items-center justify-between\">\n <h4 className=\"text-sm font-semibold text-text-primary\">Findings</h4>\n <span className=\"text-xs text-text-secondary\">\n {loading ? 'Loading…' : `${data?.securityFindings.total ?? 0} total`}\n </span>\n </div>\n\n <div className=\"overflow-hidden rounded-md border border-border-default\">\n <table className=\"w-full text-sm\">\n <thead className=\"bg-bg-sunken text-text-secondary\">\n <tr>\n <th className=\"px-3 py-2 text-left text-xs\">Severity</th>\n <th className=\"px-3 py-2 text-left text-xs\">Title</th>\n <th className=\"px-3 py-2 text-left text-xs\">File / Package</th>\n <th className=\"px-3 py-2 text-left text-xs\">Status</th>\n </tr>\n </thead>\n <tbody>\n {items.length === 0 && (\n <tr>\n <td colSpan={4} className=\"px-3 py-6 text-center text-text-secondary text-xs\">\n No findings yet. Run a scan to populate this table.\n </td>\n </tr>\n )}\n {items.map((f) => (\n <tr\n key={f.id}\n data-finding-id={f.id}\n data-severity={f.severity.toLowerCase()}\n data-status={f.status.toLowerCase()}\n className=\"border-t border-border-subtle hover:bg-bg-sunken cursor-pointer\"\n onClick={() => setSelected(f)}\n >\n <td className=\"px-3 py-2\">\n <span\n className={`px-2 py-0.5 rounded text-xs font-semibold ${SEVERITY_BADGE[f.severity]}`}\n >\n {f.severity}\n </span>\n </td>\n <td className=\"px-3 py-2 text-text-primary truncate max-w-[24rem]\">{f.title}</td>\n <td className=\"px-3 py-2 text-text-secondary text-xs\">\n {f.packageName ? (\n <span>\n {f.packageName}@{f.packageVersion ?? '?'}\n </span>\n ) : (\n <span>\n {f.file}\n {f.line ? `:${f.line}` : ''}\n </span>\n )}\n </td>\n <td className=\"px-3 py-2 text-text-secondary text-xs\">{f.status}</td>\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n\n {selected && (\n <aside\n data-testid=\"security-finding-drawer\"\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-elevated border-l border-border-default shadow-lg z-50 overflow-y-auto\"\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-subtle\">\n <h3 className=\"text-sm font-semibold text-text-primary\">{selected.title}</h3>\n <button\n type=\"button\"\n aria-label=\"Close drawer\"\n onClick={() => setSelected(null)}\n className=\"text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n <div className=\"p-4 space-y-3 text-sm\">\n <Row label=\"Rule\" value={selected.ruleId} />\n <Row label=\"Severity\" value={selected.severity} />\n <Row label=\"Category\" value={selected.category} />\n <Row label=\"Status\" value={selected.status} />\n {selected.file && (\n <Row label=\"File\" value={`${selected.file}:${selected.line ?? '?'}`} />\n )}\n {selected.packageName && (\n <Row\n label=\"Package\"\n value={`${selected.packageName}@${selected.packageVersion ?? '?'}`}\n />\n )}\n {selected.fixedVersion && <Row label=\"Fixed in\" value={selected.fixedVersion} />}\n {selected.redactedSample && (\n <Row label=\"Redacted sample\" value={selected.redactedSample} />\n )}\n {selected.remediation && (\n <div>\n <div className=\"text-xs uppercase text-text-tertiary mb-1\">Remediation</div>\n <div className=\"text-sm text-text-primary\">{selected.remediation}</div>\n </div>\n )}\n\n <div className=\"flex gap-2 pt-3 border-t border-border-subtle\">\n <button\n type=\"button\"\n data-testid=\"security-finding-action-accept-risk\"\n onClick={async () => {\n await createException({\n variables: {\n input: {\n findingFingerprint: selected.fingerprint,\n vibeId,\n reason: 'Accepted via Security tab',\n expiresAt: new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString(),\n },\n },\n });\n setSelected(null);\n await refetch();\n }}\n className=\"flex-1 px-3 py-2 rounded-md border border-border-default text-text-primary hover:bg-bg-sunken text-xs\"\n >\n Accept risk (30d)\n </button>\n <button\n type=\"button\"\n data-testid=\"security-finding-action-mark-fp\"\n onClick={async () => {\n await updateStatus({\n variables: { id: selected.id, status: 'FALSE_POSITIVE' },\n });\n setSelected(null);\n await refetch();\n }}\n className=\"flex-1 px-3 py-2 rounded-md border border-border-default text-text-primary hover:bg-bg-sunken text-xs\"\n >\n Mark false positive\n </button>\n </div>\n </div>\n </aside>\n )}\n </div>\n );\n}\n\nfunction Row({ label, value }: { label: string; value: string }): React.ReactElement {\n return (\n <div className=\"flex justify-between gap-2 border-b border-border-subtle pb-1\">\n <span className=\"text-xs uppercase text-text-tertiary\">{label}</span>\n <span className=\"text-text-primary text-xs text-right truncate\">{value}</span>\n </div>\n );\n}\n"],"mappings":"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"}
@@ -0,0 +1,67 @@
1
+ import "react";
2
+ import { Settings as e } from "lucide-react";
3
+ import { jsx as t, jsxs as n } from "react/jsx-runtime";
4
+ //#region src/components/security/SecurityHeaderStrip.tsx
5
+ function r({ policyLevel: r, totals: i, lastScanAt: a, onPolicyChange: o, onOpenSettings: s, canEditPolicy: c }) {
6
+ return /* @__PURE__ */ n("div", {
7
+ "data-testid": "security-header",
8
+ className: "flex flex-wrap items-center justify-between gap-3 p-3 rounded-md border border-border-default bg-bg-surface",
9
+ children: [/* @__PURE__ */ n("div", {
10
+ className: "flex items-center gap-3",
11
+ children: [/* @__PURE__ */ n("span", {
12
+ className: `px-3 py-1 rounded-full text-xs font-semibold ${i.critical > 0 ? "bg-status-error-bg-subtle text-status-error-text" : i.high > 0 ? "bg-status-warning-bg-subtle text-status-warning-text" : "bg-status-success-bg-subtle text-status-success-text"}`,
13
+ children: [
14
+ i.critical > 0 && `${i.critical} critical`,
15
+ i.critical === 0 && i.high > 0 && `${i.high} high`,
16
+ i.critical === 0 && i.high === 0 && "clean"
17
+ ]
18
+ }), /* @__PURE__ */ t("span", {
19
+ className: "text-xs text-text-secondary",
20
+ children: a ? `Last scan: ${new Date(a).toLocaleString()}` : "No scans yet"
21
+ })]
22
+ }), /* @__PURE__ */ n("div", {
23
+ className: "flex items-center gap-2",
24
+ children: [/* @__PURE__ */ n("label", {
25
+ "data-testid": "security-policy-selector",
26
+ className: `flex items-center gap-1 text-xs px-2 py-1 rounded-md bg-bg-sunken border border-border-subtle ${c ? "" : "opacity-50 cursor-not-allowed"}`,
27
+ children: [/* @__PURE__ */ t("span", {
28
+ className: "text-text-tertiary",
29
+ children: "Policy:"
30
+ }), /* @__PURE__ */ n("select", {
31
+ value: r,
32
+ disabled: !c,
33
+ onChange: (e) => o(e.target.value),
34
+ className: "bg-transparent text-text-primary text-xs outline-none",
35
+ "aria-label": "Security policy level",
36
+ children: [
37
+ /* @__PURE__ */ t("option", {
38
+ "data-testid": "security-policy-option-advisory",
39
+ value: "ADVISORY",
40
+ children: "advisory"
41
+ }),
42
+ /* @__PURE__ */ t("option", {
43
+ "data-testid": "security-policy-option-standard",
44
+ value: "WARN",
45
+ children: "warn"
46
+ }),
47
+ /* @__PURE__ */ t("option", {
48
+ "data-testid": "security-policy-option-strict",
49
+ value: "BLOCK",
50
+ children: "block"
51
+ })
52
+ ]
53
+ })]
54
+ }), /* @__PURE__ */ t("button", {
55
+ type: "button",
56
+ onClick: s,
57
+ className: "p-1.5 rounded-md hover:bg-bg-sunken text-text-secondary hover:text-text-primary",
58
+ "aria-label": "Open security settings",
59
+ children: /* @__PURE__ */ t(e, { className: "h-4 w-4" })
60
+ })]
61
+ })]
62
+ });
63
+ }
64
+ //#endregion
65
+ export { r as SecurityHeaderStrip };
66
+
67
+ //# sourceMappingURL=SecurityHeaderStrip.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityHeaderStrip.js","names":[],"sources":["../../../src/components/security/SecurityHeaderStrip.tsx"],"sourcesContent":["import React from 'react';\nimport { Settings } from 'lucide-react';\n\nimport type { SecurityPolicyLevel, SecuritySummary } from './types';\n\ninterface Props {\n policyLevel: SecurityPolicyLevel;\n totals: SecuritySummary;\n lastScanAt?: string | null;\n onPolicyChange: (level: SecurityPolicyLevel) => void;\n onOpenSettings: () => void;\n canEditPolicy: boolean;\n}\n\nexport function SecurityHeaderStrip({\n policyLevel,\n totals,\n lastScanAt,\n onPolicyChange,\n onOpenSettings,\n canEditPolicy,\n}: Props): React.ReactElement {\n const chipClass =\n totals.critical > 0\n ? 'bg-status-error-bg-subtle text-status-error-text'\n : totals.high > 0\n ? 'bg-status-warning-bg-subtle text-status-warning-text'\n : 'bg-status-success-bg-subtle text-status-success-text';\n\n return (\n <div\n data-testid=\"security-header\"\n className=\"flex flex-wrap items-center justify-between gap-3 p-3 rounded-md border border-border-default bg-bg-surface\"\n >\n <div className=\"flex items-center gap-3\">\n <span className={`px-3 py-1 rounded-full text-xs font-semibold ${chipClass}`}>\n {totals.critical > 0 && `${totals.critical} critical`}\n {totals.critical === 0 && totals.high > 0 && `${totals.high} high`}\n {totals.critical === 0 && totals.high === 0 && 'clean'}\n </span>\n <span className=\"text-xs text-text-secondary\">\n {lastScanAt ? `Last scan: ${new Date(lastScanAt).toLocaleString()}` : 'No scans yet'}\n </span>\n </div>\n\n <div className=\"flex items-center gap-2\">\n <label\n data-testid=\"security-policy-selector\"\n className={`flex items-center gap-1 text-xs px-2 py-1 rounded-md bg-bg-sunken border border-border-subtle ${\n canEditPolicy ? '' : 'opacity-50 cursor-not-allowed'\n }`}\n >\n <span className=\"text-text-tertiary\">Policy:</span>\n <select\n value={policyLevel}\n disabled={!canEditPolicy}\n onChange={(e) => onPolicyChange(e.target.value as SecurityPolicyLevel)}\n className=\"bg-transparent text-text-primary text-xs outline-none\"\n aria-label=\"Security policy level\"\n >\n <option data-testid=\"security-policy-option-advisory\" value=\"ADVISORY\">\n advisory\n </option>\n <option data-testid=\"security-policy-option-standard\" value=\"WARN\">\n warn\n </option>\n <option data-testid=\"security-policy-option-strict\" value=\"BLOCK\">\n block\n </option>\n </select>\n </label>\n <button\n type=\"button\"\n onClick={onOpenSettings}\n className=\"p-1.5 rounded-md hover:bg-bg-sunken text-text-secondary hover:text-text-primary\"\n aria-label=\"Open security settings\"\n >\n <Settings className=\"h-4 w-4\" />\n </button>\n </div>\n </div>\n );\n}\n"],"mappings":";;;;AAcA,SAAgB,EAAoB,EAClC,gBACA,WACA,eACA,mBACA,mBACA,oBAC4B;AAQ5B,QACE,kBAAC,OAAD;EACE,eAAY;EACZ,WAAU;YAFZ,CAIE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,QAAD;IAAM,WAAW,gDAZrB,EAAO,WAAW,IACd,qDACA,EAAO,OAAO,IACZ,yDACA;cAQF;KACG,EAAO,WAAW,KAAK,GAAG,EAAO,SAAS;KAC1C,EAAO,aAAa,KAAK,EAAO,OAAO,KAAK,GAAG,EAAO,KAAK;KAC3D,EAAO,aAAa,KAAK,EAAO,SAAS,KAAK;KAC1C;OACP,kBAAC,QAAD;IAAM,WAAU;cACb,IAAa,cAAc,IAAI,KAAK,EAAW,CAAC,gBAAgB,KAAK;IACjE,CAAA,CACH;MAEN,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,SAAD;IACE,eAAY;IACZ,WAAW,iGACT,IAAgB,KAAK;cAHzB,CAME,kBAAC,QAAD;KAAM,WAAU;eAAqB;KAAc,CAAA,EACnD,kBAAC,UAAD;KACE,OAAO;KACP,UAAU,CAAC;KACX,WAAW,MAAM,EAAe,EAAE,OAAO,MAA6B;KACtE,WAAU;KACV,cAAW;eALb;MAOE,kBAAC,UAAD;OAAQ,eAAY;OAAkC,OAAM;iBAAW;OAE9D,CAAA;MACT,kBAAC,UAAD;OAAQ,eAAY;OAAkC,OAAM;iBAAO;OAE1D,CAAA;MACT,kBAAC,UAAD;OAAQ,eAAY;OAAgC,OAAM;iBAAQ;OAEzD,CAAA;MACF;OACH;OACR,kBAAC,UAAD;IACE,MAAK;IACL,SAAS;IACT,WAAU;IACV,cAAW;cAEX,kBAAC,GAAD,EAAU,WAAU,WAAY,CAAA;IACzB,CAAA,CACL;KACF"}
@@ -0,0 +1,66 @@
1
+ import { ALL_STAGES as e, deriveStageStatus as t } from "./types.js";
2
+ import "react";
3
+ import { jsx as n, jsxs as r } from "react/jsx-runtime";
4
+ //#region src/components/security/SecurityLifecycleStepper.tsx
5
+ var i = {
6
+ "never-run": "bg-bg-sunken text-text-muted",
7
+ "in-progress": "bg-status-info-bg-subtle text-status-info-text animate-pulse",
8
+ passed: "bg-status-success-bg-subtle text-status-success-text",
9
+ warn: "bg-status-warning-bg-subtle text-status-warning-text",
10
+ failed: "bg-status-error-bg-subtle text-status-error-text",
11
+ skipped: "bg-bg-sunken text-text-tertiary"
12
+ };
13
+ function a({ status: a, activeStageId: s, onSelectStage: c }) {
14
+ return /* @__PURE__ */ n("div", {
15
+ "data-testid": "security-stepper",
16
+ className: "overflow-x-auto bg-bg-sunken border border-border-default rounded-md p-3",
17
+ children: /* @__PURE__ */ n("div", {
18
+ className: "flex gap-2 min-w-max",
19
+ children: e.map((e) => {
20
+ let l = a?.perStage?.[e.id], u = t(l ?? {}), d = l?.lastRun?.summary, f = s === e.id;
21
+ return /* @__PURE__ */ r("button", {
22
+ type: "button",
23
+ "data-testid": `security-stage-node-${e.slug}`,
24
+ "data-stage": e.slug,
25
+ "data-status": u,
26
+ onClick: () => c(e.id),
27
+ className: `flex flex-col items-start gap-1 px-3 py-2 rounded-md border min-w-[8rem] text-left transition-colors ${i[u]} ${f ? "border-action-primary-bg ring-1 ring-action-primary-bg" : "border-border-subtle hover:border-border-default"}`,
28
+ children: [
29
+ /* @__PURE__ */ n("span", {
30
+ className: "text-xs font-semibold uppercase tracking-wide",
31
+ children: e.label
32
+ }),
33
+ /* @__PURE__ */ n("span", {
34
+ className: "text-xs text-text-secondary",
35
+ children: o(u)
36
+ }),
37
+ d && (d.critical > 0 || d.high > 0) ? /* @__PURE__ */ r("span", {
38
+ className: "text-xs",
39
+ children: [d.critical > 0 && /* @__PURE__ */ r("span", {
40
+ className: "text-status-error-text mr-1",
41
+ children: [d.critical, "c"]
42
+ }), d.high > 0 && /* @__PURE__ */ r("span", {
43
+ className: "text-status-warning-text mr-1",
44
+ children: [d.high, "h"]
45
+ })]
46
+ }) : null
47
+ ]
48
+ }, e.id);
49
+ })
50
+ })
51
+ });
52
+ }
53
+ function o(e) {
54
+ switch (e) {
55
+ case "never-run": return "never";
56
+ case "in-progress": return "running";
57
+ case "passed": return "passed";
58
+ case "warn": return "warn";
59
+ case "failed": return "failed";
60
+ case "skipped": return "skipped";
61
+ }
62
+ }
63
+ //#endregion
64
+ export { a as SecurityLifecycleStepper };
65
+
66
+ //# sourceMappingURL=SecurityLifecycleStepper.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityLifecycleStepper.js","names":[],"sources":["../../../src/components/security/SecurityLifecycleStepper.tsx"],"sourcesContent":["import React from 'react';\n/**\n * Lifecycle stepper — 14 horizontal nodes, each with status + counts.\n * Uses only semantic tokens.\n */\nimport {\n ALL_STAGES,\n deriveStageStatus,\n type SecurityStage,\n type SecurityStatusPayload,\n type StageNodeStatus,\n} from './types';\n\ninterface StepperProps {\n status?: SecurityStatusPayload;\n activeStageId: SecurityStage | null;\n onSelectStage: (id: SecurityStage) => void;\n}\n\nconst STATUS_BG: Record<StageNodeStatus, string> = {\n 'never-run': 'bg-bg-sunken text-text-muted',\n 'in-progress': 'bg-status-info-bg-subtle text-status-info-text animate-pulse',\n passed: 'bg-status-success-bg-subtle text-status-success-text',\n warn: 'bg-status-warning-bg-subtle text-status-warning-text',\n failed: 'bg-status-error-bg-subtle text-status-error-text',\n skipped: 'bg-bg-sunken text-text-tertiary',\n};\n\nexport function SecurityLifecycleStepper({\n status,\n activeStageId,\n onSelectStage,\n}: StepperProps): React.ReactElement {\n return (\n <div\n data-testid=\"security-stepper\"\n className=\"overflow-x-auto bg-bg-sunken border border-border-default rounded-md p-3\"\n >\n <div className=\"flex gap-2 min-w-max\">\n {ALL_STAGES.map((s) => {\n const perStage = status?.perStage?.[s.id];\n const nodeStatus = deriveStageStatus(perStage ?? {});\n const counts = perStage?.lastRun?.summary;\n const isActive = activeStageId === s.id;\n return (\n <button\n key={s.id}\n type=\"button\"\n data-testid={`security-stage-node-${s.slug}`}\n data-stage={s.slug}\n data-status={nodeStatus}\n onClick={() => onSelectStage(s.id)}\n className={`flex flex-col items-start gap-1 px-3 py-2 rounded-md border min-w-[8rem] text-left transition-colors ${STATUS_BG[nodeStatus]} ${\n isActive\n ? 'border-action-primary-bg ring-1 ring-action-primary-bg'\n : 'border-border-subtle hover:border-border-default'\n }`}\n >\n <span className=\"text-xs font-semibold uppercase tracking-wide\">{s.label}</span>\n <span className=\"text-xs text-text-secondary\">{labelFor(nodeStatus)}</span>\n {counts && (counts.critical > 0 || counts.high > 0) ? (\n <span className=\"text-xs\">\n {counts.critical > 0 && (\n <span className=\"text-status-error-text mr-1\">{counts.critical}c</span>\n )}\n {counts.high > 0 && (\n <span className=\"text-status-warning-text mr-1\">{counts.high}h</span>\n )}\n </span>\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n}\n\nfunction labelFor(s: StageNodeStatus): string {\n switch (s) {\n case 'never-run':\n return 'never';\n case 'in-progress':\n return 'running';\n case 'passed':\n return 'passed';\n case 'warn':\n return 'warn';\n case 'failed':\n return 'failed';\n case 'skipped':\n return 'skipped';\n }\n}\n"],"mappings":";;;;AAmBA,IAAM,IAA6C;CACjD,aAAa;CACb,eAAe;CACf,QAAQ;CACR,MAAM;CACN,QAAQ;CACR,SAAS;CACV;AAED,SAAgB,EAAyB,EACvC,WACA,kBACA,oBACmC;AACnC,QACE,kBAAC,OAAD;EACE,eAAY;EACZ,WAAU;YAEV,kBAAC,OAAD;GAAK,WAAU;aACZ,EAAW,KAAK,MAAM;IACrB,IAAM,IAAW,GAAQ,WAAW,EAAE,KAChC,IAAa,EAAkB,KAAY,EAAE,CAAC,EAC9C,IAAS,GAAU,SAAS,SAC5B,IAAW,MAAkB,EAAE;AACrC,WACE,kBAAC,UAAD;KAEE,MAAK;KACL,eAAa,uBAAuB,EAAE;KACtC,cAAY,EAAE;KACd,eAAa;KACb,eAAe,EAAc,EAAE,GAAG;KAClC,WAAW,wGAAwG,EAAU,GAAY,GACvI,IACI,2DACA;eAVR;MAaE,kBAAC,QAAD;OAAM,WAAU;iBAAiD,EAAE;OAAa,CAAA;MAChF,kBAAC,QAAD;OAAM,WAAU;iBAA+B,EAAS,EAAW;OAAQ,CAAA;MAC1E,MAAW,EAAO,WAAW,KAAK,EAAO,OAAO,KAC/C,kBAAC,QAAD;OAAM,WAAU;iBAAhB,CACG,EAAO,WAAW,KACjB,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAA+C,EAAO,UAAS,IAAQ;WAExE,EAAO,OAAO,KACb,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CAAiD,EAAO,MAAK,IAAQ;UAElE;WACL;MACG;OAxBF,EAAE,GAwBA;KAEX;GACE,CAAA;EACF,CAAA;;AAIV,SAAS,EAAS,GAA4B;AAC5C,SAAQ,GAAR;EACE,KAAK,YACH,QAAO;EACT,KAAK,cACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,OACH,QAAO;EACT,KAAK,SACH,QAAO;EACT,KAAK,UACH,QAAO"}
@@ -0,0 +1,112 @@
1
+ import { RUN_SECURITY_SCAN_MUTATION as e, SECURITY_PROVIDERS_QUERY as t, SET_SECURITY_PROVIDER_MUTATION as n } from "./gql.js";
2
+ import { STAGE_BY_ID as r } from "./types.js";
3
+ import { useMemo as i, useState as a } from "react";
4
+ import { X as o } from "lucide-react";
5
+ import { useMutation as s, useQuery as c } from "@apollo/client/react";
6
+ import { jsx as l, jsxs as u } from "react/jsx-runtime";
7
+ //#region src/components/security/SecurityProviderDrawer.tsx
8
+ function d({ vibeId: d, stage: f, onClose: p }) {
9
+ let { data: m, loading: h } = c(t), [g, { loading: _ }] = s(n), [v, { loading: y }] = s(e), [b, x] = a(null), S = i(() => (m?.securityProviders ?? []).filter((e) => e.stage === f && e.enabled), [m, f]), C = r.get(f), w = C?.slug ?? f.toLowerCase().replace(/_/g, "-");
10
+ return /* @__PURE__ */ u("aside", {
11
+ "data-testid": "security-provider-drawer",
12
+ className: "fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-elevated border-l border-border-default shadow-lg z-50 overflow-y-auto",
13
+ children: [/* @__PURE__ */ u("div", {
14
+ className: "flex items-center justify-between px-4 py-3 border-b border-border-subtle",
15
+ children: [/* @__PURE__ */ u("div", { children: [/* @__PURE__ */ l("h3", {
16
+ className: "text-sm font-semibold text-text-primary",
17
+ children: C?.label
18
+ }), /* @__PURE__ */ l("p", {
19
+ className: "text-xs text-text-secondary",
20
+ children: "Configure provider for this stage"
21
+ })] }), /* @__PURE__ */ l("button", {
22
+ type: "button",
23
+ "aria-label": "Close drawer",
24
+ onClick: p,
25
+ className: "text-text-secondary hover:text-text-primary",
26
+ children: /* @__PURE__ */ l(o, { className: "h-4 w-4" })
27
+ })]
28
+ }), /* @__PURE__ */ u("div", {
29
+ className: "p-4 space-y-4",
30
+ "data-testid": "security-provider-config-form",
31
+ children: [
32
+ h && /* @__PURE__ */ l("p", {
33
+ className: "text-xs text-text-secondary",
34
+ children: "Loading providers…"
35
+ }),
36
+ !h && S.length === 0 && /* @__PURE__ */ u("p", {
37
+ className: "text-xs text-text-secondary",
38
+ children: [
39
+ "No providers registered for this stage. Install one via",
40
+ " ",
41
+ /* @__PURE__ */ l("code", {
42
+ className: "text-text-primary",
43
+ children: "vibe plugin install …"
44
+ }),
45
+ "."
46
+ ]
47
+ }),
48
+ S.map((e) => /* @__PURE__ */ u("label", {
49
+ className: "flex items-start gap-3 p-3 rounded-md border border-border-subtle hover:border-border-default cursor-pointer",
50
+ children: [/* @__PURE__ */ l("input", {
51
+ type: "radio",
52
+ name: "provider",
53
+ value: e.providerName,
54
+ checked: b === e.providerName || b === null && e.isDefault,
55
+ onChange: () => x(e.providerName),
56
+ "aria-label": e.providerName
57
+ }), /* @__PURE__ */ u("div", {
58
+ className: "flex-1",
59
+ children: [/* @__PURE__ */ l("div", {
60
+ className: "text-sm font-medium text-text-primary",
61
+ children: e.providerName
62
+ }), /* @__PURE__ */ u("div", {
63
+ className: "text-xs text-text-secondary",
64
+ children: [
65
+ e.pluginName,
66
+ " · v",
67
+ e.toolVersion,
68
+ e.isDefault && /* @__PURE__ */ l("span", {
69
+ className: "ml-2 text-action-primary-bg",
70
+ children: "default"
71
+ })
72
+ ]
73
+ })]
74
+ })]
75
+ }, e.id)),
76
+ /* @__PURE__ */ u("div", {
77
+ className: "flex gap-2 pt-2 border-t border-border-subtle",
78
+ children: [/* @__PURE__ */ l("button", {
79
+ type: "button",
80
+ disabled: !b || _,
81
+ onClick: async () => {
82
+ b && await g({ variables: { input: {
83
+ vibeId: d,
84
+ stage: f,
85
+ providerKey: b,
86
+ config: {}
87
+ } } });
88
+ },
89
+ className: "flex-1 px-3 py-2 rounded-md bg-action-primary-bg text-action-primary-text disabled:opacity-50",
90
+ children: "Save"
91
+ }), /* @__PURE__ */ l("button", {
92
+ type: "button",
93
+ disabled: y,
94
+ "data-testid": `security-run-scan-${w}`,
95
+ onClick: async () => {
96
+ await v({ variables: {
97
+ vibeId: d,
98
+ stage: f
99
+ } });
100
+ },
101
+ className: "flex-1 px-3 py-2 rounded-md border border-border-default bg-bg-sunken hover:bg-bg-surface text-text-primary disabled:opacity-50",
102
+ children: "Run scan"
103
+ })]
104
+ })
105
+ ]
106
+ })]
107
+ });
108
+ }
109
+ //#endregion
110
+ export { d as SecurityProviderDrawer };
111
+
112
+ //# sourceMappingURL=SecurityProviderDrawer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityProviderDrawer.js","names":[],"sources":["../../../src/components/security/SecurityProviderDrawer.tsx"],"sourcesContent":["import { useMemo, useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\nimport { X } from 'lucide-react';\n\nimport {\n RUN_SECURITY_SCAN_MUTATION,\n SECURITY_PROVIDERS_QUERY,\n SET_SECURITY_PROVIDER_MUTATION,\n} from './gql';\nimport { STAGE_BY_ID, type SecurityProviderRow, type SecurityStage } from './types';\n\ninterface DrawerProps {\n vibeId: string;\n stage: SecurityStage;\n onClose: () => void;\n}\n\ninterface ProvidersResp {\n securityProviders: SecurityProviderRow[];\n}\n\nexport function SecurityProviderDrawer({\n vibeId,\n stage,\n onClose,\n}: DrawerProps): React.ReactElement {\n const { data, loading } = useQuery<ProvidersResp>(SECURITY_PROVIDERS_QUERY);\n const [setProvider, { loading: saving }] = useMutation(SET_SECURITY_PROVIDER_MUTATION);\n const [runScan, { loading: scanning }] = useMutation(RUN_SECURITY_SCAN_MUTATION);\n const [selected, setSelected] = useState<string | null>(null);\n\n const available = useMemo(\n () => (data?.securityProviders ?? []).filter((p) => p.stage === stage && p.enabled),\n [data, stage]\n );\n const stageInfo = STAGE_BY_ID.get(stage);\n const stageSlug = stageInfo?.slug ?? stage.toLowerCase().replace(/_/g, '-');\n\n return (\n <aside\n data-testid=\"security-provider-drawer\"\n className=\"fixed right-0 top-0 bottom-0 w-[28rem] bg-bg-elevated border-l border-border-default shadow-lg z-50 overflow-y-auto\"\n >\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-border-subtle\">\n <div>\n <h3 className=\"text-sm font-semibold text-text-primary\">{stageInfo?.label}</h3>\n <p className=\"text-xs text-text-secondary\">Configure provider for this stage</p>\n </div>\n <button\n type=\"button\"\n aria-label=\"Close drawer\"\n onClick={onClose}\n className=\"text-text-secondary hover:text-text-primary\"\n >\n <X className=\"h-4 w-4\" />\n </button>\n </div>\n\n <div className=\"p-4 space-y-4\" data-testid=\"security-provider-config-form\">\n {loading && <p className=\"text-xs text-text-secondary\">Loading providers…</p>}\n {!loading && available.length === 0 && (\n <p className=\"text-xs text-text-secondary\">\n No providers registered for this stage. Install one via{' '}\n <code className=\"text-text-primary\">vibe plugin install …</code>.\n </p>\n )}\n {available.map((p) => (\n <label\n key={p.id}\n className=\"flex items-start gap-3 p-3 rounded-md border border-border-subtle hover:border-border-default cursor-pointer\"\n >\n <input\n type=\"radio\"\n name=\"provider\"\n value={p.providerName}\n checked={selected === p.providerName || (selected === null && p.isDefault)}\n onChange={() => setSelected(p.providerName)}\n aria-label={p.providerName}\n />\n <div className=\"flex-1\">\n <div className=\"text-sm font-medium text-text-primary\">{p.providerName}</div>\n <div className=\"text-xs text-text-secondary\">\n {p.pluginName} · v{p.toolVersion}\n {p.isDefault && <span className=\"ml-2 text-action-primary-bg\">default</span>}\n </div>\n </div>\n </label>\n ))}\n\n <div className=\"flex gap-2 pt-2 border-t border-border-subtle\">\n <button\n type=\"button\"\n disabled={!selected || saving}\n onClick={async () => {\n if (!selected) return;\n await setProvider({\n variables: {\n input: { vibeId, stage, providerKey: selected, config: {} },\n },\n });\n }}\n className=\"flex-1 px-3 py-2 rounded-md bg-action-primary-bg text-action-primary-text disabled:opacity-50\"\n >\n Save\n </button>\n <button\n type=\"button\"\n disabled={scanning}\n data-testid={`security-run-scan-${stageSlug}`}\n onClick={async () => {\n await runScan({ variables: { vibeId, stage } });\n }}\n className=\"flex-1 px-3 py-2 rounded-md border border-border-default bg-bg-sunken hover:bg-bg-surface text-text-primary disabled:opacity-50\"\n >\n Run scan\n </button>\n </div>\n </div>\n </aside>\n );\n}\n"],"mappings":";;;;;;;AAqBA,SAAgB,EAAuB,EACrC,WACA,UACA,cACkC;CAClC,IAAM,EAAE,SAAM,eAAY,EAAwB,EAAyB,EACrE,CAAC,GAAa,EAAE,SAAS,OAAY,EAAY,EAA+B,EAChF,CAAC,GAAS,EAAE,SAAS,OAAc,EAAY,EAA2B,EAC1E,CAAC,GAAU,KAAe,EAAwB,KAAK,EAEvD,IAAY,SACT,GAAM,qBAAqB,EAAE,EAAE,QAAQ,MAAM,EAAE,UAAU,KAAS,EAAE,QAAQ,EACnF,CAAC,GAAM,EAAM,CACd,EACK,IAAY,EAAY,IAAI,EAAM,EAClC,IAAY,GAAW,QAAQ,EAAM,aAAa,CAAC,QAAQ,MAAM,IAAI;AAE3E,QACE,kBAAC,SAAD;EACE,eAAY;EACZ,WAAU;YAFZ,CAIE,kBAAC,OAAD;GAAK,WAAU;aAAf,CACE,kBAAC,OAAD,EAAA,UAAA,CACE,kBAAC,MAAD;IAAI,WAAU;cAA2C,GAAW;IAAW,CAAA,EAC/E,kBAAC,KAAD;IAAG,WAAU;cAA8B;IAAqC,CAAA,CAC5E,EAAA,CAAA,EACN,kBAAC,UAAD;IACE,MAAK;IACL,cAAW;IACX,SAAS;IACT,WAAU;cAEV,kBAAC,GAAD,EAAG,WAAU,WAAY,CAAA;IAClB,CAAA,CACL;MAEN,kBAAC,OAAD;GAAK,WAAU;GAAgB,eAAY;aAA3C;IACG,KAAW,kBAAC,KAAD;KAAG,WAAU;eAA8B;KAAsB,CAAA;IAC5E,CAAC,KAAW,EAAU,WAAW,KAChC,kBAAC,KAAD;KAAG,WAAU;eAAb;MAA2C;MACe;MACxD,kBAAC,QAAD;OAAM,WAAU;iBAAoB;OAA4B,CAAA;;MAC9D;;IAEL,EAAU,KAAK,MACd,kBAAC,SAAD;KAEE,WAAU;eAFZ,CAIE,kBAAC,SAAD;MACE,MAAK;MACL,MAAK;MACL,OAAO,EAAE;MACT,SAAS,MAAa,EAAE,gBAAiB,MAAa,QAAQ,EAAE;MAChE,gBAAgB,EAAY,EAAE,aAAa;MAC3C,cAAY,EAAE;MACd,CAAA,EACF,kBAAC,OAAD;MAAK,WAAU;gBAAf,CACE,kBAAC,OAAD;OAAK,WAAU;iBAAyC,EAAE;OAAmB,CAAA,EAC7E,kBAAC,OAAD;OAAK,WAAU;iBAAf;QACG,EAAE;QAAW;QAAK,EAAE;QACpB,EAAE,aAAa,kBAAC,QAAD;SAAM,WAAU;mBAA8B;SAAc,CAAA;QACxE;SACF;QACA;OAlBD,EAAE,GAkBD,CACR;IAEF,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,UAAD;MACE,MAAK;MACL,UAAU,CAAC,KAAY;MACvB,SAAS,YAAY;AACd,YACL,MAAM,EAAY,EAChB,WAAW,EACT,OAAO;QAAE;QAAQ;QAAO,aAAa;QAAU,QAAQ,EAAE;QAAE,EAC5D,EACF,CAAC;;MAEJ,WAAU;gBACX;MAEQ,CAAA,EACT,kBAAC,UAAD;MACE,MAAK;MACL,UAAU;MACV,eAAa,qBAAqB;MAClC,SAAS,YAAY;AACnB,aAAM,EAAQ,EAAE,WAAW;QAAE;QAAQ;QAAO,EAAE,CAAC;;MAEjD,WAAU;gBACX;MAEQ,CAAA,CACL;;IACF;KACA"}
@@ -0,0 +1,105 @@
1
+ import { ALL_SECURITY_SCANS_QUERY as e, SECURITY_STATUS_QUERY as t, UPDATE_FINDING_STATUS_MUTATION as n } from "./gql.js";
2
+ import { SecurityEmptyState as r } from "./SecurityEmptyState.js";
3
+ import { SecurityEvidenceList as i } from "./SecurityEvidenceList.js";
4
+ import { SecurityFindingsTable as a } from "./SecurityFindingsTable.js";
5
+ import { SecurityHeaderStrip as o } from "./SecurityHeaderStrip.js";
6
+ import { SecurityLifecycleStepper as s } from "./SecurityLifecycleStepper.js";
7
+ import { SecurityProviderDrawer as c } from "./SecurityProviderDrawer.js";
8
+ import { useMemo as l, useState as u } from "react";
9
+ import { useMutation as d, useQuery as f } from "@apollo/client/react";
10
+ import { Fragment as p, jsx as m, jsxs as h } from "react/jsx-runtime";
11
+ //#region src/components/security/SecurityTabPanel.tsx
12
+ function g({ vibeId: g }) {
13
+ let { data: _, loading: v, refetch: y } = f(t, {
14
+ variables: { vibeId: g },
15
+ fetchPolicy: "cache-and-network",
16
+ errorPolicy: "all"
17
+ }), { data: b } = f(e, {
18
+ variables: {
19
+ filter: { vibeId: g },
20
+ pagination: { limit: 10 }
21
+ },
22
+ fetchPolicy: "cache-and-network",
23
+ errorPolicy: "all"
24
+ }), [x] = d(n), [S, C] = u(null), w = _?.securityStatus, T = b?.allSecurityScans.items ?? [], E = w?.totals ?? {
25
+ critical: 0,
26
+ high: 0,
27
+ medium: 0,
28
+ low: 0,
29
+ info: 0
30
+ }, D = T.length > 0, O = l(() => {
31
+ let e = [];
32
+ for (let t of T) for (let n of t.evidence ?? []) e.push(n);
33
+ return e.slice(0, 6);
34
+ }, [T]);
35
+ return /* @__PURE__ */ h("div", {
36
+ "data-testid": "security-tab-panel",
37
+ className: "space-y-4",
38
+ children: [
39
+ /* @__PURE__ */ m(o, {
40
+ policyLevel: w?.policyLevel ?? "WARN",
41
+ totals: E,
42
+ lastScanAt: w?.lastScanAt,
43
+ canEditPolicy: !0,
44
+ onPolicyChange: (e) => {
45
+ x({ variables: {
46
+ id: "no-op",
47
+ status: "OPEN"
48
+ } }).catch(() => void 0);
49
+ },
50
+ onOpenSettings: () => C("PULL_REQUEST_FAST")
51
+ }),
52
+ /* @__PURE__ */ m(s, {
53
+ status: w,
54
+ activeStageId: S,
55
+ onSelectStage: (e) => C(e)
56
+ }),
57
+ !D && !v && /* @__PURE__ */ m(r, { onConfigureProviders: () => C("PULL_REQUEST_FAST") }),
58
+ D && /* @__PURE__ */ h(p, { children: [
59
+ /* @__PURE__ */ m(a, { vibeId: g }),
60
+ /* @__PURE__ */ h("div", {
61
+ className: "bg-bg-surface border border-border-default rounded-md p-3",
62
+ children: [/* @__PURE__ */ m("h4", {
63
+ className: "text-sm font-semibold text-text-primary mb-2",
64
+ children: "Recent evidence"
65
+ }), /* @__PURE__ */ m(i, { evidence: O })]
66
+ }),
67
+ /* @__PURE__ */ h("div", {
68
+ "data-testid": "security-run-history",
69
+ className: "bg-bg-surface border border-border-default rounded-md p-3",
70
+ children: [/* @__PURE__ */ m("h4", {
71
+ className: "text-sm font-semibold text-text-primary mb-2",
72
+ children: "Recent runs"
73
+ }), /* @__PURE__ */ m("ul", {
74
+ className: "space-y-1 text-sm",
75
+ children: T.slice(0, 5).map((e) => /* @__PURE__ */ h("li", {
76
+ className: "flex items-center justify-between gap-2 border-b border-border-subtle pb-1",
77
+ children: [/* @__PURE__ */ h("span", {
78
+ className: "text-text-primary text-xs",
79
+ children: [
80
+ e.stage,
81
+ " · ",
82
+ e.providerName
83
+ ]
84
+ }), /* @__PURE__ */ h("span", {
85
+ className: "text-xs text-text-tertiary",
86
+ children: [e.conclusion ?? e.status, e.durationMs ? ` · ${(e.durationMs / 1e3).toFixed(1)}s` : ""]
87
+ })]
88
+ }, e.id))
89
+ })]
90
+ })
91
+ ] }),
92
+ S && /* @__PURE__ */ m(c, {
93
+ vibeId: g,
94
+ stage: S,
95
+ onClose: () => {
96
+ C(null), y();
97
+ }
98
+ })
99
+ ]
100
+ });
101
+ }
102
+ //#endregion
103
+ export { g as SecurityTabPanel };
104
+
105
+ //# sourceMappingURL=SecurityTabPanel.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SecurityTabPanel.js","names":[],"sources":["../../../src/components/security/SecurityTabPanel.tsx"],"sourcesContent":["/**\n * Root Security tab panel — pulls status + scans + findings, owns the\n * provider drawer state, and composes the sub-components.\n */\nimport { useMemo, useState } from 'react';\nimport { useMutation, useQuery } from '@apollo/client/react';\n\nimport {\n ALL_SECURITY_SCANS_QUERY,\n SECURITY_STATUS_QUERY,\n UPDATE_FINDING_STATUS_MUTATION,\n} from './gql';\nimport { SecurityEmptyState } from './SecurityEmptyState';\nimport { SecurityEvidenceList } from './SecurityEvidenceList';\nimport { SecurityFindingsTable } from './SecurityFindingsTable';\nimport { SecurityHeaderStrip } from './SecurityHeaderStrip';\nimport { SecurityLifecycleStepper } from './SecurityLifecycleStepper';\nimport { SecurityProviderDrawer } from './SecurityProviderDrawer';\nimport type {\n SecurityPolicyLevel,\n SecurityScanRun,\n SecurityStage,\n SecurityStatusPayload,\n} from './types';\n\ninterface PanelProps {\n vibeId: string;\n}\n\ninterface StatusResp {\n securityStatus: SecurityStatusPayload;\n}\n\ninterface ScansResp {\n allSecurityScans: { items: SecurityScanRun[]; total: number; hasMore: boolean };\n}\n\nexport function SecurityTabPanel({ vibeId }: PanelProps): React.ReactElement {\n const {\n data: statusData,\n loading: statusLoading,\n refetch: refetchStatus,\n } = useQuery<StatusResp>(SECURITY_STATUS_QUERY, {\n variables: { vibeId },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n const { data: scansData } = useQuery<ScansResp>(ALL_SECURITY_SCANS_QUERY, {\n variables: { filter: { vibeId }, pagination: { limit: 10 } },\n fetchPolicy: 'cache-and-network',\n errorPolicy: 'all',\n });\n const [updateStatus] = useMutation(UPDATE_FINDING_STATUS_MUTATION);\n\n const [activeStage, setActiveStage] = useState<SecurityStage | null>(null);\n\n const status = statusData?.securityStatus;\n const recentScans = scansData?.allSecurityScans.items ?? [];\n const totals = status?.totals ?? { critical: 0, high: 0, medium: 0, low: 0, info: 0 };\n const hasAnyScans = recentScans.length > 0;\n\n const evidence = useMemo(() => {\n const flattened = [];\n for (const run of recentScans) {\n for (const ev of run.evidence ?? []) flattened.push(ev);\n }\n return flattened.slice(0, 6);\n }, [recentScans]);\n\n return (\n <div data-testid=\"security-tab-panel\" className=\"space-y-4\">\n <SecurityHeaderStrip\n policyLevel={status?.policyLevel ?? 'WARN'}\n totals={totals}\n lastScanAt={status?.lastScanAt}\n canEditPolicy={true}\n onPolicyChange={(level: SecurityPolicyLevel) => {\n void updateStatus({ variables: { id: 'no-op', status: 'OPEN' } }).catch(() => undefined);\n void level;\n }}\n onOpenSettings={() => setActiveStage('PULL_REQUEST_FAST')}\n />\n\n <SecurityLifecycleStepper\n status={status}\n activeStageId={activeStage}\n onSelectStage={(id) => setActiveStage(id)}\n />\n\n {!hasAnyScans && !statusLoading && (\n <SecurityEmptyState onConfigureProviders={() => setActiveStage('PULL_REQUEST_FAST')} />\n )}\n\n {hasAnyScans && (\n <>\n <SecurityFindingsTable vibeId={vibeId} />\n <div className=\"bg-bg-surface border border-border-default rounded-md p-3\">\n <h4 className=\"text-sm font-semibold text-text-primary mb-2\">Recent evidence</h4>\n <SecurityEvidenceList evidence={evidence} />\n </div>\n <div\n data-testid=\"security-run-history\"\n className=\"bg-bg-surface border border-border-default rounded-md p-3\"\n >\n <h4 className=\"text-sm font-semibold text-text-primary mb-2\">Recent runs</h4>\n <ul className=\"space-y-1 text-sm\">\n {recentScans.slice(0, 5).map((r) => (\n <li\n key={r.id}\n className=\"flex items-center justify-between gap-2 border-b border-border-subtle pb-1\"\n >\n <span className=\"text-text-primary text-xs\">\n {r.stage} · {r.providerName}\n </span>\n <span className=\"text-xs text-text-tertiary\">\n {r.conclusion ?? r.status}\n {r.durationMs ? ` · ${(r.durationMs / 1000).toFixed(1)}s` : ''}\n </span>\n </li>\n ))}\n </ul>\n </div>\n </>\n )}\n\n {activeStage && (\n <SecurityProviderDrawer\n vibeId={vibeId}\n stage={activeStage}\n onClose={() => {\n setActiveStage(null);\n void refetchStatus();\n }}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;AAqCA,SAAgB,EAAiB,EAAE,aAA0C;CAC3E,IAAM,EACJ,MAAM,GACN,SAAS,GACT,SAAS,MACP,EAAqB,GAAuB;EAC9C,WAAW,EAAE,WAAQ;EACrB,aAAa;EACb,aAAa;EACd,CAAC,EACI,EAAE,MAAM,MAAc,EAAoB,GAA0B;EACxE,WAAW;GAAE,QAAQ,EAAE,WAAQ;GAAE,YAAY,EAAE,OAAO,IAAI;GAAE;EAC5D,aAAa;EACb,aAAa;EACd,CAAC,EACI,CAAC,KAAgB,EAAY,EAA+B,EAE5D,CAAC,GAAa,KAAkB,EAA+B,KAAK,EAEpE,IAAS,GAAY,gBACrB,IAAc,GAAW,iBAAiB,SAAS,EAAE,EACrD,IAAS,GAAQ,UAAU;EAAE,UAAU;EAAG,MAAM;EAAG,QAAQ;EAAG,KAAK;EAAG,MAAM;EAAG,EAC/E,IAAc,EAAY,SAAS,GAEnC,IAAW,QAAc;EAC7B,IAAM,IAAY,EAAE;AACpB,OAAK,IAAM,KAAO,EAChB,MAAK,IAAM,KAAM,EAAI,YAAY,EAAE,CAAE,GAAU,KAAK,EAAG;AAEzD,SAAO,EAAU,MAAM,GAAG,EAAE;IAC3B,CAAC,EAAY,CAAC;AAEjB,QACE,kBAAC,OAAD;EAAK,eAAY;EAAqB,WAAU;YAAhD;GACE,kBAAC,GAAD;IACE,aAAa,GAAQ,eAAe;IAC5B;IACR,YAAY,GAAQ;IACpB,eAAe;IACf,iBAAiB,MAA+B;AACzC,OAAa,EAAE,WAAW;MAAE,IAAI;MAAS,QAAQ;MAAQ,EAAE,CAAC,CAAC,YAAY,KAAA,EAAU;;IAG1F,sBAAsB,EAAe,oBAAoB;IACzD,CAAA;GAEF,kBAAC,GAAD;IACU;IACR,eAAe;IACf,gBAAgB,MAAO,EAAe,EAAG;IACzC,CAAA;GAED,CAAC,KAAe,CAAC,KAChB,kBAAC,GAAD,EAAoB,4BAA4B,EAAe,oBAAoB,EAAI,CAAA;GAGxF,KACC,kBAAA,GAAA,EAAA,UAAA;IACE,kBAAC,GAAD,EAA+B,WAAU,CAAA;IACzC,kBAAC,OAAD;KAAK,WAAU;eAAf,CACE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAAoB,CAAA,EACjF,kBAAC,GAAD,EAAgC,aAAY,CAAA,CACxC;;IACN,kBAAC,OAAD;KACE,eAAY;KACZ,WAAU;eAFZ,CAIE,kBAAC,MAAD;MAAI,WAAU;gBAA+C;MAAgB,CAAA,EAC7E,kBAAC,MAAD;MAAI,WAAU;gBACX,EAAY,MAAM,GAAG,EAAE,CAAC,KAAK,MAC5B,kBAAC,MAAD;OAEE,WAAU;iBAFZ,CAIE,kBAAC,QAAD;QAAM,WAAU;kBAAhB;SACG,EAAE;SAAM;SAAI,EAAE;SACV;WACP,kBAAC,QAAD;QAAM,WAAU;kBAAhB,CACG,EAAE,cAAc,EAAE,QAClB,EAAE,aAAa,OAAO,EAAE,aAAa,KAAM,QAAQ,EAAE,CAAC,KAAK,GACvD;UACJ;SAVE,EAAE,GAUJ,CACL;MACC,CAAA,CACD;;IACL,EAAA,CAAA;GAGJ,KACC,kBAAC,GAAD;IACU;IACR,OAAO;IACP,eAAe;AAER,KADL,EAAe,KAAK,EACf,GAAe;;IAEtB,CAAA;GAEA"}