4 KiB
4 KiB
1. Extension scaffold
- 1.1 Create Manifest V3
manifest.jsonwithsidePanel,tabs,webNavigation,unlimitedStoragepermissions, action icon, and keyboard command binding to open the side panel - 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
- 1.3 Wire
chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: true })in the background service worker
2. Shared storage & key module
- 2.1 Implement
buildNoteKey(url, config)with the default rule (scheme://domain/path?search, hash excluded) and a swappable config - 2.2 Define the IndexedDB schema:
notesobject store keyed byid, non-unique index on the derived URL key, index onupdatedAt - 2.3 Implement
getNoteByKey(key),saveNote(key, content, pageMeta),listNotes()(excludes soft-deleted), filteringdeletedAt == nullin retrieval - 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
- 3.1 Listen for
chrome.webNavigation.onHistoryStateUpdatedandonReferenceFragmentUpdatedin the background service worker - 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
- 4.1 Build the side panel page shell with a dark theme applied by default
- 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
- 4.3 Render the note content in a plain
<textarea>with no other interactive text-editing controls - 4.4 Implement debounced autosave on keystroke, writing through the shared storage module and capturing
pageTitle/favIconUrlfrom the active tab at save time - 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 - 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
- 5.1 Open a shared
BroadcastChannelfrom every side panel instance - 5.2 On save, post
{ key, updatedAt }on the channel - 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
- 6.1 Build the notes-list page shell (own tab, history-page-inspired layout)
- 6.2 Query notes via the
updatedAtindex in descending order and group them into day sections (Today / Yesterday / date) in local time - 6.3 Render each entry with favicon + page title (falling back to URL when no title was captured) and a formatted time
- 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 - 6.5 If a matching tab is found: focus its window (
chrome.windows.update) and activate it (chrome.tabs.update), then callchrome.sidePanel.open({ tabId })as the first synchronous action in the click handler - 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.sidePanelper-tabsetOptions/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