feat(desktop): sync knowledge, recordings and shared settings; play any recording

Knowledge documents travel as source-text chunks; each surface embeds them
with its own model, the server index is requested through embed-chunks, and
documents from the phone are stored without a file and indexed from their
chunks. Chunk text is now kept when local embedding fails, so reindexing no
longer needs the original file.

Recordings upload to the mobile storage contract (audio bucket under the
user's folder plus an audio_files row, 50 MiB cap, a Settings > Cloud
toggle) and are removed with their record. The history card gains a play
button that uses the local file or, for phone recordings, a signed URL.

Language (ko/en), system/light/dark theme, auto-polish and the active user
command follow the phone's user_settings with its revision rule; changes that
arrive from the phone reach the open window.
This commit is contained in:
Yun Chan 2026-09-27 14:44:56 +09:00
parent cee4ab9317
commit 9a8f7e6aa6
34 changed files with 1405 additions and 64 deletions

View file

@ -146,6 +146,8 @@ export const IPC_CHANNELS = {
DELETE: 'history:delete',
DELETE_ALL: 'history:deleteAll',
SET_FAVORITE: 'history:setFavorite',
/** 녹음 재생 소스: 로컬 파일 바이트 또는 다른 기기 녹음의 서명 URL */
GET_AUDIO: 'history:getAudio',
SEARCH: 'history:search',
EXPORT: 'history:export',
// Main → Renderer events

View file

@ -485,6 +485,8 @@ export interface AppConfig {
* 원격 해제된 id는 재사용할 수 없어(서버 규칙) 재로그인 때 새로 발급한다.
*/
deviceInstallationId: string | null
/** 로그인 시 녹음 파일도 클라우드에 올려 모바일·웹에서 재생 (파일당 50MiB 이하) */
cloudSyncAudio: boolean
/** 빅뱅 Phase 2: 첫 실행 온보딩 완료 여부 (로컬 모드 entry point) */
onboardingCompleted: boolean
/** Phase 6: Custom instructions (CustomInstructionService) */
@ -660,6 +662,11 @@ export interface HistorySetFavoriteParams {
isFavorite: boolean
}
/** 녹음 재생 소스. 이 기기에서 녹음했으면 파일 바이트, 모바일·웹 녹음이면 5분짜리 서명 URL */
export type HistoryAudioSource =
| { kind: 'local'; bytes: Uint8Array; mimeType: string }
| { kind: 'remote'; url: string }
export interface HistorySearchParams {
query: string
page: number

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Schließe die Anmeldung im Browser ab.",
"cloudSync.signedOut": "Abgemeldet. Die Einträge auf diesem Computer bleiben erhalten.",
"cloudSync.done": "Synchronisierung abgeschlossen ({{count}} übernommen)",
"cloudSync.doneWithErrors": "Synchronisierung abgeschlossen ({{count}} übernommen, {{errors}} fehlgeschlagen – nächster Lauf versucht es erneut)"
"cloudSync.doneWithErrors": "Synchronisierung abgeschlossen ({{count}} übernommen, {{errors}} fehlgeschlagen – nächster Lauf versucht es erneut)",
"history.playAudio": "Aufnahme abspielen",
"history.stopAudio": "Wiedergabe stoppen",
"history.audioUnavailable": "Keine Aufnahme zum Abspielen (Aufnahmen anderer Geräte erst nach deren Synchronisierung)",
"cloudSync.audio": "Aufnahmen ebenfalls synchronisieren",
"cloudSync.audioDesc": "Lädt Aufnahmen hoch, damit Handy und Web sie abspielen können. Dateien über 50 MB bleiben auf diesem Computer."
}

View file

@ -1927,5 +1927,10 @@
"mobile.devices.platform.ios": "iPhone / iPad",
"mobile.devices.platform.web": "Web browser",
"mobile.devices.platform.windows": "Windows desktop app",
"mobile.devices.platform.macos": "macOS desktop app"
"mobile.devices.platform.macos": "macOS desktop app",
"history.playAudio": "Play recording",
"history.stopAudio": "Stop playback",
"history.audioUnavailable": "No recording to play (recordings from another device play once that device has synced)",
"cloudSync.audio": "Sync recordings too",
"cloudSync.audioDesc": "Uploads recordings so the phone and web can play them. Files over 50 MB stay on this computer."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Termina de iniciar sesión en el navegador.",
"cloudSync.signedOut": "Sesión cerrada. Los registros de este equipo se conservan.",
"cloudSync.done": "Sincronización completa ({{count}} aplicados)",
"cloudSync.doneWithErrors": "Sincronización completa ({{count}} aplicados, {{errors}} fallidos: se reintentan en la próxima)"
"cloudSync.doneWithErrors": "Sincronización completa ({{count}} aplicados, {{errors}} fallidos: se reintentan en la próxima)",
"history.playAudio": "Reproducir grabación",
"history.stopAudio": "Detener",
"history.audioUnavailable": "No hay grabación para reproducir (las de otro dispositivo se reproducen cuando ese dispositivo sincroniza)",
"cloudSync.audio": "Sincronizar también las grabaciones",
"cloudSync.audioDesc": "Sube las grabaciones para reproducirlas en el móvil y la web. Los archivos de más de 50 MB se quedan en este equipo."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Terminez la connexion dans votre navigateur.",
"cloudSync.signedOut": "Déconnecté. Les enregistrements de cet ordinateur sont conservés.",
"cloudSync.done": "Synchronisation terminée ({{count}} appliqués)",
"cloudSync.doneWithErrors": "Synchronisation terminée ({{count}} appliqués, {{errors}} en échec — réessayés à la prochaine)"
"cloudSync.doneWithErrors": "Synchronisation terminée ({{count}} appliqués, {{errors}} en échec — réessayés à la prochaine)",
"history.playAudio": "Écouter l'enregistrement",
"history.stopAudio": "Arrêter la lecture",
"history.audioUnavailable": "Aucun enregistrement à lire (ceux d'un autre appareil sont disponibles après sa synchronisation)",
"cloudSync.audio": "Synchroniser aussi les enregistrements",
"cloudSync.audioDesc": "Envoie les enregistrements pour les écouter sur mobile et sur le web. Les fichiers de plus de 50 Mo restent sur cet ordinateur."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "ブラウザでログインを完了してください。",
"cloudSync.signedOut": "ログアウトしました。このコンピューターの記録はそのまま残ります。",
"cloudSync.done": "同期完了({{count}} 件反映)",
"cloudSync.doneWithErrors": "同期完了({{count}} 件反映、{{errors}} 件失敗 — 次回の同期で再試行)"
"cloudSync.doneWithErrors": "同期完了({{count}} 件反映、{{errors}} 件失敗 — 次回の同期で再試行)",
"history.playAudio": "録音を再生",
"history.stopAudio": "再生を停止",
"history.audioUnavailable": "再生できる録音がありません(他の端末の録音は、その端末の同期後に再生できます)",
"cloudSync.audio": "録音ファイルも同期",
"cloudSync.audioDesc": "録音をクラウドにアップロードし、モバイルやウェブで再生できるようにします。50MBを超えるファイルはこのコンピューターにのみ保存されます。"
}

View file

@ -1934,5 +1934,10 @@
"mobile.devices.platform.ios": "iPhone·iPad",
"mobile.devices.platform.web": "웹 브라우저",
"mobile.devices.platform.windows": "Windows 데스크톱 앱",
"mobile.devices.platform.macos": "macOS 데스크톱 앱"
"mobile.devices.platform.macos": "macOS 데스크톱 앱",
"history.playAudio": "녹음 듣기",
"history.stopAudio": "재생 멈춤",
"history.audioUnavailable": "들을 수 있는 녹음이 없습니다 (다른 기기 녹음은 그 기기의 동기화가 끝나면 들을 수 있어요)",
"cloudSync.audio": "녹음 파일도 동기화",
"cloudSync.audioDesc": "녹음을 클라우드에 올려 모바일·웹에서 들을 수 있게 합니다. 50MB를 넘는 파일은 이 컴퓨터에만 둡니다."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Conclua o login no navegador.",
"cloudSync.signedOut": "Você saiu. Os registros deste computador continuam aqui.",
"cloudSync.done": "Sincronização concluída ({{count}} aplicados)",
"cloudSync.doneWithErrors": "Sincronização concluída ({{count}} aplicados, {{errors}} com falha — tentados de novo na próxima)"
"cloudSync.doneWithErrors": "Sincronização concluída ({{count}} aplicados, {{errors}} com falha — tentados de novo na próxima)",
"history.playAudio": "Ouvir gravação",
"history.stopAudio": "Parar",
"history.audioUnavailable": "Nenhuma gravação para ouvir (as de outro dispositivo tocam depois que ele sincroniza)",
"cloudSync.audio": "Sincronizar também as gravações",
"cloudSync.audioDesc": "Envia as gravações para ouvir no celular e na web. Arquivos acima de 50 MB ficam neste computador."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Завершите вход в браузере.",
"cloudSync.signedOut": "Вы вышли. Записи на этом компьютере сохранены.",
"cloudSync.done": "Синхронизация завершена (применено: {{count}})",
"cloudSync.doneWithErrors": "Синхронизация завершена (применено: {{count}}, ошибок: {{errors}} — повтор при следующей синхронизации)"
"cloudSync.doneWithErrors": "Синхронизация завершена (применено: {{count}}, ошибок: {{errors}} — повтор при следующей синхронизации)",
"history.playAudio": "Прослушать запись",
"history.stopAudio": "Остановить",
"history.audioUnavailable": "Нет записи для воспроизведения (записи с другого устройства доступны после его синхронизации)",
"cloudSync.audio": "Синхронизировать и записи",
"cloudSync.audioDesc": "Загружает записи, чтобы их можно было слушать на телефоне и в вебе. Файлы больше 50 МБ остаются на этом компьютере."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "โปรดเข้าสู่ระบบให้เสร็จในเบราว์เซอร์",
"cloudSync.signedOut": "ออกจากระบบแล้ว ข้อมูลบนคอมพิวเตอร์นี้ยังอยู่ครบ",
"cloudSync.done": "ซิงก์เสร็จแล้ว ({{count}} รายการ)",
"cloudSync.doneWithErrors": "ซิงก์เสร็จแล้ว ({{count}} รายการ, ล้มเหลว {{errors}} — จะลองใหม่รอบหน้า)"
"cloudSync.doneWithErrors": "ซิงก์เสร็จแล้ว ({{count}} รายการ, ล้มเหลว {{errors}} — จะลองใหม่รอบหน้า)",
"history.playAudio": "เล่นเสียงที่บันทึก",
"history.stopAudio": "หยุดเล่น",
"history.audioUnavailable": "ไม่มีเสียงให้เล่น (เสียงจากอุปกรณ์อื่นจะเล่นได้หลังอุปกรณ์นั้นซิงก์)",
"cloudSync.audio": "ซิงก์ไฟล์เสียงด้วย",
"cloudSync.audioDesc": "อัปโหลดไฟล์เสียงเพื่อเล่นบนมือถือและเว็บ ไฟล์ที่ใหญ่กว่า 50MB จะเก็บไว้ในเครื่องนี้เท่านั้น"
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "Hãy hoàn tất đăng nhập trên trình duyệt.",
"cloudSync.signedOut": "Đã đăng xuất. Dữ liệu trên máy này vẫn được giữ nguyên.",
"cloudSync.done": "Đồng bộ xong (áp dụng {{count}})",
"cloudSync.doneWithErrors": "Đồng bộ xong ({{count}} áp dụng, {{errors}} lỗi — sẽ thử lại lần sau)"
"cloudSync.doneWithErrors": "Đồng bộ xong ({{count}} áp dụng, {{errors}} lỗi — sẽ thử lại lần sau)",
"history.playAudio": "Phát bản ghi",
"history.stopAudio": "Dừng phát",
"history.audioUnavailable": "Không có bản ghi để phát (bản ghi từ thiết bị khác phát được sau khi thiết bị đó đồng bộ)",
"cloudSync.audio": "Đồng bộ cả bản ghi âm",
"cloudSync.audioDesc": "Tải bản ghi lên để nghe trên điện thoại và web. Tệp lớn hơn 50MB chỉ lưu trên máy này."
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "請在瀏覽器中完成登入。",
"cloudSync.signedOut": "已登出。這台電腦上的紀錄會保留。",
"cloudSync.done": "同步完成(已套用 {{count}} 項)",
"cloudSync.doneWithErrors": "同步完成(已套用 {{count}} 項,{{errors}} 項失敗 — 下次同步時重試)"
"cloudSync.doneWithErrors": "同步完成(已套用 {{count}} 項,{{errors}} 項失敗 — 下次同步時重試)",
"history.playAudio": "播放錄音",
"history.stopAudio": "停止播放",
"history.audioUnavailable": "沒有可播放的錄音(其他裝置的錄音需在該裝置同步後才能播放)",
"cloudSync.audio": "同時同步錄音",
"cloudSync.audioDesc": "將錄音上傳到雲端,讓手機和網頁也能播放。超過 50MB 的檔案只保留在本機。"
}

View file

@ -541,5 +541,10 @@
"cloudSync.browserPending": "请在浏览器中完成登录。",
"cloudSync.signedOut": "已退出登录。本机上的记录会保留。",
"cloudSync.done": "同步完成(已应用 {{count}} 项)",
"cloudSync.doneWithErrors": "同步完成(已应用 {{count}} 项,{{errors}} 项失败 — 下次同步时重试)"
"cloudSync.doneWithErrors": "同步完成(已应用 {{count}} 项,{{errors}} 项失败 — 下次同步时重试)",
"history.playAudio": "播放录音",
"history.stopAudio": "停止播放",
"history.audioUnavailable": "没有可播放的录音(其他设备的录音需在该设备同步后才能播放)",
"cloudSync.audio": "同时同步录音",
"cloudSync.audioDesc": "将录音上传到云端,以便在手机和网页上播放。超过 50MB 的文件只保留在本机。"
}