From 20551085782a19f8bc2ed7667f028e1cd1f11c7a Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Mon, 8 Jun 2026 00:06:12 +0400 Subject: [PATCH] feat: add cookie export --- src/components/profile-info-dialog.tsx | 83 +++++++++++++++++++++++++- 1 file changed, 81 insertions(+), 2 deletions(-) diff --git a/src/components/profile-info-dialog.tsx b/src/components/profile-info-dialog.tsx index 53c3a88..3453891 100644 --- a/src/components/profile-info-dialog.tsx +++ b/src/components/profile-info-dialog.tsx @@ -2,6 +2,8 @@ import { invoke } from "@tauri-apps/api/core"; import { listen } from "@tauri-apps/api/event"; +import { save } from "@tauri-apps/plugin-dialog"; +import { writeTextFile } from "@tauri-apps/plugin-fs"; import * as React from "react"; import { useTranslation } from "react-i18next"; import { FaApple, FaLinux, FaWindows } from "react-icons/fa"; @@ -11,6 +13,7 @@ import { LuClipboardCheck, LuCookie, LuCopy, + LuDownload, LuFingerprint, LuGlobe, LuGroup, @@ -39,6 +42,12 @@ import { DialogHeader, DialogTitle, } from "@/components/ui/dialog"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; import { Input } from "@/components/ui/input"; import { ScrollArea } from "@/components/ui/scroll-area"; import { @@ -1511,6 +1520,41 @@ function CookiesSectionInline({ }; }, [profile.id, isRunning, t]); + const [isExporting, setIsExporting] = React.useState(false); + + // Export all of this profile's cookies in one of the same formats import + // accepts (JSON or Netscape). The backend formats every cookie; we just pick + // a destination file. + const handleExport = React.useCallback( + async (format: "json" | "netscape") => { + setIsExporting(true); + try { + const content = await invoke("export_profile_cookies", { + profileId: profile.id, + format, + }); + const ext = format === "json" ? "json" : "txt"; + const filePath = await save({ + defaultPath: `${profile.name}_cookies.${ext}`, + filters: [ + { + name: format === "json" ? "JSON" : "Text", + extensions: [ext], + }, + ], + }); + if (!filePath) return; + await writeTextFile(filePath, content); + showSuccessToast(t("cookies.export.success")); + } catch (e) { + showErrorToast(translateBackendError(t as never, e)); + } finally { + setIsExporting(false); + } + }, + [profile.id, profile.name, t], + ); + const domains = stats?.domains ?? []; return ( @@ -1521,6 +1565,41 @@ function CookiesSectionInline({ {t("profileInfo.sections.cookies")}
+ + + + + + { + void handleExport("json"); + }} + > + {t("cookies.export.json")} + + { + void handleExport("netscape"); + }} + > + {t("cookies.export.netscape")} + + + {onImportCookies && ( )} {onCopyCookies && ( @@ -1542,7 +1621,7 @@ function CookiesSectionInline({ onClick={onCopyCookies} > - {t("profiles.actions.copyCookies")} + {t("common.buttons.copy")} )}