Extract a typed repository interface (adapters/repo.ts) from the scattered
store/view -> api.* calls, backed by adapters/rest.ts (verbatim HTTP mapping)
and selected through adapters/index.ts. Every store and the notes-facing views
now depend on `repo`, never the HTTP client directly -- the seam the offline
local source (M10.5, over Tauri invoke) plugs into next.
Behavior-preserving for web: rest.ts maps each semantic method to the exact
endpoint the code called before; query-string and multipart building moved out
of the stores/views into rest.ts (the one place that knows the URL shape).
Client-side logic (reconcile/sort/optimistic reorder/toasts) stays in the
stores. GraphView + admin SettingsView keep direct api calls -- out of the
offline-core scope (M10.5 is board/editor/capture/search/filter/labels/
checklists/reminders).
Task 1992.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
The Search / Timeline / Reminders views each hand-rolled the same
items+loading+error scaffold, retry button, and editor-host glue.
- useNoteList(fetcher, fallbackError) (new): the load-a-note-list scaffold
(items/loading/error + a load() that never leaves a half-state). Views
supply just the fetcher; the refs drive <AsyncState>.
- SearchView / TimelineView / RemindersView: adopt useNoteList + useNoteEditor
+ <AsyncState>/<EmptyState>; drop the local list/loading/error refs, the
duplicated retry blocks, and the notes.items-shadowing navigate glue.
- GraphView: editor host now via useNoteEditor (openNode/closeEditor/onNavigate
collapse to navigate); loading/error via <AsyncState>. Its two empty states
keep inline markup/buttons, so they stay custom (not forced into EmptyState).
- reminders store: fetchReminders() is the single owner of /api/notes/reminders;
both the background poll (check) and RemindersView read through it.
Frontend-only; CI vue-tsc is the type gate (no local typecheck, rule 10).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm
Completes 1908 without Web Push. While the app is open, due reminders now
actually surface; recurring reminders + snooze/done are manageable.
- reminders store (singleton): polls /api/notes/reminders every 45s while
the app is open; each due reminder fires ONCE as a toast (with an "Open"
action) and, if the user opts in, a page-context OS Notification — no
service worker, no PWA. Silently primes a stale backlog on first load;
only announces recently-due ones. AppShell starts/stops it.
- Editor reminder section: a Repeat picker (Does not repeat / Daily /
Weekly / Monthly / Yearly) + Done (advances a recurring reminder / clears
a one-off) + Snooze 1h/1d, shown when a reminder is set.
- RemindersView rebuilt as a chronological list: per row a due time +
recurrence badge + Done / 1h / 1d, click to open; plus an "Enable
notifications" opt-in and a note that background alerts come with native.
- Note type gains `recurrence`; notes store setRecurrence /
completeReminder / snoozeReminder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01FRgehjoz7Yv8LkUfADxACm