From 149f2c995d37e17a5a22ffffd459d8ebf4d7bb28 Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Mon, 6 Jul 2026 10:40:57 -0500 Subject: [PATCH 1/8] feat(sync): expose upload flags in SyncTargetInfo for the edit form row_to_info now returns upload_transcript/notes/summary/recording, trigger_on_finalize, allow_plaintext_lan, encrypt_before_upload so the UI can pre-fill an edit form accurately (still never the secret, FR-SYNC-6). --- src-tauri/src/models.rs | 9 +++++++++ src-tauri/src/sync/mod.rs | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/src-tauri/src/models.rs b/src-tauri/src/models.rs index 3126525..0decf5d 100644 --- a/src-tauri/src/models.rs +++ b/src-tauri/src/models.rs @@ -233,6 +233,15 @@ pub struct SyncTargetInfo { pub enabled: bool, pub third_party: bool, pub host: Option, + // Upload selection + options, so the UI can pre-fill an edit form (the secret + // is never included — FR-SYNC-6). + pub upload_transcript: bool, + pub upload_notes: bool, + pub upload_summary: bool, + pub upload_recording: bool, + pub trigger_on_finalize: bool, + pub allow_plaintext_lan: bool, + pub encrypt_before_upload: bool, } #[derive(Debug, Clone, Serialize, Deserialize)] diff --git a/src-tauri/src/sync/mod.rs b/src-tauri/src/sync/mod.rs index 453be6d..4b5cb3f 100644 --- a/src-tauri/src/sync/mod.rs +++ b/src-tauri/src/sync/mod.rs @@ -281,6 +281,13 @@ pub fn row_to_info(row: &crate::storage::SyncTargetRow) -> SyncTargetInfo { enabled: row.enabled, third_party: row.kind != "webdav", host, + upload_transcript: row.upload_transcript, + upload_notes: row.upload_notes, + upload_summary: row.upload_summary, + upload_recording: row.upload_recording, + trigger_on_finalize: row.trigger_on_finalize, + allow_plaintext_lan: row.allow_plaintext_lan, + encrypt_before_upload: row.encrypt_before_upload, } } -- 2.34.1 From 45b2acbbc664fa19909d943b2d162b65e1a7b3a5 Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Mon, 6 Jul 2026 10:40:59 -0500 Subject: [PATCH 2/8] feat(api): add upload flags to SyncTargetInfo type --- src/lib/api.ts | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/lib/api.ts b/src/lib/api.ts index f530ea5..a021996 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -202,6 +202,13 @@ export interface SyncTargetInfo { enabled: boolean; third_party: boolean; host: string | null; + upload_transcript: boolean; + upload_notes: boolean; + upload_summary: boolean; + upload_recording: boolean; + trigger_on_finalize: boolean; + allow_plaintext_lan: boolean; + encrypt_before_upload: boolean; } // privacy_self_check() response (FR-SEC-2) — proves local-only handling. -- 2.34.1 From 496a3372c43a141a89bd1141d724d1348615809c Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Mon, 6 Jul 2026 10:41:01 -0500 Subject: [PATCH 3/8] feat(sync): store.updateTarget() to edit a saved target --- src/lib/stores/settings.svelte.ts | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/lib/stores/settings.svelte.ts b/src/lib/stores/settings.svelte.ts index cf6134b..d4bc3e1 100644 --- a/src/lib/stores/settings.svelte.ts +++ b/src/lib/stores/settings.svelte.ts @@ -248,6 +248,19 @@ class SettingsStore { await this.loadPrivacy(); } + async updateTarget(config: SyncTargetConfig & { id: string }) { + try { + const updated = await api.updateSyncTarget(config); + this.targets = this.targets.map((t) => (t.id === config.id ? updated : t)); + } catch { + this.backendStub = true; + this.targets = this.targets.map((t) => + t.id === config.id ? { ...t, ...stubTarget(config), id: config.id } : t, + ); + } + await this.loadPrivacy(); + } + async removeTarget(id: string) { this.targets = this.targets.filter((t) => t.id !== id); try { @@ -297,6 +310,13 @@ function stubTarget(c: SyncTargetConfig): SyncTargetInfo { enabled: c.enabled ?? false, third_party: c.kind !== "webdav", host, + upload_transcript: c.upload_transcript ?? true, + upload_notes: c.upload_notes ?? true, + upload_summary: c.upload_summary ?? true, + upload_recording: c.upload_recording ?? false, + trigger_on_finalize: c.trigger_on_finalize ?? true, + allow_plaintext_lan: c.allow_plaintext_lan ?? false, + encrypt_before_upload: c.encrypt_before_upload ?? false, }; } -- 2.34.1 From fb790091fdfb6df4389622759fb0ce9da2e5b8ce Mon Sep 17 00:00:00 2001 From: iamdoubz <> Date: Mon, 6 Jul 2026 10:41:02 -0500 Subject: [PATCH 4/8] feat(settings): edit a saved sync target (fix URL/username/options) Each webdav target gets an Edit button that loads it into the form; save routes to update_sync_target. Password left blank keeps the stored one; kind is locked during edit. Fixes not being able to correct a mistyped Server URL. --- src/lib/views/Settings.svelte | 77 ++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 15 deletions(-) diff --git a/src/lib/views/Settings.svelte b/src/lib/views/Settings.svelte index 73d29f4..a3727b2 100644 --- a/src/lib/views/Settings.svelte +++ b/src/lib/views/Settings.svelte @@ -8,7 +8,7 @@ import ConsentNotice from "../components/ConsentNotice.svelte"; import { open } from "@tauri-apps/plugin-dialog"; import { api, errorMessage, events } from "../api"; - import type { BackendId, SyncKind, SyncTargetConfig } from "../api"; + import type { BackendId, SyncKind, SyncTargetConfig, SyncTargetInfo } from "../api"; import { trapFocus } from "../actions/trapFocus"; import { X, @@ -343,6 +343,8 @@ // ---- Add-target form ---- const PROVIDERS = ["nextcloud", "owncloud", "cloudreve", "seafile", "synology", "generic"]; let form = $state(blankForm("webdav")); + // When set, the form is editing an existing target rather than adding one. + let editingId = $state(null); function blankForm(kind: SyncKind): SyncTargetConfig { return { @@ -371,11 +373,46 @@ const r = await settings.test(form); testResult = { ok: r.ok, message: r.message }; } - async function addTarget() { - await settings.addTarget(form); - form = blankForm(form.kind); + // Load an existing target into the form for editing. The secret is never + // returned to the UI (FR-SYNC-6), so it stays blank — left blank on save it + // keeps the stored password; typed, it rotates. No `id` on the form itself, so + // "Test connection" checks the typed values (needs the password re-entered). + function startEdit(t: SyncTargetInfo) { + editingId = t.id; + form = { + name: t.name, + kind: t.kind, + provider_hint: t.provider_hint ?? undefined, + base_url: t.base_url ?? "", + remote_base_path: t.remote_base_path, + username: t.username ?? "", + secret: "", + enabled: t.enabled, + upload_transcript: t.upload_transcript, + upload_notes: t.upload_notes, + upload_summary: t.upload_summary, + upload_recording: t.upload_recording, + trigger_on_finalize: t.trigger_on_finalize, + allow_plaintext_lan: t.allow_plaintext_lan, + encrypt_before_upload: t.encrypt_before_upload, + }; testResult = null; } + function cancelEdit() { + editingId = null; + form = blankForm("webdav"); + testResult = null; + } + async function saveTarget() { + if (editingId) { + await settings.updateTarget({ ...form, id: editingId }); + cancelEdit(); + } else { + await settings.addTarget(form); + form = blankForm(form.kind); + testResult = null; + } + } function linkOAuth() { // begin_oauth_link → loopback PKCE flow; the target is created in the // background on success (sync://linked), which the store handles. @@ -711,6 +748,9 @@ >{t.third_party ? "third-party" : "your server"} {t.host ?? t.kind} + {#if !t.third_party} + + {/if} @@ -719,14 +759,16 @@ {/if} -

Add a target

-
- {#each ["webdav", "onedrive", "dropbox", "box"] as k (k)} - - {/each} -
+

{editingId ? "Edit target" : "Add a target"}

+ {#if !editingId} +
+ {#each ["webdav", "onedrive", "dropbox", "box"] as k (k)} + + {/each} +
+ {/if} {#if form.kind === "webdav"}

@@ -753,7 +795,9 @@ >App password @@ -782,9 +826,12 @@

- {editingId ? "Save changes" : "Add target"} + {#if editingId} + + {/if} {#if testResult} {#if testResult.ok}