Skip to content

feat(frontend): add personal-notes menu item and modal#13134

Draft
sbpublic wants to merge 6 commits into
feat/frontend/personal-notes-vetkeys-cryptofrom
feat/frontend/personal-notes-modal
Draft

feat(frontend): add personal-notes menu item and modal#13134
sbpublic wants to merge 6 commits into
feat/frontend/personal-notes-vetkeys-cryptofrom
feat/frontend/personal-notes-modal

Conversation

@sbpublic

Copy link
Copy Markdown
Collaborator

Motivation

Final wave of the personal-notes feature (PR-3 in docs/ai/spec-driven-development/specs/2026-06-17-feat-personal-notes.md). PR-1 (#13130) landed the encrypted backend storage and API; PR-2 (#13131) landed the vetKeys crypto, API bindings, service and store. This PR adds the user-visible surface: a Notes user-menu item and the Notes modal (list / empty / add / edit / delete), wiring the existing service and store into the UI.

Stacked on PR-2 — base branch is feat/frontend/personal-notes-vetkeys-crypto; review/merge that first.

Changes

  • Menu entry: a "Notes" ButtonMenu in Menu.svelte immediately after Contacts (signed-in only), opening the modal via a new openNotes / modalNotes pair on the modal store; mounted from the global Modals.svelte host gated on $modalNotes.
  • Notes modal (components/notes/): NotesModal (list / empty / add-edit on a gix Modal, lazy load on first open with cached re-opens, cap gate, delete with Undo), NoteListItem (2-line plain-text preview, Created/Updated timestamp, edit/delete, per-note decryption-failure + Retry), InputPersonalNote (auto-focused multi-line textarea, code-point length, too-long error), EmptyNotes, NotesPrivacyInfoBox, and the pinned NoteDeletedSnackbar.
  • Safe rendering: note text is always escaped plain text (never {@html}), line breaks via CSS; bidi/control characters are neutralized on display.
  • Dismissal rules: the list/empty state closes via X, Close, or backdrop; the editor step has no X and ignores the backdrop, so unsaved text can't be lost to an accidental tap.
  • Display utils: formatPersonalNoteTimestamp, neutralizePersonalNoteText, personalNotePreview, plus the personalNotesUndoStore. New IconNotebook lucide glyph.
  • i18n: navigation.text.notes + the notes.* block in en.json (regenerated types + synced locale structure).
  • PRODUCT.md: documents the feature in this PR, as the workflow requires.

Tests

  • personal-note.utils.spec.ts: timestamp formatting (relative vs absolute), bidi neutralization, preview whitespace-collapse.
  • NotesModal.spec.ts: empty / list states, cap gate disables Add note, save and delete go through the service, Undo capture.
  • NoteListItem.spec.ts: Created vs Updated, HTML/script content renders inertly, bidi-override neutralized, edit/delete/retry callbacks, decryption-failure row.
  • InputPersonalNote.spec.ts: too-long error past the cap, emoji counted by code points (not UTF-16).
  • Gates: npm run format, npm run lint -- --max-warnings 0, npm run check all pass.

Divergence from the spec

The spec assumed the Undo affordance and the editor-step dismissal rules could ride on existing primitives; two adjustments were needed because the primitives don't support them:

  • Undo snackbar — gix ToastMsg is text-only (no action button) and no toast in the codebase renders actionable HTML, so the spec's "Undo via OISY's toast system" isn't achievable as-is. Implemented a small custom NoteDeletedSnackbar, pinned at the standard toast position and auto-dismissing, reusing the existing restorePersonalNote service to restore the note verbatim. (Confirmed with the requester before building.)
  • Editor step has no X / inert backdrop — gix WizardModal sets visible = false before onClose, so it can't refuse to close a step; Modal always renders the header X. Used a plain Modal with internal list/editor step state, a guarded onClose, and a consumer-side CSS rule to hide the close button on the editor step.

The Notes UI is gated behind sign-in and needs the vetKeys backend, so it isn't reachable in a headless preview; verification is via the unit/component tests plus type-check and lint. Live visual QA (dark mode, mobile full-page editor with the soft keyboard) is the remaining manual check.


🤖 Generated with Claude Code using Claude Opus 4.8 (claude-opus-4-8)

sbpublic and others added 6 commits June 17, 2026 14:51
… ids

Add the display helpers PR-3 needs on top of the PR-2 store/service:
`formatPersonalNoteTimestamp` (relative-then-absolute, local timezone,
reusing the existing date utils), `neutralizePersonalNoteText` (strips
bidi/control characters per spec Decision 15) and `personalNotePreview`
(collapses whitespace for the 2-line list preview). Add the
`personalNotesUndoStore` holding the whole decrypted note for the Undo
window, and the notes test ids.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the `navigation.text.notes` menu label and the `notes.*` block (modal
title, empty state, editor labels, privacy info box, timestamps, cap and
too-long messages, delete/undo, decryption-failure copy) to `en.json`,
then regenerate the i18n types and sync the locale structure via
`npm run i18n`.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add the Notes modal and its parts under `components/notes/`: `NotesModal`
(list / empty / add-edit steps on a gix `Modal`, lazy load on first open,
cap gate, delete with Undo capture), `NoteListItem` (2-line plain-text
preview, Created/Updated timestamp, edit/delete, per-note decryption
failure + Retry), `InputPersonalNote` (auto-focused multi-line textarea,
code-point length, too-long error), `EmptyNotes`, `NotesPrivacyInfoBox`
and the pinned `NoteDeletedSnackbar`. Note text is always rendered as
escaped plain text (never `{@html}`); the editor step has no X and ignores
the backdrop. Add the `IconNotebook` lucide glyph.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a "Notes" `ButtonMenu` to the user menu immediately after Contacts
(signed-in only), opening the modal via a new `openNotes`/`modalNotes`
pair on the modal store. Mount `NotesModal` from the global modals host
gated on `$modalNotes`, and mount the app-level `NoteDeletedSnackbar`
next to the toasts so Undo survives list scroll.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Describe the standalone personal-notes feature: the Notes user-menu item,
end-to-end vetKeys encryption, lazy load, the character/note caps, local
timezone timestamps, safe plain-text rendering, and immediate-but-undoable
delete.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Cover the new display utils (timestamp, bidi neutralization, preview), the
modal states (empty / list / cap gate, save + delete through the service,
Undo capture), the list-item plain-text/inert rendering and decryption
failure, and the input's code-point length cap.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant