design(kez-chat/web): restyle auth + claims pages to the dark theme (phase 6)
Completes visual consistency across the whole app — every surface now
uses the tactical-terminal token set, so the redesign can ship without
a light-on-dark login screen.
• app.css: dark defaults for input/textarea/select (bg-elevated, token
text/border/placeholder, accent focus) so forms that don't set an
explicit bg still read correctly.
• Landing / CreateAccount / Restore / Unlock: light utility classes →
tokens (bg-white→surface, text-gray-*→text tiers, gray-900 buttons→
accent, red/green/amber→danger/verified/warning).
• Claims / AddClaim: same swap, plus the nostr publish panel + format
toggle + status badges remapped (purple→accent, blue→accent,
yellow→warning).
Now consistent end to end. Remaining polish (message ticks, day
separators, contact preview-card, skeletons, emoji-picker dark theme)
tracked for a follow-up; ready to deploy for a look.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
40ebd63ed7
commit
a9ef611622
@ -72,6 +72,27 @@ samp {
|
|||||||
font-family: var(--font-mono);
|
font-family: var(--font-mono);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Dark-theme defaults for form controls so any page that doesn't set an
|
||||||
|
explicit bg (the auth + claims forms) still reads correctly. Components
|
||||||
|
that set bg-elevated/bg-surface explicitly override this. */
|
||||||
|
input,
|
||||||
|
textarea,
|
||||||
|
select {
|
||||||
|
background-color: var(--color-elevated);
|
||||||
|
color: var(--color-text);
|
||||||
|
border-color: var(--color-border);
|
||||||
|
}
|
||||||
|
input::placeholder,
|
||||||
|
textarea::placeholder {
|
||||||
|
color: var(--color-text-muted);
|
||||||
|
}
|
||||||
|
input:focus-visible,
|
||||||
|
textarea:focus-visible,
|
||||||
|
select:focus-visible {
|
||||||
|
outline: none;
|
||||||
|
border-color: var(--color-accent);
|
||||||
|
}
|
||||||
|
|
||||||
/* Cyan text-selection — reinforces the accent. */
|
/* Cyan text-selection — reinforces the accent. */
|
||||||
::selection {
|
::selection {
|
||||||
background: #28c8e840;
|
background: #28c8e840;
|
||||||
|
|||||||
@ -246,9 +246,9 @@
|
|||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Add a claim</h1>
|
<h1 class="text-2xl font-bold text-text">Add a claim</h1>
|
||||||
<button
|
<button
|
||||||
class="text-sm text-gray-500 hover:text-gray-900"
|
class="text-sm text-text-muted hover:text-text"
|
||||||
onclick={() => push("/claims")}
|
onclick={() => push("/claims")}
|
||||||
>
|
>
|
||||||
← Back to claims
|
← Back to claims
|
||||||
@ -256,25 +256,25 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Stepper -->
|
<!-- Stepper -->
|
||||||
<ol class="flex gap-2 text-xs text-gray-500">
|
<ol class="flex gap-2 text-xs text-text-muted">
|
||||||
<li class={step === "pick" ? "font-semibold text-gray-900" : ""}>1. Channel</li>
|
<li class={step === "pick" ? "font-semibold text-text" : ""}>1. Channel</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "identifier" ? "font-semibold text-gray-900" : ""}>2. Identifier</li>
|
<li class={step === "identifier" ? "font-semibold text-text" : ""}>2. Identifier</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "publish" ? "font-semibold text-gray-900" : ""}>3. Publish</li>
|
<li class={step === "publish" ? "font-semibold text-text" : ""}>3. Publish</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "done" ? "font-semibold text-gray-900" : ""}>4. Done</li>
|
<li class={step === "done" ? "font-semibold text-text" : ""}>4. Done</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
{#if step === "pick"}
|
{#if step === "pick"}
|
||||||
<div class="grid sm:grid-cols-2 gap-3">
|
<div class="grid sm:grid-cols-2 gap-3">
|
||||||
{#each CHANNELS as c}
|
{#each CHANNELS as c}
|
||||||
<button
|
<button
|
||||||
class="text-left border border-gray-200 rounded-lg p-4 bg-white hover:border-gray-400 transition"
|
class="text-left border border-border rounded-lg p-4 bg-surface hover:border-accent-dim transition"
|
||||||
onclick={() => pickChannel(c)}
|
onclick={() => pickChannel(c)}
|
||||||
>
|
>
|
||||||
<p class="font-semibold text-gray-900">{c.label}</p>
|
<p class="font-semibold text-text">{c.label}</p>
|
||||||
<p class="text-xs text-gray-500 mt-1 font-mono">{c.key}:<…></p>
|
<p class="text-xs text-text-muted mt-1 font-mono">{c.key}:<…></p>
|
||||||
</button>
|
</button>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
@ -286,7 +286,7 @@
|
|||||||
onsubmit={(e) => { e.preventDefault(); buildAndSign(); }}
|
onsubmit={(e) => { e.preventDefault(); buildAndSign(); }}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700" for="ident">
|
<label class="block text-sm font-medium text-text-secondary" for="ident">
|
||||||
{selected.identifierLabel}
|
{selected.identifierLabel}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@ -294,22 +294,22 @@
|
|||||||
type="text"
|
type="text"
|
||||||
bind:value={identifierInput}
|
bind:value={identifierInput}
|
||||||
placeholder={selected.identifierPlaceholder}
|
placeholder={selected.identifierPlaceholder}
|
||||||
class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md font-mono"
|
class="mt-1 w-full px-3 py-2 border border-border rounded-md font-mono"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
{#if selected.key === "nostr" && nip07Available}
|
{#if selected.key === "nostr" && nip07Available}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="mt-2 inline-flex items-center gap-1 px-3 py-1.5 text-xs border border-purple-300 bg-purple-50 text-purple-800 rounded-md hover:bg-purple-100"
|
class="mt-2 inline-flex items-center gap-1 px-3 py-1.5 text-xs border border-accent/40 bg-accent/10 text-accent rounded-md hover:bg-accent/20"
|
||||||
onclick={fillFromNostrExtension}
|
onclick={fillFromNostrExtension}
|
||||||
>
|
>
|
||||||
⚡ Use my nostr extension
|
⚡ Use my nostr extension
|
||||||
</button>
|
</button>
|
||||||
<span class="ml-2 text-xs text-gray-500">Reads your pubkey via NIP-07 — no copy/paste.</span>
|
<span class="ml-2 text-xs text-text-muted">Reads your pubkey via NIP-07 — no copy/paste.</span>
|
||||||
{/if}
|
{/if}
|
||||||
{#if identifierInput.trim()}
|
{#if identifierInput.trim()}
|
||||||
<p class="mt-1 text-xs text-gray-500">
|
<p class="mt-1 text-xs text-text-muted">
|
||||||
Subject will be: <code class="bg-gray-100 px-1 rounded">{selected.toSubject(identifierInput)}</code>
|
Subject will be: <code class="bg-elevated px-1 rounded">{selected.toSubject(identifierInput)}</code>
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
@ -317,14 +317,14 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => { step = "pick"; selected = null; }}
|
onclick={() => { step = "pick"; selected = null; }}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={identifierInput.trim().length === 0}
|
disabled={identifierInput.trim().length === 0}
|
||||||
>
|
>
|
||||||
Sign claim
|
Sign claim
|
||||||
@ -336,30 +336,30 @@
|
|||||||
{#if step === "publish" && envelope && selected}
|
{#if step === "publish" && envelope && selected}
|
||||||
<div class="grid lg:grid-cols-2 gap-6">
|
<div class="grid lg:grid-cols-2 gap-6">
|
||||||
<section class="space-y-3">
|
<section class="space-y-3">
|
||||||
<h2 class="text-sm font-semibold text-gray-700 uppercase tracking-wide">
|
<h2 class="text-sm font-semibold text-text-secondary uppercase tracking-wide">
|
||||||
1. Publish on {selected.label}
|
1. Publish on {selected.label}
|
||||||
</h2>
|
</h2>
|
||||||
<pre class="whitespace-pre-wrap text-sm bg-gray-50 border border-gray-200 rounded p-4 leading-relaxed">{selected.instructions(envelope.payload.subject)}</pre>
|
<pre class="whitespace-pre-wrap text-sm bg-elevated border border-border rounded p-4 leading-relaxed">{selected.instructions(envelope.payload.subject)}</pre>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<section class="space-y-3">
|
<section class="space-y-3">
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
<h2 class="text-sm font-semibold text-gray-700 uppercase tracking-wide flex-1">
|
<h2 class="text-sm font-semibold text-text-secondary uppercase tracking-wide flex-1">
|
||||||
2. Copy this:
|
2. Copy this:
|
||||||
</h2>
|
</h2>
|
||||||
<div class="flex border border-gray-300 rounded overflow-hidden text-xs">
|
<div class="flex border border-border rounded overflow-hidden text-xs">
|
||||||
<button
|
<button
|
||||||
class={`px-2 py-1 ${format === "compact" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`}
|
class={`px-2 py-1 ${format === "compact" ? "bg-accent text-accent-contrast" : "bg-surface text-text-secondary hover:bg-elevated"}`}
|
||||||
onclick={() => (format = "compact")}
|
onclick={() => (format = "compact")}
|
||||||
title="kez:z1: — zstd + base64url. Fits in tight places like DNS TXT records, QR codes, chat messages."
|
title="kez:z1: — zstd + base64url. Fits in tight places like DNS TXT records, QR codes, chat messages."
|
||||||
>compact</button>
|
>compact</button>
|
||||||
<button
|
<button
|
||||||
class={`px-2 py-1 border-l border-gray-300 ${format === "markdown" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`}
|
class={`px-2 py-1 border-l border-border ${format === "markdown" ? "bg-accent text-accent-contrast" : "bg-surface text-text-secondary hover:bg-elevated"}`}
|
||||||
onclick={() => (format = "markdown")}
|
onclick={() => (format = "markdown")}
|
||||||
title="Human-readable; embeds the JSON inside a ```kez fence. Best for gists and README files."
|
title="Human-readable; embeds the JSON inside a ```kez fence. Best for gists and README files."
|
||||||
>markdown</button>
|
>markdown</button>
|
||||||
<button
|
<button
|
||||||
class={`px-2 py-1 border-l border-gray-300 ${format === "json" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`}
|
class={`px-2 py-1 border-l border-border ${format === "json" ? "bg-accent text-accent-contrast" : "bg-surface text-text-secondary hover:bg-elevated"}`}
|
||||||
onclick={() => (format = "json")}
|
onclick={() => (format = "json")}
|
||||||
title="Raw envelope JSON. Best for .well-known/kez.json and developer tooling."
|
title="Raw envelope JSON. Best for .well-known/kez.json and developer tooling."
|
||||||
>JSON</button>
|
>JSON</button>
|
||||||
@ -367,20 +367,20 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#await renderArtifact(envelope, format)}
|
{#await renderArtifact(envelope, format)}
|
||||||
<pre class="text-xs bg-gray-900 text-gray-100 rounded p-4 font-mono leading-relaxed max-h-96 min-h-32 flex items-center justify-center text-gray-500">Computing…</pre>
|
<pre class="text-xs bg-accent text-text rounded p-4 font-mono leading-relaxed max-h-96 min-h-32 flex items-center justify-center text-text-muted">Computing…</pre>
|
||||||
{:then text}
|
{:then text}
|
||||||
<pre class="text-xs bg-gray-900 text-gray-100 rounded p-4 overflow-x-auto font-mono leading-relaxed max-h-96 overflow-y-auto whitespace-pre-wrap break-all">{text}</pre>
|
<pre class="text-xs bg-accent text-text rounded p-4 overflow-x-auto font-mono leading-relaxed max-h-96 overflow-y-auto whitespace-pre-wrap break-all">{text}</pre>
|
||||||
{#if format === "compact"}
|
{#if format === "compact"}
|
||||||
<p class="text-xs text-gray-500">
|
<p class="text-xs text-text-muted">
|
||||||
{text.length} chars · zstd-compressed signed envelope, base64url-encoded.
|
{text.length} chars · zstd-compressed signed envelope, base64url-encoded.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
{:catch e}
|
{:catch e}
|
||||||
<pre class="text-xs bg-red-50 border border-red-200 text-red-800 rounded p-4">Error: {e.message}</pre>
|
<pre class="text-xs bg-danger/10 border border-danger/40 text-danger rounded p-4">Error: {e.message}</pre>
|
||||||
{/await}
|
{/await}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="px-3 py-2 text-sm border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-3 py-2 text-sm border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={copyArtifact}
|
onclick={copyArtifact}
|
||||||
>
|
>
|
||||||
{copied ? "✓ Copied" : "Copy to clipboard"}
|
{copied ? "✓ Copied" : "Copy to clipboard"}
|
||||||
@ -389,23 +389,23 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if selected.key === "nostr" && nip07Available}
|
{#if selected.key === "nostr" && nip07Available}
|
||||||
<div class="border border-purple-200 bg-purple-50 rounded-lg p-4">
|
<div class="border border-accent/40 bg-accent/10 rounded-lg p-4">
|
||||||
<p class="text-sm font-semibold text-purple-900">⚡ One-click publish via your nostr extension</p>
|
<p class="text-sm font-semibold text-accent">⚡ One-click publish via your nostr extension</p>
|
||||||
<p class="mt-1 text-xs text-purple-800">
|
<p class="mt-1 text-xs text-accent">
|
||||||
Wraps the markdown block in a normal nostr post (kind 1), asks
|
Wraps the markdown block in a normal nostr post (kind 1), asks
|
||||||
your extension to sign it, and broadcasts to the relay pool.
|
your extension to sign it, and broadcasts to the relay pool.
|
||||||
Verifiers (web + Rust CLI) will pick it up automatically.
|
Verifiers (web + Rust CLI) will pick it up automatically.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-3 flex items-center gap-3">
|
<div class="mt-3 flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
class="px-3 py-1.5 text-sm bg-purple-700 text-white rounded-md hover:bg-purple-800 disabled:opacity-50"
|
class="px-3 py-1.5 text-sm bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
onclick={publishViaNostrExtension}
|
onclick={publishViaNostrExtension}
|
||||||
disabled={nostrPublish.status === "pending"}
|
disabled={nostrPublish.status === "pending"}
|
||||||
>
|
>
|
||||||
{nostrPublish.status === "pending" ? "Publishing…" : "Publish to nostr"}
|
{nostrPublish.status === "pending" ? "Publishing…" : "Publish to nostr"}
|
||||||
</button>
|
</button>
|
||||||
{#if nostrPublish.status === "ok"}
|
{#if nostrPublish.status === "ok"}
|
||||||
<span class="text-xs text-green-800">
|
<span class="text-xs text-verified">
|
||||||
✓ Posted to {nostrPublish.result.ok.length} relay(s).
|
✓ Posted to {nostrPublish.result.ok.length} relay(s).
|
||||||
<a
|
<a
|
||||||
href={nostrPublish.result.evidence_url}
|
href={nostrPublish.result.evidence_url}
|
||||||
@ -415,11 +415,11 @@
|
|||||||
>view on njump.me</a>
|
>view on njump.me</a>
|
||||||
</span>
|
</span>
|
||||||
{:else if nostrPublish.status === "error"}
|
{:else if nostrPublish.status === "error"}
|
||||||
<span class="text-xs text-red-800">✗ {nostrPublish.message}</span>
|
<span class="text-xs text-danger">✗ {nostrPublish.message}</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{#if nostrPublish.status === "ok" && nostrPublish.result.failed.length > 0}
|
{#if nostrPublish.status === "ok" && nostrPublish.result.failed.length > 0}
|
||||||
<p class="mt-2 text-xs text-amber-800">
|
<p class="mt-2 text-xs text-warning">
|
||||||
{nostrPublish.result.failed.length} relay(s) didn't ack:
|
{nostrPublish.result.failed.length} relay(s) didn't ack:
|
||||||
{nostrPublish.result.failed.map((f) => f.relay).join(", ")}
|
{nostrPublish.result.failed.map((f) => f.relay).join(", ")}
|
||||||
</p>
|
</p>
|
||||||
@ -427,15 +427,15 @@
|
|||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<div class="flex gap-2 pt-4 border-t border-gray-200">
|
<div class="flex gap-2 pt-4 border-t border-border">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => { step = "identifier"; }}
|
onclick={() => { step = "identifier"; }}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim"
|
||||||
onclick={saveAndDone}
|
onclick={saveAndDone}
|
||||||
>
|
>
|
||||||
Save claim
|
Save claim
|
||||||
@ -444,9 +444,9 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if step === "done" && envelope}
|
{#if step === "done" && envelope}
|
||||||
<div class="border border-green-300 bg-green-50 rounded-lg p-6">
|
<div class="border border-verified/40 bg-verified/10 rounded-lg p-6">
|
||||||
<p class="text-lg font-semibold text-green-900">✓ Claim saved</p>
|
<p class="text-lg font-semibold text-verified">✓ Claim saved</p>
|
||||||
<p class="mt-2 text-sm text-green-800">
|
<p class="mt-2 text-sm text-verified">
|
||||||
You signed a claim for
|
You signed a claim for
|
||||||
<code class="font-mono">{envelope.payload.subject}</code>.
|
<code class="font-mono">{envelope.payload.subject}</code>.
|
||||||
Once you've published the proof on that channel, come back to the
|
Once you've published the proof on that channel, come back to the
|
||||||
@ -455,12 +455,12 @@
|
|||||||
<div class="mt-4 flex gap-2">
|
<div class="mt-4 flex gap-2">
|
||||||
<a
|
<a
|
||||||
href="#/claims"
|
href="#/claims"
|
||||||
class="px-4 py-2 bg-green-700 text-white rounded-md hover:bg-green-800 no-underline"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim no-underline"
|
||||||
>
|
>
|
||||||
Back to claims
|
Back to claims
|
||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 border border-green-300 bg-white rounded-md text-green-800 hover:bg-green-100"
|
class="px-4 py-2 border border-verified/40 bg-surface rounded-md text-verified hover:bg-elevated"
|
||||||
onclick={() => {
|
onclick={() => {
|
||||||
step = "pick";
|
step = "pick";
|
||||||
selected = null;
|
selected = null;
|
||||||
|
|||||||
@ -70,10 +70,10 @@
|
|||||||
|
|
||||||
function statusBadge(c: StoredClaim) {
|
function statusBadge(c: StoredClaim) {
|
||||||
const v = c.last_verify;
|
const v = c.last_verify;
|
||||||
if (!v) return { text: "Not verified", color: "bg-gray-100 text-gray-600" };
|
if (!v) return { text: "Not verified", color: "bg-elevated text-text-secondary" };
|
||||||
if (v.status === "ok") return { text: "✓ Verified", color: "bg-green-100 text-green-800" };
|
if (v.status === "ok") return { text: "✓ Verified", color: "bg-verified/20 text-verified" };
|
||||||
if (v.status === "fail") return { text: "✗ Failed", color: "bg-red-100 text-red-800" };
|
if (v.status === "fail") return { text: "✗ Failed", color: "bg-danger/20 text-danger" };
|
||||||
return { text: "— Skipped", color: "bg-yellow-100 text-yellow-800" };
|
return { text: "— Skipped", color: "bg-warning/20 text-warning" };
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatChecked(iso: string): string {
|
function formatChecked(iso: string): string {
|
||||||
@ -88,11 +88,11 @@
|
|||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<div class="flex items-center justify-between">
|
<div class="flex items-center justify-between">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Claims</h1>
|
<h1 class="text-2xl font-bold text-text">Claims</h1>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
{#if claims.length > 0}
|
{#if claims.length > 0}
|
||||||
<button
|
<button
|
||||||
class="px-3 py-2 text-sm border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
class="px-3 py-2 text-sm border border-border rounded-md text-text-secondary hover:bg-elevated disabled:opacity-50"
|
||||||
onclick={verifyAll}
|
onclick={verifyAll}
|
||||||
disabled={verifying.size > 0}
|
disabled={verifying.size > 0}
|
||||||
>
|
>
|
||||||
@ -101,14 +101,14 @@
|
|||||||
{/if}
|
{/if}
|
||||||
<a
|
<a
|
||||||
href="#/claims/add"
|
href="#/claims/add"
|
||||||
class="px-3 py-2 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-700 no-underline"
|
class="px-3 py-2 text-sm bg-accent text-accent-contrast rounded-md hover:bg-accent-dim no-underline"
|
||||||
>
|
>
|
||||||
+ Add claim
|
+ Add claim
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="text-sm text-gray-700">
|
<p class="text-sm text-text-secondary">
|
||||||
A claim is a signed envelope that says "I control this other account."
|
A claim is a signed envelope that says "I control this other account."
|
||||||
Publish the proof on the channel itself (a public gist, a DNS TXT
|
Publish the proof on the channel itself (a public gist, a DNS TXT
|
||||||
record, a nostr event, etc.) and anyone can verify it without
|
record, a nostr event, etc.) and anyone can verify it without
|
||||||
@ -117,13 +117,13 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<p class="text-sm text-gray-500">Loading…</p>
|
<p class="text-sm text-text-muted">Loading…</p>
|
||||||
{:else if claims.length === 0}
|
{:else if claims.length === 0}
|
||||||
<div class="border border-dashed border-gray-300 rounded-lg p-8 text-center">
|
<div class="border border-dashed border-border rounded-lg p-8 text-center">
|
||||||
<p class="text-gray-500">No claims yet.</p>
|
<p class="text-text-muted">No claims yet.</p>
|
||||||
<a
|
<a
|
||||||
href="#/claims/add"
|
href="#/claims/add"
|
||||||
class="mt-3 inline-block px-3 py-2 text-sm bg-gray-900 text-white rounded-md hover:bg-gray-700 no-underline"
|
class="mt-3 inline-block px-3 py-2 text-sm bg-accent text-accent-contrast rounded-md hover:bg-accent-dim no-underline"
|
||||||
>
|
>
|
||||||
Add your first claim
|
Add your first claim
|
||||||
</a>
|
</a>
|
||||||
@ -134,67 +134,67 @@
|
|||||||
{@const badge = statusBadge(c)}
|
{@const badge = statusBadge(c)}
|
||||||
{@const isVerifying = verifying.has(c.id)}
|
{@const isVerifying = verifying.has(c.id)}
|
||||||
{@const isExpanded = expanded.has(c.id)}
|
{@const isExpanded = expanded.has(c.id)}
|
||||||
<li class="border border-gray-200 rounded-lg p-4 bg-white">
|
<li class="border border-border rounded-lg p-4 bg-surface">
|
||||||
<div class="flex items-start justify-between gap-4">
|
<div class="flex items-start justify-between gap-4">
|
||||||
<div class="flex-1 min-w-0">
|
<div class="flex-1 min-w-0">
|
||||||
<div class="flex items-center gap-2 flex-wrap">
|
<div class="flex items-center gap-2 flex-wrap">
|
||||||
<p class="font-mono font-semibold text-gray-900 truncate">
|
<p class="font-mono font-semibold text-text truncate">
|
||||||
{c.envelope.payload.subject}
|
{c.envelope.payload.subject}
|
||||||
</p>
|
</p>
|
||||||
<span class={`text-xs px-2 py-0.5 rounded font-medium ${badge.color}`}>
|
<span class={`text-xs px-2 py-0.5 rounded font-medium ${badge.color}`}>
|
||||||
{badge.text}
|
{badge.text}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-1 text-xs text-gray-500">
|
<p class="mt-1 text-xs text-text-muted">
|
||||||
Channel: <span class="font-mono">{c.channel}</span> ·
|
Channel: <span class="font-mono">{c.channel}</span> ·
|
||||||
Signed: <span class="font-mono">{c.envelope.payload.created_at}</span>
|
Signed: <span class="font-mono">{c.envelope.payload.created_at}</span>
|
||||||
</p>
|
</p>
|
||||||
{#if c.last_verify}
|
{#if c.last_verify}
|
||||||
<p class="mt-1 text-xs text-gray-700">
|
<p class="mt-1 text-xs text-text-secondary">
|
||||||
{c.last_verify.summary}
|
{c.last_verify.summary}
|
||||||
<span class="text-gray-400">· checked {formatChecked(c.last_verify.checked_at)}</span>
|
<span class="text-text-muted">· checked {formatChecked(c.last_verify.checked_at)}</span>
|
||||||
</p>
|
</p>
|
||||||
{#if c.last_verify.evidence_url || c.last_verify.details}
|
{#if c.last_verify.evidence_url || c.last_verify.details}
|
||||||
<button
|
<button
|
||||||
class="mt-1 text-xs text-gray-500 hover:text-gray-900 underline"
|
class="mt-1 text-xs text-text-muted hover:text-text underline"
|
||||||
onclick={() => toggleExpand(c.id)}
|
onclick={() => toggleExpand(c.id)}
|
||||||
>
|
>
|
||||||
{isExpanded ? "Hide" : "Show"} details
|
{isExpanded ? "Hide" : "Show"} details
|
||||||
</button>
|
</button>
|
||||||
{#if isExpanded}
|
{#if isExpanded}
|
||||||
<div class="mt-2 text-xs bg-gray-50 border border-gray-200 rounded p-3 space-y-2">
|
<div class="mt-2 text-xs bg-elevated border border-border rounded p-3 space-y-2">
|
||||||
{#if c.last_verify.evidence_url}
|
{#if c.last_verify.evidence_url}
|
||||||
<div>
|
<div>
|
||||||
<span class="text-gray-500">Evidence URL:</span>
|
<span class="text-text-muted">Evidence URL:</span>
|
||||||
<a
|
<a
|
||||||
href={c.last_verify.evidence_url}
|
href={c.last_verify.evidence_url}
|
||||||
target="_blank"
|
target="_blank"
|
||||||
rel="noopener noreferrer"
|
rel="noopener noreferrer"
|
||||||
class="font-mono text-blue-700 hover:underline break-all"
|
class="font-mono text-accent hover:underline break-all"
|
||||||
>
|
>
|
||||||
{c.last_verify.evidence_url}
|
{c.last_verify.evidence_url}
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{#if c.last_verify.details}
|
{#if c.last_verify.details}
|
||||||
<pre class="whitespace-pre-wrap font-mono text-gray-700">{c.last_verify.details}</pre>
|
<pre class="whitespace-pre-wrap font-mono text-text-secondary">{c.last_verify.details}</pre>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
{:else if c.published_at}
|
{:else if c.published_at}
|
||||||
<p class="mt-1 text-xs text-green-700">
|
<p class="mt-1 text-xs text-verified">
|
||||||
✓ You marked this published at {c.published_at}
|
✓ You marked this published at {c.published_at}
|
||||||
</p>
|
</p>
|
||||||
{:else}
|
{:else}
|
||||||
<p class="mt-1 text-xs text-amber-700">
|
<p class="mt-1 text-xs text-warning">
|
||||||
⚠ Not marked as published yet
|
⚠ Not marked as published yet
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex flex-col gap-2 shrink-0">
|
<div class="flex flex-col gap-2 shrink-0">
|
||||||
<button
|
<button
|
||||||
class="text-xs px-3 py-1 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50 disabled:opacity-50"
|
class="text-xs px-3 py-1 border border-border rounded-md text-text-secondary hover:bg-elevated disabled:opacity-50"
|
||||||
onclick={() => runVerify(c)}
|
onclick={() => runVerify(c)}
|
||||||
disabled={isVerifying}
|
disabled={isVerifying}
|
||||||
>
|
>
|
||||||
@ -202,14 +202,14 @@
|
|||||||
</button>
|
</button>
|
||||||
{#if !c.published_at}
|
{#if !c.published_at}
|
||||||
<button
|
<button
|
||||||
class="text-xs px-3 py-1 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="text-xs px-3 py-1 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => togglePublished(c)}
|
onclick={() => togglePublished(c)}
|
||||||
>
|
>
|
||||||
Mark published
|
Mark published
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
<button
|
<button
|
||||||
class="text-xs px-3 py-1 border border-gray-300 rounded-md text-gray-700 hover:bg-red-50 hover:border-red-300"
|
class="text-xs px-3 py-1 border border-border rounded-md text-text-secondary hover:bg-danger/10 hover:border-danger"
|
||||||
onclick={() => deleteClaim(c)}
|
onclick={() => deleteClaim(c)}
|
||||||
>
|
>
|
||||||
Remove
|
Remove
|
||||||
|
|||||||
@ -93,27 +93,27 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Create account</h1>
|
<h1 class="text-2xl font-bold text-text">Create account</h1>
|
||||||
|
|
||||||
{#if !serverInfo}
|
{#if !serverInfo}
|
||||||
<p class="text-sm text-amber-700 bg-amber-50 border border-amber-200 rounded p-3">
|
<p class="text-sm text-warning bg-warning/10 border border-warning/40 rounded p-3">
|
||||||
Couldn't reach the chat server. Try refreshing.
|
Couldn't reach the chat server. Try refreshing.
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<!-- Stepper -->
|
<!-- Stepper -->
|
||||||
<ol class="flex gap-2 text-xs text-gray-500">
|
<ol class="flex gap-2 text-xs text-text-muted">
|
||||||
<li class={step === "handle" ? "font-semibold text-gray-900" : ""}>1. Handle</li>
|
<li class={step === "handle" ? "font-semibold text-text" : ""}>1. Handle</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "seed" ? "font-semibold text-gray-900" : ""}>2. Back up seed</li>
|
<li class={step === "seed" ? "font-semibold text-text" : ""}>2. Back up seed</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "confirm" || step === "submitting" ? "font-semibold text-gray-900" : ""}>3. Confirm</li>
|
<li class={step === "confirm" || step === "submitting" ? "font-semibold text-text" : ""}>3. Confirm</li>
|
||||||
<li>→</li>
|
<li>→</li>
|
||||||
<li class={step === "done" ? "font-semibold text-gray-900" : ""}>4. Done</li>
|
<li class={step === "done" ? "font-semibold text-text" : ""}>4. Done</li>
|
||||||
</ol>
|
</ol>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<p class="text-sm text-red-700 bg-red-50 border border-red-200 rounded p-3">
|
<p class="text-sm text-danger bg-danger/10 border border-danger/40 rounded p-3">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
@ -124,48 +124,48 @@
|
|||||||
onsubmit={(e) => { e.preventDefault(); goToSeedStep(); }}
|
onsubmit={(e) => { e.preventDefault(); goToSeedStep(); }}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700" for="handle">
|
<label class="block text-sm font-medium text-text-secondary" for="handle">
|
||||||
Handle
|
Handle
|
||||||
</label>
|
</label>
|
||||||
<div class="mt-1 flex items-stretch border border-gray-300 rounded-md overflow-hidden bg-white">
|
<div class="mt-1 flex items-stretch border border-border rounded-md overflow-hidden bg-surface">
|
||||||
<input
|
<input
|
||||||
id="handle"
|
id="handle"
|
||||||
type="text"
|
type="text"
|
||||||
bind:value={handle}
|
bind:value={handle}
|
||||||
placeholder="tudisco"
|
placeholder="tudisco"
|
||||||
class="flex-1 px-3 py-2 outline-none text-gray-900"
|
class="flex-1 px-3 py-2 outline-none text-text"
|
||||||
autocomplete="off"
|
autocomplete="off"
|
||||||
/>
|
/>
|
||||||
{#if serverInfo}
|
{#if serverInfo}
|
||||||
<span class="px-3 py-2 text-gray-500 bg-gray-50 border-l border-gray-300">
|
<span class="px-3 py-2 text-text-muted bg-elevated border-l border-border">
|
||||||
@{serverInfo.server}
|
@{serverInfo.server}
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
<p class="mt-1 text-xs text-gray-500">
|
<p class="mt-1 text-xs text-text-muted">
|
||||||
Lowercase letters, digits, <code>-</code>, <code>_</code>. 3–32 chars.
|
Lowercase letters, digits, <code>-</code>, <code>_</code>. 3–32 chars.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700" for="pw">
|
<label class="block text-sm font-medium text-text-secondary" for="pw">
|
||||||
Passphrase (encrypts your seed in this browser)
|
Passphrase (encrypts your seed in this browser)
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="pw"
|
id="pw"
|
||||||
type="password"
|
type="password"
|
||||||
bind:value={passphrase}
|
bind:value={passphrase}
|
||||||
class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md outline-none"
|
class="mt-1 w-full px-3 py-2 border border-border rounded-md outline-none"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
bind:value={passphrase2}
|
bind:value={passphrase2}
|
||||||
placeholder="confirm passphrase"
|
placeholder="confirm passphrase"
|
||||||
class="mt-2 w-full px-3 py-2 border border-gray-300 rounded-md outline-none"
|
class="mt-2 w-full px-3 py-2 border border-border rounded-md outline-none"
|
||||||
autocomplete="new-password"
|
autocomplete="new-password"
|
||||||
/>
|
/>
|
||||||
<p class="mt-1 text-xs text-gray-500">
|
<p class="mt-1 text-xs text-text-muted">
|
||||||
The seed itself is your real identity. The passphrase only
|
The seed itself is your real identity. The passphrase only
|
||||||
protects the local copy in this browser.
|
protects the local copy in this browser.
|
||||||
</p>
|
</p>
|
||||||
@ -173,7 +173,7 @@
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={!serverInfo}
|
disabled={!serverInfo}
|
||||||
>
|
>
|
||||||
Continue
|
Continue
|
||||||
@ -183,19 +183,19 @@
|
|||||||
|
|
||||||
{#if step === "seed" && id}
|
{#if step === "seed" && id}
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<div class="border border-amber-300 bg-amber-50 rounded-lg p-4 space-y-3">
|
<div class="border border-warning/40 bg-warning/10 rounded-lg p-4 space-y-3">
|
||||||
<p class="font-semibold text-amber-900">⚠️ Back up your seed now</p>
|
<p class="font-semibold text-warning">⚠️ Back up your seed now</p>
|
||||||
<p class="text-sm text-amber-800">
|
<p class="text-sm text-warning">
|
||||||
This is the only way to recover your account on another device
|
This is the only way to recover your account on another device
|
||||||
(or after clearing this browser). The server doesn't have it.
|
(or after clearing this browser). The server doesn't have it.
|
||||||
Write it down or paste into a password manager.
|
Write it down or paste into a password manager.
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-3 p-3 bg-white border border-amber-200 rounded font-mono text-sm break-all select-all">
|
<div class="mt-3 p-3 bg-surface border border-warning/40 rounded font-mono text-sm break-all select-all">
|
||||||
{seedHex}
|
{seedHex}
|
||||||
</div>
|
</div>
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
class="text-xs px-3 py-1 bg-amber-900 text-white rounded hover:bg-amber-800"
|
class="text-xs px-3 py-1 bg-warning/10 text-accent-contrast rounded hover:bg-warning/20"
|
||||||
onclick={() => copyToClipboard(seedHex)}
|
onclick={() => copyToClipboard(seedHex)}
|
||||||
>
|
>
|
||||||
Copy seed
|
Copy seed
|
||||||
@ -203,7 +203,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label class="flex items-start gap-2 text-sm text-gray-700">
|
<label class="flex items-start gap-2 text-sm text-text-secondary">
|
||||||
<input type="checkbox" bind:checked={seedAck} class="mt-1" />
|
<input type="checkbox" bind:checked={seedAck} class="mt-1" />
|
||||||
I've saved this seed somewhere safe. I understand losing it means
|
I've saved this seed somewhere safe. I understand losing it means
|
||||||
losing my account permanently.
|
losing my account permanently.
|
||||||
@ -211,13 +211,13 @@
|
|||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => { step = "handle"; }}
|
onclick={() => { step = "handle"; }}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={!seedAck}
|
disabled={!seedAck}
|
||||||
onclick={() => { step = "confirm"; }}
|
onclick={() => { step = "confirm"; }}
|
||||||
>
|
>
|
||||||
@ -229,21 +229,21 @@
|
|||||||
|
|
||||||
{#if step === "confirm" && id}
|
{#if step === "confirm" && id}
|
||||||
<div class="space-y-4">
|
<div class="space-y-4">
|
||||||
<p class="text-gray-700">Ready to register:</p>
|
<p class="text-text-secondary">Ready to register:</p>
|
||||||
<div class="border border-gray-200 rounded-lg p-4 bg-gray-50 space-y-2 text-sm">
|
<div class="border border-border rounded-lg p-4 bg-elevated space-y-2 text-sm">
|
||||||
<div><span class="text-gray-500">Handle:</span> <span class="font-mono font-semibold">{handle}@{serverInfo?.server}</span></div>
|
<div><span class="text-text-muted">Handle:</span> <span class="font-mono font-semibold">{handle}@{serverInfo?.server}</span></div>
|
||||||
<div><span class="text-gray-500">Public key:</span> <code class="break-all">{id.identity}</code></div>
|
<div><span class="text-text-muted">Public key:</span> <code class="break-all">{id.identity}</code></div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => { step = "seed"; }}
|
onclick={() => { step = "seed"; }}
|
||||||
>
|
>
|
||||||
Back
|
Back
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={working}
|
disabled={working}
|
||||||
onclick={submitRegistration}
|
onclick={submitRegistration}
|
||||||
>
|
>
|
||||||
@ -254,17 +254,17 @@
|
|||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if step === "submitting"}
|
{#if step === "submitting"}
|
||||||
<p class="text-gray-700">Submitting registration to {serverInfo?.server}…</p>
|
<p class="text-text-secondary">Submitting registration to {serverInfo?.server}…</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if step === "done"}
|
{#if step === "done"}
|
||||||
<div class="border border-green-300 bg-green-50 rounded-lg p-6">
|
<div class="border border-verified/40 bg-verified/10 rounded-lg p-6">
|
||||||
<p class="text-lg font-semibold text-green-900">✓ Account created</p>
|
<p class="text-lg font-semibold text-verified">✓ Account created</p>
|
||||||
<p class="mt-2 text-sm text-green-800">
|
<p class="mt-2 text-sm text-verified">
|
||||||
You are <span class="font-mono font-semibold">{handle}@{serverInfo?.server}</span>.
|
You are <span class="font-mono font-semibold">{handle}@{serverInfo?.server}</span>.
|
||||||
</p>
|
</p>
|
||||||
<button
|
<button
|
||||||
class="mt-4 px-4 py-2 bg-green-700 text-white rounded-md hover:bg-green-800"
|
class="mt-4 px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim"
|
||||||
onclick={() => push("/dashboard")}
|
onclick={() => push("/dashboard")}
|
||||||
>
|
>
|
||||||
Go to dashboard
|
Go to dashboard
|
||||||
|
|||||||
@ -24,27 +24,27 @@
|
|||||||
|
|
||||||
<div class="space-y-8">
|
<div class="space-y-8">
|
||||||
<section>
|
<section>
|
||||||
<h1 class="text-3xl font-bold text-gray-900">Welcome to kez-chat</h1>
|
<h1 class="text-3xl font-bold text-text">Welcome to kez-chat</h1>
|
||||||
<p class="mt-2 text-gray-600">
|
<p class="mt-2 text-text-secondary">
|
||||||
A decentralized identity + chat system. Create an account, link your
|
A decentralized identity + chat system. Create an account, link your
|
||||||
online identities, prove who you are without trusting a central server.
|
online identities, prove who you are without trusting a central server.
|
||||||
</p>
|
</p>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{#if loading}
|
{#if loading}
|
||||||
<p class="text-gray-500 text-sm">Checking local state…</p>
|
<p class="text-text-muted text-sm">Checking local state…</p>
|
||||||
{:else if existing}
|
{:else if existing}
|
||||||
<section class="border border-gray-200 rounded-lg p-6 bg-white">
|
<section class="border border-border rounded-lg p-6 bg-surface">
|
||||||
<p class="text-sm text-gray-500 mb-1">Existing account on this device:</p>
|
<p class="text-sm text-text-muted mb-1">Existing account on this device:</p>
|
||||||
<p class="text-xl font-mono font-semibold text-gray-900">
|
<p class="text-xl font-mono font-semibold text-text">
|
||||||
{existing.handle}@{existing.server}
|
{existing.handle}@{existing.server}
|
||||||
</p>
|
</p>
|
||||||
<p class="mt-1 text-xs font-mono text-gray-500 break-all">
|
<p class="mt-1 text-xs font-mono text-text-muted break-all">
|
||||||
{existing.primary}
|
{existing.primary}
|
||||||
</p>
|
</p>
|
||||||
<div class="mt-4 flex gap-3">
|
<div class="mt-4 flex gap-3">
|
||||||
<button
|
<button
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim"
|
||||||
onclick={() => push("/unlock")}
|
onclick={() => push("/unlock")}
|
||||||
>
|
>
|
||||||
Unlock
|
Unlock
|
||||||
@ -54,21 +54,21 @@
|
|||||||
{:else}
|
{:else}
|
||||||
<section class="grid sm:grid-cols-2 gap-4">
|
<section class="grid sm:grid-cols-2 gap-4">
|
||||||
<button
|
<button
|
||||||
class="text-left border border-gray-200 rounded-lg p-6 bg-white hover:border-gray-400 transition"
|
class="text-left border border-border rounded-lg p-6 bg-surface hover:border-accent-dim transition"
|
||||||
onclick={() => push("/create")}
|
onclick={() => push("/create")}
|
||||||
>
|
>
|
||||||
<h2 class="text-lg font-semibold text-gray-900">Create a new account</h2>
|
<h2 class="text-lg font-semibold text-text">Create a new account</h2>
|
||||||
<p class="mt-1 text-sm text-gray-600">
|
<p class="mt-1 text-sm text-text-secondary">
|
||||||
Generate a fresh key pair, pick a handle, back up your seed phrase.
|
Generate a fresh key pair, pick a handle, back up your seed phrase.
|
||||||
</p>
|
</p>
|
||||||
</button>
|
</button>
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="text-left border border-gray-200 rounded-lg p-6 bg-white hover:border-gray-400 transition"
|
class="text-left border border-border rounded-lg p-6 bg-surface hover:border-accent-dim transition"
|
||||||
onclick={() => push("/restore")}
|
onclick={() => push("/restore")}
|
||||||
>
|
>
|
||||||
<h2 class="text-lg font-semibold text-gray-900">Restore from seed</h2>
|
<h2 class="text-lg font-semibold text-text">Restore from seed</h2>
|
||||||
<p class="mt-1 text-sm text-gray-600">
|
<p class="mt-1 text-sm text-text-secondary">
|
||||||
Have a 64-char hex seed from another device? Paste it to recover
|
Have a 64-char hex seed from another device? Paste it to recover
|
||||||
your identity.
|
your identity.
|
||||||
</p>
|
</p>
|
||||||
@ -76,11 +76,11 @@
|
|||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<section class="text-sm text-gray-500 border-t border-gray-200 pt-6">
|
<section class="text-sm text-text-muted border-t border-border pt-6">
|
||||||
<p class="font-medium text-gray-700">What is this?</p>
|
<p class="font-medium text-text-secondary">What is this?</p>
|
||||||
<p class="mt-1">
|
<p class="mt-1">
|
||||||
Your identity is an Ed25519 keypair — not a username + password.
|
Your identity is an Ed25519 keypair — not a username + password.
|
||||||
Account creation makes a handle (<code class="bg-gray-100 px-1 rounded">tudisco@kez.lat</code>),
|
Account creation makes a handle (<code class="bg-elevated px-1 rounded">tudisco@kez.lat</code>),
|
||||||
stores your seed locally under a passphrase, and registers your public
|
stores your seed locally under a passphrase, and registers your public
|
||||||
key with this server. There's no email, no recovery flow — keep the
|
key with this server. There's no email, no recovery flow — keep the
|
||||||
seed safe.
|
seed safe.
|
||||||
|
|||||||
@ -62,9 +62,9 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-6">
|
<div class="space-y-6">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Restore from seed</h1>
|
<h1 class="text-2xl font-bold text-text">Restore from seed</h1>
|
||||||
|
|
||||||
<p class="text-sm text-gray-700 bg-amber-50 border border-amber-200 rounded p-3">
|
<p class="text-sm text-text-secondary bg-warning/10 border border-warning/40 rounded p-3">
|
||||||
<strong>v0.1 limitation:</strong> the seed alone doesn't tell us which
|
<strong>v0.1 limitation:</strong> the seed alone doesn't tell us which
|
||||||
handle to restore. For now this flow doesn't work end-to-end — we'll
|
handle to restore. For now this flow doesn't work end-to-end — we'll
|
||||||
add <code>GET /v1/by-primary/<id></code> on the server in v0.2
|
add <code>GET /v1/by-primary/<id></code> on the server in v0.2
|
||||||
@ -76,37 +76,37 @@
|
|||||||
onsubmit={(e) => { e.preventDefault(); submit(); }}
|
onsubmit={(e) => { e.preventDefault(); submit(); }}
|
||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700" for="seed">
|
<label class="block text-sm font-medium text-text-secondary" for="seed">
|
||||||
Seed (64 hex characters)
|
Seed (64 hex characters)
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
id="seed"
|
id="seed"
|
||||||
bind:value={seedHex}
|
bind:value={seedHex}
|
||||||
rows="3"
|
rows="3"
|
||||||
class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md font-mono text-sm"
|
class="mt-1 w-full px-3 py-2 border border-border rounded-md font-mono text-sm"
|
||||||
></textarea>
|
></textarea>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<label class="block text-sm font-medium text-gray-700" for="pw">
|
<label class="block text-sm font-medium text-text-secondary" for="pw">
|
||||||
New passphrase for this device
|
New passphrase for this device
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
id="pw"
|
id="pw"
|
||||||
type="password"
|
type="password"
|
||||||
bind:value={passphrase}
|
bind:value={passphrase}
|
||||||
class="mt-1 w-full px-3 py-2 border border-gray-300 rounded-md"
|
class="mt-1 w-full px-3 py-2 border border-border rounded-md"
|
||||||
/>
|
/>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
bind:value={passphrase2}
|
bind:value={passphrase2}
|
||||||
placeholder="confirm"
|
placeholder="confirm"
|
||||||
class="mt-2 w-full px-3 py-2 border border-gray-300 rounded-md"
|
class="mt-2 w-full px-3 py-2 border border-border rounded-md"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<p class="text-sm text-red-700 bg-red-50 border border-red-200 rounded p-3">
|
<p class="text-sm text-danger bg-danger/10 border border-danger/40 rounded p-3">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
@ -114,14 +114,14 @@
|
|||||||
<div class="flex gap-2">
|
<div class="flex gap-2">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="px-4 py-2 border border-gray-300 rounded-md text-gray-700 hover:bg-gray-50"
|
class="px-4 py-2 border border-border rounded-md text-text-secondary hover:bg-elevated"
|
||||||
onclick={() => push("/")}
|
onclick={() => push("/")}
|
||||||
>
|
>
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={working}
|
disabled={working}
|
||||||
>
|
>
|
||||||
{working ? "Restoring…" : "Restore"}
|
{working ? "Restoring…" : "Restore"}
|
||||||
|
|||||||
@ -82,10 +82,10 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="space-y-6 max-w-md">
|
<div class="space-y-6 max-w-md">
|
||||||
<h1 class="text-2xl font-bold text-gray-900">Unlock</h1>
|
<h1 class="text-2xl font-bold text-text">Unlock</h1>
|
||||||
|
|
||||||
{#if meta}
|
{#if meta}
|
||||||
<p class="text-sm text-gray-600">
|
<p class="text-sm text-text-secondary">
|
||||||
Unlocking <span class="font-mono font-semibold">{meta.handle}@{meta.server}</span>
|
Unlocking <span class="font-mono font-semibold">{meta.handle}@{meta.server}</span>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
@ -93,7 +93,7 @@
|
|||||||
<div class="space-y-3">
|
<div class="space-y-3">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="w-full px-4 py-3 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50 flex items-center justify-center gap-2"
|
class="w-full px-4 py-3 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50 flex items-center justify-center gap-2"
|
||||||
onclick={submitBiometric}
|
onclick={submitBiometric}
|
||||||
disabled={working}
|
disabled={working}
|
||||||
>
|
>
|
||||||
@ -103,7 +103,7 @@
|
|||||||
{#if !showPassphrase}
|
{#if !showPassphrase}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="text-xs text-gray-500 hover:text-gray-900 underline"
|
class="text-xs text-text-muted hover:text-text underline"
|
||||||
onclick={() => (showPassphrase = true)}
|
onclick={() => (showPassphrase = true)}
|
||||||
>
|
>
|
||||||
Use passphrase instead
|
Use passphrase instead
|
||||||
@ -122,31 +122,31 @@
|
|||||||
bind:value={passphrase}
|
bind:value={passphrase}
|
||||||
placeholder="passphrase"
|
placeholder="passphrase"
|
||||||
autocomplete="current-password"
|
autocomplete="current-password"
|
||||||
class="w-full px-3 py-2 border border-gray-300 rounded-md"
|
class="w-full px-3 py-2 border border-border rounded-md"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{#if error}
|
{#if error}
|
||||||
<p class="text-sm text-red-700 bg-red-50 border border-red-200 rounded p-3">
|
<p class="text-sm text-danger bg-danger/10 border border-danger/40 rounded p-3">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="px-4 py-2 bg-gray-900 text-white rounded-md hover:bg-gray-700 disabled:opacity-50"
|
class="px-4 py-2 bg-accent text-accent-contrast rounded-md hover:bg-accent-dim disabled:opacity-50"
|
||||||
disabled={working || passphrase.length === 0}
|
disabled={working || passphrase.length === 0}
|
||||||
>
|
>
|
||||||
{working ? "Unlocking…" : "Unlock"}
|
{working ? "Unlocking…" : "Unlock"}
|
||||||
</button>
|
</button>
|
||||||
</form>
|
</form>
|
||||||
{:else if error}
|
{:else if error}
|
||||||
<p class="text-sm text-red-700 bg-red-50 border border-red-200 rounded p-3">
|
<p class="text-sm text-danger bg-danger/10 border border-danger/40 rounded p-3">
|
||||||
{error}
|
{error}
|
||||||
</p>
|
</p>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
<button
|
<button
|
||||||
class="text-xs text-gray-500 hover:text-red-700 underline"
|
class="text-xs text-text-muted hover:text-danger underline"
|
||||||
onclick={forget}
|
onclick={forget}
|
||||||
>
|
>
|
||||||
Forget this account on this device
|
Forget this account on this device
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user