Commit Graph
221 Commits
Author SHA1 Message Date
Frederic CabassutandClaude Fable 5 4b7e346206 fix(editor): keep toolbar and dropdowns above the Android keyboard
Add interactive-widget=resizes-content to the viewport meta so the
Android keyboard shrinks the layout viewport through the native inset
path. Chromium's visualViewport.height can under-report the visible
height by the system nav-bar height, which left the editor toolbar
floating above the keyboard; the native resize path is accurate. iOS
ignores the attribute and keeps using the visualViewport tracking in
NoteEditor.

Position toolbar dropdown portals with window.innerHeight instead of
visualViewport.height: CSS bottom on position:fixed and
getBoundingClientRect() both use layout-viewport coordinates, so
mixing in the visual-viewport height pushed dropdowns behind the
keyboard while it was open.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:19:19 +02:00
Romain LE DISEZ 5fb0337d03 fix(editor): address PR 67 review feedback
Restore portal dropdown test IDs so the formatting e2e suite can
find heading and link controls again.

Let the mobile formatting toolbar pan horizontally, keep dropdown
portals from drifting by closing them on scroll or resize, and use
visualViewport height for keyboard-aware positioning.

Remove the app-wide maximum-scale lock, keep editor focus targets at
16px on mobile to avoid input auto-zoom, and move toolbar scrollbar
styling to a top-level scoped style block.

Align the remove-link action with the destructive design token, use
the standard 150ms editor slide-up animation, and replace
save/restore body overflow locking with a resilient body class.
2026-07-10 16:19:19 +02:00
Romain LE DISEZandGoogle Gemini ab8b273400 feat(ui): improve mobile editor layout
Make editor full-screen natively on mobile with back button. Add
visualViewport tracking to keep bottom toolbars above the software keyboard.

disable pull-to-refresh for native PWA feel
prevent auto-zoom on input focus in mobile Safari
prevent background scrolling when editor is open
prevent touch scroll on editor toolbars to stop background scrolling
hide visual gap at bottom of editor when iOS keyboard opens
move note actions to top-right menu on mobile devices
wrap background colors in mobile menu to reduce width
make formatting toolbar scrollable on mobile and extract dropdowns
remove empty else block causing svelte compilation error
add missing data-dropdown attributes to fix click interception in toolbar portals
keep mobile keyboard open when using toolbar menus and prevent right-edge overflow

