From 1037722196c97ef3f3121be5bb178f2fc95cd66c Mon Sep 17 00:00:00 2001 From: Fabio Montefuscolo Date: Mon, 3 Aug 2026 17:54:16 +0200 Subject: [PATCH] chore: track openspec work --- .../add-notes-extension/.openspec.yaml | 2 + .../changes/add-notes-extension/design.md | 50 ++++++++++++++++ .../changes/add-notes-extension/proposal.md | 30 ++++++++++ .../specs/note-editor/spec.md | 59 +++++++++++++++++++ .../specs/notes-list/spec.md | 37 ++++++++++++ .../specs/notes-storage/spec.md | 51 ++++++++++++++++ openspec/changes/add-notes-extension/tasks.md | 47 +++++++++++++++ 7 files changed, 276 insertions(+) create mode 100644 openspec/changes/add-notes-extension/.openspec.yaml create mode 100644 openspec/changes/add-notes-extension/design.md create mode 100644 openspec/changes/add-notes-extension/proposal.md create mode 100644 openspec/changes/add-notes-extension/specs/note-editor/spec.md create mode 100644 openspec/changes/add-notes-extension/specs/notes-list/spec.md create mode 100644 openspec/changes/add-notes-extension/specs/notes-storage/spec.md create mode 100644 openspec/changes/add-notes-extension/tasks.md diff --git a/openspec/changes/add-notes-extension/.openspec.yaml b/openspec/changes/add-notes-extension/.openspec.yaml new file mode 100644 index 0000000..e08b5f8 --- /dev/null +++ b/openspec/changes/add-notes-extension/.openspec.yaml @@ -0,0 +1,2 @@ +schema: spec-driven +created: 2026-08-03 diff --git a/openspec/changes/add-notes-extension/design.md b/openspec/changes/add-notes-extension/design.md new file mode 100644 index 0000000..ca4188c --- /dev/null +++ b/openspec/changes/add-notes-extension/design.md @@ -0,0 +1,50 @@ +## Context + +Greenfield Manifest V3 Chrome extension (no existing code). See proposal.md for motivation. Must also run on Brave and other Chromium-based browsers, which rules out relying on `chrome.storage.sync` (Google-account-bound) for any future sync story, and puts some weight on verifying newer APIs (`chrome.sidePanel`) behave the same outside Chrome itself. + +## Goals / Non-Goals + +**Goals:** +- Establish a storage layer and data model that a future sync change can build on without reshaping existing records. +- Keep the editing surface's text-interaction behavior indistinguishable from native browser text editing. +- Reuse a single URL-to-key function everywhere a note needs to be located (saving, loading, and matching against currently open tabs), so key derivation logic never drifts between call sites. + +**Non-Goals:** +- Implementing remote/multi-device sync (data model only). +- Search or delete UI within the notes-list page. +- Markdown rendering/syntax highlighting in the editor. +- Handling modifier-click (open-in-new-tab) semantics on notes-list entries. +- A UI for changing the URL-key derivation rule (code-level only). + +## Decisions + +**Architecture**: a background service worker (tab/navigation event handling, tab-focus-or-navigate logic for notes-list clicks), a side panel page (per-tab, via `chrome.sidePanel`), and a separate notes-list extension page (its own tab). All three share one storage module so key derivation and the IndexedDB schema aren't duplicated. + +**URL key derivation**: one pure function, `buildNoteKey(url, config)`, used identically by (a) the side panel when loading/saving a note for the active tab, and (b) the notes-list page's click handler when checking whether any open tab already matches the clicked note. Default config: `scheme://domain/path?search`, hash excluded. The function and its config live in one module so a future code-level change to the rule is a single edit, not a hunt across call sites. + +**In-page navigation detection**: `chrome.webNavigation.onHistoryStateUpdated` (pushState/replaceState) and `onReferenceFragmentUpdated` (hash-only changes) re-trigger key resolution for a tab without a full page load. This is the "naive" SPA support agreed on — it only reacts to URL changes; a SPA that changes visible content without ever changing the URL has nothing for this extension to key off, and that's fine since notes are keyed by URL. Requires the `webNavigation` permission. + +**Storage**: IndexedDB, one object store (`notes`), keyed by the note's own `id` (not by URL key), with a non-unique index on the derived URL key and an index on `updatedAt`. Non-unique on the key (rather than enforcing uniqueness in the DB) because soft-deleted records for a URL may coexist with a later, newer record for the same URL once delete ships in a future change; the application invariant ("at most one non-deleted note per key") is enforced by retrieval logic (filter `deletedAt == null`), not by a DB constraint. The `updatedAt` index lets the notes-list page pull a reverse-chronological cursor directly instead of loading everything into memory to sort. + +**Editor**: a plain `