diff --git a/AGENTS.md b/AGENTS.md index bf3a5bb0..a02b71f7 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -31,7 +31,7 @@ This is a **Platform.Bible extension** for interlinear Bible text alignment. Pla `src/main.ts` — called by Platform.Bible on activation. Exports two lifecycle functions: -- `activate(context)` — registers the `interlinearizer.mainWebView` WebView provider, the `interlinearizer.openForWebView` command, and `onDidOpenWebView` / `onDidCloseWebView` subscriptions. All registrations are added to `context.registrations` so the platform disposes them on deactivation. +- `activate(context)` — registers the `interlinearizer.mainWebView` WebView provider, the `interlinearizer.openForWebView` command, the `interlinearizer.continuousScroll` project settings validator, and `onDidOpenWebView` / `onDidCloseWebView` subscriptions. All registrations are added to `context.registrations` so the platform disposes them on deactivation. - `deactivate()` — clears `openWebViewsByProject` and returns `true`. `openWebViewsByProject` (`Map`) tracks one open WebView ID per project to prevent duplicates; reopening an already-open project brings that tab to front via the `existingId` option. diff --git a/__mocks__/papi-backend.ts b/__mocks__/papi-backend.ts index c8d3a9f4..e14dcf66 100644 --- a/__mocks__/papi-backend.ts +++ b/__mocks__/papi-backend.ts @@ -10,6 +10,7 @@ const mockSelectProject = jest.fn(); const mockGetOpenWebViewDefinition = jest.fn(); const mockOnDidOpenWebView = jest.fn(); const mockOnDidCloseWebView = jest.fn(); +const mockRegisterValidator = jest.fn(); const mockLogger = { debug: jest.fn(), error: jest.fn(), @@ -24,6 +25,9 @@ const papi = { dialogs: { selectProject: mockSelectProject, }, + projectSettings: { + registerValidator: mockRegisterValidator, + }, webViewProviders: { registerWebViewProvider: mockRegisterWebViewProvider, }, @@ -44,6 +48,7 @@ const defaultExport = { __mockGetOpenWebViewDefinition: mockGetOpenWebViewDefinition, __mockOnDidOpenWebView: mockOnDidOpenWebView, __mockOnDidCloseWebView: mockOnDidCloseWebView, + __mockRegisterValidator: mockRegisterValidator, __mockLogger: mockLogger, }; diff --git a/src/__tests__/main.test.ts b/src/__tests__/main.test.ts index 3feaa8d2..2d49e14f 100644 --- a/src/__tests__/main.test.ts +++ b/src/__tests__/main.test.ts @@ -16,6 +16,7 @@ interface PapiBackendTestMock { __mockGetOpenWebViewDefinition: jest.Mock; __mockOnDidOpenWebView: jest.Mock; __mockOnDidCloseWebView: jest.Mock; + __mockRegisterValidator: jest.Mock; __mockLogger: { debug: jest.Mock; error: jest.Mock; info: jest.Mock; warn: jest.Mock }; } @@ -34,6 +35,7 @@ function isPapiBackendTestMock(m: unknown): m is PapiBackendTestMock { '__mockGetOpenWebViewDefinition' in m && '__mockOnDidOpenWebView' in m && '__mockOnDidCloseWebView' in m && + '__mockRegisterValidator' in m && '__mockLogger' in m ); } @@ -47,6 +49,7 @@ const { __mockGetOpenWebViewDefinition, __mockOnDidOpenWebView, __mockOnDidCloseWebView, + __mockRegisterValidator, __mockLogger, } = papiBackendMock; @@ -117,6 +120,7 @@ describe('main', () => { beforeEach(() => { __mockRegisterWebViewProvider.mockResolvedValue({ dispose: jest.fn() }); __mockRegisterCommand.mockResolvedValue({ dispose: jest.fn() }); + __mockRegisterValidator.mockResolvedValue({ dispose: jest.fn() }); __mockOpenWebView.mockResolvedValue('mock-webview-id'); __mockSelectProject.mockResolvedValue(undefined); __mockGetOpenWebViewDefinition.mockResolvedValue(undefined); @@ -151,12 +155,12 @@ describe('main', () => { ); }); - it('adds all four registrations to the activation context', async () => { + it('adds all five registrations to the activation context', async () => { const context = createTestActivationContext(); await activate(context); - expect(context.registrations.unsubscribers.size).toBe(4); + expect(context.registrations.unsubscribers.size).toBe(5); }); it('logs activation start and finish', async () => { diff --git a/src/main.ts b/src/main.ts index 554b4f62..263d0f80 100644 --- a/src/main.ts +++ b/src/main.ts @@ -99,8 +99,9 @@ async function openInterlinearizerForWebView(webViewId?: string): Promise typeof newValue === 'boolean', + ); + const webViewOpenUnsubscriber = papi.webViews.onDidOpenWebView(({ webView }) => { if (webView.webViewType !== mainWebViewType || !webView.projectId) return; openWebViewsByProject.set(webView.projectId, webView.id); @@ -155,6 +161,7 @@ export async function activate(context: ExecutionActivationContext): Promise