@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,8 +1,7 @@
|
|
|
1
1
|
import React, { useState, useEffect } from "react";
|
|
2
|
-
import { theme } from "../theme";
|
|
3
2
|
import { apiFetch } from "../api";
|
|
4
|
-
import { TopBar } from "../components/TopBar";
|
|
5
3
|
import { Button } from "../components/Button";
|
|
4
|
+
import { Toolbar } from "../primitives";
|
|
6
5
|
import type { ClefManifest } from "@clef-sh/core";
|
|
7
6
|
import type { ViewName } from "../components/Sidebar";
|
|
8
7
|
|
|
@@ -27,6 +26,12 @@ interface ApplyResult {
|
|
|
27
26
|
|
|
28
27
|
type ImportFormatOption = "auto" | "dotenv" | "json" | "yaml";
|
|
29
28
|
|
|
29
|
+
const SELECT_CLASSES =
|
|
30
|
+
"w-full rounded-md border border-edge bg-ink-850 px-2.5 py-1.5 font-sans text-[13px] text-bone outline-none cursor-pointer focus-visible:border-gold-500";
|
|
31
|
+
|
|
32
|
+
const TEXTAREA_CLASSES =
|
|
33
|
+
"w-full box-border rounded-lg border border-edge bg-ink-850 p-3.5 font-mono text-[12px] text-bone outline-none resize-y focus-visible:border-gold-500";
|
|
34
|
+
|
|
30
35
|
export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
31
36
|
const [step, setStep] = useState<1 | 2 | 3>(1);
|
|
32
37
|
const [namespace, setNamespace] = useState("");
|
|
@@ -39,7 +44,6 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
39
44
|
const [loading, setLoading] = useState(false);
|
|
40
45
|
const [error, setError] = useState<string | null>(null);
|
|
41
46
|
|
|
42
|
-
// Set defaults from manifest
|
|
43
47
|
useEffect(() => {
|
|
44
48
|
if (manifest) {
|
|
45
49
|
if (!namespace && manifest.namespaces.length > 0) {
|
|
@@ -98,7 +102,6 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
98
102
|
setLoading(true);
|
|
99
103
|
setError(null);
|
|
100
104
|
|
|
101
|
-
// Keys to import: wouldImport + any overwrite-toggled ones
|
|
102
105
|
const keysToImport = [
|
|
103
106
|
...preview.wouldImport,
|
|
104
107
|
...preview.wouldSkip.filter((s) => overwriteKeys.includes(s.key)).map((s) => s.key),
|
|
@@ -125,7 +128,6 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
125
128
|
|
|
126
129
|
const data: ApplyResult = await res.json();
|
|
127
130
|
setApplyResult(data);
|
|
128
|
-
// Clear content after successful apply
|
|
129
131
|
setContent("");
|
|
130
132
|
setStep(3);
|
|
131
133
|
} catch (err) {
|
|
@@ -155,67 +157,43 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
155
157
|
overwriteKeys.filter((k) => preview?.wouldSkip.some((s) => s.key === k)).length;
|
|
156
158
|
|
|
157
159
|
return (
|
|
158
|
-
<div
|
|
159
|
-
<
|
|
160
|
+
<div className="flex flex-1 flex-col overflow-hidden">
|
|
161
|
+
<Toolbar>
|
|
162
|
+
<div>
|
|
163
|
+
<Toolbar.Title>Import</Toolbar.Title>
|
|
164
|
+
<Toolbar.Subtitle>clef import — bulk migrate secrets</Toolbar.Subtitle>
|
|
165
|
+
</div>
|
|
166
|
+
</Toolbar>
|
|
160
167
|
|
|
161
|
-
<div
|
|
162
|
-
<div
|
|
168
|
+
<div className="flex-1 overflow-auto p-6">
|
|
169
|
+
<div className="mx-auto max-w-[620px]">
|
|
163
170
|
{/* Step indicator */}
|
|
164
|
-
<div
|
|
165
|
-
style={{
|
|
166
|
-
display: "flex",
|
|
167
|
-
alignItems: "center",
|
|
168
|
-
gap: 0,
|
|
169
|
-
marginBottom: 32,
|
|
170
|
-
}}
|
|
171
|
-
>
|
|
171
|
+
<div className="mb-8 flex items-center">
|
|
172
172
|
{([1, 2, 3] as const).map((s, i) => (
|
|
173
173
|
<React.Fragment key={s}>
|
|
174
|
-
<div
|
|
175
|
-
style={{
|
|
176
|
-
display: "flex",
|
|
177
|
-
alignItems: "center",
|
|
178
|
-
gap: 8,
|
|
179
|
-
}}
|
|
180
|
-
>
|
|
174
|
+
<div className="flex items-center gap-2">
|
|
181
175
|
<div
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
border: `1px solid ${step >= s ? theme.accent : theme.border}`,
|
|
188
|
-
display: "flex",
|
|
189
|
-
alignItems: "center",
|
|
190
|
-
justifyContent: "center",
|
|
191
|
-
fontFamily: theme.mono,
|
|
192
|
-
fontSize: 11,
|
|
193
|
-
fontWeight: 700,
|
|
194
|
-
color: step >= s ? "#000" : theme.textDim,
|
|
195
|
-
}}
|
|
176
|
+
className={`flex h-6 w-6 items-center justify-center rounded-full font-mono text-[11px] font-bold ${
|
|
177
|
+
step >= s
|
|
178
|
+
? "bg-gold-500 border border-gold-500 text-ink-950"
|
|
179
|
+
: "bg-ink-850 border border-edge text-ash-dim"
|
|
180
|
+
}`}
|
|
196
181
|
>
|
|
197
182
|
{s}
|
|
198
183
|
</div>
|
|
199
184
|
<span
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
color: step >= s ? theme.text : theme.textDim,
|
|
204
|
-
fontWeight: step === s ? 600 : 400,
|
|
205
|
-
}}
|
|
185
|
+
className={`font-sans text-[12px] ${
|
|
186
|
+
step >= s ? "text-bone" : "text-ash-dim"
|
|
187
|
+
} ${step === s ? "font-semibold" : "font-normal"}`}
|
|
206
188
|
>
|
|
207
189
|
{s === 1 ? "Source" : s === 2 ? "Preview" : "Done"}
|
|
208
190
|
</span>
|
|
209
191
|
</div>
|
|
210
192
|
{i < 2 && (
|
|
211
193
|
<div
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
background: step > s ? theme.accent : theme.border,
|
|
216
|
-
margin: "0 12px",
|
|
217
|
-
minWidth: 40,
|
|
218
|
-
}}
|
|
194
|
+
className={`mx-3 h-px min-w-[40px] flex-1 ${
|
|
195
|
+
step > s ? "bg-gold-500" : "bg-edge"
|
|
196
|
+
}`}
|
|
219
197
|
/>
|
|
220
198
|
)}
|
|
221
199
|
</React.Fragment>
|
|
@@ -223,18 +201,7 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
223
201
|
</div>
|
|
224
202
|
|
|
225
203
|
{error && (
|
|
226
|
-
<div
|
|
227
|
-
style={{
|
|
228
|
-
background: theme.redDim,
|
|
229
|
-
border: `1px solid ${theme.red}44`,
|
|
230
|
-
borderRadius: 8,
|
|
231
|
-
padding: "12px 16px",
|
|
232
|
-
marginBottom: 16,
|
|
233
|
-
fontFamily: theme.sans,
|
|
234
|
-
fontSize: 13,
|
|
235
|
-
color: theme.red,
|
|
236
|
-
}}
|
|
237
|
-
>
|
|
204
|
+
<div className="mb-4 rounded-lg border border-stop-500/30 bg-stop-500/10 px-4 py-3 font-sans text-[13px] text-stop-500">
|
|
238
205
|
{error}
|
|
239
206
|
</div>
|
|
240
207
|
)}
|
|
@@ -242,49 +209,49 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
242
209
|
{/* ── Step 1: Source ─────────────────────────────────────────── */}
|
|
243
210
|
{step === 1 && (
|
|
244
211
|
<div>
|
|
245
|
-
|
|
246
|
-
<div style={{ marginBottom: 20 }}>
|
|
212
|
+
<div className="mb-5">
|
|
247
213
|
<Label>Target</Label>
|
|
248
|
-
<div
|
|
249
|
-
<div
|
|
214
|
+
<div className="flex gap-3">
|
|
215
|
+
<div className="flex-1">
|
|
250
216
|
<SubLabel>Namespace</SubLabel>
|
|
251
|
-
<
|
|
217
|
+
<select
|
|
218
|
+
value={namespace}
|
|
219
|
+
onChange={(e) => setNamespace(e.target.value)}
|
|
220
|
+
className={SELECT_CLASSES}
|
|
221
|
+
>
|
|
252
222
|
{namespaces.map((ns) => (
|
|
253
223
|
<option key={ns.name} value={ns.name}>
|
|
254
224
|
{ns.name}
|
|
255
225
|
</option>
|
|
256
226
|
))}
|
|
257
|
-
</
|
|
227
|
+
</select>
|
|
258
228
|
</div>
|
|
259
|
-
<div
|
|
229
|
+
<div className="flex-1">
|
|
260
230
|
<SubLabel>Environment</SubLabel>
|
|
261
|
-
<
|
|
231
|
+
<select
|
|
232
|
+
value={environment}
|
|
233
|
+
onChange={(e) => setEnvironment(e.target.value)}
|
|
234
|
+
className={SELECT_CLASSES}
|
|
235
|
+
>
|
|
262
236
|
{environments.map((env) => (
|
|
263
237
|
<option key={env.name} value={env.name}>
|
|
264
238
|
{env.name}
|
|
265
239
|
</option>
|
|
266
240
|
))}
|
|
267
|
-
</
|
|
241
|
+
</select>
|
|
268
242
|
</div>
|
|
269
243
|
</div>
|
|
270
244
|
</div>
|
|
271
245
|
|
|
272
|
-
|
|
273
|
-
<div style={{ marginBottom: 20 }}>
|
|
246
|
+
<div className="mb-5">
|
|
274
247
|
<Label>Format</Label>
|
|
275
|
-
<div
|
|
248
|
+
<div className="flex gap-4">
|
|
276
249
|
{(["auto", "dotenv", "json", "yaml"] as const).map((f) => (
|
|
277
250
|
<label
|
|
278
251
|
key={f}
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
gap: 6,
|
|
283
|
-
cursor: "pointer",
|
|
284
|
-
fontFamily: theme.sans,
|
|
285
|
-
fontSize: 13,
|
|
286
|
-
color: format === f ? theme.text : theme.textMuted,
|
|
287
|
-
}}
|
|
252
|
+
className={`flex cursor-pointer items-center gap-1.5 font-sans text-[13px] ${
|
|
253
|
+
format === f ? "text-bone" : "text-ash"
|
|
254
|
+
}`}
|
|
288
255
|
>
|
|
289
256
|
<input
|
|
290
257
|
type="radio"
|
|
@@ -292,7 +259,7 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
292
259
|
value={f}
|
|
293
260
|
checked={format === f}
|
|
294
261
|
onChange={() => setFormat(f)}
|
|
295
|
-
|
|
262
|
+
className="accent-gold-500"
|
|
296
263
|
/>
|
|
297
264
|
{f === "auto" ? "Auto" : f}
|
|
298
265
|
</label>
|
|
@@ -300,8 +267,7 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
300
267
|
</div>
|
|
301
268
|
</div>
|
|
302
269
|
|
|
303
|
-
|
|
304
|
-
<div style={{ marginBottom: 8 }}>
|
|
270
|
+
<div className="mb-2">
|
|
305
271
|
<Label>Paste secrets</Label>
|
|
306
272
|
<textarea
|
|
307
273
|
value={content}
|
|
@@ -314,36 +280,11 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
314
280
|
: "DB_HOST=localhost\nDB_PORT=5432\n# Comments are ignored"
|
|
315
281
|
}
|
|
316
282
|
rows={12}
|
|
317
|
-
|
|
318
|
-
width: "100%",
|
|
319
|
-
background: theme.surface,
|
|
320
|
-
border: `1px solid ${theme.border}`,
|
|
321
|
-
borderRadius: 8,
|
|
322
|
-
padding: 14,
|
|
323
|
-
fontFamily: theme.mono,
|
|
324
|
-
fontSize: 12,
|
|
325
|
-
color: theme.text,
|
|
326
|
-
resize: "vertical",
|
|
327
|
-
outline: "none",
|
|
328
|
-
boxSizing: "border-box",
|
|
329
|
-
}}
|
|
283
|
+
className={TEXTAREA_CLASSES}
|
|
330
284
|
/>
|
|
331
285
|
</div>
|
|
332
286
|
|
|
333
|
-
|
|
334
|
-
<div
|
|
335
|
-
style={{
|
|
336
|
-
marginBottom: 24,
|
|
337
|
-
padding: "10px 14px",
|
|
338
|
-
background: theme.surface,
|
|
339
|
-
border: `1px solid ${theme.border}`,
|
|
340
|
-
borderRadius: 6,
|
|
341
|
-
fontFamily: theme.sans,
|
|
342
|
-
fontSize: 11,
|
|
343
|
-
color: theme.textMuted,
|
|
344
|
-
lineHeight: 1.5,
|
|
345
|
-
}}
|
|
346
|
-
>
|
|
287
|
+
<div className="mb-6 rounded-md border border-edge bg-ink-850 px-3.5 py-2.5 font-sans text-[11px] leading-relaxed text-ash">
|
|
347
288
|
Values are sent directly to the local Clef server (127.0.0.1) and encrypted
|
|
348
289
|
immediately. They are never stored in browser memory beyond this session.
|
|
349
290
|
</div>
|
|
@@ -361,56 +302,38 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
361
302
|
{/* ── Step 2: Preview ────────────────────────────────────────── */}
|
|
362
303
|
{step === 2 && preview && (
|
|
363
304
|
<div>
|
|
364
|
-
<div
|
|
365
|
-
style={{
|
|
366
|
-
fontFamily: theme.sans,
|
|
367
|
-
fontSize: 13,
|
|
368
|
-
color: theme.textMuted,
|
|
369
|
-
marginBottom: 20,
|
|
370
|
-
}}
|
|
371
|
-
>
|
|
305
|
+
<div className="mb-5 font-sans text-[13px] text-ash">
|
|
372
306
|
Importing to{" "}
|
|
373
|
-
<span
|
|
307
|
+
<span className="font-semibold text-gold-500">
|
|
374
308
|
{namespace}/{environment}
|
|
375
309
|
</span>
|
|
376
310
|
. {preview.totalKeys} key{preview.totalKeys !== 1 ? "s" : ""} parsed.
|
|
377
311
|
</div>
|
|
378
312
|
|
|
379
|
-
{/* Warnings */}
|
|
380
313
|
{preview.warnings.length > 0 && (
|
|
381
|
-
<div
|
|
314
|
+
<div className="mb-4">
|
|
382
315
|
{preview.warnings.map((w, i) => (
|
|
383
|
-
<div
|
|
384
|
-
key={i}
|
|
385
|
-
style={{
|
|
386
|
-
fontFamily: theme.mono,
|
|
387
|
-
fontSize: 11,
|
|
388
|
-
color: theme.yellow,
|
|
389
|
-
marginBottom: 4,
|
|
390
|
-
}}
|
|
391
|
-
>
|
|
316
|
+
<div key={i} className="mb-1 font-mono text-[11px] text-warn-500">
|
|
392
317
|
⚠ {w}
|
|
393
318
|
</div>
|
|
394
319
|
))}
|
|
395
320
|
</div>
|
|
396
321
|
)}
|
|
397
322
|
|
|
398
|
-
{/* Would import */}
|
|
399
323
|
{preview.wouldImport.length > 0 && (
|
|
400
|
-
<div
|
|
401
|
-
<SectionLabel
|
|
324
|
+
<div className="mb-4">
|
|
325
|
+
<SectionLabel toneClass="text-go-500">
|
|
402
326
|
New keys ({preview.wouldImport.length})
|
|
403
327
|
</SectionLabel>
|
|
404
328
|
{preview.wouldImport.map((key) => (
|
|
405
|
-
<KeyRow key={key} icon="
|
|
329
|
+
<KeyRow key={key} icon="→" iconClass="text-go-500" label={key} />
|
|
406
330
|
))}
|
|
407
331
|
</div>
|
|
408
332
|
)}
|
|
409
333
|
|
|
410
|
-
{/* Would skip / overwrite toggles */}
|
|
411
334
|
{preview.wouldSkip.length > 0 && (
|
|
412
|
-
<div
|
|
413
|
-
<SectionLabel
|
|
335
|
+
<div className="mb-4">
|
|
336
|
+
<SectionLabel toneClass="text-warn-500">
|
|
414
337
|
Already exists ({preview.wouldSkip.length}) — toggle to overwrite
|
|
415
338
|
</SectionLabel>
|
|
416
339
|
{preview.wouldSkip.map(({ key, reason }) => {
|
|
@@ -418,45 +341,28 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
418
341
|
return (
|
|
419
342
|
<div
|
|
420
343
|
key={key}
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
borderRadius: 6,
|
|
427
|
-
marginBottom: 4,
|
|
428
|
-
background: willOverwrite ? theme.yellowDim : "transparent",
|
|
429
|
-
border: `1px solid ${willOverwrite ? theme.yellow + "44" : theme.border}`,
|
|
430
|
-
}}
|
|
344
|
+
className={`mb-1 flex items-center gap-2.5 rounded-md border px-2.5 py-1.5 ${
|
|
345
|
+
willOverwrite
|
|
346
|
+
? "border-warn-500/30 bg-warn-500/10"
|
|
347
|
+
: "border-edge bg-transparent"
|
|
348
|
+
}`}
|
|
431
349
|
>
|
|
432
350
|
<input
|
|
433
351
|
type="checkbox"
|
|
434
352
|
checked={willOverwrite}
|
|
435
353
|
onChange={() => toggleOverwrite(key)}
|
|
436
|
-
|
|
354
|
+
className="accent-warn-500"
|
|
437
355
|
id={`overwrite-${key}`}
|
|
438
356
|
/>
|
|
439
357
|
<label
|
|
440
358
|
htmlFor={`overwrite-${key}`}
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
color: willOverwrite ? theme.yellow : theme.textMuted,
|
|
445
|
-
flex: 1,
|
|
446
|
-
cursor: "pointer",
|
|
447
|
-
}}
|
|
359
|
+
className={`flex-1 cursor-pointer font-mono text-[12px] ${
|
|
360
|
+
willOverwrite ? "text-warn-500" : "text-ash"
|
|
361
|
+
}`}
|
|
448
362
|
>
|
|
449
363
|
{key}
|
|
450
364
|
</label>
|
|
451
|
-
<span
|
|
452
|
-
style={{
|
|
453
|
-
fontFamily: theme.sans,
|
|
454
|
-
fontSize: 11,
|
|
455
|
-
color: theme.textDim,
|
|
456
|
-
}}
|
|
457
|
-
>
|
|
458
|
-
{reason}
|
|
459
|
-
</span>
|
|
365
|
+
<span className="font-sans text-[11px] text-ash-dim">{reason}</span>
|
|
460
366
|
</div>
|
|
461
367
|
);
|
|
462
368
|
})}
|
|
@@ -464,20 +370,12 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
464
370
|
)}
|
|
465
371
|
|
|
466
372
|
{preview.wouldImport.length === 0 && preview.wouldSkip.length === 0 && (
|
|
467
|
-
<div
|
|
468
|
-
style={{
|
|
469
|
-
fontFamily: theme.sans,
|
|
470
|
-
fontSize: 13,
|
|
471
|
-
color: theme.textMuted,
|
|
472
|
-
padding: "24px 0",
|
|
473
|
-
textAlign: "center",
|
|
474
|
-
}}
|
|
475
|
-
>
|
|
373
|
+
<div className="py-6 text-center font-sans text-[13px] text-ash">
|
|
476
374
|
No importable keys found.
|
|
477
375
|
</div>
|
|
478
376
|
)}
|
|
479
377
|
|
|
480
|
-
<div
|
|
378
|
+
<div className="mt-6 flex gap-2.5">
|
|
481
379
|
<Button variant="ghost" onClick={() => setStep(1)}>
|
|
482
380
|
Back
|
|
483
381
|
</Button>
|
|
@@ -497,80 +395,52 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
497
395
|
{/* ── Step 3: Done ───────────────────────────────────────────── */}
|
|
498
396
|
{step === 3 && applyResult && (
|
|
499
397
|
<div>
|
|
500
|
-
<div
|
|
501
|
-
style={{
|
|
502
|
-
display: "flex",
|
|
503
|
-
flexDirection: "column",
|
|
504
|
-
alignItems: "center",
|
|
505
|
-
paddingTop: 20,
|
|
506
|
-
paddingBottom: 32,
|
|
507
|
-
}}
|
|
508
|
-
>
|
|
398
|
+
<div className="flex flex-col items-center pt-5 pb-8">
|
|
509
399
|
<div
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
border: `1px solid ${applyResult.failed.length > 0 ? theme.red + "44" : theme.green + "44"}`,
|
|
516
|
-
display: "flex",
|
|
517
|
-
alignItems: "center",
|
|
518
|
-
justifyContent: "center",
|
|
519
|
-
fontSize: 24,
|
|
520
|
-
color: applyResult.failed.length > 0 ? theme.red : theme.green,
|
|
521
|
-
marginBottom: 16,
|
|
522
|
-
}}
|
|
400
|
+
className={`mb-4 flex h-14 w-14 items-center justify-center rounded-full text-[24px] ${
|
|
401
|
+
applyResult.failed.length > 0
|
|
402
|
+
? "border border-stop-500/30 bg-stop-500/10 text-stop-500"
|
|
403
|
+
: "border border-go-500/30 bg-go-500/10 text-go-500"
|
|
404
|
+
}`}
|
|
523
405
|
>
|
|
524
|
-
{applyResult.failed.length > 0 ? "
|
|
406
|
+
{applyResult.failed.length > 0 ? "⚠" : "✓"}
|
|
525
407
|
</div>
|
|
526
|
-
|
|
527
408
|
<div
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
fontSize: 16,
|
|
532
|
-
color: applyResult.failed.length > 0 ? theme.yellow : theme.green,
|
|
533
|
-
marginBottom: 8,
|
|
534
|
-
}}
|
|
409
|
+
className={`mb-2 font-sans text-[16px] font-semibold ${
|
|
410
|
+
applyResult.failed.length > 0 ? "text-warn-500" : "text-go-500"
|
|
411
|
+
}`}
|
|
535
412
|
>
|
|
536
413
|
{applyResult.failed.length > 0
|
|
537
414
|
? "Import completed with errors"
|
|
538
415
|
: "Import complete"}
|
|
539
416
|
</div>
|
|
540
|
-
|
|
541
|
-
<div
|
|
542
|
-
style={{
|
|
543
|
-
fontFamily: theme.mono,
|
|
544
|
-
fontSize: 12,
|
|
545
|
-
color: theme.textMuted,
|
|
546
|
-
}}
|
|
547
|
-
>
|
|
417
|
+
<div className="font-mono text-[12px] text-ash">
|
|
548
418
|
{applyResult.imported.length} imported, {applyResult.skipped.length} skipped,{" "}
|
|
549
419
|
{applyResult.failed.length} failed
|
|
550
420
|
</div>
|
|
551
421
|
</div>
|
|
552
422
|
|
|
553
423
|
{applyResult.imported.length > 0 && (
|
|
554
|
-
<div
|
|
555
|
-
<SectionLabel
|
|
424
|
+
<div className="mb-4">
|
|
425
|
+
<SectionLabel toneClass="text-go-500">
|
|
556
426
|
Imported ({applyResult.imported.length})
|
|
557
427
|
</SectionLabel>
|
|
558
428
|
{applyResult.imported.map((key) => (
|
|
559
|
-
<KeyRow key={key} icon="
|
|
429
|
+
<KeyRow key={key} icon="✓" iconClass="text-go-500" label={key} />
|
|
560
430
|
))}
|
|
561
431
|
</div>
|
|
562
432
|
)}
|
|
563
433
|
|
|
564
434
|
{applyResult.failed.length > 0 && (
|
|
565
|
-
<div
|
|
566
|
-
<SectionLabel
|
|
435
|
+
<div className="mb-4">
|
|
436
|
+
<SectionLabel toneClass="text-stop-500">
|
|
567
437
|
Failed ({applyResult.failed.length})
|
|
568
438
|
</SectionLabel>
|
|
569
439
|
{applyResult.failed.map(({ key, error: keyError }) => (
|
|
570
440
|
<KeyRow
|
|
571
441
|
key={key}
|
|
572
|
-
icon="
|
|
573
|
-
|
|
442
|
+
icon="✗"
|
|
443
|
+
iconClass="text-stop-500"
|
|
574
444
|
label={key}
|
|
575
445
|
note={keyError}
|
|
576
446
|
/>
|
|
@@ -578,7 +448,7 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
578
448
|
</div>
|
|
579
449
|
)}
|
|
580
450
|
|
|
581
|
-
<div
|
|
451
|
+
<div className="mt-6 flex gap-2.5">
|
|
582
452
|
<Button variant="primary" onClick={() => setView("matrix")}>
|
|
583
453
|
View in Matrix
|
|
584
454
|
</Button>
|
|
@@ -596,80 +466,20 @@ export function ImportScreen({ manifest, setView }: ImportScreenProps) {
|
|
|
596
466
|
|
|
597
467
|
function Label({ children }: { children: React.ReactNode }) {
|
|
598
468
|
return (
|
|
599
|
-
<div
|
|
600
|
-
style={{
|
|
601
|
-
fontFamily: theme.sans,
|
|
602
|
-
fontSize: 12,
|
|
603
|
-
fontWeight: 600,
|
|
604
|
-
color: theme.textMuted,
|
|
605
|
-
marginBottom: 8,
|
|
606
|
-
letterSpacing: "0.05em",
|
|
607
|
-
textTransform: "uppercase",
|
|
608
|
-
}}
|
|
609
|
-
>
|
|
469
|
+
<div className="mb-2 font-sans text-[12px] font-semibold uppercase tracking-[0.05em] text-ash">
|
|
610
470
|
{children}
|
|
611
471
|
</div>
|
|
612
472
|
);
|
|
613
473
|
}
|
|
614
474
|
|
|
615
475
|
function SubLabel({ children }: { children: React.ReactNode }) {
|
|
616
|
-
return
|
|
617
|
-
<div
|
|
618
|
-
style={{
|
|
619
|
-
fontFamily: theme.sans,
|
|
620
|
-
fontSize: 11,
|
|
621
|
-
color: theme.textDim,
|
|
622
|
-
marginBottom: 4,
|
|
623
|
-
}}
|
|
624
|
-
>
|
|
625
|
-
{children}
|
|
626
|
-
</div>
|
|
627
|
-
);
|
|
628
|
-
}
|
|
629
|
-
|
|
630
|
-
function Select({
|
|
631
|
-
value,
|
|
632
|
-
onChange,
|
|
633
|
-
children,
|
|
634
|
-
}: {
|
|
635
|
-
value: string;
|
|
636
|
-
onChange: (e: React.ChangeEvent<HTMLSelectElement>) => void;
|
|
637
|
-
children: React.ReactNode;
|
|
638
|
-
}) {
|
|
639
|
-
return (
|
|
640
|
-
<select
|
|
641
|
-
value={value}
|
|
642
|
-
onChange={onChange}
|
|
643
|
-
style={{
|
|
644
|
-
width: "100%",
|
|
645
|
-
background: theme.surface,
|
|
646
|
-
border: `1px solid ${theme.border}`,
|
|
647
|
-
borderRadius: 6,
|
|
648
|
-
padding: "7px 10px",
|
|
649
|
-
fontFamily: theme.sans,
|
|
650
|
-
fontSize: 13,
|
|
651
|
-
color: theme.text,
|
|
652
|
-
outline: "none",
|
|
653
|
-
cursor: "pointer",
|
|
654
|
-
}}
|
|
655
|
-
>
|
|
656
|
-
{children}
|
|
657
|
-
</select>
|
|
658
|
-
);
|
|
476
|
+
return <div className="mb-1 font-sans text-[11px] text-ash-dim">{children}</div>;
|
|
659
477
|
}
|
|
660
478
|
|
|
661
|
-
function SectionLabel({ children,
|
|
479
|
+
function SectionLabel({ children, toneClass }: { children: React.ReactNode; toneClass: string }) {
|
|
662
480
|
return (
|
|
663
481
|
<div
|
|
664
|
-
|
|
665
|
-
fontFamily: theme.sans,
|
|
666
|
-
fontSize: 11,
|
|
667
|
-
fontWeight: 600,
|
|
668
|
-
color,
|
|
669
|
-
letterSpacing: "0.06em",
|
|
670
|
-
textTransform: "uppercase",
|
|
671
|
-
marginBottom: 8,
|
|
672
|
-
}}
|
|
482
|
+
className={`mb-2 font-sans text-[11px] font-semibold uppercase tracking-[0.06em] ${toneClass}`}
|
|
673
483
|
>
|
|
674
484
|
{children}
|
|
675
485
|
</div>
|
|
@@ -678,33 +488,20 @@ function SectionLabel({ children, color }: { children: React.ReactNode; color: s
|
|
|
678
488
|
|
|
679
489
|
function KeyRow({
|
|
680
490
|
icon,
|
|
681
|
-
|
|
491
|
+
iconClass,
|
|
682
492
|
label,
|
|
683
493
|
note,
|
|
684
494
|
}: {
|
|
685
495
|
icon: string;
|
|
686
|
-
|
|
496
|
+
iconClass: string;
|
|
687
497
|
label: string;
|
|
688
498
|
note?: string;
|
|
689
499
|
}) {
|
|
690
500
|
return (
|
|
691
|
-
<div
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
gap: 10,
|
|
696
|
-
padding: "5px 10px",
|
|
697
|
-
borderRadius: 6,
|
|
698
|
-
marginBottom: 3,
|
|
699
|
-
}}
|
|
700
|
-
>
|
|
701
|
-
<span style={{ color: iconColor, fontFamily: theme.mono, fontSize: 13 }}>{icon}</span>
|
|
702
|
-
<span style={{ fontFamily: theme.mono, fontSize: 12, color: theme.text, flex: 1 }}>
|
|
703
|
-
{label}
|
|
704
|
-
</span>
|
|
705
|
-
{note && (
|
|
706
|
-
<span style={{ fontFamily: theme.sans, fontSize: 11, color: theme.textDim }}>{note}</span>
|
|
707
|
-
)}
|
|
501
|
+
<div className="mb-px flex items-center gap-2.5 rounded-md px-2.5 py-1">
|
|
502
|
+
<span className={`font-mono text-[13px] ${iconClass}`}>{icon}</span>
|
|
503
|
+
<span className="flex-1 font-mono text-[12px] text-bone">{label}</span>
|
|
504
|
+
{note && <span className="font-sans text-[11px] text-ash-dim">{note}</span>}
|
|
708
505
|
</div>
|
|
709
506
|
);
|
|
710
507
|
}
|