Co-authored-by: Google Gemini <gemini@google.com>
2026-07-10 16:19:19 +02:00
Claude 5719fc3459 fix: make admin user deletion atomic and surface errors in UI
deleteUser() ran each child-table cleanup as a separate statement, so a
foreign-key violation on any step (e.g. sync_log rows from other users
referencing the deleted user's notes) would abort mid-way and leave the
database in a partial state. The API swallowed the error and the admin
page silently ignored the non-ok response, so clicking Delete and
confirming appeared to do nothing.

- Wrap the full cascade in a single sqlite transaction so the deletion
  is atomic.
- Also remove rows in sync_log, note_user_state, note_collaborators,
  note_versions, and attachments that belong to other users but
  reference the deleted user's notes, so the final DELETE FROM notes
  cannot trip a foreign-key check.
- Surface delete failures in the users settings UI instead of silently
  swallowing them.
- Add data-testids on the user row and delete button to support tests.
- Add e2e coverage for the admin user deletion flow (happy path,
  cancelled confirmation, and a user with notes / tags / attachments /
  preferences / api keys).

https://claude.ai/code/session_015T2HE6VzB64ZmSSyy1Wz4R
2026-04-22 21:45:48 +02:00
Claude ba1b071395 fix: use currentTarget and contains() in blur handler per review
Use e.currentTarget instead of e.target so the reference is always the
contenteditable div, not a descendant <a> tag. Use el.contains() instead
of strict equality to avoid linkifying while focus is still inside the
editable element.

https://claude.ai/code/session_01VZceWPhHMHCA6SELBdCPfN
2026-04-10 21:53:20 +02:00
Claude 86cfc8c5f6 fix: defer contenteditable blur linkification to unblock mobile tap events
When a contenteditable loses focus, the onblur handler immediately modified
innerHTML to linkify URLs. On mobile, this DOM mutation causes a layout reflow
between touchstart and click, making the browser cancel taps on the adjacent
delete button. Deferring via requestAnimationFrame ensures the click event
fires before the DOM is modified.

https://claude.ai/code/session_01VZceWPhHMHCA6SELBdCPfN
2026-04-10 21:53:20 +02:00
Frederic CabassutandClaude Opus 4.6 0a30145996 fix: decode &nbsp; entities from contenteditable in checklist items
Browsers inject &nbsp; in contenteditable elements, which was not being
decoded by unlinkifyHtml, causing double-escaped "&nbsp;" to appear in
checklist item text.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-10 08:51:14 +02:00
Frederic Cabassut d3b98fddf2 fix: address PR review feedback
- Add noreferrer to rel attribute to prevent Referer header leaks
- Strip newlines from pasted text to prevent checklist corruption
- Add aria-placeholder for screen reader accessibility
- Dismiss LinkPopover on scroll/resize
- Stop keydown propagation on links in NoteCard preview
2026-04-08 22:36:19 +02:00
Frederic CabassutandClaude Opus 4.6 4c13d345fc fix: use Svelte action for contenteditable to prevent cursor destruction
The reactive {#html} binding inside contenteditable replaced innerHTML on
every keystroke, destroying the cursor and causing text duplication.

Instead, use a use:editableContent action that sets innerHTML on mount
and only syncs when the element is NOT focused. Linkification happens
on blur, matching Google Keep's behavior. Update e2e tests to blur
before asserting link visibility.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:36:19 +02:00
Frederic Cabassut 653e990071 feat: linkify URLs in shared note checklist view 2026-04-08 22:36:19 +02:00
Frederic Cabassut 04fdffb111 feat: linkify URLs in NoteCard checklist preview 2026-04-08 22:36:19 +02:00
Frederic Cabassut 5eb7a84a88 feat: linkify URLs in checklist done section 2026-04-08 22:36:19 +02:00
Frederic CabassutandClaude Opus 4.6 beb4209baf fix: decode HTML entities in unlinkifyHtml and fix indentation
- Add entity decoding (&amp; &lt; &gt; &quot;) to unlinkifyHtml to
  prevent double-encoding on contenteditable edit cycles
- Fix indentation of LinkPopover import and state declaration
- Move LinkPopover inside root div
- Add unit test for entity decoding

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 22:36:19 +02:00
Frederic Cabassut 377ce4b72c feat: replace checklist text inputs with contenteditable for inline link rendering 2026-04-08 22:36:19 +02:00
Frederic Cabassut 740a89df63 feat: add LinkPopover component for checklist hyperlinks 2026-04-08 22:36:19 +02:00
Frederic Cabassut 17216bd17d style: add checklist-link CSS and contenteditable placeholder styles 2026-04-08 22:36:19 +02:00
Frederic Cabassut c753c4b5ce feat: add linkifyText and unlinkifyHtml utilities for checklist hyperlinks 2026-04-08 22:36:19 +02:00
Frederic CabassutandClaude Opus 4.6 b6dbdbe9a5 fix: prevent click-through to NoteCards when closing editor overlay
On installed PWAs, closing the editor by tapping the overlay would cause
the browser's delayed synthetic click to land on the NoteCard underneath,
immediately reopening a different note. Fixed by keeping a transparent
shield in the DOM for 400ms after close to absorb the ghost click.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 08:15:58 +01:00
Frederic CabassutandClaude Opus 4.6 be53b4b26e fix: sync attachment changes to store and skip spurious auto-save
Two issues fixed:

1. Attachment operations (upload, feature, remove) only updated the local
   editor state, not the notes store. Before auto-save, this was masked
   because saveAndClose always called updateNote, whose PATCH response
   refreshed the store. With auto-save skipping redundant saves, the store
   was never updated with attachment changes. Now attachment handlers
   update the store directly.

2. TiptapEditor fires onUpdate on init with round-tripped markdown that
   can differ from the original. Added an autoSaveReady guard to absorb
   this initial normalization as the baseline, preventing spurious saves
   when reopening existing notes.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:49:08 +01:00
Frederic CabassutandClaude Opus 4.6 9d1c898b84 fix: address review feedback on auto-save
- Snapshot fields at start of performSave to avoid race with user edits
  during async save (fields could change between await and lastSaved update)
- Only update lastSaved tracking on successful save, so failed saves
  are retried on the next debounce cycle
- Stop restoring title after Untitled fallback in autoSave(), which was
  re-triggering the auto-save effect
- Capture note count before opening editor in empty-note e2e test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:49:08 +01:00
Frederic CabassutandClaude Opus 4.6 07b27c01e7 feat: auto-save notes with debounced persistence
Notes are now automatically saved 2 seconds after the user stops editing,
preventing data loss when the browser tab is closed without closing the
editor first. A beforeunload handler provides an additional safety net.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-24 23:49:08 +01:00
Frederic CabassutandClaude Opus 4.6 4f4cc69b9f fix: replace hardcoded test passwords with runtime-generated UUIDs
Generate random passwords in global-setup and share via temp file so
all Playwright workers use the same credentials. Screenshot scripts
use module-level crypto.randomUUID() since they run single-process.
Unit test auth passwords also use randomUUID().

Eliminates security scanner alerts for passwords committed to git.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 10:29:45 +01:00
Frederic CabassutandClaude Opus 4.6 3f81f22026 fix: align checkboxes in richtext task list previews
Add missing nested task list rule for .prose context (matching TipTap's
existing rule) and adjust checkbox vertical alignment with margin-top.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:46:13 +01:00
Frederic CabassutandClaude Opus 4.6 172ab88b33 feat: custom tooltip action replacing native title attributes
Add a reusable `use:tooltip` Svelte action as a drop-in replacement for
native title attributes. Styled to match the retro parchment theme with
JetBrains Mono font, dark background, and 400ms hover delay.

Replace all title attributes in NoteEditor, NoteCard, and NotesView
with the custom tooltip. The action supports top/bottom positioning
and auto-clamps to viewport edges.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:09:32 +01:00
Frederic CabassutandClaude Opus 4.6 c82f720b02 fix: add cursor pointer to create crumb buttons
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:04:23 +01:00
Frederic CabassutandClaude Opus 4.6 548aa14d63 fix: add cursor pointer to create crumb button
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 20:03:26 +01:00
Frederic CabassutandClaude Opus 4.6 c66217e6f1 feat: add checklist shortcut to create button
Add a checkbox icon next to the "Add a crumb..." button that opens a new
note directly in checklist mode, similar to Google Keep's quick-create
bar. The NoteEditor accepts a new initialChecklistMode prop.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:53:01 +01:00
Frederic CabassutandClaude Opus 4.6 6c9781fb24 fix: sharing indicator popover opens downward to avoid overflow clipping
The popover was opening upward (bottom-full) which got clipped by the
NoteCard's overflow-hidden. Changed to open downward (top-full) so it
stays within the card's visual area.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:50:48 +01:00
Frederic CabassutandClaude Opus 4.6 b44cd40e36 fix: align nested checkboxes in rich text task lists
Nested TipTap task lists (indented children) broke the flex layout
because the nested ul was treated as an inline flex child. Now the
li uses flex-wrap with align-items: flex-start, and nested task lists
take full width with left padding for indentation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:49:10 +01:00
Frederic CabassutandClaude Opus 4.6 8b9f9dfee8 feat: add overflow menu with checklist actions and action bar cleanup
- Add vertical dots (⋮) overflow menu to NoteEditor bottom action bar
- New checklist actions: "Delete checked" and "Uncheck all"
- Move secondary actions to menu: checklist toggle, markdown toggle,
  version history, move to trash
- Promote archive to the action bar for quick access
- Keep in bar: color picker, image upload, share, archive, ⋮, close
- Menu renders as fixed-position portal to escape card overflow-hidden
- Click-outside handler closes the menu
- Update e2e tests to use overflow menu for checklist/markdown toggles

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 19:47:02 +01:00
Frederic CabassutandClaude Opus 4.6 5a2a63e2bb fix: prevent 3-way merge from reverting sequential single-user edits
The server's 3-way content merge was incorrectly triggering on sequential
edits by the same user. When a user checked then unchecked a checklist
item, the merge saw local === base (both unchecked) and kept the server's
checked state, silently discarding the uncheck.

Now the merge only runs when there's an actual concurrent edit
(baseVersion < existing.version), meaning another user saved between the
client's load and this save. For single-user sequential edits, the
client's content is used directly.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-22 18:51:14 +01:00
Frederic CabassutandClaude Opus 4.6 2267474cd5 fix: address PR review — a11y, checked detection, test fixes
- Remove unused flushSync import
- Fix removeItem single-item branch: emit change and trigger reactivity
- Remove misleading role="button" + tabindex from drag handle
- Use anchored regex for checked detection instead of line.includes('[x]')
  in NoteCard and public share page (prevents false positive on item text)
- Fix NoteCard preview E2E test: scope assertion to specific card, use
  unique title to avoid collision
- Restore arrow key navigation E2E test

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:51:46 +01:00
Frederic CabassutandClaude Opus 4.6 0124c5e7f6 fix: unchecked child repositions after its parent and siblings
When unchecking a child from the done section, it stayed at the end of
the items array (where done items live), appearing as the last active
item instead of under its parent. Now repositions after the last sibling
or parent in the array.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:51:46 +01:00
Frederic CabassutandClaude Opus 4.6 087fe8c713 feat: NoteCard and public share page support nested checklist items
Update checklist preview parsers to detect 2-space child prefix and
render indented items with extra left padding.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:51:46 +01:00
Frederic CabassutandClaude Opus 4.6 2306883503 feat: nested checklist UI — indentation, parent labels, drag-to-indent
Checklist component changes:
- Import logic from checklist utility module
- Tab/Shift+Tab indent/outdent with sibling adoption
- Indented rendering (pl-8) for child items in active section
- Done section groups checked children under read-only parent labels
- Enter on child creates sibling at same indent level
- Backspace on parent orphans children to top-level
- Directional lock drag: horizontal = indent/outdent, vertical = reorder
  (uses window capture-phase listeners + pending indent applied after
  DnD finalize to avoid corrupting svelte-dnd-action internal state)
- flushSync for state updates from window event listeners

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:51:46 +01:00
Frederic CabassutandClaude Opus 4.6 9fb8acc719 feat: checklist utility module with parentId and nesting logic
Extract checklist logic into testable utility module:
- ChecklistItem type with parentId field for single-level nesting
- parseChecklist/serializeChecklist with 2-space prefix for children
- toggleItemWithCascade: parent check/uncheck cascades to children
- indentItem/outdentItem with sibling adoption rules
- 21 unit tests covering parse, serialize, toggle, indent, outdent

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 23:51:46 +01:00
Claude 6278f11184 fix: preserve local-only notes during loadNotes merge
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
2026-03-21 20:27:38 +01:00
Claude ee017ab550 fix: prevent checklist state loss from sync race condition
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
2026-03-21 20:27:38 +01:00
Frederic CabassutandClaude Opus 4.6 f9d29a192a feat: arrow key navigation between checklist items
ArrowUp/ArrowDown move focus between active checklist inputs.
At list boundaries, focus stays on the current item (no wrap).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 10:17:58 +01:00
Frederic CabassutandClaude Opus 4.6 e0eca4228b fix: checklist Enter key focuses new item on reopened notes
The addItem() focus logic used positional indexing (inputs[afterIndex+1])
but afterIndex came from the full items[] array while DOM inputs only
render active (unchecked) items. On reopened notes, checked items are
interleaved in the parsed content, causing the index offset to be wrong.

Fix: use data-item-id attribute for targeted querySelector lookup.
Test: reopened note with interleaved checked/unchecked items.

Introduced in e5688c9 (active/done split).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 10:11:00 +01:00
Frederic CabassutandClaude Opus 4.6 4eb97b86f9 fix: address PR review comments
- Wrap readFile in try/catch, return 404 for missing attachment files
- Remove unused CollaboratorPopover import from NoteCard
- Make darkMode a proper $state rune for reactivity (add svelte-runes
  polyfill for Vitest so $state works in unit tests)
- Add error toasts for failed share link toggle (non-2xx responses)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00
Frederic CabassutandClaude Sonnet 4.6 5dfde02749 feat: use dark note card colors in all components
Switch NoteCard, NoteEditor, and ColorPicker from static NOTE_COLORS
lookups to getNoteColor(color, getIsDarkMode()), so inline background
styles react to the active theme. Also fix the theme preference button
type cast in settings/preferences to resolve the svelte-check error.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00
Frederic Cabassut fa29321a62 feat: add theme toggle to preferences settings 2026-03-21 08:47:20 +01:00
Frederic Cabassut cdf242a692 feat: add reactive theme watcher in root layout 2026-03-21 08:47:20 +01:00
Frederic Cabassut d6638260a3 feat: add FOUC-prevention script for dark mode 2026-03-21 08:47:20 +01:00
Frederic CabassutandClaude Sonnet 4.6 b820ee3368 feat: add dark note card colors and getNoteColor helper
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00
Frederic CabassutandClaude Sonnet 4.6 80d18478da feat: add theme utility with applyTheme and isDarkMode
Adds src/lib/utils/theme.svelte.ts with applyTheme() (sets data-theme
attribute and meta theme-color) and getIsDarkMode(). Uses a plain
variable instead of $state since Vitest's node environment does not
compile Svelte 5 runes in .svelte.ts files. Adds jsdom dev dependency
and $app/environment alias in vitest config to support DOM-level unit
tests; fixes preferences.test.ts to include the theme default added
in Task 2.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00
Frederic Cabassut 35cd888547 feat: add theme preference type (system/light/dark) 2026-03-21 08:47:20 +01:00
Frederic CabassutandClaude Sonnet 4.6 73b07a3be6 feat: add dark theme CSS variables and grid-dot extraction
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 08:47:20 +01:00
Frederic CabassutandClaude Opus 4.6 92d0581186 fix: address PR review — sanitize attachments, SSR safety, a11y
- Strip server paths from public attachment responses (new PublicAttachment type)
- Use $page.url.origin instead of window.location.origin (SSR-safe)
- Fix toggle switch left-4.5 → left-[1.125rem] (valid Tailwind)
- Add aria-label to SharingIndicator button for screen readers
- Add opportunistic pruning to IP rate limiter to prevent unbounded growth
- Reduce shared attachment cache to 1 hour (respect share revocation)
- Update OpenAPI schema with PublicAttachment type
- Add unit test asserting server paths are not leaked

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 22:06:41 +01:00