diff --git a/extension/src/popup/components/org-switcher.ts b/extension/src/popup/components/org-switcher.ts index 2aca8db..443059c 100644 --- a/extension/src/popup/components/org-switcher.ts +++ b/extension/src/popup/components/org-switcher.ts @@ -5,21 +5,68 @@ // (and its offline flag) into shared state, and reloads the list. An offline // org renders a read-only banner; Dev-C's write UI reads `orgOffline` to // disable writes. -// -// SCAFFOLD STATE (HOLD until Dev-A merges): renders the select shell only. The -// `org_list_configs` fetch, the `org_switch` dispatch + list reload, and the -// offline banner are held until Dev-A's org messages land in the Request union. + +import type { OrgConfigSummary } from '../../shared/types'; +import { sendMessage, setState, navigate, getState } from '../../shared/state'; + +// Module-level teardown (one instance per browser context; popup and vault +// each have their own module scope). +let _cleanupListener: (() => void) | null = null; export async function renderOrgSwitcher(host: HTMLElement): Promise { - // HOLD(dev-a): fetch org_list_configs -> append an ' + - ''; + ``; + + // 4. Wire the change event. + const select = host.querySelector('select') as HTMLSelectElement; + + const handler = async (_e: Event) => { + const context = select.value; + const switchResp = await sendMessage({ type: 'org_switch', context }); + if (!switchResp.ok) return; + const data = switchResp.data as { context: string; offline: boolean }; + setState({ orgContext: data.context, orgOffline: data.offline }); + + // Offline banner — toggle inside host alongside the select. + const existing = host.querySelector('.org-offline-banner'); + if (data.offline) { + if (!existing) { + const banner = document.createElement('div'); + banner.className = 'org-offline-banner'; + banner.textContent = 'org offline — writes disabled'; + host.appendChild(banner); + } + } else { + existing?.remove(); + } + + navigate('list', {}); + }; + + select.addEventListener('change', handler); + _cleanupListener = () => select.removeEventListener('change', handler); } export function teardown(): void { - // HOLD(dev-a): detach the change listener once renderOrgSwitcher wires it. + _cleanupListener?.(); + _cleanupListener = null; } diff --git a/extension/src/popup/index.html b/extension/src/popup/index.html index 0175394..303d275 100644 --- a/extension/src/popup/index.html +++ b/extension/src/popup/index.html @@ -7,6 +7,7 @@ Relicario +
diff --git a/extension/src/popup/popup.ts b/extension/src/popup/popup.ts index a2ad8a2..93e4f86 100644 --- a/extension/src/popup/popup.ts +++ b/extension/src/popup/popup.ts @@ -14,6 +14,7 @@ import { renderItemDetail } from './components/item-detail'; import { renderItemForm } from './components/item-form'; import { renderSettings, teardownSettings } from './components/settings'; import { renderVaultSettings } from './components/settings-vault'; +import { renderOrgSwitcher } from './components/org-switcher'; import { renderTrash } from './components/trash'; import { renderDevices } from './components/devices'; import { renderFieldHistory } from './components/field-history'; @@ -245,6 +246,10 @@ async function init(): Promise { currentState.generatorDefaults = vs.generator_defaults; } + // Mount the org context switcher in the persistent header slot (lives + // outside #app so it survives view swaps). + void renderOrgSwitcher(document.getElementById('popup-header')!); + // Check URL params for deep linking (when opened in tab) const urlParams = parseUrlParams(); if (urlParams) { diff --git a/extension/src/vault/vault-shell.ts b/extension/src/vault/vault-shell.ts index a7e1b7a..706ba74 100644 --- a/extension/src/vault/vault-shell.ts +++ b/extension/src/vault/vault-shell.ts @@ -11,6 +11,7 @@ import { type VaultController, escapeHtml, typeIcon, } from './vault-context'; import { renderSidebarShell } from './vault-sidebar'; +import { renderOrgSwitcher } from '../popup/components/org-switcher'; // --------------------------------------------------------------------------- // Type picker (right side panel) @@ -118,6 +119,7 @@ export function renderShell(ctx: VaultController, app: HTMLElement): void { `; ctx.wireSidebar(); wireTypePanel(ctx); + void renderOrgSwitcher(document.getElementById('org-switcher-slot')!); } applyShellViewClass(ctx); diff --git a/extension/src/vault/vault-sidebar.ts b/extension/src/vault/vault-sidebar.ts index aebeb48..5158997 100644 --- a/extension/src/vault/vault-sidebar.ts +++ b/extension/src/vault/vault-sidebar.ts @@ -27,6 +27,7 @@ export function renderSidebarShell(): string {
Relicario +