From f6f9e2b1a6d9816ac2db5a63c3d01a7063038d37 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Fri, 23 Oct 2026 16:00:05 +0530 Subject: [PATCH 01/14] test(#36): cover reading controls and tabs --- tests/e2e/reading-interface.spec.js | 81 +++++++++++++++++++++++++++++ tests/popup/interface.test.js | 57 ++++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 tests/e2e/reading-interface.spec.js diff --git a/tests/e2e/reading-interface.spec.js b/tests/e2e/reading-interface.spec.js new file mode 100644 index 0000000..5ac72d7 --- /dev/null +++ b/tests/e2e/reading-interface.spec.js @@ -0,0 +1,81 @@ +import { + test, + expect, + CONFIGURED_PREFERENCES, + seedPreferences, +} from './fixtures.js'; + +test('reading controls work by keyboard and fit compact and wide layouts', async ({ + context, + extensionId, +}, testInfo) => { + const page = await context.newPage(); + await page.goto(`chrome-extension://${extensionId}/popup.html?workspace=1`); + await seedPreferences(page, CONFIGURED_PREFERENCES); + await page.reload(); + await page + .locator('#document-file') + .setInputFiles({ + name: 'Reading-notes.txt', + mimeType: 'text/plain', + buffer: Buffer.from( + 'Photosynthesis converts sunlight into chemical energy. Plants store this energy in sugars.' + ), + }); + await expect(page.locator('#current-page-status')).toContainText('Ready'); + const short = page.getByRole('radio', { name: 'Short', exact: true }); + await short.focus(); + await expect(short).toBeFocused(); + await short.press('ArrowRight'); + await expect( + page.getByRole('radio', { name: 'Medium', exact: true }) + ).toBeChecked(); + const summary = page.getByRole('tab', { name: 'Summary', exact: true }); + await summary.focus(); + await summary.press('ArrowRight'); + await expect( + page.getByRole('tab', { name: 'Chat', exact: true }) + ).toBeFocused(); + await expect( + page.getByRole('tabpanel', { name: 'Chat', exact: true }) + ).toBeVisible(); + await page.keyboard.press('Tab'); + await expect(page.locator('#clear-chat-btn')).toBeFocused(); + await page.locator('#theme-toggle').click(); + await expect(page.locator('body')).toHaveAttribute('data-theme', 'dark'); + await page.screenshot({ + path: testInfo.outputPath('workspace-dark.png'), + fullPage: true, + }); + await page.locator('#theme-toggle').click(); + await summary.click(); + await page.screenshot({ + path: testInfo.outputPath('workspace-light.png'), + fullPage: true, + }); + for (const width of [768, 400, 320]) { + await page.setViewportSize({ width, height: 800 }); + await expect(page.locator('#current-page-title')).toBeVisible(); + expect( + await page.evaluate( + () => document.documentElement.scrollWidth <= window.innerWidth + ) + ).toBe(true); + await page.screenshot({ + path: testInfo.outputPath(`workspace-${width}.png`), + fullPage: true, + }); + } + await page.goto(`chrome-extension://${extensionId}/popup.html`); + await page.setViewportSize({ width: 400, height: 600 }); + await expect(page.locator('#loading-overlay')).toBeHidden(); + await page.screenshot({ + path: testInfo.outputPath('popup.png'), + fullPage: true, + }); + expect( + await page.evaluate( + () => document.documentElement.scrollWidth <= window.innerWidth + ) + ).toBe(true); +}); diff --git a/tests/popup/interface.test.js b/tests/popup/interface.test.js index f58aeeb..cf830dc 100644 --- a/tests/popup/interface.test.js +++ b/tests/popup/interface.test.js @@ -902,6 +902,63 @@ describe('popup', () => { }); describe('tabs', () => { + it('exposes the selected tab and its panel and keeps keyboard focus on the tabs', async () => { + await openPopup(); + const summary = document.querySelector('[data-tab="summary"]'); + const chat = document.querySelector('[data-tab="chat"]'); + expect(summary.getAttribute('role')).toBe('tab'); + expect(summary.getAttribute('aria-selected')).toBe('true'); + expect(chat.tabIndex).toBe(-1); + summary.focus(); + summary.dispatchEvent( + new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }) + ); + expect(document.activeElement).toBe(chat); + expect(chat.getAttribute('aria-selected')).toBe('true'); + expect(chat.tabIndex).toBe(0); + expect(el('summary-tab').hidden).toBe(true); + expect(el('chat-tab').hidden).toBe(false); + expect(el('chat-tab').getAttribute('aria-labelledby')).toBe(chat.id); + chat.dispatchEvent( + new KeyboardEvent('keydown', { key: 'End', bubbles: true }) + ); + expect(document.activeElement.dataset.tab).toBe('tools'); + document.activeElement.dispatchEvent( + new KeyboardEvent('keydown', { key: 'ArrowRight', bubbles: true }) + ); + expect(document.activeElement).toBe(summary); + summary.dispatchEvent( + new KeyboardEvent('keydown', { key: 'ArrowLeft', bubbles: true }) + ); + expect(document.activeElement.dataset.tab).toBe('tools'); + document.activeElement.dispatchEvent( + new KeyboardEvent('keydown', { key: 'Home', bubbles: true }) + ); + expect(document.activeElement).toBe(summary); + }); + + it('preserves the complete study prompt behind a short action label', async () => { + await openPopup({ + overrides: { + ANSWER_CONTEXTUAL_QUESTION: { + success: true, + data: { answer: 'Study notes' }, + }, + }, + }); + document.querySelector('[data-tab="chat"]').click(); + const chip = [...document.querySelectorAll('.suggestion-chip')].find( + button => button.textContent.trim() === 'Study guide' + ); + expect(chip).toBeDefined(); + chip.click(); + await vi.waitFor(() => + expect(sentPayload('ANSWER_CONTEXTUAL_QUESTION')?.question).toBe( + 'Make a study guide with key concepts and examples from this document' + ) + ); + }); + it('activates the pane for the clicked tab', async () => { await openPopup(); From 5bf1915a937bfd203d70459babacd4b4dd797260 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Fri, 23 Oct 2026 16:19:30 +0530 Subject: [PATCH 02/14] feat(#36): redesign the reading workspace --- popup.html | 1105 +++++++++++------ scripts/popup-main.js | 53 +- styles/popup.css | 1711 ++++++++++++++------------ tests/e2e/learning-workspace.spec.js | 1 + tests/e2e/reading-interface.spec.js | 49 +- tests/popup/interface.test.js | 2 + 6 files changed, 1753 insertions(+), 1168 deletions(-) diff --git a/popup.html b/popup.html index 1958aab..3cc3d47 100644 --- a/popup.html +++ b/popup.html @@ -3,66 +3,125 @@ - GenAI Browser Assistant - + GenAI · Reading workspace
-
-
-
-
🤖
-

GenAI Assistant

-
-
- - - -
+ + +

GenAI Reading workspace

-
+
- - Initializing... -
-
- Loading... + Initializing…
+ + + +
+
+ + - - -
- -
-
-
-

- Read a page, understand the ideas, and keep learning. -

- -

- Import stays in this tab. Text is sent to your selected provider - only when you request an AI action. Up to 2 million characters. -

+
+
+
+ The big picture +

Summarize this document.

+

+ A focused summary of your source, at the level of detail you need. +

+
+
+
+
-
-

Content Summary

-
- Short + + - - - - - -
- - - -
-
- -
- -
- - +
+
-

Summary Result

-
- - -
-
-
+ + + +

Your summary starts here.

+

+ Your summary will appear here, with the provider and source + coverage. You can copy it or save it as a file. +

+ Choose a format above, then generate. +
+
- - -
+ - - -
-
-
-

Translation Tools

-
- -
-
-
- - -
-
- -
-
- - -
+ - - -
-
-
-

Content Analysis

-
- -
-
-
-

📊 Sentiment Analysis

- -
-
-
Click analyze to start
-
+
-
- - -
-
-
-

Productivity Tools

-
- -
-
-
📤
-

Export Data

-

- Export your settings, saved summaries, and chat history as - JSON -

- +

+ Find the topics that connect this source. +

+
+ No result yet
- -
-
📊
-

Page Stats

-

Get detailed statistics about this page

-
- -
-
🔗
-

Extract Links

-

Extract and categorize all page links

-
-
- -
+
- - -
-
-
Processing...
+
+ +
Processing…
+

Your source is being processed. This can take a moment.

- - -
+
- diff --git a/scripts/popup-main.js b/scripts/popup-main.js index c52cf9b..45e68a7 100644 --- a/scripts/popup-main.js +++ b/scripts/popup-main.js @@ -90,6 +90,26 @@ class PopupInterface { .tab; if (tab) this.switchToTab(tab); }); + button.addEventListener('keydown', event => { + const e = /** @type {KeyboardEvent} */ (event); + const tabs = Array.from(document.querySelectorAll('.tab-button')); + const current = tabs.indexOf(button); + const next = + e.key === 'Home' + ? 0 + : e.key === 'End' + ? tabs.length - 1 + : e.key === 'ArrowRight' + ? (current + 1) % tabs.length + : e.key === 'ArrowLeft' + ? (current + tabs.length - 1) % tabs.length + : -1; + if (next < 0) return; + e.preventDefault(); + const target = /** @type {HTMLElement} */ (tabs[next]); + if (target.dataset.tab) this.switchToTab(target.dataset.tab); + target.focus(); + }); }); on('settings-btn', 'click', () => chrome.runtime.openOptionsPage()); @@ -116,7 +136,9 @@ class PopupInterface { chip.addEventListener('click', event => { setValue( 'chat-input', - /** @type {HTMLElement} */ (event.target).textContent || '' + /** @type {HTMLElement} */ (event.currentTarget).dataset.prompt || + /** @type {HTMLElement} */ (event.currentTarget).textContent || + '' ); this.sendChatMessage(); }); @@ -146,14 +168,18 @@ class PopupInterface { /** @param {string} tabName */ switchToTab(tabName) { - document - .querySelectorAll('.tab-button.active, .tab-pane.active') - .forEach(el => el.classList.remove('active')); - document - .querySelector(`.tab-button[data-tab="${tabName}"]`) - ?.classList.add('active'); - document.getElementById(`${tabName}-tab`)?.classList.add('active'); - if (tabName === 'chat') document.getElementById('chat-input')?.focus(); + document.querySelectorAll('.tab-button').forEach(button => { + const tab = /** @type {HTMLElement} */ (button); + const selected = tab.dataset.tab === tabName; + tab.classList.toggle('active', selected); + tab.setAttribute('aria-selected', String(selected)); + tab.tabIndex = selected ? 0 : -1; + const panel = document.getElementById(`${tab.dataset.tab}-tab`); + if (panel) { + panel.classList.toggle('active', selected); + panel.hidden = !selected; + } + }); } // ------------------------------------------------------------------ content @@ -199,6 +225,7 @@ class PopupInterface { this.warnedAboutChatTruncation = false; if (content.url?.startsWith('local-document:')) this.sourceTabId = null; hide('summary-results'); + show('summary-empty', 'flex'); hide('translation-results'); hide('tools-results'); document.querySelectorAll('.analysis-result').forEach(result => { @@ -335,6 +362,7 @@ class PopupInterface { `${cached ? 'Saved summary · ' : ''}${describeCoverage(data)}` ); show('summary-results'); + hide('summary-empty'); if (data.historyWarning) { setText('summary-confidence', `${describeCoverage(data)} · Not saved`); this.showToast(data.historyWarning, 'warning'); @@ -623,7 +651,7 @@ class PopupInterface { const avatar = document.createElement('div'); avatar.className = `${role}-avatar`; - avatar.textContent = role === 'user' ? '👤' : '🤖'; + avatar.textContent = role === 'user' ? 'You' : 'GenAI'; const body = document.createElement('div'); body.className = 'message-content'; @@ -639,6 +667,9 @@ class PopupInterface { if (!Array.isArray(sources) || !sources.length) return; const details = document.createElement('details'); details.className = 'chat-sources'; + details.addEventListener('toggle', () => { + if (details.open) details.scrollIntoView?.({ block: 'nearest' }); + }); const summary = document.createElement('summary'); summary.textContent = retrieval ? `${sources.length} of ${retrieval.total} passages used · ${retrieval.mode === 'overview' ? 'representative overview' : 'matched to your question'}` @@ -703,10 +734,12 @@ class PopupInterface { /** @param {string} message */ showLoading(message) { setText('loading-text', message); + document.getElementById('main-content')?.setAttribute('aria-busy', 'true'); show('loading-overlay', 'flex'); } hideLoading() { + document.getElementById('main-content')?.setAttribute('aria-busy', 'false'); hide('loading-overlay'); } } diff --git a/styles/popup.css b/styles/popup.css index c638fe2..aa8774a 100644 --- a/styles/popup.css +++ b/styles/popup.css @@ -1,1017 +1,1178 @@ -/** - * @file styles/popup.css - * @description Modern styling for GenAI Browser Assistant popup - */ - -/* CSS Custom Properties */ +/* Reading desk: local system fonts, shared color tokens, no runtime assets. */ :root { - /* Colors */ - --primary-color: #4f46e5; - --primary-hover: #4338ca; - --secondary-color: #6b7280; - --success-color: #10b981; - --warning-color: #f59e0b; - --error-color: #ef4444; - --background-color: #ffffff; - --surface-color: #f9fafb; - --border-color: #e5e7eb; - --text-primary: #111827; - --text-secondary: #6b7280; - --text-inverse: #ffffff; - - /* Dark theme colors */ - --dark-background: #1f2937; - --dark-surface: #374151; - --dark-border: #4b5563; - --dark-text-primary: #f9fafb; - --dark-text-secondary: #d1d5db; - - /* Spacing */ - --spacing-xs: 0.25rem; - --spacing-sm: 0.5rem; - --spacing-md: 1rem; - --spacing-lg: 1.5rem; - --spacing-xl: 2rem; - - /* Border radius */ - --radius-sm: 0.375rem; - --radius-md: 0.5rem; - --radius-lg: 0.75rem; - - /* Shadows */ - --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05); - --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1); - --shadow-lg: - 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1); - - /* Transitions */ - --transition-fast: 150ms ease-in-out; - --transition-normal: 250ms ease-in-out; -} - -/* Dark Theme */ + color-scheme: light; + --canvas: #f5f6f4; + --paper: #ffffff; + --subtle: #f8f9f7; + --ink: #202b28; + --muted: #60706a; + --line: #dce2de; + --control-line: #81948a; + --accent: #176354; + --accent-hover: #104b40; + --accent-soft: #e8f2ed; + --on-accent: #ffffff; + --danger: #ad3535; + --warning: #8b5d10; + --focus: #167562; +} [data-theme='dark'] { - --background-color: var(--dark-background); - --surface-color: var(--dark-surface); - --border-color: var(--dark-border); - --text-primary: var(--dark-text-primary); - --text-secondary: var(--dark-text-secondary); -} - -/* Base Styles */ + color-scheme: dark; + --canvas: #151c1a; + --paper: #1b2421; + --subtle: #202c27; + --ink: #e7eee9; + --muted: #a5b7ad; + --line: #3b4b43; + --control-line: #768c7f; + --accent: #93d5ba; + --accent-hover: #b0e4ce; + --accent-soft: #2a4035; + --on-accent: #142a20; + --danger: #ffaaa5; + --warning: #edc47d; + --focus: #a1e5c9; +} * { box-sizing: border-box; - margin: 0; - padding: 0; } - body { - font-family: - 'Inter', + margin: 0; + width: 400px; + max-width: 100%; + height: 600px; + font: + 14px/1.55 'Segoe UI', -apple-system, BlinkMacSystemFont, - 'Segoe UI', - Roboto, sans-serif; - font-size: 14px; - line-height: 1.5; - color: var(--text-primary); - background-color: var(--background-color); - width: 400px; - max-height: 600px; - overflow: hidden; -} - -/* App Container */ -.workspace-only { - display: none; -} - -.page-status { - margin-top: 8px; - font-size: 12px; - color: #fff; -} -.document-overview { - padding: 12px; - margin-bottom: 16px; - border: 1px solid var(--border-color); - border-radius: 12px; - background: var(--surface-color); -} -.document-import { - margin-top: 12px; - font-weight: 600; -} -.document-import input { - display: block; - margin-top: 8px; - max-width: 100%; -} -.helper-text, -.coverage-note { - font-size: 12px; - color: var(--text-secondary); - margin-top: 8px; + background: var(--canvas); + color: var(--ink); +} +button, +input, +select, +textarea { + font: inherit; +} +button, +select, +summary, +input[type='file'] { + cursor: pointer; } -.chat-memory-controls { - display: flex; - gap: 8px; - align-items: center; - justify-content: space-between; - padding: 10px 16px; - font-size: 12px; +button { + color: inherit; } -.chat-sources { - margin: 8px 0 16px 40px; - padding: 10px; - background: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: 8px; - font-size: 12px; +button { + transition: + background-color 120ms ease, + border-color 120ms ease; } -.chat-sources summary { - cursor: pointer; - font-weight: 600; -} -.chat-sources p { - margin-top: 10px; - white-space: pre-wrap; - overflow-wrap: anywhere; - max-height: 280px; - overflow-y: auto; +button:disabled { + opacity: 0.55; + cursor: wait; } button:focus-visible, select:focus-visible, +textarea:focus-visible, input:focus-visible, -textarea:focus-visible { - outline: 2px solid var(--primary-color); +summary:focus-visible, +[tabindex='0']:focus-visible { + outline: 2px solid var(--focus); outline-offset: 3px; } - -body.workspace { - width: 100%; - max-height: none; - overflow: auto; -} -.workspace .app-container { - max-width: 1200px; - margin: 0 auto; - min-height: 100vh; - max-height: none; -} -.workspace .workspace-only { - display: block; -} -.workspace #open-workspace-btn { - display: none; -} -.workspace .page-title { - max-width: 70vw; -} -.workspace .content-section { - width: 100%; - max-width: 960px; - margin: 0 auto; -} -.workspace .chat-container { - height: calc(100vh - 250px); - min-height: 380px; - max-width: 1000px; - margin: 0 auto; - width: 100%; +h1, +h2, +h3, +p { + margin: 0; } -.workspace .tab-navigation { +svg { flex-shrink: 0; + vertical-align: middle; +} +[hidden] { + display: none !important; } - .app-container { + position: relative; display: flex; flex-direction: column; - height: 100vh; - max-height: 600px; + height: 100%; + background: var(--paper); +} +.workspace-only { + display: none; } - -/* Header */ .app-header { - background: linear-gradient( - 135deg, - var(--primary-color), - var(--primary-hover) - ); - color: var(--text-inverse); - padding: var(--spacing-md); - border-bottom: 1px solid var(--border-color); -} - -.header-content { display: flex; + flex-shrink: 0; justify-content: space-between; align-items: center; - margin-bottom: var(--spacing-sm); + gap: 12px; + min-height: 64px; + padding: 12px 16px; + border-bottom: 1px solid var(--line); } - -.logo-section { +.logo-section, +.header-actions, +.provider-status { display: flex; align-items: center; - gap: var(--spacing-sm); } - -.logo-icon { - font-size: 1.5em; +.logo-section { + gap: 9px; + min-width: 0; +} +.brand-mark { + display: inline-flex; + color: var(--accent); +} +.brand-mark svg { + width: 26px; + height: 26px; } - .app-title { - font-size: 1.1em; - font-weight: 600; - margin: 0; + font-size: 17px; + font-weight: 650; + letter-spacing: -0.5px; + white-space: nowrap; +} +.app-title span { + display: none; } - .header-actions { - display: flex; - gap: var(--spacing-xs); + gap: 6px; +} +.provider-status { + gap: 6px; + font-size: 11px; + color: var(--muted); +} +.status-indicator { + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--muted); +} +.status-indicator.active { + background: var(--accent); +} +.status-indicator.error { + background: var(--danger); +} +.status-indicator.warning { + background: var(--warning); } - .icon-button { - background: rgba(255, 255, 255, 0.1); - border: none; - border-radius: var(--radius-sm); - padding: var(--spacing-xs); - color: var(--text-inverse); - cursor: pointer; - transition: background-color var(--transition-fast); - display: flex; + display: inline-flex; align-items: center; justify-content: center; + width: 32px; + height: 32px; + padding: 5px; + border: 1px solid transparent; + border-radius: 6px; + background: transparent; + color: var(--muted); } - .icon-button:hover { - background: rgba(255, 255, 255, 0.2); + background: var(--subtle); + border-color: var(--line); + color: var(--ink); } - -.status-bar { - display: flex; - justify-content: space-between; +.action-button { + display: inline-flex; align-items: center; - font-size: 0.875em; - opacity: 0.9; + justify-content: center; + gap: 6px; + padding: 6px 10px; + border: 1px solid var(--line); + border-radius: 6px; + background: var(--paper); + font-size: 12px; + font-weight: 500; } - -.provider-status { +.action-button:hover { + background: var(--subtle); + border-color: var(--muted); +} +#open-workspace-btn { + font-size: 0; + width: 32px; + padding: 5px; +} +#open-workspace-btn svg { + width: 17px; + height: 17px; +} +.document-panel { + padding: 14px 18px; + background: var(--subtle); + border-bottom: 1px solid var(--line); +} +.document-heading { display: flex; align-items: center; - gap: var(--spacing-xs); + justify-content: space-between; + margin-bottom: 7px; } - -.status-indicator { - width: 8px; - height: 8px; - border-radius: 50%; - background-color: var(--success-color); +.eyebrow { + display: block; + font-size: 10px; + font-weight: 650; + letter-spacing: 1.5px; + text-transform: uppercase; + color: var(--muted); } - -.status-indicator.error { - background-color: var(--error-color); +.document-symbol { + color: var(--muted); } - -.status-indicator.warning { - background-color: var(--warning-color); +.document-symbol svg { + width: 16px; + height: 16px; +} +#current-page-title { + font-size: 16px; + font-weight: 600; + letter-spacing: -0.3px; + overflow-wrap: anywhere; + line-height: 1.4; +} +.page-status { + margin-top: 5px; + font-size: 11px; + color: var(--muted); + overflow-wrap: anywhere; } - -.page-title { - max-width: 200px; +.document-overview { + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + font-size: 12px; + color: var(--muted); + margin-top: 10px; +} +.document-import { + margin-top: 28px; + border-top: 1px solid var(--line); + padding-top: 18px; +} +.document-import summary { + font-size: 13px; + font-weight: 600; + color: var(--accent); + margin-bottom: 14px; +} +.document-import summary svg { + width: 15px; + height: 15px; + margin-right: 4px; +} +.document-import label { + display: block; + font-size: 11px; + color: var(--muted); + margin-bottom: 8px; +} +.document-import input { + max-width: 100%; + font-size: 11px; +} +.document-import input::file-selector-button { + font: inherit; + color: var(--ink); + border: 1px solid var(--line); + background: var(--paper); + border-radius: 5px; + padding: 7px 8px; + margin-right: 6px; + cursor: pointer; +} +.source-note { + margin-top: 40px; + border-top: 1px solid var(--line); + padding-top: 20px; + color: var(--muted); + font-size: 12px; +} +.source-note p { + margin-top: 10px; } - -/* Navigation Tabs */ .tab-navigation { display: flex; - background-color: var(--surface-color); - border-bottom: 1px solid var(--border-color); - overflow-x: auto; - scrollbar-width: none; - -ms-overflow-style: none; -} - -.tab-navigation::-webkit-scrollbar { - display: none; + flex-shrink: 0; + gap: 4px; + min-height: 52px; + padding: 0 10px; + border-bottom: 1px solid var(--line); } - .tab-button { + display: flex; flex: 1; min-width: 0; - background: none; - border: none; - padding: var(--spacing-sm); - cursor: pointer; - transition: all var(--transition-fast); - color: var(--text-secondary); + gap: 6px; + justify-content: center; + align-items: center; + padding: 13px 4px; + border: 0; border-bottom: 2px solid transparent; - font-size: 0.75em; + background: transparent; + color: var(--muted); + font-size: 11px; + font-weight: 500; +} +.tab-button svg { + width: 15px; + height: 15px; } - .tab-button:hover { - background-color: rgba(0, 0, 0, 0.05); - color: var(--text-primary); + color: var(--ink); + background: var(--subtle); } - .tab-button.active { - color: var(--primary-color); - border-bottom-color: var(--primary-color); - background-color: var(--background-color); + color: var(--accent); + border-bottom-color: var(--accent); } - -.tab-icon { - display: block; - font-size: 1.2em; - margin-bottom: 2px; -} - -.tab-label { - display: block; - font-weight: 500; -} - -/* Main Content */ .main-content { flex: 1; + min-height: 0; overflow-y: auto; scrollbar-width: thin; - scrollbar-color: var(--border-color) transparent; -} - -.main-content::-webkit-scrollbar { - width: 6px; -} - -.main-content::-webkit-scrollbar-track { - background: transparent; -} - -.main-content::-webkit-scrollbar-thumb { - background-color: var(--border-color); - border-radius: 3px; + scrollbar-color: var(--line) transparent; } - .tab-pane { - display: none; - padding: var(--spacing-md); + padding: 22px 18px; } - -.tab-pane.active { - display: block; -} - -/* Content Sections */ -.content-section { - margin-bottom: var(--spacing-lg); +.tab-pane:not(.active) { + display: none; } - .section-header { - margin-bottom: var(--spacing-md); + margin-bottom: 24px; } - .section-header h2 { - font-size: 1.1em; + margin: 7px 0; + font-size: 23px; + letter-spacing: -0.8px; font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--spacing-sm); + line-height: 1.25; } - -/* Summary Controls */ -.summary-controls { +.section-header p { + font-size: 13px; + color: var(--muted); + max-width: 55ch; +} +.summary-toolbar { display: flex; - flex-direction: column; - gap: var(--spacing-sm); + flex-wrap: wrap; + gap: 16px; + align-items: end; + padding-bottom: 24px; + border-bottom: 1px solid var(--line); +} +.summary-controls { + flex: 1 1 130px; + min-width: 0; +} +.field-label { + display: block; + color: var(--muted); + font-size: 11px; + font-weight: 600; + margin-bottom: 7px; } - .modern-select { width: 100%; - padding: var(--spacing-sm); - border: 1px solid var(--border-color); - border-radius: var(--radius-sm); - background-color: var(--background-color); - color: var(--text-primary); - font-size: 0.875em; - transition: border-color var(--transition-fast); -} - -.modern-select:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); -} - + min-width: 0; + min-height: 38px; + padding: 8px; + border: 1px solid var(--control-line); + border-radius: 6px; + background: var(--paper); + color: var(--ink); + font-size: 12px; +} +.length-control { + flex: 1 1 175px; + min-width: 0; + border: none; + margin: 0; + padding: 0; +} +.length-control legend { + padding: 0; +} .length-selector { display: flex; - gap: var(--spacing-sm); + min-height: 38px; + padding: 3px; + border: 1px solid var(--line); + border-radius: 6px; + background: var(--subtle); } - .radio-label { + position: relative; flex: 1; display: flex; align-items: center; justify-content: center; - padding: var(--spacing-sm); - border: 1px solid var(--border-color); - border-radius: var(--radius-sm); cursor: pointer; - transition: all var(--transition-fast); } - -.radio-label:hover { - background-color: var(--surface-color); +.radio-label input { + position: absolute; + opacity: 0; + width: 1px; + height: 1px; } - -.radio-label input[type='radio'] { - display: none; +.radio-text { + display: block; + width: 100%; + text-align: center; + padding: 4px 6px; + font-size: 11px; + color: var(--muted); + border-radius: 3px; } - -.radio-label input[type='radio']:checked + .radio-text { - color: var(--primary-color); - font-weight: 500; +.radio-label input:checked + .radio-text { + color: var(--accent); + background: var(--paper); + box-shadow: 0 0 0 1px var(--accent); + font-weight: 600; } - -.radio-label:has(input[type='radio']:checked) { - background-color: rgba(79, 70, 229, 0.1); - border-color: var(--primary-color); +.radio-label:has(input:focus-visible) { + outline: 2px solid var(--focus); + outline-offset: 3px; + border-radius: 3px; } - -.radio-text { - font-size: 0.875em; +.primary-button { + display: inline-flex; + align-items: center; + justify-content: space-between; + gap: 20px; + border: 1px solid transparent; + border-radius: 6px; + min-height: 39px; + padding: 9px 14px; + background: var(--accent); + color: var(--on-accent); + font-size: 12px; + font-weight: 600; } - -/* Buttons */ -.action-buttons { - display: flex; - gap: var(--spacing-sm); - margin-bottom: var(--spacing-md); +.primary-button:hover, +.send-button:hover { + background: var(--accent-hover); } - -.primary-button, -.secondary-button, -.tool-button { +.summary-toolbar .primary-button { + width: 100%; +} +.summary-empty { display: flex; + flex-direction: column; align-items: center; justify-content: center; - gap: var(--spacing-xs); - padding: var(--spacing-sm) var(--spacing-md); - border: none; - border-radius: var(--radius-md); - font-size: 0.875em; + text-align: center; + padding: 36px 18px; + min-height: 250px; + color: var(--muted); +} +.empty-icon { + width: 32px; + height: 32px; + margin-bottom: 16px; + color: var(--accent); +} +.summary-empty h3, +.chat-empty h3 { + font-size: 17px; + color: var(--ink); font-weight: 500; - cursor: pointer; - transition: all var(--transition-fast); - text-decoration: none; + letter-spacing: -0.3px; + margin-bottom: 7px; } - -.primary-button { - background-color: var(--primary-color); - color: var(--text-inverse); - flex: 1; +.summary-empty p, +.chat-empty p { + font-size: 13px; + max-width: 40ch; +} +.empty-caption { + font-size: 11px; + margin-top: 22px; } - -.primary-button:hover { - background-color: var(--primary-hover); - transform: translateY(-1px); - box-shadow: var(--shadow-md); -} - -.secondary-button { - background-color: var(--surface-color); - color: var(--text-primary); - border: 1px solid var(--border-color); -} - -.secondary-button:hover { - background-color: var(--border-color); -} - -.button-icon { - font-size: 1em; -} - -/* Results Container */ -.results-container { - background-color: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-lg); - padding: var(--spacing-md); - margin-top: var(--spacing-md); -} - -.result-header { +.results-container, +.tools-results { + margin-top: 28px; +} +.result-header, +.card-header { display: flex; - justify-content: space-between; + flex-wrap: wrap; align-items: center; - margin-bottom: var(--spacing-sm); + justify-content: space-between; + gap: 8px; + margin-bottom: 10px; } - .result-header h3 { - font-size: 1em; + font-size: 15px; font-weight: 600; - color: var(--text-primary); } - +.result-actions { + display: flex; + gap: 6px; +} .result-meta { display: flex; - gap: var(--spacing-xs); + flex-wrap: wrap; + gap: 6px 12px; + color: var(--muted); + font-size: 11px; + padding-bottom: 18px; + border-bottom: 1px solid var(--line); +} +.result-meta span:empty { + display: none; } - .provider-badge, -.confidence-score, .language-badge { - padding: 2px var(--spacing-xs); - background-color: var(--primary-color); - color: var(--text-inverse); - border-radius: var(--radius-sm); - font-size: 0.75em; - font-weight: 500; + color: var(--accent); + font-weight: 600; } - .result-content { - margin-bottom: var(--spacing-md); - line-height: 1.6; - color: var(--text-primary); + padding-top: 22px; + font-size: 15px; + line-height: 1.85; + overflow-wrap: anywhere; } - -.result-actions { - display: flex; - gap: var(--spacing-xs); +.result-content p, +.message-content p { + margin-bottom: 12px; } - -.action-button { - padding: var(--spacing-xs) var(--spacing-sm); - background-color: var(--background-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-sm); - color: var(--text-primary); - font-size: 0.75em; - cursor: pointer; - transition: all var(--transition-fast); +.result-content ul, +.result-content ol, +.message-content ul, +.message-content ol { + padding-left: 22px; } - -.action-button:hover { - background-color: var(--surface-color); - transform: translateY(-1px); +.result-content li { + margin-bottom: 8px; +} +.result-content h2, +.result-content h3 { + margin: 20px 0 10px; + line-height: 1.4; +} +code { + padding: 2px 4px; + background: var(--subtle); + border: 1px solid var(--line); + border-radius: 4px; + font-size: 0.9em; + overflow-wrap: anywhere; +} +pre { + white-space: pre-wrap; +} +.chat-memory-controls { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + align-items: center; + gap: 8px; + padding-bottom: 14px; + color: var(--muted); + font-size: 11px; + border-bottom: 1px solid var(--line); } - -/* Chat Styles */ .chat-container { display: flex; flex-direction: column; - height: 400px; + min-height: 330px; } - .chat-history { flex: 1; + min-height: 160px; + padding: 24px 0 6px; overflow-y: auto; - padding: var(--spacing-sm); scrollbar-width: thin; - scrollbar-color: var(--border-color) transparent; } - -.welcome-message, -.chat-message { - display: flex; - gap: var(--spacing-sm); - margin-bottom: var(--spacing-md); +.chat-history:empty { + display: none; } - -.assistant-avatar, -.user-avatar { - flex-shrink: 0; - width: 32px; - height: 32px; - border-radius: 50%; +.chat-empty { + display: none; +} +.chat-history:empty + .chat-empty { display: flex; - align-items: center; + flex: 1; + flex-direction: column; justify-content: center; - font-size: 1.2em; + align-items: center; + text-align: center; + color: var(--muted); + padding: 35px 12px; +} +.chat-message { + display: flex; + gap: 12px; + margin-bottom: 20px; + align-items: flex-start; } - +.user-avatar, .assistant-avatar { - background-color: var(--primary-color); - color: var(--text-inverse); + flex: 0 0 36px; + font-size: 10px; + font-weight: 650; + color: var(--muted); + padding-top: 11px; } - -.user-avatar { - background-color: var(--surface-color); - color: var(--text-primary); +.assistant-avatar { + color: var(--accent); } - .message-content { flex: 1; - background-color: var(--surface-color); - border-radius: var(--radius-md); - padding: var(--spacing-sm); - line-height: 1.5; + min-width: 0; + font-size: 14px; + line-height: 1.7; + padding: 9px 0; + overflow-wrap: anywhere; +} +.message-content > :last-child { + margin-bottom: 0; } - .chat-message.user .message-content { - background-color: var(--primary-color); - color: var(--text-inverse); + background: var(--accent-soft); + border-radius: 7px; + padding: 10px 14px; +} +.chat-sources { + margin: 0 0 24px 48px; + padding: 12px 14px; + border-left: 2px solid var(--line); + background: var(--subtle); + font-size: 12px; + color: var(--muted); +} +.chat-sources summary { + color: var(--accent); + font-weight: 600; +} +.chat-sources p { + margin-top: 12px; + white-space: pre-wrap; + overflow-wrap: anywhere; + max-height: 240px; + overflow-y: auto; } - .chat-input-container { - border-top: 1px solid var(--border-color); - padding: var(--spacing-sm); + padding-top: 18px; + border-top: 1px solid var(--line); } - .input-wrapper { display: flex; - gap: var(--spacing-xs); - margin-bottom: var(--spacing-sm); + align-items: end; + gap: 8px; + padding: 9px; + border: 1px solid var(--control-line); + border-radius: 8px; + background: var(--paper); +} +.input-wrapper:focus-within { + border-color: var(--focus); + box-shadow: 0 0 0 1px var(--focus); } - #chat-input { flex: 1; - padding: var(--spacing-sm); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - resize: none; - font-family: inherit; - font-size: 0.875em; - line-height: 1.4; - min-height: 36px; - max-height: 100px; + min-width: 0; + padding: 3px; + resize: vertical; + min-height: 48px; + max-height: 150px; + border: 0; + background: transparent; + color: var(--ink); + font-size: 13px; + line-height: 1.6; +} +#chat-input::placeholder { + color: var(--muted); } - -#chat-input:focus { - outline: none; - border-color: var(--primary-color); - box-shadow: 0 0 0 3px rgba(79, 70, 229, 0.1); +#chat-input:focus-visible { + outline-offset: 1px; } - .send-button { - padding: var(--spacing-sm); - background-color: var(--primary-color); - color: var(--text-inverse); - border: none; - border-radius: var(--radius-md); - cursor: pointer; - transition: background-color var(--transition-fast); display: flex; + flex-shrink: 0; align-items: center; justify-content: center; + width: 34px; + height: 34px; + padding: 7px; + border: 0; + border-radius: 5px; + background: var(--accent); + color: var(--on-accent); } - -.send-button:hover { - background-color: var(--primary-hover); -} - -.send-button:disabled { - opacity: 0.5; - cursor: not-allowed; -} - .input-suggestions { display: flex; - gap: var(--spacing-xs); + gap: 6px; flex-wrap: wrap; + margin-top: 10px; } - .suggestion-chip { - padding: var(--spacing-xs) var(--spacing-sm); - background-color: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-lg); - font-size: 0.75em; - color: var(--text-secondary); - cursor: pointer; - transition: all var(--transition-fast); + border: 1px solid var(--line); + background: var(--subtle); + color: var(--ink); + font-size: 11px; + padding: 5px 9px; + border-radius: 5px; } - .suggestion-chip:hover { - background-color: var(--border-color); - color: var(--text-primary); + background: var(--accent-soft); + border-color: var(--accent); +} +.helper-text, +.coverage-note { + font-size: 11px; + line-height: 1.6; + color: var(--muted); + margin-top: 9px; } - -/* Translation Styles */ .translation-controls { - margin-bottom: var(--spacing-md); + padding: 20px; + background: var(--subtle); + border: 1px solid var(--line); + border-radius: 8px; + margin-bottom: 20px; } - .language-selector { display: flex; - align-items: center; - gap: var(--spacing-sm); - margin-bottom: var(--spacing-md); + gap: 10px; + align-items: end; + margin-bottom: 18px; } - .language-input { flex: 1; + min-width: 0; } - -.language-input label { - display: block; - font-size: 0.75em; - font-weight: 500; - color: var(--text-secondary); - margin-bottom: var(--spacing-xs); -} - .language-swap { - padding: var(--spacing-sm) 0; + margin-bottom: 3px; + font-size: 20px; } - -.translation-options { - display: flex; - flex-direction: column; - gap: var(--spacing-sm); -} - .checkbox-label { display: flex; - align-items: center; - gap: var(--spacing-sm); - cursor: pointer; -} - -.checkbox-label input[type='checkbox'] { - width: 16px; - height: 16px; - accent-color: var(--primary-color); + align-items: baseline; + gap: 8px; + font-size: 12px; } - -.checkbox-text { - font-size: 0.875em; - color: var(--text-primary); +input[type='checkbox'] { + accent-color: var(--accent); } - -/* Analysis Styles */ -.analysis-grid { +.analysis-grid, +.tools-grid { display: grid; grid-template-columns: 1fr; - gap: var(--spacing-md); + gap: 16px; } - -.analysis-card { - background-color: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-lg); - padding: var(--spacing-md); -} - -.card-header { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: var(--spacing-sm); +.analysis-card, +.tool-card { + min-width: 0; + border: 1px solid var(--line); + border-radius: 8px; + padding: 20px; } - -.card-header h3 { - font-size: 0.875em; +.card-header h3, +.tool-card h3 { + font-size: 15px; font-weight: 600; - color: var(--text-primary); } - +.card-description, +.tool-card p { + font-size: 12px; + color: var(--muted); +} .analyze-btn { - padding: var(--spacing-xs) var(--spacing-sm); - background-color: var(--primary-color); - color: var(--text-inverse); - border: none; - border-radius: var(--radius-sm); - font-size: 0.75em; - cursor: pointer; - transition: background-color var(--transition-fast); + padding: 5px 10px; + background: var(--accent-soft); + border: 1px solid transparent; + border-radius: 5px; + color: var(--accent); + font-size: 12px; } - .analyze-btn:hover { - background-color: var(--primary-hover); + border-color: var(--accent); } - .analysis-result { - min-height: 60px; - display: flex; - align-items: center; - justify-content: center; + border-top: 1px solid var(--line); + margin-top: 16px; + padding-top: 14px; + overflow-wrap: anywhere; + font-size: 13px; } - .placeholder { - color: var(--text-secondary); - font-size: 0.875em; - font-style: italic; -} - -/* Tools Styles */ -.tools-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: var(--spacing-md); + color: var(--muted); + font-size: 12px; } - .tool-card { - background-color: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-lg); - padding: var(--spacing-md); - text-align: center; - transition: all var(--transition-fast); -} - -.tool-card:hover { - box-shadow: var(--shadow-md); - transform: translateY(-2px); -} - -.tool-icon { - font-size: 2em; - margin-bottom: var(--spacing-sm); + display: flex; + flex-direction: column; + align-items: start; } - -.tool-card h3 { - font-size: 0.875em; - font-weight: 600; - color: var(--text-primary); - margin-bottom: var(--spacing-xs); +.tool-card > svg { + margin-bottom: 16px; + color: var(--accent); } - .tool-card p { - font-size: 0.75em; - color: var(--text-secondary); - margin-bottom: var(--spacing-md); - line-height: 1.4; + margin-top: 8px; + flex: 1; } - .tool-button { - width: 100%; - background-color: var(--primary-color); - color: var(--text-inverse); - font-size: 0.75em; -} - -.tools-results { - margin-top: var(--spacing-lg); - background-color: var(--surface-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-lg); - padding: var(--spacing-md); -} - -.results-header { display: flex; - justify-content: space-between; align-items: center; - margin-bottom: var(--spacing-md); + justify-content: space-between; + width: 100%; + margin-top: 22px; + padding: 10px 0 0; + font-size: 12px; + color: var(--accent); + background: transparent; + border: 0; + border-top: 1px solid var(--line); } - -.close-results { - background: none; - border: none; - font-size: 1.5em; - color: var(--text-secondary); - cursor: pointer; - padding: 0; - width: 24px; - height: 24px; - display: flex; - align-items: center; - justify-content: center; +.tool-button:hover { + color: var(--ink); } - -.close-results:hover { - color: var(--text-primary); +.results-content { + overflow-wrap: anywhere; } - -/* Loading Overlay */ .loading-overlay { - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(255, 255, 255, 0.95); + position: fixed; + inset: 0; + z-index: 10; display: none; flex-direction: column; align-items: center; justify-content: center; - z-index: 1000; + background: var(--paper); + opacity: 0.97; + padding: 24px; + text-align: center; } - -[data-theme='dark'] .loading-overlay { - background-color: rgba(31, 41, 55, 0.95); +.loading-overlay p { + margin-top: 8px; + color: var(--muted); + font-size: 12px; +} +.loading-text { + margin-top: 16px; + font-size: 14px; + font-weight: 600; } - .loading-spinner { - width: 40px; - height: 40px; - border: 3px solid var(--border-color); - border-top: 3px solid var(--primary-color); + width: 24px; + height: 24px; + border: 2px solid var(--line); + border-top-color: var(--accent); border-radius: 50%; - animation: spin 1s linear infinite; - margin-bottom: var(--spacing-md); + animation: spin 0.9s linear infinite; } - @keyframes spin { - 0% { - transform: rotate(0deg); - } - 100% { + to { transform: rotate(360deg); } } - -.loading-text { - color: var(--text-primary); - font-size: 0.875em; - font-weight: 500; -} - -/* Toast Notifications */ .toast-container { position: fixed; - top: var(--spacing-md); - right: var(--spacing-md); - z-index: 1001; + z-index: 11; + bottom: 16px; + right: 16px; + left: 16px; display: flex; + align-items: end; flex-direction: column; - gap: var(--spacing-xs); + gap: 8px; + pointer-events: none; } - .toast { - background-color: var(--background-color); - border: 1px solid var(--border-color); - border-radius: var(--radius-md); - padding: var(--spacing-sm) var(--spacing-md); - box-shadow: var(--shadow-lg); - min-width: 300px; - max-width: 400px; - animation: slideInRight 0.3s ease-out; -} - -.toast.success { - border-left: 4px solid var(--success-color); -} - + max-width: 420px; + padding: 12px 16px; + border: 1px solid var(--line); + border-left: 3px solid var(--accent); + border-radius: 6px; + background: var(--paper); + color: var(--ink); + font-size: 12px; + box-shadow: 0 5px 20px #00000018; +} .toast.error { - border-left: 4px solid var(--error-color); + border-left-color: var(--danger); } - .toast.warning { - border-left: 4px solid var(--warning-color); + border-left-color: var(--warning); +} +body.workspace { + width: 100%; + height: auto; + min-height: 100vh; } - -@keyframes slideInRight { - from { - transform: translateX(100%); - opacity: 0; +.workspace .app-container { + display: grid; + grid-template-columns: 248px minmax(0, 1fr); + grid-template-rows: 72px 64px 1fr; + max-width: 1440px; + margin: 0 auto; + min-height: 100vh; + height: auto; + border-inline: 1px solid var(--line); +} +.workspace .workspace-only { + display: block; +} +.workspace .app-header { + grid-column: 1 / -1; + padding: 16px 30px; +} +.workspace .app-title span { + display: inline; + margin-left: 16px; + padding-left: 16px; + border-left: 1px solid var(--line); + font-size: 12px; + font-weight: 400; + color: var(--muted); + letter-spacing: 0; +} +.workspace .header-actions { + gap: 12px; +} +.workspace .provider-status { + padding: 5px 10px; + border: 1px solid var(--line); + border-radius: 5px; + font-size: 12px; +} +.workspace #open-workspace-btn { + display: none; +} +.workspace .document-panel { + grid-row: 2 / 4; + grid-column: 1; + padding: 30px 24px; + border-bottom: none; + border-right: 1px solid var(--line); +} +.workspace #current-page-title { + font-size: 21px; + letter-spacing: -0.5px; + margin-top: 16px; +} +.workspace .page-status { + font-size: 12px; + margin-top: 10px; +} +.workspace .document-overview { + display: block; + margin-top: 22px; +} +.workspace .tab-navigation { + grid-column: 2; + padding: 0 38px; + gap: 28px; +} +.workspace .tab-button { + flex: 0 1 auto; + padding-inline: 2px; + font-size: 13px; + gap: 9px; +} +.workspace .tab-button svg { + width: 17px; + height: 17px; +} +.workspace .main-content { + grid-column: 2; + overflow: visible; + padding: 0 38px; +} +.workspace .tab-pane { + max-width: 880px; + padding: 34px 0 40px; + margin: 0 auto; +} +.workspace .section-header h2 { + font-size: 30px; + letter-spacing: -1px; + margin-top: 9px; +} +.workspace .section-header p { + font-size: 14px; +} +.workspace .summary-toolbar { + flex-wrap: nowrap; + gap: 18px; + padding-block: 12px 26px; +} +.workspace .summary-controls { + flex-basis: 160px; +} +.workspace .length-control { + flex-basis: 190px; +} +.workspace .summary-toolbar .primary-button { + width: auto; + flex-shrink: 0; +} +.workspace .summary-empty { + min-height: 340px; +} +.workspace .result-content { + font-size: 16px; + max-width: 72ch; +} +.workspace .chat-container { + height: calc(100vh - 374px); + min-height: 340px; +} +.workspace .analysis-grid { + grid-template-columns: repeat(2, minmax(0, 1fr)); +} +.workspace .tools-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); +} +@media (max-width: 1000px) { + .workspace .app-container { + grid-template-columns: 210px minmax(0, 1fr); } - to { - transform: translateX(0); - opacity: 1; + .workspace .main-content { + padding-inline: 24px; } -} - -/* Responsive Design */ -@media (max-width: 350px) { - body { - width: 350px; + .workspace .document-panel { + padding-inline: 18px; + } + .workspace .tab-navigation { + padding-inline: 24px; + gap: 18px; } - - .tools-grid { + .workspace .summary-toolbar { + flex-wrap: wrap; + } + .workspace .summary-toolbar .primary-button { + width: 100%; + } + .workspace .tools-grid { grid-template-columns: 1fr; } - - .language-selector { - flex-direction: column; +} +@media (max-width: 700px) { + .workspace .app-container { + display: flex; + min-height: 100vh; } - - .language-swap { - align-self: center; + .workspace .app-header { + padding: 14px 18px; + } + .workspace .app-title span { + display: none; + } + .workspace .header-actions { + gap: 6px; + } + .workspace .provider-status { + border: 0; padding: 0; + font-size: 11px; + } + .workspace .document-panel { + border-right: none; + border-bottom: 1px solid var(--line); + padding: 18px; + } + .workspace #current-page-title { + margin-top: 7px; + font-size: 18px; + } + .workspace .document-overview { + margin-top: 10px; + } + .workspace .source-note { + display: none; + } + .workspace .document-import { + margin-top: 18px; + padding-top: 14px; + } + .workspace .tab-navigation { + padding-inline: 10px; + gap: 4px; + } + .workspace .tab-button { + flex: 1; + font-size: 11px; + gap: 6px; + } + .workspace .tab-button svg { + width: 15px; + height: 15px; + } + .workspace .main-content { + padding: 0 18px; + overflow: visible; + flex: auto; + } + .workspace .section-header h2 { + font-size: 25px; + } + .workspace .summary-toolbar { + padding-top: 0; + } + .workspace .chat-container { + height: auto; + min-height: 400px; + } + .workspace .analysis-grid { + grid-template-columns: 1fr; + } +} +@media (max-width: 360px) { + .tab-button svg { + display: none; + } + .workspace .tab-button svg { + display: none; + } + .header-actions { + gap: 3px; + } + .provider-status { + font-size: 10px; + } + .workspace .provider-status { + font-size: 10px; + } + .workspace .tab-pane { + padding-top: 24px; + } + .workspace .header-actions { + gap: 3px; } } - -/* Accessibility */ @media (prefers-reduced-motion: reduce) { - * { - animation-duration: 0.01ms !important; - animation-iteration-count: 1 !important; - transition-duration: 0.01ms !important; + *, + *::before, + *::after { + animation: none !important; + transition: none !important; + scroll-behavior: auto !important; } } - -/* Focus styles for keyboard navigation */ -button:focus-visible, -select:focus-visible, -textarea:focus-visible, -input:focus-visible { - outline: 2px solid var(--primary-color); - outline-offset: 2px; -} - -/* High contrast mode support */ -@media (prefers-contrast: high) { - :root { - --border-color: #000000; - --text-secondary: #000000; - } - - [data-theme='dark'] { - --border-color: #ffffff; - --text-secondary: #ffffff; +@media (forced-colors: active) { + .tab-button.active { + border-bottom-color: Highlight; + } + .radio-label input:checked + .radio-text { + outline: 1px solid Highlight; + } + .status-indicator { + background: CanvasText; } } diff --git a/tests/e2e/learning-workspace.spec.js b/tests/e2e/learning-workspace.spec.js index f258918..dc084a5 100644 --- a/tests/e2e/learning-workspace.spec.js +++ b/tests/e2e/learning-workspace.spec.js @@ -58,6 +58,7 @@ test('book questions find late evidence, remember the thread, and clear it', asy expect(requests).toHaveLength(1); expect(requests[0].messages[0].content).toContain('73 amber crystals'); await page.locator('.chat-sources summary').click(); + await expect(page.locator('.chat-sources p')).toBeInViewport({ ratio: 1 }); await page.screenshot({ path: testInfo.outputPath('learning-workspace.png'), fullPage: true, diff --git a/tests/e2e/reading-interface.spec.js b/tests/e2e/reading-interface.spec.js index 5ac72d7..9675b54 100644 --- a/tests/e2e/reading-interface.spec.js +++ b/tests/e2e/reading-interface.spec.js @@ -3,6 +3,8 @@ import { expect, CONFIGURED_PREFERENCES, seedPreferences, + FIXTURE_URL, + activeTabId, } from './fixtures.js'; test('reading controls work by keyboard and fit compact and wide layouts', async ({ @@ -13,15 +15,13 @@ test('reading controls work by keyboard and fit compact and wide layouts', async await page.goto(`chrome-extension://${extensionId}/popup.html?workspace=1`); await seedPreferences(page, CONFIGURED_PREFERENCES); await page.reload(); - await page - .locator('#document-file') - .setInputFiles({ - name: 'Reading-notes.txt', - mimeType: 'text/plain', - buffer: Buffer.from( - 'Photosynthesis converts sunlight into chemical energy. Plants store this energy in sugars.' - ), - }); + await page.locator('#document-file').setInputFiles({ + name: 'Reading-notes.txt', + mimeType: 'text/plain', + buffer: Buffer.from( + 'Photosynthesis converts sunlight into chemical energy. Plants store this energy in sugars.' + ), + }); await expect(page.locator('#current-page-status')).toContainText('Ready'); const short = page.getByRole('radio', { name: 'Short', exact: true }); await short.focus(); @@ -39,6 +39,11 @@ test('reading controls work by keyboard and fit compact and wide layouts', async await expect( page.getByRole('tabpanel', { name: 'Chat', exact: true }) ).toBeVisible(); + await expect(page.locator('#send-chat-btn')).toBeInViewport(); + await page.keyboard.press('Tab'); + await expect( + page.getByRole('tabpanel', { name: 'Chat', exact: true }) + ).toBeFocused(); await page.keyboard.press('Tab'); await expect(page.locator('#clear-chat-btn')).toBeFocused(); await page.locator('#theme-toggle').click(); @@ -46,12 +51,14 @@ test('reading controls work by keyboard and fit compact and wide layouts', async await page.screenshot({ path: testInfo.outputPath('workspace-dark.png'), fullPage: true, + animations: 'disabled', }); await page.locator('#theme-toggle').click(); await summary.click(); await page.screenshot({ path: testInfo.outputPath('workspace-light.png'), fullPage: true, + animations: 'disabled', }); for (const width of [768, 400, 320]) { await page.setViewportSize({ width, height: 800 }); @@ -61,17 +68,39 @@ test('reading controls work by keyboard and fit compact and wide layouts', async () => document.documentElement.scrollWidth <= window.innerWidth ) ).toBe(true); + for (const name of ['Chat', 'Translate', 'Analyze', 'Tools', 'Summary']) { + await page.getByRole('tab', { name, exact: true }).click(); + await expect( + page.getByRole('tabpanel', { name, exact: true }) + ).toBeVisible(); + expect( + await page.evaluate( + () => document.documentElement.scrollWidth <= window.innerWidth + ) + ).toBe(true); + } await page.screenshot({ path: testInfo.outputPath(`workspace-${width}.png`), fullPage: true, + animations: 'disabled', }); } - await page.goto(`chrome-extension://${extensionId}/popup.html`); + const source = await context.newPage(); + await source.goto(FIXTURE_URL); + await source.bringToFront(); + const tabId = await activeTabId(page); + await page.goto( + `chrome-extension://${extensionId}/popup.html?tabId=${tabId}` + ); await page.setViewportSize({ width: 400, height: 600 }); await expect(page.locator('#loading-overlay')).toBeHidden(); + await expect(page.locator('#current-page-title')).toHaveText( + 'Understanding Service Workers' + ); await page.screenshot({ path: testInfo.outputPath('popup.png'), fullPage: true, + animations: 'disabled', }); expect( await page.evaluate( diff --git a/tests/popup/interface.test.js b/tests/popup/interface.test.js index cf830dc..130fc71 100644 --- a/tests/popup/interface.test.js +++ b/tests/popup/interface.test.js @@ -378,6 +378,7 @@ describe('popup', () => { await vi.waitFor(() => expect(text('current-page-title')).toBe('new.txt') ); + expect(el('summary-empty').style.display).toBe('flex'); expect(el('translation-results').style.display).toBe('none'); expect(text('insights-result')).not.toContain('old article'); el('extract-links-btn').click(); @@ -543,6 +544,7 @@ describe('popup', () => { expect(el('summary-results').style.display).toBe('block') ); expect(el('summary-content').innerHTML).toContain('
  • point one
  • '); + expect(el('summary-empty').style.display).toBe('none'); expect(text('summary-provider')).toBe('anthropic · claude-opus-5'); expect(text('summary-confidence')).toBe( 'Whole page, summarized across 3 sections' From ae0f75dccedeabf80cf18af7be91b60d470435bd Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Fri, 23 Oct 2026 16:19:52 +0530 Subject: [PATCH 03/14] docs(#36): show the reading interface and checks --- CHANGELOG.md | 5 ++ README.md | 11 +++- docs/product/INTERFACE.md | 66 +++++++++++++++++++++ docs/product/PRODUCT.md | 5 +- docs/product/images/learning-workspace.png | Bin 77016 -> 67696 bytes docs/product/images/reading-dark.png | Bin 0 -> 65549 bytes docs/product/images/reading-summary.png | Bin 0 -> 59968 bytes 7 files changed, 85 insertions(+), 2 deletions(-) create mode 100644 docs/product/INTERFACE.md create mode 100644 docs/product/images/reading-dark.png create mode 100644 docs/product/images/reading-summary.png diff --git a/CHANGELOG.md b/CHANGELOG.md index b46a0e4..d6d4968 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Restore keyboard access to summary-length radios and expose task-tab selection, + panel relationships, and Arrow/Home/End navigation (#36). - Apply context-menu and desktop-notification preferences in the worker, including settings changes, stale clicks, and opt-out during an in-flight request (#31). - Patch Vitest and matching coverage packages to 4.1.12 and Vite to 8.2.4 for @@ -23,6 +25,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Added +- A redesigned popup and reading workspace with a persistent source panel, + neutral/teal light and dark themes, local SVG icons, clear task labels, + compact study prompts, and responsive layouts (#36). - A read-only CI guard rejects a stacked PR whose base is already contained in the default branch, with Git-history regression tests (issue #22). - Service-worker regression tests for startup failure, page context menus, diff --git a/README.md b/README.md index fdfd340..5d0c72c 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,12 @@ server or telemetry. The screenshot is from an automated browser test with an intercepted AI response. It demonstrates the interface, not live model quality. +The reading workspace keeps your source beside the tools, with light and dark +themes and a compact popup. Use **Study guide** or **Quiz me** in Chat to start +learning. Navigate task tabs with Left/Right, Home, and End; Tab moves into the +selected panel. Summary length works with native radio-keyboard controls. +See the [interface decisions and verification](docs/product/INTERFACE.md). + ## Start here ```bash @@ -37,7 +43,7 @@ dependencies. It is currently installed unpacked, not published in the Web Store | Action | Behavior | | ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Summary | Key points, TL;DR, executive, or technical; four lengths. Matching saved results are reused without another provider call. | +| Summary | Key points, TL;DR, executive, or technical; three lengths. Matching saved results are reused without another provider call. | | Chat and study prompts | Search passages across the extracted document, send up to six passages to the model, and show their source labels and excerpts. | | Remember this document | Restore the latest summary and up to 30 completed exchanges for the same URL and unchanged text. **Clear this chat** deletes that document's saved exchanges. | | Import text | Workspace accepts `.txt`/`.md`, up to 2,000,000 characters and 8 MB. Reimporting the same filename and text restores its thread. | @@ -104,6 +110,9 @@ actions; the keyboard shortcut remains available. Disabling notifications suppresses desktop notices, including errors and shortcut results. Popup and workspace results remain visible. See the [preference fix](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/31). +A failed settings read can bypass desktop-notification opt-out. This remaining +failure-path bug is tracked in [#38](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/38). + ## Troubleshooting | Symptom | Next step | diff --git a/docs/product/INTERFACE.md b/docs/product/INTERFACE.md new file mode 100644 index 0000000..4b5bf2f --- /dev/null +++ b/docs/product/INTERFACE.md @@ -0,0 +1,66 @@ +# Reading interface: decisions and verification + +The [#36 milestone](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/36) +replaces the gradient/emoji interface with a quiet reading desk. The source stays +beside the five tools on wide screens and above them on narrow screens. Task +labels describe actions; provider, memory, coverage, and source excerpts stay visible. + +![Summary workspace](images/reading-summary.png) + +## Design decisions + +- Neutral surfaces, a restrained teal accent, system fonts, and local inline SVG + icons. No font requests, runtime dependency, or permission was added. +- Main content has a bounded reading width. The chat composer fits the tested + 1280 × 720 desktop view; conversation history scrolls separately. +- Short study labels retain their full prompts in `data-prompt`. Import stays + available while using another task. Existing history and provider routes are reused. +- Native radios remain focusable, with a visible group focus indicator. Tabs + expose selected/hidden states and follow the [WAI tabs pattern](https://www.w3.org/WAI/ARIA/apg/patterns/tabs/). + Focus remains in the tab list; Tab enters the selected panel. +- [Visible focus](https://www.w3.org/WAI/WCAG22/Understanding/focus-visible.html), + reduced motion, forced colors, and light/dark tokens have explicit treatment. + Calculated muted-text contrast against the page is 5.22:1 light / 7.55:1 dark; + input-boundary contrast is 3.21:1 / 4.41:1. This is a token check, not a complete + accessibility conformance audit or a screen-reader test. + +The [Comet product page](https://www.perplexity.ai/comet) provides context for +page-based assistance and learning. It was inspected as a product reference; +no comparative usability, live-model quality, or latency test was performed. + +## Verification — 23 October 2026 + +Local Windows / Node 22.18.0 / npm 11.6.2 results: + +| Command / check | Observed result | +| ------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------- | +| `npm run verify` on main b584f4e | Lint/typecheck and 297 tests pass before changes. | +| `npx vitest run tests/popup/interface.test.js -t 'selected tab\|complete study prompt'` on test-only commit f6f9e2b | Both new cases fail before implementation. | +| `npx playwright test tests/e2e/reading-interface.spec.js` before implementation | Cannot locate the hidden summary radio through its accessible role; fails. | +| `npm run lint -- --max-warnings 0`; `npm run typecheck` | Pass. | +| `npm run test:coverage` | 299 tests pass; 94.53% statements, 83.73% branches, 94.71% functions, 96.70% lines. Floors unchanged. | +| `npm run test:e2e` | 19 real-Chromium tests pass; provider responses are intercepted. | +| Final affected popup tests and two workspace/interface browser tests | Pass after copy/contrast/layout refinements. | +| `npm audit --audit-level=high` | Zero vulnerabilities reported. | + +The [unit tests](../../tests/popup/interface.test.js) verify tab semantics/focus, +full study prompts, empty/result transitions, and existing task behavior. The +[browser test](../../tests/e2e/reading-interface.spec.js) checks native radio +keyboard operation, panel focus order, all five tabs at 768/400/320px widths, +theme switching, source extraction in the 400px popup, and horizontal overflow. +Screenshots were generated by these browser tests and visually inspected. Opening a source +excerpt scrolls it into view within the conversation. A new browser assertion failed +before that adjustment (viewport intersection 0) and passes with the excerpt fully visible. + +Global branch coverage is 83.73% versus the prior published 84.05%; the added UI +branches are not all exercised by the unit suite. The source-scroll callback is +verified in the real browser test. Existing tests and thresholds are preserved. + +![Dark conversation workspace](images/reading-dark.png) + +No provider speed improvement is claimed. Native toolbar activation, real +screen readers, Edge, OS notifications, real browser zoom, and live-model quality +were not tested. The settings/onboarding design remains in +[#37](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/37); the separately +reproduced notification read-failure gap is in +[#38](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/38). diff --git a/docs/product/PRODUCT.md b/docs/product/PRODUCT.md index abce0a3..a3d58b7 100644 --- a/docs/product/PRODUCT.md +++ b/docs/product/PRODUCT.md @@ -1,6 +1,6 @@ # Reading and learning workspace -Updated 20 October 2026 for version 5.3.0. This is the maintained product brief; +Updated 23 October 2026 for the unreleased interface revision. This is the maintained product brief; the [supplied engineering brief](ENGINEERING-BRIEF.md) is preserved separately. ## Objective @@ -19,6 +19,9 @@ repeat work fast without representing sampled evidence as complete understanding keyed by content, provider, model, style, and length. - Persisted language selection, explicit coverage notices, and optional provider connection tests using the entered configuration. +- A restrained reading interface, persistent source panel, keyboard-accessible + tabs and summary lengths, and short study actions. See the + [interface decisions and evidence](INTERFACE.md). [README](../../README.md) explains installation and use. [Browser tests](../../tests/e2e/learning-workspace.spec.js) demonstrate these flows with intercepted provider responses. They do not prove diff --git a/docs/product/images/learning-workspace.png b/docs/product/images/learning-workspace.png index e430e8979bfe2d94a8cbda8b1a3412de60374c89..bf1ddb6e3741024bf4798ccddfba782879e1a498 100644 GIT binary patch literal 67696 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⁢2CjLfqaq#11>++j&*pgPZy}_J*N!9w<+D@wQ zePXSiZX2mYW3zPm%HaoM0}|fFbq|i_HX%*ShTC9^w-jUq*2=7{W5ULj^!X+Wvr@oH z5vKyZ#5`SH{6o#l7!#+u^(-x=TP_hvs!LIm5(Ubqoop?lH`Qz}*Z+KIW~vRH(e~g6 z4YP#FEuP5H`yoT$F;d9+y-v&}`|!HL#hETzO-kl9l9b^S;{aq)6sOi1p58 zbj2#4hQ6i^M7cVrd6l0HcbZk6B6`S4BFS0iljM|%gT9@ck*ThPIv{&qy4{>8nc!Q# zdi_Zzh_t`7Coya#+nn%pme*W>Y)O@Dm`@){We)>>QfRYPCIzuypG=at&(FDkTTG1D zDjte()1PzG=O|Hs2}OMu_B=(EHxH$FkKa9&iO`ETW;TTLh`~BjW;dy)KB0oBY;aW!&6{(F8-OLL)p{j4%CxcmtA9 zWqGCMyaq8!$dfQJ8g2&jf?O|mtG@979-Jd*3#`G#X>inhPXXbI^vKy%Qyr3#a?9S; z;L>hG9ml(_U)MVCniw4lx(Hp(uYN+-Qz84b?tq1nm*2_<5;7OnRft~rg)j7%qPB!a zPna;|g@nu5WNuDB`CrJgz_WQ^THdFIl1|0ndqB!ryLA%iR(g4nt3# z7wkdI-_l(KmS#^2KN@;*W<5bzqwZVr} z;cNVzK1s{OCk~c`>aC{!WZZjbMsC`)0#Tg8ABgWRIRf(KC``;3_7geNO)-$VOb}~k zBZGH{xqc)7b-4_sQd;~7--QVgfb1d~XE2NQNn0&E@=_!&Ka8eFwC3{R!4CC5?@w9W z)^WE)u~a}S7r%JrAX1B5FlxK$ zXm?%H#Mg6=D|Cvl@69<1+D^k=;;q~*jbeYn*V@!8Z_r-|H8r5meS_Q|!_g#|~akQjBVP$_KXC2%Tt7cJQ5uxn~F z*yasPaGAODXQ7~y3B-mRoiPwJsjmehNWIgT~*>wMK6<}`L|q3gpeTz@ncE= zHf!vxt??>ri9I`>(%TiyN?Khkr@|s;cDo0?Q2MQidG#EzmQ+C1fx?`HFfaMi0pE>A zNx)9?$@JtB63ixf?0g#FMJpSG_&_CY9K&R{KfCZdYLz8CXcI`ZK~{o;kcT399bTSos9m2XKN=(?q0YP+p* zppfTEQn5C6_DZUV48czIJQMvg*bVbUE@pmG}jCj*sn3jCKr6^S(7BrTKLNxyFyK34;1L`5+^GKYz>$Y!G3()hl^rYBvG*>ub`i z*Gbyim*Dc%?3YeM?d9<-_hA2+nMA)g1O#uvU!zp40fW{W@0espgN8U_WUGj6b`jaa z5k%Ol_mAjOAD}-e`xWEjXNL}(+aChfe#n~*QaI$G59&57qynulKu7E5KKoz zXxO^9nCkW4;dfFy0yv-WY`zyYN%t!etYbJlX)3R3WOl)J$Kh075BRw)aCZ`?-!3J3MeBY}<6Sa}MHV`OV{^`BCmZ23e zsWqyHdUHRlRiko-iuBRk1eX~JBO(eX?7`doW%sZzXlKeF?g|JM;2-1Yh6$&-q;K7t znrVQ|JX(q0SosO{U^m7Yr2odTIwvhcY?$eR_{`;X8r+883>wg*m=hbqzr1tZEhad1 zR0@#RDYz zgJ-A0<=+6yd0K_`lz?p#=>QW$=K9?fpKvbPj11SxM#~3X)hwyTnLUiFJJzNMlUxlC z2}Hs{I5&{=!;r~r#bu6Z>@2;yl(9B>zOlXJ46lNmbrR>6lGf}anE>68yn00hJ zO3ZvB*_Re;8a%@#n6pv@avv< z%@TD-1gsy{j>z(G(KAz+ms`-m_0`_FlSCRNCSBDXT$}Ns*KQq2uO7>e`ynA28T= z6}MN=Fd}9l?PXXMZJ{82kwe6A#5+Q0V_G>vgAVD-Nbi3IbzAx(!Pe*=V^Jv?NWVB}T??>NIkD5Pw z-KGAR`J@DRav%z4Mmbst9D~{YX;&9j+zWU&;t&!mnril6*pm1^n3j5Hjzeq4VH^WH#H;$Dk+ideU!W>N6YvW9%<5HtLL2KC^8gbR#fNi=MVS%fFCyf?ToHUw&0y96h8@ec6eR14B0!2Icx=gg~^4y z<`XD~pt~ALU?Z?}j8DH8oB7R|B4(B+qC?iu9(S9(r{8l%zf#d1pGAEFOW<&5&Y{tb z)vs6_<}L7D0J{z{mlcK^b%|uw)98j4>kJmpfPZ@V3Xo?}_$JYdk3Z(MnA&6C7WC6* zb;9zDOtR}mXjsJ*ONu_CPGi*!JSlI#53=Ld?U;09>|0sR31wmNk9!5@#NocmBud_L zTlZ*$Hs;KdR-~QF!q+Uae9kpZyt@q?#yAK1)UWy@$phMX$BYr}>}q3wK>j^+x_s1v zK&p7%HC#M>?A$D_GN*`MztXOxDjLb4RUCG8I^tJ1OCQZlXQA^$hEIkMo%||w4tu@O z|Ft&1+@m-DE#=~$GAwgsLg8x;mh}8Mu-C8ng@ay^K6piF1wV+L^$4_xTmk2 z9vs|I5+ux6WG3)!Wg<5)=1eqod|!P=98BLodJ{a)U&e@1PmXq9E1|=@A92P_br~KQ zggl{PVwCTC>bb!(5ZXxq2e-0i;Dy{-&-B5e@?vKt3ju6Npa-oWdcEI`Vk}r0+wk*F zuJTCVGMCfl!g)!h<>14M;TkQjXxH`@gP6y!qyyxR+n!>;Ps*?85*(Fi` z);4PUwvy_TGHy8gF(hu_I0*1i7ID>fTB#{if%BBIiZbP8?qyC|&KL_$)fAITUoPpw z2#Z7JQ7ODTcB1nVCbHCIQ+5nx++0mw_-yFzrx#=j%X>fnz#FgvKRT;2ya^AWTymef z1qp>c*$-f=?Bs(7vfuw!@SdZ_6oVYz-6?s~Qh?)lYf-x6)to z6|AB4-gbcC*7?L+75Jd_#RsKjfrYi5--0Jl*G%h0+?u^T;J`$Oo1dZ;R5Q~5;#$A` zLPUPT-fPe>X8{4fehD#FEc_T&bfTCzJGakRW>i&C(~MXzPwdi3W#(j>a>)H;1X~F{ z6ZBR2u&T)UN{Ejc{U<9s+HBPE&d}&h(^WmY!g9u}4mY>XoSuh+t`saxa2 z)9=52W3Y9A+eYF77U+AGApOh-T`pmDB2bfKSkioo(M82JXd01lXe&YjhrEH!LJNA@>p z224&09sSwLgpL`)M^=IBHqdUX^ZCdL|FYX?sQF^8`q-X?2ZPZyKH_Q>s2C`v#J#j@WPXOQ+;Uvj}t?MRXx}>RLSqIvhf}V_2l@N zl4;~s2u=guqZ=eMv&$7`H9LTpRgcT!W}f&wonlGSL_#QvOk^|m*(g2-XL+GY*n(F> zp~y4lvMHj;X(n8gp$I-5>|yP>LOM>~)P#aDhT#V1DvyfyI=UDT9sv_E=wx7_a^xwWpOi_lepCV0%@K+&3NGJ;&Y3?SJt0vxf5Y8kggkzINATSv*cWf}CZl+2Bl2 z^d@mqKS|%viUNndEvwRb5n_1_a?3P{jn^vLePw#LI}6xi{CV%FN`~MDC|ddHT8Rec z_iJIQL1Knb3+>-?47U~;@L$LL@!J>U&gc&%f}dA#djJycAMU~M)?1%2cc|l^&H3mf z-P~9+0Q86`ue-6gf->q9dJp$CKa*CT**5IhHK}T};;Ok28LS$q>=d@2aeEOk5g+_) zzo>ORdI9v(%g+mL+`2nK5iTcu8I6xs-}-cq6i+)oFj7PHhoQn<4VM2uFR*-F>~$+t81<<m5)?i&VfP(RE10Gn?1n2jHAXM-_1L|+!BJB+_Fh4Vwu&d19{U_)l| zKK}TM)RBigbkf$Oq+pIc zIzKFfg0~0g!J4ozYHXI^*oNZCJ(`KkGk|%uU z%#4h^{?*D5{e(2AW+6Gj{*QI>e>SPy9q2fLWaE%qyy2&XIDVdBp)A@{ z#KyF)Su+~ntC0QmKVwzANl-&7l%K>x11ucXo`b7ZQT`(laGbxca`JL(T*JCjUMLI> z)Dq?!ti)P01~cPX6nyk9osU_#bb4FhkH)wQa}~2M&CkDx2`SXCJcvDUp`S!lcXUCs zLl)-^c;#o?N4`A#}_oy1@=KkrP zbn(8HrJaIH`T}RVlEX-L%Zy0h!)e4GsDLN%?uf`Vud%90;jzy+X&k6`$MOe}fRspR zDE_U$Veso4)TdvV_L3bSjEJ84n^cNj@TjcGM_|4`76~FC;Wu_kMsIXrs3IxQslHozNozOe zFx;OY_CY+^Kp0}5zckfzw@v!}x5d>r(RvNa@6xm}@%2OylLP}L6*)5$_fp)*C1pxu z;IAYd!y1oB%UELcWQj`?G(+tA1@}oy1HFtmIxNkF>ml;U*+TZqkJfCLvwwa%PsaDZ zyzFT`>llGNPQ`jp{;Pk%NFOGBTuaInIvgLXB*L2xKq#Hu76o z17-dX!G`9mF2U;_#^FsHI7!~lh(ugYUst_tft825F*V1$bwWFlzV~k}>{3HWb{3KQ zTPcpIkF6~3VWIg;8X;i2uV#Kvss(uH)T9paxsN7Gm-<`BOL?ew5_Z+4^txdki_d5v z48awvD|(d%#W7cwmCjR*?EGN!~wPMV&tmSteo-DCV8IU^?D?7bk_8>f^!1T-wwEy+` z@fq!U!ajmr&GuezqkP)1Wi^J?@MBYS7ekp@BnAH72mK{9(NC#$di+bc;geJ$A%;vp zWHbpeF<8{H@djaW;X?@s$a0LDSv%(iF_HyQGI5+Sg0Juh7-bqTPG_bIt=6}TEchm= zlOiy+HAHgVk)BAZ_Q_Mt-6;{*S}FwLTk?+=K=oK}JBsoYrpvSW5$o_p!Y5Z-TRD3O zPE6|IIXXC_-EgDj6Oq)UVdmz>b-k-_o9$rs?yGYzPcVvkYtgE5S?DYUIYjUw-nLcw zgNH=Y%NaGBE0Bg-(_bFSG5h1Q{sq;<9mS~ALF*@3Wun*M;47C`u6Q0H@@m6^W(e$} zXCADkS{UR`{Ju3pe7*al^h}Y;aXcOUfJT0i82~{*7Vt-}qkRO#|F7=OE2^pPUH2#| zA0i?kAYDK}I)vU;nn;sgLX(d4PN)`&bm<)g1f+vVhtPZPok$P8g&LBag?@XF|HT>O z?2CPME(ux5%3R5u^Zh-~^DenOm!ch}NrXW(AF(F|>lJlfP2HgNhAjuh*TeiNLtl0e zIn#LIKpg`0%Dp;LH|8qo+{ks+?f2c5Q$4~7+(C+eS{szV7}zHMe5=`usH#HvdbsP; zGL*(Hlv*E5*<=&w54^jg(IM@GbJ#AcLNT??Ifq-IOMJWdIO;0$LB7Z1{%aRS9zeay znl3Ku=ciE@x`FneSIt_=n0fQxFJIs`6WZb+k;)=w-d(ax3B~2$iR6(FJ(-{d5(PW2 z``W&uPZ3nLU)ZCm;ry|^1$x=pO2Xl2*My0L-p$y>Jq!7gGRv9ej7m74NDwe~Sisp@ z+!M06QH^Z(aRd4|+REY5E4wBK_6>%f*4j}`ik+eEpWIp8vxQFIx&MY zvgCQaS{$@ZeRuSLYk$6AIDN=n=x2o8=xh(DsCXOJ^sZP$tXZ$>nV+7+*;s{E@&|r4 z?e*9lMXMKWnZj-AZ>Kd4rWvDjevf#Rdsjae+>I-6$m04GPXZEPsxo75OiIwRVcn89 zW*aJ`)~mJfB`p4R7P`OB3f~Xay~7sx{ohN!{AW3vw>X(LBg6M4ffow`#f z{*I_S{HrMGOk`O3ZXa<(-9Q)P4bZa1@c>-dk8l0`{o&%jaG*{XLv5_cb}ZF8nml ztNeW1Qmqi``Hy0b=icmHd}Km+*HT)b@() zRbHVlNq~xTh4PVgvm37lBWKR_h`};;?^)y8d*7!*ce2z=3!XEX4z%{awu0iDvT<~*8X@<#do@yG*1gAo|(+9k-q1Ql$N=c z)pfRtn0`*KefiT(W!InE=DPyB7(BHDN7n5U^hz)S$sE8Rc(7cqOeTIN{sTR z4;Gqa!9KlMT$LVLMdR!>d1*RTVomVBl|=yVG7x|nzgq9wr0L3bcrW-Dj%Yq}KDHO; z|Ng4@H@x#Y`}GfqLT|gQ{(`4tPusvNrEY&(>$ZKKb-giC}`+5I+>L<{%JLTKMkDumb-rW!1@%CI{H^ zOgXosGhBz(1}qfcWMNS7MHw_~)I4Ob8&ct$Tm(;;nO@BS31?;>$?$8A(uEfEQVqlR zkLWj5Yo`*)VZJ?_!3rtUz=eBZp`)Sx%{RS@x_x8k6aFAL-6XOH`!dZ%2Xmc!YA5-$ z+EbiYiR>>GXVL-LXLn^L+p8&Bw_zCNfhV|P^;@(QCi3$TTbwZ&Qo zrl^RXzO!}jw+wOU?F}o^4V(g9r+y6?S?W2vRwX|uDJ!!om^~h;1k~o+=<~3Sy>}cn zxJ(BT8Lq0i{5~iIoRG#DLWn3yk;AqVbY)p1D%hjLwsyIG$Dqd7XIwnf07EOmSe|s- zm;@CXVYi{O8w6;%H8xAL;kCE-CU0qjJFn5WFvRVN0sP znnd|qDLuD|aH}8tmJI{A4bq@%$1+biG=opzQ_D#!95hPy8T1;DR%Rp3t}6xH8}$e; z{=}ALjA62yV13}4ZW4}N-y4o&6k+KFNE$1kPFJlvRmSd1_njay6b@qh4MzB1yqW;8 zf2d94m2&kpWvX=eBhab9*l_0ZGd4_Oip?Va!S^W4&hVNf=6HLS+P^~Htg#bxS7c<# zDa%ZbPoyf|TW}7?+xPYhP%>^B+_%~&fXpXB_#My4EL-T5B8gcfRKz|=!nX&W#>XE^)~MYtS?}#z?a4Uxu>j4mMO&}@wY_eIejm=wSQ5F z5Q@KD+k#u3r4#WAO?e`+7FOl5vAsizDI|%~{$U=T)HA2IBg1(}t|F8vd8~+d+|Tv$ zl@x~`huDeOm5bJPVnw)1>w)AgEXTXO3kSt=%@t<&ADaWFXX%660LnQgbY*1pWkq!!RA2h+4`mL_@ZFnY7PO4tEq~*1uj;M)JKgKE z1rlA~l=^gaITkL2#{=cinSxQh6;(tdTy}KPP0eopN#ZU)B{$$VV+Q}QEHg`8RbbM5 z!VJaxqu7sn#GN8azBX0MV@g-d^@|H{3yWFT!}&I&ubpD7p#t}a}fvZcV!> z$S*0#s%WN&gZ=^Sm7U%Tr7^x_1GE;PHLe)fMv~Ya;;b@ZacB2ByM+A@L`=9vk9rOS zq|zOenAPsK7^D!McHRl-oy_x!n{ZYxs~at)=OM&r>F$fr;Ta2eM0uH61*FK^eQ^kq zk$?=%0$)=xr9WV#T&7ol$2NrFEYssT7l_|HXH!TP}><)(cR~hXTA8Q9nJ-zXe zm3({)>DyGMUhguzg^>@ov>D(?9@r0EF5Rv#b!_FKBf|a-Y-MS5*`1IvB(mbVjNtIT zy$ol$5cKwody;riz&5Vhf7w7Bx;(aG&Pl%=`dzWfWulCiOi0U*{cl6bpI5S8$BSq zzR}EOu73pK@cyy4jrreZD)5kJRuvvK^lfv~1S((opbX0LG6%Y=yI981q#gKqAC(C@ zbrEZ(vSF0L#dp#z_UiG1LKYM6ybG~Fiuxz@{)+josLRhl&)N6~OSb#>$un|inBT=z zCRtogY%yv+>CeVuW-MYNBjwz|vhL zGDanP*ZQnu#;WaABUWkc>K^zkUF~Fe8n76D7yu!|jMw9)YU~`-9$c8e(gCbqNk{pZ z-`^ZX?O3+~!FP-D^DX~lkO-*kNo68b=3WhMzpl0KeCpN8REJYS2?@5SC_`A+6PLpN zfi=sEFV5W=?}F#8pA{j!)H=Hojr;C%)PKu+eqQp!y7aMuH}#B6oBv zDR{pKi$QZK0Q)?48sHlfSsd_vN?h$+t3fbavu1*3Qm{9zTw+pduyV1$VF1`V$FfCR zufYwkT&iz&yzR=#y$_}cNY2F>CN@@%Eo*Cd#dM;!XH_0k4(e)uyoTF*OUK5b zfnHsXIW@gXT)ipKDST{XqMs-n0ZCXRz3feH~3^hB( zb!N)wXmsoq7zHrL&U{N0_l@#cJ7(cVSbWLSl-XtAs8)h=Pl5fW3DAej0mIYGj`g~Y z6dQMm!^~zk1cuUo^;EL{iNtnvb_Wb4~o=W*%5qo$6PxbfN zDXi!{I`8n#mo@UWTUj|+@&iAunl<{1wzxgeVwk8XpOR&7BlOPbQCKjl+a=M=|Fn4z zoL<>@kbk|>$M;uKHGX%&+s*8S@$#5vFcS56iLC6dZbEN39JrJ~%>0Xdoc5}wX-+XY zEpeWIZzH{Lf#78ST`FZXL?ySRKz$2TJU&&bJ9=-=I3Vkg>BRG8^Z z)Ky8%ojkyr37kIG$@Ey%!UEFB9p)9b7yo-E;miSb7=Wp1#u(Tlyiv_akIc)CbQKhe z;7*>@e;pzxao8H>(1^}DZ^En!*TH7>8EUS#Ynq!bKhs`sfAj@i#rvbXMemy>GoV** zkJDk@R5~9n6`1@EkX}tbM`%j>ILPnDVlY~)`4DqCFekX=HU9jqS3WY+--+p>U;g9P zz#_d4y2)*?5_(U!yg93WJhRcs+yC{=DYvYgPR%!CE@yg9nEljXO_*TJ3heYUl(MwJ z(EfZucY^i_(d+gC2ONR_Vgc+pEzqI4rdvE7&Tn?+S#P=s0=wXuvwf6;gzI>sSSB)d z9^JIZKr6nziSu==?MAQ%y!*yO1|v{W$F+W&X<&2=pnjLvKVDE+@M0xw!6M($-K?YW zyN7cVc`v-(Y8IZ|7vOVn3!UMhS4cQo)0uiI?Ox&Gu{E<)4<+FT%xotdnU$A*C0Uvp zdz@8U>ctJpg&tHkVd|5{#CotZ?Ci!77>^K3M@$t@6(gw)`IBjzBHLo;zhRJR=q%EM zz~^`vvl4qQHV$L>nlby}!y?JxD+2XXooOzqe;w5%2$~%{5pm&TnU>sYIKh7PSp(wj zlvzNn9m%ozkNkqBZe4$WCnKAu8l}Mv(09RL=%hnj{N4IrCJtaBBGS$&{|*hH1Ti&y0!3E()#c*{776^o2z|KR6KwV_T7Ek%h&dsP zLL+GJj`8NJf1{3LKV~7{BT>758Eae~ymk{@Z)ZAUCpAFUZ^Vbb8h}V@9 zg#hT)*E3s2lGxi}JL2PFD;;Oc^Vu>nG**4$=`QP}ZQOuU{ZE2`PxL@yqj>Q#@ydJC zRpNHhoqGe4mCoy`&NmnaYrtj^Fk}Eflzd(c>(>l6)Pr*2^%*_aQ0Jt6j=5mIL?KdV zhtOFCJZtMIV^#Yb+fa44gNv=19mmb#c>Uf7P3L3%lEkm)7f=sk0;wA8e98qokIe*- zW4$>jTuCQ0w&v-Lor6=3(~iI@ck(9$RD6l4XS7sliUv}k#Ce_tLL~Uj*~0EJG^6&d z@WlAUNDkmn1Ogr;se%rqwR0B2*t_l`BK4-Et#5u8&t>uN78mV}U_wj7&3D1elPM`} z#NXS)RD})B_BkuESfTXb%e0MIJQx5R z0RL#&RrunUKZNa2zgcM#Y4A~M!2eDQ@HiHCzr|zJn;lGhKGf488E|qtL;i_ADn$Sm z)SJVy$taKV>d+^V%`=D)u!HFTrQ*>C0#bhX|GpYm4|n`)H|7732R_QI%-NKoYCK>- zoD{3&LGS!)2rml=>lSzUYac{jDzl&PO;YCrdCXrH43ebnK1Xk)iQ&MxKB@4GYUUR`Bb;Y#Z?0veWFK zua^YmQ`Ki>`Rq}>^ygPD$`Z_*N#kei~W6B;HRQvtF z?tf-2ohL~*o@GGc`NyU4{x3z1>IdOP+L3)-tTl%RB|G4(;jo8ig1Zx9@!3VDI$xIO zwoMsDSB{f^noZ05c2{-M0jWm;i-amuQ=wj}MV<22o_*P~h}6#<Gb>rknzuXaa<6GQzvyQ*aqRX2x}!C;xTBp|^wo>ul z8+=$afBdX(PKIy%tinTueJ||X!ukgD{QTU2xmDF(w!9P>hSZCQ$Tm|DDs}^sBvgku z3U&;{vCQE2Q7NLF0Nt&8Xx zrPB;BA83}hTFHOsqNzGEWN2ke-TZ+_Y3c_eoD-M6Z6v%*SyfKHQR4hOn`~PXYXzDIr zoZ>=41u6xD26XlGv2MO7?CcRX52NVG$#Gg|u#)G{j>DJXovtb+*B#=hy)L?d2Bxf} z{-$5=e!%ib61F0KiPiV*oIMF1xrXBM7}$?jRq@p1%PT$8!d(WET$3*4!mOVcci19= zB3X}ZLw*Si9xrozT*@KVQvZhSx9r-9kO`NC#i1znNAipD#M87cE%~i7j&t#Gkb$VB zi(TKsllYYhV`bFUAgu8y)d+*$gEgNod7CWP0$Xt)2icV>fmkN+|1G8d82qiN!6#gW zHCYT4mUm$7eh2G=E>Hfga?A6eitWQ^I`O%DnM!WB4bP4AiX?TnokgjEFi4p3zhMxI ze?+H9xkkeymLeG3lqNrW5hjDD7>a5muAG%_s7QG0faGd+*^Fstb&0W*3(%|Bea;M< zbx;mc5>>iEPy?r|q`Iq1Uj=u5`<`m8g0)dkUEOoZ*(Yz}kHMA2rE|I3&aJCji>tUh zZCR$Az0ZN=p@UOg@!)+%!5=R!26KXuMAPRp8_6s-E{Y;+NgB@{tE~Ev=ihU&UD1wp zK<}3j_#a~H($?uNsfPwiEu~=g_h6{)h-sJ~a<4cwQ71vS_CE(;%M}=aS?57ujq114 zKA$$rcbfET^azr)YW%>S0h3amx5@fl*ccB4Ng*^497w-hBKrc^2SV#}lo%@rLqxAh zkI=&x*G+xYde@?L`Fu088G!XHfj4J!19gC9bK_4w*ZgPlm3Vg$UfQ(=0ckj}a@izr z>W+9^{yh4L_miR5f#cJXfkYQ`ydq7q%=kHr$Zf0^_LNnba!yTzoYiS(b}6F1^(qR! z?NWWW{OvVxaV>q4uZ~=plGk-v(C|m{_czKF})0xR#zE?H)oIyp}^X)VKNd| zu8Ks^yT=EUxvVywFM;n^bI#uF&-EgeJK&^UjeWxuHITjKT?hbq?ywEi-~Bh11@`~M z{rZ*85AkDXNX{sH9x?ZE7*?@7J6-&y`EVwEhafzJMK=y|WHY|cFX)G`s%0otFD+~0 zj!j?Z#AP5pYBS9|ctV1gDT4G!!h9DN0{UlH(CXUJ?#wt^7B2rhTbWifuu%8jYnd+X z`7MVd*X(7EGft7s!kL;}PBF}ODI8S<1AvRVH}fTQI+!KYA85gGR*J+GD`8sB!YWr~7`#n;v11oxpA=(07q9 zFHrk}GA%i3b2pl&5e)GXIs9^s|@eDZ0Oh zlETwkaBdnt+lUfGc5Lm#& z!bmvR+F5r7|FE-~J$ETr&agybRES95$FjiwR4F@mt;w^T!$+MDp`9-V3oCf@SJ81V zjmP_aQ%{4E#Wl0lAJ9kmT#;1fM=OY&Rej;QhMUUPdls)&9O`TtkkWg_xk|`YZp*10 z5H98QOw)rNW9Buz^^6{AQ46Y=>}q_fb>W%pC6UhreA=Znw}0P#eS%D=uNI+Z#`e4Q z{Qyy?nb}mbB`WL5J9T7t)RPy#d@gYshsl>r#9X85+hOua7jqsud_^^9(q|EZSG$Sl z3)Lzxl*Fi|t@m<;-l&-d+*7Z)KXJ?CG#_*5>s#XD$i_u|dAu1!8zS8ds`vCO@$1Nx zIDTW-4bP{Ubw4Y25=F0!^3GRFJ5Tou`uZEsZ-bJt7fuT_Ru9`u&++|LxxVU@Jc1C= zfZS&pG7<#O*{HJ*Xw6y<=YB_LhE!UbeH`@{@Ku4`v`7C zN|xUz#!psIo!G3qav!G%Lf~gJuRE!C%J|yFJj1JDYkmdnb<&vM{Fv+`vn#iKPt+4j z2y74LBjTPos^JT%+alrj!p>k|EX_GDa6sIoLFtUO$q7RW@0E>&4{ZzTrS`&E~Y4cAKKu2jpJjOWV-=Ep2; zS_wpCh*o+nHaV@@y>c zlPrk3s=ThR|9u|;zw>*eqvBMRqmKmlv@96Bcb5)3Hj;#!KAr(|w#(JX$nvw6p3(RF z;ir83)aj4ti=K%Q3z^yD(#mAA>^Wt^L%9Sdv)02R-=R>Bkh;!IzIiI-obhF8N~V}l zfq<|;wPtO2P5ArDu%;v48sMgFWEm#4ime}D`^99+sv~>nxQXEJPrFOz;R8UWFCOvh zyS|NDSulVSdWYs7_N@X|bW0ziif;=|ok%-!U*BySaaPf19m|KJvh*5FdZ_s6j(n4| zqoi|$M;V9rQuZ72tq{9GSux14rML>_6&fiQ9M!nsy*9*@6JWUP3?aU*b3iL%=0#ce zlh?UrmjJI3@?rDEnz%=5$<=Ih;(?F}RQF6*Rkv|}z+>XUAz6l`H;l|#T{BkfVQ`bj z{o!RPmO>H;Yk$q`%uoB08N?P+dWZb-Dt`W~_luKb@v&+9%4(+80aJL(qK&pMlGbn@ z@$HG&&+SSW8HDYC_&i>$S}m%5^F`ZZxYFRcUG2d`IzbAl3XwG2fu`xot3$3XX~he& z>6e7Ot~&#c8X4#972SPE*HCEjncqu0U3A4fy658ItAs`rZCe$pgxsZ%&2C;*IH)M# z(sJR&pa{vbttrY&c#e4)Xx{8=QHrn@om?NO1eV1u99*l zG$Yt^QVm2B(kv>a>-qP9t(UnLwlRs&#maC7-0|<_lhFk#xAvg&(>FUH`S;MAuII6F$!x zKsb3WWNJw<-`6y_PJf-Sk>XsNecC{&N%q%Aqu!YfSbJ7EbXA(08`~H~Nw&U~>Pll@ zID#Nu%!GJhjf9d&^W<*t^&VfNuNBfl`G}S2H-@uUE|`1-dDLbH`&Z##%%&NS(4#pZ z@g42q2f#A9Is8Sq+-tF>zD8p2%12Hp=rYF+SB2N_3OWwRYZr)mIkiQ9aQNr@#r|h~ zb+3OXzAbr>mVv%l&P2LsY8+C(3qKs^>icNG)?U{>oawzx!my-FbBn?8>KD9W4{HBT z3{S7XBGgYlFDElaa2U+w#!Y?XtINWv()MC7WIYv%Ia#rs_RegoL5}k-Xm%CV^g4*_ zleiFOGW$eYXfE|>ausn=n0(rHY3G8u!}vzGbb_^&svhx1BY<$g@51CsmXSXNgq#Pc zO{1a+J*R3d9iyz>+M%-xG=R-lNIS$?FZzJ_&cK_Uoi#}VW|`OH>|shy@UQN43(A-F1^LE08`@ki_d|=9BlDo& zF7u1E(sl(Xm|~k^8T5Rd$W@r2@y@l<*DvbwlkakTevhgJ*?H4F)=+sER+T#*h~SkB z(a}MWr&g(CMy6g}%yA8&N4rnC*E@5XOf1aWHZ3JCtDiUd__(=zDDEc=OJ9=!<Ohm$Xc5_X`RF4wC{20e2U0IH;Rtd)O*$2*LMxny!9fHb`0%=mjbBj>f==~YHsB}o zrqq}Q`^eEN118Qz1i(H~dr(vFu1MQHi_zTE%7(KS&v^u5KWf#$^4CLO zB`P|fA&{TleYYLnM7}Y-=b-a;06s#;BZ~N5I*neD^5|dXpP!X)qjK%9kXqd0HMCYH+?-A@{{C z1H>ccAz62DDXO3ki{t8_f*_RM^QXVddBuBq@|&TC5@?~b(?QkeeKTDrSSsVyENXbb4wa z4_MT1-*xxJAjou*ZT0j$WR_jqa<5pdlhuQI3n}1ISG%5?(nPu6Ace}D-2UlS)E?hM zb((0FAx6jki?x`>8oKK8Udv^+y6k$CjVJf!Djo-B(R?7wBrtk%)w2^^%UrO^bo%WW zz@{D)s!sLlB>R4#%l3b;S|&k7x5~w}UFGsFt_U99_lU5Y%Zvl`vUzOZGobYgu_-T* zi{0EIV|ydsw(o3Y<;O`5B{CT<_GAEtt zcR`tTS;CjeYUJPCsrs8>Y3=Nh?+eI$)F9J;tL`hKDabK3LWZkHISlTm_ z26q%PIjAyh(|Cga829w=Y71hy+u1eh9c^lg@o_Vk0*WJmJ5Yv z=$d9)PZ-i%E(VVs{-1TewdhTqPY=NGC;&_>9Bw!)0VZ{3BY)p_xs7Wjml1*so+nDQ z$t!0b%V+;FUisrU=ZOcL=Z_xI9vL6vx>|3K%O{x{Ac$ElD;Cz5PTrQAk^7%CsQ7=k zu=pQ6gPlT(gC9%-!az1W3qklzn=)6wr0@$0Q^e7hjdH&V!l)bwJO-`&k2zZn%Vuq6 zEp(5Mq*@iMeGo7B!FtyTGMrg+uVXtJAMM(y=H%P9M-_513MA$dcRU-+2NQh(%Q5$`K7ijTW^?gR&HFpDSN$zAf6Tzt_%xH#e&&(p z9A>Q!b)S`07I}L(;r{GvR4E=NtYn${4(WtKp_=+2AQUOiCQ$RMG{k=F&c?tcL(@v@ z6pGWy++axtxH{K>@XD^&Jx?o{X?j2{#Ark&F{8H{L;ih;_*L2u@-b7f%(*gAcoRaF{eE<)hu#I8ZNSUB5?qEA*NwfevGF z=Zy`AUSM$DeubhmWtsKqq_)~f@AM!xcoCi=qE0$mJNddRs`@Jl zXOXr+?Ch?TaP*N8Ebj5g)Xz`Bp3=p*r$v4;!?v~fQ?Np-;48k_>a|;O*vDVDjkQ)Z z%?vFnnqt9IVer}A2Nc?2UuXm1FUU`&xe7^-%6WR;&rFL~7zT*aBMjQwO&)5r@sqxEY% zHj!V?lm`t}qh@qI>M-mbJu#u0fIA~je=-zs5!F=`V6qDtnz`f7UxZ?qp7A@=U$#Y( z3MeO>@+Wg^so(U9K}=<;phA++CwN#ot~bJuSz|La&!}m{fjlq?j-RmS0MS`gaz|u% z#83YEl5lUZRzK6fPv76tFQ^D)1I}rZ)KTQRz5sn)w4k~Y)iG$lNarhmpj-)+8WtNcJkp< zKivot`FFj3SUlfQ7B6_UYN5CBk(LS{$6N2&qp2ipzJK;CEzR`c2 zb1X)4Zex=-B$-J;Wa!4zqb0t{Rpiv$7rd_~$77kwd~dilYc<;{wR4(u8QKH5FI1od z5BXiY%s>(BO=9{7C5GfavTPztG+;$^9b1}A;>RQ2V11S1%%2Qb=MQ;sx7Tby&cy+- zI+(-inC^3`(W$kbMpV0!a7D0y9;g#X&V3Uw&j$KEtxV^kt7Np>@K67zrp~IVb2;0s ztN_&Y*0&|9Og*zBfFST1`gA`o_e%Qv$>m)@le7*cB zLQX4jgj)sF!*~$d%w`Fl1O^<{3?xy`XvnIx8%niKcLi zZ;5^4K;MByw+vP}H!ZZ+eG1C%c%U?0d%Q{;8KERaJ|Q-6dPSTc+$LWVaDSXsUb2vV zUm^^5_z3(-o%(fTZ>Kt_igJnPd4f#WLg?fZJpymrv)#rA1SU&w{#@R&E_LGL%$U%h zAHp?GhJttd^D-vVjVxSsGr|%BXkd|om@nS2r=(&!C0)Y+Wt9>{vmc zY>Dy-`A*OY(vG&8n~X8FaGL&pAx$L-svPFRGIj#QV35}Cgq#zEz;XLl;lTEUE2+fH z)rZan1s=Q!b*Kz$dn_w5k(9SCW&bgJ>zQJFTKe5kvopoqECgf`e>^ks`{ggzAYo2f z2eOgc1`}dYAlK`*MlDYr>n&*4RM=GJ$X#$%6WwcL4*ek}q39-<_a=x$FEDSllwgmP?9X zP;1Rhrt2B?cvO~hp1cFk(lJ4LQmJy$=um^^1M2%dH4~EENHbQ4AbAcF*qJ05vMTV} zoxwiWBspmEOomL5=6!`>x6l?yJ@)R3&6MR_8Sd^+z4tmznoP2C8vA@|!y_)(D_W>y zq2*};id}J6RwtEs>~Qh~meSs}SWonqlo#{AspfJbpLknEcj3qlR^!*OVoKL)^|~yU z)Cj2}VAk+j2z-h7()mlCNZ*9Esz{~;j}q!^RqkQ$Bd3(IU9Jes9H(7gVy7f!Q@fx%40#x{Zbg~X`_qk2Dg+K^4m>-Gc$fhXUu#3UvI}PrO z(d9c1xp(Aw;icYPwH+qD1*$A7W5=JS(!HZ_x1M!&hk$D*zI-J5T3fSIVcE;fd*Yly z_{N4?PzRmOHSg7at2|pBb6EZ(Fj~zpwx3AZBzK0_*a&7~#I&Y6l20j3kRIQK!)$aB zv1A@dh8yMs?=!RuEwxK1A6Jl_pV0g+`xLT}9heUd)wlM!Djrf$#J}QSO+QnJ%}&7y zrU6N?_maK$O7!|(i1WtlRKSvC!!o2`J+7pNoPU!n?}AHiAx3X|r3Nj{p1QS3hE@Jt z-^FWf*LnWc>ng0o<-du_TUajeu_=R8%ZU_-$$Yrq+d<5L6gnHFDJBkl!w-hSGZ=t- z@?uF}On+7zq^)2tZhpXAdJ8KF(}Tsp@b%WsejSuHfK~a=pDfM)PJ5OAjTSBcU%$pR p1{ih><}6Y6#*lLFX2HeA`aFiL3TxT@3Os@(FQY77`pWpz{{a`(1Z)5R literal 77016 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 diff --git a/docs/product/images/reading-dark.png b/docs/product/images/reading-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..69230308bf3e1e45d71c1e542d029ed21fdce0e1 GIT binary patch literal 65549 zcmdSBWmsEn@Ha>eN-5Anf#RiDi&Lx=EycZ1plHzGP%I$?LMa7`7k5H&ceha7o#F&{ zw_t%y<@sN`*Y1~hKkT*d`I2*T~&8Vd2ZkN~&RD-NWqO z9=v}W^CM0xe+vui5tf|f+YhdZ+q1ZOPgZVl_GQ{mzDXH1VEJf->2u$`%lGriS8-lN zb%vj#L5Hn~l`t7yQdc0wmsN`QH6|AWrxU3;XYG!)L6gfA@}W z-!}ZaH;8xtGlo{oCeHc)vkSl6`b${0(%d{H1v%wGJip~&%B>uPLA~qyt91vXw+kNH zk8`|ro>sZ1`Ot-q$JTdGOA7rs>q0}NP!D#)H~s$2*Xr@ouP>Q>39~({u4nT<1J^iZ z(PJq_-oU<)^pt|FEp$ULSgR9o0UTh$DsC0v{_35vOO#sYEBg5^+!i53Zwzym9V< z9|X{f!J^hZ{pI+NjYik{c9noYIlh#(SL#+8SriXG2kVd{1XEO6q^EGrwdWeF z*Em;JW>R|G-zxmjA15ui*Ij;mWIiy^&eVBmWz5g@XS!G~Xwl(|QWfrUKcDFK0q%~f zVr}!brj0{s-Ux_zUJIXQbwaBS-GO)cE$2R1vleNOob^HP|Go8t;xx#1%zUhtleHGI z#N1@`c9_@C9{`Y9;+wRY<16wbtZX;*yfyXLRAsd;lHYoZg|)BdcD(Jd zx7&b(%@*n{iT#2X=9geqa~B;ft1#EEEsdlYy{iJx(N%P9!SCihL;V_IR(VJv(CBzk zf8o|vd?+u|T(Q=1qtp>S8OZ|@p>ix-%}iE?TpVKrt}mWXx5oWEVYYU*4!n1| zGo2otLmm3o_}%Q3oG;Qb~R?qU7u+uwx_V5|2J#+nK5) z7qOc`;Nn*8Er;o3(uiRDOCv-jKyskXOE8nIRzau$W0BqQ^siq19FnJ)(|VZ%;xs$z3)kM)5EeF^3&yp{5h7wH1@H`{e0G8G0&SL;4Z#*J2os$tQhQ^ zk`NxDyyrjDaJ*$d6&FVUv){jjwTy7+xnHE~95#tiZR5B>d^q&uUq*>mdm^X014dt+ zFkzhnwpdKbc+&t@LpX(2MWZ;ftTJq&21f(V%%ae3P6k!(enHmmE`bl)je+0v6KFlT zkbcpIFC590o>zwJG&xzt3Li43b3v9Au5Z_=;O@nfma{}_uj>|r-nQOTOUqpY(Egc< zr!eywV&jxl|1}v3(CfyK@zi5>4z?7$3I%WX4VZ)%-ZHuK2DCv{!5P2-#Xq$9Rlc!` zpJ2aikR{}PFqYVeMuvzzmC5}%NVlio;KqKjcgIfF4%2;6d``OfSOsvSh{9bve`cbZ_3_r$>V%}7A~Acx zgyvpuwmPk|g$|ST?yIZYweJpdM(zmXPzgD1%QNiO**Zv{_?y&H=kPW zieOTVMHt@7;j`)xX)5ZMZxUI_T?a8;3Iw~{Cx|}9(wD5fPB3U?D@#kUj+-b`YivsV%@?dub-mrH z@)$?%JRnY=DJ=DHyMd%?i9+yoglw&&36YVe!9=BnZE28i>02JH4zB6*5RWc?w^83G zi}SlL#X3vzCdql#Y0%*og%iOs`R%N`hZ0JnS7egk@K{hGvOz}CnmA}pVqr>d~<){^~yZXan}YN08XdVq|A9dzEW#kew}bg z%Pi~X$n>-m191~u0MrOEMgSXxt4v2L?w&?eR zi^=2(^Vz@0G*k6>bN~x2iWgbmXh83yqeIYO`AHfLibPf~kFpg&^H?vL{cm=wT4#I6 zv#~uQZg#)Rra0FHd!i^5M~7#@49Jm=e$oY7R1)%#TeW6FCcDJe$*9cyl5~3}c|9ax zJ2I5NU%1P_FXIx~E?x%K40%1)=EI(ntxH#m;rzXt?RosUr^R%;A5c#zW-_I|_L?Hf z=aqT*7r*4*4Jj;q_J{v6Nr9Itf_LMg+Qww75iCb^JkrSy5T)MW;KO07%?NYN%;WWi zG?2)4O$^4S(eUvD3(NO84`MJg=@Vvs98Z_2um+QoJfOVqU_P#GF$ zbqO)7tPIOS)3!{xhA6+$&(ipme9vamOuHDnG8h3q0XDIfvK;9m9*19bk;QLYtf+&j zC~4RB*4CCM=(#t_9alD%BR@NsT%J1Y4o~K)uZ7ryphrJj{5FViA=H~n*sqUKl3M(e ztW<7?0>fM0MBJZRxw~XUHVE81P^)1DhdBkBd+wL9d&{NtAKT8%gFCfF;n6=vzSl4q zbLWr@^GjuRmZeDA4wh08qaTEn3VvjIEI%HLcg7I&K^g)u)|ahSymFhE;p#^`ef+NV zM~JbCNrVE^-g{{QcmlTUNcfliN)jT(oxg%Q<*2b^yiP7E$g2x0@wTO6#fm zlFIA;I8mFt6vxqm=T0^@YRH3#=n>OwMV6?jM%v2*K2=+y{L(6%ErW^n$l%DzE7NUfn=N%M-&392i21W7K2dHll%(;=ZLo^-el<%J9A~`t;I&_ zOv)GQ5h|nO$U5j=(Yi(LGg5!p`9>DIu4l%E@!^|MINhKwGlD)+Y*BWL1F%AHu$OfF zLwhkq^oEh;&`Zj{qx+>6_jQe|qawR~{{1ZfK|7k9c+RL?tiJA5pL=%`2_`{2@Vzzy z9ZkEmg+k$1x3 z8DRYqmdy`zB0pDp*;dwlln_&mSq7(5B%o9i`1S_k`A(uPE6 z1ljWP0rBMUwy1sG!+l5lxqNNqQw=p+$07i{%J65^j~`;No7vPs#;U!U_jZd1u8l2z zq0}N5+vi8Rm3H(+a>3Y5puPIpp`X&{hd;L8URO|Rn`^`<;85milCIi6)9QY zu%@;x*wFPdd{lcrIOo+4Z;S{c)+`o+_Z@8lK3&`fr|fd}%Ks{U^Gy*KKm(*~;4Uh1 zZ{jcWqzImtGfH-7I^?o@P*|}&Zwut*1K*VKDs+>I-v|cAQ?$btn8gml_22$xJ=SJ| zQ;vBI$H&Em9YNDG=yBHDJLjJ)I_8u)CyF;m4_AfOj2JmP4wwxt;QBIEfw~$V)OiHZJEz<{?&v&ON~UX_6?!m z2|2gwTIQD-UG}L;tBK-;;2!Ore71^8rtJw&)9F1)`}~XrO+7wIV>rrDW4^l5WLWer`t?69}j~l@>-d zs3h>)0te1!QX7244TtL?M^|6IoVECYx2Kf67fK0vt84%sWLUNke6!CHX-jZn*i3O) zyNRDt!B=XiGY`LYOT%(1GLMYIEGgf%l{jN<})HloJ6qo?{pJeM>5Hz$Gi zW)Lv$sHWACEKjIjb1w#D!ECC2^{aBlG$bNtTYdmh{krV8-q@b#YTO}vcG|rkCU3kb z(nRh5K^iL!*V-v~g-B7#guzn%t$d!MH96J4Pf@6MJxG1qg3Xz%wbkNgMZFpc7)?^o z+I`2ecI!TFm7{*Ica_c^s-=fd zcxV>D4wJ}LfA=?xR26zc`lJ#AEd;$7qC7U;csWAM?9z&XCG7VTBCNy%GkhX8zy1eK z1YVY=$`I02ZW+$a><7EiVvqcvPz0G7)@p0R!B~4!+1w}W-~UAUIhqf>{z5wQPiFd| z-u4sC=~C8Z{Jly)PR23zGk{ivqZgs;mkQ`FNTjswohIL>K*or;s_9*^pze%RRda{R zI3aj1GTOqeeE*|@jN;L+npo8$VUI&zEw=(6haS>)b_{N?tkIa2g1r4itj4owUGt}s z8v4;+i~&nLW0tjAca}ank81bU|E>@dTII#&KUe{4laC3jzpGXnv*Xlr`ahV!ML;Hu z`!2;FvBCP^0y+C%Lie(nUHjr=wZZA0f5Js9{fGClK8Ue~X>{}_$rsv>6xvSJ;K?yZ z+^^5~#SXqWCoI_*@5!#ft;*a7U7xow-^^Zj9AyoBgK=?jJpymJah$MuX^rGKF^`-A zF+Nbe%Tn~R1O-MRKVRIt#U-xv33Y5SRBC3lAX0GBtV}IsyrpS7MRmp#FTPUoxRUnG z<;W`g!<%W3`Ar{%sxn6%-FWai5dG`YAXSv$viTaU;1;~@1p@9!hn&8{x?*7ydf+jK z;ThDU7AO^J?kK_fc%QJ|a3t+qfijH0K2#IYW#5?e@YSX_mD^&~F;fTuz)EqspUS z$=H$-9z1yPD_@V#VaoKv>k__xC{`C&&*vBkCR89kjX^m~!FdFz=-f7wRK#4}Zx)g> zwvtK3hNhem^K-j{V@q27PB#+*ZYRGUkf3~XP9h0$j<;vRJL=tZJ~^&c!{OlpWyN?2 z`^sr1J?7iYd=ie=i?y`l5AHJ2U&#~_0%XR^t1QIklWyt4<_^=(G*;CV~YD`bDS32;*QT>U%6m+@}d;dkcd;XT+D2P@Km{VnTJzr*gF{u6#{DRmlPtq5Ao!SR*DDXe5wZM7H`Ob19!M53 zEK-6?-jtZ3zA_wsDnNAVmLqjy8)T^u-0V$tPi0r{HE6wCL_(*0ZzPqG4lLV7Ucqf^_FcxRcO*-tNUt_ADQ<;w zKE{)I&+g|eD09bdu3Abmq8)rjH_9rf&^~t5yk{cFv24q9%*vRy`iY|DE zex^=Aev|fHi$J5^qv6;NcYR*-xBF%01JRrW(5sJpQxX$hr5PpM`s))lOWrtyg=YHf zY2`s8Td*@f*fVZays#RZ&{a!O)<(Bq(Mj%a{eAFTe=V9=YOXyOwlH4$h>t)W!g8{! z^y*7~pVBurGvw-o)3H>4;iyTkd~k`U@y($4<<Zn7TBb=gLc5pC>6%SqUy_sIlI?A<$xi>OgFT zTrd**lYN6PerZRrHEo%I8HILQkI`455wo+kOioDF23Zzl201?p}5nYOnlzHKQV5O2y&AIygxWyh+Z^Vk~I_Q9WZi=G-e9DF6Q#{XfqHd93GC> z4LqM5DocZ?UtKsY5zwsA1ipYAyx@#&Uj%2J3*wzSk7o(`X>kUt<>f3_n6~K^Dk30$ zK2RQVM0F+lHZwu`z2T?A=>2GyA#D{|7am)CriO4ir~19dJ^DJ`&Vpsi1EoGiOC->s zi+9h^poU1dw$bvM%f#f#0p;{;3uqjBm%|wKa$$~%OnEi-a;?uC%RkNmvbUYdjx&D|FZ=?j-HulnqTUIOJ0>$s4`)YX@ikW zrJ91wRbYe|-eU9}W>1M5J+8I9OVnPB=F~`Yf}mo@5$%vzvhaN2+dE~~CO}C2WIRmh z==lBaq?mw>5G3K#7W$4$z8055roNH&a5){s?Pj}CFhS+_+C*}<35##D;ql&3sPCq= zW6{c0$t7WY9gnrW=Jf&d01$F95t<;C`uvotAM)Y)R%?BkA=1~SRm3nm81=9xksbl_ zObJAIdXg1+;f$nCyf2m3TotG)P%geO%vUJFEjt|8nSH8!5lqc=dI~w(Uv+BDo@^j~ zjAU9n)Dunog*^9wtFKiLIW}?I~XYc5IzVmmTD5{!`q?sM1BT-fy|9>oHbl zMljZhxOtY-wWQ;$^4WFK0`|Ucyw6eu`~{sSp4V!UhYjxav-bf*OTy4)yPnK&Jx0Elt3`JrGF3>y#9|(Y z%cjk^D;GcLkzy`aru}0vaO`~BIzQdjUSZVtM{7oYrHPxLO*BC)-Rc3q%mM7 z_@I>nGK2@*zdZF55Pq<~#q93r*b@G(Ci;%_eLk6r==$i?RLwn$H&V&R&{+HVs}1u# zP(8$S2kZxjx?kj6c9<0PAys#rREHw>%GP?*m4fjp1nioZR-K5Zzg-@?WZv38GFQQ; z(Ab^SiJq&p-7}|>6=fw}Ls*V4DLI}d8WSVR&Iy3)LbLm3bn=ud8$J4C zGb1^~8yP+6?uhiAaH=^!AI^4i_G9WJq3i2g_Asxxf2F6^PCwk}WMBKjy^Q|m{e}M% zwVilgd!4Iq=O1SKyHoeF!BJuob%mRCd;*JNZ1n&GBU1L@F7!;f8dFbg@;-V`Ks`J( zu{#sqjeIo77apMi*1tyeqk|&M8Bq5JQph2PXWJqa%;AeTVD-|y@p*BJ&Hf!B@^A$p z_0(SIR8jp{eNtS`(R4q?dFo=VjyuL7MyugMt6CEqAVOl+NOCt4Uc1cd2i1Xob%yZ= z)BHf1PRT0D3Y*h;&|zT(l+T`?1Hvv6wL;1g0Xrwur)tIS)o^>uq+r6)nHjBLAX`!A zvP2rQJjAc>=tro&NX^%o0jQx@-}gXfsneejj)C#xG57g?ocr|dER}5LVE3|7dtvN7lSQ z?p}=svA>)s7x*El*3rZz(-z;{&bRORvdgC&^Yo$Q8R8ll4-vHO&M$OdNRp7WP40xS z6_wwdG5zo;#45JT-6zaD@aK@p8`y?U{2&)!+IA!RN{j*(*RPriHZuK{RsJW&dO^_U z{evYp)dzcFj_qI5%?2uM7x6c0HzJm*KMp^r1mUdpSV6Jmrg{N9ds%6%6_! zug=kV_@JA0TkqEiGw68?cm{Pttb1J$p~M6`4Fd-rQ*WKN10YLjgM`rVakFI$1V<{z zB}ytu_VvNlxfA8IU5nV$q2cNkO#Y7wDrh%*mbYfe*)b z(S~LK@TtXe-89LAVI6lvl&?WrUSXPg`CuK1L#6`ATS9O2vU)M7#`HX$$@>?%FRX-|vbv(lOUqj1#mV@u!( z4D95#o3S@#-)t^@V=J0?E_%5AugS@o!x&~D=j5I@^*&u%ZM%UR zZ+^D?^bE0u9hQ_gUmL5L5fRXA3ASed2F?3xI{IfBR@oIM_w24}60dhx&$B{h<$_h` zwx`k|Bb8Rf7L>e}7_%9rQM8M3f>6E%ob~!8GC8uR_Hd|uQ{-C_DkQi0DhTFyb5TN^ zu?=x?hsFMk*8Jm>5k)uL-ICXv*?13_G&q!K{cln`*4P&ndg}Kmn z8D%6#&e^2wx`W~zM~+MoNQZIg=^=RcW`imwL1th$uPOUj|A#ptr$=HxdeUCZ5=_I6&utF3_qR-!vcIH#pnS6n+^rWyvwIF^N-9|=Kpr8nyN z>Ti4Go%fhsCx?oRxGaLrKk2h7XxBYkJUOAv+MjEfA6R{Qh`+{p?zRvEa7f!#y351~ zSd1{C9H$7T9r3gnH1Ca#Ii5J#BIxP0u!L`X}9^ph* z|6o3F1gJ#si4Jb+*_DRm9{Tl@v(nvF&lvSn@^*CNs2I$+jd!DC+i$?*yRiUyRiV`w^>T3k$oBLBsL zEp6LIbB*qU10}E3O=t%xd2I$KyDCjW?Yb_POgtw~t~PjX=g;-qCy69oh#&Nh-GtD< z`Jordog()o3@C!3Ro91GeqJUwWQ~=MBbA8K@xvYs2aNW2$qu;gi^EDp-1qmpGkOSV zyJ-hc=YQU*J=uuZ^o-=eSTr9EBkYg7j@KWBV5dK|w-$M$qY|oTBUwqd+xR}`)aa^81q6Mk*?RzY|vFRMibeKS# zJ)zIU`H001)VZw?uHfTC5z(Zh4dz~?CVhK)^qxpFjT&rKXpzE!zpgv``WC`Hw{ zECDw+KB%F-q2Jxv!MAp69!q{VV@Y&25S8#so#T-zed%LlJ#5L&%c?(vrh#gJ2@d)4 zdU@<)V8PMu@(`_>lfeKd8D>Hr8WrS*T_K&L%3h!bw3LI~Y0??*^C+BJySKT9vA9ez zN({MAs}*gYbxY^=(@Px)m8U6=GG`J8H>McCzEdymDKV|t_dZl zLP*}^B<~htZ2%zbd94TC!2{9^5qGF8FE3*U;XlJNb929;O9lK!bh9H_8gX9lx>x#D zB~eFQYXm*u7g!}ucYYyyUM4dl#!-8Y%I01 zo$+)DPZ3c1rt)A?z?s&1p@Ctv?s#ERwbmCOBeL1(l^#sN*5&D*wY9lI3|zGBquB33 zv9gUd;%{XVv<*Gve4pu=llUe42=t`$oqX3$>E3aA$%^j>a-?0X=gXHf@3tg>_xvQy zM=VC^JEPbSfnCqdAmf-3^@Fu`XsCNfL)K9wA3y3iZ_k8X);ORqE;M>6YdPF^(5cV= zQ2M6uV2*h9{)YxkZs0bv2hr>ICr1aZ;`H?N z|Hi_%n&*}cr^XYAI5+8FTcW{Ax2#ZHBd@vR*{u(wWKM{06GRS67x-&cN%#+!kOtfX=h#*7>fbC+%G5I~-|^L-=*trBl7D|mf4E`z z_~$MxtO~4GP5Ts>)KPOzOhmk3V z&#BKWK3Go;yfGs9=ARhoQ>9 z_-?Bf#pzZu{~;~~$#s|F*yq1`;?Ipl0dI`%^Zq%9wSEr+!u-9A?7u|N=ig4m-#^`M z-TGH6`v2;}O(m?Wwo`hU5L&^*0eP`ijY|~5;AHEx4~bp#mvdNH-Jd@cgt6YlO|_^x z=^xB)g%gMMyhEi&#J{qN)dDL>u<7D^id{|6{FO*7w<4xjP4&^4l?Ef8Q&&F3IWKY7 z(%ZfY_oxWDB#ttQT&?BLG+q{oA&%%Z?DxN#S%YsJK3ygT(5e;e{LLa}zaM*o)kCCp zvNaNVPG09cFj?2jV$cB=rB<-&EU}k?9B(c0^IBZGsKq#Mj1>75^C9Fa>e=mUPj`X8 z!nNYw{w~AOL;|EB@Cuk@xu^$w)_gqCwl$E1f?!sh;Y@s8PG2TrHy*Tn# zq<5k&IM6Qfddc&gf{(>cDtdQMz$)iL0ih%naL2M_Qs$Q*+l-5n!v|<+N*w%Q2@ju* zWL3ALI_hTcR2Kl%F2&vX`Bq3?MC>NY^WMokarlYf&FXVA+_qPiGXp#lsAZxPpbG8U z#)}WhT8+maB`3z~M#df(Jw1(eyy|?C*#x77sT+XfAc}DZ3qrH(im~#R6;kK^Veias zepxKU=!QNl`Ob-lLoF!!jnvW2KGCo^l#!hqHGjN-r{ct(SiC##z%Q(p=px#at{XEv z^{1K{OBFTRI-yy%KnmQJu#;xE1jEV2>|+R|K zj_>PmNx=-Eh<%MCG&I)Hh+%d--w#ln@G|Ev43*&&OH{2T+&E}`D4-t2czo`7R0Ig! zPWuL%#4fKqy+H6yuO0tj4WK0*I*Qu6%#RgB`Gp4d>~O?zYW5yD?RH8cW;Bz3_-p%X zl8L?0Br)ja3?O~;9~MAraD3KX%7-fuO9JG8cV2=9ooOjEAvF%x*NqxF(7(d7nZAjM zO_HS92~Z_K(!FHS;(+sM%l_rZycHi4T?~H!JUK(JY0$v!0psRcnl#`;V?sC0Na&kG z>l@h2uafDJxavG@f$@ma#jY!j zZhgfO^UNrqA1{k=Lso&cU+BR4yV&%$PuTYCkT3)%qN`mO2WLbwWMR zdv5?%I@_nFV?Qx+Yt0JSy;%6RoDAyGbeC7Sl;Z?u|it(2=YjYNfsJ? zf&x2%Gq|^$RBoP>hv2>LdGEejrJ_Y`+Rgx&=AjRvd~5S!UE<~#+7W{5DR(M{>|OCC z1*lv>+|K;n)o|O)!72~TRan4CG8~SQEuHw{Aq4UTvwGoGkvTfDGg)W%=NR0v&(3s% zH(lnXS@ej0eS5&82H9)y#cmCth>@hi)2!8!96gJbpIa@;Llnp6-gipJ0}EM0X@};@lOkOPe;D1gJxxTaIU9H1-Mq`zW2o zmmP=Dpp`=N+*wEhbD9?;ynS5pU`GmA+pWc4r$H$rp&Sw3y;V5dwLxF|@hD#V^5!DO zX)`RFUH7@Nmeu0c*5e*j&Gpp}+cLW)Q8NQFMmu;3{I6(_ieW6QQ<=0j;W=X9i!Yd` z9CDM;r_@Mw$xeJnDB)=d^a--Ro1MksqcROR(EgDSRovvxkDx*u?aYb8fggWW)p;EVCapZ2dJ|Abj)Wlsv zg%)XdqtKskMX)_1E6vZFqq?sk=Y{C%2k4#84+};WF!W z7jY3JqcyQ0C_XF(ymw$ysWCP6)GB`l-L^lwZ1$o*e!Mf1NngQ^vzDIyvPrEj@VovG z+TE#{7SGYA@G9tiU7~jfmp`2j2^7&$A5@`;_p>Am0s~VgBccmOq5r%Lx2!31FXj9C zLOvS`SpIiZe_}_S`3zM*G}Nw2$QBk58qc36^(OI?VjDXo->n%j=cra zB3{}^8q9fZ3uB55w_oWOXk-%4Jqc$Cdl3eA8Ru5s+`>6o?@XRbqLh&rEUw>O)DxmE zvAM{m^QwClr&X^Ua-Ff)gS{Z1dCk-Dy23O$;K}2GyAwcrm7A%5eMzHYeEUz+mjJtO z`hw)(M&aeWE? zoN)q=ZBE;>rr)G|}{_S*IS5v>Rre#!g+n{eY5yd$Rfj*FZF2(x}z3)=&uX%kmV+3)?LKrBisJ_39 z=$J*A;D|*mmf>hRIXSOg7Yld^euMBjs9vi5=bs4heInmZj?g%G>3tG7(%kt;OPvCE zJwzBcP}?_C+VFpIrN=rSt=HxbeBy>eZDG5X|J+m;-M)ujg?PXM^pPjVmc3Z@BSPGq ztkx9n#J0`wToU~AezT{)-_o=&eenpYc-C&xT(-DqE}U$9v9{QY(44QqBo}^fF0`+j z9^ACIX07xv$EOZ);(PA_8Kn!ZOt-84EgV>z!ls`|LeLj0C4j#H5vCiW^Iu)p|AIT; z#5Ra^;-l15FGq0x9@8X|{i_Dkg2EWdX>Mub2q)ULl{0;5O(v-~_`*v?ewXv~PiM() zFO_w8xzSK2?+(AKGXBxy?wE|BGObpt3a=+jn3|U8%rpoE`}_PDn)^B=dB3>&)K!HZ zLrOM#x`92q^qlgLZ*E75Py8aM?oj+Go$2Hp+8)j|87GF2Y?nX+3O4<+-4w@U3zSF3 z8h*1w)dACNw<**Y8$nMq9)n+amV_z2y%!v8GunX-MBi!U-o!cFxpP}N=0)z^z7(AX ze8sEP`P~|Jc38!-V z_7PQs9@xR`D_$>%SA10%b5qLMGEi7OOiklqoUD&;5Y8@oFM6=sJg zHm)gGk}0dU(92Pc*N|niovr!ReRw3#{%Y0mL7DkyPfxlqnkXDU{#*SL6y4qL1}D}{ z$tjh!LNp1phjy!2azd78O(_RpB6JG=(qAy>5LQY6-JOT3NOD0;2hddr&M9#vH=SHHiYv#~sZCWI3*byqh>zIDXKX`ZJHspi( zNPqfr*li~YJXQmebbX?aZ$K_pmgAGMpmU-UvT*lVzRaXXhBB~ovH36}Af=>%yN}Fz z$KIGplNIp+!>q05$|O0>CF)5YHQ+el$D8NBTP%P955mn8`tFr;Z79uJ8tBjdiM2_* zA99I%bVzFQnB(NM_r0PISTRce-e_8u&!YXFM}Vp8EChDV&lnJ(?IPY?MYwLQrs=^u zoH^DvdVe66U()>XkMW06`eb&IK|nKz;3lV&GO+WT__l2uXYcDIh3+P%@BS=k1%1}< zQ4{Q9G1h9zZ;iAwqQ>qVZjufXr(ZrYldsR;9JX7H(V*tJ>tEgtYkqg9iCIl5H6d7b#e9c@G(3DI zX6R0?N9}~O!Hy<$#JnjYR}hD=~f!-~ep-fd@7 zb%niKCvmA79nl&|SFMvjdU`V-Q463PmBLkcKt|K46{d3xAPe51_;y9nyEB^twV+#X>$=lzR!+pJ;8L~)^0SWW6Ez5+;AmhVw; zqFAD&rsaZSnGhhy#yc)8NTpYYOeQz1Q_S`0WCL$%Z!~`F(@k4CKy-$k_S17Uk9SCU z2RA-A!Ebu7#QU-ru_o9p{Qaarph~OY&alK-l3qzw#ZRw?3J4_Cc@t@@WlGD;qQpKB z)i{&pu$P)hG%w*^W&eA42T&|Xa)=WleM6zV9 zFT***YMZw!nlKwEy-doGFd_H|$Xiik9BB`-$}J_jndc_(-Ij-3mj$SNA%`V?YU_@u zRE(~NY~TwN;vbR@^IwKF;L$T~H~Rs9M!Y0yH3GV3D=kX?hyKk( zOpC7+J$m~=2)6ZCCBb{8jkZ#}D=ejZk^!;;T8=LamM7wtGhW#J#0PqFP<*}@+PiR@ zD^S_gsgn)2%)3gCrRolG+hcxgkc&&F*lTOKijdM(q1O@kKk103a!Y>j_9j<^Sc(J0 zhUoe(ZO%arlbNp$(zCV**XxLU2fWfRFiR_@lc`*bKgkNzsj1?4`dM7-em z5|p@SnOHCQ6<26dHy@L~(RKA57SRCEegeynL=#tj_qX%=)kdvD);yc5l($MDXt?-P ziuM&d^Z5pYsOTglHtm~i>{J1JsmfRSi$68)pKG(EZyQSS))}zcg{SM$wd`2j-JYnK zx43QOZNzh{hKQbS)s6vD_1rr5Vbi+fDJ}=f!Y+SmK~>KRxv>2)bTjKeEMWNLc3f76 zJ7R{&Npz?_;{?)HuNMI98u?*U7>f-p6o|IiO-WER;4H|?e#VrnEkzHZzRcJ)OJI?! zWjiuia_=iR(9>UScW-QqH3J2l+}lp>cIULE;c=Uc?No?B`D*It>eW)TL4u7C+Rs`? zW9=;-S4+NEf2v+QI#z6+V$!;9^VrWR2)2*<$0n^y~%{DaM4A(d)hSg0`J z`-gK(y=0Gkm!-SZo~t|=1pCFh@W`zg!~W3nxi)hOerJ1i@?73DXJFn)E%3I0bvS89 z?^q+`Ga4NvEp28_C^q&SjDB5dhE>bhiP zH2F!hBIO}^YN}?a(x!fhnGtgQe4G%Xz!k#c1Z-Ca@7Uqx{^{ye%Sf0^Bh) zFm>k0;w$#IOMmvmEV#>cb50&`tRwbD0ok-`~L{I zv+;gvU@1y+SBZe8%*G$bi1}x@1Fq3nr;(fNtgBnWYTPSgYbnX(PJ_jt$?=Q+YT?vj zIZn4@zPb@(T4hqZ8NbHSEtR{MkP*XNo6zAzC3&>EA$MnsA(xZL<^AEj)E%Ua2A?q80cPW1_|6ln2gdwQ~ zR`#=-ijonx!^yV%7tBaiwFU`L^6^F|2MOc#`WwM7qv~nnS3I-+Tbdh<2DEHU6+JUa zH~1B%F;!;CBd{$q=-^(z9h;vIB4Y{#vOG~8r#$Rsn7QN zK~Y|qvouGjQj%=Ms>==!r8f`|R}(R?Jio_(eXSyKyg4)mlC}JGb?7JA&h`YLa^_<8 z(5p7>0q1Y=a21rL#eB+xwr+-)-ej}RHl2l@}C z+$E=v3ITnh zLNA=5^lbaig=xSx4h}=JGo#sNH&voJ`L)tTbNqcn-=PiymMb0Vl?Ln{_LT506TfN~ z;&{BXLmf=Cw_=tYFjeX3p{Pt=;i{Z~_E#A=JxmMOlUI?le|TvyPB-KKTGv`L49aaZ z-pyFYun7&(p}rv>cbv@GL&lZsmnA|Fj2HL)t@1X9S{>axHkfY``Z=Nxix<0ThkrU< z1RcCf`s%B9(fBbs?&--<0PRhqj!s^rOn|TQbtm|{iA+TqNA#@RB`W41FjW>J38Nd4 zsh)~jpDPW?aTQ}N*=vmP&aSxNi)I$AaGK38kc)B`ie=o%dNCaoD>RF2X%E_JNYxEb zL%0iq1q4GQ#J7$IwR%D~;@|gJ#cdxE(J=kJx z)|~`TN=F&)FuSJ`Q^uTDT25vzH#H2jDyBH?sBgOGq1;n(g31s3VB~QFng#hLR5y{7 zLp;)AKCLSrFC!Yzck7hsT=Vd$l}(j7T^91@Ib6=JgNd4sp{J5D@cj<#gLTYHaL7iU_cwjAv*rE?sbCvx}ur;Bd*LbQUh4W9Q4Wig#W zLIQ@8r{)5h37-ilV)r${eb=mQJe@NZn_}zB+e2g=ns-xVYb0Z&#$TQnBrS7d}W`Y}}5DS*YW) zl$pP%X$y2+-j0*<(9>G~UvG=&B;Mm7$a9k+D}5t`J7Q4pocLW5MXX-KM>s5hjlj7^ zwaZzBIOnR@_OY<6LP2}E^x^~?bN5pshf-g|JSh9a`0$!aoyK}3XHLHwTR0aI-Q?Ij zK0HK)e-kQS6xCz*xo+BesT@HRnKgFhbUyD})>Dl07;|DFlUlQvJ;#AII~DJLvK=?D z$mu+v5D+XyW?+$nJCzx9a^K`gtw}7}npn+RS$sUg6TsmRZK!X2cGdq_qTD?j*~f8M zB27i*EShr*F{@3hS`Cg)xv?%joY`fc5WgsiM?DfkUwEnJM#qnLFNeuS%bV_zuz?F7 zC(<@qfxa7;To7{C%)O!Yf+yZZ%B%X|U%HAj#JS!_NByY$Cw?NS;dc9GHpt|7va|uXAjG9&CVe8IS0}?r%`|yy+~)hxQ&XIo0j*pdO;&Pi28xE>qXr{lQY=MCI*(pWUbTDlKW~{Quy7Q}@Z64CaxbW9C zruBu!z zyF!}-%o<`LB14p_%(mH4I%M<0ccId}6DiGZXpSZKxm{U0b8lis?E1xKm8Ppl62SrV z>KyG=x)O3F!EYrJ6t{PE^}mg0t5D1Bz2_^mk}-jB+M6BEEK6OW4j3kwbfL}Ght^r+ z?%-D$jNRd44$x;zO1lOLh(iq_DUx#Lnyr_>^?mkyx;|yF$Xqa%qLmi&ymsh%_Q}JN zUrf}pRzG*HeV40c(908!3a3r!htoFd<9DgWSMFWT`J`wxNl2>YY`SLtspqSwi#_!3 zFMTSJezwrMx``ylkPp&UWAUC0otz}kan@38ywQ{=Uc}~P`D&1OxJb>#s=Bp)aCPI0 z%V#TNzL1ywV6A zE^yf`Pl{?@>*V#~#``GONGZT+J~`@I;QdY^Hts}liapjoEjuHk z)C}6ozX2M!wCG0DQB@$prvWq9;EDIOMh3iNQvBIXI~-*)wBKzx_0{zeuxb9ihOv{QxZMcrnVIr%nG^)4fcP7|{PpGysG_v;R`69pqD>3ePeQ~%|yk}_I zsDtm_HzeXH7TI^*+D2grvsqV3Rna>YC^mrW^gJ$TgI9f~CTsO84@c0TqY(*-yI`(< z4_k@`rYYvZC&%=X)%b<8dzMBC&e($ZGO?BS3?mJKl*1uB=mFe=+*PR>^_N!_I#mr; z$(s+nSumZ{6Hkeh_g;eB~)CJgD5 z=fbBD_LzfWW>YZ1RCcG#Ay$^2H}YxIGR-=QrzQ(NqT%y#v9esT?dRT$&Y4|+8?cTRguSH|N7+Nn=&86x!TQTJGM;srU9JsWdoPJs0hVD+? z2C0(NO|%{x{a94zK8X0tsk?4%$_&I?6gUBd7aq(j9O9Uucka4x3Q$$!zQ8`>d+!DSQOhTX#`3B@=sjTvbAJJP4w9&*C(Y1u?BM>fAl+JjF6iP#@2w;b zpH{4`;xRW-Yq~CWH*IZY_V%HDoSoU&mZ>DWjHAw_+0Liyrf|vSaRa~5k!Ic!a%DZ( z5qv~0gUxY2?6R6saQRoj5z^_5%^~v+(Pz?>m>&zd{P|8>4Uoo0Uvlsb-rx4hUcYQ3 z7f?Q^(;)Nwgw}SO@85=$rUv#mF$s9T9Q@b2!`BD`a@P>rYITn z`d3QFC`?DehA?DreKrJF~ef0+prn=8nAm#LefY zY;HnWN;j5zJEt0tcY_b!5)JbWa3f&zo=v}gGbAf{TTBV&Q@?@97WIwW>&Z;~OMB%a z{W+7mpzhs(_oOz;Lxat@iex6&WlO;!3BbOhd-{u@+w!H>ce)kkyx4Q`5i2Sv)768D z7d-nLA9^ef4wC(4*{98U>cz+JUN@_2ggh~nm4jl`gLC#18K=QxsY7OqeDcMc$3tB3 zTsC|I+jZY=F6%PXBxtD|_twiU^eM-G#qwsD-bpzBu(FuF$(P*F=pTA%Cd6_>>P$Xk z-sdDKu1pg&=a|3|IrFpk4zTB&<%{x$6;WC?D76Rw*G2#RV{34o)h3_pdRivG0oO zuPhD{>ZL-AG4Iuvvb(Fz(}tHnkbgb@04;@YY*H>5zn4Vq=xISKC)%CVuaTCmw%uVz zDeO_WfaZ(CQS;#zFNzxQa;RudL+Yh6o@K>GDSsW%Ke`++?=7t=(u>434SxV)r}@GN z>aUx&^fZVS1^$Sxg_bC|IGyeZPf!IP+B^FWa!}6K#n8^|dfuP{VRf-{D|l5M_XpCZ z8`7Y=&6ikd@ilazlOH!@p*fM%e`PUeF|v~d1I)jK3b1_y0QIbyDC%|XG4$jpb8_8{>zQrku0U>0Yy)p}omAGc0~%j#GN8n*9*?iB zZo06!FLtwSIEX<84J(vRw*I4dn5f3U{`%p-B1~jrjfkl?tSVApKB^_?TLh#k92vMK zj?At^zuqCjX&kXgFX3bcHi*qfHap!mfDf~%4z`!P9{j=F z*_^-@W{-qHAq)nUQj^3N1ydua>cc)}WHldMP%0)6Ga>1IBQv1UDPr??anO6}H#`Ys0 znO!v+3LNNUE&&;SL>6nprc%mSHZPWq1OkZblzCyn-<`hoF_jme$?PwLxZ1TbdAS8y~faug-~M260C+!W-cmM zE2iR|-rG!_FB;g7*n71d+Nq0qsKO|*IR8&%k@W`^XaHuAFANLeI z?r7HHC>)L^(TKgSL65kafAs^;8_jZUSo_b^aB?p+eq;9}fv1SN2_Y{eta;0lxu;gH zkAe1*DM+MI7|2yAQvyK879I^T>3Qi_a1vUa&Y~0D0rA27Es&LD1rD+~`dQ@WRP62f zl41>G@#!n0@Qp>dt z^ZV_&i8R9`yARZNuqXE@@a^P{cxBIdIAK>rgSxwVh#NJ>abSx z8X6my6P0_QZem<6%;Ye7AVwclb0iCXLwn*_+b2z(V37T6Hdb%E|Er+QTRjY&QXY#% zn!Y04%*(s^$P@VY*RYqv^Tp!%OtjW`Dx2KeoHxOp;7**Vl1~vnSrWB+c6iLoGF1$T z{B_&VkrFh}J~dxzZar;d11gBg7$0lSxiQwvt8m#a!?M)hrqnV*%Q9sDta5xG2!oqT zlcqo!SpULhnh~ei;4rv0*(Q@3DD5PgRv1~%drS545Wzp?HeU-F8X8hNgi5BM^&zwm zNlVemk@x`#n9kmB8cV2OGp_`UAxo(zV&xs|b*X$xyaS113_V4M`H4)y5J6qB{-AwQ zJTuqmskXTwVWs@f5z7p)0=yrwcYn3&MbcT=OI4yb)(z zNOT}uoRDhWilhbK_qXpt=IW0=?CGn6gbMm0g0iX!b}}`*dWP!*QwQvkiC=p9$lK+r z55sSg21$Y?KK?FF90=i%VpHVJ(@<}5o&>6EfMXKwKP!F>I#q#_LA|rVXqg;uh560M zEBPV-z8d4It zN$S8f>p56lvk~haB)@}W$Zdbp*bjDyiUnacWKM4}?1Armdp~_ZmFt?KI6=Y_ zcAy7^2d?iKujy3VbPPyUu1`MSn0tmM&_X+#L!`JCLk)?Ie-$%+b;{T^Kj8xnxN^XP z{=PU{84z8P#O_J<$LEL`g9tQWk8zg?46rfYC-9nTsP_wyu-tRSa*$aC;X3+o&X;}C zy&g7$-%2MklC2HZ>n53hkq6*}?GheP{ozyboQi$e9C0yJH#r{zj zJ&zIO8x6z6NogU9owQ(iZaqA4jWNMcVA($R3I(&TC{Tm3s@Cz@-o{c{;|ZN|O<@Du zWRAHiL(>WSQ74J!#h`GJnrVFkqzcj$rx-~66GHz?BOXOfx0jdbcR{kJs*QL9XN z1?j#|iq8`D;QYPHV`HUcm3%iz)@ZGrXG1#pGkV;~l5x?T$95%ByB`>})%9?z4*xh} zA(jQ&v3;wd)A53?goAJO2}WH-q;qAQ`s__U@!|f*CvgEjK~#~LkeK2S4(lj|gCTth zY0^kskt%_vhDd>9Frg9&E7O0naZ13iOu~*@Ukql^b#tpcWe`Ep)-*^93~v^6hKMLu zq)BR4lCWkOedEH~jMYr1v8Pu{A=#+}mzB)jm(RfK{n=zB{Gu}*$RJ}UzrLYHghL6Y zRmD2@(I67Vu;B|1DLWiJ5pP*|6G3dG?C_!voHMD|*Mh&^FU-aN=#?;fN4Y$a9FBuK zMAtSrz%6KayB4j5v;MxXnP=-tOG~xAPWXO^=Y8CqQ%xB9xwXN|K@{N!IV+sL_M7s4mfaQOANGQBP!o9wZ|_c_8KSUSgJoePxLKR8U1n{3zz#Rj1e>FM356Kc6pa75vfe{r_n zd5ax-<{n7N)Kdjl?RSWjXh0?7e_{cr&6CUYicqY8A`O`ANymhy(rDB8xD@&d|9p*v z0p_WFq8eSE1g)ZIH(1wvBKB}n_HFTHI@z;s|ODGc~LWAY?b zaU7N0tR!_CA{;e%E?|?zFEq*nKiuY$tgQ3dW@sc|l6k9&DB-#y?xP7}#Wd*0M57Y3 zmTi6IGW>fS4WFwzwiT}N?Laj>z90)#tM5HFfZ8a84dwBH|E3{1h-eN?XPse*&4ms0 zc2;I<&2V8f*nd{0<%y$9!K)1`-LL4Di>Vq>!)!=R%!BnEmXcRs#42nmxPZGUGC!dT zoiMm*tCppNA~1-t&zrzuy+q2()Ac%)ObpwAx-)*%3n&mY+nIb zUnU?#<~exK#-^Uogf$E zg$I61MxJBz1-P5*G=(lp&gntoQb=&1ORyo$#MB(?rl23Vxxn_#nZJ36k3~L`ExK|c z(PF&gm*XWyE;Uq2N8@ce!Cq|I3nZ()u*8NyjUCM!+#U4h*=X@*tzD^{!TtV77%lyT z)Pn=gP&sjci_Eq(ozhjlXO6>#Erzx}Mt|QCxaYYkxdj@e+;hUSE2Jq~=Qx7iMuEOCPt0nbMAnJl z-uxac!v~z7nPPGB_5n%)#7=a1aB4C1;2@jRV4B7T8xa+?@ZIj09*htSopV^+ z?v#<*ZR#{jmGwg~t2db$Z8Q3a0P6KQU{UdP9qg1Ghi4D4YuYsZ8Xca9QD9Di&McAZ zh>6-0>lXt}w`i*34Y+?q+MYBRK3K1c? zj$AZIQWI>rL*sM-;qi*W;()-et4q>!35O9omYS-uoYn^+hXy{%k%|Z*_Zx8$Ua*)~ zG#B#_8&hrt9m4w*|M3^W6Jqn#w6(=Q#?&)28{>T`{rRoCbKlxtIuM9O1fzw4(uZ`3 zN%dRia>USEhKjea>*(EEm{xVLv8}%i*w=(&QRj4^@AS07SDakZ(dwvG{#2tW371gG2yD~ND-Mb9QzkvrZgwxy|_<5Rn!kA|k^uo}_Ae`A9t738XNoP(JFGps}*f6ix{ zdq)?#A$gl^BJ3Y?$~IQd!zs~fnxJf7z5D+9xvfRq><5h*N#nLi&eCrntQ~LIY(Y$w z0X7T#+_*q7uH3Hi(myx1M4b5-OYP6p4_NhnO}^2a=#J4SkK%Hr;T0zzQ;AS3?>?u-H<^8-@C#N~BmaUf3{c!q6B$7dj3%nh&m)XnNw>2^K?_O66trBdiUye`*~_!iACnxB^GoQ=<`cT~XT^h_ z(=GIEl{%x_>JqE8vrg?*FL00NXavbT+Oa1_YDk~*zpZ?ZC1fN>v+L_8ksDS`t%Jyb z2H>?#(iUvr)`zX-yyAy~uKize#8`Z=Rb`h*cBbqymlAz|R5R`Ai|>K`&|-cn7;^}2 zqf4W?zx98mlfv3cw1~cAVG-n*vA{3hI%bSQh`l{fZnfB1f==aB5ff}&7-8jB?`sk> zncVMInnQWEp}hfWH5ii}olp2Ngee+EN20G*9O`(d`s~tQ0w zrno4-F9$xR2z~HGfF!3{u!$2jqmY^;Q&4}7deEYIPDybqpZ!}^0oEX?gFL{jFZ>JrmiN+>9o@d*6pdLl-YlF{lFeJxW`q3snX^Nk}m7lz~Y zdpYTMIIc>&Q_Ek?1URF9rbtTIwX8L+jTh$HYgDwQ?jvu%)tovhXl`(YWm7s!85i3t z(e~6DcpJ}}u-*711=D2bhW(&91jEi{!-0Nm2^HcybcxDWEK(NS;<_nFB{Nx1uP2Xc z_!>omJNXkYzKMv|J$l65K5dm4%&ErYlF@{6>&1izTbTOA1`|H06m^oS67DFkB)B17 ze|?2ZTC>n8IgO|PlSWU#1H4!qyuW5}-b`@stNOSphSrw4Ce{ANo?;hi+UuY4SOmw4 zxZ4LZNqH*tJOdIGQbqf3zLnk!+PCUYT?LC5h_aKtlW!8efghw1+`@+P3XO4aI5RnO zWRT*^Xtalv*q!;HxR_$@aVS>&5Vl6AT>t*I;sn8}Px5c|$Z6SAJ>kS-tBP(^68@14!Gq z#7GUi4JaB_%`vuvm9o*8{o)w}0uw?6V^L#hP8sQ1kW(z}q`iaYk~$>OH!aMv*SLJ% zYMLbm{^Cvp8Gd zU;1KWl>SQ_p_wxC?-Nk5`ZPc)U&mBBTJ;Y~t?!EtwU)}Ts|KY|^Jl*}JDaLwZ#;;b zu=wN3($dRfxqqgzBSRM`OQTq0NQ;~S460wI(|-%EB^Fm-$U4TW)y9dJBCq7*X=hq4 zu;zA7fea{lEPkvw(@y=e-Q}+G1t^%~d(!F$k}F5!XP3)3K-^RVklj6L*z&VJO1p`d z;V~9rM9d956&z$23!SOq#%n_>vwg2xp2K*}!8}!EZ!=#P_nqYF2~2%YeNo&zufpmD ze9nq}C$iBf{J)k)>07|3#y_Duo{s+sc6-$L{QFqnbvv=p-@q$Ox>9|5So!m;l;L zPVIzGJc&+DYNMEtvGzTriXvFacltBPa?yW6qf(EdHEU?7^<8vaWF*|Ffj~f)iF*4B ztYJw=7M#vs0rOjjC%qnUcqz-*JY&%RJw{8WH_b`54c#N>d{3#JsRj5>3<#!>&yJb% z#r9kn7GzKEA=bMp>ifPWm3F%b4yry@y5xG!b^PQSlF+{&2BQF?Lh`CYvVog_3Y_RL zH;xXIaH*mUh!e@^(O2MoUPUax)wTd{sr^(jAorxCMvjR7C(H#9Ab-SvfSl%4AvcT% zC20?b1jg&b^n~B~5?B8Q5(Y*wV-8ShF7(wMrEd4MApbRtvKvICy}=zSy+21I`*Nyu zDrvg_WzE0g)>Jt+^g=3`|L@LffJ)==shWh<_h*ER4|)TOiw=c`fnyx0r6!g(FtX(A zrb-TWRyYV=|7v)CQ9ohCdC2#$p5@PgY3C(KmH%=Qwy87n(Nh0=YyQuQ^#6~_{@cC& zj{mkdfRISS{>WxL9?xR*d8Ip~H!O{tf~HZ~DsCI$<*z^Qk{$WT!e(SXeXtX?dZ*?H zaIHO#S6_{}B4Dwsu$t@WndzBKJ~$kJfSwbmJYIyO|4tg)zVw!K;JTb%3kXBLw=v=y zT}pT7cl~jJG=}t_zFWMe+6$4J9)}yt#Od(Rk8^z0ae;XM!5EyY*b;Yk`0zi^$+{PC z4cdOT@NekORiem1A^1FGvYc~#;Dzrle&)x+xenoSo;ljT9;T0S&w+E}^qjQ{9Dq`;g>NYl*d9cQ27BP^jKe zvxf=XPM}K6t4i#oCp_|Z%v$u(XNj9S7MF|QZ)E)+4~CjaDx+)QiRk*YLXqk+>18f& zOqz+)ECz!U8#skke5MJ_TtOl zkYRbhQnyz{@7l4=3Om=cguF)v$sg6SWTveBatN%%e}A9#wb-S_TU*2EG;%w*mc<)v zSJ1rIwU&GFsUcWn6f(76_ra{QD%)?}g`J8 zNq5V84>c?Ez_@B71(ss(*h8nWZFb{M_N4LD@wlBmn?ug{@WSz2^F0Gl)!^(Ghwl*Z zi|oW`wI+`*0HI4J5Mq2h_n%mRt4f4K1gS21YnC zG?U2l&v)Bc=N_2lT@addGofHY*laIeIqbc=oJHS7sV`@SFmO?TAbPIW4x>~=_!W}G zEJARSM-2;~Og8t?2QbF{OO4HbFMM7QA~zOO(xWZ$|KT&}9u?1IoW2dz#({!vt}gET zd(sn(&D7bunH|n+#)~G#R&X9+p77EkvtDe)Z8t%ALVkkSwke&jJN;^zf56A*HTE*J zS5d6DCBnoHXr!Eq?FKlI8C|HPjZZWkucE=b+%u zCyiB$&FE=dHvKxA!F+iD0j>^mn0vZ&xf9Usk#>D)F<;F%;q&NTLo6^Q{9>@(J8Pj> zGBG^QF^YK8TvwTG52f0u*`E32%Ah0n@G`~Kkn(_9r^iRTbUv}I1+ROkJrr65WM|Oj?&Ix0p1=e6=;^Q97X=q^wB+Zo^G` zv%alde8hQnHT3mxn)Ln%K#txTb1rGg1D{=gU)jNbXmPfYnT?&i%bO-PeBDrgIiKW^ z0#O`g*b7M5vnUMKi9!G<#0&W>o43~dl0&H$Z!{+L`p%9K4^oxj=HK6{wjItZTba)^ zKjM1iFZ#54qrHA>*<-Hha-=i@*Ewfn3IZqf6suJ?-NuhBrtqj=q;RP_opz-yTktyG z1}Xm9m$`NMBvLwFj0+5>z z7b+16Nrid$$R=(K>-LkP;91);s^JJ?6b$SW5lY>DD`rHOmJ-hSPPsmn=C+WzUip3a zSVwv88y(_1`^&3-?IY#ok^Z}pA{OOwaGhtx>y%{e)E!t`-ZE`k5zA+VN>$3dn zBO%~X;4Ybo`Koje^(1g`gt-Q}^J3En@*<}sXJNd$tVF|F;H@wnfw=J_-l5C1O&Z9( z^#2GKV5>($4sC48iGb6vJ=Jc)^IjqbffAWORA!Q&)f8X<0y5%opJN7qeg^42I*7Sv zNXhJUB6wHM()tJRv=wo)k_3agdCEuoCI!}QMU^-fS4C7YYMX>C?b}emBUIaT43@My zSo_^dS;BwHH}&v$b_E;@yy*g{5YZ|TXXb9_nZf1gLu zlhX3iH&;rLJ{4#FlZ?qB^!F0=%40I_rF#S67ZeF>7B&KzR16z>y6hb5XXTA#L)Y1H zGbE0u)#ih-1`CJ&Ooh~tmD#DU8lClo?VQrG&|l{L!AgwGLNP9H z88H(M>GGx%(5_;V>enfk*^$&W{b#&0>H5FO zG0wsOHO5_-4`}}4?T1qfM~CNyTQaB2nVtx35G`JIMH=J9LB7%6rQ(l-GWd(FSC?T) zGPU>|wr=!MJu;_{T(?KG0eN8M3oz!wHK&C|B%f~30?&1uv!M&+Ajv>Onaw)%2hn&D@a2aC2C%tJna)ItvnY1X-Pg$SgpuqV4<5{KGoe@^M+Pk2_u>UQcfr0t2!S_Fau+HZ~ zFaax-K$bJtQ?r@izo$(ahT-%5W;N$ex2yIvqDSHRPfOn2Dp-FZ;U3|a;v%;J7pP+| z{%fcYD<6 za-qnHw>e|ifhP!ZSQo< z1N4QdDDPHTtp8ZQF#m^ium8Q{U^QfY#pc!{qdWB1SAl7MT|)}9$!a<031+x4>_%ct z!sR4Bv4xc*ZbbS}`_QDVD|*i|>bXF#g|saKti8ao%lJah{>Si5byka1|GDQ6%f>S; z@sF@)Q`zD&UbYsh!oPzM?7IX##M_O^!M%A6H zmtq8xE*~NbC>T!1eJ?cTcqDn_m-;&kUJ=s14jB26?A&+Qq^CHvm>hfC7!Ut$`!|xs zT^+~%w`PMu4R;X`)NnevfblkB?#AN9YpIjnxke5`nS-s|o>GY04r){DOj9Q>-e`fo zZX+-I!!7jE-S8a-Nf{!Cou9?&3COKZXuJ_YduxzSYpx>op*8Dfw9vRS^0JicEFcq2 zE)>}`S1imD2XeYGynvE6@?jx@V}tTHB0ibLqn<6@zdUt7Arnk1X-n+Y7Ga(vY<`*K z>_?3rLIfB^}aO(-LELqO9IXxsR1V_|Bc7~Edzaf2` z8PS##%{%3)dlX&5+M3s2VF>9%PbH0a|`DkNs z_@isN@$PUloo4G7O$?-Q-G@5*q)+D<*e!S0oHwBl^-y&ZN3rB z!11>~AZYjQ_rBYk8F4@J)^apoI_l~5E~TZj&~n@#Z*fW5n0~?kmHf~AJ*v!(q|R*a6oZ!vCo0hUZ``!e{| z0%vp5|8iy3a*qx#i{3+e_j=I_gLFn~ae0VtG$*P4cG$MmoyLhhl=dCppdL_D(uK;@ zv$rvP8Bx^YrH7~LL2~%4KSZjH++lY#j6_qP>vvlkJ)SFnDCy>ft1`j%=aZ4;rpIWG z#k-=*VYGYzo{9gbcG64vYxlFGD3LHUhyBYpXIXK6|zLlRk9B{7(fJVKgZ zwU>rq_dA$DQ!;6kJ>-Kkj_<2UF6GDZ2WK|Xd5B(vlKQpK`P>8tCYm+k;2!AM%n_2< zEIZ^$oY3d0voeg9&OaSYq3P>_!BaC)u8up}K4i2({sms|$qclJVw6_hhUQ22E5^LE zI%0ONTtVybh0+A;pjhaRGVpSa@N#sf3_(zVM`heb!2z( zRhS%V=GSr1ee^pDL7JS_Nj3blDvs(-oqWjlOYz(gEgcoD-(OrZGCmtu(+Y*C;XB&z zjP_WrdRYvmX^L}8jLVN~ML1Zt@%5JqIqsF7wlp5SrSBr&{d98Ro5#X?uuvwHx4B!S z_JOJD8&8(AZh-b`q|BAnK(o-##?TKE)HU&E2@l@?fc6H)i%vgEJ%uA47coYKZwZOZP(z*+{nzi3Oy z=N&nRyb!TTbFfNayX_==?aVHLS7SSpdV2Xv+BbIykEq#BDPhLlYCY`eg>oRP2L9E~ zyrnu*pBf15iJ37CxN4To%Huy>XOA>QNf{t`wNZ_ z#;)|s;Sg{6`zDrQYEy@zHl&mx&+%L>*%jqh^M5qc`^kSePPsKK7PCI7+u~#>#5mA( z6~7|K++A@W&FkPEz1kL@$%+>GpIE>%9z6>Po~_`YMR}V!BVT@M`NiEhKsEjI48VMj zaUFVAUR72%j8k?}Ao}-37{%4|SHI{r^-`j$G1X#fdG;i+76TMwh?CfHAd>%1)M}}P zxdPf^HDQzx{X32IIWg$(>i_?sS{o>k`FncHe~g{~F|hpq(;HuB4+IJix5PdL zRS7Bg0-vF6RfkX;rj1jbBH`)UI|K5k8ea_{4NCG9zmAA2nY8b?t3AfjZzU>~36?`U zg$ll^uIcj3)EIBXRaE{vIxq^d=&yhMkxP_k6zqXWfeI`|0xeuMizDAWtyrf%{UNmW6&G(^k)#8R0HwViQqe(Bj7> zxX#`irOsGeMO>g3T3AQ>NMMe%h}D-{`jqVTRvHZYX`aetNh8Mx4~YNPR7V_%Kax3s zhXDn3Y<^{vos1BuVx5Nn64v=dgb zVV{; zH_CB?Z_=0Gu8)J{o~i#ci52p8ai6DX97HRWZOf{*GqKl^+%A!57x?8GHq@}R?W&g( zI<@$<5H@sual`t03L2uqv;)%aKbjD&NJ&7c5%%E)NnQvIS~sPdKi&S$#(b5jPcJ#y z%lb0Q(p;4Bl`wYtOc~?%N)8C#>3aFPE>5V#=Bj4Hf@i9se5yHJ1{1GOp zTIt)rY$D}D3PDNWB)1aFXaBt!9ZcO8>*sTAzQ%e2W}$Mx>wL#RH!w^$?mnAgPpGG} zUHM(L8&D}5t)S+{0Igw9N?@BPRa0@%GC%xQUxBKJchTe-Aak`3Dfcb+lr4`cStm}V zk^f6F@nhA$a~S#GG=Y2)pQcoOeyZ;Pj!CS4{u#{ww+rF-&xNX|n?uVhZDaLtWk9FX zY&zHD)$XX??o0hkd~T=Df35~5J+ufw6!q2XN0Bu*Iljf?bvjzThL?+DHJVLVIo+gH z`-si*8y#Z-2t7X?vT=$V$VN}@;oI4vg|BVo1!f4Ew{Mk zCN}Sd_oNm+cF?+nKz&KGm!ryV)vYC&Uu%!$Y|P6%b8ttl?fT|fO6fACi?C?L{t~VnI*y2E z)%E9nt(@*VdwGQ9Xh>g~F}g^6(fzt79Ag_TUT88m;D!FV!-ee^RmyndZD39**%EAX za=pWRMl=^nzMCWtl{ed336UD>IS{V%>h;sYhyEYu-Bb84Zdx}cFMYeo#T1bn&QRg5 z&i9w{u|82M*VPFDI?6IX zlc1`B>+)R;262#II%IE2-#MKHE{ z+HawoDAiBw$A9u5$ze6vt7q8DV($R&b>)xOstPP%EN9L z4xiN}v3LyaUvrff)-*&r#CkmIGP_r4pmD#8hZX|Ve%a1HfcAV04%lqT@bR8We|O^p zQWkQOQ!tw`9s-LbbhbT>2P%bN%pH|*Rk!=eyLX0|lV@-UJV0&H#q4;J#_fwszc^D; z7U)P%OljeHS3vWT*~f0q=XhOuSl@WFM8_Z?z&XM0I@slVv@3yc&@#BmN}e>qS*1cK z0gk+?OvJ%J(;zixtn6_v%@DB~z!9OKU^Cap`i0uM5^DyyHrff$z1rrL&gDOxZ^8NKjck!O2)qzs=`k=?SJ7 zTv1dr)Os6&Soau)!tULIZKob;K%Zz`;+=xo`1&q&e%F;MR9!pUoP;>r1g_wnCWjb0 z3EKmdYj0XHO^l`*l(wuY&+SLD#Hcvgfu?2cxmwFR<1@A7UImwtQRj3tE&K30=-MC& zoadoTcwZs|!nd46{6Vu1AP>k&bH>_2c|%qT*vXN!_o5dks?VuslT&73s3j~vfrgpm z;czw^49nd)oI0@uqHaq;Z-N1f1e`%Xx6O$LKLFzCNrsodfVF38ZJm&8+I+Ht%N4u4 zOtcTtK9nrmZ9+*fY9$zB%8>nN#5=6o_B zUysza|BJo1h>9!Rxz3U>_&8r)rjJHdk#B!S@W?(S}d1$XyCgS%T{ zcaxkm#(m>|jn{aM+ql((+O-S5{mEK$%{3>V#f<1?LRe)GWShsc@|&Rh+bH3Owt=`( z*JUSX>0velvm-Wl%mV(0=BP9X>(Rps^E{l82>Ku>?CocyNsQE$B;;xbC)}2Kj3k$|=CLkQ#s?JI|1?OwNkkB4aP87iAdi}x9v{y5 zLB4Fq%O?&rRSfvpXG>J6ZbWyId=oHVQ|;2<@!IOqp_b0$i|ikeSx#zWIDo_tyaF&mwT`U9l9fZ zpGg-xdbVi;UX4v4((koB$@O|~vsQrT;PSww%yQ{l#4^TVn{$g(xycphCHVf#GXv#K z!)aQ9&bTTX`uy+aOVs5@o5OUBR@yD*Mko%Au*RR(xK3MC^ov_&%Z)NH)(2%A#R@k+ zZ7wcZQM(dn$4asUV{=pN*I9>WoMm{!582C#x=|n)~_82ic_keG1IRD+({Z(ca|s zl>4O3=i+txLEo~MqT`{WsOCzV8WDwt(-Q#?v{JXX_^zMle4AI}Isf>Gb*jdb)8@T9 zY%@rJEe^Y&B-}Y$DtXO6X4_Am&fx&O7fd(_kx$BQW#QC&4e=8yZ40r*H;4uPuM+CY zxY@{c?bX2GL!Lw%V?3h<(?}dp)MjZg<{o79FI?96=EU!az6p(4YNt7B;p zjFCKRPD6JFcGfsF+6w9^IbUXJ#(MNhIxFP{xRyH+{kS#v1eXB8b$mK)h}aZlVF*iA z0ug3^X0&^RTx+>4=4BO>=WQ7#sv8QA+RL0d8~K8ch%SB`m{3sk7`KMK#jV=3+Pvlx zO|j}}n4teN_1v zw;^*2?f~WX6a$;Lc9bbvRQpizD!`T8Vp(yo%rJJt%PaZ(D2yU4;KxJ9B5zW|3&fF7 z0;1dtPw-~ftALTf(+f0X`-bM&heL6?ba?!>=bW0QKV2~4R;Z>XF}D?p1f(NXVZO1m zE0s}1rB!SqEe1=rr_cClyplkA8?BtGA4q9ELs~ZpZOBUEj5!Rq2#3BYDd^lK#svs# zML77Kx_eYtc0S^5-nrfNzP{X=VVatmSU-3x)j_H>d!c8w4EweDMbBz0biQ@GwHkiI zFW&cPW?so|KY|jvwM{=H=jq=halTQu3VJk{0mQH(f$}5AtkVf0n@3$)^LGd|HU=l@ zcktuE&S+rwE46scYV8H1rmzhz<(30WYe_l_H8*N z9x|E1$3DVaC8RRXYw!1ijp6emu%pAwoKqiUKHogLFEI6GE2I^yU5^DmU)J58PI!m` z@uW8&rT7gJnJxVe8OTkP!&J4(EPm7HiJi zVRIcSL0%S6mJJ_R#D%DNld_9&(-qOd#Z!cn*^e8Xww3WJdt(;)UMeWW+-Y&7a-+$q zoI~kU>e}R7&&4fx?B&~p_i=Y)WqN6z_w8~?uN1=UU8KECy;R3`rogZmVpajuba z@e3Y`(U*l&DEl2D9<|#-oGeU_y+^BTetgc%b0df4yN*Slr2TIy(6gC?Y$TIyq|6kvMFIkLINn2^UlT1hJ}UAbllN& zrW~@bRdFz>L#XQdfC(|(x?FaCxFK>FP^@r#EK73$hTiXMA9kOA}*wj7jnW?UT9j^0_?J zIH2$;oc-40RL!Kqjet0vG7W0fIVRuf_zz5tF3YiUS-Cu3*FA@-F=^dQ991lb92$2| z*u8ZjVpz6$WxZrO0Atjv($ka{w3WU+Xp=oG78oL8$?}+qeS>`G(&W=nHSgBRS?g5` zHXA5X*P7XvF>1jtzg+H1xxTbBsIfYpmN`kN){@rQf!5h=jMqRP=Wl)$<^LLvT=<|n zn7T59>NSg_-=pSHmF}b{V?XDNK3Bg3A>+KSDOM}`gThe->?Zj~=){jqEHFqRVGt6W zyhfQTAP(9oI7@RXN^JNT*4Ei~%3-v(el3k%HTJG^SkX=1Gwtj4Q9yPK?i|dP*DkSE z>BG;&!*qXpK?imtbOjo|i+inpc@5N(;c!a>2;EW#;IZ7PJYgQF3ALjaZ-GU5hpSd ztrA4fv2KM=#uzmXFnU_bE*4(+yq~j`4IJi$2wWI!V}!d+_xrevKUI9vHv&+gm}wH& zvG5U6wpDxz1L%v2(Cag%TSjI|KR|3`C`pjfjy~?Is{8ygtSZ53p_kgH!9+wlvE}Y) zpz_bA2?-!bg7SH6Bfea2uHyILG#UOwWcXS5G~@nzoP~fu?u(BuAcvMm`&AnW1r`=| zT<86^R`5!X@XP>r!9w{vWVYg~g!Ngsjc|R!I9W6(6N6iKvaGn&vj~Yt-IIW$OdrXb zYy%2vhQQ1W%8_vz#}Kat<=Q2K)by&Xq$=w@({V$u2hzN?{OlTcTx+wDEdiPyJaEfb zcdVgy6#8Ezd`Sc!dIf60o^9*?$&r4*v`oDmVXO5T{JhJ=l8|kX`5~?L1GzZ0;EA{? zg>!O5L2c@*losYclTHMyNlq@#2ej*bjGg-nB~|Ik3tC|}LgICwQW+^c0ad8!hBPaB zS4GpfM`EdE_|&%Uxvu@;7TYxky6HhlT_D9N`}80A*Uu`47HvE;AkvWMo=AcXZfrlu|9D;!_4lu%0blxYYObRAFmNTwU;5=GA(~ zHt;>NEN3}m9OqB*Eq~bL;}KGGGJYM%yR%JI)0f|};Ff*cFy#VciPASNx!a?x+#tEp zX#vhATSAV{2h^)Ptq&82a?X4B(7H&4tk#Py>faQgM*?wAg6uVC1FsNx4w~PIXM4`4 ztN9?p9?ymgrYgbuf`(b-y561RPcz`q(ep~Tc@1zsqpQ^CdzI-ffB;Yya1%V#dK>T= zR(d7IRHpBb@D!$)sjDdwtcZFgvz{m{n(`ghr8Jtf87M)7>nr-RX6l^>M;0Z8l&XA4 zPoE1fIZzA|zbWo&u{{{TiVy2=qEtGR=g}(LBA_dD7hMj12zo_GX%a_?#TY?)WMB6H z1C+W3TXi#NE1k7l4SPdOr*n1nX?sZJC<6_}ONsR((m-iSysT!UyCSd{_J}3gO5*ZL zA*ClDy2+PYJNNNDkEsE|=YU2pM|QW#kI?va+39XsDgM6UzHr=)^&(5W?E@vwru&ie zs7ID4EoO`?Q`YVvaMr$TKgM9NTTrfpN|;jShE>>GDBq#-zI^nxAysNo^|p6RZs6`q z8R|V*O0~hdtjku{WcT&^(4AK9M_kACUd-H2?6;b~2Y2hTR1+T9E?%nnJ9@P$Na1Qt zq7FV3jgV1=g}j=d6t^p;x0_nAoy|VSL~ZB%;)EIS&f5nXCe7n%gi^Pe^5h)6!jaH$ z>5`|)Ulj@u7=Ep6r2!8D&{{7bA9YGTBp;P`d1}%BXpL2QmMtgVzF^*Da(PFxA;<{)=z|-;+P^M0{X^ELX8wmr*0yt}!ZEdc_ z1YUaY(2inf{6Ept!SBii?LQ$yNnEzHW^?n!GTw+ze24(O{V%qQ8iaZO4}_h3u0jll z&5050|AemsFAF06|9BYw&nnt3deV2-Ri#CcgU=I_oQ=+@Q_4QA!efcM;!-pBx84mj zT=F{jhG_=B%&!759k4NEJc(lq-b{FhD4D1EAmNfkoUvaOjqTZe=uyg zF8@}6#rKinPL$t1o85?3(4%Q|CzdaZq?BY!R&V6fvpR`-^6WJ%%k z%>Fx13lfS}Pn=&bd;IvyEu(GIx}h)N0*_WCgP6q!7DQH@xWX$InaoD{E+UC6-L*tDJh0Rb6 zU*aQA=qq~q8!r7wN06qK9>{A133H1o9InBCCh$?Ua*tnnLuM0`sjhk+ta&w#$-@4Y z#wf|ng#{PkMKb!Fsw75!Ktue-nocH$`OT9N41U+wtvB3VN;e0>1SK&?3pZF-hxNB% zhPP%5YPuG-rOpZ#i$&kK>JFq~7W`+7>(^KKZ{=eIvQ6s4f7G*^n8 z-Q5*cyNdzFXMKlmzI@Yv_)&a#l;H6H%df zqB1!r8Texyf$GgHbd^6X3-PNrhNYv8ZLFfzjITbUHg}k{YiOjP^(ib%LVmr83N%ps z^V*7(!^)SRhiO0w6m}``=!}bk4JA4|V~X9zY`M?gxa?O`Jg6HO_iK<{M?vizG$C}_ zs_;&0c$l7Zq6%UN+Jz}#Lkxtq+@@DyEVuY@lTV&KKw~GLOGWr`f|hiQB@71bti1V{ zs2OaGgjgjkmmo8!Q>h!3yF~w5%>JsZvO^=mSWkx|0rmRE)ZG8iZ(}puy7pQ&TKZn{ zDS8uJHc3^MS#?TTMa$q>>2&o0lU63Pnq)DvpS7HY7iF17ZPbUNx@c!9zwExLxnN1% zMh$h+^{e^R;7ZaKwes2LEPD=)x}Gr%y+WycAORydk>NheMh!KEXH9I);o3SqT@!gO_L>4Dpz(gFyJbhm6`8 zz&KlG*eioI$(^|pHMxBA2bM&bO^*DWN4dGEJM6l}Ph~~D+?plPo-fQEu{F?re50Bfs zxZ+jo_)q^RMEzIfj2!Jyn>rx9fB&~W@vS*tpJT6F42u!4j~VFc07o^bvna4d6=nbM zqv)4}R+pt!fi$#8RoWB5`}apvO>>9v++)!5%OQ^f0{;F|)E# zTt3;4SJzNDOS+BeoW9OXFpqqrqCTIM2N+n7kXt5_Fmv-Hm91=EnN6od7~R(*sA zM=Ak_%p`V91;`|lVBl$IEy^;T5ac2mLfJxOD?(=?)z)+@;^&KC@dKn-aQOpsT_p3hQ6*iySTL%8zgXE8zMhd&WRDAEGBb|R5 z_5&jR&PmF9PxP8<{TbsbYyHUB0~8eEefnyxflMA1TW6PCMwpsbBBinHr!jrPH1~3- zabHs0_^Em9?0@+z6n*6%Yq3B7^=gktFg}<=$_hYII-QjV)xH-MJ++T6F-R0y7dOAc z+WTv_0yvBmgm&;5?=O9lyeu>`3I|(oyT6@iN|NeWOSpQ^v^@MnWem^^Z3UDRye6d) zXGcq!=2$`M?PB5}G#J~ff_T5}uVN)sP@OaizUq3Dhvny^PMWLP0! zV@G;d{anLg3J`tL9ujG1pLP0}y^a}xx>d{irGY4dc_0v+pHoGF9D(sF)bQs=(Su@c zp{xaN*VzS`gcEd9TFpw~DY{(7i!8~N$TsW{hEuu0$n9N~%v%AhcV9W^0t2)Ot&6SX z0+>iTcdQKS9TZxWC>~e)vn#p@#!lJYOh~NPiN#+>jJ6-};l87%@^rvC_{WaSN9VwX z!_N`3{}J*}MCi57`-UhjuAu#_y?V62LUA%u`(jn0!w5vNo#teT;Gbv}X7=2-{90of z)%!Nt9_;571?~->6#-9pzIqP6Znaoqd>UKpmK4}wXo|$CO~`x|cKz{_eLKI15xhC$ zkXAI^_^e<4hMz<&?duwqsnRLNo?(DcGirQF1H>#vb%6x~9em%6h9gOG5Q3z!z7^ZC zV}ISgW`wo-r@aN}D>~?#3SnE>OrhdD!Py7Q2O&;34&^9P5C+djy!{oCKK28L&Vu(?WvdmdGprWJPRA(R0 zm1i{4nTP)Z8X(e1Czv83DdkN6Y6y`xI2t@OIm3 z%{kucpTkU{?v%-Qb7efXFLSf_?4dhr zv6>L@ZFBQciwHbQbkMYY*+9`ozg_&pU9eyM1*;+Si{y`5BCZjbt*?z8*p@AyA(czhP~Dx*Lq0 z=^qy+fB#WuD1GCK!M}fdzoT^P@;sSvy7i+j4&VD3o-j-4oxCiKtjv>LcS(#FBvwSb z; zSH_qiH|KrB!g634=q>a^tDDlHkEYHSZ_SC#%D1y-wL3pEMR!SK?UQVw!KMKG5=D5g z47AK9LUH<~2WO2(N@K@^D)#%v!{Zsqv(DbBRHR6}(*c^Kt^M8(wH*nK8&lYp&j$CR zAXv{CyJ&rxo>MQW?ehWOHwg5AH$*cV7#kO;!g=gcnlo->9|YZ9o|CD7X<(1ys3kT8 zj#%$8R?#(HCPeu8hBFpN7zmBe`qvwmtP|3yCqY)a_Lm=VF{9$H@wr|Hw3NC(XIg#G zP|NT1GJ`l9TTVyw_cqplMJ31miU^-FmIR9rkw z=E=tg`9;bmF7ws2P86+V3R4-QiexQnVHle)S~ja1qy<8k9`CoFW;7@6So z*be1a?>M{{G!^odM91wD!QvttpWGx@zQkln=FF;( z0)=aC8S9c! zi|ST4Qr19{waR|<73lklyvY>U0~3vcZV$&krYKW0?e6R*%RZ6&;Qbt)9|DIsuMIid zU&Jbc{5-NJztzjo$3--;HrHs@38_jBECs5&f$-+FTDLU}aM{0qq04wy-fSiu za536=AY+Hh0F8GykJ2<5+eO1pM8oRI&=(8nDy$Li)U)^ zsN&*qoM@V_VeuC&b8pT4xjzmgQ_0chQ$pgO0=FOX#kkKJrJ`ptOIEW9;l!66h!U2f zUti1;9 z7hq<0i))OAGqWU1GX~tmTlvpUeF*;UKsc7yoE(=Yaxf!q}CTS$m^)H?H!|@D3^C6bIYS~tLGH; zc$&E?QHQ$WlQ}O=S*?qjrV*)-EWeJF*ewT73k^_5RvpQzc8C&~fXb$zc7_qglS$uh zJ6LFW!lU9EPCj}?yC z5-aW!ENl=FnO{*kBRdWkDpq7QY79NNHYX5pHVi50G1hHI8|nMrmD#BnyXnlK8xJI_ zsDQk?_8)?aB7Z1z-fB7{D0Z^eHBdNIW5(dJ8GorLEc{4I?xyJi!)~QzMJQ1wTgrc_ zN+cal7hpCP8=aw?KOLS~Q(s8|_?)NH#FXMqDg18QpOVhgt$M6!RjVv3u{fM>DRz{# ztRd_ki2n9zYco9cz4>nGVX$JCDch34z5(=+V)J_o$ALj9z5Jiw`7eRNxq-{jb`Fx_~6w1 zbjsL~!B8GQ!VOW-aE}f;Nurxd-0o%dw8e(8wSW{&ul31UK&r52_4M}nAP$()((|op zgqcM&&p6uljm;{a>mY*jJ%o_jf4B-0z56Tm{%e-Ez;GXk)68+ zydKV!d(AD$J>R`qjicmz>d4_~P)tp2K#m5BLz^T{iK)Xz zK20u^->+~@=G!)$4hNC|Q4chxq0zSr4(?6isSqhpZD|YP`kV&8sH~Iw?o|<;XfM__ zbC4>Cw$o`y(_V4meJQ1BEZ+Ld)$&@7J{uXVncuCa8iq{6hii5bL9sf>DIoVTV2Vmf z!9Jd0?T) zGO3{&M*G=w>#RHza}4YLzCIk&FdtFt>1rNl@Qj(APBX9b^sYLUL$<{|tBR$Te0EKu zRycVzqeh;(f0nd0j1xhAU-D^LBZL7nJPcI4)TZEa4GvC+3;Yu@E4Y%`Yim4{&! zX^T{6wVOuPgS7jla#=)aNz#Mn?eYF1!{`7rtvf%Hz9V!I6Pwawj2@^tPBApjnqDh5 z+|>3N9M;}?>QTSq)5SMP|r1btwg7$}>6N<-S@g%RK6ete3B;h1?6o{@H9{QjGsy#0sH!olVK zM=^;R0bU=t2<|)We|o&70Y}59U+AO!EfT@ODgCD&7Z3jwyaD&d`A-AI|GmZkHWti^ z?_~R<``|z4hh94T9cnD{h-b_dD1XOt)Bu_N-`HU#JVX47oim^J^u~&2gN|v}S8=#k zudq$7F`i^`z6z+rKlvWP{U85eC`IAtf;gj%)bG4pcl!a$3G1tx!zJZC6k-MKTHrK4 zo{UrDLMrTIC>g9ncsSkHt84DduP`Q3GkIB33j_fOoXy#di$yALV45K8b<4S$hHhM4 zF@Mn_5K5G$i|V0MfG*;j{VGjws>ZtcOHwauNu*-8EEZlvZUxCWD>nztX9qR40{Jv1 z|D@WH(Z8!$EEIKnq6Y4V{8s0BgMqZM-n*?<$zw0-pdMZ1=Orm{qf+$i)2sc)^%x{g znm)D56V;m7bJ5HKyUZyH4{Q8P*i@6Dj>9*cc{z(?T21E|Ke8k(x-4yhLlvQ`-lfpt z_jDF$0XOak((57|Hp>E^JY~Y02bVkwKKk~g-fONb);Bj*)cKRV5faN8)-p4Bs>O=M+v6hvVM!tt(1jy z?5soPixF2}Lz`YVTQ9VDnHYO2)XJTgFo{`qshQW^YO>nnO*Q)ec&)VAbgrL?aMAhmd}x3tzc`9-dF9V#5|O!&?Apkr!5um#QvYH*bxar z+J|%Vt=0qA8&g@QwUR1Q_scN?yc54+@3O}$J>phgd(G9Xu!R)o+s`LAc?U=jUwW7p z%)|I^%}#Ik^laqM8mXqQra7hEF30F^elsj6f_or2T11n9#9=-*3ruwj228G-?&t&g zMF+RFRz*juSi>&vdZ4m<_Tmof9~B|VHC6{%XOUt7hioK59;MbvmGwrVx{1co^)SAx z-;XZ0t;r2^-9sQo4OY|f5_=b@V2J=TRi!(0n89naLL!CckzOXW==S(f&;OP*b_xa| zkE|pEHQV|cXD9o4yFau{yQ=KW=GUg4m`92ApO)P(+HO7wQaNH=3M2O%J0eS zC!s<>-s_K%TrJ>{?r}6GF2~&U`+Dw)bo}9&H1`94 zx%$27+bq3~kiI&{yOBK3raRr4Xb7q>o+gpAdScz52-hsx!$_IdIoF~G^M37zwWAQ* z$3C`7zayQ*rsG!nG%EV5yKi&Jf)3NsAdb6{c&=CP!g!UMJ?~qb2bypN>o4^Br))fE z1Z6x2s*N_lSs0J^URsccBXr%A$C)d0smh+9Xa$J6KcGLT%z{N>8c(CD=0H z$;;a}_E{rRy{ov6QGI^#eo5q`*+~-N9HN0JM{sra5OQp7?_fDS@weeBmJ3dOjgYE7 zy#P#sC3KdzgmrNrpoQ9j7J~rTipC=|8p(KT zfa#fLV&a{P$kv|B^Veb~rn#Z}++cP8j-EvdfioN92pjhvs)1s%Vn)W#kS;yO9lo5a zg@@I{$1%*y`T~WlTn0Cfj@Y53SRNY!j#E|ZwV2uthP1#DzGZ6xn#h&}&NVHimQ_B7 z8)~$u^iH8_ydZWFjqh)41g5ZV9PPZI6g> zlS=n{oW7jnQXE&@%*pW&eashd-mqBc9^o;ryqdU5CTVwpjh@I&*(^K^(XtN9$`SB< zaF^*iOPV&O(QD?Q)k*Wqs*I7tsMEueh#LR9_vHF8{&1eM(N!yIolaFCZeDCPKgz9a zW_hqGWZc_xNEOlwD4zHlba}loYurv3ysVw9`l=RUK{sGYfgcvys}Xq%Dm?u>kB^AYm(p4tz36UDB_a!vb^lsbp9_EuvW|1aDSy%y1=`X zXb<_Si$a=mIwRO(`$ydC(MqK!jR{z%y&Dgd;(hwCvyqNkFl=ti08Yg5db#ZKq<-0p zaC>-aT33+HUx=0?RG8t2NC)e zS4a55?k9%TmiFH8kW;BJ+-U5T`l3c1-9R@&!?(CKY)G6gCJsyG#CbP!5aRa2B$AR` zi{8Y*?&%ZM1Sag~7jKXTs`uyW{3F)zw?+sj4)`a>PEL-x>IKtWit4G@Ea&6H(HCK+ zh*uLtHo=%vU%GaJ7TZ~^Q>|dr8V{tkHBP!DQJiu+tJm&_3Dyng3Q|1@p;8o7mgb46 z?RYE@d|~K~%|LLnYn0og{c-GAGX_8ag!7jCL-rG4#iV?FNCx+?@#>s?d#a<=Zu!bv z!)5v5R$_!fB5K+3KI80qfWrpK0E&rL(`aCTL%zEbanJIN zoTD_`y{1gEE&e+Nm(4|@!n`}Fo$2QkZfg&-&l_%SA3juf0J`~24Yn}sAT^u*yXrIL zy6KLhqoakCQ!jkM)4SNRmpOiqYbDO)^Lk*=j&0oCP%NY~t(4cRVzow((2*(hGHr@D z{(Oy$zxf8K@?qG^>)>d~g6;hVzgyGo*g_>kEqG7QRSp;reg8LhGk=<%Len?Xin-Fx zkm0|9sY#4QT0fQ3BZ?ZIOhFe5<#0n@b0w)w)nEiO~3!^r?B|=b|Oyi(&Nt_ zzt?K<-u}k0RJ-)c?>|;emfCNktfJ(|Tu8W=JT}(kTJ9#|{(gYyB<8iDO{;aK_36(e zM*brnm+Ln2FNE|z5Pf4rgU(u6|9~E~&*W%ANglIr4KwwCFA)ik^FI|*&}Hlo5_8>o z&-C_}Igs6S_zm7av~0ONo-84CHUpP!$26Hm$AzB4E9#1&H~?vmZ$QObk4&Y^hjZIU z@~1KR5QQIp%BrXswHa%#iBwZ? zS&$b7>3t#qrt7UEWH$vz<43i>Kzs{VJCd+_@r}*F0Rhn;JohtuI>mS+$wG(1&PZM- z5!=*D$!TsgQH0aKlTgt756=h>uy@0qNB!sXSY&X}s$5q&E5VJ5940I{oA|LhjZ%-h z0N^4xn1rrae~`o%c;rWdckW`ER|SM)@Az-n97GJO8cWQCBe*9y83X}vO`wDibk#WN zVVEH^w`aMi13+2FHZ2*!`ZsuINdqxyf zo|}?s=yY>A?{l3>6=mar=UOo`*cZOhS?5Yoo;)9N+Bm}FB2Eny z<3DCTUlDB}&^2xF7~4L_;k6g7B30C6-3-#%*wdDjR|~J2ar&gyxfiD9(1h3Xj$E69 zW^taf*kQkYLvy{{HN~PA_C22gG$TAT)er(58Gx~t_)W(g$Y+UI4wRmLU>s6uRMTrI z(0Eir4W5aiMJhu5o+M-_loMlBp=7SPQ(IeS=iY#K+IQOjG4Y^lE`!AXS}FAJp|DHJ z6K;ID*_>%6oZ+=EIZLY<;!a`D3b`sPvI->jF8&%PzbO)6d(ic}VAKNIT3ReoRBq%{ zidTJ0qbF7lDl~FrKpjXJROQ9q&Lxgh*I}6`5be4wZkvty%4MohQpD0t{Ip2gFe6sr zxLtCG91I|^%A4rCx8Mk~ad;ZNJuM6AZC?9sHTEUOL?&19t+?WrY#N>WU%xu$siR6I zndzzDxyrH+q|aCoSc3`%N42(3=6}8V4rY+iG}>;QwWif>*fm&Ij3Cj~`|{iO4=Sv= zm{v8Yt2VP=uTE~7L5R{^Z(yVbb4YP$f!wV? zl?YN)Q)0T~6CMx{FZ?vs-%wx?!EI$jeTGzN&%%E&!05K$%S^E<06)as?4m$(vHK&< z-Q&33vl~>5w&*Lvl;RYNGVy3kf8Ax|8X4PEWioppSCgw9Y&zAjrf4Z}Xlk^8GF9QP zgaSNEj^bjtc6qeC(M9eODjN-cxLh?Jz_1KBq%WLwnDhAxX&4iDV@s4(G+{G+&+u6M zfc*#flbA)`os+8v5%;I$@Acn3PP$vo4g9&ckv$7_f@pSCzk)Ox&F}U~T-G*5(^okC zey-Fk>s30=*;=Q!9I<+Sh{wVK%8oCZuvD;AO&h><6Rs=Dt!x@LOiIXl$F_Dk8p$aR zHD?;Gbv5#$)?@%8GjCo>Ud(GHUr0N&Ky0JG?zzy=kDxZnO8kx8zIAm0g-jWqkkA%> zMJ?(y>t96|k(Ze_oFfI#f@HlN{tA)^i%_t&k?*B(^tW%`uu}75>2TJE&(2g@R!K4J zGsG`XWtTPzII9||$6SmEHE+~-s!a6=n$(atVk&ImyX`qIygvOqBtE?kF1U1 z-yy0|4ir}^K5jNn;XYhUNy@dyUBH^Ab6B4hsg*{Dm9k|%f9=)S`AJR%NDO~ijjfF# zD&ML~&7mZ~@OFjEJtXG4^HZhI1*N52rUozDk!S_754nWj|V( z2H;URxcXH9jcNP16vZTP<}dtC5=g642eF?Ub)T9Rl983q+v;isgs+?dL(AOx8t(Zf z*6Y_iq^{0VU8gI{tcqy&Rgbt!Z|{$`M$MiG6VC(S-Z01cVl2k)X#DufNHs^%ojsJ2 z@wgKoz0U{kqy;Yd{>9(@`?IKm$9!-f39V4Rf=w#r)Z(FRjV#`u?7ImTm&Tmi=8tzt zirNaSrokSoe3!cp9FjkpIHrCnO@dn}`O>^}whR<6NVL#!co>!&sc*1a-kZfPB*V;{Om16hpw-KEQLgh;yC(o9%)|xk;GBYe|e@V=!C!ym9X3;;A z1C|@^$@9wZS|3Z_E7uXBDZNP+*N}5o)88}}Ms4aw0=BGITt?rYRqY(>N;u)F z|9LQj`W@0%O+htHjbw<{oMCJ1Kn1zN^Z+(qbJWP-fO1xSYLHfGd|$*sQI!A9w<=c~ zz-6W{BF4(H>NDk;v|$m#sQ9?9mZSsw({)GuY|Ta?^VkHviK$|Gt)q__hDo;Ez^+V?Dt3Pf?uri7=rxL zEBvL)GaTY2bXK_EPf5R}J=xU8zj(&1&pDix2iELN;^M2h??AL0-jiw;zHzUd`t$G} z(*hPcX<-);949jVf=ah=xe&>oCM>xDt1jZ_9ZzP?r!W{{ZQmQkjc-Q3aa>Xa2_2`K z^17B>T{Zrw>j7w?DXi^<@qUkcBouZ{AjBoMS&%e_SzUnwNjKG_Sme zb#KVTs72@5(vaKQ^X23Fh;e&@QQmHvo2IR5%%ab}>a2LDP6u4vAH{lMAU`jb68Y(g zZrj}3%rEO(d%G`-ZfxeFf>`Bfu`?Xtp^L1%mie@0T1xlhu~ZaR_g`(j_xABr4>sZl z#f#XU^UYrvFFhjuIB z{$j4{YAftJd~^bW>+woT<9F!GZ1B7_mf@Ap5RXTD{nc=W7%(S9pmH(s@+LT4 z-e;#Gkw?-x$@wx*r%Z{CURg8aZVoPod9lh?d^GW_l>~YKn(=VME#-}Ps(;Hb_4hm@ zwS7X z;zJq#B)d~>WwDOVb3n1DExmkOTrmFX{wUBV^2fPXB9G1k+n2%Gc3`ny4`LC_hLVk{KXXSASvI0wGt;-Qy zq>(vs>iJ-H_jXEPD@*C)+=Zt0l}=sCtl_+OT6cX88yd!$BlH?K^Mkykv?!?tH&ma; zNi0ohZq-teyoM!jjOh=wk8goYX5l02RnqY`S9Gy9C%H6J1%ZBoi%vp@Zu|}?+*qhm z1d3Tn6`uL3WmrRH z^bI_>wtxC-yg#GX{3anwbbH*)-3Vl%Tdj+O_nyIZwnSF-K(6`TdKXyu z$~eCL+xWSvRSYibY-zQ~z`9$J?_ z|J_4<+XLCk6Hf(K!*blCmjJv04s`X-RwI-2PpSJ%%I>ziR*r#8P4WvbTfNp{y-3z= zkB$K+^k!r|2z2JE(L=lm-8x$`QGgkl^+4}@`@9yB)C&QwZ6*As`Io`>K5F%s0)I-o zTuBbLM1(}>u~dc9%}p=ld&o8e)SrF*p%MfWf^XlpFkFWYXM@y-4>`r5;l0;C%7Cf6pzn{Fgl)H zQE)Et?BHhO*74oy#Z|TURO^+rcmK-j9YPpvV<4gey9{c0;;Yv;<@&-W z2m-p26@js7x5DY0o3Z;j8qRj_rhlgj&npBGbB_EbA%7h?c!&a^<1t=5>86>O5zi)X zN>gvJ_Z6V-=eNfI25ue$vA}Spo4^mC{!*iE#j5nb+WX45DBkv83_wW*DHRZqP*Rjm z2}wb^1f&}Tq+>;;yF~<~yE~WeuBDf5SZWv8h21mwd!En#c|PaOd2`OI!z(_sGqbZZ z_gwdNedD?dD9$_%bQfK8H*0QEdJ)Ekl&HgZac>Gn$sFztdD)EEvg8j&-8W^lueHbu zo}SzMbmzq@&kJ8J&$G*1$W2DR(MvW&XJr9lgl%8NOeJPGkEIigM6-@cC^ZZ@=Lvd~ zU&ZeHtqu%!G|sj&lrrcK{6eZ;^D!c^A24!q7Vdp|anp_RdzZKjm@g-|jWb#}?x@&FZ$ZZtyho~G z#PXf4POw?x0=AxPS)JGX{v&a6!NBBQ7c<{5m$l}~$XaC67U~0+@fXDE{&p;j%x|F`MSb+*2}$-%S#3zRPzRd?j`&?O=ci%r$6;G^SnxmjeRjp1nyOh!?u0a~7_F-gY1%=&=_9OBcUf$}EFtTg`G`N~)>b>X51T;hVbIXu( zfy#T$`HIA@knqyC3wVS9f7n!p5r=H^oOqh-B-Tu`3AFp5G*3&pK=tt(0Ld{cC?&VEucMl+uX~9%H z0wVKK^eDmSWjWb?=g40`?*~Y62}}N@=5hb;vW7`b9g9YZ#qCpf0-AODiQcx%NtEpq zASM1#BVo>c4d3Uw@Ouadh|Ch@S?}OGnw1av>3#EAoX+USW1Q1Wou7O(D-WYGm(H9} z(|&OBpoo7lqJYc#Uaon`;L&sHK^}nx87j5Fc;-2-Hrd1a*+4xw0cqE@!3YA zVN2s)F7y(84miLDp@j=qT7||3|Lg{2`K_1Wi013vLuDV4TF~oDPS_A>(7%Z3fC4oc z-wAaTq6_M|zE|oEFN6-GBE)T(?tRs-PUZ_<>CsTD|C_yY<<}&v4OKg&iOq{L&AxmW z_VZIR+XR>ajOOv1A;CQQi+i=U3Y;d#Vm`Fgn>)kppo%RfzMJtJuo$q%g=&vSPm{oH z$MU;>mw5^ZsHF9sJi|2mpa&f+7U2|7OsSY)U1>}9@iiD(8tHwIPzt)-NR04s#$Khz zx+xF8whgb@+RU6PQ|E$&P={deWe1Qq>Y^M~gOLjLeeHC3l?%q;CYvB3n0<1;x_^lE zPe;3#7qO83T6fHOw&M%ZMD#&q?nnyzX>6Mt;AHKV-z_npT{4>Be&yh0fLQTgnbV8K|alh(2>&q16pc($1Cg|sHog$ ztlxUN_14jp{6LX)vTOeLSSCa>?e&&&=~)wI1mk%)IZFd$MqFNDsiu|v>!$zAy1%x` zIqZcg7IfHRyhJ8rtYXAzpL1g+>_EMfAJp`RU>7&?xx7pUaxs>;RxsShSl+2!PfV`q%9)>03HYvxNlEr^P%5DIYWut2)@vul2KJY~bsu`AjIg@xYlg0nn*$F#LF4wnSlRY>s4OG~5dcOVy1Mn)2deD{|>;*92g|dx8B##o| zSPH?59P=z2zJG%Cw0NQ3%~xPO4+-;i+n+g}e7$j-Zz`yfe9NUOjkL8a;eSUWTAxiM zZo74e!5-XkI5jK|sUG+zhv572k2vI~Jgqa>(A!sopzO(L=Q5xQx%q-S^ly;iAmXD- zH89zoeVjM1E&!kb*e<>*Ed@St`Q6Tr@f82BBkq$^Uw0c~`<%3{bbv47DY+_~W`A%s zKe-lay*VM*{vvv)+%Jgl%;jk~=AtsxLO2Pk$N%Db1T6USRrDN1nxP12x0d@{;jHkl z#~B#;Zv*nB3hsn3j^3Q$uOcWQSj^{gBd9d&#ttDa);A#Q@#=-*S1d|OCQs4h@cL`s z|AMdqh~FsV^vb`lrtjvVZ!>#Pz2Ic>zkdVEcj`c9cPjUbz5x+a9Ndd9`=6fy1i_Zb z)+T6+^M=M^#EbO z_nqr}id+Cbqp6qks5?STXiC(@=$XddyKLk-ny9%u>MCwMN)TnuEln(pFm+dA@!z4(ZB6u{I9w>dHa6s z*2XOrp=ZqWwN*~Okdh%DBaQjcfrOAW8QZ@wSTcv-tumV%+VKP$s;M>jB|(et{FsYM zDm#s3evHNb>yz2(*2j64^`Kt`1Hb-`R06_(AWN*D?O0;ix=b$>s?5exulH*Bf&n$o ztbvPuRdb!K^hw)w#xp3>kKzjBw|j?W-Z^YKpVWwL?c{zHaOM;0m>uMwGbeq2BI2U! z%JafU#QIh%){)W#piic;zT85>0{i9wq`l4lB6LT-=^OKG|Rq}>aWkKVOy2VLU5(mnE zygdDS1ss3t*6QDVy_>$p>W*Yk3U3fZhVi}bAQ$$zxv!tU6PRKZu}|o0<@A_j3%s8i z654%AhArULb&+jl;p$-R8fzu5UYgB@#c^?_hd}Np` zt1S9B%cM{;Y`5Us!aonP{h6~{@kvi!Sm0oPxS|>9XEq=x@olO*XBwN>bjDx7UI!o# zhz>tf)zW;oE11z}87AY+z?Liabe6cFFiF(1S@Zmq)a>N&r>G|2V?g5EbyXK)h=)`Q z+p93bEcYIVUjzrpbT~i)m5?{;*5{BZae-xh343aGe|g}H#)n^^|6*s@@UQaaFvHr?+kZO9^B-WHSrhGR+l~@L9i*D zk&%#NwnxY8p6&}twd=2s=mgIoi2HKwnjh#oWPPAgpa=E~Ki^)Cj^(#Cy`|WzO1;~i z5r=3hWpph<7kdnl08!t_Ku2N2~ckHGOwoV z?4D+r(pPaC>Zo`REk6#iQ?($)Rp zAV8YjWJcAcX`>h0&{5_Bc@EpY)Fnj-sIjjW1(kaAG~fdzZj@1}21z?Gp!J8w{6EU$ zl!pq${~HSs;;Zc@yGI@0Tlsu+UNqs{K_@QR=#}nmk=HVBcmuTJcEr8*QO8_t3e!=3$+J<)UJYH1V~^{R*VE>t|$@wZc_g**=#(CnpD z#Jpxh;@PoQIixmJY|m8eNY(DQ`d(`qtWC$mI~aH0rS+_lmSfZ6W)FTBo~*tpV9+iD zD@o(s2aU{e#ZmRfAgJq`0E8xcQTK0j;3uj$P+=Dsh%37aA%{^i{AbhGCikCsxyYW! zS=kZ|%C-EC-3?mx;WwPiZ^Ke}oajBkXAk#TMRjD4tn*}33a)2gLLBNhYS!n$z5|d( zP8X^Ks)o!)-$7+I^VB4x*N7H^yI5bPsQyK*8$W2Y5?h)dp?yOm5ikOyJJ$gOq4ru1 zR7g6dO^TC9JB%%f%p`r=@#zaB@-nz<5TIDQX)K-r357hsng0Q} z3P8A>yfyj$-qij8`HV30FKjNUu<|9QQ(;NuOG8%!qPIlPJt16_i*OTP<5#DH1j*$+ zQ~(ii|4l1#TZ3jBZMp_6xl3l+lOl9bNrmRDqJS!a%gy5j)tx0#uA+}-E#%e+$Pb7k z|MR&P`C(y2iGP2CrJ0uGefs9*NHk5i{mGf}r<){=GOK?50RipQYvFfp*FV#=2ZdjE znD3Y407Kz6A@X0z!SnwknK0AwWHK&MQ$3xn+A%BmYB@zKBwSwXy4%mNS@roUH%sLT zb2AMOSE}cP*aNc9Lo4K2NhPg{uUBUXw)9>(9eF@F{~=DpNA5-lub)Dfw_|4WG;KsJWn zBLUhRNd1qeb958y$Pud**{fU0HYEq!E8@eg02tEvoz&8>=J=M1Uj6x8?sE$bjT*Nt z_~h5Or9FJE2?SOKXBho277dJ=vlPB{N7!SbEphc@yjNLouD`(BnXT`(!=Dzn@X<}E z>UlcYfr%a@)3!KQQdXtxBcU`eB2J#Ze zF;Q0DKiVL|B zV;Chwy@;{yay>7Fz?SJBWTGt>=|j>}4hxrU#8dUEfme&PEqNok+Y?(X>8ZNh zT34Pn%bk56KKd#idg)Gv@MYk%C<+DVqltrpF2Xv6>LsLnt${hjL;JSK-PtWgxS-qb zqZ#wfdw*atOf|GHG~#-$VLD^iKjSzUAML{_TJ?D)}~{M z?S4G%(1EX>$Q7S)NX@|xpXr|rjhSj$eL2>NuKqOqT7e1J9t62Anv7NZh#Ch2Cg=EGs zQs@+shYXu%w(Rc%k{vhlflo|cNXzIqa^%{sxtXX7{OH#o^gRE}M;9|aiNB32-^D7X z>f|V&bSv0bW`uo8v!Ufw{&QjESrzEUIQlPapdug$|-yt>^QCTPM)jul>`W*|D)+y#Bc&*zL7W-}20ig`qe~8Y;lcKmpdwD7WwV99*URY|Yfv5Pf z#xZ+5rgRESGE?GkT$@o6C+f~y{H#g4DQ6?pn@5_Ig*JwP(5v{>LSd2eu=*(@ps3k3 zw@cJ4E;ZA-=s)kL`K!xskK^OkSHtM?#h$&rmv zt~}Luo~zsT?5W->?x>9WCs1Xd0dqN@t5)2|P;q~rHmM94M1FyVM6pj&Nkw8|$g}j_ z{HE{cPvtceA{vuVg|8VHb9|0>bmWF>O*a2zY_%Qt-s;J>gNCaV-;=Y)S1y=A`JG0i z;*QL0&*!;~4LZfTBKsrX{-N0~UE?bmc+PyUx9sO0qdcsZ`8p)iO8tDZH7 zD@GPYB$3dBQ3Bb4Cry+tl^Wd=es{R0W2ksl?lUu7HP02V;WJG0OL=)tKRWjdyhBjg^Yw z)uz4bHARx4jkcGs_XZ$#T9UK$UM9~T*xB3n0pe*2j(R%g#Z3Wq>U%``iA@0Lx(=2; z9DJT8+y6nnk9#rKG@8w*Z;)JQpkH4eE5GFK^lkHcX&X-$`!&QVeS`L&Qk?g!ZRt_h zw&Zi~YqAA&FXog51OS^JL<474m7R;Dlw4OGJV@i2Rtyw-+;As_`KhOF4d38ty14`^ z|Hyno$U=GTIU%lno_J1(hF=?hZr@Rl5B`Le7Mp3ecP`gCni0Uq9m(g~aB4@TaN#Wf z#8*`i(VBv9IH@TdayLl3{Bkn6|JS@fr=zy=-D+{$tvObIK)J4&ychbjNOK0SI-<`MW@+{;-Bg&Wo^|uf{fE3N@0*LhCA_VkD^Z-+1)O|B zfzmcUl|iK#;3W-hFm~?B$STW@>>`cva;<9Xw9 z#Mgyw>)f#Fk1csinEWCSa^0kIe2NH-T}MMD-8$JkHM=)=xIa5BfYNHjb!#tIgrHWm zLPo7=9qL#8cuCVGqCD3!NUMtNfv_o*L<+9W8bMhpQiy!MC>P$st_Xih9F8Pl+QmuA zU{BU+$U-mr&L#>ffPIs80lmVm;?F zdbu(sv;eBJ4;Fgnw0!F%uH`wJh_?1ssAWhZ*CH1T`+FK)0OkogL10HwC=vHor2w)yb>5Z?DEMa=cuPd^wLw2aSD`e ze!pY$BX^MrMyGbd;%siF@Twjz5zCI2 zS+(oM+|FpQd|9hn&JTn-^M+}Pu1bOE-M#eInMfL!LPqPV?6rPtgS7dzQ!RSuNnF!Z zVS2VVFF)#eF1HX*Y78A_%6N4L#2o?|PMZTk(m6S>lryILgN1?Js(+r}Ufh#-_Duf@ zg|llbyW~t!V8x-Y$xP|DJ_l+&5-r;gBH!OwHw~#2+9)(gP~%P6UW(E%txmOy2pjsP z>`^g+|D$$^@#CuP*d2x9#X#dF%=6Qdzvc1urCruh4iH0GPZgYhO$z5W10o-}Nu%NUWo+7);>W%!~{gn_T+ zVzm8ed-eYHQdbnz(&IRtHh?UDY=9C?qiX7Z=PFsQZ44V!Y6e^FRqmc#AEU`+%kZSg zV{)SL&4boa?qf(-rUITm*$)H9MCJ4Tx&?nBh4i%j?n%P3dj-gakiL$W2VKMmgiyHT zT14|vM<7G3gYQN9D9OXXeIJR!OKgm3{|R);a4G=@9LbL!Z4Z%U^NpuN%sRKWO{-R@Xcd-aY?umUrkWg@r{-bhD1Y<^KE@%Zdavqf3{o zNEk=hdKoEK6b|PI2RYfKY7cV8-onBO{(;lV4Xg;MucDfij^4;;-J6K)=M`+`PSepM z*Vn|!yTorALOwQ+j^4KoSwt+ohZh!TYMK^+@*~Sgu5BCgJt1}Btnl>$RmsZHX$dW| zyeA^5d4us|OTRVxAKUu>zLnjPJ{6Fgzoum`u8(PAFqNFRuZ;dC;OfX$kmnS8iUMk*=@Bh?~;tjd(*5R-2hMV?So zAweh^%f9hjSKk<3-BY`TWzO|4EkWl{mZ%ZGH@xEhuWrKlzsP%YVcXA!b0E` zf%0@2)qfPq$t&O~VA#r{)7FN7O53Hp{50psAHH&{8@#8L(AAmtTXqRKf|Hl`{lv|t zugS}sIITlM-@tM=5}%to)~kdBD)jW!N1o`>)C1 zyN|^su8u3ke6JJqIv+3xKEowx_W7?pO$Y(>;4VNE1_ZM%DYMn;nBqwj=v16K-E(?2 z))>&XT|#?G8k%HbJ`*M~ERKaG`=@knJQpciPyf}(Zt%cF^zbCa=-^Zg?KqjIVp3Fl zy%Q?tbKFH?EE^4Jsa^0aeY` zdNe1$;jpsIZfdzY84&YD^e8uoqEkwlHIveX8uogwI*m7ho(?9kftyQtbo2IW+iA_(Yz@}}6H&UknRTmTpvtkbg3cH2S4pss0-xCoa!*pdcgl@jEb+Uq zF%_7xz(H;FV0WX;i_8R{rK6*9p`L2DVc!gB3Nh~&hqp)yIA%}rOSH|;Snv z)=ym9?*M&6reZmjm`D0Z(Dz0S{~#Fyo$8wIf&dp{sH%&47WGUvZ$c%gNT^GX?J}xDQ-LrtV?He|QFg zbkBlA+?4}}n9iQ3>Y)csroSVZGi3MgUuRu-qLwE^HZD_O!Cv18vw!$(Gxh9$7uGq? z3i)}ec~m#vQIuG-P)zW?6GYKv&3LqV--j7@By>8DcYS$pxP1m-kB`e0vuW+!ykctU zxy!4Cb;-E%mWu9RIjug(WfMWUZ|cPwA2~;5*v#^2%x;UxS3AJb`>}LJ^NUG zm?;6?P=(r;TCoF(Lc8a-tYkJXnJgjs9?JcN^VNIG1zP3Gn>dYon?=PP7HraFKoFW&!(aY8Funl6Qn-xg(HHslypNIQJDjfr{6?8~D&5tUSF@2L2XBG9>OIi?n{CqB0->C7)2nmQ> zAE#ToD==P-)Z*FhIT79c-R*PJKgB_@+AZ)K?c32qnGO$N{T&~)spW*O@~Zde_0OuX zB>i08tn=6Fl8LpO=NIpyJ@1g-|m#L*FWIWqDIpJ;UTP+oj8`Gl#_0rqI=} zHy4{KuKtixmwug71IX*sr>j|N0$qzR)(*}`ub|HdUN@c>`xxwvFRn0fUyS&-%`<#h zxI{Yd8B(A{`6emfP!u(;nAs7<6T7E==9Thci5OM__m$(b zdLNW1HGdHHSXXq@bXW0Uw@k{A@$B~_qAOin#5LiZ7)nl=LoaFCxk>6PxTZT%=fh&* zp)oRPO?kPlTiH6QuWQx!D25MW<1ujR*;3aokFzh3kJVxPs)hO(pykzY>Z3Vz%g6GW zMIA-aTDw!6W~f-*hhzwyXV-bAj<8qSM^9VKoSFjGe`~9nSwld}1Y=Y`ojYm>H%Hy) zBr7c z!N?ju#wPsZm*YLE7!{PQ(GhR*E2CeQmsZ;L>;jD$S*Yf+ZLPgN5rtHC(cBb#ok-O3 z0CJG$sZJE1PA9GG_7g?Q7Ut_A>9o!Oh|NeZ=f|7@<=EFvR%3z05h#6;pSGVH@Tutt zt@IA_wU-{IsJKCPF5d|_C>+YZvSegIH?j=NN*@i8HlmS{4XhPUPri&O>;XNYxPHwq zCqMuE(hLw}JwqIwis$iV(7vB&B{l*dy6q zl46S|$QdXJ987&-#p0sx*7d_6RHisr!}zDL5u9epS$*MEt$5Cf=5h!^`+g_W+Du$t=0$IElFLkD%7Y84t=yC})aCtg90xyA}+f-B&chC9hT`i2O z#v`8;`7~?Ue*lGmBZm(ywuV0k?dT6fy2435-hKu~_U&btDQ2-=F5%eiG#WkJ0F!x{ z?z#_HMP`F1ZQ4<56cx$Urx*?bk5{->XM zt2#ZD-=#_QF!bM^gDB;SW@Xv%er0l1N4KdHNoESMWgUn>NJpZ!ea?YUm5 zSdJ{-4LWT{Y{(rOeUbbk#l!ET!!n09?gG(f#BtWO(e?! z9;or1iQ~KGq2D}Sx>r27G~crCHpo}F)nLjER)_^v$03{$fh%|~ngts9djYh6NMcqD zi%&2)%u7%{yxe(+sbV31H}`l?WG9S2*sJrqjy|qJiW1$(H_uAPGAlz`QGWYZdQVbD zFi~Z#frmbOCVm#l(nTEPM8yvMjj{qPo=e~S*TA8>wFvaiYe7yro;+BRme=)omeG8R zP>m&R!Zx2ZbZOVG;Bf@o2h_fe8%oNX`TXhrw6$w(V|eHf5mklY!f>&N!)RZ?G~dzw zkBa9UwR(?I_upn&N^uBcq5ADR5A)OuE3of};^kxZN-;cdk6VDy9&pjh-}KtKhbhH< zQEy{mh+oKg9c+Vzg(Jq@%7x9{3Z%nd`}zpD{?&!&e`Fy4zm?Aam3wgn0`52l^S-<$ h7x)L~rZT>R<$`{>u{#d#0=8huNh?W}NPPPCzW{XZ$u0l@ literal 0 HcmV?d00001 diff --git a/docs/product/images/reading-summary.png b/docs/product/images/reading-summary.png new file mode 100644 index 0000000000000000000000000000000000000000..9adddde92dde87000c1e60800d4e9197cafc35ea GIT binary patch literal 59968 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�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 Gy!s!~QhN~q literal 0 HcmV?d00001 From 4d31f6773f58b4803f30b80ccfe7dd73369a32fc Mon Sep 17 00:00:00 2001 From: Shahid Shamim Shah Date: Mon, 2 Nov 2026 03:22:18 +0530 Subject: [PATCH 04/14] fix: stop queued sections after summary failure --- background.js | 11 +++++-- docs/DEVELOPMENT.md | 3 +- tests/core/concurrency.test.js | 20 +++++++++++++ tests/integration/extension-workflow.test.js | 31 ++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/background.js b/background.js index cf7c8a2..48c4dcf 100644 --- a/background.js +++ b/background.js @@ -786,13 +786,20 @@ export async function mapWithConcurrency(items, limit, fn) { /** @type {R[]} */ const results = new Array(items.length); let next = 0; + let failed = false; const workers = Array.from( { length: Math.min(limit, items.length) }, async () => { - while (next < items.length) { + while (!failed && next < items.length) { const index = next++; - results[index] = await fn(/** @type {T} */ (items[index]), index); + try { + results[index] = await fn(/** @type {T} */ (items[index]), index); + } catch (error) { + // eslint-disable-next-line require-atomic-updates -- Failure only transitions to true. + failed = true; + throw error; + } } } ); diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 52e37b1..70f09ab 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -22,7 +22,8 @@ flowchart LR A summary extracts page text, checks the saved-result key, then either makes one request or summarizes up to eight 24,000-character sections (three concurrently) and merges the notes. Any failed section fails the task; partial results are not -presented as complete. Errors retain their code and message. +presented as complete. A failed section stops queued sections from starting; +already-started requests may settle. Errors retain their code and message. ## Source map diff --git a/tests/core/concurrency.test.js b/tests/core/concurrency.test.js index 99b2c94..601c56c 100644 --- a/tests/core/concurrency.test.js +++ b/tests/core/concurrency.test.js @@ -47,6 +47,26 @@ describe('mapWithConcurrency', () => { await expect(mapWithConcurrency([1, 2], 8, async n => n * 2)).resolves.toEqual([2, 4]); }); + it('does not claim queued items after a sibling fails', async () => { + let release; + const pending = new Promise(resolve => { + release = resolve; + }); + const seen = []; + const failure = new Error('section failed'); + const result = mapWithConcurrency([0, 1, 2, 3, 4, 5], 3, async item => { + seen.push(item); + if (item === 0) throw failure; + await pending; + return item; + }); + await expect(result).rejects.toBe(failure); + expect(seen).toEqual([0, 1, 2]); + release(); + await new Promise(resolve => setTimeout(resolve, 0)); + expect(seen).toEqual([0, 1, 2]); + }); + it('propagates a rejection', async () => { await expect( mapWithConcurrency([1, 2, 3], 2, async n => { diff --git a/tests/integration/extension-workflow.test.js b/tests/integration/extension-workflow.test.js index 6a15522..8f2988c 100644 --- a/tests/integration/extension-workflow.test.js +++ b/tests/integration/extension-workflow.test.js @@ -96,6 +96,37 @@ describe('Extension workflow', () => { ); }); + it('stops queued section requests after a terminal provider error', async () => { + let release; + const pending = new Promise(resolve => { + release = resolve; + }); + global.fetch + .mockReset() + .mockResolvedValueOnce({ + ok: false, + status: 400, + statusText: 'Bad Request', + json: async () => ({ error: { message: 'section rejected' } }), + }) + .mockImplementation(async () => { + await pending; + return CLAUDE_REPLY; + }); + + const response = await dispatch('GENERATE_CONTENT_SUMMARY', { + content: 'word '.repeat(38000), + summaryType: 'key-points', + }); + expect(response.success).toBe(false); + expect(response.errorCode).toBe('PROVIDER_ERROR'); + expect(global.fetch).toHaveBeenCalledTimes(3); + release(); + await new Promise(resolve => setTimeout(resolve, 0)); + expect(global.fetch).toHaveBeenCalledTimes(3); + expect(chrome.storage.local.set).not.toHaveBeenCalled(); + }); + it('persists the summary to local history', async () => { global.fetch.mockResolvedValue(CLAUDE_REPLY); From 8ff93179e3eeeb3a7b062f222eb7675d37022c65 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:13:45 +0530 Subject: [PATCH 05/14] fix(workspace): scope delayed document results Ignore obsolete extraction and analysis responses after accepting a new document. Keep pending history snapshots from restoring chat after a successful clear or a new exchange; preserve restoration when clearing fails. Fixes #47 Fixes #48 --- scripts/popup-main.js | 44 ++++-- tests/e2e/workspace-races.spec.js | 156 +++++++++++++++++++++ tests/popup/interface.test.js | 223 +++++++++++++++++++++++++++++- 3 files changed, 411 insertions(+), 12 deletions(-) create mode 100644 tests/e2e/workspace-races.spec.js diff --git a/scripts/popup-main.js b/scripts/popup-main.js index 45e68a7..116936b 100644 --- a/scripts/popup-main.js +++ b/scripts/popup-main.js @@ -21,6 +21,9 @@ class PopupInterface { /** @type {any} */ this.preferences = null; this.warnedAboutChatTruncation = false; + this.documentGeneration = 0; + this.chatGeneration = 0; + this.chatHistoryPending = false; /** @type {number | null} */ this.sourceTabId = null; const params = new URLSearchParams(window.location.search); @@ -31,17 +34,20 @@ class PopupInterface { } async initialize() { + const generation = this.documentGeneration; this.setupEventListeners(); try { await this.applyPreferences(); await this.updateProviderStatus(); - await this.extractCurrentPageContent(); + if (this.documentGeneration !== generation) return; + await this.extractCurrentPageContent(generation); } catch (error) { const err = /** @type {Error} */ (error); + if (this.documentGeneration !== generation) return; this.setPageInfo('Could not read this document', err.message); this.showToast(err.message, 'error'); } finally { - this.hideLoading(); + if (!this.isProcessing) this.hideLoading(); } } @@ -184,7 +190,8 @@ class PopupInterface { // ------------------------------------------------------------------ content - async extractCurrentPageContent() { + /** @param {number} [generation] */ + async extractCurrentPageContent(generation = this.documentGeneration) { const requestedTab = Number( new URLSearchParams(window.location.search).get('tabId') ); @@ -193,6 +200,8 @@ class PopupInterface { ? await chrome.tabs.get(requestedTab) : (await chrome.tabs.query({ active: true, currentWindow: true }))[0]; + if (this.documentGeneration !== generation) return; + if (!activeTab?.id || (activeTab.url && !/^https?:/.test(activeTab.url))) { this.setPageInfo( 'This page cannot be read', @@ -207,17 +216,22 @@ class PopupInterface { const response = await this.send('EXTRACT_PAGE_CONTENT', { tabId: activeTab.id, }); + if (this.documentGeneration !== generation) return; if (!response.success) { this.setPageInfo(activeTab.title || 'Untitled', response.error); return; } - await this.acceptDocument(response.data); + await this.acceptDocument(response.data, generation); } - /** @param {any} content */ - async acceptDocument(content) { + /** @param {any} content @param {number} [generation] */ + async acceptDocument(content, generation) { validateDocument(content.mainText); + if (generation === undefined) generation = ++this.documentGeneration; + if (generation !== this.documentGeneration) return; + const chatGeneration = ++this.chatGeneration; + this.chatHistoryPending = true; this.pageContent = content; this.conversationHistory = []; this.lastSummary = null; @@ -251,7 +265,9 @@ class PopupInterface { url: content.url, content: content.mainText, }); - if (this.pageContent !== content) return; + if (this.pageContent !== content || this.chatGeneration !== chatGeneration) + return; + this.chatHistoryPending = false; const conversations = response.success ? response.data.conversations || [] : []; @@ -318,6 +334,8 @@ class PopupInterface { content: this.pageContent.mainText, }); if (!response.success) throw new Error(response.error); + this.chatGeneration += 1; + this.chatHistoryPending = false; this.conversationHistory = []; setHtml('chat-history', ''); setText('chat-memory-status', 'Chat cleared for this document'); @@ -374,6 +392,11 @@ class PopupInterface { const question = value('chat-input').trim(); if (!question || !this.requireContent()) return; + this.chatGeneration += 1; + if (this.chatHistoryPending) { + this.chatHistoryPending = false; + setText('chat-memory-status', 'Current conversation'); + } setValue('chat-input', ''); this.addChatMessage('user', question); @@ -447,6 +470,7 @@ class PopupInterface { /** @param {string} type */ async runAnalysis(type) { if (!this.requireContent()) return; + const content = this.pageContent; const target = document.getElementById(`${type}-result`); if (target) target.innerHTML = '
    '; @@ -471,8 +495,9 @@ class PopupInterface { if (!action) throw new Error(`No analysis available for "${type}"`); const response = await this.send(action, { - text: this.pageContent.mainText, + text: content.mainText, }); + if (this.pageContent !== content) return; if (!response.success) throw new Error(response.error); if (!target) return; @@ -493,7 +518,8 @@ class PopupInterface { } catch (error) { /** @type {any} */ const err = error; - if (target) target.textContent = err.message; + if (this.pageContent === content && target) + target.textContent = err.message; } } diff --git a/tests/e2e/workspace-races.spec.js b/tests/e2e/workspace-races.spec.js new file mode 100644 index 0000000..e7727c0 --- /dev/null +++ b/tests/e2e/workspace-races.spec.js @@ -0,0 +1,156 @@ +import { + test, + expect, + CONFIGURED_PREFERENCES, + seedPreferences, + FIXTURE_URL, + activeTabId, +} from './fixtures.js'; + +// Delay delivery after the real worker has produced its response. Storage and +// provider handling still run normally; only the UI message timing is controlled. +async function delayResponse(page, action) { + await page.addInitScript(actionType => { + const send = chrome.runtime.sendMessage.bind(chrome.runtime); + chrome.runtime.sendMessage = async message => { + const response = await send(message); + if (message.actionType === actionType) { + await new Promise(resolve => { + window.releaseResponse = resolve; + }); + window.responseReleased = true; + } + return response; + }; + }, action); +} + +async function releaseResponse(page) { + await page.evaluate(() => window.releaseResponse()); + await page.waitForFunction(() => window.responseReleased); + // Let the controller's awaiting message continuation finish before asserting + // that no stale UI or conversation state was applied. + await page.evaluate(() => new Promise(resolve => setTimeout(resolve, 0))); +} + +async function importBook(page, name = 'book-B.txt') { + await page.locator('#document-file').setInputFiles({ + name, + mimeType: 'text/plain', + buffer: Buffer.from('Book B is about bananas.'), + }); + await expect(page.locator('#current-page-title')).toHaveText(name); + await expect(page.locator('#loading-overlay')).toBeHidden(); +} + +test('a late source extraction cannot replace an imported book', async ({ + context, + extensionId, +}) => { + const setup = await context.newPage(); + await setup.goto(`chrome-extension://${extensionId}/options.html`); + await seedPreferences(setup, CONFIGURED_PREFERENCES); + const source = await context.newPage(); + await source.goto(FIXTURE_URL); + const tabId = await activeTabId(setup); + const page = await context.newPage(); + await delayResponse(page, 'EXTRACT_PAGE_CONTENT'); + await page.goto( + `chrome-extension://${extensionId}/popup.html?workspace=1&tabId=${tabId}` + ); + await page.waitForFunction(() => Boolean(window.releaseResponse)); + await importBook(page); + await releaseResponse(page); + await expect(page.locator('#current-page-title')).toHaveText('book-B.txt'); + await expect(page.locator('#current-page-status')).toContainText('Ready'); + await page.locator('[data-tab="tools"]').click(); + await page.locator('#page-stats-btn').click(); + await expect(page.locator('#tools-results')).toContainText('5 words'); +}); + +test('a late analysis result cannot populate the next book', async ({ + context, + extensionId, +}) => { + await context.route('https://api.anthropic.com/**', route => + route.fulfill({ + contentType: 'application/json', + body: JSON.stringify({ + content: [{ type: 'text', text: 'Old book insight' }], + }), + }) + ); + const page = await context.newPage(); + await delayResponse(page, 'EXTRACT_KEY_INSIGHTS'); + await page.goto(`chrome-extension://${extensionId}/popup.html?workspace=1`); + await seedPreferences(page, CONFIGURED_PREFERENCES); + await importBook(page, 'book-A.txt'); + await page.locator('[data-tab="analyze"]').click(); + await page.locator('[data-type="insights"]').click(); + await page.waitForFunction(() => Boolean(window.releaseResponse)); + await importBook(page); + await releaseResponse(page); + await expect(page.locator('#insights-result')).toHaveText( + 'Run this analysis for this document.' + ); + await expect(page.locator('#current-page-title')).toHaveText('book-B.txt'); +}); + +test('cleared chat stays cleared when an earlier history response arrives', async ({ + context, + extensionId, +}) => { + const requests = []; + await context.route('https://api.anthropic.com/**', route => { + requests.push(route.request().postDataJSON()); + return route.fulfill({ + contentType: 'application/json', + body: JSON.stringify({ + content: [{ type: 'text', text: 'A saved answer' }], + }), + }); + }); + const setup = await context.newPage(); + await setup.goto(`chrome-extension://${extensionId}/options.html`); + await seedPreferences(setup, CONFIGURED_PREFERENCES); + const source = await context.newPage(); + await source.goto(FIXTURE_URL); + const tabId = await activeTabId(setup); + const page = await context.newPage(); + await page.goto( + `chrome-extension://${extensionId}/popup.html?workspace=1&tabId=${tabId}` + ); + await expect(page.locator('#current-page-status')).toContainText('Ready'); + await page.locator('[data-tab="chat"]').click(); + await page.locator('#chat-input').fill('Remember this old question'); + await page.locator('#send-chat-btn').click(); + await expect(page.locator('#chat-history')).toContainText('A saved answer'); + await expect(page.locator('#loading-overlay')).toBeHidden(); + await delayResponse(page, 'GET_DOCUMENT_HISTORY'); + await page.reload(); + await page.waitForFunction(() => Boolean(window.releaseResponse)); + await page.locator('[data-tab="chat"]').click(); + await page.locator('#clear-chat-btn').click(); + await expect(page.locator('#chat-memory-status')).toContainText( + 'Chat cleared' + ); + await releaseResponse(page); + await expect(page.locator('#chat-history')).toBeEmpty(); + await expect(page.locator('#chat-memory-status')).toContainText( + 'Chat cleared' + ); + expect( + await page.evaluate( + async () => + (await chrome.storage.local.get('genai_conversation_history')) + .genai_conversation_history + ) + ).toEqual([]); + await page.locator('#chat-input').fill('A fresh question'); + await page.locator('#send-chat-btn').click(); + await expect(page.locator('#chat-history')).toContainText('A saved answer'); + expect(requests).toHaveLength(2); + expect(JSON.stringify(requests[1])).not.toContain( + 'Remember this old question' + ); +}); diff --git a/tests/popup/interface.test.js b/tests/popup/interface.test.js index 130fc71..040053e 100644 --- a/tests/popup/interface.test.js +++ b/tests/popup/interface.test.js @@ -47,6 +47,7 @@ async function openPopup({ preferences = {}, providerStatus = {}, overrides = {}, + waitForReady = true, } = {}) { responses = { GET_USER_PREFERENCES: { @@ -89,17 +90,233 @@ async function openPopup({ await import('../../scripts/popup-main.js'); document.dispatchEvent(new Event('DOMContentLoaded')); - await vi.waitFor(() => - expect(el('loading-overlay').style.display).toBe('none') - ); + if (waitForReady) + await vi.waitFor(() => + expect(el('loading-overlay').style.display).toBe('none') + ); +} + +function deferred() { + let resolve; + const promise = new Promise(done => { + resolve = done; + }); + return { promise, resolve }; +} + +async function importBook() { + const input = el('document-file'); + Object.defineProperty(input, 'files', { + configurable: true, + value: [ + { + name: 'book-B.txt', + size: 24, + text: async () => 'Book B is about bananas.', + }, + ], + }); + input.dispatchEvent(new Event('change')); + await vi.waitFor(() => { + expect(text('current-page-title')).toBe('book-B.txt'); + expect(el('loading-overlay').style.display).toBe('none'); + }); } +// Drain the message continuation chain before asserting that stale work stayed inert. +const settleResponses = () => new Promise(done => setTimeout(done, 0)); + describe('popup', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('learning workspace', () => { + it.each([true, false])( + 'ignores a previous document analysis response after import (success=%s)', + async success => { + const pending = deferred(); + await openPopup({ + overrides: { EXTRACT_KEY_INSIGHTS: pending.promise }, + }); + document.querySelector('[data-type="insights"]').click(); + await vi.waitFor(() => + expect(sentPayload('EXTRACT_KEY_INSIGHTS')?.text).toBe(PAGE.mainText) + ); + await importBook(); + pending.resolve( + success + ? { success: true, data: { text: 'Old article result' } } + : { success: false, error: 'Old article failure' } + ); + await settleResponses(); + expect(text('current-page-title')).toBe('book-B.txt'); + expect(text('insights-result')).toBe( + 'Run this analysis for this document.' + ); + } + ); + + it.each([true, false])( + 'keeps an imported document when startup extraction finishes late (success=%s)', + async success => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { EXTRACT_PAGE_CONTENT: pending.promise }, + }); + await vi.waitFor(() => + expect(sentPayload('EXTRACT_PAGE_CONTENT')).toEqual({ tabId: 7 }) + ); + await importBook(); + pending.resolve( + success + ? { success: true, data: PAGE } + : { success: false, error: 'Old extraction failed' } + ); + await settleResponses(); + expect(text('current-page-title')).toBe('book-B.txt'); + expect(text('current-page-status')).toContain('Ready'); + responses.ANSWER_CONTEXTUAL_QUESTION = { + success: true, + data: { answer: 'Bananas.' }, + }; + el('chat-input').value = 'What fruit?'; + el('send-chat-btn').click(); + await vi.waitFor(() => + expect(sentPayload('ANSWER_CONTEXTUAL_QUESTION')?.context).toBe( + 'Book B is about bananas.' + ) + ); + } + ); + + it('keeps an imported document when preferences finish loading before extraction', async () => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { GET_USER_PREFERENCES: pending.promise }, + }); + await vi.waitFor(() => + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ actionType: 'GET_USER_PREFERENCES' }) + ) + ); + await importBook(); + pending.resolve({ success: true, data: { theme: 'light' } }); + await settleResponses(); + expect(text('current-page-title')).toBe('book-B.txt'); + expect(sentPayload('EXTRACT_PAGE_CONTENT')).toBeUndefined(); + }); + + it('does not restore an old history snapshot after a successful clear', async () => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { + GET_DOCUMENT_HISTORY: pending.promise, + CLEAR_DOCUMENT_CHAT: { success: true, data: { removed: 1 } }, + }, + }); + await vi.waitFor(() => + expect(sentPayload('GET_DOCUMENT_HISTORY')?.url).toBe(PAGE.url) + ); + el('clear-chat-btn').click(); + await vi.waitFor(() => + expect(text('chat-memory-status')).toContain('Chat cleared') + ); + pending.resolve({ + success: true, + data: { + conversations: [{ question: 'Old question', answer: 'Old answer' }], + memoryEnabled: true, + }, + }); + await settleResponses(); + expect(text('chat-history')).toBe(''); + expect(text('chat-memory-status')).toContain('Chat cleared'); + responses.ANSWER_CONTEXTUAL_QUESTION = { + success: true, + data: { answer: 'Fresh answer' }, + }; + el('chat-input').value = 'Fresh question'; + el('send-chat-btn').click(); + await vi.waitFor(() => + expect(sentPayload('ANSWER_CONTEXTUAL_QUESTION')).toBeDefined() + ); + expect( + JSON.stringify( + sentPayload('ANSWER_CONTEXTUAL_QUESTION').conversationHistory + ) + ).not.toContain('Old'); + }); + + it('allows pending saved history to restore after clearing fails', async () => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { + GET_DOCUMENT_HISTORY: pending.promise, + CLEAR_DOCUMENT_CHAT: { + success: false, + error: 'Could not clear saved chat', + }, + }, + }); + await vi.waitFor(() => + expect(sentPayload('GET_DOCUMENT_HISTORY')).toBeDefined() + ); + el('clear-chat-btn').click(); + await vi.waitFor(() => + expect(text('toast-container')).toContain('Could not clear saved chat') + ); + pending.resolve({ + success: true, + data: { + conversations: [ + { question: 'Saved question', answer: 'Saved answer' }, + ], + memoryEnabled: true, + }, + }); + await vi.waitFor(() => + expect(text('chat-history')).toContain('Saved answer') + ); + expect(text('chat-memory-status')).toContain('Restored 1'); + }); + + it('keeps a new exchange when an older restoration snapshot arrives', async () => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { + GET_DOCUMENT_HISTORY: pending.promise, + ANSWER_CONTEXTUAL_QUESTION: { + success: true, + data: { answer: 'Fresh answer' }, + }, + }, + }); + await vi.waitFor(() => + expect(sentPayload('GET_DOCUMENT_HISTORY')).toBeDefined() + ); + el('chat-input').value = 'Fresh question'; + el('send-chat-btn').click(); + await vi.waitFor(() => + expect(text('chat-history')).toContain('Fresh answer') + ); + pending.resolve({ + success: true, + data: { + conversations: [{ question: 'Old question', answer: 'Old answer' }], + memoryEnabled: true, + }, + }); + await settleResponses(); + expect(text('chat-history')).toContain('Fresh answer'); + expect(text('chat-history')).not.toContain('Old answer'); + }); + it('keeps a generated summary visible when it could not be saved', async () => { await openPopup({ overrides: { From bd3dc25ed3fb25f4a539ce9e582e6034bd7508f2 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Sun, 1 Nov 2026 19:33:59 +0530 Subject: [PATCH 06/14] fix(provider): cover response body deadlines Keep the request deadline active through success and error body reads. Preserve timeout classification without retrying, and leave parsing failures outside the transient network retry policy. Fixes #51 --- providers/ai-client.js | 53 ++++++++++---------- tests/providers/ai-client.test.js | 80 ++++++++++++++++++++++++++++++- 2 files changed, 108 insertions(+), 25 deletions(-) diff --git a/providers/ai-client.js b/providers/ai-client.js index 09b24bd..1736c3a 100644 --- a/providers/ai-client.js +++ b/providers/ai-client.js @@ -211,6 +211,29 @@ export class AIClient { body, signal: controller.signal, }); + + if (!response.ok) { + const retryable = RETRYABLE_STATUSES.has(response.status); + const result = { + ok: /** @type {false} */ (false), + retryable, + error: new AIError( + response.status === 401 || response.status === 403 + ? 'AUTH_ERROR' + : 'PROVIDER_ERROR', + // The provider's own message is the useful part; it never contains the key. + `${spec.label} returned ${response.status}: ${await readErrorMessage(response)}` + ), + }; + const retryAfterMs = parseRetryAfter( + response.headers?.get('retry-after') + ); + return retryAfterMs === undefined + ? result + : { ...result, retryAfterMs }; + } + + return { ok: true, text: spec.parse(await response.json()).trim() }; } catch (error) { /** @type {any} */ const err = error; @@ -226,6 +249,8 @@ export class AIClient { ), }; } + // A received response that cannot be parsed is not a retryable fetch failure. + if (response) throw error; return { ok: false, retryable: true, @@ -237,27 +262,6 @@ export class AIClient { } finally { clearTimeout(timer); } - - if (!response.ok) { - const retryable = RETRYABLE_STATUSES.has(response.status); - const result = { - ok: /** @type {false} */ (false), - retryable, - error: new AIError( - response.status === 401 || response.status === 403 - ? 'AUTH_ERROR' - : 'PROVIDER_ERROR', - // The provider's own message is the useful part; it never contains the key. - `${spec.label} returned ${response.status}: ${await readErrorMessage(response)}` - ), - }; - const retryAfterMs = parseRetryAfter( - response.headers?.get('retry-after') - ); - return retryAfterMs === undefined ? result : { ...result, retryAfterMs }; - } - - return { ok: true, text: spec.parse(await response.json()).trim() }; } } @@ -297,8 +301,8 @@ export function parseRetryAfter(value) { } /** - * Pull a human-readable message out of an error response without ever throwing - * a second time — a malformed error body must not mask the original failure. + * Pull a human-readable message out of an error response. Malformed bodies must + * not mask the original failure; aborts propagate to enforce the request deadline. * * @param {Response} response * @returns {Promise} @@ -312,7 +316,8 @@ async function readErrorMessage(response) { response.statusText || 'unknown error' ); - } catch { + } catch (error) { + if (/** @type {Error} */ (error)?.name === 'AbortError') throw error; return response.statusText || 'unknown error'; } } diff --git a/tests/providers/ai-client.test.js b/tests/providers/ai-client.test.js index 1816041..ceaa7d7 100644 --- a/tests/providers/ai-client.test.js +++ b/tests/providers/ai-client.test.js @@ -1,4 +1,4 @@ -import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import { AIClient, AIError, @@ -255,6 +255,73 @@ describe('AIClient', () => { }); }); + describe('response body deadlines', () => { + beforeEach(() => vi.useFakeTimers()); + afterEach(() => { + vi.clearAllTimers(); + vi.useRealTimers(); + }); + + it.each([ + { label: 'successful', status: 200, body: ANTHROPIC_OK }, + { + label: 'retryable error', + status: 503, + body: { error: { message: 'overloaded' } }, + }, + { + label: 'authentication error', + status: 401, + body: { error: { message: 'invalid key' } }, + }, + ])( + 'times out a delayed $label body without retrying', + async ({ status, body }) => { + const response = mockResponse(body, { ok: status === 200, status }); + global.fetch.mockImplementation((_url, { signal }) => { + response.json.mockImplementation( + () => + new Promise((resolve, reject) => { + const timer = setTimeout(() => resolve(body), 200); + signal.addEventListener( + 'abort', + () => { + clearTimeout(timer); + reject(new DOMException('Body read aborted', 'AbortError')); + }, + { once: true } + ); + }) + ); + return Promise.resolve(response); + }); + + let outcome; + const completion = fastClient({ timeoutMs: 50 }) + .complete('s', 'u') + .then( + text => { + outcome = { text }; + }, + error => { + outcome = { error }; + } + ); + await vi.advanceTimersByTimeAsync(49); + expect(response.json).toHaveBeenCalledTimes(1); + expect(outcome).toBeUndefined(); + + await vi.advanceTimersByTimeAsync(1); + expect(outcome).toMatchObject({ + error: { code: 'TIMEOUT', message: expect.stringContaining('50ms') }, + }); + await completion; + expect(outcome.error).toBeInstanceOf(AIError); + expect(global.fetch).toHaveBeenCalledTimes(1); + } + ); + }); + describe('backoff', () => { it('grows exponentially and stays bounded', () => { const first = backoffDelay(0); @@ -373,6 +440,17 @@ describe('AIClient', () => { }); }); + it('does not retry malformed successful JSON as a network failure', async () => { + const response = mockResponse({}); + response.json.mockRejectedValue(new SyntaxError('not json')); + global.fetch.mockResolvedValue(response); + + await expect(fastClient().complete('s', 'u')).rejects.toBeInstanceOf( + SyntaxError + ); + expect(global.fetch).toHaveBeenCalledTimes(1); + }); + it('reports a network failure without masking the cause', async () => { global.fetch.mockRejectedValue(new TypeError('Failed to fetch')); const client = new AIClient({ From 7de3f8a86609c332bf909041b30291cb40a84635 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Sun, 1 Nov 2026 19:32:49 +0530 Subject: [PATCH 07/14] fix(worker): stop queued work after failure Stop scheduling summary chunks after a fatal section error while already-started siblings settle. Preserve the original rejection and cover deferred sibling completion through the mapper and worker. Fixes #49 --- background.js | 12 ++++- tests/core/concurrency.test.js | 52 ++++++++++++++++---- tests/integration/extension-workflow.test.js | 31 ++++++++++++ 3 files changed, 84 insertions(+), 11 deletions(-) diff --git a/background.js b/background.js index cf7c8a2..630919d 100644 --- a/background.js +++ b/background.js @@ -786,13 +786,21 @@ export async function mapWithConcurrency(items, limit, fn) { /** @type {R[]} */ const results = new Array(items.length); let next = 0; + let failed = false; const workers = Array.from( { length: Math.min(limit, items.length) }, async () => { - while (next < items.length) { + while (!failed && next < items.length) { const index = next++; - results[index] = await fn(/** @type {T} */ (items[index]), index); + try { + results[index] = await fn(/** @type {T} */ (items[index]), index); + } catch (error) { + // Siblings may finish, but no queued work can produce a usable result. + // eslint-disable-next-line require-atomic-updates -- This flag only transitions to true. + failed = true; + throw error; + } } } ); diff --git a/tests/core/concurrency.test.js b/tests/core/concurrency.test.js index 99b2c94..a1fec2b 100644 --- a/tests/core/concurrency.test.js +++ b/tests/core/concurrency.test.js @@ -19,12 +19,16 @@ describe('mapWithConcurrency', () => { let inFlight = 0; let peak = 0; - await mapWithConcurrency(Array.from({ length: 8 }, (_, i) => i), 3, async () => { - inFlight += 1; - peak = Math.max(peak, inFlight); - await sleep(5); - inFlight -= 1; - }); + await mapWithConcurrency( + Array.from({ length: 8 }, (_, i) => i), + 3, + async () => { + inFlight += 1; + peak = Math.max(peak, inFlight); + await sleep(5); + inFlight -= 1; + } + ); // The whole point: eight sections must not become eight simultaneous // requests, which is a reliable way to trip a per-minute rate limit. @@ -33,8 +37,10 @@ describe('mapWithConcurrency', () => { }); it('passes the index through', async () => { - const seen = await mapWithConcurrency(['a', 'b', 'c'], 2, async (item, index) => - `${index}:${item}` + const seen = await mapWithConcurrency( + ['a', 'b', 'c'], + 2, + async (item, index) => `${index}:${item}` ); expect(seen).toEqual(['0:a', '1:b', '2:c']); }); @@ -44,7 +50,9 @@ describe('mapWithConcurrency', () => { }); it('handles fewer items than the limit', async () => { - await expect(mapWithConcurrency([1, 2], 8, async n => n * 2)).resolves.toEqual([2, 4]); + await expect( + mapWithConcurrency([1, 2], 8, async n => n * 2) + ).resolves.toEqual([2, 4]); }); it('propagates a rejection', async () => { @@ -55,4 +63,30 @@ describe('mapWithConcurrency', () => { }) ).rejects.toThrow('section failed'); }); + + it('does not start queued work when in-flight siblings finish after a failure', async () => { + let rejectFirst; + let finishSiblings; + const first = new Promise((_resolve, reject) => { + rejectFirst = reject; + }); + const siblings = new Promise(resolve => { + finishSiblings = resolve; + }); + const started = []; + const work = mapWithConcurrency([0, 1, 2, 3, 4, 5, 6, 7], 3, item => { + started.push(item); + return item === 0 ? first : siblings.then(() => item); + }); + const failure = new Error('section failed'); + const rejected = expect(work).rejects.toBe(failure); + + expect(started).toEqual([0, 1, 2]); + rejectFirst(failure); + await rejected; + finishSiblings(); + await sleep(0); + + expect(started).toEqual([0, 1, 2]); + }); }); diff --git a/tests/integration/extension-workflow.test.js b/tests/integration/extension-workflow.test.js index 6a15522..c0fb5cb 100644 --- a/tests/integration/extension-workflow.test.js +++ b/tests/integration/extension-workflow.test.js @@ -161,6 +161,37 @@ describe('Extension workflow', () => { expect(global.fetch).toHaveBeenCalledTimes(1); }); + it('stops queued sections after a fatal failure without merging or saving partial notes', async () => { + let finishSiblings; + const siblings = new Promise(resolve => { + finishSiblings = resolve; + }); + global.fetch + .mockResolvedValueOnce({ + ok: false, + status: 400, + headers: { get: () => null }, + json: async () => ({ error: { message: 'Rejected section' } }), + }) + .mockReturnValue(siblings); + + const response = await dispatch('GENERATE_CONTENT_SUMMARY', { + content: 'x'.repeat(190000), + }); + + expect(response).toMatchObject({ + success: false, + errorCode: 'PROVIDER_ERROR', + error: 'Anthropic Claude returned 400: Rejected section', + }); + expect(global.fetch).toHaveBeenCalledTimes(3); + finishSiblings(CLAUDE_REPLY); + await new Promise(resolve => setTimeout(resolve, 0)); + + expect(global.fetch).toHaveBeenCalledTimes(3); + expect(chrome.storage.local.set).not.toHaveBeenCalled(); + }); + it('rejects empty content before spending an API call', async () => { global.fetch.mockResolvedValue(CLAUDE_REPLY); From 41ebef084d6ce37cdd821d5e1b81aeea84217a54 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Sun, 1 Nov 2026 19:36:12 +0530 Subject: [PATCH 08/14] fix(worker): preserve the daily cleanup alarm Check for an existing cleanup alarm before creating one so ordinary worker wakes do not restart its daily delay. Keep listener registration synchronous and contain alarm read/create failures for a later retry. Cover restart timing, absent alarms, blocked reads, rejection recovery, and scheduling despite unavailable settings. Fixes #50 --- background.js | 14 ++- tests/integration/extension-workflow.test.js | 113 +++++++++++++++++++ tests/setup.js | 1 + 3 files changed, 127 insertions(+), 1 deletion(-) diff --git a/background.js b/background.js index 630919d..b4566ec 100644 --- a/background.js +++ b/background.js @@ -110,7 +110,7 @@ class BackgroundService { ); chrome.commands.onCommand.addListener(this.handleCommand.bind(this)); chrome.alarms.onAlarm.addListener(this.handleAlarm.bind(this)); - chrome.alarms.create('cleanupOldData', { periodInMinutes: 1440 }); + this.ensureCleanupAlarm(); chrome.storage.onChanged.addListener((changes, area) => { const change = changes.user_preferences; if (area !== 'sync' || !change) return; @@ -123,6 +123,18 @@ class BackgroundService { }); } + async ensureCleanupAlarm() { + try { + // Replacing a named alarm restarts its delay on every worker wake. + if (!(await chrome.alarms.get('cleanupOldData'))) { + await chrome.alarms.create('cleanupOldData', { periodInMinutes: 1440 }); + } + } catch { + // Keep startup usable; a later worker start will check the alarm again. + this.logger.error('History cleanup alarm initialization failed'); + } + } + // ---------------------------------------------------------------- messaging /** diff --git a/tests/integration/extension-workflow.test.js b/tests/integration/extension-workflow.test.js index c0fb5cb..d331629 100644 --- a/tests/integration/extension-workflow.test.js +++ b/tests/integration/extension-workflow.test.js @@ -70,6 +70,8 @@ describe('Extension workflow', () => { chrome.storage.sync.get.mockResolvedValue(CONFIGURED); chrome.storage.local.get.mockResolvedValue({}); chrome.storage.local.set.mockReset().mockResolvedValue(undefined); + chrome.alarms.get.mockReset().mockResolvedValue(undefined); + chrome.alarms.create.mockReset().mockResolvedValue(undefined); await import('../../background.js'); await vi.waitFor(() => { @@ -1009,6 +1011,116 @@ describe('Extension workflow', () => { }); describe('worker lifecycle', () => { + it('preserves the cleanup alarm scheduled time across worker restarts', async () => { + const scheduledTime = Date.now() + 30 * 60 * 1000; + let alarm = { + name: 'cleanupOldData', + periodInMinutes: 1440, + scheduledTime, + }; + chrome.alarms.get.mockImplementation(async () => structuredClone(alarm)); + chrome.alarms.create.mockImplementation(async (name, info) => { + alarm = { name, ...info, scheduledTime: Date.now() + 86400000 }; + }); + vi.clearAllMocks(); + + for (let wake = 0; wake < 2; wake++) { + vi.resetModules(); + await import('../../background.js'); + } + + expect(alarm.scheduledTime).toBe(scheduledTime); + expect(chrome.alarms.create).not.toHaveBeenCalled(); + expect(chrome.alarms.get).toHaveBeenCalledTimes(2); + expect(chrome.alarms.get).toHaveBeenCalledWith('cleanupOldData'); + }); + + it('creates a missing cleanup alarm at startup', async () => { + vi.resetModules(); + vi.clearAllMocks(); + await import('../../background.js'); + + await vi.waitFor(() => { + expect(chrome.alarms.create).toHaveBeenCalledOnce(); + }); + expect(chrome.alarms.get).toHaveBeenCalledWith('cleanupOldData'); + expect(chrome.alarms.create).toHaveBeenCalledWith('cleanupOldData', { + periodInMinutes: 1440, + }); + }); + + it('registers listeners and routes messages while the alarm read is pending', async () => { + let finishRead; + chrome.alarms.get.mockReturnValueOnce( + new Promise(resolve => { + finishRead = resolve; + }) + ); + vi.resetModules(); + vi.clearAllMocks(); + await import('../../background.js'); + + try { + expect(chrome.runtime.onMessage.addListener).toHaveBeenCalledOnce(); + expect(chrome.alarms.onAlarm.addListener).toHaveBeenCalledOnce(); + expect(chrome.storage.onChanged.addListener).toHaveBeenCalledOnce(); + expect(chrome.alarms.create).not.toHaveBeenCalled(); + expect(await dispatch('GET_HISTORY', {})).toMatchObject({ + success: true, + data: { summaries: [], conversations: [] }, + }); + } finally { + finishRead(undefined); + } + await vi.waitFor(() => { + expect(chrome.alarms.create).toHaveBeenCalledOnce(); + }); + }); + + it.each(['get', 'create'])( + 'handles a rejected alarm %s and retries at the next worker start', + async method => { + const errorLog = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + chrome.alarms[method].mockRejectedValueOnce( + new Error('fixture-private-alarm-detail') + ); + vi.resetModules(); + vi.clearAllMocks(); + + try { + await import('../../background.js'); + await vi.waitFor(() => { + expect(errorLog).toHaveBeenCalledWith( + expect.stringContaining( + 'History cleanup alarm initialization failed' + ), + '' + ); + }); + expect(await dispatch('GET_HISTORY', {})).toMatchObject({ + success: true, + }); + if (method === 'get') + expect(chrome.alarms.create).not.toHaveBeenCalled(); + expect(JSON.stringify(errorLog.mock.calls)).not.toContain( + 'fixture-private-alarm-detail' + ); + + vi.resetModules(); + await import('../../background.js'); + await vi.waitFor(() => { + expect(chrome.alarms.create).toHaveBeenCalledTimes( + method === 'create' ? 2 : 1 + ); + }); + } finally { + errorLog.mockRestore(); + } + } + ); + it.each(['install', 'update'])( 'handles unavailable settings during %s without skipping first-install options', async reason => { @@ -1088,6 +1200,7 @@ describe('Extension workflow', () => { ).toHaveBeenCalledOnce(); expect(chrome.runtime.onInstalled.addListener).toHaveBeenCalledOnce(); expect(chrome.alarms.onAlarm.addListener).toHaveBeenCalledOnce(); + expect(chrome.alarms.create).toHaveBeenCalledOnce(); const response = await dispatch('GET_HISTORY', {}); expect(response).toMatchObject({ success: true, diff --git a/tests/setup.js b/tests/setup.js index 056383f..4509241 100644 --- a/tests/setup.js +++ b/tests/setup.js @@ -71,6 +71,7 @@ const mockChrome = { commands: { onCommand: { addListener: vi.fn() } }, notifications: { create: vi.fn(), clear: vi.fn() }, alarms: { + get: vi.fn().mockResolvedValue(undefined), create: vi.fn(), clear: vi.fn(), onAlarm: { addListener: vi.fn() }, From f4f0eaac4ca670fa8a8b778b0e2e4b33e2b774ca Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:13:25 +0530 Subject: [PATCH 09/14] fix(content): preserve visible text boundaries Read the original DOM with cached computed styles and exclude hidden or unwanted content without changing the source page. Keep block boundaries and inline words intact. Add unit and real Chromium regressions for hidden text, adjacent blocks, inline content, and an unchanged source DOM. Fixes #52 --- content.js | 119 +++++++++++++++++++++++----- tests/content/extraction.test.js | 94 ++++++++++++++++++---- tests/e2e/extension-loading.spec.js | 39 +++++++++ 3 files changed, 217 insertions(+), 35 deletions(-) diff --git a/content.js b/content.js index 8bc4031..4ba2aec 100644 --- a/content.js +++ b/content.js @@ -12,8 +12,9 @@ class PageContentExtractor { chrome.runtime.onMessage.addListener((request, _sender, sendResponse) => { this.handleMessage(request) .then(data => sendResponse({ success: true, data })) - .catch(/** @param {any} error */ error => - sendResponse({ success: false, error: error.message }) + .catch( + /** @param {any} error */ error => + sendResponse({ success: false, error: error.message }) ); return true; // async sendResponse }); @@ -49,24 +50,34 @@ class PageContentExtractor { headings: this.extractHeadings(), metadata: this.getPageMetadata(), language: document.documentElement.lang || 'unknown', - timestamp: Date.now() + timestamp: Date.now(), }; } getPageTitle() { - return document.title || document.querySelector('h1')?.textContent?.trim() || 'Untitled Page'; + return ( + document.title || + document.querySelector('h1')?.textContent?.trim() || + 'Untitled Page' + ); } /** - * Take the most specific container that looks like article content, then strip - * chrome (nav, ads, comments) from a clone so the live page is never touched. + * Read the most specific article container without changing the live page. + * A detached clone loses CSS visibility and innerText's block boundaries. * * @returns {string} */ getMainText() { const selectors = [ - 'article', '[role="main"]', 'main', '.post-content', '.entry-content', - '#content', '.story-body', 'body' + 'article', + '[role="main"]', + 'main', + '.post-content', + '.entry-content', + '#content', + '.story-body', + 'body', ]; const mainElement = selectors .map(selector => document.querySelector(selector)) @@ -74,25 +85,91 @@ class PageContentExtractor { if (!mainElement) return ''; - const clone = /** @type {HTMLElement} */ (mainElement.cloneNode(true)); const unwanted = [ - 'script', 'style', 'noscript', 'nav', 'header', 'footer', 'aside', - '.ad', '.sidebar', '.comments', '[role="navigation"]', '[role="banner"]' - ]; - unwanted.forEach(selector => { - clone.querySelectorAll(selector).forEach(element => element.remove()); - }); + 'script', + 'style', + 'noscript', + 'nav', + 'header', + 'footer', + 'aside', + '.ad', + '.sidebar', + '.comments', + '[role="navigation"]', + '[role="banner"]', + ].join(','); + /** @type {Map} */ + const styles = new Map(); + /** @param {Element} element */ + const styleFor = element => { + let style = styles.get(element); + if (!style) { + style = window.getComputedStyle(element); + styles.set(element, style); + } + return style; + }; + // A selected article can itself be inside a non-rendered container. + for ( + let parent = mainElement.parentElement; + parent; + parent = parent.parentElement + ) { + const style = styleFor(parent); + if (style.display === 'none' || style.contentVisibility === 'hidden') + return ''; + } - // innerText (not textContent) so hidden elements stay out and block-level - // structure survives as line breaks. - return (clone.innerText || clone.textContent || '').trim(); + /** @type {string[]} */ + const parts = []; + const lineBreak = () => { + const last = parts.at(-1); + if (last?.endsWith('\n')) return; + if (last) parts[parts.length - 1] = last.trimEnd(); + parts.push('\n'); + }; + /** @param {Element} element @returns {void} */ + const visit = element => { + if (element.matches(unwanted)) return; + const style = styleFor(element); + if (style.display === 'none' || style.contentVisibility === 'hidden') + return; + + const visible = + style.visibility !== 'hidden' && style.visibility !== 'collapse'; + if (element.tagName === 'BR') { + if (visible) lineBreak(); + return; + } + const block = /^(block|flow-root|list-item|flex|grid|table)/.test( + style.display + ); + if (block) lineBreak(); + for (const child of element.childNodes) { + if (child.nodeType === Node.ELEMENT_NODE) { + // visibility can be explicitly restored by a child of a hidden parent. + visit(/** @type {Element} */ (child)); + } else if (child.nodeType === Node.TEXT_NODE && visible) { + let text = child.textContent || ''; + if (!/^(pre|break-spaces)/.test(style.whiteSpace)) { + text = text.replace(/\s+/g, ' '); + if (/[\s]$/.test(parts.at(-1) || '')) text = text.trimStart(); + } + if (text) parts.push(text); + } + } + if (block) lineBreak(); + }; + visit(mainElement); + return parts.join('').trim(); } extractHeadings() { return Array.from(document.querySelectorAll('h1, h2, h3, h4, h5, h6')) .map(heading => ({ level: Number(heading.tagName[1]), - text: heading.textContent?.trim() || '' + text: heading.textContent?.trim() || '', })) .filter(heading => heading.text); } @@ -121,7 +198,7 @@ class PageContentExtractor { return { href: link.href, text: link.textContent?.trim() || '', - isExternal: link.hostname !== window.location.hostname + isExternal: link.hostname !== window.location.hostname, }; }) .filter(link => link.href.startsWith('http')); @@ -133,7 +210,7 @@ class PageContentExtractor { src: image.src, alt: image.alt, width: image.naturalWidth, - height: image.naturalHeight + height: image.naturalHeight, })) .filter(image => image.src && !image.src.startsWith('data:')); } diff --git a/tests/content/extraction.test.js b/tests/content/extraction.test.js index ace7f27..a359a88 100644 --- a/tests/content/extraction.test.js +++ b/tests/content/extraction.test.js @@ -6,8 +6,8 @@ import { describe, it, expect, beforeAll, beforeEach } from 'vitest'; * Chrome does — import once for the side effect, capture the listener, and send * it messages — which is also how `tests/integration` drives `background.js`. * - * Note that jsdom does not implement `innerText`, so these exercise the - * `|| textContent` fallback in `getMainText()` rather than the innerText path. + * These test text traversal and CSS filtering, not layout. The real-Chromium + * extraction regression verifies visibility, block boundaries, and a stable DOM. */ /** @type {(request: any, sender: any, sendResponse: (r: any) => void) => boolean} */ @@ -38,7 +38,8 @@ beforeEach(() => { describe('extractContent', () => { it('returns the page title, url, and main text', async () => { - document.body.innerHTML = '

    The body of the article.

    '; + document.body.innerHTML = + '

    The body of the article.

    '; const { success, data } = await send({ action: 'extractContent' }); @@ -71,6 +72,58 @@ describe('extractContent', () => { expect(data.mainText).toContain('bare paragraph'); }); + it('separates adjacent blocks while preserving inline words and whitespace', async () => { + document.body.innerHTML = + '

    First visible paragraph.

    Second paragraph.

    Cooperate with inline text.

    '; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe( + 'First visible paragraph.\nSecond paragraph.\nCooperate with inline text.' + ); + }); + + it('omits non-rendered descendants and respects CSS block and inline display', async () => { + document.head.innerHTML = + ''; + document.body.innerHTML = + '
    First block.Second block.

    Inline

    text.
    '; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe('First block.\nSecond block.\nInline text.'); + }); + + it.each([ + '
    Hidden article.
    ', + '
    Hidden article.
    ', + '
    Hidden content.
    ', + ])('omits content in a non-rendered container: %s', async markup => { + document.body.innerHTML = markup; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe(''); + }); + + it('allows a descendant to restore visibility without including hidden siblings', async () => { + document.body.innerHTML = + '
    Hidden Visible child.Hidden sibling.
    Collapsed text.
    '; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe('Visible child.'); + }); + + it('keeps explicit line breaks and preformatted internal spacing', async () => { + document.body.innerHTML = + '

    First line.
    Second line.

    one  two\n  three
    '; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe('First line.\nSecond line.\none two\n three'); + }); + it('strips navigation, scripts, ads, and comments from the extracted text', async () => { document.body.innerHTML = `
    @@ -86,18 +139,26 @@ describe('extractContent', () => { const { data } = await send({ action: 'extractContent' }); expect(data.mainText).toContain('the actual article'); - for (const noise of ['skip to content', 'var tracking', 'buy this', 'first!', 'related links']) { + for (const noise of [ + 'skip to content', + 'var tracking', + 'buy this', + 'first!', + 'related links', + ]) { expect(data.mainText).not.toContain(noise); } }); - // The script is read-only by design; stripping happens on a clone so the page - // the user is looking at is never altered. - it('does not modify the live page while stripping', async () => { - document.body.innerHTML = '

    text

    '; + // The script is read-only by design; excluded nodes remain in the source DOM. + it('does not modify the live page while excluding navigation', async () => { + document.body.innerHTML = + '

    text

    '; + const before = document.documentElement.outerHTML; await send({ action: 'extractContent' }); + expect(document.documentElement.outerHTML).toBe(before); expect(document.getElementById('nav')).not.toBeNull(); }); @@ -108,7 +169,7 @@ describe('extractContent', () => { expect(data.headings).toEqual([ { level: 1, text: 'Title' }, - { level: 3, text: 'Sub' } + { level: 3, text: 'Sub' }, ]); }); @@ -139,7 +200,10 @@ describe('getPageMetadata', () => { const { data } = await send({ action: 'getPageMetadata' }); - expect(data).toMatchObject({ description: 'a description', 'og:title': 'social title' }); + expect(data).toMatchObject({ + description: 'a description', + 'og:title': 'social title', + }); expect(Object.values(data)).not.toContain('orphan with no name'); }); }); @@ -179,10 +243,12 @@ describe('extractImages', () => { const { data } = await send({ action: 'extractImages' }); - expect(data).toEqual([expect.objectContaining({ - src: 'https://example.com/a.png', - alt: 'A picture' - })]); + expect(data).toEqual([ + expect.objectContaining({ + src: 'https://example.com/a.png', + alt: 'A picture', + }), + ]); }); }); diff --git a/tests/e2e/extension-loading.spec.js b/tests/e2e/extension-loading.spec.js index cd4ab37..4e4fc2b 100644 --- a/tests/e2e/extension-loading.spec.js +++ b/tests/e2e/extension-loading.spec.js @@ -104,6 +104,45 @@ test.describe('options page', () => { }); test.describe('content extraction', () => { + test('extracts visible blocks and inline text without changing the source DOM', async ({ + context, + extensionId, + }) => { + const fixtureUrl = `${FIXTURE_URL}?visible-text`; + await context.route(fixtureUrl, route => + route.fulfill({ + contentType: 'text/html', + body: `

    First visible paragraph.

    Second visible paragraph.

    Inline text stays together.

    `, + }) + ); + const contentPage = await context.newPage(); + await contentPage.goto(fixtureUrl); + const before = await contentPage + .locator('html') + .evaluate(root => root.outerHTML); + + const extensionPage = await context.newPage(); + await extensionPage.goto(`chrome-extension://${extensionId}/options.html`); + await contentPage.bringToFront(); + const tabId = await activeTabId(extensionPage); + const response = await extensionPage.evaluate( + id => + chrome.runtime.sendMessage({ + actionType: 'EXTRACT_PAGE_CONTENT', + payload: { tabId: id }, + }), + tabId + ); + + expect(response.success).toBe(true); + expect(response.data.mainText).toBe( + 'First visible paragraph.\nSecond visible paragraph.\nInline text stays together.' + ); + expect( + await contentPage.locator('html').evaluate(root => root.outerHTML) + ).toBe(before); + }); + test('content script extracts article text and drops navigation chrome', async ({ context, extensionId, From cacbaa2287bf2a3d879ac3e55fe7554af4b06c07 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:16:51 +0530 Subject: [PATCH 10/14] fix(workspace): preserve current restored results Restore saved summaries independently of obsolete chat snapshots without replacing a fresh result. Ignore delayed source-link successes and errors after importing another document. Refs #47, #48 --- scripts/popup-main.js | 13 ++-- tests/popup/interface.test.js | 113 ++++++++++++++++++++++++++++++++++ 2 files changed, 121 insertions(+), 5 deletions(-) diff --git a/scripts/popup-main.js b/scripts/popup-main.js index 116936b..a55f4e3 100644 --- a/scripts/popup-main.js +++ b/scripts/popup-main.js @@ -265,8 +265,10 @@ class PopupInterface { url: content.url, content: content.mainText, }); - if (this.pageContent !== content || this.chatGeneration !== chatGeneration) - return; + if (this.pageContent !== content) return; + if (response.success && response.data.summaries?.[0] && !this.lastSummary) + this.renderSummary(response.data.summaries[0], true); + if (this.chatGeneration !== chatGeneration) return; this.chatHistoryPending = false; const conversations = response.success ? response.data.conversations || [] @@ -280,8 +282,6 @@ class PopupInterface { { role: 'assistant', content: exchange.answer } ); } - if (response.success && response.data.summaries?.[0]) - this.renderSummary(response.data.summaries[0], true); setText( 'chat-memory-status', !response.success @@ -539,6 +539,7 @@ class PopupInterface { } async extractPageLinks() { + const content = this.pageContent; if (!this.sourceTabId) { this.showToolsResult( '

    Links are available for a source web page. Imported text has no live page links.

    ' @@ -550,6 +551,7 @@ class PopupInterface { const response = await chrome.tabs.sendMessage(this.sourceTabId, { action: 'extractLinks', }); + if (this.pageContent !== content) return; if (!response?.success) throw new Error(response?.error || 'Could not read links'); @@ -565,7 +567,8 @@ class PopupInterface { } catch (error) { /** @type {any} */ const err = error; - this.showToolsResult(`

    ${escapeHtml(err.message)}

    `); + if (this.pageContent === content) + this.showToolsResult(`

    ${escapeHtml(err.message)}

    `); } } diff --git a/tests/popup/interface.test.js b/tests/popup/interface.test.js index 040053e..4a06261 100644 --- a/tests/popup/interface.test.js +++ b/tests/popup/interface.test.js @@ -132,6 +132,35 @@ describe('popup', () => { }); describe('learning workspace', () => { + it.each([true, false])( + 'ignores old source links after import (success=%s)', + async success => { + const pending = deferred(); + await openPopup(); + chrome.tabs.sendMessage.mockReturnValue(pending.promise); + el('extract-links-btn').click(); + await vi.waitFor(() => + expect(chrome.tabs.sendMessage).toHaveBeenCalledWith(7, { + action: 'extractLinks', + }) + ); + await importBook(); + pending.resolve( + success + ? { + success: true, + data: [ + { href: 'https://old.example/', text: 'Old source link' }, + ], + } + : { success: false, error: 'Old source error' } + ); + await settleResponses(); + expect(el('tools-results').style.display).toBe('none'); + expect(text('tools-results')).not.toMatch(/Old source/); + } + ); + it.each([true, false])( 'ignores a previous document analysis response after import (success=%s)', async success => { @@ -251,6 +280,90 @@ describe('popup', () => { ).not.toContain('Old'); }); + it.each(['send', 'clear'])( + 'restores the saved summary when %s invalidates only pending chat history', + async action => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { + GET_DOCUMENT_HISTORY: pending.promise, + CLEAR_DOCUMENT_CHAT: { success: true, data: { removed: 1 } }, + ANSWER_CONTEXTUAL_QUESTION: { + success: true, + data: { answer: 'Fresh answer' }, + }, + }, + }); + await vi.waitFor(() => + expect(sentPayload('GET_DOCUMENT_HISTORY')).toBeDefined() + ); + if (action === 'send') { + el('chat-input').value = 'Fresh question'; + el('send-chat-btn').click(); + await vi.waitFor(() => + expect(text('chat-history')).toContain('Fresh answer') + ); + } else { + el('clear-chat-btn').click(); + await vi.waitFor(() => + expect(text('chat-memory-status')).toContain('Chat cleared') + ); + } + pending.resolve({ + success: true, + data: { + summaries: [ + { summary: 'Saved summary', provider: 'test', model: 'fixture' }, + ], + conversations: [{ question: 'Old question', answer: 'Old answer' }], + memoryEnabled: true, + }, + }); + await vi.waitFor(() => + expect(text('summary-content')).toContain('Saved summary') + ); + expect(text('chat-history')).not.toContain('Old answer'); + } + ); + + it('keeps a freshly generated summary when an older history snapshot arrives', async () => { + const pending = deferred(); + await openPopup({ + waitForReady: false, + overrides: { + GET_DOCUMENT_HISTORY: pending.promise, + GENERATE_CONTENT_SUMMARY: { + success: true, + data: { + summary: 'Fresh summary', + provider: 'test', + model: 'fixture', + }, + }, + }, + }); + await vi.waitFor(() => + expect(sentPayload('GET_DOCUMENT_HISTORY')).toBeDefined() + ); + el('generate-summary-btn').click(); + await vi.waitFor(() => + expect(text('summary-content')).toContain('Fresh summary') + ); + pending.resolve({ + success: true, + data: { + summaries: [ + { summary: 'Old summary', provider: 'test', model: 'fixture' }, + ], + conversations: [], + memoryEnabled: true, + }, + }); + await settleResponses(); + expect(text('summary-content')).toBe('Fresh summary'); + }); + it('allows pending saved history to restore after clearing fails', async () => { const pending = deferred(); await openPopup({ From d2551ac1f1a611895f39bda22b1be24017984771 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:16:54 +0530 Subject: [PATCH 11/14] fix(settings): serialize writes in the worker Route options updates and confirmed resets through the worker so every UI shares one preferences writer. Queue initialization, updates, and replacement writes without allowing a failure to block later edits. Cover overlapping top-level and nested edits, initialization/reset ordering, read/write recovery, and real two-context browser persistence. Inject failed save reads in the worker while preserving page-load errors. Fixes #46 --- background.js | 5 + core/configuration-manager.js | 55 +++++-- options.js | 19 ++- tests/core/configuration-manager.test.js | 155 +++++++++++++++++++ tests/e2e/settings-concurrency.spec.js | 124 +++++++++++++++ tests/e2e/settings-failure.spec.js | 3 +- tests/integration/extension-workflow.test.js | 78 ++++++++++ tests/options/settings.test.js | 37 ++++- 8 files changed, 447 insertions(+), 29 deletions(-) create mode 100644 tests/e2e/settings-concurrency.spec.js diff --git a/background.js b/background.js index b4566ec..62db309 100644 --- a/background.js +++ b/background.js @@ -232,6 +232,11 @@ class BackgroundService { case 'UPDATE_USER_PREFERENCES': return this.configManager.updateUserPreferences(payload); + case 'RESET_USER_PREFERENCES': + return this.configManager.setUserPreferences( + this.configManager.defaultSettings + ); + case 'GET_PROVIDER_STATUS': return this.configManager.getProviderStatus(); diff --git a/core/configuration-manager.js b/core/configuration-manager.js index a89c10d..dcc8070 100644 --- a/core/configuration-manager.js +++ b/core/configuration-manager.js @@ -25,6 +25,8 @@ const STORAGE_KEY = 'user_preferences'; */ export class ConfigurationManager { constructor() { + /** @type {Promise} */ + this.writes = Promise.resolve(); this.defaultSettings = { preferredProvider: 'anthropic', /** @type {Record} */ @@ -53,10 +55,12 @@ export class ConfigurationManager { * distinguish a first run from a later one. */ async initialize() { - const stored = await chrome.storage.sync.get([STORAGE_KEY]); - if (!stored[STORAGE_KEY]) { - await this.setUserPreferences(this.defaultSettings); - } + return this.serializeWrite(async () => { + const stored = await chrome.storage.sync.get([STORAGE_KEY]); + if (!stored[STORAGE_KEY]) { + await chrome.storage.sync.set({ [STORAGE_KEY]: this.defaultSettings }); + } + }); } /** @@ -102,22 +106,41 @@ export class ConfigurationManager { * @returns {Promise} */ async updateUserPreferences(newSettings) { - const current = await this.getUserPreferences(); - const updated = { - ...current, - ...newSettings, - apiKeys: { ...current.apiKeys, ...(newSettings.apiKeys || {}) }, - models: { ...current.models, ...(newSettings.models || {}) }, - features: { ...current.features, ...(newSettings.features || {}) }, - updatedAt: Date.now(), - }; - await chrome.storage.sync.set({ [STORAGE_KEY]: updated }); - return updated; + return this.serializeWrite(async () => { + const current = await this.getUserPreferences(); + const updated = { + ...current, + ...newSettings, + apiKeys: { ...current.apiKeys, ...(newSettings.apiKeys || {}) }, + models: { ...current.models, ...(newSettings.models || {}) }, + features: { ...current.features, ...(newSettings.features || {}) }, + updatedAt: Date.now(), + }; + await chrome.storage.sync.set({ [STORAGE_KEY]: updated }); + return updated; + }); } /** @param {any} settings */ async setUserPreferences(settings) { - await chrome.storage.sync.set({ [STORAGE_KEY]: settings }); + return this.serializeWrite(async () => { + await chrome.storage.sync.set({ [STORAGE_KEY]: settings }); + return this.withDefaults(settings); + }); + } + + /** + * All settings writes run through the worker's manager. Read-modify-write, + * first-run seeding, and replacement/reset must share the same ordering. + * A failed operation reaches its caller without blocking later writes. + * @template T + * @param {() => Promise} write + * @returns {Promise} + */ + serializeWrite(write) { + const operation = this.writes.then(write, write); + this.writes = operation; + return operation; } /** diff --git a/options.js b/options.js index 77f7868..cef1622 100644 --- a/options.js +++ b/options.js @@ -137,7 +137,12 @@ class OptionsPage { if (!update) return; try { - this.settings = await this.configManager.updateUserPreferences(update); + const response = await chrome.runtime.sendMessage({ + actionType: 'UPDATE_USER_PREFERENCES', + payload: update, + }); + if (!response.success) throw new Error(response.error); + this.settings = response.data; this.showSaveIndicator(); } catch (error) { /** @type {any} */ @@ -149,12 +154,12 @@ class OptionsPage { async resetSettings() { if (!confirm('Reset all settings, including saved API keys?')) return; try { - await this.configManager.setUserPreferences( - this.configManager.defaultSettings - ); - this.settings = this.configManager.withDefaults( - this.configManager.defaultSettings - ); + const response = await chrome.runtime.sendMessage({ + actionType: 'RESET_USER_PREFERENCES', + payload: {}, + }); + if (!response.success) throw new Error(response.error); + this.settings = response.data; this.render(); this.showSaveIndicator('Settings reset to defaults'); } catch { diff --git a/tests/core/configuration-manager.test.js b/tests/core/configuration-manager.test.js index db92b12..db73c0b 100644 --- a/tests/core/configuration-manager.test.js +++ b/tests/core/configuration-manager.test.js @@ -2,6 +2,20 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { ConfigurationManager } from '../../core/configuration-manager.js'; import { AIError } from '../../providers/ai-client.js'; +/** Back reads and writes with cloned data, as Chrome storage does. */ +function givenStorage(preferences) { + const stored = preferences + ? { user_preferences: structuredClone(preferences) } + : {}; + chrome.storage.sync.get.mockImplementation(async () => + structuredClone(stored) + ); + chrome.storage.sync.set.mockImplementation(async values => { + Object.assign(stored, structuredClone(values)); + }); + return stored; +} + describe('ConfigurationManager', () => { /** @type {ConfigurationManager} */ let configManager; @@ -10,6 +24,147 @@ describe('ConfigurationManager', () => { configManager = new ConfigurationManager(); vi.clearAllMocks(); chrome.storage.sync.get.mockResolvedValue({}); + chrome.storage.sync.set.mockReset().mockResolvedValue(undefined); + }); + + describe('serialized settings writes', () => { + it('keeps overlapping top-level and nested edits', async () => { + const stored = givenStorage({ apiKeys: { anthropic: 'fixture-key' } }); + await Promise.all([ + configManager.updateUserPreferences({ theme: 'dark' }), + configManager.updateUserPreferences({ summaryType: 'tldr' }), + configManager.updateUserPreferences({ + models: { openai: 'fixture-openai' }, + }), + configManager.updateUserPreferences({ + models: { gemini: 'fixture-gemini' }, + }), + configManager.updateUserPreferences({ + features: { notifications: false }, + }), + configManager.updateUserPreferences({ + features: { saveHistory: false }, + }), + configManager.updateUserPreferences({ + apiKeys: { openai: 'fixture-other-key' }, + }), + ]); + expect(stored.user_preferences).toMatchObject({ + theme: 'dark', + summaryType: 'tldr', + models: { openai: 'fixture-openai', gemini: 'fixture-gemini' }, + features: { notifications: false, saveHistory: false }, + apiKeys: { anthropic: 'fixture-key', openai: 'fixture-other-key' }, + }); + }); + + it('finishes first-run initialization before applying an overlapping edit', async () => { + const stored = givenStorage(); + let finishRead; + chrome.storage.sync.get.mockImplementationOnce( + () => + new Promise(resolve => { + finishRead = () => resolve({}); + }) + ); + const initialization = configManager.initialize(); + await vi.waitFor(() => + expect(chrome.storage.sync.get).toHaveBeenCalledOnce() + ); + const update = configManager.updateUserPreferences({ theme: 'dark' }); + await new Promise(resolve => setTimeout(resolve, 0)); + finishRead(); + await Promise.all([initialization, update]); + expect(stored.user_preferences.theme).toBe('dark'); + }); + + it('orders a reset between overlapping edits without restoring cleared keys', async () => { + const original = { + apiKeys: { openai: 'fixture-old-key' }, + theme: 'light', + }; + const stored = givenStorage(original); + let finishRead; + chrome.storage.sync.get.mockImplementationOnce( + () => + new Promise(resolve => { + finishRead = () => + resolve({ user_preferences: structuredClone(original) }); + }) + ); + const first = configManager.updateUserPreferences({ + features: { notifications: false }, + }); + await vi.waitFor(() => + expect(chrome.storage.sync.get).toHaveBeenCalledOnce() + ); + const reset = configManager.setUserPreferences( + configManager.defaultSettings + ); + const last = configManager.updateUserPreferences({ theme: 'dark' }); + await new Promise(resolve => setTimeout(resolve, 0)); + finishRead(); + await Promise.all([first, reset, last]); + expect(stored.user_preferences).toMatchObject({ + apiKeys: {}, + theme: 'dark', + features: { notifications: true }, + }); + expect(stored.user_preferences.apiKeys).toEqual({}); + }); + + it.each(['read', 'write'])( + 'recovers queued edits after a failed %s', + async failure => { + const stored = givenStorage({ theme: 'light' }); + if (failure === 'read') + chrome.storage.sync.get.mockRejectedValueOnce( + new Error('fixture failure') + ); + else + chrome.storage.sync.set.mockRejectedValueOnce( + new Error('fixture failure') + ); + const first = configManager.updateUserPreferences({ + summaryType: 'tldr', + }); + const rejected = expect(first).rejects.toMatchObject({ + message: + failure === 'read' + ? 'Saved settings are unavailable. Try again.' + : 'fixture failure', + }); + const later = configManager.updateUserPreferences({ theme: 'dark' }); + await Promise.all([rejected, later]); + expect(stored.user_preferences.theme).toBe('dark'); + expect(stored.user_preferences.summaryType).toBe('key-points'); + } + ); + + it('recovers queued edits after a failed initialization or replacement', async () => { + const stored = givenStorage({ apiKeys: { openai: 'fixture-key' } }); + chrome.storage.sync.get.mockRejectedValueOnce( + new Error('initialization failed') + ); + const initialization = configManager.initialize(); + const rejectedInitialization = expect(initialization).rejects.toThrow( + 'initialization failed' + ); + chrome.storage.sync.set.mockRejectedValueOnce( + new Error('replacement failed') + ); + const replacement = configManager.setUserPreferences( + configManager.defaultSettings + ); + const rejectedReplacement = + expect(replacement).rejects.toThrow('replacement failed'); + const later = configManager.updateUserPreferences({ theme: 'dark' }); + await Promise.all([rejectedInitialization, rejectedReplacement, later]); + expect(stored.user_preferences).toMatchObject({ + theme: 'dark', + apiKeys: { openai: 'fixture-key' }, + }); + }); }); describe('defaults', () => { diff --git a/tests/e2e/settings-concurrency.spec.js b/tests/e2e/settings-concurrency.spec.js new file mode 100644 index 0000000..81dcbb7 --- /dev/null +++ b/tests/e2e/settings-concurrency.spec.js @@ -0,0 +1,124 @@ +import { + test, + expect, + CONFIGURED_PREFERENCES, + seedPreferences, +} from './fixtures.js'; + +/** Hold writes without replacing Chrome's reads or final persistence. */ +async function holdWrites(surface) { + await surface.evaluate(() => { + const write = chrome.storage.sync.set.bind(chrome.storage.sync); + let release; + const gate = new Promise(resolve => { + release = resolve; + }); + globalThis.settingsWrites = { started: 0, release }; + chrome.storage.sync.set = async values => { + globalThis.settingsWrites.started++; + await gate; + return write(values); + }; + }); +} + +for (const scenario of [ + { + name: 'top-level and feature edits', + changes: [ + { id: 'summary-type-pref', value: 'tldr' }, + { id: 'notifications', checked: false }, + ], + expected: { + summaryType: 'tldr', + features: { notifications: false, saveHistory: false }, + }, + }, + { + name: 'two entries in the same nested map', + changes: [ + { id: 'anthropic-model', value: 'fixture-anthropic' }, + { id: 'openai-model', value: 'fixture-openai' }, + ], + expected: { + models: { anthropic: 'fixture-anthropic', openai: 'fixture-openai' }, + }, + }, +]) { + test(`two extension contexts preserve overlapping ${scenario.name}`, async ({ + context, + extensionId, + }) => { + const first = await context.newPage(); + const second = await context.newPage(); + const url = `chrome-extension://${extensionId}/options.html`; + await first.goto(url); + const preferences = { + ...CONFIGURED_PREFERENCES, + features: { + contextMenus: false, + notifications: true, + saveHistory: false, + }, + }; + await seedPreferences(first, preferences); + await Promise.all([first.reload(), second.goto(url)]); + for (const page of [first, second]) { + await expect(page.locator('#anthropic-key')).toHaveValue( + preferences.apiKeys.anthropic + ); + } + const worker = context.serviceWorkers()[0]; + const surfaces = [first, second, worker]; + await Promise.all(surfaces.map(holdWrites)); + + try { + await Promise.all( + [first, second].map((page, index) => + page.evaluate(change => { + const input = document.getElementById(change.id); + if ('checked' in change) input.checked = change.checked; + else input.value = change.value; + input.dispatchEvent(new Event('change', { bubbles: true })); + }, scenario.changes[index]) + ) + ); + // With legacy page writers, both stale snapshots reach the held writes. + // With one worker writer, only its first write can enter before release. + await expect + .poll(async () => { + const [pageA, pageB, background] = await Promise.all( + surfaces.map(surface => + surface.evaluate(() => globalThis.settingsWrites.started) + ) + ); + return background > 0 || pageA + pageB === 2; + }) + .toBe(true); + } finally { + await Promise.all( + surfaces.map(surface => + surface.evaluate(() => globalThis.settingsWrites.release()) + ) + ); + } + + for (const page of [first, second]) { + await expect(page.locator('#save-indicator .text')).toHaveText( + 'Settings saved' + ); + } + const saved = await first.evaluate( + async () => + (await chrome.storage.sync.get('user_preferences')).user_preferences + ); + expect(saved).toMatchObject(scenario.expected); + expect(saved.apiKeys).toEqual(preferences.apiKeys); + const counts = await Promise.all( + surfaces.map(surface => + surface.evaluate(() => globalThis.settingsWrites.started) + ) + ); + expect(counts).toEqual([0, 0, 2]); + }); +} diff --git a/tests/e2e/settings-failure.spec.js b/tests/e2e/settings-failure.spec.js index a82b046..b2aa3b1 100644 --- a/tests/e2e/settings-failure.spec.js +++ b/tests/e2e/settings-failure.spec.js @@ -20,7 +20,8 @@ test('failed option reads preserve stored settings, report errors, and recover o }; await seedPreferences(page, prefs); await page.reload(); - await page.evaluate(() => { + const worker = context.serviceWorkers()[0]; + await worker.evaluate(() => { const read = chrome.storage.sync.get.bind(chrome.storage.sync); chrome.storage.sync.get = () => { chrome.storage.sync.get = read; diff --git a/tests/integration/extension-workflow.test.js b/tests/integration/extension-workflow.test.js index d331629..59f322b 100644 --- a/tests/integration/extension-workflow.test.js +++ b/tests/integration/extension-workflow.test.js @@ -1010,6 +1010,84 @@ describe('Extension workflow', () => { }); }); + describe('settings writes', () => { + let stored; + beforeEach(() => { + stored = structuredClone(CONFIGURED); + chrome.storage.sync.get.mockImplementation(async () => + structuredClone(stored) + ); + chrome.storage.sync.set.mockImplementation(async values => { + Object.assign(stored, structuredClone(values)); + }); + }); + + it('preserves overlapping edits received by the shared worker writer', async () => { + const results = await Promise.all([ + dispatch('UPDATE_USER_PREFERENCES', { theme: 'dark' }), + dispatch('UPDATE_USER_PREFERENCES', { + features: { notifications: false }, + }), + dispatch('UPDATE_USER_PREFERENCES', { + features: { saveHistory: false }, + }), + dispatch('UPDATE_USER_PREFERENCES', { + models: { openai: 'fixture-model' }, + }), + ]); + expect(results.every(response => response.success)).toBe(true); + expect(stored.user_preferences).toMatchObject({ + theme: 'dark', + features: { notifications: false, saveHistory: false }, + models: { openai: 'fixture-model' }, + apiKeys: CONFIGURED.user_preferences.apiKeys, + }); + }); + + it('replaces preferences on explicit reset without requiring another read', async () => { + chrome.storage.sync.get.mockRejectedValue( + new Error('storage unavailable') + ); + const result = await dispatch('RESET_USER_PREFERENCES', {}); + expect(result.success).toBe(true); + expect(result.data.apiKeys).toEqual({}); + expect(result.data.preferredProvider).toBe('anthropic'); + expect(stored.user_preferences.apiKeys).toEqual({}); + expect(stored.user_preferences.models).toEqual({}); + }); + + it('orders reset before a later edit and recovers after a failed reset', async () => { + const [reset, update] = await Promise.all([ + dispatch('RESET_USER_PREFERENCES', {}), + dispatch('UPDATE_USER_PREFERENCES', { theme: 'dark' }), + ]); + expect(reset.success).toBe(true); + expect(update.success).toBe(true); + expect(stored.user_preferences).toMatchObject({ + apiKeys: {}, + theme: 'dark', + }); + expect(stored.user_preferences.apiKeys).toEqual({}); + + chrome.storage.sync.set.mockRejectedValueOnce( + new Error('fixture write failure') + ); + const [failedReset, later] = await Promise.all([ + dispatch('RESET_USER_PREFERENCES', {}), + dispatch('UPDATE_USER_PREFERENCES', { summaryType: 'tldr' }), + ]); + expect(failedReset).toMatchObject({ + success: false, + error: 'fixture write failure', + }); + expect(later.success).toBe(true); + expect(stored.user_preferences).toMatchObject({ + theme: 'dark', + summaryType: 'tldr', + }); + }); + }); + describe('worker lifecycle', () => { it('preserves the cleanup alarm scheduled time across worker restarts', async () => { const scheduledTime = Date.now() + 30 * 60 * 1000; diff --git a/tests/options/settings.test.js b/tests/options/settings.test.js index ed3d577..6d50902 100644 --- a/tests/options/settings.test.js +++ b/tests/options/settings.test.js @@ -2,8 +2,8 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { readFileSync } from 'node:fs'; /** - * The options page is the only writer of `user_preferences`. What matters is - * that every control maps to the field the background service later reads — + * Options controls send settings edits to the worker's shared writer. Every + * control must map to the field the background service later reads — * a mismatch here is invisible in the UI and shows up as "changing the setting * does nothing", which is exactly the class of bug 5.1.0 had to fix. * @@ -55,6 +55,14 @@ function lastSaved() { return calls.at(-1)[0].user_preferences; } +/** Route extension messages through the shipped worker. */ +function forwardToWorker(message) { + const handler = chrome.runtime.onMessage.addListener.mock.calls.at(-1)[0]; + return new Promise(resolve => { + handler(message, { id: 'mock-extension-id' }, resolve); + }); +} + /** * @param {string} id * @param {string} value @@ -68,11 +76,19 @@ function change(id, value) { /** @param {Record} stored */ async function loadPage(stored = {}) { document.body.innerHTML = OPTIONS_MARKUP; - chrome.storage.sync.get.mockResolvedValue( - Object.keys(stored).length ? { user_preferences: stored } : {} + const contents = Object.keys(stored).length + ? { user_preferences: structuredClone(stored) } + : {}; + chrome.storage.sync.get.mockImplementation(async () => + structuredClone(contents) ); + chrome.storage.sync.set.mockImplementation(async values => { + Object.assign(contents, structuredClone(values)); + }); vi.resetModules(); + await import('../../background.js'); + chrome.runtime.sendMessage.mockImplementation(forwardToWorker); await import('../../options.js'); document.dispatchEvent(new Event('DOMContentLoaded')); @@ -147,6 +163,7 @@ describe('options page', () => { ).toMatch(/Failed to fetch/) ); expect(chrome.runtime.sendMessage).toHaveBeenCalledTimes(1); + chrome.runtime.sendMessage.mockImplementation(forwardToWorker); change('anthropic-key', 'replacement-key'); await vi.waitFor(() => expect(lastSaved().apiKeys.anthropic).toBe('replacement-key') @@ -161,7 +178,8 @@ describe('options page', () => { }); beforeEach(() => { vi.clearAllMocks(); - chrome.storage.sync.set.mockResolvedValue(undefined); + chrome.storage.sync.set.mockReset().mockResolvedValue(undefined); + chrome.runtime.sendMessage.mockReset(); }); describe('rendering saved settings', () => { @@ -220,6 +238,10 @@ describe('options page', () => { expect(chrome.storage.sync.set).toHaveBeenCalled() ); expect(lastSaved().apiKeys.openai).toBe('sk-typed'); + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + actionType: 'UPDATE_USER_PREFERENCES', + payload: { apiKeys: { openai: 'sk-typed' } }, + }); }); it('stores a model override under models.', async () => { @@ -324,6 +346,7 @@ describe('options page', () => { await Promise.resolve(); expect(chrome.storage.sync.set).not.toHaveBeenCalled(); + expect(chrome.runtime.sendMessage).not.toHaveBeenCalled(); }); it('clears stored keys once confirmed', async () => { @@ -337,6 +360,10 @@ describe('options page', () => { ); expect(lastSaved().apiKeys).toEqual({}); expect(lastSaved().preferredProvider).toBe('anthropic'); + expect(chrome.runtime.sendMessage).toHaveBeenCalledWith({ + actionType: 'RESET_USER_PREFERENCES', + payload: {}, + }); }); it('finishes a confirmed reset even if subsequent reads are unavailable', async () => { From 21c2bbe224dcca352408b09a54f22e8f0e1f695d Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:20:43 +0530 Subject: [PATCH 12/14] fix(settings): save only changed language fields Keep a stale popup from replacing the sibling language saved in Settings. Preserve the intentional two-field swap, with unit payload and native storage browser regressions. Refs #46 --- scripts/popup-main.js | 21 +++++++++---- tests/e2e/settings-concurrency.spec.js | 41 ++++++++++++++++++++++++++ tests/popup/interface.test.js | 28 ++++++++++++++---- 3 files changed, 80 insertions(+), 10 deletions(-) diff --git a/scripts/popup-main.js b/scripts/popup-main.js index a55f4e3..0f77a7d 100644 --- a/scripts/popup-main.js +++ b/scripts/popup-main.js @@ -152,8 +152,12 @@ class PopupInterface { on('translate-btn', 'click', () => this.translateContent()); on('swap-languages', 'click', () => this.swapLanguages()); - on('source-language', 'change', () => this.persistLanguages()); - on('target-language', 'change', () => this.persistLanguages()); + on('source-language', 'change', () => + this.persistLanguages('sourceLanguage') + ); + on('target-language', 'change', () => + this.persistLanguages('targetLanguage') + ); on('copy-translation-btn', 'click', () => this.copyToClipboard('translation-content') ); @@ -342,11 +346,18 @@ class PopupInterface { }); } - async persistLanguages() { - const response = await this.send('UPDATE_USER_PREFERENCES', { + /** @param {'sourceLanguage' | 'targetLanguage'} [changed] */ + async persistLanguages(changed) { + const languages = { targetLanguage: value('target-language'), sourceLanguage: value('source-language'), - }); + }; + // A single control change must not overwrite a sibling edited in Settings. + // An explicit swap changes both controls and therefore persists both. + const response = await this.send( + 'UPDATE_USER_PREFERENCES', + changed ? { [changed]: languages[changed] } : languages + ); if (!response.success) this.showToast(response.error, 'error'); } diff --git a/tests/e2e/settings-concurrency.spec.js b/tests/e2e/settings-concurrency.spec.js index 81dcbb7..9ec0925 100644 --- a/tests/e2e/settings-concurrency.spec.js +++ b/tests/e2e/settings-concurrency.spec.js @@ -5,6 +5,47 @@ import { seedPreferences, } from './fixtures.js'; +test('a stale popup language control preserves a newer Settings edit', async ({ + context, + extensionId, +}) => { + const options = await context.newPage(); + await options.goto(`chrome-extension://${extensionId}/options.html`); + await seedPreferences(options, { + ...CONFIGURED_PREFERENCES, + sourceLanguage: 'auto', + targetLanguage: 'en', + }); + await options.reload(); + const popup = await context.newPage(); + await popup.goto(`chrome-extension://${extensionId}/popup.html?workspace=1`); + await popup.locator('[data-tab="translate"]').click(); + await expect(popup.locator('#source-language')).toHaveValue('auto'); + await options.locator('[data-section="features"]').click(); + await options.locator('#source-language-pref').selectOption('fr'); + await expect(options.locator('#save-indicator .text')).toHaveText( + 'Settings saved' + ); + await popup.locator('#target-language').selectOption('es'); + await expect + .poll( + async () => + ( + await popup.evaluate( + async () => + (await chrome.storage.sync.get('user_preferences')) + .user_preferences + ) + ).targetLanguage + ) + .toBe('es'); + const saved = await options.evaluate( + async () => + (await chrome.storage.sync.get('user_preferences')).user_preferences + ); + expect(saved).toMatchObject({ sourceLanguage: 'fr', targetLanguage: 'es' }); +}); + /** Hold writes without replacing Chrome's reads or final persistence. */ async function holdWrites(surface) { await surface.evaluate(() => { diff --git a/tests/popup/interface.test.js b/tests/popup/interface.test.js index 4a06261..43cf3c8 100644 --- a/tests/popup/interface.test.js +++ b/tests/popup/interface.test.js @@ -718,16 +718,34 @@ describe('popup', () => { expect(chrome.tabs.sendMessage).not.toHaveBeenCalled(); }); - it('persists translation preferences on change', async () => { + it.each([ + ['target-language', 'targetLanguage'], + ['source-language', 'sourceLanguage'], + ])( + 'persists only the changed translation preference: %s', + async (id, field) => { + await openPopup({ + overrides: { UPDATE_USER_PREFERENCES: { success: true } }, + }); + el(id).value = 'fr'; + el(id).dispatchEvent(new Event('change')); + await vi.waitFor(() => + expect(sentPayload('UPDATE_USER_PREFERENCES')).toEqual({ + [field]: 'fr', + }) + ); + } + ); + + it('persists both languages after an explicit swap', async () => { await openPopup({ - overrides: { UPDATE_USER_PREFERENCES: { success: true } }, + preferences: { sourceLanguage: 'fr', targetLanguage: 'en' }, }); - el('target-language').value = 'fr'; - el('target-language').dispatchEvent(new Event('change')); + el('swap-languages').click(); await vi.waitFor(() => expect(sentPayload('UPDATE_USER_PREFERENCES')).toEqual({ + sourceLanguage: 'en', targetLanguage: 'fr', - sourceLanguage: 'auto', }) ); }); From 9c3db4b6c3793bc756d4ec6d2211dd5c520419c5 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 11:20:31 +0530 Subject: [PATCH 13/14] fix(content): respect collapsed disclosures Read only the first direct summary of collapsed details and reject selected article containers hidden by a collapsed ancestor. Keep content inside a visible first summary readable without changing the source DOM. Preserve blank lines around rendered paragraphs so summary chunking keeps whole paragraphs. Add red-first unit and Chromium details regressions and an actual extraction-to-chunkContent browser check. Refs #52 --- content.js | 25 ++++-- tests/content/extraction.test.js | 55 +++++++++++++- tests/e2e/extension-loading.spec.js | 114 +++++++++++++++++++++++++++- 3 files changed, 185 insertions(+), 9 deletions(-) diff --git a/content.js b/content.js index 4ba2aec..e218692 100644 --- a/content.js +++ b/content.js @@ -119,15 +119,19 @@ class PageContentExtractor { const style = styleFor(parent); if (style.display === 'none' || style.contentVisibility === 'hidden') return ''; + if (parent.tagName === 'DETAILS' && !parent.hasAttribute('open')) { + const summary = parent.querySelector(':scope > summary'); + if (!summary?.contains(mainElement)) return ''; + } } /** @type {string[]} */ const parts = []; - const lineBreak = () => { + const lineBreak = (count = 1) => { const last = parts.at(-1); - if (last?.endsWith('\n')) return; + if (last?.endsWith('\n'.repeat(count))) return; if (last) parts[parts.length - 1] = last.trimEnd(); - parts.push('\n'); + parts.push('\n'.repeat(count)); }; /** @param {Element} element @returns {void} */ const visit = element => { @@ -145,8 +149,17 @@ class PageContentExtractor { const block = /^(block|flow-root|list-item|flex|grid|table)/.test( style.display ); - if (block) lineBreak(); - for (const child of element.childNodes) { + // Blank lines retain the paragraph boundaries consumed by summary chunking. + const breaks = element.tagName === 'P' ? 2 : 1; + if (block) lineBreak(breaks); + // Closed details render only their first direct summary, not the body. + const collapsed = + element.tagName === 'DETAILS' && !element.hasAttribute('open'); + const children = collapsed + ? [element.querySelector(':scope > summary')] + : element.childNodes; + for (const child of children) { + if (!child) continue; if (child.nodeType === Node.ELEMENT_NODE) { // visibility can be explicitly restored by a child of a hidden parent. visit(/** @type {Element} */ (child)); @@ -159,7 +172,7 @@ class PageContentExtractor { if (text) parts.push(text); } } - if (block) lineBreak(); + if (block) lineBreak(breaks); }; visit(mainElement); return parts.join('').trim(); diff --git a/tests/content/extraction.test.js b/tests/content/extraction.test.js index a359a88..e25b4f6 100644 --- a/tests/content/extraction.test.js +++ b/tests/content/extraction.test.js @@ -79,7 +79,7 @@ describe('extractContent', () => { const { data } = await send({ action: 'extractContent' }); expect(data.mainText).toBe( - 'First visible paragraph.\nSecond paragraph.\nCooperate with inline text.' + 'First visible paragraph.\n\nSecond paragraph.\n\nCooperate with inline text.' ); }); @@ -121,7 +121,58 @@ describe('extractContent', () => { const { data } = await send({ action: 'extractContent' }); - expect(data.mainText).toBe('First line.\nSecond line.\none two\n three'); + expect(data.mainText).toBe( + 'First line.\nSecond line.\n\none two\n three' + ); + }); + + it.each([ + { + label: 'closed, open, and nested details', + markup: + '
    Closed summary.

    Hidden closed body.

    Hidden second summary.
    Hidden nested summary.

    Hidden nested body.

    Open summary.

    Open body.

    Nested closed summary.

    Hidden nested closed body.

    ', + expected: + 'Closed summary.\nOpen summary.\n\nOpen body.\n\nNested closed summary.', + }, + { + label: 'a selected article inside a closed details body', + markup: + '
    Visible summary.
    Hidden selected article.
    ', + expected: '', + }, + { + label: 'a selected article inside the first summary', + markup: + '
    Visible selected article.

    Hidden body.

    ', + expected: 'Visible selected article.', + }, + { + label: 'a selected article inside a later summary', + markup: + '
    First summary.
    Hidden selected article.
    ', + expected: '', + }, + { + label: + 'a selected article inside nested open details under a closed ancestor', + markup: + '
    Outer summary.
    Inner summary.
    Hidden selected article.
    ', + expected: '', + }, + { + label: 'closed details with no summary', + markup: + '
    Hidden text.

    Hidden body.

    ', + expected: '', + }, + ])('respects $label', async ({ markup, expected }) => { + document.body.innerHTML = markup; + const before = document.documentElement.outerHTML; + + const { data } = await send({ action: 'extractContent' }); + + expect(data.mainText).toBe(expected); + expect(document.documentElement.outerHTML).toBe(before); }); it('strips navigation, scripts, ads, and comments from the extracted text', async () => { diff --git a/tests/e2e/extension-loading.spec.js b/tests/e2e/extension-loading.spec.js index 4e4fc2b..40be910 100644 --- a/tests/e2e/extension-loading.spec.js +++ b/tests/e2e/extension-loading.spec.js @@ -6,6 +6,7 @@ import { seedPreferences, activeTabId, } from './fixtures.js'; +import { chunkContent } from '../../core/tasks.js'; /** * Real-browser checks: this repository is loaded into Chrome as an unpacked @@ -104,6 +105,117 @@ test.describe('options page', () => { }); test.describe('content extraction', () => { + test('keeps extracted paragraphs intact across summary chunk boundaries', async ({ + context, + extensionId, + }) => { + const paragraphs = Array.from( + { length: 30 }, + (_, index) => + `Paragraph ${index}. ${'word '.repeat(230)}End paragraph ${index}.` + ); + const fixtureUrl = `${FIXTURE_URL}?paragraph-chunks`; + await context.route(fixtureUrl, route => + route.fulfill({ + contentType: 'text/html', + body: `
    ${paragraphs.map(text => `

    ${text}

    `).join('\n\n')}
    `, + }) + ); + const contentPage = await context.newPage(); + await contentPage.goto(fixtureUrl); + const before = await contentPage + .locator('html') + .evaluate(root => root.outerHTML); + const extensionPage = await context.newPage(); + await extensionPage.goto(`chrome-extension://${extensionId}/options.html`); + await contentPage.bringToFront(); + const tabId = await activeTabId(extensionPage); + const response = await extensionPage.evaluate( + id => + chrome.runtime.sendMessage({ + actionType: 'EXTRACT_PAGE_CONTENT', + payload: { tabId: id }, + }), + tabId + ); + + expect(response.success).toBe(true); + const { chunks, droppedChars } = chunkContent(response.data.mainText); + expect(chunks).toHaveLength(2); + expect(droppedChars).toBe(0); + for (const chunk of chunks) { + expect(chunk.slice(0, 60)).toMatch(/^Paragraph \d+\./); + expect(chunk.slice(-80)).toMatch(/End paragraph \d+\.$/); + } + expect(chunks.join('\n\n')).toBe(paragraphs.join('\n\n')); + expect( + await contentPage.locator('html').evaluate(root => root.outerHTML) + ).toBe(before); + }); + + for (const { label, body, expected } of [ + { + label: 'closed, open, and nested details', + body: '
    Closed summary.

    Hidden closed body.

    Hidden second summary.
    Hidden nested summary.

    Hidden nested body.

    Open summary.

    Open body.

    Nested closed summary.

    Hidden nested closed body.

    ', + expected: + 'Closed summary.\nOpen summary.\n\nOpen body.\n\nNested closed summary.', + }, + { + label: 'a selected article inside a closed details body', + body: '
    Visible summary.
    Hidden selected article.
    ', + expected: '', + }, + { + label: 'a selected article inside the first summary', + body: '
    Visible selected article.

    Hidden body.

    ', + expected: 'Visible selected article.', + }, + { + label: + 'a selected article inside nested open details under a closed ancestor', + body: '
    Outer summary.
    Inner summary.
    Hidden selected article.
    ', + expected: '', + }, + ]) { + test(`respects ${label} without changing the source DOM`, async ({ + context, + extensionId, + }) => { + const fixtureUrl = `${FIXTURE_URL}?details`; + await context.route(fixtureUrl, route => + route.fulfill({ + contentType: 'text/html', + body: `${body}`, + }) + ); + const contentPage = await context.newPage(); + await contentPage.goto(fixtureUrl); + const before = await contentPage + .locator('html') + .evaluate(root => root.outerHTML); + const extensionPage = await context.newPage(); + await extensionPage.goto( + `chrome-extension://${extensionId}/options.html` + ); + await contentPage.bringToFront(); + const tabId = await activeTabId(extensionPage); + const response = await extensionPage.evaluate( + id => + chrome.runtime.sendMessage({ + actionType: 'EXTRACT_PAGE_CONTENT', + payload: { tabId: id }, + }), + tabId + ); + + expect(response.success).toBe(true); + expect(response.data.mainText).toBe(expected); + expect( + await contentPage.locator('html').evaluate(root => root.outerHTML) + ).toBe(before); + }); + } + test('extracts visible blocks and inline text without changing the source DOM', async ({ context, extensionId, @@ -136,7 +248,7 @@ test.describe('content extraction', () => { expect(response.success).toBe(true); expect(response.data.mainText).toBe( - 'First visible paragraph.\nSecond visible paragraph.\nInline text stays together.' + 'First visible paragraph.\n\nSecond visible paragraph.\n\nInline text stays together.' ); expect( await contentPage.locator('html').evaluate(root => root.outerHTML) From c2e6e2ea7e7f08f82342531dd0b2b4d571c8d655 Mon Sep 17 00:00:00 2001 From: Aaron Sequeira Date: Mon, 2 Nov 2026 16:12:08 +0530 Subject: [PATCH 14/14] docs: record combined reliability delivery Keep the product checkpoint concise and link the requirement-to-regression evidence, validation limits, milestone, and tooling follow-up. Document worker-owned settings writes and the explicit reset message. --- CHANGELOG.md | 8 ++++ README.md | 3 ++ docs/DEVELOPMENT.md | 10 +++++ docs/product/INTERFACE.md | 4 ++ docs/product/PRODUCT.md | 10 +++-- docs/product/PROJECT-STATUS.md | 67 ++++++++++++++++++---------------- docs/reliability-evidence.md | 65 +++++++++++++++++++++++++++++++++ 7 files changed, 132 insertions(+), 35 deletions(-) create mode 100644 docs/reliability-evidence.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 8b7047b..48df9d8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed +- Serialize Settings and popup writes in the worker, including first-run defaults + and confirmed resets, to preserve overlapping edits (#46). +- Keep delayed extraction, analysis, links, and saved chat scoped to the current + document/conversation; preserve current and independently restored summaries (#47, #48). +- Stop queued summary sections after a fatal failure; preserve daily cleanup + alarms across worker starts (#49, #50). +- Keep provider deadlines active through response bodies, and extract visible + text with block boundaries without changing the source DOM (#51, #52). - Restore keyboard access to summary-length radios and expose task-tab selection, panel relationships, and Arrow/Home/End navigation (#36). - Replace fictional contribution examples, consolidate setup and validation diff --git a/README.md b/README.md index b35a862..e05cbe3 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,9 @@ themes and a compact popup. Use **Study guide** or **Quiz me** in Chat to start learning. Navigate task tabs with Left/Right, Home, and End; Tab moves into the selected panel. Summary length works with native radio-keyboard controls. See the [interface decisions and verification](docs/product/INTERFACE.md). +The combined interface and reliability delivery is tracked in +[PR #39](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/39) and the +[current checkpoint](docs/product/PROJECT-STATUS.md). ## Start here diff --git a/docs/DEVELOPMENT.md b/docs/DEVELOPMENT.md index 52e37b1..679fffe 100644 --- a/docs/DEVELOPMENT.md +++ b/docs/DEVELOPMENT.md @@ -58,6 +58,7 @@ and this extension's sender ID; the [router](../background.js) rejects unknown a | `EXTRACT_KEY_INSIGHTS`, `GENERATE_SMART_TAGS`, `EXTRACT_ENTITIES` | `text`; requested analysis | | `EXTRACT_PAGE_CONTENT` | `tabId`, falling back to sender tab; extraction | | `GET_USER_PREFERENCES`, `UPDATE_USER_PREFERENCES` | Read settings or merge a settings payload | +| `RESET_USER_PREFERENCES` | Confirmed reset; replace settings, including API keys, with defaults | | `GET_PROVIDER_STATUS` | Selected provider and configured-key status | | `TEST_PROVIDER_CONNECTION` | Entered `provider`, `apiKey`, optional `model`; 16-token request, 10s timeout, no retries; never saves key | | `GET_DOCUMENT_HISTORY` | `url`, `content`; summary, up to 30 exchanges, `memoryEnabled` | @@ -74,6 +75,9 @@ The content script separately accepts `{ action: 'extractContent' }`. `ConfigurationManager` owns `chrome.storage.sync.user_preferences`: keys, models, provider, summary/language/theme preferences, and feature flags. +Options and popup send mutations to one worker manager. Initialization, updates, +and confirmed resets share a recoverable queue; reads in Options remain local. +This orders this extension's writes, not updates from other synced devices. Keys are plaintext at the API level, sync with the browser profile, and are excluded from exports. Unreadable preferences cannot authorize optional effects or replacement updates. See [security and data handling](SECURITY.md). @@ -84,6 +88,7 @@ or replacement updates. See [security and data handling](SECURITY.md). worker, including clear-chat and cleanup. A failed mutation read propagates instead of overwriting history with an empty list; read-only access retains its logged fallback. +Worker startup preserves an existing cleanup alarm instead of resetting its delay. ### Document retrieval, cache, and memory @@ -111,6 +116,11 @@ warning, without another provider request. popup startup queries the active tab. User-selected UTF-8 text/Markdown imports validate 8 MB and 2,000,000-character limits. Their identity uses `local-document:`, filename, and content hash. Extraction/import sends no AI request. +Extraction reads visible DOM text without changing the source page. Pending +extraction, analysis, and links cannot populate a subsequently imported document. +Chat restoration cannot undo a successful clear or a new exchange; saved summaries +restore independently without replacing a fresh result. Provider deadlines cover +response headers and body consumption; fatal section failures stop queued work. ## Local validation diff --git a/docs/product/INTERFACE.md b/docs/product/INTERFACE.md index d664a11..f4853bd 100644 --- a/docs/product/INTERFACE.md +++ b/docs/product/INTERFACE.md @@ -5,6 +5,10 @@ replaces the gradient/emoji interface with a quiet reading desk. The source stay beside the five tools on wide screens and above them on narrow screens. Task labels describe actions; provider, memory, coverage, and source excerpts stay visible. +Current combined UI/reliability validation is recorded in the +[reliability evidence](../reliability-evidence.md); dated results below describe +the original interface revisions. + ![Summary workspace](images/reading-summary.png) ## Design decisions diff --git a/docs/product/PRODUCT.md b/docs/product/PRODUCT.md index dc63857..e573d65 100644 --- a/docs/product/PRODUCT.md +++ b/docs/product/PRODUCT.md @@ -1,6 +1,6 @@ # Reading and learning workspace -Updated 28 October 2026 for the unreleased interface revision. This is the maintained product brief; +Updated 2 November 2026 for the unreleased interface/reliability revision. This is the maintained product brief; the [supplied engineering brief](ENGINEERING-BRIEF.md) is preserved separately. ## Objective @@ -53,8 +53,12 @@ Its implementation merged through [PR #42](https://github.com/aaron-seq/GenAI-Br see the [checkpoint](PROJECT-STATUS.md) and [verification record](../security-testing-evidence.md). The separate [reading-interface draft #39](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/39) -also awaits review. The [private dashboard](https://github.com/users/aaron-seq/projects/6) -tracks both without counting an issue and its PR as separate outcomes. +now also carries the seven reliability fixes under +[milestone 3](https://github.com/aaron-seq/GenAI-Browser-Tool/milestone/3). +The [bug board](https://github.com/users/aaron-seq/projects/10) tracks #46–#52; +the [delivery dashboard](https://github.com/users/aaron-seq/projects/6) retains +the remaining verification and tooling work, including the new published +[dependency-advisory follow-up #54](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/54). CI action and runner validation is delivered through [PR #43 / issue #32](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/32), diff --git a/docs/product/PROJECT-STATUS.md b/docs/product/PROJECT-STATUS.md index efe7738..ea38580 100644 --- a/docs/product/PROJECT-STATUS.md +++ b/docs/product/PROJECT-STATUS.md @@ -1,37 +1,40 @@ # Project status and continuation -Checkpoint: 28 October 2026 (Asia/Calcutta). -[GitHub issues](https://github.com/aaron-seq/GenAI-Browser-Tool/issues) · -[private dashboard](https://github.com/users/aaron-seq/projects/6) · -[product / roadmap](PRODUCT.md). - -## Current state - -- Cleanup [#35](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/35) merged in - [#44](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/44), main - [`bd0e0f7`](https://github.com/aaron-seq/GenAI-Browser-Tool/commit/bd0e0f7e6a648117a801aea38a161ee2fad9f8ab). - Contribution examples, consolidated docs, and runtime packaging are updated. -- [#42](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/42) (settings failures) - and [#43](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/43) (CI actions) - merged. The [post-merge CI run](https://github.com/aaron-seq/GenAI-Browser-Tool/actions/runs/40081266808) - passed; historical details remain in [settings](../security-testing-evidence.md) - and [CI evidence](../ci-modernization-evidence.md). No release/deployment is implied. -- [UI draft #39](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/39) is reconciled - with main bd0e0f7; [combined validation](INTERFACE.md#integration-verification--28-october-2026) - passes locally. It remains open for review before integration. -- Restricted triage remains open under the owner's instruction. Its access-controlled - GitHub record/private Project item is authoritative; no details belong here. +Checkpoint: 2 November 2026 (Asia/Calcutta). +[Delivery dashboard](https://github.com/users/aaron-seq/projects/6) · +[Bug board](https://github.com/users/aaron-seq/projects/10) · [Product roadmap](PRODUCT.md). + +## Current delivery + +[Draft PR #39](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/39) combines the +reading-interface work (#36) with the seven verified reliability fixes (#46–#52). +[Milestone 3](https://github.com/aaron-seq/GenAI-Browser-Tool/milestone/3) requires +preserved settings edits, document/chat response isolation, stopped queued work +after failure, stable cleanup scheduling, bounded response bodies, and visible-text +extraction. Combined local checks and fresh agent review pass; use the PR's +exact head for hosted CI and [evidence](../reliability-evidence.md) for results/limits. +Open issues are not counted as delivered before acceptance/merge. + +Main remains [bd0e0f7](https://github.com/aaron-seq/GenAI-Browser-Tool/commit/bd0e0f7e6a648117a801aea38a161ee2fad9f8ab): +settings failure handling #42, CI modernization #43, and documentation/package +cleanup #44 are already merged. No release or deployment is implied. + +## Remaining work + +- [#40 native entry/OS verification](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/40), + [#41 scanning setup](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/41), + [#45 optional Codecov authentication](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/45), + and [#54 development dependency advisory](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/54). +- [#37 Settings design](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/37), + PDF/OCR, semantic RAG, long translation/analysis, and user-data import retain + their existing [roadmap issues](PRODUCT.md#next-steps-and-material-gaps). +- Live-provider quality, real-book recall/latency/cost, minimum-browser/macOS, + screen readers, and production recovery remain unverified. Restricted triage + stays open under the owner's instruction; its access-controlled record is authoritative. ## Resume here -Check current Git status, main, linked issue/PR heads, comments, and CI before work. -Use [development validation](../DEVELOPMENT.md#local-validation); record actual -results and limits on the issue/PR rather than duplicating them in this checkpoint. -Dashboard statuses are manual. Draft review feedback stays unpublished until authorized. - -Next: review #39 against current main; verify [native entry/OS behavior (#40)](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/40); -propose [scanner setup (#41)](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/41). -The optional [coverage-upload failure (#45)](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/45) -remains separate from passing local coverage gates. -Live-provider quality, minimum-browser/macOS support, real-document recall/latency/cost, -and production recovery remain unverified. Long-document work stays in the product roadmap. +Check worktree status, remote heads, PR comments, and current-head CI before work. +Use [development validation](../DEVELOPMENT.md#local-validation). Keep issue/project +statuses accurate, retain the supplied engineering brief unchanged, and keep draft +review comments unpublished until requested. Merge and publication are separate decisions. diff --git a/docs/reliability-evidence.md b/docs/reliability-evidence.md new file mode 100644 index 0000000..126d7be --- /dev/null +++ b/docs/reliability-evidence.md @@ -0,0 +1,65 @@ +# Reading workspace reliability evidence + +Delivery: [PR #39](https://github.com/aaron-seq/GenAI-Browser-Tool/pull/39), +[milestone 3](https://github.com/aaron-seq/GenAI-Browser-Tool/milestone/3). +Baseline: [d544b8e](https://github.com/aaron-seq/GenAI-Browser-Tool/commit/d544b8e7936e9e1959eb71bcc9ebbe6da2dce7b1), +which already includes main bd0e0f7 and the reading-interface changes. +The [checkpoint](product/PROJECT-STATUS.md) records remaining work. + +## Requirements and regression evidence + +| Issue | Behavior and regression oracle | +| ---------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [#46](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/46) | One worker writer queues initialize/update/reset; overlapping field/map edits survive. [Core](../tests/core/configuration-manager.test.js), [Options](../tests/options/settings.test.js), [router](../tests/integration/extension-workflow.test.js), and [two-context browser tests](../tests/e2e/settings-concurrency.spec.js) check ordering, failure recovery, final native storage and writer ownership. Popup changes save only the edited language; explicit swap saves both. | +| [#47](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/47) | Delayed extraction/analysis/links successes and errors cannot populate an imported document. [Popup](../tests/popup/interface.test.js) and [browser races](../tests/e2e/workspace-races.spec.js) check the selected title, result panels and document context. | +| [#48](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/48) | Old history cannot undo successful clear/new chat. Same-document summary restoration remains independent, and fresh summaries win. Tests check DOM, subsequent prompt history and actual cleared storage; a failed clear still permits restoration. | +| [#49](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/49) | [Mapper](../tests/core/concurrency.test.js)/worker tests hold sibling requests, fail the first, then release siblings: three calls stay three, with no merge/history save. Already-started calls may finish. | +| [#50](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/50) | Worker lifecycle tests preserve an existing scheduled time, create missing alarms and contain get/create failures while registering listeners synchronously. Timing/restarts are simulated, not a real 24-hour run. | +| [#51](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/51) | [Provider tests](../tests/providers/ai-client.test.js) delay 200/503/401 bodies past the deadline: terminal TIMEOUT and one call. Malformed success JSON is not retried as a network error. | +| [#52](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/52) | [Extraction unit tests](../tests/content/extraction.test.js) and [real Chromium](../tests/e2e/extension-loading.spec.js) check hidden text, collapsed disclosures/ancestors, visible blocks/inline words and unchanged source HTML. Extraction-to-chunking preserves all 30 fixture paragraphs across two chunks. | + +The implementation sessions ran new failing regressions before their respective +fixes. Primary-session checks independently reproduced the UI races and reran +provider/worker suites. Fresh agent review caught shared-response summary restoration, +stale popup language payloads, collapsed disclosures and paragraph chunking gaps. +Each was reproduced, corrected, and rechecked, including actual Chromium diagnostics. +Agent review is not independent human approval. + +## Validation — 2 November 2026 + +The primary session ran combined checks on source revision +[9c3db4b](https://github.com/aaron-seq/GenAI-Browser-Tool/commit/9c3db4b6c3793bc756d4ec6d2211dd5c520419c5), +followed by documentation-only updates. Environment: Windows, Node 22.18.0, +npm 11.6.2. Current-head hosted CI and ZIP inspection are recorded in the PR body. + +| Command/check | Observed result | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------- | +| `npm run verify` | Lint/typecheck and 361 tests in 13 files pass. | +| `npm run test:coverage` | 361 pass; statements 95.01%, branches 85.16%, functions 95.60%, lines 97.02%. Existing floors retained. | +| `npm run test:e2e` | 33 real-Chromium cases pass, including UI keyboard/layout, settings, extraction and delayed-response flows. | +| `npm audit --json --audit-level=high` | Exit 0; five moderate affected development packages, zero high/critical. | +| `npm pack --dry-run --json --ignore-scripts` | 25 files; no tests/docs/tooling/dependencies. Read-only reference check resolves 26 manifest/HTML/JS targets within that inventory. | +| Visual inspection | Generated 320px light workspace, desktop dark Chat and populated source-excerpt views inspected; no clipping observed in those views. | + +A primary-session native Node fetch diagnostic used a loopback server that sent +headers and an incomplete JSON body. With a 150ms deadline, statuses 200/503/401 +returned TIMEOUT in 168/164/155ms, signal aborted, one request each despite retries +being enabled. Only the destination was substituted; no real provider/key was used. +This diagnostic is distinct from the committed fake-timer regressions and does not +establish live-provider quality or latency. + +The fresh baseline audit reports five moderate affected development packages, +zero high/critical; [#54](https://github.com/aaron-seq/GenAI-Browser-Tool/issues/54) +tracks the published minimatch advisory. Passing `--audit-level=high` is not a clean +audit. This delivery changes no dependency, scanner configuration, permission, +provider/model default, storage schema, or deployed service. + +Browser tests load the unpacked extension and intercept fixture/provider traffic; +the race harness delays delivery after the real worker responds. Two-context +settings tests hold writes but use native Chrome reads/persistence. No live-provider, +native-toolbar/OS notification, minimum-version, screen-reader, or real-book +quality/performance claim follows. The original interface verification and visual +decisions remain in [INTERFACE.md](product/INTERFACE.md). + +Rollback reverts the combined PR and reloads the extension; no migration is needed. +That restores the defects, so it is a recovery option rather than a permanent fix.