mirror of
https://github.com/XFox111/TabsAsideExtension.git
synced 2026-04-22 07:58:01 +03:00
feat: add netscape bookmark import/export #203
This commit is contained in:
@@ -41,5 +41,9 @@ export const useOptionsStyles = makeStyles({
|
|||||||
flexFlow: "column",
|
flexFlow: "column",
|
||||||
alignItems: "flex-start",
|
alignItems: "flex-start",
|
||||||
gap: tokens.spacingVerticalSNudge
|
gap: tokens.spacingVerticalSNudge
|
||||||
|
},
|
||||||
|
messageBar:
|
||||||
|
{
|
||||||
|
flexShrink: 0
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -2,12 +2,13 @@ import { useDialog } from "@/contexts/DialogProvider";
|
|||||||
import { clearGraphicsStorage, cloudDisabled, setCloudStorage, thumbnailCaptureEnabled } from "@/features/collectionStorage";
|
import { clearGraphicsStorage, cloudDisabled, setCloudStorage, thumbnailCaptureEnabled } from "@/features/collectionStorage";
|
||||||
import { useDangerStyles } from "@/hooks/useDangerStyles";
|
import { useDangerStyles } from "@/hooks/useDangerStyles";
|
||||||
import useStorageInfo from "@/hooks/useStorageInfo";
|
import useStorageInfo from "@/hooks/useStorageInfo";
|
||||||
import { Button, Field, InfoLabel, LabelProps, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar, Switch } from "@fluentui/react-components";
|
import { Button, Divider, Field, InfoLabel, LabelProps, MessageBar, MessageBarBody, MessageBarTitle, ProgressBar, Subtitle2, Switch } from "@fluentui/react-components";
|
||||||
import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons";
|
import { ArrowDownload20Regular, ArrowUpload20Regular } from "@fluentui/react-icons";
|
||||||
import { Unwatch } from "wxt/utils/storage";
|
import { Unwatch } from "wxt/utils/storage";
|
||||||
import { useOptionsStyles } from "../hooks/useOptionsStyles";
|
import { useOptionsStyles } from "../hooks/useOptionsStyles";
|
||||||
import exportData from "../utils/exportData";
|
import exportData from "../utils/exportData";
|
||||||
import importData from "../utils/importData";
|
import importData from "../utils/importData";
|
||||||
|
import BookmarksSection from "@/features/netscapeBookmarks/layouts/BookmarksSection";
|
||||||
|
|
||||||
export default function StorageSection(): React.ReactElement
|
export default function StorageSection(): React.ReactElement
|
||||||
{
|
{
|
||||||
@@ -78,6 +79,59 @@ export default function StorageSection(): React.ReactElement
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
<Subtitle2>{ i18n.t("options_page.storage.manage_title") }</Subtitle2>
|
||||||
|
|
||||||
|
{ isCloudDisabled === false &&
|
||||||
|
<Field
|
||||||
|
label={ i18n.t("options_page.storage.capacity.title") }
|
||||||
|
hint={ i18n.t("options_page.storage.capacity.description", [(bytesInUse / 1024).toFixed(1), storageQuota / 1024]) }
|
||||||
|
validationState={ usedStorageRatio >= 0.8 ? "error" : undefined }
|
||||||
|
>
|
||||||
|
<ProgressBar value={ usedStorageRatio } thickness="large" />
|
||||||
|
</Field>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={ cls.horizontalButtons }>
|
||||||
|
{ isCloudDisabled === true &&
|
||||||
|
<Button appearance="primary" onClick={ () => setCloudStorage(true) }>
|
||||||
|
{ i18n.t("options_page.storage.enable") }
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
|
||||||
|
{ isCloudDisabled === false &&
|
||||||
|
<div className={ cls.horizontalButtons }>
|
||||||
|
<Button
|
||||||
|
appearance="subtle" className={ dangerCls.buttonSubtle }
|
||||||
|
onClick={ handleDisableCloud }
|
||||||
|
>
|
||||||
|
{ i18n.t("options_page.storage.disable") }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={ cls.horizontalButtons }>
|
||||||
|
<Button icon={ <ArrowDownload20Regular /> } onClick={ exportData }>
|
||||||
|
{ i18n.t("options_page.storage.export") }
|
||||||
|
</Button>
|
||||||
|
<Button icon={ <ArrowUpload20Regular /> } onClick={ handleImport }>
|
||||||
|
{ i18n.t("options_page.storage.import") }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{ importResult !== null &&
|
||||||
|
<MessageBar intent={ importResult ? "success" : "error" } className={ cls.messageBar }>
|
||||||
|
<MessageBarBody>
|
||||||
|
{ importResult === true ?
|
||||||
|
i18n.t("options_page.storage.import_results.success") :
|
||||||
|
i18n.t("options_page.storage.import_results.error")
|
||||||
|
}
|
||||||
|
</MessageBarBody>
|
||||||
|
</MessageBar>
|
||||||
|
}
|
||||||
|
|
||||||
|
<Divider />
|
||||||
|
<Subtitle2>{ i18n.t("options_page.storage.thumbnails_title") }</Subtitle2>
|
||||||
<div className={ cls.group }>
|
<div className={ cls.group }>
|
||||||
<Switch
|
<Switch
|
||||||
checked={ isThumbnailCaptureEnabled ?? true }
|
checked={ isThumbnailCaptureEnabled ?? true }
|
||||||
@@ -101,52 +155,8 @@ export default function StorageSection(): React.ReactElement
|
|||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{ isCloudDisabled === false &&
|
<Divider />
|
||||||
<Field
|
<BookmarksSection />
|
||||||
label={ i18n.t("options_page.storage.capacity.title") }
|
|
||||||
hint={ i18n.t("options_page.storage.capacity.description", [(bytesInUse / 1024).toFixed(1), storageQuota / 1024]) }
|
|
||||||
validationState={ usedStorageRatio >= 0.8 ? "error" : undefined }
|
|
||||||
>
|
|
||||||
<ProgressBar value={ usedStorageRatio } thickness="large" />
|
|
||||||
</Field>
|
|
||||||
}
|
|
||||||
|
|
||||||
{ isCloudDisabled === true &&
|
|
||||||
<Button appearance="primary" onClick={ () => setCloudStorage(true) }>
|
|
||||||
{ i18n.t("options_page.storage.enable") }
|
|
||||||
</Button>
|
|
||||||
}
|
|
||||||
|
|
||||||
<div className={ cls.horizontalButtons }>
|
|
||||||
<Button icon={ <ArrowDownload20Regular /> } onClick={ exportData }>
|
|
||||||
{ i18n.t("options_page.storage.export") }
|
|
||||||
</Button>
|
|
||||||
<Button icon={ <ArrowUpload20Regular /> } onClick={ handleImport }>
|
|
||||||
{ i18n.t("options_page.storage.import") }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{ importResult !== null &&
|
|
||||||
<MessageBar intent={ importResult ? "success" : "error" }>
|
|
||||||
<MessageBarBody>
|
|
||||||
{ importResult === true ?
|
|
||||||
i18n.t("options_page.storage.import_results.success") :
|
|
||||||
i18n.t("options_page.storage.import_results.error")
|
|
||||||
}
|
|
||||||
</MessageBarBody>
|
|
||||||
</MessageBar>
|
|
||||||
}
|
|
||||||
|
|
||||||
{ isCloudDisabled === false &&
|
|
||||||
<div className={ cls.horizontalButtons }>
|
|
||||||
<Button
|
|
||||||
appearance="subtle" className={ dangerCls.buttonSubtle }
|
|
||||||
onClick={ handleDisableCloud }
|
|
||||||
>
|
|
||||||
{ i18n.t("options_page.storage.disable") }
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import { useDialog } from "@/contexts/DialogProvider";
|
||||||
|
import { Body1, Button, makeStyles, MessageBar, MessageBarBody, Subtitle2, tokens } from "@fluentui/react-components";
|
||||||
|
import { ArrowDownload24Regular, ArrowUpload24Regular } from "@fluentui/react-icons";
|
||||||
|
import importBookmarks from "../utils/importBookmarks";
|
||||||
|
import exportBookmarks from "../utils/exportBookmarks";
|
||||||
|
|
||||||
|
export default function BookmarksSection(): React.ReactElement
|
||||||
|
{
|
||||||
|
const cls = useStyles();
|
||||||
|
const dialog = useDialog();
|
||||||
|
|
||||||
|
const [importResult, setImportResult] = useState<number | null>(null);
|
||||||
|
|
||||||
|
const handleImport = (): void =>
|
||||||
|
dialog.pushPrompt({
|
||||||
|
title: i18n.t("features.netscape_bookmarks.import_dialog.title"),
|
||||||
|
confirmText: i18n.t("options_page.storage.import_prompt.proceed"),
|
||||||
|
onConfirm: () => importBookmarks().then(setImportResult),
|
||||||
|
content: (
|
||||||
|
<Body1 as="p">
|
||||||
|
{ i18n.t("features.netscape_bookmarks.import_dialog.content") }
|
||||||
|
</Body1>
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ cls.root }>
|
||||||
|
<Subtitle2>{ i18n.t("features.netscape_bookmarks.title") }</Subtitle2>
|
||||||
|
|
||||||
|
{ importResult !== null &&
|
||||||
|
<MessageBar intent={ importResult >= 0 ? "success" : "error" } layout="multiline">
|
||||||
|
<MessageBarBody>
|
||||||
|
{ importResult >= 0 ?
|
||||||
|
i18n.t("features.netscape_bookmarks.import_result.success", [importResult]) :
|
||||||
|
i18n.t("features.netscape_bookmarks.import_result.error")
|
||||||
|
}
|
||||||
|
</MessageBarBody>
|
||||||
|
</MessageBar>
|
||||||
|
}
|
||||||
|
|
||||||
|
<div className={ cls.buttons }>
|
||||||
|
<Button icon={ <ArrowDownload24Regular /> } onClick={ exportBookmarks }>
|
||||||
|
{ i18n.t("features.netscape_bookmarks.export") }
|
||||||
|
</Button>
|
||||||
|
<Button icon={ <ArrowUpload24Regular /> } onClick={ handleImport }>
|
||||||
|
{ i18n.t("features.netscape_bookmarks.import") }
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles({
|
||||||
|
root:
|
||||||
|
{
|
||||||
|
display: "flex",
|
||||||
|
flexFlow: "column",
|
||||||
|
gap: tokens.spacingVerticalMNudge
|
||||||
|
},
|
||||||
|
buttons:
|
||||||
|
{
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
gap: tokens.spacingVerticalSNudge
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,99 @@
|
|||||||
|
import { CollectionItem, GraphicsStorage, GroupItem, TabItem } from "@/models/CollectionModels";
|
||||||
|
import { Bookmark } from "node-bookmarks-parser/build/interfaces/bookmark";
|
||||||
|
|
||||||
|
export default function convertBookmarks(bookmarks: Bookmark[]): [CollectionItem[], GraphicsStorage, number]
|
||||||
|
{
|
||||||
|
let count: number = 0;
|
||||||
|
const graphics: GraphicsStorage = {};
|
||||||
|
const items: CollectionItem[] = [];
|
||||||
|
const untitled: CollectionItem = {
|
||||||
|
items: [],
|
||||||
|
timestamp: Date.now(),
|
||||||
|
type: "collection"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const bookmark of bookmarks)
|
||||||
|
{
|
||||||
|
if (bookmark.type === "bookmark")
|
||||||
|
{
|
||||||
|
untitled.items.push(getTab(bookmark, graphics));
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
else if (bookmark.type === "folder" && bookmark.children)
|
||||||
|
{
|
||||||
|
const collection: CollectionItem = getCollection(bookmark, graphics);
|
||||||
|
items.push(collection);
|
||||||
|
count += collection.items.reduce((acc, item) =>
|
||||||
|
{
|
||||||
|
if (item.type === "tab")
|
||||||
|
return acc + 1;
|
||||||
|
else if (item.type === "group")
|
||||||
|
return acc + item.items.length;
|
||||||
|
return acc;
|
||||||
|
}, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return [items, graphics, count];
|
||||||
|
}
|
||||||
|
|
||||||
|
function getTab(bookmark: Bookmark, graphics: GraphicsStorage): TabItem
|
||||||
|
{
|
||||||
|
if (bookmark.icon)
|
||||||
|
graphics[bookmark.url!] = {
|
||||||
|
icon: bookmark.icon
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
type: "tab",
|
||||||
|
url: bookmark.url!,
|
||||||
|
title: bookmark.title || bookmark.url!
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCollection(bookmark: Bookmark, graphics: GraphicsStorage): CollectionItem
|
||||||
|
{
|
||||||
|
const collection: CollectionItem = {
|
||||||
|
items: [],
|
||||||
|
title: bookmark.title,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
type: "collection"
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const child of bookmark.children!)
|
||||||
|
{
|
||||||
|
if (child.type === "bookmark")
|
||||||
|
collection.items.push(getTab(child, graphics));
|
||||||
|
else if (child.type === "folder" && child.children)
|
||||||
|
collection.items.push(getGroup(child, graphics));
|
||||||
|
}
|
||||||
|
|
||||||
|
return collection;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getGroup(bookmark: Bookmark, graphics: GraphicsStorage): GroupItem
|
||||||
|
{
|
||||||
|
const group: GroupItem = {
|
||||||
|
items: [],
|
||||||
|
title: bookmark.title,
|
||||||
|
pinned: false,
|
||||||
|
type: "group",
|
||||||
|
color: getRandomColor()
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const child of bookmark.children!)
|
||||||
|
{
|
||||||
|
if (child.type === "bookmark")
|
||||||
|
group.items.push(getTab(child, graphics));
|
||||||
|
else if (child.type === "folder")
|
||||||
|
group.items.push(...getGroup(child, graphics).items);
|
||||||
|
}
|
||||||
|
|
||||||
|
return group;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getRandomColor(): "blue" | "cyan" | "green" | "grey" | "orange" | "pink" | "purple" | "red" | "yellow"
|
||||||
|
{
|
||||||
|
const colors = ["blue", "cyan", "green", "grey", "orange", "pink", "purple", "red", "yellow"] as const;
|
||||||
|
return colors[Math.floor(Math.random() * colors.length)];
|
||||||
|
}
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
import { getCollectionTitle } from "@/entrypoints/sidepanel/utils/getCollectionTitle";
|
||||||
|
import { getCollections } from "@/features/collectionStorage";
|
||||||
|
import { CollectionItem, GroupItem } from "@/models/CollectionModels";
|
||||||
|
|
||||||
|
export default async function exportBookmarks(): Promise<void>
|
||||||
|
{
|
||||||
|
const [collections] = await getCollections();
|
||||||
|
const lines: string[] = [
|
||||||
|
"<!DOCTYPE NETSCAPE-Bookmark-file-1>",
|
||||||
|
"<!-- This is an automatically generated file.",
|
||||||
|
" It will be read and overwritten.",
|
||||||
|
" DO NOT EDIT! -->",
|
||||||
|
"<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=UTF-8\">",
|
||||||
|
"<TITLE>Bookmarks</TITLE>",
|
||||||
|
"<H1>Bookmarks</H1>",
|
||||||
|
"<DL><p>"
|
||||||
|
];
|
||||||
|
|
||||||
|
for (const collection of collections)
|
||||||
|
lines.push(...createFolder(collection));
|
||||||
|
|
||||||
|
lines.push("</DL><p>");
|
||||||
|
|
||||||
|
const data: string = lines.join("\n");
|
||||||
|
|
||||||
|
const blob: Blob = new Blob([data], { type: "text/html" });
|
||||||
|
|
||||||
|
const element: HTMLAnchorElement = document.createElement("a");
|
||||||
|
element.style.display = "none";
|
||||||
|
element.href = URL.createObjectURL(blob);
|
||||||
|
element.setAttribute("download", "collections.html");
|
||||||
|
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
URL.revokeObjectURL(element.href);
|
||||||
|
document.body.removeChild(element);
|
||||||
|
}
|
||||||
|
|
||||||
|
function createFolder(item: CollectionItem | GroupItem): string[]
|
||||||
|
{
|
||||||
|
const lines: string[] = [];
|
||||||
|
const title: string = item.type === "collection" ?
|
||||||
|
(item.title ?? getCollectionTitle(item)) :
|
||||||
|
(item.pinned ? i18n.t("groups.pinned") : (item.title ?? ""));
|
||||||
|
|
||||||
|
lines.push(`<DT><H3>${sanitizeString(title)}</H3>`);
|
||||||
|
lines.push("<DL><p>");
|
||||||
|
|
||||||
|
for (const subItem of item.items)
|
||||||
|
{
|
||||||
|
if (subItem.type === "tab")
|
||||||
|
lines.push(`<DT><A HREF="${encodeURI(subItem.url)}">${sanitizeString(subItem.title || subItem.url)}</A>`);
|
||||||
|
else if (subItem.type === "group")
|
||||||
|
lines.push(...createFolder(subItem));
|
||||||
|
}
|
||||||
|
|
||||||
|
lines.push("</DL><p>");
|
||||||
|
return lines;
|
||||||
|
}
|
||||||
|
|
||||||
|
function sanitizeString(str: string): string
|
||||||
|
{
|
||||||
|
return str.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import { getCollections, saveCollections } from "@/features/collectionStorage";
|
||||||
|
import { sendMessage } from "@/utils/messaging";
|
||||||
|
import parse from "node-bookmarks-parser";
|
||||||
|
import { Bookmark } from "node-bookmarks-parser/build/interfaces/bookmark";
|
||||||
|
import convertBookmarks from "./convertBookmarks";
|
||||||
|
|
||||||
|
export default async function importBookmarks(): Promise<number | null>
|
||||||
|
{
|
||||||
|
const element: HTMLInputElement = document.createElement("input");
|
||||||
|
element.style.display = "none";
|
||||||
|
element.hidden = true;
|
||||||
|
element.type = "file";
|
||||||
|
element.accept = ".html";
|
||||||
|
|
||||||
|
document.body.appendChild(element);
|
||||||
|
element.click();
|
||||||
|
|
||||||
|
await new Promise(resolve =>
|
||||||
|
{
|
||||||
|
const listener = () =>
|
||||||
|
{
|
||||||
|
element.removeEventListener("input", listener);
|
||||||
|
resolve(null);
|
||||||
|
};
|
||||||
|
element.addEventListener("input", listener);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!element.files || element.files.length < 1)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
const file: File = element.files[0];
|
||||||
|
const content: string = await file.text();
|
||||||
|
|
||||||
|
document.body.removeChild(element);
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
const bookmarks: Bookmark[] = parse(content);
|
||||||
|
const [data, graphics, tabCount] = convertBookmarks(bookmarks);
|
||||||
|
const [collections, cloudIssues] = await getCollections();
|
||||||
|
|
||||||
|
await saveCollections([...data, ...collections], cloudIssues === null, graphics);
|
||||||
|
sendMessage("refreshCollections", undefined);
|
||||||
|
|
||||||
|
return tabCount;
|
||||||
|
}
|
||||||
|
catch (error)
|
||||||
|
{
|
||||||
|
console.error("Failed to parse bookmarks file", error);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "See the full list of what we collect"
|
p3_text: "See the full list of what we collect"
|
||||||
p3_link: "here"
|
p3_link: "here"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Browser bookmarks"
|
||||||
|
export: "Export collections as bookmarks"
|
||||||
|
import: "Import from bookmarks file"
|
||||||
|
import_dialog:
|
||||||
|
title: "Import bookmarks"
|
||||||
|
content: "Import bookmarks from a Netscape-format bookmarks file exported from your browser."
|
||||||
|
import_result:
|
||||||
|
success: "Successfully imported $1 bookmarks."
|
||||||
|
error: "Failed to import bookmarks. Please ensure the file is a valid bookmarks file."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "New collection created"
|
title: "New collection created"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Open tabs and remove the collection"
|
restore: "Open tabs and remove the collection"
|
||||||
storage:
|
storage:
|
||||||
title: "Storage"
|
title: "Storage"
|
||||||
|
manage_title: "Storage management"
|
||||||
|
thumbnails_title: "Thumbnails & icons"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Cloud storage capacity"
|
title: "Cloud storage capacity"
|
||||||
description: "$1 of $2 KiB"
|
description: "$1 of $2 KiB"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Ver la lista completa de lo que recopilamos"
|
p3_text: "Ver la lista completa de lo que recopilamos"
|
||||||
p3_link: "aquí"
|
p3_link: "aquí"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Marcadores del navegador"
|
||||||
|
export: "Exportar colecciones como marcadores"
|
||||||
|
import: "Importar desde archivo de marcadores"
|
||||||
|
import_dialog:
|
||||||
|
title: "Importar marcadores"
|
||||||
|
content: "Importa marcadores desde un archivo de marcadores en formato Netscape exportado desde tu navegador."
|
||||||
|
import_result:
|
||||||
|
success: "Se importaron correctamente $1 marcadores."
|
||||||
|
error: "No se pudieron importar los marcadores. Asegúrate de que el archivo sea un archivo de marcadores válido."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Nueva colección creada"
|
title: "Nueva colección creada"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Abrir pestañas y eliminar la colección"
|
restore: "Abrir pestañas y eliminar la colección"
|
||||||
storage:
|
storage:
|
||||||
title: "Almacenamiento"
|
title: "Almacenamiento"
|
||||||
|
manage_title: "Administrar almacenamiento"
|
||||||
|
thumbnails_title: "Miniaturas e íconos"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Capacidad de almacenamiento en la nube"
|
title: "Capacidad de almacenamiento en la nube"
|
||||||
description: "$1 de $2 KiB"
|
description: "$1 de $2 KiB"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Vedi l'elenco completo di ciò che raccogliamo"
|
p3_text: "Vedi l'elenco completo di ciò che raccogliamo"
|
||||||
p3_link: "qui"
|
p3_link: "qui"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Segnalibri del browser"
|
||||||
|
export: "Esporta collezioni come segnalibri"
|
||||||
|
import: "Importa da file di segnalibri"
|
||||||
|
import_dialog:
|
||||||
|
title: "Importa segnalibri"
|
||||||
|
content: "Importa segnalibri da un file di segnalibri in formato Netscape esportato dal tuo browser."
|
||||||
|
import_result:
|
||||||
|
success: "Importati con successo $1 segnalibri."
|
||||||
|
error: "Impossibile importare i segnalibri. Assicurati che il file sia un file di segnalibri valido."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Nuova collezione creata"
|
title: "Nuova collezione creata"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Apri le schede e rimuovi la collezione"
|
restore: "Apri le schede e rimuovi la collezione"
|
||||||
storage:
|
storage:
|
||||||
title: "Archiviazione"
|
title: "Archiviazione"
|
||||||
|
manage_title: "Gestisci archiviazione"
|
||||||
|
thumbnails_title: "Miniature e icone"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Capacità di archiviazione cloud"
|
title: "Capacità di archiviazione cloud"
|
||||||
description: "$1 di $2 KiB"
|
description: "$1 di $2 KiB"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Pełną listę zbieranych danych można zobaczyć"
|
p3_text: "Pełną listę zbieranych danych można zobaczyć"
|
||||||
p3_link: "tutaj"
|
p3_link: "tutaj"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Import/eksport zakładek"
|
||||||
|
export: "Eksportuj kolekcje jako plik zakładek"
|
||||||
|
import: "Importuj z pliku zakładek"
|
||||||
|
import_dialog:
|
||||||
|
title: "Import zakładek"
|
||||||
|
content: "Importuj zakładki z pliku zakładek w formacie Netscape wyeksportowanego z przeglądarki."
|
||||||
|
import_result:
|
||||||
|
success: "Zakładki zostały pomyślnie zaimportowane ($1)"
|
||||||
|
error: "Nie udało się zaimportować zakładek. Upewnij się, że plik jest poprawnym plikiem zakładek."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Utworzono nową kolekcję"
|
title: "Utworzono nową kolekcję"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Otwórz karty i usuń kolekcję"
|
restore: "Otwórz karty i usuń kolekcję"
|
||||||
storage:
|
storage:
|
||||||
title: "Magazyn"
|
title: "Magazyn"
|
||||||
|
manage_title: "Zarządzaj magazynem"
|
||||||
|
thumbnails_title: "Podglądy i ikony"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Magazyn w chmurze"
|
title: "Magazyn w chmurze"
|
||||||
description: "$1 z $2 KiB"
|
description: "$1 z $2 KiB"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Veja a lista completa do que coletamos"
|
p3_text: "Veja a lista completa do que coletamos"
|
||||||
p3_link: "aqui"
|
p3_link: "aqui"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Favoritos do navegador"
|
||||||
|
export: "Exportar coleções como favoritos"
|
||||||
|
import: "Importar de arquivo de favoritos"
|
||||||
|
import_dialog:
|
||||||
|
title: "Importar favoritos"
|
||||||
|
content: "Importe favoritos de um arquivo de favoritos no formato Netscape exportado do seu navegador."
|
||||||
|
import_result:
|
||||||
|
success: "Importados com sucesso $1 favoritos."
|
||||||
|
error: "Falha ao importar favoritos. Por favor, certifique-se de que o arquivo é um arquivo de favoritos válido."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Nova coleção criada"
|
title: "Nova coleção criada"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Abrir abas e remover a coleção"
|
restore: "Abrir abas e remover a coleção"
|
||||||
storage:
|
storage:
|
||||||
title: "Armazenamento"
|
title: "Armazenamento"
|
||||||
|
manage_title: "Gerenciar armazenamento"
|
||||||
|
thumbnails_title: "Miniaturas e ícones"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Capacidade de armazenamento na nuvem"
|
title: "Capacidade de armazenamento na nuvem"
|
||||||
description: "$1 de $2 KiB"
|
description: "$1 de $2 KiB"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Полный список собираемых данных можно посмотреть"
|
p3_text: "Полный список собираемых данных можно посмотреть"
|
||||||
p3_link: "здесь"
|
p3_link: "здесь"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Импорт/экспорт закладок"
|
||||||
|
export: "Экспортировать коллекции как файл закладок"
|
||||||
|
import: "Импорт из файла закладок"
|
||||||
|
import_dialog:
|
||||||
|
title: "Импорт закладок"
|
||||||
|
content: "Импортируйте закладки из файла закладок в формате Netscape, экспортированного из вашего браузера."
|
||||||
|
import_result:
|
||||||
|
success: "Закладки успешно импортированы ($1 шт.)"
|
||||||
|
error: "Не удалось импортировать закладки. Пожалуйста, убедитесь, что файл является допустимым файлом закладок."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Создана новая коллекция"
|
title: "Создана новая коллекция"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Открыть вкладки и удалить коллекцию"
|
restore: "Открыть вкладки и удалить коллекцию"
|
||||||
storage:
|
storage:
|
||||||
title: "Хранилище"
|
title: "Хранилище"
|
||||||
|
manage_title: "Управление хранилищем"
|
||||||
|
thumbnails_title: "Превью и иконки"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Объём облачного хранилища"
|
title: "Объём облачного хранилища"
|
||||||
description: "$1 из $2 КиБ"
|
description: "$1 из $2 КиБ"
|
||||||
|
|||||||
+16
-3
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "Повний список зібраних даних можна подивитися"
|
p3_text: "Повний список зібраних даних можна подивитися"
|
||||||
p3_link: "тут"
|
p3_link: "тут"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "Імпорт/експорт закладок"
|
||||||
|
export: "Експортувати колекції як файл закладок"
|
||||||
|
import: "Імпорт із файлу закладок"
|
||||||
|
import_dialog:
|
||||||
|
title: "Імпорт закладок"
|
||||||
|
content: "Імпортуйте закладки з файлу закладок у форматі Netscape, експортованого з вашого браузера."
|
||||||
|
import_result:
|
||||||
|
success: "Закладки успішно імпортовані ($1 шт.)"
|
||||||
|
error: "Не вдалося імпортувати закладки. Будь ласка, переконайтеся, що файл є коректним файлом закладок."
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "Створено нову колекцію"
|
title: "Створено нову колекцію"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "Відкрити вкладки та видалити колекцію"
|
restore: "Відкрити вкладки та видалити колекцію"
|
||||||
storage:
|
storage:
|
||||||
title: "Сховище"
|
title: "Сховище"
|
||||||
|
manage_title: "Керування сховищем"
|
||||||
|
thumbnails_title: "Прев'ю та іконки"
|
||||||
capacity:
|
capacity:
|
||||||
title: "Хмарне сховище"
|
title: "Хмарне сховище"
|
||||||
description: "$1 з $2 КіБ"
|
description: "$1 з $2 КіБ"
|
||||||
@@ -132,13 +145,13 @@ options_page:
|
|||||||
disable_prompt:
|
disable_prompt:
|
||||||
text: "Ця дія вимкне синхронізацію колекцій між вашими пристроями. Налаштування продовжать зберігатися у хмарі."
|
text: "Ця дія вимкне синхронізацію колекцій між вашими пристроями. Налаштування продовжать зберігатися у хмарі."
|
||||||
action: "Вимкнути та перезавантажити розширення"
|
action: "Вимкнути та перезавантажити розширення"
|
||||||
thumbnail_capture: "Зберігати превью і іконки для збережених вкладок"
|
thumbnail_capture: "Зберігати прев'ю і іконки для збережених вкладок"
|
||||||
thumbnail_capture_notice1: "Необхідний доступ до вмісту відвіданих веб-сайтів"
|
thumbnail_capture_notice1: "Необхідний доступ до вмісту відвіданих веб-сайтів"
|
||||||
thumbnail_capture_notice2: "Вимкнення цієї функції може покращити продуктивність при великій кількості збережених вкладок"
|
thumbnail_capture_notice2: "Вимкнення цієї функції може покращити продуктивність при великій кількості збережених вкладок"
|
||||||
clear_thumbnails:
|
clear_thumbnails:
|
||||||
action: "Видалити збережені іконки"
|
action: "Видалити збережені іконки"
|
||||||
title: "Видалити превью і іконки?"
|
title: "Видалити прев'ю і іконки?"
|
||||||
prompt: "Ця дія видалить всі превью і іконки у ваших збережених вкладках. Цю дію не можна скасувати."
|
prompt: "Ця дія видалить всі прев'ю і іконки у ваших збережених вкладках. Цю дію не можна скасувати."
|
||||||
about:
|
about:
|
||||||
title: "О розширенні"
|
title: "О розширенні"
|
||||||
developed_by: "Розробник: Євген Лис"
|
developed_by: "Розробник: Євген Лис"
|
||||||
|
|||||||
@@ -46,6 +46,17 @@ features:
|
|||||||
p3_text: "请参阅我们收集内容的"
|
p3_text: "请参阅我们收集内容的"
|
||||||
p3_link: "完整列表"
|
p3_link: "完整列表"
|
||||||
|
|
||||||
|
netscape_bookmarks:
|
||||||
|
title: "浏览器书签"
|
||||||
|
export: "将收藏导出为书签"
|
||||||
|
import: "从书签文件导入"
|
||||||
|
import_dialog:
|
||||||
|
title: "导入书签"
|
||||||
|
content: "从您的浏览器导出的 Netscape 格式书签文件中导入书签。"
|
||||||
|
import_result:
|
||||||
|
success: "成功导入 $1 个书签。"
|
||||||
|
error: "导入书签失败。请确保该文件是有效的书签文件。"
|
||||||
|
|
||||||
notifications:
|
notifications:
|
||||||
tabs_saved:
|
tabs_saved:
|
||||||
title: "已创建新收藏"
|
title: "已创建新收藏"
|
||||||
@@ -114,6 +125,8 @@ options_page:
|
|||||||
restore: "打开标签页并删除收藏"
|
restore: "打开标签页并删除收藏"
|
||||||
storage:
|
storage:
|
||||||
title: "存储"
|
title: "存储"
|
||||||
|
manage_title: "存储管理"
|
||||||
|
thumbnails_title: "缩略图和图标"
|
||||||
capacity:
|
capacity:
|
||||||
title: "云存储容量"
|
title: "云存储容量"
|
||||||
description: "$1 / $2 KiB"
|
description: "$1 / $2 KiB"
|
||||||
|
|||||||
Generated
+162
@@ -19,6 +19,7 @@
|
|||||||
"@wxt-dev/analytics": "^0.5.1",
|
"@wxt-dev/analytics": "^0.5.1",
|
||||||
"@wxt-dev/i18n": "^0.2.4",
|
"@wxt-dev/i18n": "^0.2.4",
|
||||||
"lzutf8": "^0.6.3",
|
"lzutf8": "^0.6.3",
|
||||||
|
"node-bookmarks-parser": "^2.0.0",
|
||||||
"react": "^19.2.1",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.1"
|
"react-dom": "^19.2.1"
|
||||||
},
|
},
|
||||||
@@ -4595,6 +4596,48 @@
|
|||||||
"url": "https://github.com/chalk/chalk?sponsor=1"
|
"url": "https://github.com/chalk/chalk?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/cheerio": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-IkxPpb5rS/d1IiLbHMgfPuS0FgiWTtFIm/Nj+2woXDLTZ7fOT2eqzgYbdMlLweqlHbsZjxEChoVK+7iph7jyQg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cheerio-select": "^2.1.0",
|
||||||
|
"dom-serializer": "^2.0.0",
|
||||||
|
"domhandler": "^5.0.3",
|
||||||
|
"domutils": "^3.2.2",
|
||||||
|
"encoding-sniffer": "^0.2.1",
|
||||||
|
"htmlparser2": "^10.0.0",
|
||||||
|
"parse5": "^7.3.0",
|
||||||
|
"parse5-htmlparser2-tree-adapter": "^7.1.0",
|
||||||
|
"parse5-parser-stream": "^7.1.2",
|
||||||
|
"undici": "^7.12.0",
|
||||||
|
"whatwg-mimetype": "^4.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/cheeriojs/cheerio?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/cheerio-select": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"boolbase": "^1.0.0",
|
||||||
|
"css-select": "^5.1.0",
|
||||||
|
"css-what": "^6.1.0",
|
||||||
|
"domelementtype": "^2.3.0",
|
||||||
|
"domhandler": "^5.0.3",
|
||||||
|
"domutils": "^3.0.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/sponsors/fb55"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/chokidar": {
|
"node_modules/chokidar": {
|
||||||
"version": "4.0.3",
|
"version": "4.0.3",
|
||||||
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
"resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz",
|
||||||
@@ -5412,6 +5455,19 @@
|
|||||||
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
|
"integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/encoding-sniffer": {
|
||||||
|
"version": "0.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz",
|
||||||
|
"integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "^0.6.3",
|
||||||
|
"whatwg-encoding": "^3.1.1"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/encoding-sniffer?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/entities": {
|
"node_modules/entities": {
|
||||||
"version": "4.5.0",
|
"version": "4.5.0",
|
||||||
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
"resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz",
|
||||||
@@ -6675,6 +6731,18 @@
|
|||||||
"url": "https://github.com/fb55/entities?sponsor=1"
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/iconv-lite": {
|
||||||
|
"version": "0.6.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
|
||||||
|
"integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"safer-buffer": ">= 2.1.2 < 3.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/ieee754": {
|
"node_modules/ieee754": {
|
||||||
"version": "1.2.1",
|
"version": "1.2.1",
|
||||||
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
"resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
|
||||||
@@ -8080,6 +8148,15 @@
|
|||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/node-bookmarks-parser": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-bookmarks-parser/-/node-bookmarks-parser-2.0.0.tgz",
|
||||||
|
"integrity": "sha512-BHQpogPEifFP+eToF+GS6cP9DInDsh++c3PFtMiH0oJ1ByeEXcoZw0joDio1sIpm1lJAFyY6msguZ5ZJTEueZg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"cheerio": "^1.0.0-rc.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/node-fetch-native": {
|
"node_modules/node-fetch-native": {
|
||||||
"version": "1.6.7",
|
"version": "1.6.7",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch-native/-/node-fetch-native-1.6.7.tgz",
|
||||||
@@ -8550,6 +8627,55 @@
|
|||||||
"url": "https://github.com/sponsors/sindresorhus"
|
"url": "https://github.com/sponsors/sindresorhus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/parse5": {
|
||||||
|
"version": "7.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
|
||||||
|
"integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"entities": "^6.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5-htmlparser2-tree-adapter": {
|
||||||
|
"version": "7.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz",
|
||||||
|
"integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"domhandler": "^5.0.3",
|
||||||
|
"parse5": "^7.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5-parser-stream": {
|
||||||
|
"version": "7.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz",
|
||||||
|
"integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"parse5": "^7.0.0"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/inikulin/parse5?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/parse5/node_modules/entities": {
|
||||||
|
"version": "6.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
|
||||||
|
"integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=0.12"
|
||||||
|
},
|
||||||
|
"funding": {
|
||||||
|
"url": "https://github.com/fb55/entities?sponsor=1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/path-exists": {
|
"node_modules/path-exists": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
|
||||||
@@ -9287,6 +9413,12 @@
|
|||||||
"node": ">=10"
|
"node": ">=10"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
|
||||||
|
"license": "MIT"
|
||||||
|
},
|
||||||
"node_modules/sax": {
|
"node_modules/sax": {
|
||||||
"version": "1.4.3",
|
"version": "1.4.3",
|
||||||
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
"resolved": "https://registry.npmjs.org/sax/-/sax-1.4.3.tgz",
|
||||||
@@ -10201,6 +10333,15 @@
|
|||||||
"url": "https://github.com/sponsors/ljharb"
|
"url": "https://github.com/sponsors/ljharb"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/undici": {
|
||||||
|
"version": "7.16.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/undici/-/undici-7.16.0.tgz",
|
||||||
|
"integrity": "sha512-QEg3HPMll0o3t2ourKwOeUAZ159Kn9mx5pnzHRQO8+Wixmh88YdZRiIwat0iNzNNXn0yoEtXJqFpyW7eM8BV7g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=20.18.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/undici-types": {
|
"node_modules/undici-types": {
|
||||||
"version": "7.16.0",
|
"version": "7.16.0",
|
||||||
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz",
|
||||||
@@ -10526,6 +10667,27 @@
|
|||||||
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
"integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==",
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/whatwg-encoding": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"iconv-lite": "0.6.3"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/whatwg-mimetype": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=18"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/when": {
|
"node_modules/when": {
|
||||||
"version": "3.7.7",
|
"version": "3.7.7",
|
||||||
"resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz",
|
"resolved": "https://registry.npmjs.org/when/-/when-3.7.7.tgz",
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
"@wxt-dev/analytics": "^0.5.1",
|
"@wxt-dev/analytics": "^0.5.1",
|
||||||
"@wxt-dev/i18n": "^0.2.4",
|
"@wxt-dev/i18n": "^0.2.4",
|
||||||
"lzutf8": "^0.6.3",
|
"lzutf8": "^0.6.3",
|
||||||
|
"node-bookmarks-parser": "^2.0.0",
|
||||||
"react": "^19.2.1",
|
"react": "^19.2.1",
|
||||||
"react-dom": "^19.2.1"
|
"react-dom": "^19.2.1"
|
||||||
},
|
},
|
||||||
|
|||||||
Reference in New Issue
Block a user