/// Typed-item add/edit form dispatcher. Each type's renderForm lives in /// its own module under ./types/. Document stays "coming soon" until γ. import { navigate, getState, setState, escapeHtml, popOutToTab, isInTab } from '../../shared/state'; import type { Item, ItemType } from '../../shared/types'; import { GLYPH_TYPE_LOGIN, GLYPH_TYPE_SECURE_NOTE, GLYPH_TYPE_TOTP, GLYPH_TYPE_CARD, GLYPH_TYPE_IDENTITY, GLYPH_TYPE_KEY, GLYPH_TYPE_DOCUMENT, } from '../../shared/glyphs'; const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string; description: string }> = [ { type: 'login', icon: GLYPH_TYPE_LOGIN, label: 'Login', description: 'Username + password' }, { type: 'secure_note', icon: GLYPH_TYPE_SECURE_NOTE, label: 'Secure Note', description: 'Encrypted text note' }, { type: 'identity', icon: GLYPH_TYPE_IDENTITY, label: 'Identity', description: 'Personal details' }, { type: 'card', icon: GLYPH_TYPE_CARD, label: 'Card', description: 'Credit / debit card' }, { type: 'key', icon: GLYPH_TYPE_KEY, label: 'SSH / API Key', description: 'Keys and tokens' }, { type: 'document', icon: GLYPH_TYPE_DOCUMENT, label: 'Document', description: 'File attachment' }, { type: 'totp', icon: GLYPH_TYPE_TOTP, label: 'TOTP', description: '2FA authenticator' }, ]; import * as login from './types/login'; import * as secureNote from './types/secure-note'; import * as identity from './types/identity'; import * as card from './types/card'; import * as key from './types/key'; import * as totp from './types/totp'; import * as documentType from './types/document'; export function renderItemForm(app: HTMLElement, mode: 'add' | 'edit'): void { login.teardown(); secureNote.teardown(); identity.teardown(); card.teardown(); key.teardown(); totp.teardown(); documentType.teardown(); const state = getState(); const existing = mode === 'edit' ? state.selectedItem : null; // Show type selection for new items when no type is pre-selected if (mode === 'add' && !state.newType) { renderTypeSelection(app); return; } const type: ItemType = existing?.type ?? state.newType ?? 'login'; switch (type) { case 'login': return login.renderForm(app, mode, existing, { surface: isInTab() ? 'fullscreen' : 'popup', externalActions: isInTab() }); case 'secure_note': return secureNote.renderForm(app, mode, existing); case 'identity': return identity.renderForm(app, mode, existing); case 'card': return card.renderForm(app, mode, existing); case 'key': return key.renderForm(app, mode, existing); case 'totp': return totp.renderForm(app, mode, existing); case 'document': return documentType.renderForm(app, mode, existing); } } function renderTypeSelection(app: HTMLElement): void { app.innerHTML = `
New item ${isInTab() ? '' : ''}
${TYPE_OPTIONS.map((opt) => ` `).join('')}
Esc back
`; document.getElementById('back-btn')?.addEventListener('click', () => navigate('list')); document.getElementById('popout-btn')?.addEventListener('click', popOutToTab); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') navigate('list'); }, { once: true }); document.querySelectorAll('[data-type]').forEach((btn) => { btn.addEventListener('click', () => { const type = btn.dataset.type as ItemType; setState({ newType: type }); renderItemForm(app, 'add'); }); }); } function renderComingSoon(app: HTMLElement, type: ItemType): void { app.innerHTML = `
${type.replace('_', ' ')}

Editing ${type} items is not available yet.

`; document.getElementById('back-btn')?.addEventListener('click', () => navigate('list')); }