- The composer only enables its scroller once the text is taller than the
seven-line max, so it no longer flashes on every growth step.
- The composer re-asserts first responder only when canStealFocus is true;
it is off while a modal field or the sync overlay has the keyboard, so
typing in the edit dialog, search and sync fields no longer bounces back.
- The edit dialog is a ComposerView now: Return saves, Shift+Return inserts
a newline, Esc cancels, and paste uses the same path as the composer.
- Shift+Return never actually worked: returning false from doCommandBy does
not fall through to the default newline insertion, so the event died. The
newline is inserted explicitly and the Shift state comes from the keyDown.
@Published fires even when the assigned value is unchanged, so the poll's
unconditional lastError = nil invalidated PanelView — which observes
SyncService — every 5 seconds, forever, hidden or visible. Over five days
that was ~185 CPU-minutes and ~190 MB, and clicks started to lag.
- Route poll state through setError, which skips equal values.
- Watch SyncService from a tiny SyncDot view instead of the panel body.
- Skip the URL cache on polls and give them timeouts.
- Only reassign the composer placeholder when it changed.
Long prompts hit a single-line field before; the composer is now an
NSTextView that wraps and grows up to seven lines, keeps Return as send,
and uses Shift+Return for a newline. Launch at Login already existed in
the menu bar icon's right-click menu, so it is now also a toggle in the
sync panel where it is easier to find.