diff --git a/openspec/specs/note-editor/spec.md b/openspec/specs/note-editor/spec.md new file mode 100644 index 0000000..569f256 --- /dev/null +++ b/openspec/specs/note-editor/spec.md @@ -0,0 +1,61 @@ +# note-editor Specification + +## Purpose + +Provides the per-tab side panel where a user reads and edits the markdown note for the current page, preserving native text-editing behavior and staying consistent when the same note is open in more than one tab. + +## Requirements + +### Requirement: Side panel scoped to a browser tab +The system SHALL provide a side panel, opened via the toolbar action or a keyboard shortcut, that displays the note for the active tab's current page. Each tab SHALL have its own panel instance, so editor state in one tab is unaffected by other tabs. + +#### Scenario: Opening the panel shows the current tab's note +- **WHEN** a user opens the side panel while viewing a page +- **THEN** the panel loads and displays the note associated with that page's URL key + +#### Scenario: Switching tabs shows the new tab's note +- **WHEN** a user activates a different tab that also has the side panel open +- **THEN** that tab's panel shows the note for its own current URL, independent of other tabs' panels + +### Requirement: Native text editing +Note content SHALL be edited in a plain text input area, so that text selection, copy, cut, paste, and undo/redo behave exactly as native browser text-field editing, with no custom interaction to learn. + +#### Scenario: Copy and paste behave like any text field +- **WHEN** a user selects text in the note and copies it, then pastes it elsewhere +- **THEN** the pasted text matches the selection exactly, using the browser's native clipboard behavior + +### Requirement: Autosave on edit +Edits to a note SHALL be saved automatically as the user types, without requiring an explicit save action. + +#### Scenario: Content is saved without a save button +- **WHEN** a user types in the note editor and then closes the panel or navigates away +- **THEN** the typed content is already persisted and reappears the next time the note is opened + +### Requirement: In-panel find +WHEN the panel has focus and the user presses the find shortcut (Ctrl+F / Cmd+F), the system SHALL open an in-panel find control instead of triggering the browser's page-level find, allow searching the note's text, and indicate matches using the editor's native text selection. + +#### Scenario: Find highlights a match via selection +- **WHEN** a user opens in-panel find and types a search term that matches note text +- **THEN** the matching text becomes selected in the editor and is scrolled into view + +#### Scenario: Browser page find is not triggered +- **WHEN** the side panel's editor has focus and the user presses Ctrl+F / Cmd+F +- **THEN** the browser's own page-level find UI does not open + +### Requirement: Consistency across panels sharing a note +WHEN the same note is open in more than one tab's panel and one panel saves an edit, the other open panel(s) showing that note SHALL refresh to the latest saved content, except that a panel whose editor currently has input focus SHALL defer the refresh until focus leaves the editor. + +#### Scenario: Unfocused panel picks up the update +- **WHEN** a note open in tab A is edited and saved, and the same note is open in tab B without focus in B's editor +- **THEN** tab B's panel updates to show the content saved from tab A + +#### Scenario: Focused panel is not overwritten mid-edit +- **WHEN** a note open in tab A is edited and saved, and the same note is open in tab B with focus currently in B's editor +- **THEN** tab B's editor content is not replaced until focus leaves B's editor + +### Requirement: Dark theme by default +The side panel SHALL render using a dark color theme by default. + +#### Scenario: Panel opens in dark theme +- **WHEN** a user opens the side panel with no prior theme preference set +- **THEN** the panel is displayed using the dark theme diff --git a/openspec/specs/notes-list/spec.md b/openspec/specs/notes-list/spec.md new file mode 100644 index 0000000..fa65fc5 --- /dev/null +++ b/openspec/specs/notes-list/spec.md @@ -0,0 +1,39 @@ +# notes-list Specification + +## Purpose + +Provides a dedicated page for browsing all saved notes, grouped and sorted the way a browser history page is, and lets the user navigate back to a note's page from that list. + +## Requirements + +### Requirement: Notes grouped by day and sorted by recency +The notes-list page SHALL display all non-deleted notes grouped into sections by the calendar day of each note's last-updated timestamp, with sections ordered most-recent-day first, and notes within a section ordered by last-updated time descending. + +#### Scenario: Notes updated today appear before older notes +- **WHEN** one note was last updated today and another was last updated yesterday +- **THEN** the notes-list page shows a section for today containing the first note above a section for the earlier day containing the second note + +### Requirement: Entries show page title and favicon +Each entry in the notes-list SHALL display the page's captured title as its primary text, with the page's favicon, rather than the raw URL. If no title was captured for a note, the entry SHALL fall back to displaying the URL. + +#### Scenario: Title shown instead of URL +- **WHEN** a note has a captured page title +- **THEN** its entry in the list shows that title as the primary text, not the raw URL + +#### Scenario: Missing title falls back to URL +- **WHEN** a note has no captured page title +- **THEN** its entry in the list shows the note's URL as the primary text + +### Requirement: Clicking an entry focuses an already-open matching tab +WHEN a user clicks a notes-list entry and a browser tab is already open at a URL that resolves to the same note key, the system SHALL bring that tab and its window into focus and open the side panel on it showing that note, without opening a new tab or navigating any tab. + +#### Scenario: Existing tab is focused instead of duplicated +- **WHEN** the note's page is already open in another tab and the user clicks its entry in the notes list +- **THEN** that existing tab becomes the active tab, its window is brought to the front, and its side panel opens showing the note, with no new tab created + +### Requirement: Clicking an entry navigates the current tab when no match exists +WHEN a user clicks a notes-list entry and no open tab currently resolves to that note's key, the system SHALL navigate the tab currently showing the notes-list page to the note's URL and open the side panel on it showing that note. + +#### Scenario: No existing tab is open for the note +- **WHEN** the user clicks an entry for a note whose page is not open in any tab +- **THEN** the tab that was showing the notes-list page navigates to that note's URL and its side panel opens with that note loaded diff --git a/openspec/specs/notes-storage/spec.md b/openspec/specs/notes-storage/spec.md new file mode 100644 index 0000000..f3032f8 --- /dev/null +++ b/openspec/specs/notes-storage/spec.md @@ -0,0 +1,53 @@ +# notes-storage Specification + +## Purpose + +Persists exactly one markdown note per page, keyed by a normalized form of the page's URL, in local storage that survives restarts and is shaped to support multi-device sync in a future change. + +## Requirements + +### Requirement: Note identified by normalized URL key +The system SHALL derive a storage key for a page from its URL using a normalization rule that, by default, includes the scheme, domain, path, and query string, and excludes the fragment/hash. + +#### Scenario: Two URLs differing only by hash resolve to the same note +- **WHEN** a user visits `https://example.com/article#section1` and later `https://example.com/article#section2` +- **THEN** both pages resolve to the same note key and show the same note content + +#### Scenario: Two URLs differing by query string resolve to different notes +- **WHEN** a user visits `https://example.com/search?q=cats` and `https://example.com/search?q=dogs` +- **THEN** each URL resolves to a distinct note key with independent content + +### Requirement: Key derivation rule is configurable +The system SHALL allow the URL normalization rule used for key derivation to be changed at the code level, without requiring a UI for end users in this change. + +#### Scenario: Key builder is swapped +- **WHEN** a developer changes the configured key-derivation rule (e.g., to include the hash) +- **THEN** notes saved after the change use keys produced by the new rule + +### Requirement: In-page navigation is detected +The system SHALL detect URL changes that occur without a full page load within a tab (e.g., History API `pushState`/`replaceState` navigation used by single-page applications) and re-resolve the note key for that tab accordingly. + +#### Scenario: In-page navigation changes the resolved note +- **WHEN** a single-page application changes the browser URL via the History API without a full page load +- **THEN** the note associated with the new URL is resolved for that tab without requiring a manual reload + +### Requirement: Note content persists across restarts +The system SHALL persist each note's content in local storage that survives browser restarts and computer restarts, without requiring network access. + +#### Scenario: Note survives a browser restart +- **WHEN** a user writes content in a note and later restarts the browser +- **THEN** reopening the side panel on the same page shows the previously written content + +### Requirement: Note record data model +Each stored note record SHALL include: a stable unique identifier, the derived URL key, the page title and favicon URL captured at save time, the markdown content, a creation timestamp, a last-updated timestamp, and an optional deletion timestamp. + +#### Scenario: Metadata captured on first save +- **WHEN** a user's edits to a page's note are saved for the first time +- **THEN** the stored record includes a unique id, the derived key, the page's title, its favicon URL, the content, and matching creation and last-updated timestamps + +### Requirement: Soft deletion is honored by retrieval +A note record marked with a deletion timestamp SHALL be excluded from normal retrieval (opening a note for a page) and from any listing of notes, while remaining present in storage. + +#### Scenario: Deleted note is hidden but not erased +- **WHEN** a note record has a deletion timestamp set +- **THEN** it is not returned when its page's note is requested or when notes are listed, and its record still exists in storage