@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.
Closing the panel only hid it, and launching the app again did nothing
because the running instance ignored the reopen, so it looked like the
app refused to exit. Reopening now shows the panel, and the main menu
makes Cmd+Q and Cmd+W work while the panel has focus. Termination also
tears down the event tap, hotkeys and sync timers instead of leaving
them live.
A Postgres URL without an sslmode made lib/pq ask for TLS and die with
"SSL is not enabled on the server", which reads like a server fault when
it is really a connection-string default. The error now says what to
append, and the server retries briefly so a deploy is not marked
unhealthy just because Postgres was still starting.
The sync server URL also defaults to the hosted instance now, so a fresh
install needs no setup.
Polling re-downloaded the whole store, photos included, every five
seconds. The app now checks a tiny version endpoint and only fetches the
payload when it actually moved, and the server gained request logging,
graceful shutdown, and self-hosting docs so it can run on a VPS instead
of only on localhost.
💘 Generated with Crush
Assisted-by: Crush:kimi-k3
Notes and photos were stuck on one machine. A small Go/Postgres backend
stores each group's payload under a code that doubles as the shared
secret, so a second Mac just needs the code — no account or login.
Applying a remote change must not push it straight back, otherwise the
two Macs would bump the version back and forth forever, so payloads are
fingerprinted and identical ones are skipped.
💘 Generated with Crush
Assisted-by: Crush:kimi-k3