feat(ext/popup): session expiry listener, open-vault links, Shift+F shortcut
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
/// type-iconed rows. Clicking a row fetches the full Item and navigates
|
/// type-iconed rows. Clicking a row fetches the full Item and navigates
|
||||||
/// to the detail view.
|
/// to the detail view.
|
||||||
|
|
||||||
import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup';
|
import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../popup';
|
||||||
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.
|
||||||
@@ -66,6 +66,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)">⤴</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>
|
||||||
@@ -92,6 +93,8 @@ export function renderItemList(app: HTMLElement): void {
|
|||||||
wireRowClicks();
|
wireRowClicks();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
document.getElementById('vault-btn')?.addEventListener('click', () => openVaultTab());
|
||||||
|
|
||||||
document.getElementById('new-btn')?.addEventListener('click', () => {
|
document.getElementById('new-btn')?.addEventListener('click', () => {
|
||||||
setState({ newType: null });
|
setState({ newType: null });
|
||||||
navigate('add');
|
navigate('add');
|
||||||
@@ -203,6 +206,12 @@ function handleListKeydown(e: KeyboardEvent): void {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (e.key === 'F' && e.shiftKey) {
|
||||||
|
e.preventDefault();
|
||||||
|
openVaultTab();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const filtered = getFilteredEntries();
|
const filtered = getFilteredEntries();
|
||||||
|
|
||||||
if (e.key === 'ArrowDown') {
|
if (e.key === 'ArrowDown') {
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/// Unlock view — passphrase input with ENTER to submit.
|
/// Unlock view — passphrase input with ENTER to submit.
|
||||||
|
|
||||||
import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup';
|
import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../popup';
|
||||||
import type { ItemId, ManifestEntry } from '../../shared/types';
|
import type { ItemId, ManifestEntry } from '../../shared/types';
|
||||||
|
|
||||||
export function renderUnlock(app: HTMLElement): void {
|
export function renderUnlock(app: HTMLElement): void {
|
||||||
@@ -23,6 +23,7 @@ export function renderUnlock(app: HTMLElement): void {
|
|||||||
${state.loading ? '<div style="margin:12px 0;"><span class="spinner"></span></div>' : ''}
|
${state.loading ? '<div style="margin:12px 0;"><span class="spinner"></span></div>' : ''}
|
||||||
${state.error ? `<div class="error">${escapeHtml(state.error)}</div>` : ''}
|
${state.error ? `<div class="error">${escapeHtml(state.error)}</div>` : ''}
|
||||||
<div style="margin-top:24px;">
|
<div style="margin-top:24px;">
|
||||||
|
<button class="btn" id="vault-btn" style="font-size:11px;">open vault</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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -52,6 +53,8 @@ export function renderUnlock(app: HTMLElement): void {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
document.getElementById('vault-btn')?.addEventListener('click', () => openVaultTab());
|
||||||
|
|
||||||
const settingsBtn = document.getElementById('settings-btn');
|
const settingsBtn = document.getElementById('settings-btn');
|
||||||
settingsBtn?.addEventListener('click', () => navigate('settings'));
|
settingsBtn?.addEventListener('click', () => navigate('settings'));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,13 +34,20 @@ export function isInTab(): boolean {
|
|||||||
return window.location.search.length > 0;
|
return window.location.search.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function openVaultTab(hash?: string): void {
|
||||||
|
const url = chrome.runtime.getURL('vault.html') + (hash ? `#${hash}` : '');
|
||||||
|
chrome.tabs.create({ url });
|
||||||
|
}
|
||||||
|
|
||||||
export function popOutToTab(): void {
|
export function popOutToTab(): void {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
const params = new URLSearchParams();
|
if (state.newType) {
|
||||||
params.set('view', state.view);
|
openVaultTab(`add/${state.newType}`);
|
||||||
if (state.newType) params.set('type', state.newType);
|
} else if (state.selectedId) {
|
||||||
if (state.selectedId) params.set('id', state.selectedId);
|
openVaultTab(`${state.view}/${state.selectedId}`);
|
||||||
chrome.tabs.create({ url: `popup.html?${params.toString()}` });
|
} else {
|
||||||
|
openVaultTab();
|
||||||
|
}
|
||||||
window.close();
|
window.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -272,4 +279,16 @@ async function init(): Promise<void> {
|
|||||||
navigate('locked');
|
navigate('locked');
|
||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener('DOMContentLoaded', init);
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
init();
|
||||||
|
|
||||||
|
chrome.runtime.onMessage.addListener((msg) => {
|
||||||
|
if (msg.type === 'session_expired') {
|
||||||
|
currentState.view = 'locked';
|
||||||
|
currentState.error = null;
|
||||||
|
currentState.selectedItem = null;
|
||||||
|
currentState.selectedId = null;
|
||||||
|
render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user