import { useState, useEffect, useRef } from "react"; import { AnimatedModal } from "../ui/AnimatedModal"; import { Button } from "../ui/Button"; import { Select } from "../ui/Select"; import { save } from "@tauri-apps/plugin-dialog"; import { listen } from "@tauri-apps/api/event"; import { exportQueryToFile, cancelQuery } from "../../lib/commands"; interface StreamExportDialogProps { connectionId: string; sql: string; onClose: () => void; } const FORMAT_OPTIONS = [ { value: "csv", label: "CSV" }, { value: "jsonl", label: "JSON Lines" }, { value: "json", label: "JSON" }, ]; const EXTENSIONS: Record = { csv: "csv", jsonl: "jsonl", json: "json", }; export function StreamExportDialog({ connectionId, sql, onClose }: StreamExportDialogProps) { const [format, setFormat] = useState("csv"); const [path, setPath] = useState(""); const [running, setRunning] = useState(false); const [progress, setProgress] = useState(0); const [done, setDone] = useState(false); const [rowsWritten, setRowsWritten] = useState(null); const [error, setError] = useState(null); const jobIdRef = useRef(null); // Filter export-progress events to this job so concurrent exports don't // clobber each other's progress bar. useEffect(() => { const unlisten = listen<{ job_id: string; percent: number }>("export-progress", (e) => { if (e.payload.job_id === jobIdRef.current) { setProgress(e.payload.percent); } }); return () => { unlisten.then((fn) => fn()); }; }, []); const handleChooseFile = async () => { const ext = EXTENSIONS[format] ?? "csv"; const picked = await save({ defaultPath: `export.${ext}`, filters: [{ name: format.toUpperCase(), extensions: [ext] }], }); if (picked) setPath(picked); }; const handleExport = async () => { setRunning(true); setError(null); setDone(false); setProgress(0); try { let target = path; if (!target) { const ext = EXTENSIONS[format] ?? "csv"; target = (await save({ defaultPath: `export.${ext}`, filters: [{ name: format.toUpperCase(), extensions: [ext] }], })) ?? ""; if (!target) { setRunning(false); return; } setPath(target); } jobIdRef.current = `export-${Date.now()}`; const res = await exportQueryToFile(connectionId, sql, format, target); setDone(true); setProgress(100); setRowsWritten(res.rows_written); } catch (e) { setError(e instanceof Error ? e.message : String(e)); } finally { setRunning(false); } }; const handleCancel = async () => { try { await cancelQuery(connectionId); } catch { // ignore — the export may already have finished } setRunning(false); }; return (

Export all rows to file