feat(export): Export/Import section in Settings → Storage (FR-STORE-4)
This commit is contained in:
1 parent
6129866ac5
commit
23cfca7ea2
1 file changed
+68
@@ -5,6 +5,7 @@
|
||||
import { onMount } from "svelte";
|
||||
import { settings } from "../stores/settings.svelte";
|
||||
import { calendar } from "../stores/calendar.svelte";
|
||||
import { meetings } from "../stores/meetings.svelte";
|
||||
import ConsentNotice from "../components/ConsentNotice.svelte";
|
||||
import HostedAiBanner from "../components/HostedAiBanner.svelte";
|
||||
import { open } from "@tauri-apps/plugin-dialog";
|
||||
@@ -34,6 +35,8 @@
|
||||
Globe,
|
||||
Download,
|
||||
Trash2,
|
||||
FolderInput,
|
||||
FolderOutput,
|
||||
} from "@lucide/svelte";
|
||||
import {
|
||||
OLLAMA_OPTIONS,
|
||||
@@ -503,6 +506,50 @@
|
||||
settings.setRetentionPolicy(age, size);
|
||||
}
|
||||
|
||||
// ---- Export / Import (FR-STORE-4) — move recordings between computers as
|
||||
// portable "bundle" folders (audio + transcript + notes + summary +
|
||||
// meeting.json manifest). Drop a bundle on any synced/removable drive, or a
|
||||
// sync target's local mount, to carry it across. ----
|
||||
let exportBusy = $state(false);
|
||||
let importBusy = $state(false);
|
||||
let ioStatus = $state<string | null>(null);
|
||||
let ioError = $state(false);
|
||||
|
||||
async function exportAllMeetings() {
|
||||
const dir = await open({ directory: true, title: "Export all meetings into…" });
|
||||
if (typeof dir !== "string") return;
|
||||
exportBusy = true;
|
||||
ioStatus = null;
|
||||
ioError = false;
|
||||
try {
|
||||
const n = await api.bulkExportMeetings(dir, "bundle");
|
||||
ioStatus = `Exported ${n} meeting${n === 1 ? "" : "s"}.`;
|
||||
} catch (e) {
|
||||
ioStatus = errorMessage(e);
|
||||
ioError = true;
|
||||
} finally {
|
||||
exportBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
async function importMeetings() {
|
||||
const dir = await open({ directory: true, title: "Import a bundle (or a folder of bundles)…" });
|
||||
if (typeof dir !== "string") return;
|
||||
importBusy = true;
|
||||
ioStatus = null;
|
||||
ioError = false;
|
||||
try {
|
||||
const n = await api.importMeetingBundle(dir);
|
||||
await meetings.load();
|
||||
ioStatus = `Imported ${n} meeting${n === 1 ? "" : "s"}.`;
|
||||
} catch (e) {
|
||||
ioStatus = errorMessage(e);
|
||||
ioError = true;
|
||||
} finally {
|
||||
importBusy = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- Add-target form ----
|
||||
const PROVIDERS = ["nextcloud", "owncloud", "cloudreve", "seafile", "synology", "generic"];
|
||||
let form = $state<SyncTargetConfig>(blankForm("webdav"));
|
||||
@@ -1022,6 +1069,27 @@
|
||||
/></label
|
||||
>
|
||||
</div>
|
||||
|
||||
<h4>Export & import</h4>
|
||||
<p class="muted">
|
||||
Move recordings between computers as portable bundle folders (audio, transcript, notes,
|
||||
summary, and a <code>meeting.json</code> manifest). Export to any folder — a synced drive,
|
||||
a USB stick, or a sync target's local mount — then import it on the other machine.
|
||||
Imported meetings get a fresh id, so re-importing never overwrites anything.
|
||||
</p>
|
||||
<div class="row btns">
|
||||
<button onclick={exportAllMeetings} disabled={exportBusy}>
|
||||
<FolderOutput size={14} aria-hidden="true" />
|
||||
{exportBusy ? "Exporting…" : "Export all meetings…"}
|
||||
</button>
|
||||
<button onclick={importMeetings} disabled={importBusy}>
|
||||
<FolderInput size={14} aria-hidden="true" />
|
||||
{importBusy ? "Importing…" : "Import meetings…"}
|
||||
</button>
|
||||
</div>
|
||||
{#if ioStatus}
|
||||
<p class={ioError ? "error" : "muted"}>{ioStatus}</p>
|
||||
{/if}
|
||||
</section>
|
||||
{:else if section === "calendar"}
|
||||
<section>
|
||||
|
||||
Reference in new issue
Block a user