@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,10 +1,11 @@
|
|
|
1
1
|
import React, { useState, useEffect, useCallback } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { RefreshCw } 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 { EnvBadge } from "../components/EnvBadge";
|
|
7
6
|
import { CopyButton } from "../components/CopyButton";
|
|
7
|
+
import { Toolbar, Card, Badge, EmptyState } from "../primitives";
|
|
8
|
+
import type { BadgeTone } from "../primitives";
|
|
8
9
|
import type { ViewName } from "../components/Sidebar";
|
|
9
10
|
import type { LintResult, LintIssue } from "@clef-sh/core";
|
|
10
11
|
|
|
@@ -13,8 +14,77 @@ interface LintViewProps {
|
|
|
13
14
|
setNs: (ns: string) => void;
|
|
14
15
|
}
|
|
15
16
|
|
|
17
|
+
type Severity = "error" | "warning" | "info";
|
|
18
|
+
type Category = "matrix" | "schema" | "sops";
|
|
19
|
+
|
|
20
|
+
const SEVERITY_TW: Record<
|
|
21
|
+
Severity,
|
|
22
|
+
{
|
|
23
|
+
label: string;
|
|
24
|
+
icon: string;
|
|
25
|
+
text: string;
|
|
26
|
+
bg: string;
|
|
27
|
+
border: string;
|
|
28
|
+
rowStripe: string;
|
|
29
|
+
}
|
|
30
|
+
> = {
|
|
31
|
+
error: {
|
|
32
|
+
label: "Error",
|
|
33
|
+
icon: "✕",
|
|
34
|
+
text: "text-stop-500",
|
|
35
|
+
bg: "bg-stop-500/10",
|
|
36
|
+
border: "border-stop-500/30",
|
|
37
|
+
rowStripe: "border-l-[3px] border-l-stop-500/40",
|
|
38
|
+
},
|
|
39
|
+
warning: {
|
|
40
|
+
label: "Warning",
|
|
41
|
+
icon: "⚠",
|
|
42
|
+
text: "text-warn-500",
|
|
43
|
+
bg: "bg-warn-500/10",
|
|
44
|
+
border: "border-warn-500/30",
|
|
45
|
+
rowStripe: "border-l-[3px] border-l-warn-500/40",
|
|
46
|
+
},
|
|
47
|
+
info: {
|
|
48
|
+
label: "Info",
|
|
49
|
+
icon: "i",
|
|
50
|
+
text: "text-blue-400",
|
|
51
|
+
bg: "bg-blue-400/10",
|
|
52
|
+
border: "border-blue-400/30",
|
|
53
|
+
rowStripe: "border-l-[3px] border-l-blue-400/40",
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const CATEGORY_TW: Record<Category, { label: string; tone: BadgeTone }> = {
|
|
58
|
+
matrix: { label: "Matrix", tone: "gold" },
|
|
59
|
+
schema: { label: "Schema", tone: "blue" },
|
|
60
|
+
sops: { label: "SOPS", tone: "purple" },
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const FILTER_TW: Record<string, { text: string; bgActive: string; borderActive: string }> = {
|
|
64
|
+
all: {
|
|
65
|
+
text: "text-ash",
|
|
66
|
+
bgActive: "bg-ash/15",
|
|
67
|
+
borderActive: "border-ash/30",
|
|
68
|
+
},
|
|
69
|
+
error: {
|
|
70
|
+
text: "text-stop-500",
|
|
71
|
+
bgActive: "bg-stop-500/15",
|
|
72
|
+
borderActive: "border-stop-500/40",
|
|
73
|
+
},
|
|
74
|
+
warning: {
|
|
75
|
+
text: "text-warn-500",
|
|
76
|
+
bgActive: "bg-warn-500/15",
|
|
77
|
+
borderActive: "border-warn-500/40",
|
|
78
|
+
},
|
|
79
|
+
info: {
|
|
80
|
+
text: "text-blue-400",
|
|
81
|
+
bgActive: "bg-blue-400/15",
|
|
82
|
+
borderActive: "border-blue-400/40",
|
|
83
|
+
},
|
|
84
|
+
};
|
|
85
|
+
|
|
16
86
|
export function LintView({ setView, setNs }: LintViewProps) {
|
|
17
|
-
const [filter, setFilter] = useState("all");
|
|
87
|
+
const [filter, setFilter] = useState<string>("all");
|
|
18
88
|
const [dismissed, setDismissed] = useState<number[]>([]);
|
|
19
89
|
const [lintResult, setLintResult] = useState<LintResult | null>(null);
|
|
20
90
|
const [loading, setLoading] = useState(false);
|
|
@@ -60,123 +130,84 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
60
130
|
|
|
61
131
|
const allClear = visible.length === 0;
|
|
62
132
|
|
|
133
|
+
const severityFilters: Array<{
|
|
134
|
+
key: string;
|
|
135
|
+
label: string;
|
|
136
|
+
count: number;
|
|
137
|
+
}> = [
|
|
138
|
+
{ key: "all", label: "All issues", count: issues.length },
|
|
139
|
+
{ key: "error", label: "Errors", count: errors.length },
|
|
140
|
+
{ key: "warning", label: "Warnings", count: warnings.length },
|
|
141
|
+
{ key: "info", label: "Info", count: infos.length },
|
|
142
|
+
];
|
|
143
|
+
|
|
63
144
|
return (
|
|
64
|
-
<div
|
|
65
|
-
<
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
145
|
+
<div className="flex flex-1 flex-col overflow-hidden">
|
|
146
|
+
<Toolbar>
|
|
147
|
+
<div>
|
|
148
|
+
<Toolbar.Title>Lint</Toolbar.Title>
|
|
149
|
+
<Toolbar.Subtitle>clef lint — full repo health check</Toolbar.Subtitle>
|
|
150
|
+
</div>
|
|
151
|
+
<Toolbar.Actions>
|
|
152
|
+
<Button onClick={loadLint}>
|
|
153
|
+
<RefreshCw size={12} className="mr-1 inline-block align-[-2px]" />
|
|
154
|
+
Re-run
|
|
155
|
+
</Button>
|
|
156
|
+
{errors.length === 0 && <Button variant="primary">All clear — commit</Button>}
|
|
157
|
+
</Toolbar.Actions>
|
|
158
|
+
</Toolbar>
|
|
75
159
|
|
|
76
160
|
{/* Summary bar — only shown when there are issues */}
|
|
77
161
|
{!loading && !allClear && (
|
|
78
|
-
<div
|
|
79
|
-
style={{
|
|
80
|
-
padding: "14px 24px",
|
|
81
|
-
background: "#0D0F14",
|
|
82
|
-
borderBottom: `1px solid ${theme.border}`,
|
|
83
|
-
display: "flex",
|
|
84
|
-
alignItems: "center",
|
|
85
|
-
gap: 10,
|
|
86
|
-
flexWrap: "wrap",
|
|
87
|
-
}}
|
|
88
|
-
>
|
|
162
|
+
<div className="flex flex-wrap items-center gap-2.5 border-b border-edge bg-ink-800 px-6 py-3.5">
|
|
89
163
|
{/* Severity filters */}
|
|
90
|
-
{
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
key: "warning",
|
|
105
|
-
label: "Warnings",
|
|
106
|
-
count: warnings.length,
|
|
107
|
-
color: theme.yellow,
|
|
108
|
-
},
|
|
109
|
-
{
|
|
110
|
-
key: "info",
|
|
111
|
-
label: "Info",
|
|
112
|
-
count: infos.length,
|
|
113
|
-
color: theme.blue,
|
|
114
|
-
},
|
|
115
|
-
].map((f) => (
|
|
116
|
-
<button
|
|
117
|
-
key={f.key}
|
|
118
|
-
data-testid={`filter-${f.key}`}
|
|
119
|
-
onClick={() => setFilter(f.key)}
|
|
120
|
-
style={{
|
|
121
|
-
display: "flex",
|
|
122
|
-
alignItems: "center",
|
|
123
|
-
gap: 6,
|
|
124
|
-
padding: "5px 12px",
|
|
125
|
-
borderRadius: 20,
|
|
126
|
-
cursor: "pointer",
|
|
127
|
-
fontFamily: theme.sans,
|
|
128
|
-
fontSize: 12,
|
|
129
|
-
fontWeight: filter === f.key ? 600 : 400,
|
|
130
|
-
color: filter === f.key ? f.color : theme.textMuted,
|
|
131
|
-
background: filter === f.key ? `${f.color}18` : "transparent",
|
|
132
|
-
border: `1px solid ${filter === f.key ? `${f.color}55` : theme.border}`,
|
|
133
|
-
transition: "all 0.12s",
|
|
134
|
-
}}
|
|
135
|
-
>
|
|
136
|
-
<span
|
|
137
|
-
style={{
|
|
138
|
-
fontFamily: theme.mono,
|
|
139
|
-
fontSize: 11,
|
|
140
|
-
fontWeight: 700,
|
|
141
|
-
color: f.color,
|
|
142
|
-
}}
|
|
164
|
+
{severityFilters.map((f) => {
|
|
165
|
+
const tw = FILTER_TW[f.key];
|
|
166
|
+
const active = filter === f.key;
|
|
167
|
+
const baseClasses =
|
|
168
|
+
"flex items-center gap-1.5 rounded-pill border px-3 py-1 font-sans text-[12px] transition-colors";
|
|
169
|
+
const activeClasses = active
|
|
170
|
+
? `${tw.text} ${tw.bgActive} ${tw.borderActive} font-semibold`
|
|
171
|
+
: "text-ash border-edge font-normal hover:border-edge-strong";
|
|
172
|
+
return (
|
|
173
|
+
<button
|
|
174
|
+
key={f.key}
|
|
175
|
+
data-testid={`filter-${f.key}`}
|
|
176
|
+
onClick={() => setFilter(f.key)}
|
|
177
|
+
className={`${baseClasses} ${activeClasses}`}
|
|
143
178
|
>
|
|
144
|
-
{f.count}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
)
|
|
179
|
+
<span className={`font-mono text-[11px] font-bold ${tw.text}`}>{f.count}</span>
|
|
180
|
+
{f.label}
|
|
181
|
+
</button>
|
|
182
|
+
);
|
|
183
|
+
})}
|
|
149
184
|
|
|
150
|
-
<div
|
|
185
|
+
<div className="flex-1" />
|
|
151
186
|
|
|
152
187
|
{/* Category filters */}
|
|
153
188
|
{(["matrix", "schema", "sops"] as const).map((cat) => {
|
|
154
|
-
const m =
|
|
189
|
+
const m = CATEGORY_TW[cat];
|
|
190
|
+
const active = filter === cat;
|
|
155
191
|
return (
|
|
156
192
|
<button
|
|
157
193
|
key={cat}
|
|
158
|
-
onClick={() => setFilter(
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
fontSize: 10,
|
|
165
|
-
fontWeight: 600,
|
|
166
|
-
color: filter === cat ? m.color : theme.textDim,
|
|
167
|
-
background: filter === cat ? `${m.color}18` : "transparent",
|
|
168
|
-
border: `1px solid ${filter === cat ? `${m.color}55` : theme.borderLight}`,
|
|
169
|
-
letterSpacing: "0.06em",
|
|
170
|
-
}}
|
|
194
|
+
onClick={() => setFilter(active ? "all" : cat)}
|
|
195
|
+
className={`rounded-sm border px-2.5 py-1 font-mono text-[10px] font-semibold uppercase tracking-[0.06em] transition-colors ${
|
|
196
|
+
active
|
|
197
|
+
? "bg-ink-800 border-edge-strong text-bone"
|
|
198
|
+
: "border-edge-strong text-ash-dim hover:text-ash"
|
|
199
|
+
}`}
|
|
171
200
|
>
|
|
172
|
-
{m.
|
|
201
|
+
<Badge tone={m.tone} variant={active ? "solid" : "outline"}>
|
|
202
|
+
{m.label}
|
|
203
|
+
</Badge>
|
|
173
204
|
</button>
|
|
174
205
|
);
|
|
175
206
|
})}
|
|
176
207
|
</div>
|
|
177
208
|
)}
|
|
178
209
|
|
|
179
|
-
<div
|
|
210
|
+
<div className="flex-1 overflow-auto p-6">
|
|
180
211
|
{loading && (
|
|
181
212
|
<>
|
|
182
213
|
<style>{`
|
|
@@ -191,103 +222,35 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
191
222
|
0%, 100% { opacity: 0.4; }
|
|
192
223
|
50% { opacity: 1; }
|
|
193
224
|
}
|
|
225
|
+
.clef-scan-line { animation: clef-scan-line 1.8s ease-in-out infinite; transform-origin: left; opacity: 0; }
|
|
226
|
+
.clef-scan-line-0 { animation-delay: 0s; width: 120px; }
|
|
227
|
+
.clef-scan-line-1 { animation-delay: 0.3s; width: 90px; }
|
|
228
|
+
.clef-scan-line-2 { animation-delay: 0.6s; width: 105px; }
|
|
229
|
+
.clef-scan-glow { animation: clef-scan-glow 1.8s ease-in-out infinite; }
|
|
194
230
|
`}</style>
|
|
195
|
-
<div
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
justifyContent: "center",
|
|
200
|
-
padding: "48px 24px",
|
|
201
|
-
}}
|
|
202
|
-
>
|
|
203
|
-
<div
|
|
204
|
-
style={{
|
|
205
|
-
background: theme.surface,
|
|
206
|
-
border: `1px solid ${theme.border}`,
|
|
207
|
-
borderRadius: 10,
|
|
208
|
-
padding: "28px 40px",
|
|
209
|
-
textAlign: "center",
|
|
210
|
-
minWidth: 200,
|
|
211
|
-
}}
|
|
212
|
-
>
|
|
213
|
-
<div style={{ marginBottom: 16, display: "flex", flexDirection: "column", gap: 6 }}>
|
|
214
|
-
{[0, 0.3, 0.6].map((delay, i) => (
|
|
231
|
+
<div className="flex items-center justify-center px-6 py-12">
|
|
232
|
+
<div className="min-w-[200px] rounded-card border border-edge bg-ink-850 px-10 py-7 text-center">
|
|
233
|
+
<div className="mb-4 flex flex-col gap-1.5">
|
|
234
|
+
{[0, 1, 2].map((i) => (
|
|
215
235
|
<div
|
|
216
236
|
key={i}
|
|
217
|
-
|
|
218
|
-
height: 3,
|
|
219
|
-
borderRadius: 2,
|
|
220
|
-
background: theme.accent,
|
|
221
|
-
transformOrigin: "left",
|
|
222
|
-
animation: `clef-scan-line 1.8s ease-in-out ${delay}s infinite`,
|
|
223
|
-
opacity: 0,
|
|
224
|
-
width: [120, 90, 105][i],
|
|
225
|
-
}}
|
|
237
|
+
className={`h-[3px] rounded-sm bg-gold-500 clef-scan-line clef-scan-line-${i}`}
|
|
226
238
|
/>
|
|
227
239
|
))}
|
|
228
240
|
</div>
|
|
229
|
-
<div
|
|
230
|
-
style={{
|
|
231
|
-
fontFamily: theme.mono,
|
|
232
|
-
fontSize: 11,
|
|
233
|
-
color: theme.textMuted,
|
|
234
|
-
animation: "clef-scan-glow 1.8s ease-in-out infinite",
|
|
235
|
-
}}
|
|
236
|
-
>
|
|
237
|
-
Linting...
|
|
238
|
-
</div>
|
|
241
|
+
<div className="font-mono text-[11px] text-ash clef-scan-glow">Linting...</div>
|
|
239
242
|
</div>
|
|
240
243
|
</div>
|
|
241
244
|
</>
|
|
242
245
|
)}
|
|
243
246
|
|
|
244
247
|
{!loading && allClear && (
|
|
245
|
-
<
|
|
248
|
+
<EmptyState
|
|
246
249
|
data-testid="all-clear"
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
justifyContent: "center",
|
|
252
|
-
gap: 14,
|
|
253
|
-
padding: "60px 0",
|
|
254
|
-
}}
|
|
255
|
-
>
|
|
256
|
-
<div
|
|
257
|
-
style={{
|
|
258
|
-
width: 56,
|
|
259
|
-
height: 56,
|
|
260
|
-
borderRadius: "50%",
|
|
261
|
-
background: theme.greenDim,
|
|
262
|
-
border: `1px solid ${theme.green}44`,
|
|
263
|
-
display: "flex",
|
|
264
|
-
alignItems: "center",
|
|
265
|
-
justifyContent: "center",
|
|
266
|
-
fontSize: 24,
|
|
267
|
-
}}
|
|
268
|
-
>
|
|
269
|
-
{"\u2713"}
|
|
270
|
-
</div>
|
|
271
|
-
<div
|
|
272
|
-
style={{
|
|
273
|
-
fontFamily: theme.sans,
|
|
274
|
-
fontWeight: 600,
|
|
275
|
-
fontSize: 16,
|
|
276
|
-
color: theme.green,
|
|
277
|
-
}}
|
|
278
|
-
>
|
|
279
|
-
All clear
|
|
280
|
-
</div>
|
|
281
|
-
<div
|
|
282
|
-
style={{
|
|
283
|
-
fontFamily: theme.mono,
|
|
284
|
-
fontSize: 12,
|
|
285
|
-
color: theme.textMuted,
|
|
286
|
-
}}
|
|
287
|
-
>
|
|
288
|
-
No issues found across {fileCount} files
|
|
289
|
-
</div>
|
|
290
|
-
</div>
|
|
250
|
+
icon={<span className="text-go-500">{"✓"}</span>}
|
|
251
|
+
title="All clear"
|
|
252
|
+
body={`No issues found across ${fileCount} files`}
|
|
253
|
+
/>
|
|
291
254
|
)}
|
|
292
255
|
|
|
293
256
|
{/* Grouped issues */}
|
|
@@ -296,127 +259,58 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
296
259
|
(["error", "warning", "info"] as const).map((sev) => {
|
|
297
260
|
const group = visible.filter((i) => i.severity === sev);
|
|
298
261
|
if (!group.length) return null;
|
|
299
|
-
const meta =
|
|
262
|
+
const meta = SEVERITY_TW[sev];
|
|
300
263
|
|
|
301
264
|
return (
|
|
302
|
-
<div key={sev}
|
|
265
|
+
<div key={sev} className="mb-6">
|
|
303
266
|
{/* Group header */}
|
|
304
|
-
<div
|
|
305
|
-
style={{
|
|
306
|
-
display: "flex",
|
|
307
|
-
alignItems: "center",
|
|
308
|
-
gap: 10,
|
|
309
|
-
marginBottom: 10,
|
|
310
|
-
}}
|
|
311
|
-
>
|
|
267
|
+
<div className="mb-2.5 flex items-center gap-2.5">
|
|
312
268
|
<div
|
|
313
|
-
|
|
314
|
-
width: 22,
|
|
315
|
-
height: 22,
|
|
316
|
-
borderRadius: "50%",
|
|
317
|
-
background: meta.bg,
|
|
318
|
-
border: `1px solid ${meta.color}44`,
|
|
319
|
-
display: "flex",
|
|
320
|
-
alignItems: "center",
|
|
321
|
-
justifyContent: "center",
|
|
322
|
-
fontFamily: theme.mono,
|
|
323
|
-
fontSize: 11,
|
|
324
|
-
fontWeight: 700,
|
|
325
|
-
color: meta.color,
|
|
326
|
-
}}
|
|
269
|
+
className={`flex h-[22px] w-[22px] items-center justify-center rounded-full border font-mono text-[11px] font-bold ${meta.bg} ${meta.border} ${meta.text}`}
|
|
327
270
|
>
|
|
328
271
|
{meta.icon}
|
|
329
272
|
</div>
|
|
330
|
-
<span
|
|
331
|
-
style={{
|
|
332
|
-
fontFamily: theme.sans,
|
|
333
|
-
fontWeight: 600,
|
|
334
|
-
fontSize: 13,
|
|
335
|
-
color: meta.color,
|
|
336
|
-
}}
|
|
337
|
-
>
|
|
273
|
+
<span className={`font-sans text-[13px] font-semibold ${meta.text}`}>
|
|
338
274
|
{meta.label}s
|
|
339
275
|
</span>
|
|
340
276
|
<span
|
|
341
|
-
|
|
342
|
-
fontFamily: theme.mono,
|
|
343
|
-
fontSize: 10,
|
|
344
|
-
color: meta.color,
|
|
345
|
-
background: meta.bg,
|
|
346
|
-
border: `1px solid ${meta.color}33`,
|
|
347
|
-
borderRadius: 10,
|
|
348
|
-
padding: "1px 8px",
|
|
349
|
-
}}
|
|
277
|
+
className={`rounded-pill border px-2 py-px font-mono text-[10px] ${meta.bg} ${meta.border} ${meta.text}`}
|
|
350
278
|
>
|
|
351
279
|
{group.length}
|
|
352
280
|
</span>
|
|
353
281
|
</div>
|
|
354
282
|
|
|
355
283
|
{/* Issue cards */}
|
|
356
|
-
<
|
|
357
|
-
style={{
|
|
358
|
-
background: theme.surface,
|
|
359
|
-
border: `1px solid ${theme.border}`,
|
|
360
|
-
borderRadius: 10,
|
|
361
|
-
overflow: "hidden",
|
|
362
|
-
}}
|
|
363
|
-
>
|
|
284
|
+
<Card>
|
|
364
285
|
{group.map((issue, i) => {
|
|
365
|
-
const
|
|
286
|
+
const catKey = (issue.category as Category) ?? "matrix";
|
|
287
|
+
const catMeta = CATEGORY_TW[catKey] ?? {
|
|
366
288
|
label: issue.category,
|
|
367
|
-
|
|
289
|
+
tone: "default" as BadgeTone,
|
|
368
290
|
};
|
|
369
291
|
const fileParts = issue.file?.split("/") ?? [];
|
|
370
292
|
const envName =
|
|
371
293
|
fileParts.length >= 2
|
|
372
294
|
? fileParts[fileParts.length - 1]?.replace(".enc.yaml", "")
|
|
373
295
|
: undefined;
|
|
296
|
+
const isLast = i === group.length - 1;
|
|
374
297
|
|
|
375
298
|
return (
|
|
376
299
|
<div
|
|
377
300
|
key={issue._idx}
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
borderBottom: i < group.length - 1 ? `1px solid ${theme.border}` : "none",
|
|
382
|
-
borderLeft: `3px solid ${meta.color}66`,
|
|
383
|
-
transition: "background 0.1s",
|
|
384
|
-
padding: "14px 18px",
|
|
385
|
-
gap: 14,
|
|
386
|
-
}}
|
|
301
|
+
className={`flex items-start gap-3.5 px-[18px] py-3.5 ${meta.rowStripe} ${
|
|
302
|
+
!isLast ? "border-b border-edge" : ""
|
|
303
|
+
}`}
|
|
387
304
|
>
|
|
388
305
|
{/* Category badge */}
|
|
389
|
-
<div
|
|
390
|
-
<
|
|
391
|
-
style={{
|
|
392
|
-
fontFamily: theme.mono,
|
|
393
|
-
fontSize: 9,
|
|
394
|
-
fontWeight: 700,
|
|
395
|
-
color: catMeta.color,
|
|
396
|
-
background: `${catMeta.color}18`,
|
|
397
|
-
border: `1px solid ${catMeta.color}33`,
|
|
398
|
-
borderRadius: 3,
|
|
399
|
-
padding: "2px 6px",
|
|
400
|
-
letterSpacing: "0.07em",
|
|
401
|
-
textTransform: "uppercase",
|
|
402
|
-
}}
|
|
403
|
-
>
|
|
404
|
-
{catMeta.label}
|
|
405
|
-
</span>
|
|
306
|
+
<div className="shrink-0 pt-0.5">
|
|
307
|
+
<Badge tone={catMeta.tone}>{catMeta.label}</Badge>
|
|
406
308
|
</div>
|
|
407
309
|
|
|
408
310
|
{/* Main content */}
|
|
409
|
-
<div
|
|
311
|
+
<div className="min-w-0 flex-1">
|
|
410
312
|
{/* File + key */}
|
|
411
|
-
<div
|
|
412
|
-
style={{
|
|
413
|
-
display: "flex",
|
|
414
|
-
alignItems: "center",
|
|
415
|
-
gap: 8,
|
|
416
|
-
marginBottom: 4,
|
|
417
|
-
flexWrap: "wrap",
|
|
418
|
-
}}
|
|
419
|
-
>
|
|
313
|
+
<div className="mb-1 flex flex-wrap items-center gap-2">
|
|
420
314
|
<span
|
|
421
315
|
data-testid={`file-ref-${issue.file}`}
|
|
422
316
|
role="link"
|
|
@@ -425,41 +319,18 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
425
319
|
onKeyDown={(e) => {
|
|
426
320
|
if (e.key === "Enter") handleNavigate(issue);
|
|
427
321
|
}}
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
cursor: issue.file ? "pointer" : "default",
|
|
434
|
-
textDecoration: issue.file ? "underline" : "none",
|
|
435
|
-
textDecorationColor: `${theme.accent}55`,
|
|
436
|
-
textDecorationStyle: "dotted",
|
|
437
|
-
}}
|
|
322
|
+
className={`font-mono text-[12px] font-semibold text-gold-500 ${
|
|
323
|
+
issue.file
|
|
324
|
+
? "cursor-pointer underline decoration-gold-500/40 decoration-dotted"
|
|
325
|
+
: "cursor-default no-underline"
|
|
326
|
+
}`}
|
|
438
327
|
>
|
|
439
328
|
{issue.file}
|
|
440
329
|
</span>
|
|
441
330
|
{issue.key && (
|
|
442
331
|
<>
|
|
443
|
-
<span
|
|
444
|
-
|
|
445
|
-
fontFamily: theme.mono,
|
|
446
|
-
fontSize: 11,
|
|
447
|
-
color: theme.textDim,
|
|
448
|
-
}}
|
|
449
|
-
>
|
|
450
|
-
{"\u2192"}
|
|
451
|
-
</span>
|
|
452
|
-
<span
|
|
453
|
-
style={{
|
|
454
|
-
fontFamily: theme.mono,
|
|
455
|
-
fontSize: 11,
|
|
456
|
-
color: theme.text,
|
|
457
|
-
background: "#1A1F2B",
|
|
458
|
-
border: `1px solid ${theme.borderLight}`,
|
|
459
|
-
borderRadius: 3,
|
|
460
|
-
padding: "1px 7px",
|
|
461
|
-
}}
|
|
462
|
-
>
|
|
332
|
+
<span className="font-mono text-[11px] text-ash-dim">{"→"}</span>
|
|
333
|
+
<span className="rounded-sm border border-edge-strong bg-ink-900 px-[7px] py-px font-mono text-[11px] text-bone">
|
|
463
334
|
{issue.key}
|
|
464
335
|
</span>
|
|
465
336
|
</>
|
|
@@ -469,46 +340,16 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
469
340
|
|
|
470
341
|
{/* Message */}
|
|
471
342
|
<div
|
|
472
|
-
|
|
473
|
-
fontFamily: theme.sans,
|
|
474
|
-
fontSize: 12,
|
|
475
|
-
color: theme.textMuted,
|
|
476
|
-
marginBottom: issue.fixCommand ? 10 : 0,
|
|
477
|
-
}}
|
|
343
|
+
className={`font-sans text-[12px] text-ash ${issue.fixCommand ? "mb-2.5" : ""}`}
|
|
478
344
|
>
|
|
479
345
|
{issue.message}
|
|
480
346
|
</div>
|
|
481
347
|
|
|
482
348
|
{/* Fix command */}
|
|
483
349
|
{issue.fixCommand && (
|
|
484
|
-
<div
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
alignItems: "center",
|
|
488
|
-
gap: 8,
|
|
489
|
-
background: "#0D0F14",
|
|
490
|
-
border: `1px solid ${theme.borderLight}`,
|
|
491
|
-
borderRadius: 6,
|
|
492
|
-
padding: "6px 10px",
|
|
493
|
-
width: "fit-content",
|
|
494
|
-
}}
|
|
495
|
-
>
|
|
496
|
-
<span
|
|
497
|
-
style={{
|
|
498
|
-
fontFamily: theme.mono,
|
|
499
|
-
fontSize: 11,
|
|
500
|
-
color: theme.green,
|
|
501
|
-
}}
|
|
502
|
-
>
|
|
503
|
-
$
|
|
504
|
-
</span>
|
|
505
|
-
<span
|
|
506
|
-
style={{
|
|
507
|
-
fontFamily: theme.mono,
|
|
508
|
-
fontSize: 11,
|
|
509
|
-
color: theme.text,
|
|
510
|
-
}}
|
|
511
|
-
>
|
|
350
|
+
<div className="flex w-fit items-center gap-2 rounded-md border border-edge-strong bg-ink-800 px-2.5 py-1.5">
|
|
351
|
+
<span className="font-mono text-[11px] text-go-500">$</span>
|
|
352
|
+
<span className="font-mono text-[11px] text-bone">
|
|
512
353
|
{issue.fixCommand}
|
|
513
354
|
</span>
|
|
514
355
|
<CopyButton text={issue.fixCommand} />
|
|
@@ -521,62 +362,26 @@ export function LintView({ setView, setNs }: LintViewProps) {
|
|
|
521
362
|
onClick={() => setDismissed((d) => [...d, issue._idx])}
|
|
522
363
|
title="Dismiss"
|
|
523
364
|
aria-label="Dismiss issue"
|
|
524
|
-
|
|
525
|
-
background: "none",
|
|
526
|
-
border: "none",
|
|
527
|
-
cursor: "pointer",
|
|
528
|
-
color: theme.textDim,
|
|
529
|
-
fontSize: 16,
|
|
530
|
-
flexShrink: 0,
|
|
531
|
-
padding: "0 4px",
|
|
532
|
-
lineHeight: 1,
|
|
533
|
-
transition: "color 0.1s",
|
|
534
|
-
}}
|
|
365
|
+
className="shrink-0 cursor-pointer border-none bg-transparent px-1 text-[16px] leading-none text-ash-dim transition-colors hover:text-bone"
|
|
535
366
|
>
|
|
536
|
-
{"
|
|
367
|
+
{"×"}
|
|
537
368
|
</button>
|
|
538
369
|
</div>
|
|
539
370
|
);
|
|
540
371
|
})}
|
|
541
|
-
</
|
|
372
|
+
</Card>
|
|
542
373
|
</div>
|
|
543
374
|
);
|
|
544
375
|
})}
|
|
545
376
|
|
|
546
377
|
{/* Footer hint */}
|
|
547
378
|
{!loading && !allClear && (
|
|
548
|
-
<div
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
background: theme.surface,
|
|
553
|
-
border: `1px solid ${theme.border}`,
|
|
554
|
-
borderRadius: 8,
|
|
555
|
-
display: "flex",
|
|
556
|
-
alignItems: "center",
|
|
557
|
-
gap: 12,
|
|
558
|
-
}}
|
|
559
|
-
>
|
|
560
|
-
<span style={{ fontSize: 14 }}>{"\uD83D\uDCA1"}</span>
|
|
561
|
-
<span
|
|
562
|
-
style={{
|
|
563
|
-
fontFamily: theme.sans,
|
|
564
|
-
fontSize: 12,
|
|
565
|
-
color: theme.textMuted,
|
|
566
|
-
}}
|
|
567
|
-
>
|
|
568
|
-
Fix all errors before committing. Warnings and info items won't block commits but
|
|
379
|
+
<div className="mt-2 flex items-center gap-3 rounded-md border border-edge bg-ink-850 px-4 py-3">
|
|
380
|
+
<span className="text-[14px]">{"💡"}</span>
|
|
381
|
+
<span className="font-sans text-[12px] text-ash">
|
|
382
|
+
Fix all errors before committing. Warnings and info items won't block commits but
|
|
569
383
|
should be reviewed. Run{" "}
|
|
570
|
-
<code
|
|
571
|
-
style={{
|
|
572
|
-
fontFamily: theme.mono,
|
|
573
|
-
fontSize: 11,
|
|
574
|
-
color: theme.accent,
|
|
575
|
-
background: theme.accentDim,
|
|
576
|
-
padding: "1px 6px",
|
|
577
|
-
borderRadius: 3,
|
|
578
|
-
}}
|
|
579
|
-
>
|
|
384
|
+
<code className="rounded-sm bg-gold-500/15 px-1.5 py-px font-mono text-[11px] text-gold-500">
|
|
580
385
|
clef lint --fix
|
|
581
386
|
</code>{" "}
|
|
582
387
|
to auto-resolve safe issues.
|