feat(desktop): two-way cloud sync with mobile and web

Rewrites the desktop mirror as services/sync/SyncEngine: a persistent
outbox, per-account server-clock keyset cursors with paging, pulls that never
overwrite unsent local edits, deletions both ways through sync_tombstones and
per-row failure isolation. It now covers history titles and favorites,
dictionary, every meeting's memos and documents, memo tags, user commands and
dictation/meeting templates, and registers the desktop as a device that the
phone can disconnect.

Fixes shipped defects: the first pull after sign-in fetched nothing, only
the first meeting's children were pushed, team meetings leaked into the
personal database and lost team_id on re-push, and Realtime never connected
because Electron's Node 20 has no global WebSocket (ws is now the transport).
Anonymous local-mode records are imported into the first account that signs
in. The settings sync section is translated and shows pending/rejected
changes; synced screens reload on app:dataChanged.
This commit is contained in:
Yun Chan 2026-09-27 14:04:49 +09:00
parent b5c9ff9f31
commit 0a4f5aee64
49 changed files with 3940 additions and 1033 deletions

View file

@ -86,6 +86,19 @@ export function HistoryPage(): React.ReactElement {
[loadData],
)
const handleToggleFavorite = useCallback(async (entry: HistoryEntry) => {
const next = !entry.isFavorite
const apply = (value: boolean): void =>
setData((prev) =>
prev
? { ...prev, entries: prev.entries.map((e) => (e.id === entry.id ? { ...e, isFavorite: value } : e)) }
: prev,
)
apply(next)
const result = await window.electronAPI.history.setFavorite({ id: entry.id, isFavorite: next })
if (!result.success) apply(entry.isFavorite)
}, [])
const handleTagClick = useCallback((tag: string) => {
setActiveTag((prev) => (prev === tag ? null : tag))
setSearch('')
@ -232,6 +245,7 @@ export function HistoryPage(): React.ReactElement {
entry={entry}
onCopy={handleCopy}
onDelete={handleDelete}
onToggleFavorite={handleToggleFavorite}
showTags
onTagClick={handleTagClick}
/>