5 Commits

3 changed files with 55 additions and 12 deletions

View File

@@ -3,6 +3,12 @@
/// to the detail view. /// to the detail view.
import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../../shared/state'; import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../../shared/state';
import {
GLYPH_VAULT_TAB,
GLYPH_DEVICES, GLYPH_LOCK,
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';
import type { ItemId, ItemType, ManifestEntry, Item } from '../../shared/types'; import type { ItemId, ItemType, ManifestEntry, Item } from '../../shared/types';
/// Extract the display hostname from an icon_hint or fallback to the first tag. /// Extract the display hostname from an icon_hint or fallback to the first tag.
@@ -12,16 +18,16 @@ function metaLine(e: ManifestEntry): string {
return ''; return '';
} }
/// Emoji icon per item type. Placeholder until we ship real SVG icons. /// Glyph icon per item type.
function typeIcon(t: ItemType): string { function typeIcon(t: ItemType): string {
switch (t) { switch (t) {
case 'login': return '🔑'; case 'login': return GLYPH_TYPE_LOGIN;
case 'secure_note': return '📝'; case 'secure_note': return GLYPH_TYPE_SECURE_NOTE;
case 'identity': return '🪪'; case 'identity': return GLYPH_TYPE_IDENTITY;
case 'card': return '💳'; case 'card': return GLYPH_TYPE_CARD;
case 'key': return '🗝'; case 'key': return GLYPH_TYPE_KEY;
case 'document': return '📄'; case 'document': return GLYPH_TYPE_DOCUMENT;
case 'totp': return '⏱'; case 'totp': return GLYPH_TYPE_TOTP;
} }
} }
@@ -31,7 +37,7 @@ function buildRowsHtml(): string {
return filtered.length > 0 return filtered.length > 0
? filtered.map(([id, e], i) => ` ? filtered.map(([id, e], i) => `
<div class="entry-row ${i === state.selectedIndex ? 'selected' : ''}" data-id="${escapeHtml(id)}" data-index="${i}"> <div class="entry-row ${i === state.selectedIndex ? 'selected' : ''}" data-id="${escapeHtml(id)}" data-index="${i}">
<span class="entry-name"><span class="type-icon" aria-hidden="true">${typeIcon(e.type)}</span> ${escapeHtml(e.title)}${e.attachment_summaries.length > 0 ? ' <span class="entry-row__attach-indicator" title="has attachments">📎</span>' : ''}</span> <span class="entry-name"><span class="type-icon" aria-hidden="true">${typeIcon(e.type)}</span> ${escapeHtml(e.title)}${e.attachment_summaries.length > 0 ? ' <span class="entry-row__attach-indicator" title="has attachments"></span>' : ''}</span>
<span class="entry-meta">${escapeHtml(metaLine(e))}</span> <span class="entry-meta">${escapeHtml(metaLine(e))}</span>
</div> </div>
`).join('') `).join('')
@@ -66,7 +72,7 @@ export function renderItemList(app: HTMLElement): void {
<button class="btn" id="new-btn" style="font-size:11px;">+ new</button> <button class="btn" id="new-btn" style="font-size:11px;">+ new</button>
<button class="btn" id="sync-btn" style="font-size:11px;">sync</button> <button class="btn" id="sync-btn" style="font-size:11px;">sync</button>
<span style="flex:1;"></span> <span style="flex:1;"></span>
<button class="btn" id="vault-btn" style="font-size:11px;" title="Open vault (Shift+F)">&#x2934;</button> <button class="btn" id="vault-btn" style="font-size:11px;" title="Open vault (Shift+F)">${GLYPH_VAULT_TAB}</button>
<button class="btn" id="settings-btn" style="font-size:11px;">settings</button> <button class="btn" id="settings-btn" style="font-size:11px;">settings</button>
<button class="btn" id="lock-btn" style="font-size:11px;">lock</button> <button class="btn" id="lock-btn" style="font-size:11px;">lock</button>
</div> </div>
@@ -253,8 +259,8 @@ function handleListKeydown(e: KeyboardEvent): void {
// ---------------------------------------------------------------------- // ----------------------------------------------------------------------
const SETTINGS_OPTIONS: Array<{ view: 'settings' | 'settings-vault'; icon: string; label: string }> = [ const SETTINGS_OPTIONS: Array<{ view: 'settings' | 'settings-vault'; icon: string; label: string }> = [
{ view: 'settings', icon: '🖥', label: 'device settings' }, { view: 'settings', icon: GLYPH_DEVICES, label: 'device settings' },
{ view: 'settings-vault', icon: '🔐', label: 'vault settings' }, { view: 'settings-vault', icon: GLYPH_LOCK, label: 'vault settings' },
]; ];
function showSettingsPicker(anchor: HTMLElement): void { function showSettingsPicker(anchor: HTMLElement): void {

View File

@@ -41,3 +41,30 @@ describe('glyph constants', () => {
expect(GLYPH_NEXT).toBe('▸'); expect(GLYPH_NEXT).toBe('▸');
}); });
}); });
describe('Stream A glyphs (vault tab + type icons)', () => {
it('exports GLYPH_VAULT_TAB as U+29C9', () => {
expect(glyphs.GLYPH_VAULT_TAB).toBe('⧉');
});
it('exports per-type glyph constants', () => {
expect(glyphs.GLYPH_TYPE_LOGIN).toBe('◉');
expect(glyphs.GLYPH_TYPE_SECURE_NOTE).toBe('◫');
expect(glyphs.GLYPH_TYPE_TOTP).toBe('⊡');
expect(glyphs.GLYPH_TYPE_CARD).toBe('▭');
expect(glyphs.GLYPH_TYPE_IDENTITY).toBe('⌬');
expect(glyphs.GLYPH_TYPE_KEY).toBe('⊹');
expect(glyphs.GLYPH_TYPE_DOCUMENT).toBe('≡');
});
it('per-type glyphs are single codepoints (no emoji)', () => {
const typeGlyphs = [
glyphs.GLYPH_TYPE_LOGIN, glyphs.GLYPH_TYPE_SECURE_NOTE, glyphs.GLYPH_TYPE_TOTP,
glyphs.GLYPH_TYPE_CARD, glyphs.GLYPH_TYPE_IDENTITY, glyphs.GLYPH_TYPE_KEY,
glyphs.GLYPH_TYPE_DOCUMENT,
];
for (const g of typeGlyphs) {
expect([...g].length).toBe(1);
}
});
});

