Restructures the unlock screen so the form sits in a glass card with a primary 'unlock vault' button. Logo, brand, and tagline are grouped as a lockup. Open-vault and settings are demoted to secondary buttons. Body gets the .surface-backdrop wrapper.
70 lines
2.8 KiB
TypeScript
70 lines
2.8 KiB
TypeScript
/// Unlock view — passphrase input with ENTER to submit.
|
|
|
|
import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../../shared/state';
|
|
import type { ItemId, ManifestEntry } from '../../shared/types';
|
|
|
|
export function renderUnlock(app: HTMLElement): void {
|
|
const state = getState();
|
|
|
|
app.innerHTML = `
|
|
<div class="pad" style="text-align:center; padding-top:32px;">
|
|
<div class="logo-lockup" style="margin-bottom:24px;">
|
|
<img class="brand-logo" src="icons/relicario-logo.svg" alt="">
|
|
<div class="brand">Relicario</div>
|
|
<p class="tagline">two-factor vault</p>
|
|
</div>
|
|
|
|
<div class="glass" style="padding:16px; text-align:left; margin-bottom:16px;">
|
|
<div class="card-label" style="font-size:10px;text-transform:uppercase;letter-spacing:1.2px;color:var(--text-muted);margin-bottom:8px;">unlock</div>
|
|
<div class="form-group" style="margin-bottom:10px;">
|
|
<input
|
|
type="password"
|
|
id="passphrase-input"
|
|
placeholder="passphrase"
|
|
autocomplete="off"
|
|
${state.loading ? 'disabled' : ''}
|
|
>
|
|
</div>
|
|
${state.loading ? '<div style="margin:12px 0;"><span class="spinner"></span></div>' : ''}
|
|
${state.error ? `<div class="error">${escapeHtml(state.error)}</div>` : ''}
|
|
<button class="btn-primary" id="unlock-btn" style="width:100%;justify-content:center;" ${state.loading ? 'disabled' : ''}>unlock vault</button>
|
|
</div>
|
|
|
|
<div style="display:flex; gap:8px; justify-content:center;">
|
|
<button class="btn-secondary" id="vault-btn">open vault</button>
|
|
<button class="btn-secondary" id="settings-btn">settings</button>
|
|
</div>
|
|
</div>
|
|
`;
|
|
|
|
const input = document.getElementById('passphrase-input') as HTMLInputElement;
|
|
const unlockBtn = document.getElementById('unlock-btn') as HTMLButtonElement | null;
|
|
|
|
const submit = async () => {
|
|
const passphrase = input.value;
|
|
if (!passphrase) return;
|
|
setState({ loading: true, error: null });
|
|
const resp = await sendMessage({ type: 'unlock', passphrase });
|
|
if (resp.ok) {
|
|
const listResp = await sendMessage({ type: 'list_items' });
|
|
if (listResp.ok) {
|
|
const data = listResp.data as { items: Array<[ItemId, ManifestEntry]> };
|
|
navigate('list', { entries: data.items });
|
|
} else {
|
|
setState({ loading: false, error: listResp.error });
|
|
}
|
|
} else {
|
|
setState({ loading: false, error: resp.error });
|
|
}
|
|
};
|
|
|
|
if (input && !state.loading) {
|
|
input.focus();
|
|
input.addEventListener('keydown', (e) => { if (e.key === 'Enter') submit(); });
|
|
}
|
|
unlockBtn?.addEventListener('click', submit);
|
|
|
|
document.getElementById('vault-btn')?.addEventListener('click', () => openVaultTab());
|
|
document.getElementById('settings-btn')?.addEventListener('click', () => navigate('settings'));
|
|
}
|