mergeNotesByVersion only iterated incoming notes, dropping any
local-only notes (e.g. just created but not yet in the server/IDB
response). This caused note cards to disappear briefly after creation
when loadNotes raced with createNote.
Also rename E2E test note title to avoid name collision with existing
"Groceries" test.
https://claude.ai/code/session_01TdPpATUkmdY7DhBkmqrvWs
Two issues caused checked items to revert after closing:
1. loadNotes() blindly overwrote the store with notes.set(), so a stale
response from the periodic sync (every 30s) could replace a just-saved
note with older server data. Now merges by version number, keeping the
higher-versioned note.
2. Checklist component ignored external content prop changes (from sync or
history restore). Added $effect that re-parses items when the content
prop changes from an external source, using lastEmitted tracking to
distinguish self-originated vs external updates.
https://claude.ai/code/session_01TdPpATUkmdY7DhBkmqrvWs
Users pulling the image without authentication get "unauthorized" because
ghcr.io packages default to private under an organization. Add a step in
both release workflows to explicitly set the container package to public.
https://claude.ai/code/session_01QovQeW26KfEEmU1KdFfCPU
Concurrent edits to shared notes (e.g., one user checks a shopping list
item while another adds an item) were lost because the sync system used
whole-document Last-Write-Wins. This replaces it with a 3-way line-level
content merge using node-diff3, so changes to different lines are
preserved from both users.
Key changes:
- Add node-diff3 for 3-way line-level content merging
- Remove the timestamp gate in processSyncPush that rejected entire
offline sync pushes when another user had updated the note
- Add content merge to both the sync push path and the PATCH endpoint,
using version snapshots as the merge base
- Send baseVersion from the client so the server can find the correct
common ancestor for 3-way merge
- Protect pending local fields during client pull merge by reading
the sync queue and preserving queued-but-unpushed changes
- Add comprehensive tests for content merge, server sync, and CRDT merge
https://claude.ai/code/session_01FNpVBBjM6vKzfR8y9NM9Ci
The editor card's onpointerdown stopPropagation prevented the
FormattingToolbar's document-level pointerdown listener from
detecting clicks outside the dropdown. Since both overlay handlers
already use e.target === e.currentTarget to ignore bubbled events,
no stopPropagation is needed on the card.
https://claude.ai/code/session_018DYwSysqysMmaRi5XkqTpo
Pointer events are never synthetically re-dispatched from touch
(unlike mouse events), so the tap that opened the editor cannot
ghost-click the overlay. This eliminates the mobile first-load bug
without relying on an arbitrary delay.
Both pointerdown and pointerup must target the overlay itself to
trigger close, which also handles the text-selection-outside-editor
edge case.
https://claude.ai/code/session_018DYwSysqysMmaRi5XkqTpo
On first page load on mobile, the synthetic mousedown from a tap can
arrive ~300ms after the touch event — well after a single
requestAnimationFrame (~16ms). Replace the rAF-based overlayReady
guard with a 400ms timestamp check from mount time to reliably
ignore stale synthetic events on slow first loads.
https://claude.ai/code/session_018DYwSysqysMmaRi5XkqTpo
Send an email to collaborators when they are added to a shared note.
Uses nodemailer with optional SMTP configuration (auto-enabled when
SMTP_HOST is set). Users can opt out via the notifyOnShare preference.
https://claude.ai/code/session_01P4HSMr7QvKw9TMNikUJD9e
Add more vertical padding (py-1 → py-1.5) to checklist items for better
readability. Add min-w-0 to the text input so long text is constrained
within the flex container instead of overflowing out of the note.
https://claude.ai/code/session_0163L1u9MfL1C6Rv3yAw8RKs
- Replace `truncate` with `break-words min-w-0` so long checklist text
wraps visibly instead of being clipped
- Increase vertical spacing between items (space-y-1 → space-y-2)
- Add bottom margin (mb-6) to prevent action buttons overlapping content
- Align checkboxes to top of multi-line items (items-start + mt-0.5)
https://claude.ai/code/session_01UVNA5wucVEjRQzwRxMhTZ9
On mobile, tapping a NoteCard to open the editor produces synthetic mouse
events (mousedown → mouseup → click) at the original touch coordinates.
If those coordinates land on the overlay backdrop rather than the editor
card, the mousedown sets mousedownOnOverlay = true and the click
immediately closes the editor — causing a brief flicker.
Guard against this by deferring overlay readiness to the next animation
frame after mount, so any synthetic events from the originating tap are
ignored.
https://claude.ai/code/session_011C8bfgShjDSmDr3rUCb2gw
On touch devices, hover states don't work, so the pin/archive/delete
buttons were invisible. Now they're always visible on screens below the
md breakpoint and only hover-toggled on desktop.
https://claude.ai/code/session_013DdAk1yj6r3VF6Ze4ycidp