diff --git a/frontend/src/App.svelte b/frontend/src/App.svelte index bdf2f49..89ccf37 100644 --- a/frontend/src/App.svelte +++ b/frontend/src/App.svelte @@ -98,6 +98,14 @@ let renameValue = '' let renameError = '' + // ===== Sync state ===== + let showSettings = false + let syncStatus = null + let syncLoading = false + let syncServerUrl = '' + let syncApiKey = '' + let syncResult = '' + const tabs = [ { id: 'overview', label: 'Обзор' }, { id: 'notes', label: 'Заметки' }, @@ -138,6 +146,7 @@ window.addEventListener('keydown', handleKeydown) loading = false + loadSyncStatus() }) onDestroy(() => { @@ -882,6 +891,53 @@ error = String(e) } } + + // ===== Sync ===== + async function loadSyncStatus() { + try { + syncStatus = await wailsCall('SyncStatus') + } catch (e) { + syncStatus = { configured: false, serverUrl: '', deviceId: '', unpushedOps: 0, lastSyncAt: '' } + } + } + + function openSettings() { + showSettings = true + syncServerUrl = syncStatus?.serverUrl || '' + syncApiKey = '' + syncResult = '' + } + + function closeSettings() { + showSettings = false + syncResult = '' + } + + async function saveSyncConfig() { + syncLoading = true + syncResult = '' + try { + await wailsCall('SyncConfigure', syncServerUrl, syncApiKey) + syncResult = 'ok' + await loadSyncStatus() + } catch (e) { + syncResult = 'err: ' + String(e) + } + syncLoading = false + } + + async function runSyncNow() { + syncLoading = true + syncResult = '' + try { + const r = await wailsCall('SyncNow') + syncResult = 'pushed ' + r.pushed + ', pulled ' + r.pulled + ' (rev ' + r.serverRevision + ')' + await loadSyncStatus() + } catch (e) { + syncResult = 'err: ' + String(e) + } + syncLoading = false + }
@@ -914,7 +970,12 @@
{/if} - + @@ -1425,6 +1486,39 @@ on:cancel={handleCancel} /> {/if} + + {#if showSettings} + + {/if} @@ -1636,4 +1730,15 @@ .activity-feed-type { font-size: 11px; color: #666; } .activity-feed-target { font-size: 10px; color: #555; background: #1e1e2e; padding: 1px 6px; border-radius: 8px; } .activity-feed-time { font-size: 11px; color: #555; } + +/* Sync */ +.settings-btn { background: none; border: none; color: #555; cursor: pointer; padding: 4px; display: inline-flex; align-items: center; border-radius: 4px; margin-left: auto; } +.settings-btn:hover { color: #ccc; background: #222233; } +.modal-sync { width: 460px; } +.sync-status { background: #13131f; border-radius: 8px; padding: 12px; margin-bottom: 16px; } +.sync-row { display: flex; justify-content: space-between; padding: 4px 0; font-size: 13px; } +.sync-label { color: #666; } +.sync-value { color: #e4e4ef; } +.sync-value.mono { font-family: 'SF Mono', 'Fira Code', monospace; font-size: 12px; } +.sync-result { font-size: 12px; color: #6366f1; padding: 4px 0; }