aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/web/components/settings/ImportExport.tsx30
-rw-r--r--apps/web/lib/i18n/locales/en/translation.json1
-rw-r--r--apps/web/lib/i18n/locales/es/translation.json3
-rw-r--r--apps/web/lib/importBookmarkParser.ts40
4 files changed, 72 insertions, 2 deletions
diff --git a/apps/web/components/settings/ImportExport.tsx b/apps/web/components/settings/ImportExport.tsx
index d6086c97..ab4fb640 100644
--- a/apps/web/components/settings/ImportExport.tsx
+++ b/apps/web/components/settings/ImportExport.tsx
@@ -16,6 +16,7 @@ import {
parseNetscapeBookmarkFile,
parseOmnivoreBookmarkFile,
parsePocketBookmarkFile,
+ parseTabSessionManagerStateFile,
} from "@/lib/importBookmarkParser";
import { cn } from "@/lib/utils";
import { useMutation } from "@tanstack/react-query";
@@ -161,7 +162,13 @@ export function ImportExportRow() {
source,
}: {
file: File;
- source: "html" | "pocket" | "omnivore" | "hoarder" | "linkwarden";
+ source:
+ | "html"
+ | "pocket"
+ | "omnivore"
+ | "hoarder"
+ | "linkwarden"
+ | "tab-session-manager";
}) => {
if (source === "html") {
return await parseNetscapeBookmarkFile(file);
@@ -173,6 +180,8 @@ export function ImportExportRow() {
return await parseOmnivoreBookmarkFile(file);
} else if (source === "linkwarden") {
return await parseLinkwardenBookmarkFile(file);
+ } else if (source === "tab-session-manager") {
+ return await parseTabSessionManagerStateFile(file);
} else {
throw new Error("Unknown source");
}
@@ -346,6 +355,25 @@ export function ImportExportRow() {
</FilePickerButton>
</ImportCard>
<ImportCard
+ text="Tab Session Manager"
+ description={t(
+ "settings.import.import_bookmarks_from_tab_session_manager_export",
+ )}
+ >
+ <FilePickerButton
+ size={"sm"}
+ loading={false}
+ accept=".json"
+ multiple={false}
+ className="flex items-center gap-2"
+ onFileSelect={(file) =>
+ runUploadBookmarkFile({ file, source: "tab-session-manager" })
+ }
+ >
+ <p>Import</p>
+ </FilePickerButton>
+ </ImportCard>
+ <ImportCard
text="Hoarder"
description={t(
"settings.import.import_bookmarks_from_hoarder_export",
diff --git a/apps/web/lib/i18n/locales/en/translation.json b/apps/web/lib/i18n/locales/en/translation.json
index 3398045e..4e622df5 100644
--- a/apps/web/lib/i18n/locales/en/translation.json
+++ b/apps/web/lib/i18n/locales/en/translation.json
@@ -142,6 +142,7 @@
"import_bookmarks_from_omnivore_export": "Import Bookmarks from Omnivore export",
"import_bookmarks_from_linkwarden_export": "Import Bookmarks from Linkwarden export",
"import_bookmarks_from_hoarder_export": "Import Bookmarks from Hoarder export",
+ "import_bookmarks_from_tab_session_manager_export": "Import Bookmarks from Tab Session Manager",
"export_links_and_notes": "Export Links and Notes",
"imported_bookmarks": "Imported Bookmarks"
},
diff --git a/apps/web/lib/i18n/locales/es/translation.json b/apps/web/lib/i18n/locales/es/translation.json
index 9ee40bd2..89af3229 100644
--- a/apps/web/lib/i18n/locales/es/translation.json
+++ b/apps/web/lib/i18n/locales/es/translation.json
@@ -73,7 +73,8 @@
"import_bookmarks_from_hoarder_export": "Importar marcadores desde exportación de Hoarder",
"import_bookmarks_from_html_file": "Importar marcadores desde archivo HTML",
"import_bookmarks_from_omnivore_export": "Importar marcadores desde exportación de Omnivore",
- "import_bookmarks_from_linkwarden_export": "Importar marcadores desde Linkwarden"
+ "import_bookmarks_from_linkwarden_export": "Importar marcadores desde Linkwarden",
+ "import_bookmarks_from_tab_session_manager_export": "Importar marcadores desde Tab Session Manager"
},
"api_keys": {
"api_keys": "Claves APIs",
diff --git a/apps/web/lib/importBookmarkParser.ts b/apps/web/lib/importBookmarkParser.ts
index bea92da1..25415975 100644
--- a/apps/web/lib/importBookmarkParser.ts
+++ b/apps/web/lib/importBookmarkParser.ts
@@ -177,6 +177,46 @@ export async function parseLinkwardenBookmarkFile(
});
}
+export async function parseTabSessionManagerStateFile(
+ file: File,
+): Promise<ParsedBookmark[]> {
+ const textContent = await file.text();
+
+ const zTab = z.object({
+ url: z.string(),
+ title: z.string(),
+ lastAccessed: z.number(),
+ });
+
+ const zSession = z.object({
+ windows: z.record(z.string(), z.record(z.string(), zTab)),
+ date: z.number(),
+ });
+
+ const zTabSessionManagerSchema = z.array(zSession);
+
+ const parsed = zTabSessionManagerSchema.safeParse(JSON.parse(textContent));
+ if (!parsed.success) {
+ throw new Error(
+ `The uploaded JSON file contains an invalid Tab Session Manager bookmark file: ${parsed.error.toString()}`,
+ );
+ }
+
+ // Get the object in data that has the most recent `date`
+ const { windows } = parsed.data.reduce((prev, curr) =>
+ prev.date > curr.date ? prev : curr,
+ );
+
+ return Object.values(windows).flatMap((window) =>
+ Object.values(window).map((tab) => ({
+ title: tab.title,
+ content: { type: BookmarkTypes.LINK as const, url: tab.url },
+ tags: [],
+ addDate: tab.lastAccessed,
+ })),
+ );
+}
+
export function deduplicateBookmarks(
bookmarks: ParsedBookmark[],
): ParsedBookmark[] {