From ce59223fc07d145721f8205f6225c80dc688afe7 Mon Sep 17 00:00:00 2001 From: adlee-was-taken Date: Mon, 27 Apr 2026 16:38:06 -0400 Subject: [PATCH] =?UTF-8?q?feat(ext):=20shared=20state=20host=20=E2=80=94?= =?UTF-8?q?=20decouple=20components=20from=20popup.ts?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce shared/state.ts as a service-locator so popup components (item-detail, item-form, trash, devices, settings, etc.) work in both the popup and vault tab bundles. Both entry points register themselves as the host; components import from shared/state instead of popup.ts. Vault.ts now delegates to the real popup components, removing ~300 lines of placeholder renderers. Co-Authored-By: Claude Opus 4.6 --- .../__tests__/attachments-disclosure.test.ts | 6 +- .../components/__tests__/devices.test.ts | 7 +- .../__tests__/field-history.test.ts | 7 +- .../popup/components/__tests__/fields.test.ts | 8 + .../__tests__/generator-panel.test.ts | 6 +- .../__tests__/sections-editor.test.ts | 8 + .../__tests__/sections-render.test.ts | 10 +- .../__tests__/settings-vault.test.ts | 6 +- .../popup/components/__tests__/trash.test.ts | 7 +- .../components/attachments-disclosure.ts | 2 +- extension/src/popup/components/devices.ts | 2 +- .../src/popup/components/field-history.ts | 2 +- extension/src/popup/components/fields.ts | 2 +- .../src/popup/components/generator-panel.ts | 2 +- extension/src/popup/components/item-detail.ts | 3 +- extension/src/popup/components/item-form.ts | 2 +- extension/src/popup/components/item-list.ts | 5 +- .../src/popup/components/settings-vault.ts | 2 +- extension/src/popup/components/settings.ts | 2 +- extension/src/popup/components/trash.ts | 2 +- .../types/__tests__/card.save.test.ts | 6 +- .../types/__tests__/document.save.test.ts | 8 +- .../types/__tests__/identity.save.test.ts | 6 +- .../types/__tests__/key.save.test.ts | 6 +- .../types/__tests__/sections-save.test.ts | 6 +- .../types/__tests__/secure-note.save.test.ts | 6 +- .../types/__tests__/totp.save.test.ts | 6 +- extension/src/popup/components/types/card.ts | 2 +- .../src/popup/components/types/document.ts | 2 +- .../src/popup/components/types/identity.ts | 2 +- extension/src/popup/components/types/key.ts | 2 +- extension/src/popup/components/types/login.ts | 2 +- .../src/popup/components/types/secure-note.ts | 2 +- extension/src/popup/components/types/totp.ts | 2 +- extension/src/popup/components/unlock.ts | 2 +- extension/src/popup/popup.ts | 14 + extension/src/shared/state.ts | 62 +++ extension/src/vault/vault.ts | 473 ++++-------------- 38 files changed, 259 insertions(+), 441 deletions(-) create mode 100644 extension/src/shared/state.ts diff --git a/extension/src/popup/components/__tests__/attachments-disclosure.test.ts b/extension/src/popup/components/__tests__/attachments-disclosure.test.ts index 27c5390..4131103 100644 --- a/extension/src/popup/components/__tests__/attachments-disclosure.test.ts +++ b/extension/src/popup/components/__tests__/attachments-disclosure.test.ts @@ -1,13 +1,13 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../popup', async () => { +vi.mock('../../../shared/state', async () => { const sendMessage = vi.fn(); const escapeHtml = (s: string): string => s.replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]!)); - return { sendMessage, escapeHtml }; + return { sendMessage, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderAttachmentsDisclosure, wireAttachmentsDisclosure } from '../attachments-disclosure'; -import { sendMessage } from '../../popup'; +import { sendMessage } from '../../../shared/state'; import type { AttachmentRef } from '../../../shared/types'; const REF1: AttachmentRef = { id: 'a1', filename: 'doc.pdf', mime_type: 'application/pdf', size: 12345, created: 1700000000 }; diff --git a/extension/src/popup/components/__tests__/devices.test.ts b/extension/src/popup/components/__tests__/devices.test.ts index 44bc455..551091c 100644 --- a/extension/src/popup/components/__tests__/devices.test.ts +++ b/extension/src/popup/components/__tests__/devices.test.ts @@ -12,14 +12,17 @@ globalThis.chrome = { }; // Mock popup module -vi.mock('../../popup', () => ({ +vi.mock('../../../shared/state', () => ({ setState: vi.fn(), sendMessage: vi.fn(), navigate: vi.fn(), escapeHtml: (s: string) => s, + popOutToTab: vi.fn(), + isInTab: vi.fn(() => false), + openVaultTab: vi.fn(), })); -import { sendMessage, navigate } from '../../popup'; +import { sendMessage, navigate } from '../../../shared/state'; describe('devices view', () => { let app: HTMLElement; diff --git a/extension/src/popup/components/__tests__/field-history.test.ts b/extension/src/popup/components/__tests__/field-history.test.ts index 78ab386..33df7cd 100644 --- a/extension/src/popup/components/__tests__/field-history.test.ts +++ b/extension/src/popup/components/__tests__/field-history.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { renderFieldHistory, teardown } from '../field-history'; // Mock popup module -vi.mock('../../popup', () => ({ +vi.mock('../../../shared/state', () => ({ getState: vi.fn(() => ({ historyItemId: 'item123', selectedItem: { id: 'item123', title: 'Test Item', modified: 1000 }, @@ -11,9 +11,12 @@ vi.mock('../../popup', () => ({ sendMessage: vi.fn(), navigate: vi.fn(), escapeHtml: (s: string) => s, + popOutToTab: vi.fn(), + isInTab: vi.fn(() => false), + openVaultTab: vi.fn(), })); -import { sendMessage, navigate } from '../../popup'; +import { sendMessage, navigate } from '../../../shared/state'; describe('field-history view', () => { let app: HTMLElement; diff --git a/extension/src/popup/components/__tests__/fields.test.ts b/extension/src/popup/components/__tests__/fields.test.ts index ed23d46..09e5e2e 100644 --- a/extension/src/popup/components/__tests__/fields.test.ts +++ b/extension/src/popup/components/__tests__/fields.test.ts @@ -1,4 +1,12 @@ import { describe, expect, it, vi } from 'vitest'; + +vi.mock('../../../shared/state', async () => { + const escapeHtml = (s: string) => s + .replace(/&/g, '&').replace(//g, '>') + .replace(/"/g, '"').replace(/'/g, '''); + return { escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; +}); + import { renderRow, renderConcealedRow, diff --git a/extension/src/popup/components/__tests__/generator-panel.test.ts b/extension/src/popup/components/__tests__/generator-panel.test.ts index 3c26d5a..269c693 100644 --- a/extension/src/popup/components/__tests__/generator-panel.test.ts +++ b/extension/src/popup/components/__tests__/generator-panel.test.ts @@ -1,12 +1,12 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../popup', async () => { +vi.mock('../../../shared/state', async () => { const sendMessage = vi.fn(); - return { sendMessage }; + return { sendMessage, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { openGeneratorPanel, closeGeneratorPanel, isGeneratorPanelOpen } from '../generator-panel'; -import { sendMessage } from '../../popup'; +import { sendMessage } from '../../../shared/state'; import type { GeneratorRequest } from '../../../shared/types'; const DEFAULT_REQ: GeneratorRequest = { diff --git a/extension/src/popup/components/__tests__/sections-editor.test.ts b/extension/src/popup/components/__tests__/sections-editor.test.ts index 86d3ac7..92e9516 100644 --- a/extension/src/popup/components/__tests__/sections-editor.test.ts +++ b/extension/src/popup/components/__tests__/sections-editor.test.ts @@ -1,4 +1,12 @@ import { describe, expect, it, vi } from 'vitest'; + +vi.mock('../../../shared/state', async () => { + const escapeHtml = (s: string) => s + .replace(/&/g, '&').replace(//g, '>') + .replace(/"/g, '"').replace(/'/g, '''); + return { escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; +}); + import { renderSectionsEditor, generateFieldId, wireSectionsEditor } from '../fields'; import type { Section } from '../../../shared/types'; diff --git a/extension/src/popup/components/__tests__/sections-render.test.ts b/extension/src/popup/components/__tests__/sections-render.test.ts index 435d26f..0c1f7ee 100644 --- a/extension/src/popup/components/__tests__/sections-render.test.ts +++ b/extension/src/popup/components/__tests__/sections-render.test.ts @@ -1,4 +1,12 @@ -import { describe, expect, it } from 'vitest'; +import { describe, expect, it, vi } from 'vitest'; + +vi.mock('../../../shared/state', async () => { + const escapeHtml = (s: string) => s + .replace(/&/g, '&').replace(//g, '>') + .replace(/"/g, '"').replace(/'/g, '''); + return { escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; +}); + import { renderSections } from '../fields'; import type { Item } from '../../../shared/types'; diff --git a/extension/src/popup/components/__tests__/settings-vault.test.ts b/extension/src/popup/components/__tests__/settings-vault.test.ts index 45d1c2c..eedcc5e 100644 --- a/extension/src/popup/components/__tests__/settings-vault.test.ts +++ b/extension/src/popup/components/__tests__/settings-vault.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../popup', async () => { +vi.mock('../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -25,7 +25,7 @@ vi.mock('../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); vi.mock('../generator-panel', () => ({ @@ -34,7 +34,7 @@ vi.mock('../generator-panel', () => ({ })); import { renderVaultSettings } from '../settings-vault'; -import { sendMessage } from '../../popup'; +import { sendMessage } from '../../../shared/state'; describe('settings-vault', () => { beforeEach(() => { diff --git a/extension/src/popup/components/__tests__/trash.test.ts b/extension/src/popup/components/__tests__/trash.test.ts index b602a74..87d912c 100644 --- a/extension/src/popup/components/__tests__/trash.test.ts +++ b/extension/src/popup/components/__tests__/trash.test.ts @@ -2,7 +2,7 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; import { renderTrash } from '../trash'; // Mock popup module -vi.mock('../../popup', () => ({ +vi.mock('../../../shared/state', () => ({ getState: vi.fn(() => ({ vaultSettings: { trash_retention: { kind: 'days', value: 30 } }, })), @@ -10,9 +10,12 @@ vi.mock('../../popup', () => ({ sendMessage: vi.fn(), navigate: vi.fn(), escapeHtml: (s: string) => s, + popOutToTab: vi.fn(), + isInTab: vi.fn(() => false), + openVaultTab: vi.fn(), })); -import { sendMessage, navigate } from '../../popup'; +import { sendMessage, navigate } from '../../../shared/state'; describe('trash view', () => { let app: HTMLElement; diff --git a/extension/src/popup/components/attachments-disclosure.ts b/extension/src/popup/components/attachments-disclosure.ts index 5a69a00..422c37a 100644 --- a/extension/src/popup/components/attachments-disclosure.ts +++ b/extension/src/popup/components/attachments-disclosure.ts @@ -5,7 +5,7 @@ /// Image-mime rows lazy-load 16×16 thumbnails via object URLs; /// teardownAttachmentsDisclosure() revokes them on view exit. -import { sendMessage, escapeHtml } from '../popup'; +import { sendMessage, escapeHtml } from '../../shared/state'; import type { AttachmentRef, VaultSettings } from '../../shared/types'; export type DisclosureMode = 'edit' | 'view'; diff --git a/extension/src/popup/components/devices.ts b/extension/src/popup/components/devices.ts index 5d70c37..0574888 100644 --- a/extension/src/popup/components/devices.ts +++ b/extension/src/popup/components/devices.ts @@ -1,6 +1,6 @@ /// Device management view — list devices with revoke actions. -import { setState, sendMessage, navigate, escapeHtml } from '../popup'; +import { setState, sendMessage, navigate, escapeHtml } from '../../shared/state'; import type { Device } from '../../shared/types'; function relativeTime(unixSec: number): string { diff --git a/extension/src/popup/components/field-history.ts b/extension/src/popup/components/field-history.ts index e1f8e6f..a61ee6b 100644 --- a/extension/src/popup/components/field-history.ts +++ b/extension/src/popup/components/field-history.ts @@ -1,6 +1,6 @@ /// Field history view — shows password/concealed field history for an item. -import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml } from '../../shared/state'; import type { FieldHistoryView } from '../../shared/types'; function relativeTime(unixSec: number): string { diff --git a/extension/src/popup/components/fields.ts b/extension/src/popup/components/fields.ts index b04af27..f89c325 100644 --- a/extension/src/popup/components/fields.ts +++ b/extension/src/popup/components/fields.ts @@ -5,7 +5,7 @@ /// After mounting, call `wireFieldHandlers(scope)` once to bind reveal + /// copy click handlers on any rendered rows. -import { escapeHtml } from '../popup'; +import { escapeHtml } from '../../shared/state'; import type { Item, Section, Field, FieldValue } from '../../shared/types'; export interface RowOpts { diff --git a/extension/src/popup/components/generator-panel.ts b/extension/src/popup/components/generator-panel.ts index afcb1d4..526c23b 100644 --- a/extension/src/popup/components/generator-panel.ts +++ b/extension/src/popup/components/generator-panel.ts @@ -4,7 +4,7 @@ /// between Random + BIP39 knob sets. Action row varies by context: /// fill-field shows cancel+use; configure-defaults shows only save-default. -import { sendMessage } from '../popup'; +import { sendMessage } from '../../shared/state'; import type { GeneratorRequest, VaultSettings } from '../../shared/types'; interface UiKnobs { diff --git a/extension/src/popup/components/item-detail.ts b/extension/src/popup/components/item-detail.ts index e4f7349..a4446fd 100644 --- a/extension/src/popup/components/item-detail.ts +++ b/extension/src/popup/components/item-detail.ts @@ -1,9 +1,8 @@ /// Typed-item detail view dispatcher. Each type's renderDetail lives in /// its own module under ./types/. Document stays "coming soon" until γ. -import { navigate } from '../popup'; +import { navigate, getState } from '../../shared/state'; import type { Item } from '../../shared/types'; -import { getState } from '../popup'; import * as login from './types/login'; import * as secureNote from './types/secure-note'; import * as identity from './types/identity'; diff --git a/extension/src/popup/components/item-form.ts b/extension/src/popup/components/item-form.ts index 36809cc..6f513c2 100644 --- a/extension/src/popup/components/item-form.ts +++ b/extension/src/popup/components/item-form.ts @@ -1,7 +1,7 @@ /// 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 } from '../popup'; +import { navigate, getState, setState, escapeHtml, popOutToTab } from '../../shared/state'; import type { Item, ItemType } from '../../shared/types'; const TYPE_OPTIONS: Array<{ type: ItemType; icon: string; label: string }> = [ diff --git a/extension/src/popup/components/item-list.ts b/extension/src/popup/components/item-list.ts index d816856..b7058fe 100644 --- a/extension/src/popup/components/item-list.ts +++ b/extension/src/popup/components/item-list.ts @@ -2,7 +2,7 @@ /// type-iconed rows. Clicking a row fetches the full Item and navigates /// to the detail view. -import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml, openVaultTab } from '../../shared/state'; import type { ItemId, ItemType, ManifestEntry, Item } from '../../shared/types'; /// Extract the display hostname from an icon_hint or fallback to the first tag. @@ -148,8 +148,9 @@ async function openItem(id: ItemId): Promise { /// Compute the visible (filtered) entry list from current state. function getFilteredEntries(): Array<[ItemId, ManifestEntry]> { const state = getState(); + const entries: Array<[ItemId, ManifestEntry]> = state.entries; // Hide trashed items from the main list. - let filtered = state.entries.filter(([, e]) => e.trashed_at === undefined || e.trashed_at === null); + let filtered = entries.filter(([, e]) => e.trashed_at === undefined || e.trashed_at === null); if (state.searchQuery) { const q = state.searchQuery.toLowerCase(); filtered = filtered.filter(([, e]) => { diff --git a/extension/src/popup/components/settings-vault.ts b/extension/src/popup/components/settings-vault.ts index 176399e..17598aa 100644 --- a/extension/src/popup/components/settings-vault.ts +++ b/extension/src/popup/components/settings-vault.ts @@ -2,7 +2,7 @@ /// generator defaults (preview + "configure" → opens popover), and /// autofill origin-ack revocation. -import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml } from '../../shared/state'; import type { VaultSettings, TrashRetention, HistoryRetention, GeneratorRequest, } from '../../shared/types'; diff --git a/extension/src/popup/components/settings.ts b/extension/src/popup/components/settings.ts index 97b2ce7..6584b77 100644 --- a/extension/src/popup/components/settings.ts +++ b/extension/src/popup/components/settings.ts @@ -1,6 +1,6 @@ /// Settings view — capture toggle, prompt style, and blacklist management. -import { sendMessage, navigate, escapeHtml } from '../popup'; +import { sendMessage, navigate, escapeHtml } from '../../shared/state'; import type { DeviceSettings } from '../../shared/types'; export async function renderSettings(app: HTMLElement): Promise { diff --git a/extension/src/popup/components/trash.ts b/extension/src/popup/components/trash.ts index c1a60a5..63b5b2d 100644 --- a/extension/src/popup/components/trash.ts +++ b/extension/src/popup/components/trash.ts @@ -1,6 +1,6 @@ /// Trash view — lists soft-deleted items with restore/purge actions. -import { getState, setState, sendMessage, navigate, escapeHtml } from '../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml } from '../../shared/state'; import type { ItemId, ManifestEntry, VaultSettings } from '../../shared/types'; const TYPE_ICONS: Record = { diff --git a/extension/src/popup/components/types/__tests__/card.save.test.ts b/extension/src/popup/components/types/__tests__/card.save.test.ts index 6bc2854..2001922 100644 --- a/extension/src/popup/components/types/__tests__/card.save.test.ts +++ b/extension/src/popup/components/types/__tests__/card.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -11,11 +11,11 @@ vi.mock('../../../popup', async () => { })); const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../card'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; describe('Card save shape', () => { beforeEach(() => { diff --git a/extension/src/popup/components/types/__tests__/document.save.test.ts b/extension/src/popup/components/types/__tests__/document.save.test.ts index 543cfe0..f7485b8 100644 --- a/extension/src/popup/components/types/__tests__/document.save.test.ts +++ b/extension/src/popup/components/types/__tests__/document.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -12,11 +12,11 @@ vi.mock('../../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../document'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; import type { Item, AttachmentRef } from '../../../../shared/types'; const PRIMARY: AttachmentRef = { @@ -43,7 +43,7 @@ describe('Document form save', () => { await new Promise((r) => setTimeout(r, 50)); expect(alertSpy).not.toHaveBeenCalled(); // setState called with the error - const { setState } = await import('../../../popup'); + const { setState } = await import('../../../../shared/state'); expect(vi.mocked(setState)).toHaveBeenCalledWith( expect.objectContaining({ error: expect.stringContaining('primary attachment') }), ); diff --git a/extension/src/popup/components/types/__tests__/identity.save.test.ts b/extension/src/popup/components/types/__tests__/identity.save.test.ts index 2a9b17e..6b20fcb 100644 --- a/extension/src/popup/components/types/__tests__/identity.save.test.ts +++ b/extension/src/popup/components/types/__tests__/identity.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -12,11 +12,11 @@ vi.mock('../../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../identity'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; describe('Identity save shape', () => { beforeEach(() => { diff --git a/extension/src/popup/components/types/__tests__/key.save.test.ts b/extension/src/popup/components/types/__tests__/key.save.test.ts index 18c9bd1..8790b60 100644 --- a/extension/src/popup/components/types/__tests__/key.save.test.ts +++ b/extension/src/popup/components/types/__tests__/key.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -11,11 +11,11 @@ vi.mock('../../../popup', async () => { })); const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../key'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; describe('Key save shape', () => { beforeEach(() => { diff --git a/extension/src/popup/components/types/__tests__/sections-save.test.ts b/extension/src/popup/components/types/__tests__/sections-save.test.ts index 0c0483a..fe48d4f 100644 --- a/extension/src/popup/components/types/__tests__/sections-save.test.ts +++ b/extension/src/popup/components/types/__tests__/sections-save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -13,11 +13,11 @@ vi.mock('../../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../login'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; describe('Login form packs sectionsDraft into Item.sections', () => { beforeEach(() => { diff --git a/extension/src/popup/components/types/__tests__/secure-note.save.test.ts b/extension/src/popup/components/types/__tests__/secure-note.save.test.ts index f915bce..256694a 100644 --- a/extension/src/popup/components/types/__tests__/secure-note.save.test.ts +++ b/extension/src/popup/components/types/__tests__/secure-note.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -12,11 +12,11 @@ vi.mock('../../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../secure-note'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; describe('SecureNote save shape', () => { beforeEach(() => { diff --git a/extension/src/popup/components/types/__tests__/totp.save.test.ts b/extension/src/popup/components/types/__tests__/totp.save.test.ts index 036bf4f..a7fe803 100644 --- a/extension/src/popup/components/types/__tests__/totp.save.test.ts +++ b/extension/src/popup/components/types/__tests__/totp.save.test.ts @@ -1,6 +1,6 @@ import { beforeEach, describe, expect, it, vi } from 'vitest'; -vi.mock('../../../popup', async () => { +vi.mock('../../../../shared/state', async () => { const navigate = vi.fn(); const setState = vi.fn(); const sendMessage = vi.fn(); @@ -12,11 +12,11 @@ vi.mock('../../../popup', async () => { const escapeHtml = (s: string) => s .replace(/&/g, '&').replace(//g, '>') .replace(/"/g, '"').replace(/'/g, '''); - return { navigate, setState, sendMessage, getState, escapeHtml }; + return { navigate, setState, sendMessage, getState, escapeHtml, popOutToTab: vi.fn(), isInTab: vi.fn(() => false), openVaultTab: vi.fn() }; }); import { renderForm } from '../totp'; -import { sendMessage } from '../../../popup'; +import { sendMessage } from '../../../../shared/state'; import { base32Decode } from '../../../../shared/base32'; describe('Totp save shape', () => { diff --git a/extension/src/popup/components/types/card.ts b/extension/src/popup/components/types/card.ts index e36ffb8..60fc280 100644 --- a/extension/src/popup/components/types/card.ts +++ b/extension/src/popup/components/types/card.ts @@ -1,7 +1,7 @@ /// Card: number / holder / expiry MonthYear / cvv / pin / kind. /// Detail view has a styled card-silhouette signature block. -import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../../shared/state'; import type { Item, ItemId, ManifestEntry, CardKind, Section, AttachmentRef } from '../../../shared/types'; import { renderConcealedRow, renderSignatureBlock, wireFieldHandlers, renderSections, diff --git a/extension/src/popup/components/types/document.ts b/extension/src/popup/components/types/document.ts index 4eb891b..7a25597 100644 --- a/extension/src/popup/components/types/document.ts +++ b/extension/src/popup/components/types/document.ts @@ -2,7 +2,7 @@ /// notes/tags + optional supplementary attachments. /// Primary attachment is referenced by ID from the item's attachments array. -import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../../shared/state'; import type { Item, ItemId, ManifestEntry, Section, AttachmentRef } from '../../../shared/types'; import { renderSectionsEditor, wireSectionsEditor, diff --git a/extension/src/popup/components/types/identity.ts b/extension/src/popup/components/types/identity.ts index 9f9c1a7..0f5330b 100644 --- a/extension/src/popup/components/types/identity.ts +++ b/extension/src/popup/components/types/identity.ts @@ -1,7 +1,7 @@ /// Identity: full_name, address (multiline), phone, email, date_of_birth. /// Detail view shows a "profile card" signature block + plain rows. -import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../popup'; +import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../../shared/state'; import type { Item, ItemId, ManifestEntry, Section, AttachmentRef } from '../../../shared/types'; import { renderRow, renderSignatureBlock, wireFieldHandlers, renderSections, diff --git a/extension/src/popup/components/types/key.ts b/extension/src/popup/components/types/key.ts index 3b4eee5..50215c9 100644 --- a/extension/src/popup/components/types/key.ts +++ b/extension/src/popup/components/types/key.ts @@ -2,7 +2,7 @@ /// Form's key_material textarea uses CSS text-security to mask characters /// since