Commit graph

2 commits

Author SHA1 Message Date
cogwheel
8aa3826458
Fix citation links, note checklists, and Hermes PDF input (#665)
* Fix citations, note checklists, and Hermes PDFs

* Address Hermes attachment review feedback

* Wait for Hermes PDF capability discovery

* Handle mixed Hermes attachments during discovery

* Reject replaying Hermes PDF attachments
2026-08-25 19:47:30 +05:30
cogwheel
9e33709935
feat(notes): rich-text editing with Fleather (#518)
* feat(notes): rich-text editing with Fleather

Replace the plain-text note editor with Fleather, a WYSIWYG rich-text
editor built on the Parchment/Delta model. Markdown stays the canonical
stored format (content.md + derived content.html via parchment codecs;
content.json left null) so notes remain fully editable on the Open WebUI
web client.

- Add fleather + parchment dependencies.
- Add note_document_codec util wrapping the parchment markdown/HTML
  codecs with best-effort fallback for malformed input.
- Rewire the editor page: FleatherController + FleatherEditor with a
  formatting toolbar shown above the keyboard; word/char counts, dirty
  detection, auto-save, AI-enhance, and dictation all driven off the
  document. Drop the hand-rolled markdown->HTML converter.
- Hide underline/colour toolbar buttons that markdown cannot round-trip.
- Add codec round-trip + fallback unit tests.

* address greptile review feedback (greploop iteration 1)

- P1: keep _savedMarkdown baseline in sync after file-attachment saves
  (audio upload + file removal) so post-save edits aren't missed.
- P2: defer the delta->markdown re-encode from every keystroke to the
  debounced auto-save (authoritative dirty check), with an optimistic
  unsaved indicator.
- P2: cache the FleatherThemeData (didChangeDependencies) so it is not
  rebuilt twice per frame by the editor and toolbar.
- P2: correct the misleading _enhanceContent comment about _savedMarkdown.

* fix(notes): save on close, anchor toolbar over keyboard, drop lossy controls

Addresses two reported issues with the Fleather note editor:

- Formatting/edits lost on quick close: the debounced auto-save was
  cancelled by dispose if the editor closed within the 800ms window. Add
  a PopScope that flushes the pending save while still mounted before
  popping, plus a save-on-focus-loss safety net for non-pop exits.
- Toolbar rendered at the stats row when the keyboard was up: it was
  anchored at viewInsets.bottom, double-counting the inset since the
  scaffold already resizes above the keyboard. Anchor at bottom: 0.
- Also hide toolbar controls markdown can't round-trip (alignment,
  indentation, text direction) so users can't apply formatting that
  silently vanishes on save/reopen.

Verified on iOS simulator: bold applied + immediate close persists as
**markdown** and re-renders bold on reopen; toolbar now docks above the
keyboard.

* fix(notes): reload note on reopen; add safe pull-to-refresh

- Fix reopened note showing empty/stale content until an app restart.
  noteByIdProvider is keepAlive, so after an in-session edit it kept
  serving the note as first opened (e.g. empty for a new note). Invalidate
  it after every editor save so the next open re-reads the saved content.

- Add pull-to-refresh to the note editor. It flushes any pending edit,
  runs a full sync cycle (push outbox + pull) via the sync engine, then
  reloads from the reconciled LOCAL Drift row (new readLocalNote helper)
  rather than a fresh server fetch — so a server copy that's behind a
  not-yet-pushed local edit can't clobber it with stale/empty content.

Verified on iOS simulator: create note, type, close, reopen -> content
shows (previously empty); pull-to-refresh on an unsynced note keeps the
local content instead of wiping it.

* fix(notes): don't discard edits made during pull-to-refresh

If the user types while requestPull is in flight, those keystrokes re-set
_hasChanges and queue a fresh debounce. The refresh then overwrote the
controller and baseline, so the queued autosave saw no diff and the edits
were lost. Re-check _hasChanges after the awaits and bail out of the
reload when new edits arrived mid-flight, leaving them for their own
debounce to save. (greploop iteration 2)
2026-06-20 03:41:49 +05:30