@clef-sh/ui 0.1.20 → 0.1.21
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/client/assets/index-DPWHjBbB.js +34 -0
- package/dist/client/assets/index-qsLTYpc9.css +2 -0
- package/dist/client/clef.svg +2 -0
- package/dist/client/index.html +3 -31
- package/dist/client-lib/components/Button.d.ts +1 -1
- package/dist/client-lib/components/Button.d.ts.map +1 -1
- package/dist/client-lib/components/CopyButton.d.ts.map +1 -1
- package/dist/client-lib/components/EnvBadge.d.ts.map +1 -1
- package/dist/client-lib/components/MatrixGrid.d.ts.map +1 -1
- package/dist/client-lib/components/Sidebar.d.ts +1 -1
- package/dist/client-lib/components/Sidebar.d.ts.map +1 -1
- package/dist/client-lib/components/StatusDot.d.ts.map +1 -1
- package/dist/client-lib/components/SyncPanel.d.ts.map +1 -1
- package/dist/client-lib/components/TopBar.d.ts +6 -0
- package/dist/client-lib/components/TopBar.d.ts.map +1 -1
- package/dist/client-lib/primitives/Badge.d.ts +11 -0
- package/dist/client-lib/primitives/Badge.d.ts.map +1 -0
- package/dist/client-lib/primitives/Card.d.ts +28 -0
- package/dist/client-lib/primitives/Card.d.ts.map +1 -0
- package/dist/client-lib/primitives/Dialog.d.ts +30 -0
- package/dist/client-lib/primitives/Dialog.d.ts.map +1 -0
- package/dist/client-lib/primitives/EmptyState.d.ts +10 -0
- package/dist/client-lib/primitives/EmptyState.d.ts.map +1 -0
- package/dist/client-lib/primitives/Field.d.ts +36 -0
- package/dist/client-lib/primitives/Field.d.ts.map +1 -0
- package/dist/client-lib/primitives/Input.d.ts +6 -0
- package/dist/client-lib/primitives/Input.d.ts.map +1 -0
- package/dist/client-lib/primitives/Stat.d.ts +11 -0
- package/dist/client-lib/primitives/Stat.d.ts.map +1 -0
- package/dist/client-lib/primitives/Table.d.ts +37 -0
- package/dist/client-lib/primitives/Table.d.ts.map +1 -0
- package/dist/client-lib/primitives/Tabs.d.ts +29 -0
- package/dist/client-lib/primitives/Tabs.d.ts.map +1 -0
- package/dist/client-lib/primitives/Toast.d.ts +16 -0
- package/dist/client-lib/primitives/Toast.d.ts.map +1 -0
- package/dist/client-lib/primitives/Toolbar.d.ts +29 -0
- package/dist/client-lib/primitives/Toolbar.d.ts.map +1 -0
- package/dist/client-lib/primitives/index.d.ts +23 -0
- package/dist/client-lib/primitives/index.d.ts.map +1 -0
- package/dist/client-lib/theme.d.ts +18 -41
- package/dist/client-lib/theme.d.ts.map +1 -1
- package/dist/server/api.d.ts.map +1 -1
- package/dist/server/api.js +215 -0
- package/dist/server/api.js.map +1 -1
- package/dist/server/envelope.d.ts +15 -0
- package/dist/server/envelope.d.ts.map +1 -0
- package/dist/server/envelope.js +310 -0
- package/dist/server/envelope.js.map +1 -0
- package/package.json +7 -2
- package/src/client/App.tsx +16 -41
- package/src/client/components/Button.tsx +13 -22
- package/src/client/components/CopyButton.tsx +5 -12
- package/src/client/components/EnvBadge.tsx +30 -15
- package/src/client/components/MatrixGrid.tsx +108 -252
- package/src/client/components/Sidebar.tsx +123 -199
- package/src/client/components/StatusDot.tsx +10 -15
- package/src/client/components/SyncPanel.tsx +14 -62
- package/src/client/components/TopBar.tsx +11 -36
- package/src/client/index.html +1 -30
- package/src/client/main.tsx +1 -0
- package/src/client/primitives/Badge.test.tsx +47 -0
- package/src/client/primitives/Badge.tsx +64 -0
- package/src/client/primitives/Card.test.tsx +50 -0
- package/src/client/primitives/Card.tsx +85 -0
- package/src/client/primitives/Dialog.test.tsx +55 -0
- package/src/client/primitives/Dialog.tsx +96 -0
- package/src/client/primitives/EmptyState.test.tsx +25 -0
- package/src/client/primitives/EmptyState.tsx +38 -0
- package/src/client/primitives/Field.test.tsx +46 -0
- package/src/client/primitives/Field.tsx +95 -0
- package/src/client/primitives/Input.tsx +26 -0
- package/src/client/primitives/Stat.test.tsx +32 -0
- package/src/client/primitives/Stat.tsx +52 -0
- package/src/client/primitives/Table.test.tsx +58 -0
- package/src/client/primitives/Table.tsx +113 -0
- package/src/client/primitives/Tabs.test.tsx +44 -0
- package/src/client/primitives/Tabs.tsx +100 -0
- package/src/client/primitives/Toast.test.tsx +77 -0
- package/src/client/primitives/Toast.tsx +89 -0
- package/src/client/primitives/Toolbar.test.tsx +50 -0
- package/src/client/primitives/Toolbar.tsx +86 -0
- package/src/client/primitives/index.ts +43 -0
- package/src/client/public/clef.svg +2 -0
- package/src/client/screens/BackendScreen.tsx +104 -363
- package/src/client/screens/DiffView.tsx +187 -378
- package/src/client/screens/EnvelopeScreen.test.tsx +542 -0
- package/src/client/screens/EnvelopeScreen.tsx +948 -0
- package/src/client/screens/GitLogView.tsx +48 -106
- package/src/client/screens/ImportScreen.tsx +105 -308
- package/src/client/screens/LintView.tsx +184 -379
- package/src/client/screens/ManifestScreen.tsx +283 -445
- package/src/client/screens/MatrixView.tsx +75 -91
- package/src/client/screens/NamespaceEditor.tsx +234 -609
- package/src/client/screens/PolicyView.tsx +183 -453
- package/src/client/screens/RecipientsScreen.tsx +71 -350
- package/src/client/screens/ResetScreen.tsx +67 -237
- package/src/client/screens/ScanScreen.tsx +85 -249
- package/src/client/screens/SchemaEditor.test.tsx +237 -0
- package/src/client/screens/SchemaEditor.tsx +435 -0
- package/src/client/screens/ServiceIdentitiesScreen.tsx +251 -788
- package/src/client/styles.css +77 -0
- package/src/client/theme.ts +27 -48
- package/dist/client/assets/index-Db6WgHgY.js +0 -38
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { ScanSearch } from "lucide-react";
|
|
3
3
|
import { apiFetch } from "../api";
|
|
4
|
-
import { TopBar } from "../components/TopBar";
|
|
5
4
|
import { Button } from "../components/Button";
|
|
6
5
|
import { CopyButton } from "../components/CopyButton";
|
|
6
|
+
import { Toolbar } from "../primitives";
|
|
7
7
|
import type { ScanResult } from "@clef-sh/core";
|
|
8
8
|
|
|
9
9
|
type ScanState = "idle" | "scanning" | "clean" | "issues";
|
|
@@ -87,70 +87,50 @@ export function ScanScreen() {
|
|
|
87
87
|
const totalIssues = (result?.matches.length ?? 0) + (result?.unencryptedMatrixFiles.length ?? 0);
|
|
88
88
|
const durationSec = result ? (result.durationMs / 1000).toFixed(1) : "0.0";
|
|
89
89
|
|
|
90
|
+
const filterButtons: ReadonlyArray<{ key: MatchFilter; label: string }> = [
|
|
91
|
+
{ key: "all", label: "All" },
|
|
92
|
+
{ key: "unencrypted", label: "Unencrypted" },
|
|
93
|
+
{ key: "pattern", label: "Pattern" },
|
|
94
|
+
{ key: "entropy", label: "Entropy" },
|
|
95
|
+
];
|
|
96
|
+
|
|
90
97
|
return (
|
|
91
|
-
<div
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
<div className="flex flex-1 flex-col overflow-hidden">
|
|
99
|
+
<Toolbar>
|
|
100
|
+
<div>
|
|
101
|
+
<Toolbar.Title>Scan</Toolbar.Title>
|
|
102
|
+
<Toolbar.Subtitle>clef scan — detect plaintext secrets</Toolbar.Subtitle>
|
|
103
|
+
</div>
|
|
104
|
+
{(scanState === "issues" || scanState === "clean") && (
|
|
105
|
+
<Toolbar.Actions>
|
|
106
|
+
<Button onClick={runScan}>↺ Scan again</Button>
|
|
107
|
+
</Toolbar.Actions>
|
|
108
|
+
)}
|
|
109
|
+
</Toolbar>
|
|
101
110
|
|
|
102
|
-
<div
|
|
111
|
+
<div className="flex-1 overflow-auto p-6">
|
|
103
112
|
{/* ── Idle ────────────────────────────────────────────────────── */}
|
|
104
113
|
{scanState === "idle" && (
|
|
105
|
-
<div
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
>
|
|
113
|
-
<div
|
|
114
|
-
style={{
|
|
115
|
-
fontFamily: theme.sans,
|
|
116
|
-
fontSize: 14,
|
|
117
|
-
color: theme.textMuted,
|
|
118
|
-
marginBottom: 24,
|
|
119
|
-
lineHeight: 1.6,
|
|
120
|
-
}}
|
|
121
|
-
>
|
|
122
|
-
Scans your repository for secrets that have escaped the Clef matrix — plaintext values
|
|
123
|
-
in files that should be encrypted.
|
|
114
|
+
<div data-testid="scan-idle" className="mx-auto max-w-[520px] pt-10">
|
|
115
|
+
<div className="mb-6 flex flex-col items-center gap-3 text-center">
|
|
116
|
+
<ScanSearch className="text-ash-dim" size={40} aria-hidden />
|
|
117
|
+
<div className="font-sans text-[14px] leading-relaxed text-ash">
|
|
118
|
+
Scans your repository for secrets that have escaped the Clef matrix — plaintext
|
|
119
|
+
values in files that should be encrypted.
|
|
120
|
+
</div>
|
|
124
121
|
</div>
|
|
125
122
|
|
|
126
123
|
{/* Severity selector */}
|
|
127
|
-
<div
|
|
128
|
-
<div
|
|
129
|
-
style={{
|
|
130
|
-
fontFamily: theme.sans,
|
|
131
|
-
fontSize: 12,
|
|
132
|
-
fontWeight: 600,
|
|
133
|
-
color: theme.textMuted,
|
|
134
|
-
marginBottom: 10,
|
|
135
|
-
letterSpacing: "0.05em",
|
|
136
|
-
textTransform: "uppercase",
|
|
137
|
-
}}
|
|
138
|
-
>
|
|
124
|
+
<div className="mb-6">
|
|
125
|
+
<div className="mb-2.5 font-sans text-[12px] font-semibold uppercase tracking-[0.05em] text-ash">
|
|
139
126
|
Severity
|
|
140
127
|
</div>
|
|
141
128
|
{(["all", "high"] as const).map((sev) => (
|
|
142
129
|
<label
|
|
143
130
|
key={sev}
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
gap: 10,
|
|
148
|
-
marginBottom: 8,
|
|
149
|
-
cursor: "pointer",
|
|
150
|
-
fontFamily: theme.sans,
|
|
151
|
-
fontSize: 13,
|
|
152
|
-
color: severity === sev ? theme.text : theme.textMuted,
|
|
153
|
-
}}
|
|
131
|
+
className={`mb-2 flex cursor-pointer items-center gap-2.5 font-sans text-[13px] ${
|
|
132
|
+
severity === sev ? "text-bone" : "text-ash"
|
|
133
|
+
}`}
|
|
154
134
|
>
|
|
155
135
|
<input
|
|
156
136
|
type="radio"
|
|
@@ -158,7 +138,7 @@ export function ScanScreen() {
|
|
|
158
138
|
value={sev}
|
|
159
139
|
checked={severity === sev}
|
|
160
140
|
onChange={() => setSeverity(sev)}
|
|
161
|
-
|
|
141
|
+
className="accent-gold-500"
|
|
162
142
|
data-testid={`severity-${sev}`}
|
|
163
143
|
/>
|
|
164
144
|
{sev === "all" ? "All (patterns + entropy)" : "High (patterns only)"}
|
|
@@ -170,20 +150,9 @@ export function ScanScreen() {
|
|
|
170
150
|
Scan repository
|
|
171
151
|
</Button>
|
|
172
152
|
|
|
173
|
-
<div
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
padding: "12px 16px",
|
|
177
|
-
background: theme.surface,
|
|
178
|
-
border: `1px solid ${theme.border}`,
|
|
179
|
-
borderRadius: 8,
|
|
180
|
-
fontFamily: theme.sans,
|
|
181
|
-
fontSize: 12,
|
|
182
|
-
color: theme.textMuted,
|
|
183
|
-
}}
|
|
184
|
-
>
|
|
185
|
-
ℹ️ <code style={{ fontFamily: theme.mono }}>clef scan</code> runs
|
|
186
|
-
automatically on every commit via the pre-commit hook.
|
|
153
|
+
<div className="mt-6 rounded-md border border-edge bg-ink-850 px-4 py-3 font-sans text-[12px] text-ash">
|
|
154
|
+
ℹ️ <code className="font-mono">clef scan</code> runs automatically on every commit via
|
|
155
|
+
the pre-commit hook.
|
|
187
156
|
</div>
|
|
188
157
|
</div>
|
|
189
158
|
)}
|
|
@@ -192,28 +161,10 @@ export function ScanScreen() {
|
|
|
192
161
|
{scanState === "scanning" && (
|
|
193
162
|
<div
|
|
194
163
|
data-testid="scan-scanning"
|
|
195
|
-
|
|
196
|
-
display: "flex",
|
|
197
|
-
flexDirection: "column",
|
|
198
|
-
alignItems: "center",
|
|
199
|
-
justifyContent: "center",
|
|
200
|
-
gap: 16,
|
|
201
|
-
paddingTop: 80,
|
|
202
|
-
}}
|
|
164
|
+
className="flex flex-col items-center justify-center gap-4 pt-20"
|
|
203
165
|
>
|
|
204
|
-
<div
|
|
205
|
-
|
|
206
|
-
width: 40,
|
|
207
|
-
height: 40,
|
|
208
|
-
borderRadius: "50%",
|
|
209
|
-
border: `3px solid ${theme.accent}44`,
|
|
210
|
-
borderTopColor: theme.accent,
|
|
211
|
-
animation: "spin 0.8s linear infinite",
|
|
212
|
-
}}
|
|
213
|
-
/>
|
|
214
|
-
<div style={{ fontFamily: theme.sans, fontSize: 14, color: theme.textMuted }}>
|
|
215
|
-
Scanning...
|
|
216
|
-
</div>
|
|
166
|
+
<div className="h-10 w-10 animate-spin rounded-full border-[3px] border-gold-500/30 border-t-gold-500" />
|
|
167
|
+
<div className="font-sans text-[14px] text-ash">Scanning...</div>
|
|
217
168
|
</div>
|
|
218
169
|
)}
|
|
219
170
|
|
|
@@ -221,40 +172,16 @@ export function ScanScreen() {
|
|
|
221
172
|
{scanState === "clean" && result && (
|
|
222
173
|
<div
|
|
223
174
|
data-testid="scan-clean"
|
|
224
|
-
|
|
225
|
-
display: "flex",
|
|
226
|
-
flexDirection: "column",
|
|
227
|
-
alignItems: "center",
|
|
228
|
-
justifyContent: "center",
|
|
229
|
-
gap: 14,
|
|
230
|
-
paddingTop: 60,
|
|
231
|
-
}}
|
|
175
|
+
className="flex flex-col items-center justify-center gap-3.5 pt-14"
|
|
232
176
|
>
|
|
233
|
-
<div
|
|
234
|
-
|
|
235
|
-
width: 56,
|
|
236
|
-
height: 56,
|
|
237
|
-
borderRadius: "50%",
|
|
238
|
-
background: theme.greenDim,
|
|
239
|
-
border: `1px solid ${theme.green}44`,
|
|
240
|
-
display: "flex",
|
|
241
|
-
alignItems: "center",
|
|
242
|
-
justifyContent: "center",
|
|
243
|
-
fontSize: 24,
|
|
244
|
-
color: theme.green,
|
|
245
|
-
}}
|
|
246
|
-
>
|
|
247
|
-
✓
|
|
177
|
+
<div className="flex h-14 w-14 items-center justify-center rounded-full border border-go-500/30 bg-go-500/15 text-[24px] text-go-500">
|
|
178
|
+
✓
|
|
248
179
|
</div>
|
|
249
|
-
<div
|
|
250
|
-
|
|
251
|
-
>
|
|
252
|
-
No issues found
|
|
253
|
-
</div>
|
|
254
|
-
<div style={{ fontFamily: theme.mono, fontSize: 12, color: theme.textMuted }}>
|
|
180
|
+
<div className="font-sans text-[16px] font-semibold text-go-500">No issues found</div>
|
|
181
|
+
<div className="font-mono text-[12px] text-ash">
|
|
255
182
|
{result.filesScanned} files scanned in {durationSec}s
|
|
256
183
|
</div>
|
|
257
|
-
<div
|
|
184
|
+
<div className="font-mono text-[11px] text-ash-dim">
|
|
258
185
|
Last run: {formatRunAt(lastRunAt)}
|
|
259
186
|
</div>
|
|
260
187
|
</div>
|
|
@@ -264,50 +191,30 @@ export function ScanScreen() {
|
|
|
264
191
|
{scanState === "issues" && result && (
|
|
265
192
|
<div>
|
|
266
193
|
{/* Summary */}
|
|
267
|
-
<div
|
|
268
|
-
|
|
269
|
-
display: "flex",
|
|
270
|
-
alignItems: "center",
|
|
271
|
-
gap: 12,
|
|
272
|
-
marginBottom: 20,
|
|
273
|
-
flexWrap: "wrap",
|
|
274
|
-
}}
|
|
275
|
-
>
|
|
276
|
-
<span
|
|
277
|
-
style={{ fontFamily: theme.sans, fontSize: 14, color: theme.text, fontWeight: 600 }}
|
|
278
|
-
>
|
|
194
|
+
<div className="mb-5 flex flex-wrap items-center gap-3">
|
|
195
|
+
<span className="font-sans text-[14px] font-semibold text-bone">
|
|
279
196
|
{totalIssues} issue{totalIssues !== 1 ? "s" : ""} found in {result.filesScanned}{" "}
|
|
280
197
|
files ({durationSec}s)
|
|
281
198
|
</span>
|
|
282
|
-
<div
|
|
199
|
+
<div className="flex-1" />
|
|
283
200
|
{/* Filter */}
|
|
284
|
-
{(
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
fontSize: 11,
|
|
302
|
-
fontWeight: filter === key ? 600 : 400,
|
|
303
|
-
color: filter === key ? theme.accent : theme.textMuted,
|
|
304
|
-
background: filter === key ? theme.accentDim : "transparent",
|
|
305
|
-
border: `1px solid ${filter === key ? theme.accent + "55" : theme.borderLight}`,
|
|
306
|
-
}}
|
|
307
|
-
>
|
|
308
|
-
{label}
|
|
309
|
-
</button>
|
|
310
|
-
))}
|
|
201
|
+
{filterButtons.map(({ key, label }) => {
|
|
202
|
+
const active = filter === key;
|
|
203
|
+
return (
|
|
204
|
+
<button
|
|
205
|
+
key={key}
|
|
206
|
+
data-testid={`filter-${key}`}
|
|
207
|
+
onClick={() => setFilter(key)}
|
|
208
|
+
className={`cursor-pointer rounded-md border px-2.5 py-1 font-mono text-[11px] ${
|
|
209
|
+
active
|
|
210
|
+
? "border-gold-500/30 bg-gold-500/10 font-semibold text-gold-500"
|
|
211
|
+
: "border-edge-strong bg-transparent text-ash"
|
|
212
|
+
}`}
|
|
213
|
+
>
|
|
214
|
+
{label}
|
|
215
|
+
</button>
|
|
216
|
+
);
|
|
217
|
+
})}
|
|
311
218
|
</div>
|
|
312
219
|
|
|
313
220
|
{/* Unencrypted matrix files */}
|
|
@@ -347,14 +254,7 @@ export function ScanScreen() {
|
|
|
347
254
|
))}
|
|
348
255
|
|
|
349
256
|
{dismissedCount > 0 && (
|
|
350
|
-
<div
|
|
351
|
-
style={{
|
|
352
|
-
fontFamily: theme.mono,
|
|
353
|
-
fontSize: 11,
|
|
354
|
-
color: theme.textDim,
|
|
355
|
-
marginTop: 12,
|
|
356
|
-
}}
|
|
357
|
-
>
|
|
257
|
+
<div className="mt-3 font-mono text-[11px] text-ash-dim">
|
|
358
258
|
{dismissedCount} dismissed
|
|
359
259
|
</div>
|
|
360
260
|
)}
|
|
@@ -384,55 +284,26 @@ function IssueCard({
|
|
|
384
284
|
onViewFile,
|
|
385
285
|
onDismiss,
|
|
386
286
|
}: IssueCardProps) {
|
|
387
|
-
const
|
|
287
|
+
const stripeClass = type === "error" ? "border-l-stop-500/40" : "border-l-warn-500/40";
|
|
288
|
+
const tagClass =
|
|
289
|
+
type === "error"
|
|
290
|
+
? "text-stop-500 bg-stop-500/15 border-stop-500/30"
|
|
291
|
+
: "text-warn-500 bg-warn-500/15 border-warn-500/30";
|
|
388
292
|
|
|
389
293
|
return (
|
|
390
294
|
<div
|
|
391
|
-
|
|
392
|
-
background: theme.surface,
|
|
393
|
-
border: `1px solid ${theme.border}`,
|
|
394
|
-
borderLeft: `3px solid ${color}66`,
|
|
395
|
-
borderRadius: 8,
|
|
396
|
-
padding: "14px 18px",
|
|
397
|
-
marginBottom: 12,
|
|
398
|
-
display: "flex",
|
|
399
|
-
alignItems: "flex-start",
|
|
400
|
-
gap: 14,
|
|
401
|
-
}}
|
|
295
|
+
className={`mb-3 flex items-start gap-3.5 rounded-md border border-edge border-l-[3px] bg-ink-850 px-4.5 py-3.5 ${stripeClass}`}
|
|
402
296
|
>
|
|
403
|
-
<div
|
|
297
|
+
<div className="min-w-0 flex-1">
|
|
404
298
|
{/* Type badge + file */}
|
|
405
|
-
<div
|
|
406
|
-
style={{
|
|
407
|
-
display: "flex",
|
|
408
|
-
alignItems: "center",
|
|
409
|
-
gap: 8,
|
|
410
|
-
marginBottom: 6,
|
|
411
|
-
flexWrap: "wrap",
|
|
412
|
-
}}
|
|
413
|
-
>
|
|
299
|
+
<div className="mb-1.5 flex flex-wrap items-center gap-2">
|
|
414
300
|
<span
|
|
415
|
-
|
|
416
|
-
fontFamily: theme.mono,
|
|
417
|
-
fontSize: 9,
|
|
418
|
-
fontWeight: 700,
|
|
419
|
-
color,
|
|
420
|
-
background: `${color}18`,
|
|
421
|
-
border: `1px solid ${color}33`,
|
|
422
|
-
borderRadius: 3,
|
|
423
|
-
padding: "2px 6px",
|
|
424
|
-
letterSpacing: "0.07em",
|
|
425
|
-
}}
|
|
301
|
+
className={`rounded-sm border px-1.5 py-0.5 font-mono text-[9px] font-bold tracking-[0.07em] ${tagClass}`}
|
|
426
302
|
>
|
|
427
303
|
{typeLabel}
|
|
428
304
|
</span>
|
|
429
305
|
<span
|
|
430
|
-
|
|
431
|
-
fontFamily: theme.mono,
|
|
432
|
-
fontSize: 12,
|
|
433
|
-
color: theme.accent,
|
|
434
|
-
cursor: onViewFile ? "pointer" : "default",
|
|
435
|
-
}}
|
|
306
|
+
className={`font-mono text-[12px] text-gold-500 ${onViewFile ? "cursor-pointer" : ""}`}
|
|
436
307
|
onClick={onViewFile}
|
|
437
308
|
role={onViewFile ? "button" : undefined}
|
|
438
309
|
tabIndex={onViewFile ? 0 : undefined}
|
|
@@ -449,30 +320,14 @@ function IssueCard({
|
|
|
449
320
|
</div>
|
|
450
321
|
|
|
451
322
|
{/* Message (preview) */}
|
|
452
|
-
<div
|
|
453
|
-
style={{ fontFamily: theme.mono, fontSize: 12, color: theme.text, marginBottom: 10 }}
|
|
454
|
-
data-testid="match-preview"
|
|
455
|
-
>
|
|
323
|
+
<div className="mb-2.5 font-mono text-[12px] text-bone" data-testid="match-preview">
|
|
456
324
|
{message}
|
|
457
325
|
</div>
|
|
458
326
|
|
|
459
327
|
{/* Fix command */}
|
|
460
|
-
<div
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
alignItems: "center",
|
|
464
|
-
gap: 8,
|
|
465
|
-
background: "#0D0F14",
|
|
466
|
-
border: `1px solid ${theme.borderLight}`,
|
|
467
|
-
borderRadius: 6,
|
|
468
|
-
padding: "6px 10px",
|
|
469
|
-
width: "fit-content",
|
|
470
|
-
}}
|
|
471
|
-
>
|
|
472
|
-
<span style={{ fontFamily: theme.mono, fontSize: 11, color: theme.green }}>$</span>
|
|
473
|
-
<span style={{ fontFamily: theme.mono, fontSize: 11, color: theme.text }}>
|
|
474
|
-
{fixCommand}
|
|
475
|
-
</span>
|
|
328
|
+
<div className="flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5">
|
|
329
|
+
<span className="font-mono text-[11px] text-go-500">$</span>
|
|
330
|
+
<span className="font-mono text-[11px] text-bone">{fixCommand}</span>
|
|
476
331
|
<CopyButton text={fixCommand} />
|
|
477
332
|
</div>
|
|
478
333
|
|
|
@@ -481,17 +336,7 @@ function IssueCard({
|
|
|
481
336
|
<button
|
|
482
337
|
data-testid="view-file-button"
|
|
483
338
|
onClick={onViewFile}
|
|
484
|
-
|
|
485
|
-
marginTop: 8,
|
|
486
|
-
background: "none",
|
|
487
|
-
border: `1px solid ${theme.borderLight}`,
|
|
488
|
-
borderRadius: 4,
|
|
489
|
-
cursor: "pointer",
|
|
490
|
-
color: theme.textMuted,
|
|
491
|
-
fontFamily: theme.sans,
|
|
492
|
-
fontSize: 11,
|
|
493
|
-
padding: "3px 8px",
|
|
494
|
-
}}
|
|
339
|
+
className="mt-2 cursor-pointer rounded-md border border-edge-strong bg-transparent px-2 py-0.5 font-sans text-[11px] text-ash"
|
|
495
340
|
>
|
|
496
341
|
View file
|
|
497
342
|
</button>
|
|
@@ -504,18 +349,9 @@ function IssueCard({
|
|
|
504
349
|
onClick={onDismiss}
|
|
505
350
|
title="Dismiss"
|
|
506
351
|
aria-label="Dismiss issue"
|
|
507
|
-
|
|
508
|
-
background: "none",
|
|
509
|
-
border: "none",
|
|
510
|
-
cursor: "pointer",
|
|
511
|
-
color: theme.textDim,
|
|
512
|
-
fontSize: 16,
|
|
513
|
-
flexShrink: 0,
|
|
514
|
-
padding: "0 4px",
|
|
515
|
-
lineHeight: 1,
|
|
516
|
-
}}
|
|
352
|
+
className="shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim"
|
|
517
353
|
>
|
|
518
|
-
|
|
354
|
+
×
|
|
519
355
|
</button>
|
|
520
356
|
)}
|
|
521
357
|
</div>
|