Compare commits
5 Commits
feature/v0
...
8b197a7525
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8b197a7525 | ||
|
|
117716f6cf | ||
|
|
c5e8b52e12 | ||
|
|
a1b66a9147 | ||
|
|
934dfe05c2 |
@@ -3,6 +3,12 @@
|
||||
/// to the detail view.
|
||||
|
||||
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';
|
||||
|
||||
/// Extract the display hostname from an icon_hint or fallback to the first tag.
|
||||
@@ -12,16 +18,16 @@ function metaLine(e: ManifestEntry): string {
|
||||
return '';
|
||||
}
|
||||
|
||||
/// Emoji icon per item type. Placeholder until we ship real SVG icons.
|
||||
/// Glyph icon per item type.
|
||||
function typeIcon(t: ItemType): string {
|
||||
switch (t) {
|
||||
case 'login': return '🔑';
|
||||
case 'secure_note': return '📝';
|
||||
case 'identity': return '🪪';
|
||||
case 'card': return '💳';
|
||||
case 'key': return '🗝';
|
||||
case 'document': return '📄';
|
||||
case 'totp': return '⏱';
|
||||
case 'login': return GLYPH_TYPE_LOGIN;
|
||||
case 'secure_note': return GLYPH_TYPE_SECURE_NOTE;
|
||||
case 'identity': return GLYPH_TYPE_IDENTITY;
|
||||
case 'card': return GLYPH_TYPE_CARD;
|
||||
case 'key': return GLYPH_TYPE_KEY;
|
||||
case 'document': return GLYPH_TYPE_DOCUMENT;
|
||||
case 'totp': return GLYPH_TYPE_TOTP;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,7 +37,7 @@ function buildRowsHtml(): string {
|
||||
return filtered.length > 0
|
||||
? filtered.map(([id, e], 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>
|
||||
</div>
|
||||
`).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="sync-btn" style="font-size:11px;">sync</button>
|
||||
<span style="flex:1;"></span>
|
||||
<button class="btn" id="vault-btn" style="font-size:11px;" title="Open vault (Shift+F)">⤴</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="lock-btn" style="font-size:11px;">lock</button>
|
||||
</div>
|
||||
@@ -253,8 +259,8 @@ function handleListKeydown(e: KeyboardEvent): void {
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
const SETTINGS_OPTIONS: Array<{ view: 'settings' | 'settings-vault'; icon: string; label: string }> = [
|
||||
{ view: 'settings', icon: '🖥', label: 'device settings' },
|
||||
{ view: 'settings-vault', icon: '🔐', label: 'vault settings' },
|
||||
{ view: 'settings', icon: GLYPH_DEVICES, label: 'device settings' },
|
||||
{ view: 'settings-vault', icon: GLYPH_LOCK, label: 'vault settings' },
|
||||
];
|
||||
|
||||
function showSettingsPicker(anchor: HTMLElement): void {
|
||||
|
||||
@@ -41,3 +41,30 @@ describe('glyph constants', () => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,6 +18,16 @@ export const GLYPH_SETTINGS = '⚙'; // sidebar settings nav
|
||||
export const GLYPH_LOCK = '⏻'; // sidebar lock nav
|
||||
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:
|
||||
/// `<label class="label" for="f-title">title ${REQUIRED_PILL_HTML}</label>`
|
||||
///
|
||||
|
||||
Reference in New Issue
Block a user