feat(ext/setup): vault-presence probe + mode-mismatch banners on Step 2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
adlee-was-taken
2026-04-27 18:22:45 -04:00
parent a182c1ac5a
commit f44aedfa76
2 changed files with 87 additions and 1 deletions

View File

@@ -1256,3 +1256,23 @@ textarea {
.mode-card.active { border-color: #58a6ff; background: rgba(88,166,255,0.08); } .mode-card.active { border-color: #58a6ff; background: rgba(88,166,255,0.08); }
.mode-card-title { font-weight: 600; margin-bottom: 4px; font-size: 14px; } .mode-card-title { font-weight: 600; margin-bottom: 4px; font-size: 14px; }
.mode-card-blurb { color: #8b949e; font-size: 12px; margin: 0; line-height: 1.5; } .mode-card-blurb { color: #8b949e; font-size: 12px; margin: 0; line-height: 1.5; }
/* --- Setup wizard probe banners (Step 2) --- */
.banner { padding: 12px; border-radius: 6px; margin: 12px 0; font-size: 13px; }
.banner-ok {
background: rgba(46,160,67,0.10);
border: 1px solid rgba(46,160,67,0.4);
}
.banner-warn {
background: rgba(218,54,51,0.08);
border: 1px solid rgba(218,54,51,0.4);
}
.banner strong { display: block; margin-bottom: 4px; }
.banner p { margin: 6px 0; line-height: 1.5; color: #c9d1d9; }
.banner code {
font-family: ui-monospace, SFMono-Regular, monospace;
font-size: 12px;
background: #21262d;
padding: 1px 5px;
border-radius: 3px;
}

View File

@@ -7,6 +7,7 @@
/// Step 5: Finish (download reference image, push config to extension or copy JSON) /// Step 5: Finish (download reference image, push config to extension or copy JSON)
import { createGitHost, uint8ArrayToBase64 } from '../service-worker/git-host'; import { createGitHost, uint8ArrayToBase64 } from '../service-worker/git-host';
import { probeVault } from './probe';
import type { VaultConfig } from '../shared/types'; import type { VaultConfig } from '../shared/types';
// --- WASM module (loaded dynamically) --- // --- WASM module (loaded dynamically) ---
@@ -400,7 +401,55 @@ function attachStep1(): void {
// --- Step 2: Configure Connection --- // --- Step 2: Configure Connection ---
function renderProbeBanner(): string {
const probe = state.vaultProbe;
if (!state.connectionTested || !probe) return '';
const meta = probe.lastCommit
? `Last commit: <code>${escapeHtml(probe.lastCommit.sha)}</code> by ${escapeHtml(probe.lastCommit.author)} on ${escapeHtml(probe.lastCommit.date.slice(0, 10))}.`
: '';
if (state.mode === 'new' && probe.exists) {
return `
<div class="banner banner-warn">
<strong>⚠ This repository already contains a relicario vault.</strong>
<p>${meta}</p>
<p>Creating a new vault here would overwrite the existing one and <strong>destroy all data inside</strong>.
To use this vault on this device, switch to <em>attach</em> mode instead.
If you really mean to start over, delete the repository via your git host's web UI and come back here.</p>
<div class="form-actions">
<button class="btn" id="switch-mode-btn" data-target="attach">switch to attach mode</button>
</div>
</div>`;
}
if (state.mode === 'attach' && !probe.exists) {
return `
<div class="banner banner-warn">
<strong>No vault found in this repo.</strong>
<p>Did you mean to create a new vault?</p>
<div class="form-actions">
<button class="btn" id="switch-mode-btn" data-target="new">switch to new-vault mode</button>
</div>
</div>`;
}
if (state.mode === 'attach' && probe.exists) {
return `
<div class="banner banner-ok">
<strong>✓ Existing vault found.</strong>
<p>${meta}</p>
<p>Continue to attach this device.</p>
</div>`;
}
// mode = new, !exists
return `
<div class="banner banner-ok">
<strong>✓ Repo is empty — ready to create a new vault.</strong>
</div>`;
}
function renderStep2(): string { function renderStep2(): string {
const probe = state.vaultProbe;
const modeMismatch =
!!probe && ((state.mode === 'new' && probe.exists) || (state.mode === 'attach' && !probe.exists));
const nextDisabled = !state.connectionTested || !probe || modeMismatch;
return ` return `
<div class="wizard-step"> <div class="wizard-step">
<h3>configure connection</h3> <h3>configure connection</h3>
@@ -420,9 +469,10 @@ function renderStep2(): string {
<button class="btn" id="test-btn">test connection</button> <button class="btn" id="test-btn">test connection</button>
${state.connectionTested ? '<span class="test-result pass">connected</span>' : ''} ${state.connectionTested ? '<span class="test-result pass">connected</span>' : ''}
</div> </div>
${renderProbeBanner()}
<div class="form-actions" style="margin-top:12px;"> <div class="form-actions" style="margin-top:12px;">
<button class="btn" id="back-btn">back</button> <button class="btn" id="back-btn">back</button>
<button class="btn btn-primary" id="next-btn" ${!state.connectionTested ? 'disabled' : ''}>next</button> <button class="btn btn-primary" id="next-btn" ${nextDisabled ? 'disabled' : ''}>next</button>
</div> </div>
</div> </div>
`; `;
@@ -430,6 +480,8 @@ function renderStep2(): string {
function attachStep2(): void { function attachStep2(): void {
document.getElementById('test-btn')?.addEventListener('click', async () => { document.getElementById('test-btn')?.addEventListener('click', async () => {
state.connectionTested = false;
state.vaultProbe = null;
const hostUrl = state.hostType === 'github' const hostUrl = state.hostType === 'github'
? 'https://api.github.com' ? 'https://api.github.com'
: (document.getElementById('host-url') as HTMLInputElement).value.trim(); : (document.getElementById('host-url') as HTMLInputElement).value.trim();
@@ -456,8 +508,15 @@ function attachStep2(): void {
await host.listDir(''); await host.listDir('');
state.connectionTested = true; state.connectionTested = true;
state.error = null; state.error = null;
try {
state.vaultProbe = await probeVault(host);
} catch (probeErr) {
state.vaultProbe = null;
state.error = `Could not check repo state: ${probeErr instanceof Error ? probeErr.message : String(probeErr)}`;
}
} catch (err: unknown) { } catch (err: unknown) {
state.connectionTested = false; state.connectionTested = false;
state.vaultProbe = null;
state.error = `Connection failed: ${err instanceof Error ? err.message : String(err)}`; state.error = `Connection failed: ${err instanceof Error ? err.message : String(err)}`;
} }
render(); render();
@@ -475,6 +534,13 @@ function attachStep2(): void {
state.error = null; state.error = null;
render(); render();
}); });
document.getElementById('switch-mode-btn')?.addEventListener('click', (e) => {
const target = (e.currentTarget as HTMLElement).dataset.target as 'new' | 'attach';
state.mode = target;
state.error = null;
render();
});
} }
// --- Step 3 (new-vault variant): Create Vault --- // --- Step 3 (new-vault variant): Create Vault ---