View File

@@ -18,6 +18,16 @@ export const GLYPH_SETTINGS = '⚙'; // sidebar settings nav
export const GLYPH_LOCK = '⏻'; // sidebar lock nav export const GLYPH_LOCK = '⏻'; // sidebar lock nav
export const GLYPH_NEXT = '▸'; // forward / next button (matches ▾/▸ disclosure family) export const GLYPH_NEXT = '▸'; // forward / next button (matches ▾/▸ disclosure family)
export const GLYPH_VAULT_TAB = '⧉'; // U+29C9 pop-out to fullscreen vault tab
export const GLYPH_TYPE_LOGIN = '◉'; // login
export const GLYPH_TYPE_SECURE_NOTE = '◫'; // secure note
export const GLYPH_TYPE_TOTP = '⊡'; // totp / 2FA
export const GLYPH_TYPE_CARD = '▭'; // card
export const GLYPH_TYPE_IDENTITY = '⌬'; // identity
export const GLYPH_TYPE_KEY = '⊹'; // SSH / API key
export const GLYPH_TYPE_DOCUMENT = '≡'; // document
/// Inline HTML snippet for the required-field pill. Use after a label's text: /// Inline HTML snippet for the required-field pill. Use after a label's text:
/// `<label class="label" for="f-title">title ${REQUIRED_PILL_HTML}</label>` /// `<label class="label" for="f-title">title ${REQUIRED_PILL_HTML}</label>`
/// ///