side-notes/openspec/changes/archive/2026-08-03-add-notes-extension/tasks.md

47 lines
4 KiB
Markdown

## 1. Extension scaffold
- [x] 1.1 Create Manifest V3 `manifest.json` with `sidePanel`, `tabs`, `webNavigation`, `unlimitedStorage` permissions, action icon, and keyboard command binding to open the side panel
- [x] 1.2 Set up the build/dev tooling for a background service worker, a side panel page, and a notes-list page as separate entry points
- [x] 1.3 Wire `chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })` in the background service worker
## 2. Shared storage & key module
- [x] 2.1 Implement `buildNoteKey(url, config)` with the default rule (`scheme://domain/path?search`, hash excluded) and a swappable config
- [x] 2.2 Define the IndexedDB schema: `notes` object store keyed by `id`, non-unique index on the derived URL key, index on `updatedAt`
- [x] 2.3 Implement `getNoteByKey(key)`, `saveNote(key, content, pageMeta)`, `listNotes()` (excludes soft-deleted), filtering `deletedAt == null` in retrieval
- [x] 2.4 Implement soft-delete support at the storage layer (`markDeleted(id)`), even though no UI triggers it in this change
## 3. In-page navigation detection
- [x] 3.1 Listen for `chrome.webNavigation.onHistoryStateUpdated` and `onReferenceFragmentUpdated` in the background service worker
- [x] 3.2 On either event, re-resolve the note key for the affected tab and notify that tab's open side panel (if any) to reload
## 4. Side panel note editor
- [x] 4.1 Build the side panel page shell with a dark theme applied by default
- [x] 4.2 On panel load/tab activation, resolve the active tab's URL to a key via the shared module and load (or initialize) that note
- [x] 4.3 Render the note content in a plain `<textarea>` with no other interactive text-editing controls
- [x] 4.4 Implement debounced autosave on keystroke, writing through the shared storage module and capturing `pageTitle`/`favIconUrl` from the active tab at save time
- [x] 4.5 Implement in-panel find: intercept Ctrl+F/Cmd+F while the panel document has focus, prevent the browser's page-level find, render a find bar, search `textarea.value`, and move the native selection (`setSelectionRange` + scroll into view) to each match
- [x] 4.6 Handle tab switching: when the panel's hosting tab changes its active URL (activation or navigation), reload the note for the new key
## 5. Cross-panel consistency
- [x] 5.1 Open a shared `BroadcastChannel` from every side panel instance
- [x] 5.2 On save, post `{ key, updatedAt }` on the channel
- [x] 5.3 On receiving a message for the currently displayed key, reload from storage unless the textarea currently has focus; if focused, defer the reload until blur
## 6. Notes-list page
- [x] 6.1 Build the notes-list page shell (own tab, history-page-inspired layout)
- [x] 6.2 Query notes via the `updatedAt` index in descending order and group them into day sections (Today / Yesterday / date) in local time
- [x] 6.3 Render each entry with favicon + page title (falling back to URL when no title was captured) and a formatted time
- [x] 6.4 On entry click: query `chrome.tabs.query({})`, compute each tab's key via the shared module, and compare to the clicked note's key
- [x] 6.5 If a matching tab is found: focus its window (`chrome.windows.update`) and activate it (`chrome.tabs.update`), then call `chrome.sidePanel.open({ tabId })` as the first synchronous action in the click handler
- [x] 6.6 If no matching tab is found: navigate the tab hosting the notes-list page to the note's URL, then call `chrome.sidePanel.open({ tabId })` on it
## 7. Cross-browser verification
- [ ] 7.1 Load the unpacked extension in Chrome and manually verify: per-tab side panel isolation, autosave/reload across restart, in-panel find, cross-panel refresh, notes-list grouping and click behavior (both matched-tab and no-match paths)
- [ ] 7.2 Repeat the same manual verification in Brave, specifically checking `chrome.sidePanel` per-tab `setOptions`/`open()` gesture behavior parity with Chrome
- [ ] 7.3 Note any Brave-specific discrepancies found and decide whether a workaround is needed before considering this change complete