export
This commit is contained in:
87
src/App.tsx
87
src/App.tsx
@@ -565,6 +565,13 @@ function AppShell({
|
||||
const [showInScope, setShowInScope] = useState<boolean>(true);
|
||||
const [visibilityOpen, setVisibilityOpen] = useState<boolean>(false);
|
||||
const [allVideoNames, setAllVideoNames] = useState<string[]>([]);
|
||||
// Some Save dialogs (notably GTK on Linux) hide the file-filter dropdown,
|
||||
// so users couldn't tell they were even picking a format. We surface the
|
||||
// choice directly in the side panel — this is the format the next Export
|
||||
// click will use.
|
||||
const [exportFormat, setExportFormat] = useState<
|
||||
"source" | "geojson" | "kml" | "csv"
|
||||
>("source");
|
||||
const DUP_EPS_KEY = "kml_map_tool.dup_eps_m";
|
||||
const [dupEpsM, setDupEpsM] = useState<number>(() => {
|
||||
if (typeof window === "undefined") return 3;
|
||||
@@ -3103,31 +3110,36 @@ function AppShell({
|
||||
async function handleExport() {
|
||||
if (busy) return;
|
||||
try {
|
||||
// The dropdown next to the button is the source of truth for format.
|
||||
// We seed the Save dialog with the matching extension so the user
|
||||
// doesn't have to hunt for the file-filter menu inside it.
|
||||
const extByFmt: Record<typeof exportFormat, string> = {
|
||||
source: "json",
|
||||
geojson: "geojson",
|
||||
kml: "kml",
|
||||
csv: "csv",
|
||||
};
|
||||
const wantExt = extByFmt[exportFormat];
|
||||
const picked = await save({
|
||||
filters: [
|
||||
{ name: "Source JSON (round-trip)", extensions: ["json"] },
|
||||
{ name: "GeoJSON", extensions: ["geojson"] },
|
||||
{ name: "KML", extensions: ["kml"] },
|
||||
{ name: "CSV", extensions: ["csv"] },
|
||||
],
|
||||
defaultPath: `assets.${wantExt}`,
|
||||
filters: [{ name: exportFormat.toUpperCase(), extensions: [wantExt] }],
|
||||
});
|
||||
if (typeof picked !== "string") return;
|
||||
const lower = picked.toLowerCase();
|
||||
const fmt = lower.endsWith(".kml")
|
||||
? "kml"
|
||||
: lower.endsWith(".csv")
|
||||
? "csv"
|
||||
: lower.endsWith(".json")
|
||||
? "source"
|
||||
: "geojson";
|
||||
// Auto-append the chosen extension if the user typed a bare name.
|
||||
let outPath = picked;
|
||||
if (!outPath.toLowerCase().endsWith(`.${wantExt}`)) {
|
||||
outPath = `${outPath}.${wantExt}`;
|
||||
}
|
||||
setBusy(true);
|
||||
const count = await invoke<number>("export_assets", {
|
||||
path: picked,
|
||||
format: fmt,
|
||||
path: outPath,
|
||||
format: exportFormat,
|
||||
});
|
||||
setStatus(`Exported ${count} asset(s) to ${picked}`);
|
||||
setStatus(`Exported ${count} asset(s) to ${outPath}`);
|
||||
showToast(`Exported ${count} asset(s) (${exportFormat})`);
|
||||
} catch (e) {
|
||||
setStatus(`Export failed: ${e}`);
|
||||
showToast(`Export failed: ${e}`);
|
||||
} finally {
|
||||
setBusy(false);
|
||||
}
|
||||
@@ -6165,15 +6177,40 @@ function AppShell({
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
className="primary-btn"
|
||||
style={{ marginTop: 6, background: "#2c3e50" }}
|
||||
onClick={handleExport}
|
||||
disabled={busy}
|
||||
title="Export all live assets. Pick .json for round-trip (re-importable), .geojson / .kml for GIS, .csv for spreadsheet."
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
gap: 6,
|
||||
marginTop: 6,
|
||||
alignItems: "stretch",
|
||||
}}
|
||||
>
|
||||
Export data…
|
||||
</button>
|
||||
<select
|
||||
value={exportFormat}
|
||||
onChange={(e) =>
|
||||
setExportFormat(
|
||||
e.target.value as "source" | "geojson" | "kml" | "csv",
|
||||
)
|
||||
}
|
||||
disabled={busy}
|
||||
title="File format for the next export"
|
||||
style={{ fontSize: 12, padding: "2px 4px" }}
|
||||
>
|
||||
<option value="source">JSON (round-trip + bbox)</option>
|
||||
<option value="geojson">GeoJSON</option>
|
||||
<option value="kml">KML</option>
|
||||
<option value="csv">CSV</option>
|
||||
</select>
|
||||
<button
|
||||
className="primary-btn"
|
||||
style={{ background: "#2c3e50", flex: 1 }}
|
||||
onClick={handleExport}
|
||||
disabled={busy}
|
||||
title="Export all live assets in the selected format. JSON is the only re-importable format and includes bbox overlays."
|
||||
>
|
||||
Export data…
|
||||
</button>
|
||||
</div>
|
||||
<h3 style={{ marginTop: 16 }}>Layers</h3>
|
||||
<div className="layer-toggles">
|
||||
{(
|
||||
|
||||
Reference in New Issue
Block a user