diff --git a/surfsense_browser_extension/.env.example b/surfsense_browser_extension/.env.example new file mode 100644 index 000000000..46a36cf41 --- /dev/null +++ b/surfsense_browser_extension/.env.example @@ -0,0 +1,2 @@ +PLASMO_PUBLIC_API_SECRET_KEY = "surfsense" +PLASMO_PUBLIC_BACKEND_URL = "http://127.0.0.1:8000" \ No newline at end of file diff --git a/surfsense_browser_extension/.github/workflows/submityml b/surfsense_browser_extension/.github/workflows/submityml new file mode 100644 index 000000000..09142023d --- /dev/null +++ b/surfsense_browser_extension/.github/workflows/submityml @@ -0,0 +1,34 @@ +# name: "Submit to Web Store" +# on: +# workflow_dispatch: + +# jobs: +# build: +# runs-on: ubuntu-latest +# steps: +# - uses: actions/checkout@v3 +# - name: Cache pnpm modules +# uses: actions/cache@v3 +# with: +# path: ~/.pnpm-store +# key: ${{ runner.os }}-${{ hashFiles('**/pnpm-lock.yaml') }} +# restore-keys: | +# ${{ runner.os }}- +# - uses: pnpm/action-setup@v2.2.4 +# with: +# version: latest +# run_install: true +# - name: Use Node.js 16.x +# uses: actions/setup-node@v3.4.1 +# with: +# node-version: 16.x +# cache: "pnpm" +# - name: Build the extension +# run: pnpm build +# - name: Package the extension into a zip artifact +# run: pnpm package +# - name: Browser Platform Publish +# uses: PlasmoHQ/bpp@v3 +# with: +# keys: ${{ secrets.SUBMIT_KEYS }} +# artifact: build/chrome-mv3-prod.zip diff --git a/surfsense_browser_extension/.gitignore b/surfsense_browser_extension/.gitignore new file mode 100644 index 000000000..7f9398de8 --- /dev/null +++ b/surfsense_browser_extension/.gitignore @@ -0,0 +1,35 @@ + +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.js + +# testing +/coverage + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# local env files +.env*.local + +out/ +build/ +dist/ + +# plasmo +.plasmo + +# typescript +.tsbuildinfo + +/trash diff --git a/surfsense_browser_extension/.prettierrc.mjs b/surfsense_browser_extension/.prettierrc.mjs new file mode 100644 index 000000000..77f84c218 --- /dev/null +++ b/surfsense_browser_extension/.prettierrc.mjs @@ -0,0 +1,26 @@ +/** + * @type {import('prettier').Options} + */ +export default { + printWidth: 80, + tabWidth: 2, + useTabs: false, + semi: false, + singleQuote: false, + trailingComma: "none", + bracketSpacing: true, + bracketSameLine: true, + plugins: ["@ianvs/prettier-plugin-sort-imports"], + importOrder: [ + "", // Node.js built-in modules + "", // Imports not matched by other special words or groups. + "", // Empty line + "^@plasmo/(.*)$", + "", + "^@plasmohq/(.*)$", + "", + "^~(.*)$", + "", + "^[./]" + ] +} diff --git a/surfsense_browser_extension/README.md b/surfsense_browser_extension/README.md new file mode 100644 index 000000000..2c8addbfe --- /dev/null +++ b/surfsense_browser_extension/README.md @@ -0,0 +1,37 @@ +# SurfSense Cross Browser Extension + +Use this guide to build for your browser https://docs.plasmo.com/framework/workflows/build + +This is a [Plasmo extension](https://docs.plasmo.com/) project bootstrapped with [`plasmo init`](https://www.npmjs.com/package/plasmo). + +## Getting Started + +First, run the development server: + +```bash +pnpm dev +# or +npm run dev +``` + +Open your browser and load the appropriate development build. For example, if you are developing for the chrome browser, using manifest v3, use: `build/chrome-mv3-dev`. + +You can start editing the popup by modifying `popup.tsx`. It should auto-update as you make changes. To add an options page, simply add a `options.tsx` file to the root of the project, with a react component default exported. Likewise to add a content page, add a `content.ts` file to the root of the project, importing some module and do some logic, then reload the extension on your browser. + +For further guidance, [visit our Documentation](https://docs.plasmo.com/) + +## Making production build + +Run the following: + +```bash +pnpm build +# or +npm run build +``` + +This should create a production bundle for your extension, ready to be zipped and published to the stores. + +## Submit to the webstores + +The easiest way to deploy your Plasmo extension is to use the built-in [bpp](https://bpp.browser.market) GitHub action. Prior to using this action however, make sure to build your extension and upload the first version to the store to establish the basic credentials. Then, simply follow [this setup instruction](https://docs.plasmo.com/framework/workflows/submit) and you should be on your way for automated submission! diff --git a/surfsense_browser_extension/assets/brain.png b/surfsense_browser_extension/assets/brain.png new file mode 100644 index 000000000..7d54b178a Binary files /dev/null and b/surfsense_browser_extension/assets/brain.png differ diff --git a/surfsense_browser_extension/assets/icon.png b/surfsense_browser_extension/assets/icon.png new file mode 100644 index 000000000..5d1464a7a Binary files /dev/null and b/surfsense_browser_extension/assets/icon.png differ diff --git a/surfsense_browser_extension/background/index.ts b/surfsense_browser_extension/background/index.ts new file mode 100644 index 000000000..8710fd453 --- /dev/null +++ b/surfsense_browser_extension/background/index.ts @@ -0,0 +1,77 @@ +import { initQueues, initWebHistory } from "~utils/commons" +import type { WebHistory } from "~utils/interfaces" +import { Storage } from "@plasmohq/storage" +import {getRenderedHtml} from '~utils/commons' + +chrome.tabs.onCreated.addListener(async (tab: any) => { + try { + await initWebHistory(tab.id) + await initQueues(tab.id) + } catch (error) { + console.log(error) + } +}) + +chrome.tabs.onUpdated.addListener( + async (tabId: number, changeInfo: any, tab: any) => { + if (changeInfo.status === "complete" && tab.url) { + const storage = new Storage({ area: "local" }) + await initWebHistory(tab.id) + await initQueues(tab.id) + + const result = await chrome.scripting.executeScript({ + // @ts-ignore + target: { tabId: tab.id }, + // @ts-ignore + func: getRenderedHtml + }) + + let toPushInTabHistory: any = result[0].result // const { renderedHtml, title, url, entryTime } = result[0].result; + + let urlQueueListObj: any = await storage.get("urlQueueList") + let timeQueueListObj: any = await storage.get("timeQueueList") + + urlQueueListObj.urlQueueList + .find((data: WebHistory) => data.tabsessionId === tabId) + .urlQueue.push(toPushInTabHistory.url) + timeQueueListObj.timeQueueList + .find((data: WebHistory) => data.tabsessionId === tabId) + .timeQueue.push(toPushInTabHistory.entryTime) + + await storage.set("urlQueueList", { + urlQueueList: urlQueueListObj.urlQueueList + }) + await storage.set("timeQueueList", { + timeQueueList: timeQueueListObj.timeQueueList + }) + } + } +) + +chrome.tabs.onRemoved.addListener(async (tabId: number, removeInfo: object) => { + const storage = new Storage({ area: "local" }) + let urlQueueListObj: any = await storage.get("urlQueueList") + let timeQueueListObj: any = await storage.get("timeQueueList") + if (urlQueueListObj.urlQueueList && timeQueueListObj.timeQueueList) { + const urlQueueListToSave = urlQueueListObj.urlQueueList.map( + (element: WebHistory) => { + if (element.tabsessionId !== tabId) { + return element + } + } + ) + const timeQueueListSave = timeQueueListObj.timeQueueList.map( + (element: WebHistory) => { + if (element.tabsessionId !== tabId) { + return element + } + } + ) + await storage.set("urlQueueList", { + urlQueueList: urlQueueListToSave.filter((item: any) => item) + }) + await storage.set("timeQueueList", { + timeQueueList: timeQueueListSave.filter((item: any) => item) + }) + } +}) diff --git a/surfsense_browser_extension/background/messages/savedata.ts b/surfsense_browser_extension/background/messages/savedata.ts new file mode 100644 index 000000000..e1e010a48 --- /dev/null +++ b/surfsense_browser_extension/background/messages/savedata.ts @@ -0,0 +1,149 @@ +import type { PlasmoMessaging } from "@plasmohq/messaging" +import { Storage } from "@plasmohq/storage" + +import { + emptyArr, + webhistoryToLangChainDocument +} from "~utils/commons" + +const clearMemory = async () => { + try { + const storage = new Storage({ area: "local" }) + + let webHistory: any = await storage.get("webhistory") + let urlQueue: any = await storage.get("urlQueueList") + let timeQueue: any = await storage.get("timeQueueList") + + if (!webHistory.webhistory) { + return + } + + //Main Cleanup COde + chrome.tabs.query({}, async (tabs) => { + //Get Active Tabs Ids + // console.log("Event Tabs",tabs) + let actives = tabs.map((tab) => { + if (tab.id) { + return tab.id + } + }) + + actives = actives.filter((item: any) => item) + + //Only retain which is still active + const newHistory = webHistory.webhistory.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + const newUrlQueue = urlQueue.urlQueueList.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + const newTimeQueue = timeQueue.timeQueueList.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + await storage.set("webhistory", { + webhistory: newHistory.filter((item: any) => item) + }) + await storage.set("urlQueueList", { + urlQueueList: newUrlQueue.filter((item: any) => item) + }) + await storage.set("timeQueueList", { + timeQueueList: newTimeQueue.filter((item: any) => item) + }) + }) + } catch (error) { + console.log(error) + } +} + +const handler: PlasmoMessaging.MessageHandler = async (req, res) => { + try { + const storage = new Storage({ area: "local" }) + + const webhistoryObj: any = await storage.get("webhistory") + const webhistory = webhistoryObj.webhistory + if (webhistory) { + let toSaveFinally: any[] = [] + let newHistoryAfterCleanup: any[] = [] + + for (let i = 0; i < webhistory.length; i++) { + const markdownFormat = webhistoryToLangChainDocument( + webhistory[i].tabsessionId, + webhistory[i].tabHistory + ) + toSaveFinally.push(...markdownFormat) + newHistoryAfterCleanup.push({ + tabsessionId: webhistory[i].tabsessionId, + tabHistory: emptyArr + }) + } + + await storage.set("webhistory",{ webhistory: newHistoryAfterCleanup }); + + // Log first item to debug metadata structure + if (toSaveFinally.length > 0) { + console.log("First item metadata:", toSaveFinally[0].metadata); + } + + // Create content array for documents in the format expected by the new API + const content = toSaveFinally.map(item => ({ + metadata: { + BrowsingSessionId: String(item.metadata.BrowsingSessionId || ""), + VisitedWebPageURL: String(item.metadata.VisitedWebPageURL || ""), + VisitedWebPageTitle: String(item.metadata.VisitedWebPageTitle || "No Title"), + VisitedWebPageDateWithTimeInISOString: String(item.metadata.VisitedWebPageDateWithTimeInISOString || ""), + VisitedWebPageReffererURL: String(item.metadata.VisitedWebPageReffererURL || ""), + VisitedWebPageVisitDurationInMilliseconds: String(item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0") + }, + pageContent: String(item.pageContent || "") + })); + + const token = await storage.get("token"); + const search_space_id = parseInt(await storage.get("search_space_id"), 10); + + const toSend = { + document_type: "EXTENSION", + content: content, + search_space_id: search_space_id + } + + console.log("toSend", toSend) + + const requestOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + }, + body: JSON.stringify(toSend) + } + + const response = await fetch( + `${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/documents/`, + requestOptions + ) + const resp = await response.json() + if (resp) { + await clearMemory() + res.send({ + message: "Save Job Started" + }) + } + } + } catch (error) { + console.log(error) + } +} + +export default handler diff --git a/surfsense_browser_extension/background/messages/savesnapshot.ts b/surfsense_browser_extension/background/messages/savesnapshot.ts new file mode 100644 index 000000000..90aea7b65 --- /dev/null +++ b/surfsense_browser_extension/background/messages/savesnapshot.ts @@ -0,0 +1,145 @@ +import { DOMParser } from "linkedom" + +import { Storage } from "@plasmohq/storage" +import type { PlasmoMessaging } from "@plasmohq/messaging" + +import type { WebHistory } from "~utils/interfaces" +import { webhistoryToLangChainDocument, getRenderedHtml } from "~utils/commons" +import { convertHtmlToMarkdown } from "dom-to-semantic-markdown" + +// @ts-ignore +global.Node = { + ELEMENT_NODE: 1, + ATTRIBUTE_NODE: 2, + TEXT_NODE: 3, + CDATA_SECTION_NODE: 4, + PROCESSING_INSTRUCTION_NODE: 7, + COMMENT_NODE: 8, + DOCUMENT_NODE: 9, + DOCUMENT_TYPE_NODE: 10, + DOCUMENT_FRAGMENT_NODE: 11, +}; + +const handler: PlasmoMessaging.MessageHandler = async (req, res) => { + try { + chrome.tabs.query( + { active: true, currentWindow: true }, + async function (tabs) { + const storage = new Storage({ area: "local" }) + const tab = tabs[0] + if (tab.id) { + const tabId: number = tab.id + console.log("tabs", tabs) + const result = await chrome.scripting.executeScript({ + // @ts-ignore + target: { tabId: tab.id }, + // @ts-ignore + func: getRenderedHtml, + // world: "MAIN" + }) + + console.log("SnapRes", result) + + let toPushInTabHistory: any = result[0].result // const { renderedHtml, title, url, entryTime } = result[0].result; + + toPushInTabHistory.pageContentMarkdown = convertHtmlToMarkdown( + toPushInTabHistory.renderedHtml, + { + extractMainContent: true, + enableTableColumnTracking: true, + includeMetaData: false, + overrideDOMParser: new DOMParser() + } + ) + + delete toPushInTabHistory.renderedHtml + + console.log("toPushInTabHistory", toPushInTabHistory) + + const urlQueueListObj: any = await storage.get("urlQueueList") + const timeQueueListObj: any = await storage.get("timeQueueList") + + const isUrlQueueThere = urlQueueListObj.urlQueueList.find( + (data: WebHistory) => data.tabsessionId === tabId + ) + const isTimeQueueThere = timeQueueListObj.timeQueueList.find( + (data: WebHistory) => data.tabsessionId === tabId + ) + + toPushInTabHistory.duration = + toPushInTabHistory.entryTime - + isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1] + if (isUrlQueueThere.urlQueue.length == 1) { + toPushInTabHistory.reffererUrl = "START" + } + if (isUrlQueueThere.urlQueue.length > 1) { + toPushInTabHistory.reffererUrl = + isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2] + } + + let toSaveFinally: any[] = [] + + const markdownFormat = webhistoryToLangChainDocument( + tab.id, + [toPushInTabHistory] + ) + toSaveFinally.push(...markdownFormat) + + console.log("toSaveFinally", toSaveFinally) + + // Log first item to debug metadata structure + if (toSaveFinally.length > 0) { + console.log("First item metadata:", toSaveFinally[0].metadata); + } + + // Create content array for documents in the format expected by the new API + // The metadata is already in the correct format in toSaveFinally + const content = toSaveFinally.map(item => ({ + metadata: { + BrowsingSessionId: String(item.metadata.BrowsingSessionId || ""), + VisitedWebPageURL: String(item.metadata.VisitedWebPageURL || ""), + VisitedWebPageTitle: String(item.metadata.VisitedWebPageTitle || "No Title"), + VisitedWebPageDateWithTimeInISOString: String(item.metadata.VisitedWebPageDateWithTimeInISOString || ""), + VisitedWebPageReffererURL: String(item.metadata.VisitedWebPageReffererURL || ""), + VisitedWebPageVisitDurationInMilliseconds: String(item.metadata.VisitedWebPageVisitDurationInMilliseconds || "0") + }, + pageContent: String(item.pageContent || "") + })); + + const token = await storage.get("token"); + const search_space_id = parseInt(await storage.get("search_space_id"), 10); + + const toSend = { + document_type: "EXTENSION", + content: content, + search_space_id: search_space_id + } + + const requestOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + "Authorization": `Bearer ${token}` + }, + body: JSON.stringify(toSend) + } + + const response = await fetch( + `${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/documents/`, + requestOptions + ) + const resp = await response.json() + if (resp) { + res.send({ + message: "Snapshot Saved Successfully" + }) + } + } + } + ) + } catch (error) { + console.log(error) + } +} + +export default handler diff --git a/surfsense_browser_extension/content.ts b/surfsense_browser_extension/content.ts new file mode 100644 index 000000000..c13fbfe8e --- /dev/null +++ b/surfsense_browser_extension/content.ts @@ -0,0 +1,8 @@ +import type { PlasmoCSConfig } from "plasmo" + +export const config: PlasmoCSConfig = { + matches: [""], + all_frames: true, + world: "MAIN" +} + diff --git a/surfsense_browser_extension/font.css b/surfsense_browser_extension/font.css new file mode 100644 index 000000000..ac400cd99 --- /dev/null +++ b/surfsense_browser_extension/font.css @@ -0,0 +1,10 @@ +@font-face { + font-family: "Fascinate"; + font-style: normal; + font-weight: 400; + font-display: swap; + src: url(data-base64:~assets/Fascinate.woff2) format("woff2"); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, + U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, + U+FEFF, U+FFFD; + } \ No newline at end of file diff --git a/surfsense_browser_extension/lib/utils.ts b/surfsense_browser_extension/lib/utils.ts new file mode 100644 index 000000000..bd0c391dd --- /dev/null +++ b/surfsense_browser_extension/lib/utils.ts @@ -0,0 +1,6 @@ +import { clsx, type ClassValue } from "clsx" +import { twMerge } from "tailwind-merge" + +export function cn(...inputs: ClassValue[]) { + return twMerge(clsx(inputs)) +} diff --git a/surfsense_browser_extension/package.json b/surfsense_browser_extension/package.json new file mode 100644 index 000000000..25600ce12 --- /dev/null +++ b/surfsense_browser_extension/package.json @@ -0,0 +1,62 @@ +{ + "name": "surfsense", + "displayName": "Surfsense", + "version": "0.0.1", + "description": "Extension to collect Browsing History for SurfSense.", + "author": "https://github.com/MODSetter", + "scripts": { + "dev": "plasmo dev", + "build": "plasmo build", + "package": "plasmo package" + }, + "dependencies": { + "@plasmohq/messaging": "^0.6.2", + "@plasmohq/storage": "^1.11.0", + "@radix-ui/react-dialog": "^1.1.2", + "@radix-ui/react-icons": "^1.3.2", + "@radix-ui/react-popover": "^1.1.2", + "@radix-ui/react-slot": "^1.1.0", + "@radix-ui/react-toast": "^1.2.2", + "class-variance-authority": "^0.7.0", + "clsx": "^2.1.1", + "cmdk": "^1.0.3", + "dom-to-semantic-markdown": "^1.2.11", + "linkedom": "0.1.34", + "lucide-react": "^0.454.0", + "plasmo": "0.89.4", + "postcss-loader": "^8.1.1", + "radix-ui": "^1.0.1", + "react": "18.2.0", + "react-dom": "18.2.0", + "react-hooks-global-state": "^2.1.0", + "react-router-dom": "^6.26.1", + "tailwind-merge": "^2.5.4", + "tailwindcss-animate": "^1.0.7" + }, + "devDependencies": { + "@ianvs/prettier-plugin-sort-imports": "4.1.1", + "@types/chrome": "0.0.258", + "@types/node": "20.11.5", + "@types/react": "18.2.48", + "@types/react-dom": "18.2.18", + "autoprefixer": "^10.4.20", + "postcss": "^8.4.41", + "prettier": "3.2.4", + "tailwindcss": "^3.4.10", + "typescript": "5.3.3" + }, + "manifest": { + "host_permissions": [ + "" + ], + "name": "SurfSense", + "description": "Extension to collect Browsing History for SurfSense.", + "version": "0.0.3" + }, + "permissions": [ + "storage", + "scripting", + "unlimitedStorage", + "activeTab" + ] +} diff --git a/surfsense_browser_extension/pnpm-lock.yaml b/surfsense_browser_extension/pnpm-lock.yaml new file mode 100644 index 000000000..1d963608e --- /dev/null +++ b/surfsense_browser_extension/pnpm-lock.yaml @@ -0,0 +1,8814 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@plasmohq/messaging': + specifier: ^0.6.2 + version: 0.6.2(react@18.2.0) + '@plasmohq/storage': + specifier: ^1.11.0 + version: 1.11.0(react@18.2.0) + '@radix-ui/react-dialog': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-icons': + specifier: ^1.3.2 + version: 1.3.2(react@18.2.0) + '@radix-ui/react-popover': + specifier: ^1.1.2 + version: 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': + specifier: ^1.1.0 + version: 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-toast': + specifier: ^1.2.2 + version: 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + class-variance-authority: + specifier: ^0.7.0 + version: 0.7.0 + clsx: + specifier: ^2.1.1 + version: 2.1.1 + cmdk: + specifier: ^1.0.3 + version: 1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + dom-to-semantic-markdown: + specifier: ^1.2.11 + version: 1.2.11 + linkedom: + specifier: 0.1.34 + version: 0.1.34 + lucide-react: + specifier: ^0.454.0 + version: 0.454.0(react@18.2.0) + plasmo: + specifier: 0.89.4 + version: 0.89.4(@swc/core@1.7.14(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(lodash@4.17.21)(postcss@8.4.41)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + postcss-loader: + specifier: ^8.1.1 + version: 8.1.1(postcss@8.4.41)(typescript@5.3.3) + radix-ui: + specifier: ^1.0.1 + version: 1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: + specifier: 18.2.0 + version: 18.2.0 + react-dom: + specifier: 18.2.0 + version: 18.2.0(react@18.2.0) + react-hooks-global-state: + specifier: ^2.1.0 + version: 2.1.0(react@18.2.0) + react-router-dom: + specifier: ^6.26.1 + version: 6.26.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + tailwind-merge: + specifier: ^2.5.4 + version: 2.5.4 + tailwindcss-animate: + specifier: ^1.0.7 + version: 1.0.7(tailwindcss@3.4.10) + devDependencies: + '@ianvs/prettier-plugin-sort-imports': + specifier: 4.1.1 + version: 4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.2.4) + '@types/chrome': + specifier: 0.0.258 + version: 0.0.258 + '@types/node': + specifier: 20.11.5 + version: 20.11.5 + '@types/react': + specifier: 18.2.48 + version: 18.2.48 + '@types/react-dom': + specifier: 18.2.18 + version: 18.2.18 + autoprefixer: + specifier: ^10.4.20 + version: 10.4.20(postcss@8.4.41) + postcss: + specifier: ^8.4.41 + version: 8.4.41 + prettier: + specifier: 3.2.4 + version: 3.2.4 + tailwindcss: + specifier: ^3.4.10 + version: 3.4.10 + typescript: + specifier: 5.3.3 + version: 5.3.3 + +packages: + + '@alloc/quick-lru@5.2.0': + resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} + engines: {node: '>=10'} + + '@ampproject/remapping@2.3.0': + resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} + engines: {node: '>=6.0.0'} + + '@babel/code-frame@7.24.7': + resolution: {integrity: sha512-BcYH1CVJBO9tvyIZ2jVeXgSIMvGZ2FDRvDdOIVQyuklNKSsx+eppDEBq/g47Ayw+RqNFE+URvOShmf+f/qwAlA==} + engines: {node: '>=6.9.0'} + + '@babel/compat-data@7.25.4': + resolution: {integrity: sha512-+LGRog6RAsCJrrrg/IO6LGmpphNe5DiK30dGjCoxxeGv49B10/3XYGxPsAwrDlMFcFEvdAUavDT8r9k/hSyQqQ==} + engines: {node: '>=6.9.0'} + + '@babel/core@7.25.2': + resolution: {integrity: sha512-BBt3opiCOxUr9euZ5/ro/Xv8/V7yJ5bjYMqG/C1YAo8MIKAnumZalCN+msbci3Pigy4lIQfPUpfMM27HMGaYEA==} + engines: {node: '>=6.9.0'} + + '@babel/generator@7.25.5': + resolution: {integrity: sha512-abd43wyLfbWoxC6ahM8xTkqLpGB2iWBVyuKC9/srhFunCd1SDNrV1s72bBpK4hLj8KLzHBBcOblvLQZBNw9r3w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-compilation-targets@7.25.2': + resolution: {integrity: sha512-U2U5LsSaZ7TAt3cfaymQ8WHh0pxvdHoEk6HVpaexxixjyEquMh0L0YNJNM6CTGKMXV1iksi0iZkGw4AcFkPaaw==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-imports@7.24.7': + resolution: {integrity: sha512-8AyH3C+74cgCVVXow/myrynrAGv+nTVg5vKu2nZph9x7RcRwzmh0VFallJuFTZ9mx6u4eSdXZfcOzSqTUm0HCA==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.25.2': + resolution: {integrity: sha512-BjyRAbix6j/wv83ftcVJmBt72QtHI56C7JXZoG2xATiLpmoC7dpd8WnkikExHDVPpi/3qCmO6WY1EaXOluiecQ==} + engines: {node: '>=6.9.0'} + peerDependencies: + '@babel/core': ^7.0.0 + + '@babel/helper-simple-access@7.24.7': + resolution: {integrity: sha512-zBAIvbCMh5Ts+b86r/CjU+4XGYIs+R1j951gxI3KmmxBMhCg4oQMsv6ZXQ64XOm/cvzfU1FmoCyt6+owc5QMYg==} + engines: {node: '>=6.9.0'} + + '@babel/helper-string-parser@7.24.8': + resolution: {integrity: sha512-pO9KhhRcuUyGnJWwyEgnRJTSIZHiT+vMD0kPeD+so0l7mxkMT19g3pjY9GTnHySck/hDzq+dtW/4VgnMkippsQ==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-identifier@7.24.7': + resolution: {integrity: sha512-rR+PBcQ1SMQDDyF6X0wxtG8QyLCgUB0eRAGguqRLfkCA87l7yAP7ehq8SNj96OOGTO8OBV70KhuFYcIkHXOg0w==} + engines: {node: '>=6.9.0'} + + '@babel/helper-validator-option@7.24.8': + resolution: {integrity: sha512-xb8t9tD1MHLungh/AIoWYN+gVHaB9kwlu8gffXGSt3FFEIT7RjS+xWbc2vUD1UTZdIpKj/ab3rdqJ7ufngyi2Q==} + engines: {node: '>=6.9.0'} + + '@babel/helpers@7.25.0': + resolution: {integrity: sha512-MjgLZ42aCm0oGjJj8CtSM3DB8NOOf8h2l7DCTePJs29u+v7yO/RBX9nShlKMgFnRks/Q4tBAe7Hxnov9VkGwLw==} + engines: {node: '>=6.9.0'} + + '@babel/highlight@7.24.7': + resolution: {integrity: sha512-EStJpq4OuY8xYfhGVXngigBJRWxftKX9ksiGDnmlY3o7B/V7KIAc9X4oiK87uPJSc/vs5L869bem5fhZa8caZw==} + engines: {node: '>=6.9.0'} + + '@babel/parser@7.25.4': + resolution: {integrity: sha512-nq+eWrOgdtu3jG5Os4TQP3x3cLA8hR8TvJNjD8vnPa20WGycimcparWnLK4jJhElTK6SDyuJo1weMKO/5LpmLA==} + engines: {node: '>=6.0.0'} + hasBin: true + + '@babel/runtime@7.25.4': + resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} + engines: {node: '>=6.9.0'} + + '@babel/template@7.25.0': + resolution: {integrity: sha512-aOOgh1/5XzKvg1jvVz7AVrx2piJ2XBi227DHmbY6y+bM9H2FlN+IfecYu4Xl0cNiiVejlsCri89LUsbj8vJD9Q==} + engines: {node: '>=6.9.0'} + + '@babel/traverse@7.25.4': + resolution: {integrity: sha512-VJ4XsrD+nOvlXyLzmLzUs/0qjFS4sK30te5yEFlvbbUNEgKaVb2BHZUpAL+ttLPQAHNrsI3zZisbfha5Cvr8vg==} + engines: {node: '>=6.9.0'} + + '@babel/types@7.25.4': + resolution: {integrity: sha512-zQ1ijeeCXVEh+aNL0RlmkPkG8HUiDcU2pzQQFjtbntgAczRASFzj4H+6+bV+dy1ntKR14I/DypeuRG1uma98iQ==} + engines: {node: '>=6.9.0'} + + '@esbuild/android-arm64@0.18.20': + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.18.20': + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.18.20': + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.18.20': + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.18.20': + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.18.20': + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.18.20': + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.18.20': + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.18.20': + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.18.20': + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.18.20': + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.18.20': + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.18.20': + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.18.20': + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.18.20': + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.18.20': + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-x64@0.18.20': + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-x64@0.18.20': + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.18.20': + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.18.20': + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.18.20': + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.18.20': + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + + '@expo/spawn-async@1.7.2': + resolution: {integrity: sha512-QdWi16+CHB9JYP7gma19OVVg0BFkvU8zNj9GjWorYI8Iv8FUxjOCcYRuAmX4s/h91e4e7BPsskc8cSrZYho9Ew==} + engines: {node: '>=12'} + + '@floating-ui/core@1.6.8': + resolution: {integrity: sha512-7XJ9cPU+yI2QeLS+FCSlqNFZJq8arvswefkZrYI1yQBbftw6FyrZOxYSh+9S7z7TpeWlRt9zJ5IhM1WIL334jA==} + + '@floating-ui/dom@1.6.12': + resolution: {integrity: sha512-NP83c0HjokcGVEMeoStg317VD9W7eDlGK7457dMBANbKA6GJZdc7rjujdgqzTaz93jkGgc5P/jeWbaCHnMNc+w==} + + '@floating-ui/react-dom@2.1.2': + resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.8': + resolution: {integrity: sha512-kym7SodPp8/wloecOpcmSnWJsK7M0E5Wg8UcFA+uO4B9s5d0ywXOEro/8HM9x0rW+TljRzul/14UYz3TleT3ig==} + + '@ianvs/prettier-plugin-sort-imports@4.1.1': + resolution: {integrity: sha512-kJhXq63ngpTQ2dxgf5GasbPJWsJA3LgoOdd7WGhpUSzLgLgI4IsIzYkbJf9kmpOHe7Vdm/o3PcRA3jmizXUuAQ==} + peerDependencies: + '@vue/compiler-sfc': '>=3.0.0' + prettier: 2 || 3 + peerDependenciesMeta: + '@vue/compiler-sfc': + optional: true + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@jridgewell/gen-mapping@0.3.5': + resolution: {integrity: sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==} + engines: {node: '>=6.0.0'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/set-array@1.2.1': + resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@lezer/common@0.15.12': + resolution: {integrity: sha512-edfwCxNLnzq5pBA/yaIhwJ3U3Kz8VAUOTRg0hhxaizaI1N+qxV7EXDv/kLCkLeq2RzSFvxexlaj5Mzfn2kY0Ig==} + + '@lezer/common@1.2.1': + resolution: {integrity: sha512-yemX0ZD2xS/73llMZIK6KplkjIjf2EvAHcinDi/TfJ9hS25G0388+ClHt6/3but0oOxinTcQHJLDXh6w1crzFQ==} + + '@lezer/lr@0.15.8': + resolution: {integrity: sha512-bM6oE6VQZ6hIFxDNKk8bKPa14hqFrV07J/vHGOeiAbJReIaQXmkVb6xQu4MR+JBTLa5arGRyAAjJe1qaQt3Uvg==} + + '@lezer/lr@1.4.2': + resolution: {integrity: sha512-pu0K1jCIdnQ12aWNaAVU5bzi7Bd1w54J3ECgANPmYLtQKP0HBj2cE/5coBD66MT10xbtIuUr7tg0Shbsvk0mDA==} + + '@ljharb/through@2.3.13': + resolution: {integrity: sha512-/gKJun8NNiWGZJkGzI/Ragc53cOdcLNdzjLaIa+GEjguQs0ulsurx8WN0jijdK9yPqDvziX995sMRLyLt1uZMQ==} + engines: {node: '>= 0.4'} + + '@lmdb/lmdb-darwin-arm64@2.5.2': + resolution: {integrity: sha512-+F8ioQIUN68B4UFiIBYu0QQvgb9FmlKw2ctQMSBfW2QBrZIxz9vD9jCGqTCPqZBRbPHAS/vG1zSXnKqnS2ch/A==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-arm64@2.7.11': + resolution: {integrity: sha512-r6+vYq2vKzE+vgj/rNVRMwAevq0+ZR9IeMFIqcSga+wMtMdXQ27KqQ7uS99/yXASg29bos7yHP3yk4x6Iio0lw==} + cpu: [arm64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@2.5.2': + resolution: {integrity: sha512-KvPH56KRLLx4KSfKBx0m1r7GGGUMXm0jrKmNE7plbHlesZMuPJICtn07HYgQhj1LNsK7Yqwuvnqh1QxhJnF1EA==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-darwin-x64@2.7.11': + resolution: {integrity: sha512-jhj1aB4K8ycRL1HOQT5OtzlqOq70jxUQEWRN9Gqh3TIDN30dxXtiHi6EWF516tzw6v2+3QqhDMJh8O6DtTGG8Q==} + cpu: [x64] + os: [darwin] + + '@lmdb/lmdb-linux-arm64@2.5.2': + resolution: {integrity: sha512-aLl89VHL/wjhievEOlPocoefUyWdvzVrcQ/MHQYZm2JfV1jUsrbr/ZfkPPUFvZBf+VSE+Q0clWs9l29PCX1hTQ==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm64@2.7.11': + resolution: {integrity: sha512-7xGEfPPbmVJWcY2Nzqo11B9Nfxs+BAsiiaY/OcT4aaTDdykKeCjvKMQJA3KXCtZ1AtiC9ljyGLi+BfUwdulY5A==} + cpu: [arm64] + os: [linux] + + '@lmdb/lmdb-linux-arm@2.5.2': + resolution: {integrity: sha512-5kQAP21hAkfW5Bl+e0P57dV4dGYnkNIpR7f/GAh6QHlgXx+vp/teVj4PGRZaKAvt0GX6++N6hF8NnGElLDuIDw==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-arm@2.7.11': + resolution: {integrity: sha512-dHfLFVSrw/v5X5lkwp0Vl7+NFpEeEYKfMG2DpdFJnnG1RgHQZngZxCaBagFoaJGykRpd2DYF1AeuXBFrAUAXfw==} + cpu: [arm] + os: [linux] + + '@lmdb/lmdb-linux-x64@2.5.2': + resolution: {integrity: sha512-xUdUfwDJLGjOUPH3BuPBt0NlIrR7f/QHKgu3GZIXswMMIihAekj2i97oI0iWG5Bok/b+OBjHPfa8IU9velnP/Q==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-linux-x64@2.7.11': + resolution: {integrity: sha512-vUKI3JrREMQsXX8q0Eq5zX2FlYCKWMmLiCyyJNfZK0Uyf14RBg9VtB3ObQ41b4swYh2EWaltasWVe93Y8+KDng==} + cpu: [x64] + os: [linux] + + '@lmdb/lmdb-win32-x64@2.5.2': + resolution: {integrity: sha512-zrBczSbXKxEyK2ijtbRdICDygRqWSRPpZMN5dD1T8VMEW5RIhIbwFWw2phDRXuBQdVDpSjalCIUMWMV2h3JaZA==} + cpu: [x64] + os: [win32] + + '@lmdb/lmdb-win32-x64@2.7.11': + resolution: {integrity: sha512-BJwkHlSUgtB+Ei52Ai32M1AOMerSlzyIGA/KC4dAGL+GGwVMdwG8HGCOA2TxP3KjhbgDPMYkv7bt/NmOmRIFng==} + cpu: [x64] + os: [win32] + + '@mischnic/json-sourcemap@0.1.0': + resolution: {integrity: sha512-dQb3QnfNqmQNYA4nFSN/uLaByIic58gOXq4Y4XqLOWmOrw73KmJPt/HLyG0wvn1bnR6mBKs/Uwvkh+Hns1T0XA==} + engines: {node: '>=12.0.0'} + + '@mischnic/json-sourcemap@0.1.1': + resolution: {integrity: sha512-iA7+tyVqfrATAIsIRWQG+a7ZLLD0VaOCKV2Wd/v4mqIU3J9c4jx9p7S0nw1XH3gJCKNBOOwACOPYYSUu9pgT+w==} + engines: {node: '>=12.0.0'} + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + resolution: {integrity: sha512-QZHtlVgbAdy2zAqNA9Gu1UpIuI8Xvsd1v8ic6B2pZmeFnFcMWiPLfWXh7TVw4eGEZ/C9TH281KwhVoeQUKbyjw==} + cpu: [arm64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + resolution: {integrity: sha512-mdzd3AVzYKuUmiWOQ8GNhl64/IoFGol569zNRdkLReh6LRLHOXxU4U8eq0JwaD8iFHdVGqSy4IjFL4reoWCDFw==} + cpu: [x64] + os: [darwin] + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + resolution: {integrity: sha512-YxQL+ax0XqBJDZiKimS2XQaf+2wDGVa1enVRGzEvLLVFeqa5kx2bWbtcSXgsxjQB7nRqqIGFIcLteF/sHeVtQg==} + cpu: [arm64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + resolution: {integrity: sha512-fg0uy/dG/nZEXfYilKoRe7yALaNmHoYeIoJuJ7KJ+YyU2bvY8vPv27f7UKhGRpY6euFYqEVhxCFZgAUNQBM3nw==} + cpu: [arm] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + resolution: {integrity: sha512-cvwNfbP07pKUfq1uH+S6KJ7dT9K8WOE4ZiAcsrSes+UY55E/0jLYc+vq+DO7jlmqRb5zAggExKm0H7O/CBaesg==} + cpu: [x64] + os: [linux] + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + resolution: {integrity: sha512-x0fWaQtYp4E6sktbsdAqnehxDgEc/VwM7uLsRCYWaiGu0ykYdZPiS8zCWdnjHwyiumousxfBm4SO31eXqwEZhQ==} + cpu: [x64] + os: [win32] + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@parcel/bundler-default@2.9.3': + resolution: {integrity: sha512-JjJK8dq39/UO/MWI/4SCbB1t/qgpQRFnFDetAAAezQ8oN++b24u1fkMDa/xqQGjbuPmGeTds5zxGgYs7id7PYg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/cache@2.8.3': + resolution: {integrity: sha512-k7xv5vSQrJLdXuglo+Hv3yF4BCSs1tQ/8Vbd6CHTkOhf7LcGg6CPtLw053R/KdMpd/4GPn0QrAsOLdATm1ELtQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.8.3 + + '@parcel/cache@2.9.3': + resolution: {integrity: sha512-Bj/H2uAJJSXtysG7E/x4EgTrE2hXmm7td/bc97K8M9N7+vQjxf7xb0ebgqe84ePVMkj4MVQSMEJkEucXVx4b0Q==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/codeframe@2.8.3': + resolution: {integrity: sha512-FE7sY53D6n/+2Pgg6M9iuEC6F5fvmyBkRE4d9VdnOoxhTXtkEqpqYgX7RJ12FAQwNlxKq4suBJQMgQHMF2Kjeg==} + engines: {node: '>= 12.0.0'} + + '@parcel/codeframe@2.9.3': + resolution: {integrity: sha512-z7yTyD6h3dvduaFoHpNqur74/2yDWL++33rjQjIjCaXREBN6dKHoMGMizzo/i4vbiI1p9dDox2FIDEHCMQxqdA==} + engines: {node: '>= 12.0.0'} + + '@parcel/compressor-raw@2.9.3': + resolution: {integrity: sha512-jz3t4/ICMsHEqgiTmv5i1DJva2k5QRpZlBELVxfY+QElJTVe8edKJ0TiKcBxh2hx7sm4aUigGmp7JiqqHRRYmA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/config-default@2.9.3': + resolution: {integrity: sha512-tqN5tF7QnVABDZAu76co5E6N8mA9n8bxiWdK4xYyINYFIEHgX172oRTqXTnhEMjlMrdmASxvnGlbaPBaVnrCTw==} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/core@2.9.3': + resolution: {integrity: sha512-4KlM1Zr/jpsqWuMXr2zmGsaOUs1zMMFh9vfCNKRZkptf+uk8I3sugHbNdo+F5B+4e2yMuOEb1zgAmvJLeuH6ww==} + engines: {node: '>= 12.0.0'} + + '@parcel/diagnostic@2.8.3': + resolution: {integrity: sha512-u7wSzuMhLGWZjVNYJZq/SOViS3uFG0xwIcqXw12w54Uozd6BH8JlhVtVyAsq9kqnn7YFkw6pXHqAo5Tzh4FqsQ==} + engines: {node: '>= 12.0.0'} + + '@parcel/diagnostic@2.9.3': + resolution: {integrity: sha512-6jxBdyB3D7gP4iE66ghUGntWt2v64E6EbD4AetZk+hNJpgudOOPsKTovcMi/i7I4V0qD7WXSF4tvkZUoac0jwA==} + engines: {node: '>= 12.0.0'} + + '@parcel/events@2.8.3': + resolution: {integrity: sha512-hoIS4tAxWp8FJk3628bsgKxEvR7bq2scCVYHSqZ4fTi/s0+VymEATrRCUqf+12e5H47uw1/ZjoqrGtBI02pz4w==} + engines: {node: '>= 12.0.0'} + + '@parcel/events@2.9.3': + resolution: {integrity: sha512-K0Scx+Bx9f9p1vuShMzNwIgiaZUkxEnexaKYHYemJrM7pMAqxIuIqhnvwurRCsZOVLUJPDDNJ626cWTc5vIq+A==} + engines: {node: '>= 12.0.0'} + + '@parcel/fs-search@2.8.3': + resolution: {integrity: sha512-DJBT2N8knfN7Na6PP2mett3spQLTqxFrvl0gv+TJRp61T8Ljc4VuUTb0hqBj+belaASIp3Q+e8+SgaFQu7wLiQ==} + engines: {node: '>= 12.0.0'} + + '@parcel/fs-search@2.9.3': + resolution: {integrity: sha512-nsNz3bsOpwS+jphcd+XjZL3F3PDq9lik0O8HPm5f6LYkqKWT+u/kgQzA8OkAHCR3q96LGiHxUywHPEBc27vI4Q==} + engines: {node: '>= 12.0.0'} + + '@parcel/fs@2.8.3': + resolution: {integrity: sha512-y+i+oXbT7lP0e0pJZi/YSm1vg0LDsbycFuHZIL80pNwdEppUAtibfJZCp606B7HOjMAlNZOBo48e3hPG3d8jgQ==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.8.3 + + '@parcel/fs@2.9.3': + resolution: {integrity: sha512-/PrRKgCRw22G7rNPSpgN3Q+i2nIkZWuvIOAdMG4KWXC4XLp8C9jarNaWd5QEQ75amjhQSl3oUzABzkdCtkKrgg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/graph@2.9.3': + resolution: {integrity: sha512-3LmRJmF8+OprAr6zJT3X2s8WAhLKkrhi6RsFlMWHifGU5ED1PFcJWFbOwJvSjcAhMQJP0fErcFIK1Ludv3Vm3g==} + engines: {node: '>= 12.0.0'} + + '@parcel/hash@2.8.3': + resolution: {integrity: sha512-FVItqzjWmnyP4ZsVgX+G00+6U2IzOvqDtdwQIWisCcVoXJFCqZJDy6oa2qDDFz96xCCCynjRjPdQx2jYBCpfYw==} + engines: {node: '>= 12.0.0'} + + '@parcel/hash@2.9.3': + resolution: {integrity: sha512-qlH5B85XLzVAeijgKPjm1gQu35LoRYX/8igsjnN8vOlbc3O8BYAUIutU58fbHbtE8MJPbxQQUw7tkTjeoujcQQ==} + engines: {node: '>= 12.0.0'} + + '@parcel/logger@2.8.3': + resolution: {integrity: sha512-Kpxd3O/Vs7nYJIzkdmB6Bvp3l/85ydIxaZaPfGSGTYOfaffSOTkhcW9l6WemsxUrlts4za6CaEWcc4DOvaMOPA==} + engines: {node: '>= 12.0.0'} + + '@parcel/logger@2.9.3': + resolution: {integrity: sha512-5FNBszcV6ilGFcijEOvoNVG6IUJGsnMiaEnGQs7Fvc1dktTjEddnoQbIYhcSZL63wEmzBZOgkT5yDMajJ/41jw==} + engines: {node: '>= 12.0.0'} + + '@parcel/markdown-ansi@2.8.3': + resolution: {integrity: sha512-4v+pjyoh9f5zuU/gJlNvNFGEAb6J90sOBwpKJYJhdWXLZMNFCVzSigxrYO+vCsi8G4rl6/B2c0LcwIMjGPHmFQ==} + engines: {node: '>= 12.0.0'} + + '@parcel/markdown-ansi@2.9.3': + resolution: {integrity: sha512-/Q4X8F2aN8UNjAJrQ5NfK2OmZf6shry9DqetUSEndQ0fHonk78WKt6LT0zSKEBEW/bB/bXk6mNMsCup6L8ibjQ==} + engines: {node: '>= 12.0.0'} + + '@parcel/namer-default@2.9.3': + resolution: {integrity: sha512-1ynFEcap48/Ngzwwn318eLYpLUwijuuZoXQPCsEQ21OOIOtfhFQJaPwXTsw6kRitshKq76P2aafE0BioGSqxcA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/node-resolver-core@3.0.3': + resolution: {integrity: sha512-AjxNcZVHHJoNT/A99PKIdFtwvoze8PAiC3yz8E/dRggrDIOboUEodeQYV5Aq++aK76uz/iOP0tST2T8A5rhb1A==} + engines: {node: '>= 12.0.0'} + + '@parcel/optimizer-css@2.9.3': + resolution: {integrity: sha512-RK1QwcSdWDNUsFvuLy0hgnYKtPQebzCb0vPPzqs6LhL+vqUu9utOyRycGaQffHCkHVQP6zGlN+KFssd7YtFGhA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/optimizer-data-url@2.9.3': + resolution: {integrity: sha512-k8lOKLzgZ24JKOuyrNe5PptoH8GJ78AwnumG1xEOKZ77gZnUgdrn3XdjzE28ZqTI4LFkT3jApUiBKBmqnWDe7Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/optimizer-htmlnano@2.9.3': + resolution: {integrity: sha512-9g/KBck3c6DokmJfvJ5zpHFBiCSolaGrcsTGx8C3YPdCTVTI9P1TDCwUxvAr4LjpcIRSa82wlLCI+nF6sSgxKA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/optimizer-image@2.9.3': + resolution: {integrity: sha512-530YzthE7kmecnNhPbkAK+26yQNt69pfJrgE0Ev0BZaM1Wu2+33nki7o8qvkTkikhPrurEJLGIXt1qKmbKvCbA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/optimizer-svgo@2.9.3': + resolution: {integrity: sha512-ytQS0wY5JJhWU4mL0wfhYDUuHcfuw+Gy2+JcnTm1t1AZXHlOTbU6EzRWNqBShsgXjvdrQQXizAe3B6GFFlFJVQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/optimizer-swc@2.9.3': + resolution: {integrity: sha512-GQINNeqtdpL1ombq/Cpwi6IBk02wKJ/JJbYbyfHtk8lxlq13soenpwOlzJ5T9D2fdG+FUhai9NxpN5Ss4lNoAg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/package-manager@2.8.3': + resolution: {integrity: sha512-tIpY5pD2lH53p9hpi++GsODy6V3khSTX4pLEGuMpeSYbHthnOViobqIlFLsjni+QA1pfc8NNNIQwSNdGjYflVA==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.8.3 + + '@parcel/package-manager@2.9.3': + resolution: {integrity: sha512-NH6omcNTEupDmW4Lm1e4NUYBjdqkURxgZ4CNESESInHJe6tblVhNB8Rpr1ar7zDar7cly9ILr8P6N3Ei7bTEjg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/packager-css@2.9.3': + resolution: {integrity: sha512-mePiWiYZOULY6e1RdAIJyRoYqXqGci0srOaVZYaP7mnrzvJgA63kaZFFsDiEWghunQpMUuUjM2x/vQVHzxmhKQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/packager-html@2.9.3': + resolution: {integrity: sha512-0Ex+O0EaZf9APNERRNGgGto02hFJ6f5RQEvRWBK55WAV1rXeU+kpjC0c0qZvnUaUtXfpWMsEBkevJCwDkUMeMg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/packager-js@2.9.3': + resolution: {integrity: sha512-V5xwkoE3zQ3R+WqAWhA1KGQ791FvJeW6KonOlMI1q76Djjgox68hhObqcLu66AmYNhR2R/wUpkP18hP2z8dSFw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/packager-raw@2.9.3': + resolution: {integrity: sha512-oPQTNoYanQ2DdJyL61uPYK2py83rKOT8YVh2QWAx0zsSli6Kiy64U3+xOCYWgDVCrHw9+9NpQMuAdSiFg4cq8g==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/packager-svg@2.9.3': + resolution: {integrity: sha512-p/Ya6UO9DAkaCUFxfFGyeHZDp9YPAlpdnh1OChuwqSFOXFjjeXuoK4KLT+ZRalVBo2Jo8xF70oKMZw4MVvaL7Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/plugin@2.8.3': + resolution: {integrity: sha512-jZ6mnsS4D9X9GaNnvrixDQwlUQJCohDX2hGyM0U0bY2NWU8Km97SjtoCpWjq+XBCx/gpC4g58+fk9VQeZq2vlw==} + engines: {node: '>= 12.0.0'} + + '@parcel/plugin@2.9.3': + resolution: {integrity: sha512-qN85Gqr2GMuxX1dT1mnuO9hOcvlEv1lrYrCxn7CJN2nUhbwcfG+LEvcrCzCOJ6XtIHm+ZBV9h9p7FfoPLvpw+g==} + engines: {node: '>= 12.0.0'} + + '@parcel/profiler@2.9.3': + resolution: {integrity: sha512-pyHc9lw8VZDfgZoeZWZU9J0CVEv1Zw9O5+e0DJPDPHuXJYr72ZAOhbljtU3owWKAeW+++Q2AZWkbUGEOjI/e6g==} + engines: {node: '>= 12.0.0'} + + '@parcel/reporter-bundle-buddy@2.9.3': + resolution: {integrity: sha512-9ftzLZ161USdvnxueT55EWufLI48va0xJfB5MAJLG92VAS1N1FSFgYKdkGFzBKw0eK9UScQNYnntCGC17rBayQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/reporter-dev-server@2.9.3': + resolution: {integrity: sha512-s6eboxdLEtRSvG52xi9IiNbcPKC0XMVmvTckieue2EqGDbDcaHQoHmmwkk0rNq0/Z/UxelGcQXoIYC/0xq3ykQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/resolver-default@2.9.3': + resolution: {integrity: sha512-8ESJk1COKvDzkmOnppNXoDamNMlYVIvrKc2RuFPmp8nKVj47R6NwMgvwxEaatyPzvkmyTpq5RvG9I3HFc+r4Cw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/runtime-browser-hmr@2.9.3': + resolution: {integrity: sha512-EgiDIDrVAWpz7bOzWXqVinQkaFjLwT34wsonpXAbuI7f7r00d52vNAQC9AMu+pTijA3gyKoJ+Q4NWPMZf7ACDA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/runtime-js@2.8.3': + resolution: {integrity: sha512-IRja0vNKwvMtPgIqkBQh0QtRn0XcxNC8HU1jrgWGRckzu10qJWO+5ULgtOeR4pv9krffmMPqywGXw6l/gvJKYQ==} + engines: {node: '>= 12.0.0', parcel: ^2.8.3} + + '@parcel/runtime-js@2.9.3': + resolution: {integrity: sha512-EvIy+qXcKnB5qxHhe96zmJpSAViNVXHfQI5RSdZ2a7CPwORwhTI+zPNT9sb7xb/WwFw/WuTTgzT40b41DceU6Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/runtime-react-refresh@2.9.3': + resolution: {integrity: sha512-XBgryZQIyCmi6JwEfMUCmINB3l1TpTp9a2iFxmYNpzHlqj4Ve0saKaqWOVRLvC945ZovWIBzcSW2IYqWKGtbAA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/runtime-service-worker@2.9.3': + resolution: {integrity: sha512-qLJLqv1mMdWL7gyh8aKBFFAuEiJkhUUgLKpdn6eSfH/R7kTtb76WnOwqUrhvEI9bZFUM/8Pa1bzJnPpqSOM+Sw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/source-map@2.1.1': + resolution: {integrity: sha512-Ejx1P/mj+kMjQb8/y5XxDUn4reGdr+WyKYloBljpppUy8gs42T+BNoEOuRYqDVdgPc6NxduzIDoJS9pOFfV5Ew==} + engines: {node: ^12.18.3 || >=14} + + '@parcel/transformer-babel@2.9.3': + resolution: {integrity: sha512-pURtEsnsp3h6tOBDuzh9wRvVtw4PgIlqwAArIWdrG7iwqOUYv9D8ME4+ePWEu7MQWAp58hv9pTJtqWv4T+Sq8A==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-css@2.9.3': + resolution: {integrity: sha512-duWMdbEBBPjg3fQdXF16iWIdThetDZvCs2TpUD7xOlXH6kR0V5BJy8ONFT15u1RCqIV9hSNGaS3v3I9YRNY5zQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-graphql@2.9.3': + resolution: {integrity: sha512-cIohsH3WlXgn63baU35ZoWHzttmkyE2Q1pexKjszODzSUq3pdcg+9k4rB/z8GGMzXvFRYuBgl2M2Ukqz7SueMg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-html@2.9.3': + resolution: {integrity: sha512-0NU4omcHzFXA1seqftAXA2KNZaMByoKaNdXnLgBgtCGDiYvOcL+6xGHgY6pw9LvOh5um10KI5TxSIMILoI7VtA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-image@2.9.3': + resolution: {integrity: sha512-7CEe35RaPadQzLIuxzTtIxnItvOoy46hcbXtOdDt6lmVa4omuOygZYRIya2lsGIP4JHvAaALMb5nt99a1uTwJg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/transformer-inline-string@2.9.3': + resolution: {integrity: sha512-IZNd0Ksl32psX1M41KbUc4BmvVSoLVnlpaMrh9C/l+piFSkDXWMnF0PONX/RcxYMBIwB2jYllheIKH54naeNaA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-js@2.9.3': + resolution: {integrity: sha512-Z2MVVg5FYcPOfxlUwxqb5l9yjTMEqE3KI3zq2MBRUme6AV07KxLmCDF23b6glzZlHWQUE8MXzYCTAkOPCcPz+Q==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@parcel/transformer-json@2.9.3': + resolution: {integrity: sha512-yNL27dbOLhkkrjaQjiQ7Im9VOxmkfuuSNSmS0rA3gEjVcm07SLKRzWkAaPnyx44Lb6bzyOTWwVrb9aMmxgADpA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-less@2.9.3': + resolution: {integrity: sha512-qwF5NQ8rPZjS79tv9RRPxzkZcwLcI4Xg2gHm9c1PvsgoaL2tVNpfjiRA6MOrzfJp+xr7xEzeMDZksOJ1WQiiQg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-postcss@2.9.3': + resolution: {integrity: sha512-HoDvPqKzhpmvMmHqQhDnt8F1vH61m6plpGiYaYnYv2Om4HHi5ZIq9bO+9QLBnTKfaZ7ndYSefTKOxTYElg7wyw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-posthtml@2.9.3': + resolution: {integrity: sha512-2fQGgrzRmaqbWf3y2/T6xhqrNjzqMMKksqJzvc8TMfK6f2kg3Ddjv158eaSW2JdkV39aY7tvAOn5f1uzo74BMA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-raw@2.9.3': + resolution: {integrity: sha512-oqdPzMC9QzWRbY9J6TZEqltknjno+dY24QWqf8ondmdF2+W+/2mRDu59hhCzQrqUHgTq4FewowRZmSfpzHxwaQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-react-refresh-wrap@2.9.3': + resolution: {integrity: sha512-cb9NyU6oJlDblFIlzqIE8AkvRQVGl2IwJNKwD4PdE7Y6sq2okGEPG4hOw3k/Y9JVjM4/2pUORqvjSRhWwd9oVQ==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-sass@2.9.3': + resolution: {integrity: sha512-i9abj9bKg3xCHghJyTM3rUVxIEn9n1Rl+DFdpyNAD8VZ52COfOshFDQOWNuhU1hEnJOFYCjnfcO0HRTsg3dWmg==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-svg-react@2.9.3': + resolution: {integrity: sha512-RXmCn58CkCBhpsS1AaRBrSRla0U5JN3r3hb7kQvEb+d7chGnsxCCWsBxtlrxPUjoUFLdQli9rhpCTkiyOBXY2A==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-svg@2.9.3': + resolution: {integrity: sha512-ypmE+dzB09IMCdEAkOsSxq1dEIm2A3h67nAFz4qbfHbwNgXBUuy/jB3ZMwXN/cO0f7SBh/Ap8Jhq6vmGqB5tWw==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/transformer-worklet@2.9.3': + resolution: {integrity: sha512-Fgd81OTOvAxAKoBGsQow/mgxELaNG1FeZW4DuDEPo/hR3lbs90oYuVpG2thdx7hmi/W6xqhrLaEN5Ea1v0LvEA==} + engines: {node: '>= 12.0.0', parcel: ^2.9.3} + + '@parcel/types@2.8.3': + resolution: {integrity: sha512-FECA1FB7+0UpITKU0D6TgGBpGxYpVSMNEENZbSJxFSajNy3wrko+zwBKQmFOLOiPcEtnGikxNs+jkFWbPlUAtw==} + + '@parcel/types@2.9.3': + resolution: {integrity: sha512-NSNY8sYtRhvF1SqhnIGgGvJocyWt1K8Tnw5cVepm0g38ywtX6mwkBvMkmeehXkII4mSUn+frD9wGsydTunezvA==} + + '@parcel/utils@2.8.3': + resolution: {integrity: sha512-IhVrmNiJ+LOKHcCivG5dnuLGjhPYxQ/IzbnF2DKNQXWBTsYlHkJZpmz7THoeLtLliGmSOZ3ZCsbR8/tJJKmxjA==} + engines: {node: '>= 12.0.0'} + + '@parcel/utils@2.9.3': + resolution: {integrity: sha512-cesanjtj/oLehW8Waq9JFPmAImhoiHX03ihc3JTWkrvJYSbD7wYKCDgPAM3JiRAqvh1LZ6P699uITrYWNoRLUg==} + engines: {node: '>= 12.0.0'} + + '@parcel/watcher-android-arm64@2.2.0': + resolution: {integrity: sha512-nU2wh00CTQT9rr1TIKTjdQ9lAGYpmz6XuKw0nAwAN+S2A5YiD55BK1u+E5WMCT8YOIDe/n6gaj4o/Bi9294SSQ==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [android] + + '@parcel/watcher-darwin-arm64@2.2.0': + resolution: {integrity: sha512-cJl0UZDcodciy3TDMomoK/Huxpjlkkim3SyMgWzjovHGOZKNce9guLz2dzuFwfObBFCjfznbFMIvAZ5syXotYw==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [darwin] + + '@parcel/watcher-darwin-x64@2.2.0': + resolution: {integrity: sha512-QI77zxaGrCV1StKcoRYfsUfmUmvPMPfQrubkBBy5XujV2fwaLgZivQOTQMBgp5K2+E19u1ufpspKXAPqSzpbyg==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [darwin] + + '@parcel/watcher-linux-arm-glibc@2.2.0': + resolution: {integrity: sha512-I2GPBcAXazPzabCmfsa3HRRW+MGlqxYd8g8RIueJU+a4o5nyNZDz0CR1cu0INT0QSQXEZV7w6UE8Hz9CF8u3Pg==} + engines: {node: '>= 10.0.0'} + cpu: [arm] + os: [linux] + + '@parcel/watcher-linux-arm64-glibc@2.2.0': + resolution: {integrity: sha512-St5mlfp+2lS9AmgixUqfwJa/DwVmTCJxC1HcOubUTz6YFOKIlkHCeUa1Bxi4E/tR/HSez8+heXHL8HQkJ4Bd8g==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-arm64-musl@2.2.0': + resolution: {integrity: sha512-jS+qfhhoOBVWwMLP65MaG8xdInMK30pPW8wqTCg2AAuVJh5xepMbzkhHJ4zURqHiyY3EiIRuYu4ONJKCxt8iqA==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [linux] + + '@parcel/watcher-linux-x64-glibc@2.2.0': + resolution: {integrity: sha512-xJvJ7R2wJdi47WZBFS691RDOWvP1j/IAs3EXaWVhDI8FFITbWrWaln7KoNcR0Y3T+ZwimFY/cfb0PNht1q895g==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-linux-x64-musl@2.2.0': + resolution: {integrity: sha512-D+NMpgr23a+RI5mu8ZPKWy7AqjBOkURFDgP5iIXXEf/K3hm0jJ3ogzi0Ed2237B/CdYREimCgXyeiAlE/FtwyA==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [linux] + + '@parcel/watcher-win32-arm64@2.2.0': + resolution: {integrity: sha512-z225cPn3aygJsyVUOWwfyW+fY0Tvk7N3XCOl66qUPFxpbuXeZuiuuJemmtm8vxyqa3Ur7peU/qJxrpC64aeI7Q==} + engines: {node: '>= 10.0.0'} + cpu: [arm64] + os: [win32] + + '@parcel/watcher-win32-x64@2.2.0': + resolution: {integrity: sha512-JqGW0RJ61BkKx+yYzIURt9s53P7xMVbv0uxYPzAXLBINGaFmkIKSuUPyBVfy8TMbvp93lvF4SPBNDzVRJfvgOw==} + engines: {node: '>= 10.0.0'} + cpu: [x64] + os: [win32] + + '@parcel/watcher@2.2.0': + resolution: {integrity: sha512-71S4TF+IMyAn24PK4KSkdKtqJDR3zRzb0HE3yXpacItqTM7XfF2f5q9NEGLEVl0dAaBAGfNwDCjH120y25F6Tg==} + engines: {node: '>= 10.0.0'} + + '@parcel/workers@2.8.3': + resolution: {integrity: sha512-+AxBnKgjqVpUHBcHLWIHcjYgKIvHIpZjN33mG5LG9XXvrZiqdWvouEzqEXlVLq5VzzVbKIQQcmsvRy138YErkg==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.8.3 + + '@parcel/workers@2.9.3': + resolution: {integrity: sha512-zRrDuZJzTevrrwElYosFztgldhqW6G9q5zOeQXfVQFkkEJCNfg36ixeiofKRU8uu2x+j+T6216mhMNB6HiuY+w==} + engines: {node: '>= 12.0.0'} + peerDependencies: + '@parcel/core': ^2.9.3 + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@plasmohq/consolidate@0.17.0': + resolution: {integrity: sha512-Na8imBnvzYPtzkK+9Uv9hPZ/oJti/0jgiQWD222SHxHw2QCVuR4KzslxXCy/rS8gGluSiTs1BGVvc3d2O6aJCA==} + engines: {node: '>= 0.10.0'} + peerDependencies: + arc-templates: ^0.5.3 + atpl: '>=0.7.6' + babel-core: ^6.26.3 + bracket-template: ^1.1.5 + coffeescript: ^2.7.0 + dot: ^1.1.3 + eco: ^1.1.0-rc-3 + ect: ^0.5.9 + ejs: ^3.1.5 + haml-coffee: ^1.14.1 + hamlet: ^0.3.3 + hamljs: ^0.6.2 + handlebars: ^4.7.6 + hogan.js: ^3.0.2 + htmling: ^0.0.8 + jazz: ^0.0.18 + jqtpl: ~1.1.0 + just: ^0.1.8 + liquid: ^5.1.1 + liquor: ^0.0.5 + lodash: ^4.17.20 + marko: ^3.14.4 + mote: ^0.2.0 + mustache: ^4.0.1 + nunjucks: ^3.2.2 + plates: ~0.4.11 + pug: ^3.0.0 + qejs: ^3.0.5 + ractive: ^1.3.12 + razor-tmpl: ^1.3.1 + react: ^18.2.0 + react-dom: ^18.2.0 + slm: ^2.0.0 + squirrelly: ^5.1.0 + teacup: ^2.0.0 + templayed: '>=0.2.3' + then-pug: '*' + tinyliquid: ^0.2.34 + toffee: ^0.3.6 + twig: ^1.15.2 + twing: ^5.0.2 + underscore: ^1.11.0 + vash: ^0.13.0 + velocityjs: ^2.0.1 + walrus: ^0.10.1 + whiskers: ^0.4.0 + peerDependenciesMeta: + arc-templates: + optional: true + atpl: + optional: true + babel-core: + optional: true + bracket-template: + optional: true + coffeescript: + optional: true + dot: + optional: true + eco: + optional: true + ect: + optional: true + ejs: + optional: true + haml-coffee: + optional: true + hamlet: + optional: true + hamljs: + optional: true + handlebars: + optional: true + hogan.js: + optional: true + htmling: + optional: true + jazz: + optional: true + jqtpl: + optional: true + just: + optional: true + liquid: + optional: true + liquor: + optional: true + lodash: + optional: true + marko: + optional: true + mote: + optional: true + mustache: + optional: true + nunjucks: + optional: true + plates: + optional: true + pug: + optional: true + qejs: + optional: true + ractive: + optional: true + razor-tmpl: + optional: true + react: + optional: true + react-dom: + optional: true + slm: + optional: true + squirrelly: + optional: true + teacup: + optional: true + templayed: + optional: true + then-pug: + optional: true + tinyliquid: + optional: true + toffee: + optional: true + twig: + optional: true + twing: + optional: true + underscore: + optional: true + vash: + optional: true + velocityjs: + optional: true + walrus: + optional: true + whiskers: + optional: true + + '@plasmohq/init@0.7.0': + resolution: {integrity: sha512-P75g48dqOGneJ+n0AcqnLE/TYflcaPc3B7h6EopnCBBYUDnCNBMwYmKAkaf5pnhsEB0ybPS6TU1C2DTGfqaW7A==} + + '@plasmohq/messaging@0.6.2': + resolution: {integrity: sha512-CGfcvfVE0wsN/Y/i/jV0nwjkwh2gBCEujZFhLoxJ12N0ScoP3JVEIvUxJSFsAD4ylBQ8IjD2FyjQozwiSxWc4Q==} + peerDependencies: + react: ^16.8.6 || ^17 || ^18 + peerDependenciesMeta: + react: + optional: true + + '@plasmohq/parcel-bundler@0.5.6': + resolution: {integrity: sha512-kjwj5tQUhdAK00AxXOzgqJ2jryZg4X6aleYAcjbREPzVMqKEu1NrNSNy5VGAfNRG6NCT6ZYg39ZCyuUOR2lEsQ==} + engines: {node: '>= 16.0.0', parcel: '>= 2.7.0'} + + '@plasmohq/parcel-compressor-utf8@0.1.1': + resolution: {integrity: sha512-9zcF39XIBzauYLERoGNVSy7qR1MzEqjhQn16RrlCpZ1AyNMlBJ3B28SmnUpBQNgne8JOHTtcx6cUVm1IvM3J+g==} + engines: {parcel: '>= 2.8.0'} + + '@plasmohq/parcel-config@0.41.1': + resolution: {integrity: sha512-peNpm+F1tVIZmDx8Mca8b3769cxc2IWS7q0+0Y4BLT1+2kis7X4b46IAYgOXtsDRZCb9pvxQhRhrVHpwGtdVLg==} + + '@plasmohq/parcel-core@0.1.10': + resolution: {integrity: sha512-XbJrqlgPNo+uQaukWayfRDZnAvdkYrmcydCOz0wfmuksTjDisyGkL3ZbWeX86QPN65CXFyou11/9h3+U9IsHfA==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-namer-manifest@0.3.12': + resolution: {integrity: sha512-mNyIVK4nRbjlnXXUygBcmV7xLzgS1HZ3vedxUrMQah0Wp0Y20GFcomToDBC0w9NXIZVSSKY0bRIeh0B6/verfQ==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-optimizer-encapsulate@0.0.8': + resolution: {integrity: sha512-iXDXoLtYBvV4rHhFw3O6nrS3dEWYe9k2m0i/Tvzg2lz4lUHFyvK5NN9RWqkOLfI8JviaqQzaaMKteJhLsX6z1A==} + engines: {parcel: '>= 2.8.0'} + + '@plasmohq/parcel-optimizer-es@0.4.1': + resolution: {integrity: sha512-2FvBq3L5wHyD+TNHpO0IVMJKX1XQ+uBruFVcRUgo+lDkIAyop7P8wpsY4iq3dOKXJrqjwBop9nzNcq0L/zaalQ==} + engines: {parcel: '>= 2.8.0'} + + '@plasmohq/parcel-packager@0.6.15': + resolution: {integrity: sha512-c6Afk5l8EqxyZ/N7p8avWEBt5teTQPQsvZZpPHWhsAY9eonX+h8bFdmXym1oevaq5TySJOpNCSUdTvqqZtlSnQ==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-resolver-post@0.4.5': + resolution: {integrity: sha512-Y5la9wruh3fMHlUoWtVBcbSyvg2xZE1kSRp5BAjtfyZlKS2cT/vIbFTUkqk9nPvXLExBDNajIxTKk9ag/9WzpA==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-resolver@0.14.1': + resolution: {integrity: sha512-1nmmMI7N5rtpni2TpUyPkI8XU1wIk/lTDGNZXLxtkzOoFiFP2sc2xZq4OGhmnRYvWphZYrnhMjRrjNJmqOFqxw==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-runtime@0.25.1': + resolution: {integrity: sha512-asr4DMXJSKPilye0uiyZf51NUC3WZAr0Y6mzl+mtRGIcywuv42+X52qnZl9a9xYkVZeYlVJq62Kfk4+wPthakg==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-transformer-inject-env@0.2.12': + resolution: {integrity: sha512-QhM5Je0LyuAAJMAXeBEu4YvDirIPXuO2SoxHkwTMIwCMXpstPinnKiElrIoolqZjcxLmDCfsXerXZfbN6NhSlA==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-transformer-inline-css@0.3.11': + resolution: {integrity: sha512-EUSwEowFNSgC/F1q/V4H4NXJ23wwLzlmRI6lvIr6S0mIuG/FCga+lAV3IZ+yAuXqUM2VexX6JyYYpNVidrMSxw==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-transformer-manifest@0.20.1': + resolution: {integrity: sha512-fA2d+u7eAURr8Vyi1HAB8zwndBW2czi5YcLgZRVwEqHODYYIyNcmqMJHLt7TAQYTD+POG+z4WpM81AKdhcq8mg==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-transformer-svelte@0.6.0': + resolution: {integrity: sha512-5lZW6NBtzhJaCyjpKaZF1/YzY9CF+kbfNknvASJB/Cf6uJPJlrgdxoWiVJ8IWMs3DyLgAnJXTdIU+uwjwXP1wg==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/parcel-transformer-vue@0.5.0': + resolution: {integrity: sha512-/3oVbajt+DRqtbM0RkKFtfyZR8DVjcsYpj1jHqPParGVBiXwgP0D/8Bj5p5/5Iqihs08gzasTcjKcwQKKdj0og==} + engines: {parcel: '>= 2.7.0'} + + '@plasmohq/storage@1.11.0': + resolution: {integrity: sha512-JLu8cCjn+yzbaIK2Gw+NDuDJgiTle3nj1HbMC/irnnUTc7lnJSiCEN/EsUcWcwNtv9BJM2r8DAk1stsQ8dCKug==} + peerDependencies: + react: ^16.8.6 || ^17 || ^18 + peerDependenciesMeta: + react: + optional: true + + '@pnpm/config.env-replace@1.1.0': + resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} + engines: {node: '>=12.22.0'} + + '@pnpm/network.ca-file@1.0.2': + resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} + engines: {node: '>=12.22.0'} + + '@pnpm/npm-conf@2.3.1': + resolution: {integrity: sha512-c83qWb22rNRuB0UaVCI0uRPNRr8Z0FWnEIvT47jiHAmOIUHbBOg5XvV7pM5x+rKn9HRpjxquDbXYSXr3fAKFcw==} + engines: {node: '>=12'} + + '@radix-ui/number@1.1.0': + resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + + '@radix-ui/primitive@1.1.0': + resolution: {integrity: sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==} + + '@radix-ui/primitive@1.1.1': + resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} + + '@radix-ui/react-accessible-icon@1.1.2': + resolution: {integrity: sha512-+rnMO0SEfzkcHr93RshkQVpOA26MtGOv4pcS9QUnLg4F8+GDmCJ8c2FEPhPz5e7arf31EzbTqJxFbzg3qen14g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-accordion@1.2.3': + resolution: {integrity: sha512-RIQ15mrcvqIkDARJeERSuXSry2N8uYnxkdDetpfmalT/+0ntOXLkFOsh9iwlAsCv+qcmhZjbdJogIm6WBa6c4A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-alert-dialog@1.1.6': + resolution: {integrity: sha512-p4XnPqgej8sZAAReCAKgz1REYZEBLR8hU9Pg27wFnCWIMc8g1ccCs0FjBcy05V15VTu8pAePw/VDYeOm/uZ6yQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.0': + resolution: {integrity: sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.2': + resolution: {integrity: sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-aspect-ratio@1.1.2': + resolution: {integrity: sha512-TaJxYoCpxJ7vfEkv2PTNox/6zzmpKXT6ewvCuf2tTOIVN45/Jahhlld29Yw4pciOXS2Xq91/rSGEdmEnUWZCqA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-avatar@1.1.3': + resolution: {integrity: sha512-Paen00T4P8L8gd9bNsRMw7Cbaz85oxiv+hzomsRZgFm2byltPFDtfcoqlWJ8GyZlIBWgLssJlzLCnKU0G0302g==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.1.4': + resolution: {integrity: sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.3': + resolution: {integrity: sha512-jFSerheto1X03MUC0g6R7LedNW9EEGWdg9W1+MlpkMLwGkgkbUXLPBH/KIuWKXUoeYRVY11llqbTBDzuLg7qrw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.0': + resolution: {integrity: sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.2': + resolution: {integrity: sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-compose-refs@1.1.0': + resolution: {integrity: sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-compose-refs@1.1.1': + resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context-menu@2.2.6': + resolution: {integrity: sha512-aUP99QZ3VU84NPsHeaFt4cQUNgJqFsLLOt/RbbWXszZ6MP0DpDyjkFZORr4RpAEx3sUBk+Kc8h13yGtC5Qw8dg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-context@1.1.0': + resolution: {integrity: sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-context@1.1.1': + resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.2': + resolution: {integrity: sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dialog@1.1.6': + resolution: {integrity: sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.0': + resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.1': + resolution: {integrity: sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.5': + resolution: {integrity: sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.6': + resolution: {integrity: sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.1': + resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.0': + resolution: {integrity: sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-scope@1.1.2': + resolution: {integrity: sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-hover-card@1.1.6': + resolution: {integrity: sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + + '@radix-ui/react-id@1.1.0': + resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.2': + resolution: {integrity: sha512-zo1uGMTaNlHehDyFQcDZXRJhUPDuukcnHz0/jnrup0JA6qL+AFpAnty+7VKa9esuU5xTblAZzTGYJKSKaBxBhw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menu@2.1.6': + resolution: {integrity: sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-navigation-menu@1.2.5': + resolution: {integrity: sha512-myMHHQUZ3ZLTi8W381/Vu43Ia0NqakkQZ2vzynMmTUtQQ9kNkjzhOwkZC9TAM5R07OZUVIQyHC06f/9JZJpvvA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.2': + resolution: {integrity: sha512-u2HRUyWW+lOiA2g0Le0tMmT55FGOEWHwPFt1EPfbLly7uXQExFo5duNKqG2DzmFXIdqOeNd+TpE8baHWJCyP9w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.6': + resolution: {integrity: sha512-NQouW0x4/GnkFJ/pRqsIS3rM/k97VzKnVb2jB7Gq7VEGPy5g7uNV1ykySFt7eWSp3i2uSGFwaJcvIRJBAHmmFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.0': + resolution: {integrity: sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.2': + resolution: {integrity: sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.2': + resolution: {integrity: sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.4': + resolution: {integrity: sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.1': + resolution: {integrity: sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.2': + resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.0': + resolution: {integrity: sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.0.2': + resolution: {integrity: sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-progress@1.1.2': + resolution: {integrity: sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-radio-group@1.2.3': + resolution: {integrity: sha512-xtCsqt8Rp09FK50ItqEqTJ7Sxanz8EM8dnkVIhJrc/wkMMomSmXHvYbhv3E7Zx4oXh98aaLt9W679SUYXg4IDA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.2': + resolution: {integrity: sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.3': + resolution: {integrity: sha512-l7+NNBfBYYJa9tNqVcP2AGvxdE3lmE6kFTBXdvHgUaZuy+4wGCL1Cl2AfaR7RKyimj7lZURGLwFO59k4eBnDJQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.1.6': + resolution: {integrity: sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.2': + resolution: {integrity: sha512-oZfHcaAp2Y6KFBX6I5P1u7CQoy4lheCGiYj+pGFrHy8E/VNRb5E39TkTr3JrV520csPBTZjkuKFdEsjS5EUNKQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slider@1.2.3': + resolution: {integrity: sha512-nNrLAWLjGESnhqBqcCNW4w2nn7LxudyMzeB6VgdyAnFLC6kfQgnAjSL2v6UkQTnDctJBlxrmxfplWS4iYjdUTw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-slot@1.1.0': + resolution: {integrity: sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-slot@1.1.2': + resolution: {integrity: sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.1.3': + resolution: {integrity: sha512-1nc+vjEOQkJVsJtWPSiISGT6OKm4SiOdjMo+/icLxo2G4vxz1GntC5MzfL4v8ey9OEfw787QCD1y3mUv0NiFEQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.3': + resolution: {integrity: sha512-9mFyI30cuRDImbmFF6O2KUJdgEOsGh9Vmx9x/Dh9tOhL7BngmQPQfwW4aejKm5OHpfWIdmeV6ySyuxoOGjtNng==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.2': + resolution: {integrity: sha512-Z6pqSzmAP/bFJoqMAston4eSNa+ud44NSZTiZUmUen+IOZ5nBY8kzuU5WDBVyFXPtcW6yUalOHsxM/BP6Sv8ww==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.6': + resolution: {integrity: sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle-group@1.1.2': + resolution: {integrity: sha512-JBm6s6aVG/nwuY5eadhU2zDi/IwYS0sDM5ZWb4nymv/hn3hZdkw+gENn0LP4iY1yCd7+bgJaCwueMYJIU3vk4A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toggle@1.1.2': + resolution: {integrity: sha512-lntKchNWx3aCHuWKiDY+8WudiegQvBpDRAYL8dKLRvKEH8VOpl0XX6SSU/bUBqIRJbcTy4+MW06Wv8vgp10rzQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toolbar@1.1.2': + resolution: {integrity: sha512-wT20eQ7ScFk+kBMDmHp+lMk18cgxhu35b2Bn5deUcPxiVwfn5vuZgi7NGcHu8ocdkinahmp4FaSZysKDyRVPWQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.1.8': + resolution: {integrity: sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.0': + resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.1.0': + resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.0': + resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.0': + resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.0': + resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.0': + resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.0': + resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.1.0': + resolution: {integrity: sha512-N8MDZqtgCgG5S3aV60INAB475osJousYpZ4cTJ2cFbMpdHS5Y6loLTH8LPtkj2QN0x93J30HT/M3qJXM0+lyeQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-visually-hidden@1.1.2': + resolution: {integrity: sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.0': + resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} + + '@remix-run/router@1.19.1': + resolution: {integrity: sha512-S45oynt/WH19bHbIXjtli6QmwNYvaz+vtnubvNpNDvUOoA/OWh6j1OikIP3G+v5GHdxyC6EXoChG3HgYGEUfcg==} + engines: {node: '>=14.0.0'} + + '@sindresorhus/is@5.6.0': + resolution: {integrity: sha512-TV7t8GKYaJWsn00tFDqBw8+Uqmr8A0fRU1tvTQhyZzGv0sJCGRQL3JGMI3ucuKo3XIZdUP+Lx7/gh2t3lewy7g==} + engines: {node: '>=14.16'} + + '@svgr/babel-plugin-add-jsx-attribute@6.5.1': + resolution: {integrity: sha512-9PYGcXrAxitycIjRmZB+Q0JaN07GZIWaTBIGQzfaZv+qr1n8X1XUEJ5rZ/vx6OVD9RRYlrNnXWExQXcmZeD/BQ==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0': + resolution: {integrity: sha512-BcCkm/STipKvbCl6b7QFrMh/vx00vIP63k2eM66MfHJzPr6O2U0jYEViXkHJWqXqQYjdeA9cuCl5KWmlwjDvbA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0': + resolution: {integrity: sha512-5BcGCBfBxB5+XSDSWnhTThfI9jcO5f0Ai2V24gZpG+wXF14BzwxxdDb4g6trdOux0rhibGs385BeFMSmxtS3uA==} + engines: {node: '>=14'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1': + resolution: {integrity: sha512-8DPaVVE3fd5JKuIC29dqyMB54sA6mfgki2H2+swh+zNJoynC8pMPzOkidqHOSc6Wj032fhl8Z0TVn1GiPpAiJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-dynamic-title@6.5.1': + resolution: {integrity: sha512-FwOEi0Il72iAzlkaHrlemVurgSQRDFbk0OC8dSvD5fSBPHltNh7JtLsxmZUhjYBZo2PpcU/RJvvi6Q0l7O7ogw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-svg-em-dimensions@6.5.1': + resolution: {integrity: sha512-gWGsiwjb4tw+ITOJ86ndY/DZZ6cuXMNE/SjcDRg+HLuCmwpcjOktwRF9WgAiycTqJD/QXqL2f8IzE2Rzh7aVXA==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-react-native-svg@6.5.1': + resolution: {integrity: sha512-2jT3nTayyYP7kI6aGutkyfJ7UMGtuguD72OjeGLwVNyfPRBD8zQthlvL+fAbAKk5n9ZNcvFkp/b1lZ7VsYqVJg==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-plugin-transform-svg-component@6.5.1': + resolution: {integrity: sha512-a1p6LF5Jt33O3rZoVRBqdxL350oge54iZWHNI6LJB5tQ7EelvD/Mb1mfBiZNAan0dt4i3VArkFRjA4iObuNykQ==} + engines: {node: '>=12'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/babel-preset@6.5.1': + resolution: {integrity: sha512-6127fvO/FF2oi5EzSQOAjo1LE3OtNVh11R+/8FXa+mHx1ptAaS4cknIjnUA7e6j6fwGGJ17NzaTJFUwOV2zwCw==} + engines: {node: '>=10'} + peerDependencies: + '@babel/core': ^7.0.0-0 + + '@svgr/core@6.5.1': + resolution: {integrity: sha512-/xdLSWxK5QkqG524ONSjvg3V/FkNyCv538OIBdQqPNaAta3AsXj/Bd2FbvR87yMbXO2hFSWiAe/Q6IkVPDw+mw==} + engines: {node: '>=10'} + + '@svgr/hast-util-to-babel-ast@6.5.1': + resolution: {integrity: sha512-1hnUxxjd83EAxbL4a0JDJoD3Dao3hmjvyvyEV8PzWmLK3B9m9NPlW7GKjFyoWE8nM7HnXzPcmmSyOW8yOddSXw==} + engines: {node: '>=10'} + + '@svgr/plugin-jsx@6.5.1': + resolution: {integrity: sha512-+UdQxI3jgtSjCykNSlEMuy1jSRQlGC7pqBCPvkG/2dATdWo082zHTTK3uhnAju2/6XpE6B5mZ3z4Z8Ns01S8Gw==} + engines: {node: '>=10'} + peerDependencies: + '@svgr/core': ^6.0.0 + + '@svgr/plugin-svgo@6.5.1': + resolution: {integrity: sha512-omvZKf8ixP9z6GWgwbtmP9qQMPX4ODXi+wzbVZgomNFsUIlHA1sf4fThdwTWSsZGgvGAG6yE+b/F5gWUkcZ/iQ==} + engines: {node: '>=10'} + peerDependencies: + '@svgr/core': '*' + + '@swc/core-darwin-arm64@1.3.96': + resolution: {integrity: sha512-8hzgXYVd85hfPh6mJ9yrG26rhgzCmcLO0h1TIl8U31hwmTbfZLzRitFQ/kqMJNbIBCwmNH1RU2QcJnL3d7f69A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-arm64@1.7.14': + resolution: {integrity: sha512-V0OUXjOH+hdGxDYG8NkQzy25mKOpcNKFpqtZEzLe5V/CpLJPnpg1+pMz70m14s9ZFda9OxsjlvPbg1FLUwhgIQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [darwin] + + '@swc/core-darwin-x64@1.3.96': + resolution: {integrity: sha512-mFp9GFfuPg+43vlAdQZl0WZpZSE8sEzqL7sr/7Reul5McUHP0BaLsEzwjvD035ESfkY8GBZdLpMinblIbFNljQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-darwin-x64@1.7.14': + resolution: {integrity: sha512-9iFvUnxG6FC3An5ogp5jbBfQuUmTTwy8KMB+ZddUoPB3NR1eV+Y9vOh/tfWcenSJbgOKDLgYC5D/b1mHAprsrQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [darwin] + + '@swc/core-linux-arm-gnueabihf@1.3.96': + resolution: {integrity: sha512-8UEKkYJP4c8YzYIY/LlbSo8z5Obj4hqcv/fUTHiEePiGsOddgGf7AWjh56u7IoN/0uEmEro59nc1ChFXqXSGyg==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm-gnueabihf@1.7.14': + resolution: {integrity: sha512-zGJsef9qPivKSH8Vv4F/HiBXBTHZ5Hs3ZjVGo/UIdWPJF8fTL9OVADiRrl34Q7zOZEtGXRwEKLUW1SCQcbDvZA==} + engines: {node: '>=10'} + cpu: [arm] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.3.96': + resolution: {integrity: sha512-c/IiJ0s1y3Ymm2BTpyC/xr6gOvoqAVETrivVXHq68xgNms95luSpbYQ28rqaZC8bQC8M5zdXpSc0T8DJu8RJGw==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-gnu@1.7.14': + resolution: {integrity: sha512-AxV3MPsoI7i4B8FXOew3dx3N8y00YoJYvIPfxelw07RegeCEH3aHp2U2DtgbP/NV1ugZMx0TL2Z2DEvocmA51g==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.3.96': + resolution: {integrity: sha512-i5/UTUwmJLri7zhtF6SAo/4QDQJDH2fhYJaBIUhrICmIkRO/ltURmpejqxsM/ye9Jqv5zG7VszMC0v/GYn/7BQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-arm64-musl@1.7.14': + resolution: {integrity: sha512-JDLdNjUj3zPehd4+DrQD8Ltb3B5lD8D05IwePyDWw+uR/YPc7w/TX1FUVci5h3giJnlMCJRvi1IQYV7K1n7KtQ==} + engines: {node: '>=10'} + cpu: [arm64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.3.96': + resolution: {integrity: sha512-USdaZu8lTIkm4Yf9cogct/j5eqtdZqTgcTib4I+NloUW0E/hySou3eSyp3V2UAA1qyuC72ld1otXuyKBna0YKQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-gnu@1.7.14': + resolution: {integrity: sha512-Siy5OvPCLLWmMdx4msnEs8HvEVUEigSn0+3pbLjv78iwzXd0qSBNHUPZyC1xeurVaUbpNDxZTpPRIwpqNE2+Og==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.3.96': + resolution: {integrity: sha512-QYErutd+G2SNaCinUVobfL7jWWjGTI0QEoQ6hqTp7PxCJS/dmKmj3C5ZkvxRYcq7XcZt7ovrYCTwPTHzt6lZBg==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-linux-x64-musl@1.7.14': + resolution: {integrity: sha512-FtEGm9mwtRYQNK43WMtUIadxHs/ja2rnDurB99os0ZoFTGG2IHuht2zD97W0wB8JbqEabT1XwSG9Y5wmN+ciEQ==} + engines: {node: '>=10'} + cpu: [x64] + os: [linux] + + '@swc/core-win32-arm64-msvc@1.3.96': + resolution: {integrity: sha512-hjGvvAduA3Un2cZ9iNP4xvTXOO4jL3G9iakhFsgVhpkU73SGmK7+LN8ZVBEu4oq2SUcHO6caWvnZ881cxGuSpg==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-arm64-msvc@1.7.14': + resolution: {integrity: sha512-Jp8KDlfq7Ntt2/BXr0y344cYgB1zf0DaLzDZ1ZJR6rYlAzWYSccLYcxHa97VGnsYhhPspMpmCvHid97oe2hl4A==} + engines: {node: '>=10'} + cpu: [arm64] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.3.96': + resolution: {integrity: sha512-Far2hVFiwr+7VPCM2GxSmbh3ikTpM3pDombE+d69hkedvYHYZxtTF+2LTKl/sXtpbUnsoq7yV/32c9R/xaaWfw==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-ia32-msvc@1.7.14': + resolution: {integrity: sha512-I+cFsXF0OU0J9J4zdWiQKKLURO5dvCujH9Jr8N0cErdy54l9d4gfIxdctfTF+7FyXtWKLTCkp+oby9BQhkFGWA==} + engines: {node: '>=10'} + cpu: [ia32] + os: [win32] + + '@swc/core-win32-x64-msvc@1.3.96': + resolution: {integrity: sha512-4VbSAniIu0ikLf5mBX81FsljnfqjoVGleEkCQv4+zRlyZtO3FHoDPkeLVoy6WRlj7tyrRcfUJ4mDdPkbfTO14g==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core-win32-x64-msvc@1.7.14': + resolution: {integrity: sha512-NNrprQCK6d28mG436jVo2TD+vACHseUECacEBGZ9Ef0qfOIWS1XIt2MisQKG0Oea2VvLFl6tF/V4Lnx/H0Sn3Q==} + engines: {node: '>=10'} + cpu: [x64] + os: [win32] + + '@swc/core@1.3.96': + resolution: {integrity: sha512-zwE3TLgoZwJfQygdv2SdCK9mRLYluwDOM53I+dT6Z5ZvrgVENmY3txvWDvduzkV+/8IuvrRbVezMpxcojadRdQ==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': ^0.5.0 + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/core@1.7.14': + resolution: {integrity: sha512-9aeXeifnyuvc2pcuuhPQgVUwdpGEzZ+9nJu0W8/hNl/aESFsJGR5i9uQJRGu0atoNr01gK092fvmqMmQAPcKow==} + engines: {node: '>=10'} + peerDependencies: + '@swc/helpers': '*' + peerDependenciesMeta: + '@swc/helpers': + optional: true + + '@swc/counter@0.1.3': + resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==} + + '@swc/helpers@0.5.12': + resolution: {integrity: sha512-KMZNXiGibsW9kvZAO1Pam2JPTDBm+KSHMMHWdsyI/1DbIZjT2A6Gy3hblVXUMEDvUAKq+e0vL0X0o54owWji7g==} + + '@swc/types@0.1.12': + resolution: {integrity: sha512-wBJA+SdtkbFhHjTMYH+dEH1y4VpfGdAc2Kw/LK09i9bXd/K6j6PkDcFCEzb6iVfZMkPRrl/q0e3toqTAJdkIVA==} + + '@szmarczak/http-timer@5.0.1': + resolution: {integrity: sha512-+PmQX0PiAYPMeVYe237LJAYvOMYW1j2rH5YROyS3b4CTVJum34HfRvKvAzozHAQG0TnHNdUfY9nCeUyRAs//cw==} + engines: {node: '>=14.16'} + + '@trysound/sax@0.2.0': + resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==} + engines: {node: '>=10.13.0'} + + '@types/chrome@0.0.258': + resolution: {integrity: sha512-vicJi6cg2zaFuLmLY7laG6PHBknjKFusPYlaKQ9Zlycskofy71rStlGvW07MUuqUIVorZf8k5KH+zeTTGcH2dQ==} + + '@types/estree@1.0.5': + resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} + + '@types/filesystem@0.0.36': + resolution: {integrity: sha512-vPDXOZuannb9FZdxgHnqSwAG/jvdGM8Wq+6N4D/d80z+D4HWH+bItqsZaVRQykAn6WEVeEkLm2oQigyHtgb0RA==} + + '@types/filewriter@0.0.33': + resolution: {integrity: sha512-xFU8ZXTw4gd358lb2jw25nxY9QAgqn2+bKKjKOYfNCzN4DKCFetK7sPtrlpg66Ywe3vWY9FNxprZawAh9wfJ3g==} + + '@types/har-format@1.2.15': + resolution: {integrity: sha512-RpQH4rXLuvTXKR0zqHq3go0RVXYv/YVqv4TnPH95VbwUxZdQlK1EtcMvQvMpDngHbt13Csh9Z4qT9AbkiQH5BA==} + + '@types/http-cache-semantics@4.0.4': + resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/node@20.11.5': + resolution: {integrity: sha512-g557vgQjUUfN76MZAN/dt1z3dzcUsimuysco0KeluHgrPdJXkP/XdAURgyO2W9fZWHRtRBiVKzKn8vyOAwlG+w==} + + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + + '@types/prop-types@15.7.12': + resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} + + '@types/react-dom@18.2.18': + resolution: {integrity: sha512-TJxDm6OfAX2KJWJdMEVTwWke5Sc/E/RlnPGvGfS0W7+6ocy2xhDVQVh/KvC2Uf7kACs+gDytdusDSdWfWkaNzw==} + + '@types/react@18.2.48': + resolution: {integrity: sha512-qboRCl6Ie70DQQG9hhNREz81jqC1cs9EVNcjQ1AU+jH6NFfSAhVVbrrY/+nSF+Bsk4AOwm9Qa61InvMCyV+H3w==} + + '@types/scheduler@0.23.0': + resolution: {integrity: sha512-YIoDCTH3Af6XM5VuwGG/QL/CJqga1Zm3NkU3HZ4ZHK2fRMPYP1VczsTUqtsf43PH/iJNVlPHAo2oWX7BSdB2Hw==} + + '@types/trusted-types@2.0.7': + resolution: {integrity: sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==} + + '@ungap/event-target@0.2.4': + resolution: {integrity: sha512-u9Fd3k2qfMtn+0dxbCn/y0pzQ9Ucw6lWR984CrHcbxc+WzcMkJE4VjWHWSb9At40MjwMyHCkJNXroS55Osshhw==} + + '@ungap/event@0.2.2': + resolution: {integrity: sha512-31PwUE7asaFeXdRatnlsNYyfmO8xSEhsRAP+v7lm77hnn/oOjlpt7pJgc7C76LGlZjiEH9nGSx9vTc/5MZ6W7A==} + + '@vue/compiler-core@3.3.4': + resolution: {integrity: sha512-cquyDNvZ6jTbf/+x+AgM2Arrp6G4Dzbb0R64jiG804HRMfRiFXWI6kqUVqZ6ZR0bQhIoQjB4+2bhNtVwndW15g==} + + '@vue/compiler-dom@3.3.4': + resolution: {integrity: sha512-wyM+OjOVpuUukIq6p5+nwHYtj9cFroz9cwkfmP9O1nzH68BenTTv0u7/ndggT8cIQlnBeOo6sUT/gvHcIkLA5w==} + + '@vue/compiler-sfc@3.3.4': + resolution: {integrity: sha512-6y/d8uw+5TkCuzBkgLS0v3lSM3hJDntFEiUORM11pQ/hKvkhSKZrXW6i69UyXlJQisJxuUEJKAWEqWbWsLeNKQ==} + + '@vue/compiler-ssr@3.3.4': + resolution: {integrity: sha512-m0v6oKpup2nMSehwA6Uuu+j+wEwcy7QmwMkVNVfrV9P2qE5KshC6RwOCq8fjGS/Eak/uNb8AaWekfiXxbBB6gQ==} + + '@vue/reactivity-transform@3.3.4': + resolution: {integrity: sha512-MXgwjako4nu5WFLAjpBnCj/ieqcjE2aJBINUNQzkZQfzIZA4xn+0fV1tIYBJvvva3N3OvKGofRLvQIwEQPpaXw==} + + '@vue/reactivity@3.3.4': + resolution: {integrity: sha512-kLTDLwd0B1jG08NBF3R5rqULtv/f8x3rOFByTDz4J53ttIQEDmALqKqXY0J+XQeN0aV2FBxY8nJDf88yvOPAqQ==} + + '@vue/runtime-core@3.3.4': + resolution: {integrity: sha512-R+bqxMN6pWO7zGI4OMlmvePOdP2c93GsHFM/siJI7O2nxFRzj55pLwkpCedEY+bTMgp5miZ8CxfIZo3S+gFqvA==} + + '@vue/runtime-dom@3.3.4': + resolution: {integrity: sha512-Aj5bTJ3u5sFsUckRghsNjVTtxZQ1OyMWCr5dZRAPijF/0Vy4xEoRCwLyHXcj4D0UFbJ4lbx3gPTgg06K/GnPnQ==} + + '@vue/server-renderer@3.3.4': + resolution: {integrity: sha512-Q6jDDzR23ViIb67v+vM1Dqntu+HUexQcsWKhhQa4ARVzxOY2HbC7QRW/ggkDBd5BU+uM1sV6XOAP0b216o34JQ==} + peerDependencies: + vue: 3.3.4 + + '@vue/shared@3.3.4': + resolution: {integrity: sha512-7OjdcV8vQ74eiz1TZLzZP4JwqM5fA94K6yntPS5Z25r9HDuGNzaGdgvwKYq6S+MxwF0TFRwe50fIR/MYnakdkQ==} + + abortcontroller-polyfill@1.7.5: + resolution: {integrity: sha512-JMJ5soJWP18htbbxJjG7bG6yuI6pRhgJ0scHHTfkUjf6wjP912xZWvM+A4sJK3gqd9E8fcPbDnOefbA9Th/FIQ==} + + acorn@8.12.1: + resolution: {integrity: sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==} + engines: {node: '>=0.4.0'} + hasBin: true + + ansi-escapes@4.3.2: + resolution: {integrity: sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==} + engines: {node: '>=8'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.0.1: + resolution: {integrity: sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + arg@5.0.2: + resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + aria-hidden@1.2.4: + resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + engines: {node: '>=10'} + + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + + array-union@2.1.0: + resolution: {integrity: sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==} + engines: {node: '>=8'} + + autoprefixer@10.4.20: + resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + engines: {node: ^10 || ^12 || >=14} + hasBin: true + peerDependencies: + postcss: ^8.1.0 + + axobject-query@3.2.4: + resolution: {integrity: sha512-aPTElBrbifBU1krmZxGZOlBkslORe7Ll7+BDnI50Wy4LgOt69luMgevkDfTq1O/ZgprooPCtWpjCwKSZw/iZ4A==} + engines: {node: '>= 0.4'} + + b4a@1.6.6: + resolution: {integrity: sha512-5Tk1HLk6b6ctmjIkAcU/Ujv/1WqiDl0F0JdRCR80VsOcUlHcu7pWeWRlOqQLHfDEsVx9YH/aif5AG4ehoCtTmg==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + bare-events@2.4.2: + resolution: {integrity: sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==} + + bare-fs@2.3.1: + resolution: {integrity: sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==} + + bare-os@2.4.0: + resolution: {integrity: sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==} + + bare-path@2.1.3: + resolution: {integrity: sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==} + + bare-stream@2.1.3: + resolution: {integrity: sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==} + + base-x@3.0.10: + resolution: {integrity: sha512-7d0s06rR9rYaIWHkpfLIFICM/tkSVdoPC9qYAQRpxn9DdKNWNsKC0uk++akckyLq16Tx2WIinnZ6WRriAt6njQ==} + + base64-js@1.5.1: + resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + bl@4.1.0: + resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} + + bluebird@3.7.2: + resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==} + + boolbase@1.0.0: + resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} + + brace-expansion@2.0.1: + resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browserslist@4.22.1: + resolution: {integrity: sha512-FEVc202+2iuClEhZhrWy6ZiAcRLvNMyYcxZ8raemul1DYVOVdFsbqckWLdsixQZCpJlwe77Z3UTalE7jsjnKfQ==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + browserslist@4.23.3: + resolution: {integrity: sha512-btwCFJVjI4YWDNfau8RhZ+B1Q/VLoUITrm3RlP6y1tYGWIOa+InuYiRGXUBXo8nA1qKmHMyLB/iVQg5TT4eFoA==} + engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} + hasBin: true + + buffer@5.7.1: + resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} + + buffer@6.0.3: + resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==} + + bundle-require@4.2.1: + resolution: {integrity: sha512-7Q/6vkyYAwOmQNRw75x+4yRtZCZJXUDmHHlFdkiV0wgv/reNjtJwpu1jPJ0w2kbEpIM0uoKI3S4/f39dU7AjSA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.17' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + cacheable-lookup@7.0.0: + resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} + engines: {node: '>=14.16'} + + cacheable-request@10.2.14: + resolution: {integrity: sha512-zkDT5WAF4hSSoUgyfg5tFIxz8XQK+25W/TLVojJTMKBaxevLBBtLxgqguAuVQB8PVW79FVjHcU+GJ9tVbDZ9mQ==} + engines: {node: '>=14.16'} + + call-bind@1.0.7: + resolution: {integrity: sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase-css@2.0.1: + resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} + engines: {node: '>= 6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + caniuse-lite@1.0.30001651: + resolution: {integrity: sha512-9Cf+Xv1jJNe1xPZLGuUXLNkE1BoDkqRqYyFJ9TDYSqhduqA4hu4oR9HluGoWYQC/aj8WHjsGVV+bwkh0+tegRg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.3.0: + resolution: {integrity: sha512-dLitG79d+GV1Nb/VYcCDFivJeK1hiukt9QjRNVOsUtTy1rR1YJsmpGGTZ3qJos+uw7WmWF4wUwBd9jxjocFC2w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + change-case@5.1.2: + resolution: {integrity: sha512-CAtbGEDulyjzs05RXy3uKcwqeztz/dMEuAc1Xu9NQBsbrhuGMneL0u9Dj5SoutLKBFYun8txxYIwhjtLNfUmCA==} + + chardet@0.7.0: + resolution: {integrity: sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + chownr@1.1.4: + resolution: {integrity: sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==} + + chrome-trace-event@1.0.4: + resolution: {integrity: sha512-rNjApaLzuwaOTjCiT8lSDdGN1APCiqkChLMJxJPWLunPAt5fy8xgU9/jNOchV84wfIxrA0lRQB7oCT8jrn/wrQ==} + engines: {node: '>=6.0'} + + class-variance-authority@0.7.0: + resolution: {integrity: sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A==} + + cli-cursor@3.1.0: + resolution: {integrity: sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==} + engines: {node: '>=8'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cli-width@4.1.0: + resolution: {integrity: sha512-ouuZd4/dm2Sw5Gmqy6bGyNNNe1qt9RpmxveLSO7KcgsTnU7RXfsw+/bukWGo1abgBiMAic068rclZsO4IWmmxQ==} + engines: {node: '>= 12'} + + clone@1.0.4: + resolution: {integrity: sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==} + engines: {node: '>=0.8'} + + clone@2.1.2: + resolution: {integrity: sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==} + engines: {node: '>=0.8'} + + clsx@2.0.0: + resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==} + engines: {node: '>=6'} + + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + + cmdk@1.0.3: + resolution: {integrity: sha512-2c3uTjwT4YeHj60q2k8S1B0WHSoGR6t5CPnec6PMFD2QF4gwid0t1VSPNeEmL02EwBwNky/A3gwPCOViKTtoPA==} + peerDependencies: + react: ^18 || ^19 || ^19.0.0-rc + react-dom: ^18 || ^19 || ^19.0.0-rc + + code-red@1.0.4: + resolution: {integrity: sha512-7qJWqItLA8/VPVlKJlFXU+NBlo/qyfs39aJcuMT/2ere32ZqvF5OSxgdM5xOfJJ7O429gg2HM47y8v9P+9wrNw==} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + color-string@1.9.1: + resolution: {integrity: sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==} + + color@4.2.3: + resolution: {integrity: sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==} + engines: {node: '>=12.5.0'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + + config-chain@1.1.13: + resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} + + content-security-policy-parser@0.4.1: + resolution: {integrity: sha512-NNJS8XPnx3OKr/CUOSwDSJw+lWTrZMYnclLKj0Y9CYOfJNJTWLFGPg3u2hYgbXMXKVRkZR2fbyReNQ1mUff/Qg==} + engines: {node: '>=8.0.0'} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + copy-anything@2.0.6: + resolution: {integrity: sha512-1j20GZTsvKNkc4BY3NpMOM8tt///wY3FpIzozTOFO2ffuZcV61nojHXVKIy3WM+7ADCy5FVhdZYHYDdgTU0yJw==} + + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + + cosmiconfig@9.0.0: + resolution: {integrity: sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==} + engines: {node: '>=14'} + peerDependencies: + typescript: '>=4.9.5' + peerDependenciesMeta: + typescript: + optional: true + + cross-spawn@7.0.3: + resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} + engines: {node: '>= 8'} + + crypto-random-string@4.0.0: + resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} + engines: {node: '>=12'} + + css-select@3.1.2: + resolution: {integrity: sha512-qmss1EihSuBNWNNhHjxzxSfJoFBM/lERB/Q4EnsJQQC62R2evJDW481091oAdOr9uh46/0n4nrg0It5cAnj1RA==} + + css-select@4.3.0: + resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} + + css-tree@1.1.3: + resolution: {integrity: sha512-tRpdppF7TRazZrjJ6v3stzv93qxRcSsFmW6cX0Zm2NVKpxE1WV1HblnghVv9TreireHkqI/VDEsfolRF1p6y7Q==} + engines: {node: '>=8.0.0'} + + css-tree@2.3.1: + resolution: {integrity: sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==} + engines: {node: ^10 || ^12.20.0 || ^14.13.0 || >=15.0.0} + + css-what@4.0.0: + resolution: {integrity: sha512-teijzG7kwYfNVsUh2H/YN62xW3KK9YhXEgSlbxMlcyjPNvdKJqFx5lrwlJgoFP1ZHlB89iGDlo/JyshKeRhv5A==} + engines: {node: '>= 6'} + + css-what@6.1.0: + resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} + engines: {node: '>= 6'} + + cssesc@3.0.0: + resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} + engines: {node: '>=4'} + hasBin: true + + csso@4.2.0: + resolution: {integrity: sha512-wvlcdIbf6pwKEk7vHj8/Bkc0B4ylXZruLvOgs9doS5eOsOpuodOV2zJChSpkp+pRpYQLQMeF04nr3Z68Sta9jA==} + engines: {node: '>=8.0.0'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + debug@4.3.6: + resolution: {integrity: sha512-O/09Bd4Z1fBrU4VzkhFqVgpPzaGbw6Sm9FEkBT1A/YBXQFGuuSxa1dN2nxgxS34JmKXqYx8CZAwEVoJFImUXIg==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decompress-response@6.0.0: + resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} + engines: {node: '>=10'} + + deep-extend@0.6.0: + resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} + engines: {node: '>=4.0.0'} + + deepmerge@4.3.1: + resolution: {integrity: sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==} + engines: {node: '>=0.10.0'} + + defaults@1.0.4: + resolution: {integrity: sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==} + + defer-to-connect@2.0.1: + resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} + engines: {node: '>=10'} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + detect-libc@1.0.3: + resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} + engines: {node: '>=0.10'} + hasBin: true + + detect-libc@2.0.3: + resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + engines: {node: '>=8'} + + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + + didyoumean@1.2.2: + resolution: {integrity: sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==} + + dir-glob@3.0.1: + resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} + engines: {node: '>=8'} + + dlv@1.1.3: + resolution: {integrity: sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==} + + dom-serializer@1.4.1: + resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} + + dom-to-semantic-markdown@1.2.11: + resolution: {integrity: sha512-VDDrQjBO2pNlgcSyuK5NPMBBMCChbV4bjstok8MfT41lzD87uqjoIhTXbA0kxN2id1Dou4iRg8tadnudCVY5oQ==} + + domelementtype@2.3.0: + resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} + + domhandler@4.3.1: + resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} + engines: {node: '>= 4'} + + domutils@2.8.0: + resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} + + dotenv-expand@10.0.0: + resolution: {integrity: sha512-GopVGCpVS1UKH75VKHGuQFqS1Gusej0z4FyQkPdwjil2gNIv+LNsqBlboOzpJFZKVT95GkCyWJbBSdFEFUWI2A==} + engines: {node: '>=12'} + + dotenv-expand@5.1.0: + resolution: {integrity: sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==} + + dotenv@16.3.1: + resolution: {integrity: sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==} + engines: {node: '>=12'} + + dotenv@7.0.0: + resolution: {integrity: sha512-M3NhsLbV1i6HuGzBUH8vXrtxOk+tWmzWKDMbAVSUp3Zsjm7ywFeuwrUXhmhQyRK1q5B5GGy7hcXPbj3bnfZg2g==} + engines: {node: '>=6'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + electron-to-chromium@1.5.13: + resolution: {integrity: sha512-lbBcvtIJ4J6sS4tb5TLp1b4LyfCdMkwStzXPyAgVgTRAsep4bvrAGaBOP7ZJtQMNJpSQ9SqG4brWOroNaQtm7Q==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + end-of-stream@1.4.4: + resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} + + entities@2.2.0: + resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} + + entities@3.0.1: + resolution: {integrity: sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==} + engines: {node: '>=0.12'} + + entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + + env-paths@2.2.1: + resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} + engines: {node: '>=6'} + + errno@0.1.8: + resolution: {integrity: sha512-dJ6oBr5SQ1VSd9qkk7ByRgb/1SH4JZjCHSW/mr63/QcXO9zLVxvJ6Oy13nio03rxpSnVDDjFor75SjVeZWPW/A==} + hasBin: true + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-define-property@1.0.0: + resolution: {integrity: sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + + escalade@3.1.2: + resolution: {integrity: sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + estree-walker@2.0.2: + resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + + execa@5.1.1: + resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} + engines: {node: '>=10'} + + expand-template@2.0.3: + resolution: {integrity: sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==} + engines: {node: '>=6'} + + external-editor@3.1.0: + resolution: {integrity: sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==} + engines: {node: '>=4'} + + fast-fifo@1.3.2: + resolution: {integrity: sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==} + + fast-glob@3.3.2: + resolution: {integrity: sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==} + engines: {node: '>=8.6.0'} + + fastq@1.17.1: + resolution: {integrity: sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==} + + fflate@0.8.1: + resolution: {integrity: sha512-/exOvEuc+/iaUm105QIiOt4LpBdMTWsXxqR0HDF35vx3fmaKzw7354gTilCh5rkzEt8WYyG//ku3h3nRmd7CHQ==} + + figures@5.0.0: + resolution: {integrity: sha512-ej8ksPF4x6e5wvK9yevct0UCXh8TTFlWGVLlgjZuoBH1HwjIfKE/IdL5mq89sFA7zELi1VhKpmtDnrs7zWyeyg==} + engines: {node: '>=14'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + foreground-child@3.3.0: + resolution: {integrity: sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==} + engines: {node: '>=14'} + + form-data-encoder@2.1.4: + resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} + engines: {node: '>= 14.17'} + + fraction.js@4.3.7: + resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + + fs-constants@1.0.0: + resolution: {integrity: sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==} + + fs-extra@11.1.1: + resolution: {integrity: sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ==} + engines: {node: '>=14.14'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + gensync@1.0.0-beta.2: + resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} + engines: {node: '>=6.9.0'} + + get-intrinsic@1.2.4: + resolution: {integrity: sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==} + engines: {node: '>= 0.4'} + + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + + get-port@7.0.0: + resolution: {integrity: sha512-mDHFgApoQd+azgMdwylJrv2DX47ywGq1i5VFJE7fZ0dttNq3iQMfsU4IvEgBHojA3KqEudyu7Vq+oN8kNaNkWw==} + engines: {node: '>=16'} + + get-stream@6.0.1: + resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} + engines: {node: '>=10'} + + github-from-package@0.0.0: + resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + globals@11.12.0: + resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} + engines: {node: '>=4'} + + globals@13.24.0: + resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} + engines: {node: '>=8'} + + globby@11.1.0: + resolution: {integrity: sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==} + engines: {node: '>=10'} + + gopd@1.0.1: + resolution: {integrity: sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==} + + got@12.6.1: + resolution: {integrity: sha512-mThBblvlAF1d4O5oqyvN+ZxLAYwIJK7bpMxgYqPD9okW0C3qm5FFn7k811QrcuEBwaogR3ngOFoCfs6mRv7teQ==} + engines: {node: '>=14.16'} + + got@13.0.0: + resolution: {integrity: sha512-XfBk1CxOOScDcMr9O1yKkNaQyy865NbYs+F7dr4H0LZMVgCj2Le59k6PqbNHoL5ToeaEQUYh6c6yMfVcc6SJxA==} + engines: {node: '>=16'} + + graceful-fs@4.2.10: + resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphql-import-macro@1.0.0: + resolution: {integrity: sha512-YK4g6iP60H++MpP93tb0VwOg7aM5iIC0hdSQKTrEDANeLWf0KFAT9dwlBeMDrhY+jcW7qsAEDtaw58cgVnQXAw==} + + graphql@15.9.0: + resolution: {integrity: sha512-GCOQdvm7XxV1S4U4CGrsdlEN37245eC8P9zaYCMr6K1BG0IPGy5lUwmJsEOGyl1GD6HXjOtl2keCP9asRBwNvA==} + engines: {node: '>= 10.x'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.0.3: + resolution: {integrity: sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==} + engines: {node: '>= 0.4'} + + has-symbols@1.0.3: + resolution: {integrity: sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + html-escaper@3.0.3: + resolution: {integrity: sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==} + + htmlnano@2.1.1: + resolution: {integrity: sha512-kAERyg/LuNZYmdqgCdYvugyLWNFAm8MWXpQMz1pLpetmCbFwoMxvkSoaAMlFrOC4OKTWI4KlZGT/RsNxg4ghOw==} + peerDependencies: + cssnano: ^7.0.0 + postcss: ^8.3.11 + purgecss: ^6.0.0 + relateurl: ^0.2.7 + srcset: 5.0.1 + svgo: ^3.0.2 + terser: ^5.10.0 + uncss: ^0.17.3 + peerDependenciesMeta: + cssnano: + optional: true + postcss: + optional: true + purgecss: + optional: true + relateurl: + optional: true + srcset: + optional: true + svgo: + optional: true + terser: + optional: true + uncss: + optional: true + + htmlparser2@6.1.0: + resolution: {integrity: sha512-gyyPk6rgonLFEDGoeRgQNaEUvdJ4ktTmmUh/h2t7s+M8oPpIPxgNACWa+6ESR57kXstwqPiCut0V8NRpcwgU7A==} + + htmlparser2@7.2.0: + resolution: {integrity: sha512-H7MImA4MS6cw7nbyURtLPO1Tms7C5H602LRETv95z1MxO/7CP7rDVROehUYeYBUYEON94NXXDEPmZuq+hX4sog==} + + http-cache-semantics@4.1.1: + resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} + + http2-wrapper@2.2.1: + resolution: {integrity: sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==} + engines: {node: '>=10.19.0'} + + human-signals@2.1.0: + resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} + engines: {node: '>=10.17.0'} + + iconv-lite@0.4.24: + resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} + engines: {node: '>=0.10.0'} + + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + + ieee754@1.2.1: + resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} + + ignore@5.2.4: + resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==} + engines: {node: '>= 4'} + + image-size@0.5.5: + resolution: {integrity: sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ==} + engines: {node: '>=0.10.0'} + hasBin: true + + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + + import-fresh@3.3.0: + resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} + engines: {node: '>=6'} + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + ini@1.3.8: + resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} + + inquirer@9.2.12: + resolution: {integrity: sha512-mg3Fh9g2zfuVWJn6lhST0O7x4n03k7G8Tx5nvikJkbq8/CK47WDVm+UznF0G6s5Zi0KcyUisr6DU8T67N5U+1Q==} + engines: {node: '>=14.18.0'} + + invariant@2.2.4: + resolution: {integrity: sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-arrayish@0.3.2: + resolution: {integrity: sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-core-module@2.15.1: + resolution: {integrity: sha512-z0vtXSwucUJtANQWldhbtbt7BnL0vxiFjIdDLAatwhDYty2bad6s+rijD6Ri4YuYJubLzIJLUidCh09e1djEVQ==} + engines: {node: '>= 0.4'} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-interactive@1.0.0: + resolution: {integrity: sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==} + engines: {node: '>=8'} + + is-json@2.0.1: + resolution: {integrity: sha512-6BEnpVn1rcf3ngfmViLM6vjUjGErbdrL4rwlv+u1NO1XO8kqT4YGL8+19Q+Z/bas8tY90BTWMk2+fW1g6hQjbA==} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-path-inside@4.0.0: + resolution: {integrity: sha512-lJJV/5dYS+RcL8uQdBDW9c9uWFLLBNRyFhnAKXw5tVqLlKZ4RMGZKv+YQ/IA3OhD+RpbJa1LLFM1FQPGyIXvOA==} + engines: {node: '>=12'} + + is-reference@3.0.2: + resolution: {integrity: sha512-v3rht/LgVcsdZa3O2Nqs+NMowLOxeOm7Ay9+/ARQ2F+qEoANRcqrjAZKGN0v8ymUetZGgkp26LTnGT7H0Qo9Pg==} + + is-stream@2.0.1: + resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} + engines: {node: '>=8'} + + is-stream@3.0.0: + resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-what@3.14.1: + resolution: {integrity: sha512-sNxgpk9793nzSs7bA6JQJGeIuRBQhAaNGG77kzYQgMkrID+lS6SlK07K5LaptscDlSaIgH+GPFzf+d75FVxozA==} + + isbinaryfile@4.0.10: + resolution: {integrity: sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw==} + engines: {node: '>= 8.0.0'} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + jiti@1.21.6: + resolution: {integrity: sha512-2yTgeWTWzMWkHu6Jp9NKgePDaYHbntiwvYuuJLbbN9vl7DC9DvXKOB2BC3ZZ92D3cvV/aflH0osDfwpHepQ53w==} + hasBin: true + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + jsesc@2.5.2: + resolution: {integrity: sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==} + engines: {node: '>=4'} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + + json-schema-to-ts@2.9.2: + resolution: {integrity: sha512-h9WqLkTVpBbiaPb5OmeUpz/FBLS/kvIJw4oRCPiEisIu2WjMh+aai0QIY2LoOhRFx5r92taGLcerIrzxKBAP6g==} + engines: {node: '>=16'} + + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + + jsonfile@6.1.0: + resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + less@4.2.0: + resolution: {integrity: sha512-P3b3HJDBtSzsXUl0im2L7gTO5Ubg8mEN6G8qoTS77iXxXX4Hvu4Qj540PZDvQ8V6DmX6iXo98k7Md0Cm1PrLaA==} + engines: {node: '>=6'} + hasBin: true + + lightningcss-darwin-arm64@1.21.8: + resolution: {integrity: sha512-BOMoGfcgkk2f4ltzsJqmkjiqRtlZUK+UdwhR+P6VgIsnpQBV3G01mlL6GzYxYqxq+6/3/n/D+4oy2NeknmADZw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-arm64@1.26.0: + resolution: {integrity: sha512-n4TIvHO1NY1ondKFYpL2ZX0bcC2y6yjXMD6JfyizgR8BCFNEeArINDzEaeqlfX9bXz73Bpz/Ow0nu+1qiDrBKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.21.8: + resolution: {integrity: sha512-YhF64mcVDPKKufL4aNFBnVH7uvzE0bW3YUsPXdP4yUcT/8IXChypOZ/PE1pmt2RlbmsyVuuIIeZU4zTyZe5Amw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-darwin-x64@1.26.0: + resolution: {integrity: sha512-Rf9HuHIDi1R6/zgBkJh25SiJHF+dm9axUZW/0UoYCW1/8HV0gMI0blARhH4z+REmWiU1yYT/KyNF3h7tHyRXUg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.21.8: + resolution: {integrity: sha512-CV6A/vTG2Ryd3YpChEgfWWv4TXCAETo9TcHSNx0IP0dnKcnDEiAko4PIKhCqZL11IGdN1ZLBCVPw+vw5ZYwzfA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-freebsd-x64@1.26.0: + resolution: {integrity: sha512-C/io7POAxp6sZxFSVGezjajMlCKQ8KSwISLLGRq8xLQpQMokYrUoqYEwmIX8mLmF6C/CZPk0gFmRSzd8biWM0g==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.21.8: + resolution: {integrity: sha512-9PMbqh8n/Xq0F4/j2NR/hHM2HRDiFXFSF0iOvV67pNWKJkHIO6mR8jBw/88Aro5Ye/ILsX5OuWsxIVJDFv0NXA==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm-gnueabihf@1.26.0: + resolution: {integrity: sha512-Aag9kqXqkyPSW+dXMgyWk66C984Nay2pY8Nws+67gHlDzV3cWh7TvFlzuaTaVFMVqdDTzN484LSK3u39zFBnzg==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.21.8: + resolution: {integrity: sha512-JTM/TuMMllkzaXV7/eDjG4IJKLlCl+RfYZwtsVmC82gc0QX0O37csGAcY2OGleiuA4DnEo/Qea5WoFfZUNC6zg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-gnu@1.26.0: + resolution: {integrity: sha512-iJmZM7fUyVjH+POtdiCtExG+67TtPUTer7K/5A8DIfmPfrmeGvzfRyBltGhQz13Wi15K1lf2cPYoRaRh6vcwNA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.21.8: + resolution: {integrity: sha512-01gWShXrgoIb8urzShpn1RWtZuaSyKSzF2hfO+flzlTPoACqcO3rgcu/3af4Cw54e8vKzL5hPRo4kROmgaOMLg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.26.0: + resolution: {integrity: sha512-XxoEL++tTkyuvu+wq/QS8bwyTXZv2y5XYCMcWL45b8XwkiS8eEEEej9BkMGSRwxa5J4K+LDeIhLrS23CpQyfig==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.21.8: + resolution: {integrity: sha512-yVB5vYJjJb/Aku0V9QaGYIntvK/1TJOlNB9GmkNpXX5bSSP2pYW4lWW97jxFMHO908M0zjEt1qyOLMyqojHL+Q==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-gnu@1.26.0: + resolution: {integrity: sha512-1dkTfZQAYLj8MUSkd6L/+TWTG8V6Kfrzfa0T1fSlXCXQHrt1HC1/UepXHtKHDt/9yFwyoeayivxXAsApVxn6zA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.21.8: + resolution: {integrity: sha512-TYi+KNtBVK0+FZvxTX/d5XJb+tw3Jq+2Rr9hW359wp1afsi1Vkg+uVGgbn+m2dipa5XwpCseQq81ylMlXuyfPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.26.0: + resolution: {integrity: sha512-yX3Rk9m00JGCUzuUhFEojY+jf/6zHs3XU8S8Vk+FRbnr4St7cjyMXdNjuA2LjiT8e7j8xHRCH8hyZ4H/btRE4A==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.26.0: + resolution: {integrity: sha512-X/597/cFnCogy9VItj/+7Tgu5VLbAtDF7KZDPdSw0MaL6FL940th1y3HiOzFIlziVvAtbo0RB3NAae1Oofr+Tw==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.21.8: + resolution: {integrity: sha512-mww+kqbPx0/C44l2LEloECtRUuOFDjq9ftp+EHTPiCp2t+avy0sh8MaFwGsrKkj2XfZhaRhi4CPVKBoqF1Qlwg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss-win32-x64-msvc@1.26.0: + resolution: {integrity: sha512-pYS3EyGP3JRhfqEFYmfFDiZ9/pVNfy8jVIYtrx9TVNusVyDK3gpW1w/rbvroQ4bDJi7grdUtyrYU6V2xkY/bBw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.21.8: + resolution: {integrity: sha512-jEqaL7m/ZckZJjlMAfycr1Kpz7f93k6n7KGF5SJjuPSm6DWI6h3ayLZmgRHgy1OfrwoCed6h4C/gHYPOd1OFMA==} + engines: {node: '>= 12.0.0'} + + lightningcss@1.26.0: + resolution: {integrity: sha512-a/XZ5hdgifrofQJUArr5AiJjx26SwMam3SJUSMjgebZbESZ96i+6Qsl8tLi0kaUsdMzBWXh9sN1Oe6hp2/dkQw==} + engines: {node: '>= 12.0.0'} + + lilconfig@2.1.0: + resolution: {integrity: sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==} + engines: {node: '>=10'} + + lilconfig@3.1.2: + resolution: {integrity: sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + linkedom@0.1.34: + resolution: {integrity: sha512-VGiPDn8Uv1MGnYq8EDxuS/8GW1UxeA6mmY6o7DJdRW8eLVOjPDVjG1ZTeeQx9PzkeKLrSZBkuqRXGLOmI9aDng==} + + lmdb@2.5.2: + resolution: {integrity: sha512-V5V5Xa2Hp9i2XsbDALkBTeHXnBXh/lEmk9p22zdr7jtuOIY9TGhjK6vAvTpOOx9IKU4hJkRWZxn/HsvR1ELLtA==} + + lmdb@2.7.11: + resolution: {integrity: sha512-x9bD4hVp7PFLUoELL8RglbNXhAMt5CYhkmss+CEau9KlNoilsTzNi9QDsPZb3KMpOGZXG6jmXhW3bBxE2XVztw==} + hasBin: true + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + locate-character@3.0.0: + resolution: {integrity: sha512-SW13ws7BjaeJ6p7Q6CO2nchbYEc3X3J6WrmTTDto7yMPqVSZTUyY5Tjbid+Ab8gLnATtygYtiDIJGQRRn2ZOiA==} + + lodash.sortby@4.7.0: + resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} + + lodash@4.17.21: + resolution: {integrity: sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + loose-envify@1.4.0: + resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} + hasBin: true + + lowercase-keys@3.0.0: + resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lru-cache@5.1.1: + resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + + lru-cache@6.0.0: + resolution: {integrity: sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==} + engines: {node: '>=10'} + + lucide-react@0.454.0: + resolution: {integrity: sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc + + magic-string@0.30.11: + resolution: {integrity: sha512-+Wri9p0QHMy+545hKww7YAu5NyzF8iomPL/RQazugQ9+Ez4Ic3mERMd8ZTX5rfK944j+560ZJi8iAwgak1Ac7A==} + + make-dir@2.1.0: + resolution: {integrity: sha512-LS9X+dc8KLxXCb8dni79fLIIUA5VyZoyjSMCwTluaXA0o27cCK0bhXkpgw+sTXVpPy/lSO57ilRixqk0vDmtRA==} + engines: {node: '>=6'} + + mdn-data@2.0.14: + resolution: {integrity: sha512-dn6wd0uw5GsdswPFfsgMp5NSB0/aDe6fK94YJV/AJDYXL6HVLWBsxeq7js7Ad+mU2K9LAlwpk6kN2D5mwCPVow==} + + mdn-data@2.0.30: + resolution: {integrity: sha512-GaqWWShW4kv/G9IEucWScBx9G1/vsFZZJUO+tD26M8J8z3Kw5RDQjaoZe03YAClgeS/SWPOcb4nkFBTEi5DUEA==} + + merge-stream@2.0.0: + resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mime@1.6.0: + resolution: {integrity: sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==} + engines: {node: '>=4'} + hasBin: true + + mime@2.6.0: + resolution: {integrity: sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg==} + engines: {node: '>=4.0.0'} + hasBin: true + + mimic-fn@2.1.0: + resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} + engines: {node: '>=6'} + + mimic-response@3.1.0: + resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} + engines: {node: '>=10'} + + mimic-response@4.0.0: + resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minimist@1.2.8: + resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mkdirp-classic@0.5.3: + resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + + mnemonic-id@3.2.7: + resolution: {integrity: sha512-kysx9gAGbvrzuFYxKkcRjnsg/NK61ovJOV4F1cHTRl9T5leg+bo6WI0pWIvOFh1Z/yDL0cjA5R3EEGPPLDv/XA==} + + ms@2.1.2: + resolution: {integrity: sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==} + + msgpackr-extract@3.0.3: + resolution: {integrity: sha512-P0efT1C9jIdVRefqjzOQ9Xml57zpOXnIuS+csaB4MdZbTdmGDLo8XhzBG1N7aO11gKDDkJvBLULeFTo46wwreA==} + hasBin: true + + msgpackr@1.11.0: + resolution: {integrity: sha512-I8qXuuALqJe5laEBYoFykChhSXLikZmUhccjGsPuSJ/7uPip2TJ7lwdIQwWSAi0jGZDXv4WOP8Qg65QZRuXxXw==} + + msgpackr@1.8.5: + resolution: {integrity: sha512-mpPs3qqTug6ahbblkThoUY2DQdNXcm4IapwOS3Vm/87vmpzLVelvp9h3It1y9l1VPpiFLV11vfOXnmeEwiIXwg==} + + mute-stream@1.0.0: + resolution: {integrity: sha512-avsJQhyd+680gKXyG/sQc0nXaC6rBkPOfyHYcFb9+hdkqQkR9bdnkJ0AMZhke0oesPqIO+mFFJ+IdBc7mst4IA==} + engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.7: + resolution: {integrity: sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + nanoid@5.0.3: + resolution: {integrity: sha512-I7X2b22cxA4LIHXPSqbBCEQSL+1wv8TuoefejsX4HFWyC6jc5JG7CEaxOltiKjc1M+YCS2YkrZZcj4+dytw9GA==} + engines: {node: ^18 || >=20} + hasBin: true + + napi-build-utils@1.0.2: + resolution: {integrity: sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==} + + needle@3.3.1: + resolution: {integrity: sha512-6k0YULvhpw+RoLNiQCRKOl09Rv1dPLr8hHnVjHqdolKwDrdNyk+Hmrthi4lIGPPz3r39dLx0hsF5s40sZ3Us4Q==} + engines: {node: '>= 4.4.x'} + hasBin: true + + node-abi@3.67.0: + resolution: {integrity: sha512-bLn/fU/ALVBE9wj+p4Y21ZJWYFjUXLXPi/IewyLZkx3ApxKDNBWCKdReeKOtD8dWpOdDCeMyLh6ZewzcLsG2Nw==} + engines: {node: '>=10'} + + node-addon-api@4.3.0: + resolution: {integrity: sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==} + + node-addon-api@6.1.0: + resolution: {integrity: sha512-+eawOlIgy680F0kBzPUNFhMZGtJ1YmqM6l4+Crf4IkImjYrO/mqPwRMh352g23uIaQKFItcQ64I7KMaJxHgAVA==} + + node-addon-api@7.1.1: + resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} + + node-gyp-build-optional-packages@5.0.3: + resolution: {integrity: sha512-k75jcVzk5wnnc/FMxsf4udAoTEUv2jY3ycfdSd3yWu6Cnd1oee6/CfZJApyscA4FJOmdoixWwiwOyf16RzD5JA==} + hasBin: true + + node-gyp-build-optional-packages@5.0.6: + resolution: {integrity: sha512-2ZJErHG4du9G3/8IWl/l9Bp5BBFy63rno5GVmjQijvTuUZKsl6g8RB4KH/x3NLcV5ZBb4GsXmAuTYr6dRml3Gw==} + hasBin: true + + node-gyp-build-optional-packages@5.2.2: + resolution: {integrity: sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw==} + hasBin: true + + node-object-hash@3.0.0: + resolution: {integrity: sha512-jLF6tlyletktvSAawuPmH1SReP0YfZQ+tBrDiTCK+Ai7eXPMS9odi5xW/iKC7ZhrWJJ0Z5xYcW/x+1fVMn1Qvw==} + engines: {node: '>=16', pnpm: '>=8'} + + node-releases@2.0.18: + resolution: {integrity: sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + normalize-range@0.1.2: + resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} + engines: {node: '>=0.10.0'} + + normalize-url@8.0.1: + resolution: {integrity: sha512-IO9QvjUMWxPQQhs60oOu10CRkWCiZzSUkzbXGGV9pviYl1fXYcvkzQ5jV9z8Y6un8ARoVRl4EtC6v6jNqbaJ/w==} + engines: {node: '>=14.16'} + + npm-run-path@4.0.1: + resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} + engines: {node: '>=8'} + + nth-check@2.1.1: + resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} + + nullthrows@1.1.1: + resolution: {integrity: sha512-2vPPEi+Z7WqML2jZYddDIfy5Dqb0r2fze2zTxNNknZaFpVHU3mFB3R+DWeJWGVx0ecvttSGlJTI+WG+8Z4cDWw==} + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + object-hash@3.0.0: + resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} + engines: {node: '>= 6'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@5.1.2: + resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} + engines: {node: '>=6'} + + ora@5.4.1: + resolution: {integrity: sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==} + engines: {node: '>=10'} + + ordered-binary@1.5.1: + resolution: {integrity: sha512-5VyHfHY3cd0iza71JepYG50My+YUbrFtGoUz2ooEydPyPM7Aai/JW098juLr+RG6+rDJuzNNTsEQu2DZa1A41A==} + + os-tmpdir@1.0.2: + resolution: {integrity: sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==} + engines: {node: '>=0.10.0'} + + p-cancelable@3.0.0: + resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} + engines: {node: '>=12.20'} + + package-json-from-dist@1.0.0: + resolution: {integrity: sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==} + + package-json@8.1.1: + resolution: {integrity: sha512-cbH9IAIJHNj9uXi196JVsRlt7cHKak6u/e6AkL/bkRelZ7rlL3X1YKxsZwa36xipOEKAsdtmaG6aAJoM1fx2zA==} + engines: {node: '>=14.16'} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + + parse-node-version@1.0.1: + resolution: {integrity: sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==} + engines: {node: '>= 0.10'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@4.0.0: + resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} + engines: {node: '>=8'} + + periscopic@3.1.0: + resolution: {integrity: sha512-vKiQ8RRtkl9P+r/+oefh25C3fhybptkHKCZSPlcXiJux2tJF55GnEj3BVn4A5gKfq9NWWXXrxkHBwVPUfH0opw==} + + picocolors@1.0.1: + resolution: {integrity: sha512-anP1Z8qwhkbmu7MFP5iTt+wQKXgwzf7zTyGlcdzabySa9vd0Xt392U0rVmz9poOaBj0uHJKyyo9/upk0HrEQew==} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pify@2.3.0: + resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} + engines: {node: '>=0.10.0'} + + pify@4.0.1: + resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} + engines: {node: '>=6'} + + pify@6.1.0: + resolution: {integrity: sha512-KocF8ve28eFjjuBKKGvzOBGzG8ew2OqOOSxTTZhirkzH7h3BI1vyzqlR0qbfcDBve1Yzo3FVlWUAtCRrbVN8Fw==} + engines: {node: '>=14.16'} + + pirates@4.0.6: + resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + engines: {node: '>= 6'} + + plasmo@0.89.4: + resolution: {integrity: sha512-vsoMe8ts0tyW27fZxwQLqWR/58NKqRepLFrZMVBH4ceSIyPDryfPpXzVxmBDH43odbiUVFdh8BGAt2ri2vQuGw==} + hasBin: true + + postcss-import@15.1.0: + resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} + engines: {node: '>=14.0.0'} + peerDependencies: + postcss: ^8.0.0 + + postcss-js@4.0.1: + resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + engines: {node: ^12 || ^14 || >= 16} + peerDependencies: + postcss: ^8.4.21 + + postcss-load-config@4.0.2: + resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} + engines: {node: '>= 14'} + peerDependencies: + postcss: '>=8.0.9' + ts-node: '>=9.0.0' + peerDependenciesMeta: + postcss: + optional: true + ts-node: + optional: true + + postcss-loader@8.1.1: + resolution: {integrity: sha512-0IeqyAsG6tYiDRCYKQJLAmgQr47DX6N7sFSWvQxt6AcupX8DIdmykuk/o/tx0Lze3ErGHJEp5OSRxrelC6+NdQ==} + engines: {node: '>= 18.12.0'} + peerDependencies: + '@rspack/core': 0.x || 1.x + postcss: ^7.0.0 || ^8.0.1 + webpack: ^5.0.0 + peerDependenciesMeta: + '@rspack/core': + optional: true + webpack: + optional: true + + postcss-nested@6.2.0: + resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} + engines: {node: '>=12.0'} + peerDependencies: + postcss: ^8.2.14 + + postcss-selector-parser@6.1.2: + resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + engines: {node: '>=4'} + + postcss-value-parser@4.2.0: + resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} + + postcss@8.4.41: + resolution: {integrity: sha512-TesUflQ0WKZqAvg52PWL6kHgLKP6xB6heTOdoYM0Wt2UHyxNa4K25EZZMgKns3BH1RLVbZCREPpLY0rhnNoHVQ==} + engines: {node: ^10 || ^12 || >=14} + + posthtml-parser@0.10.2: + resolution: {integrity: sha512-PId6zZ/2lyJi9LiKfe+i2xv57oEjJgWbsHGGANwos5AvdQp98i6AtamAl8gzSVFGfQ43Glb5D614cvZf012VKg==} + engines: {node: '>=12'} + + posthtml-parser@0.11.0: + resolution: {integrity: sha512-QecJtfLekJbWVo/dMAA+OSwY79wpRmbqS5TeXvXSX+f0c6pW4/SE6inzZ2qkU7oAMCPqIDkZDvd/bQsSFUnKyw==} + engines: {node: '>=12'} + + posthtml-render@3.0.0: + resolution: {integrity: sha512-z+16RoxK3fUPgwaIgH9NGnK1HKY9XIDpydky5eQGgAFVXTCSezalv9U2jQuNV+Z9qV1fDWNzldcw4eK0SSbqKA==} + engines: {node: '>=12'} + + posthtml@0.16.6: + resolution: {integrity: sha512-JcEmHlyLK/o0uGAlj65vgg+7LIms0xKXe60lcDOTU7oVX/3LuEuLwrQpW3VJ7de5TaFKiW4kWkaIpJL42FEgxQ==} + engines: {node: '>=12.0.0'} + + prebuild-install@7.1.2: + resolution: {integrity: sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==} + engines: {node: '>=10'} + hasBin: true + + prettier@3.2.4: + resolution: {integrity: sha512-FWu1oLHKCrtpO1ypU6J0SbK2d9Ckwysq6bHj/uaCP26DxrPpppCLQRGVuqAxSTvhF00AcvDRyYrLNW7ocBhFFQ==} + engines: {node: '>=14'} + hasBin: true + + process@0.11.10: + resolution: {integrity: sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==} + engines: {node: '>= 0.6.0'} + + proto-list@1.2.4: + resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} + + prr@1.0.1: + resolution: {integrity: sha512-yPw4Sng1gWghHQWj0B3ZggWUm4qVbPwPFcRG8KyxiU7J2OHFSoEHKS+EZ3fv5l1t9CyCiop6l/ZYeWbrgoQejw==} + + pump@3.0.0: + resolution: {integrity: sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + queue-tick@1.0.1: + resolution: {integrity: sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==} + + quick-lru@5.1.1: + resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} + engines: {node: '>=10'} + + radix-ui@1.0.1: + resolution: {integrity: sha512-qfGibbqtbOlxP3b+1JjbLUc8Q7+e9DL8gFycLtkBkoAQyUkKuHAEBfFUcyG5MaQHjqRuML+YLtt/R1/dUYQafQ==} + peerDependencies: + react: ^16.8 || ^17.0 || ^18.0 + react-dom: ^16.8 || ^17.0 || ^18.0 + + rc@1.2.8: + resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} + hasBin: true + + react-dom@18.2.0: + resolution: {integrity: sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==} + peerDependencies: + react: ^18.2.0 + + react-error-overlay@6.0.9: + resolution: {integrity: sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew==} + + react-hooks-global-state@2.1.0: + resolution: {integrity: sha512-tLSRhB5pD3QiNsGOo893m8lVZXDuIkdin8PKKmGqnJniAaD/tzl3BdAkba1vOv1/q1bpuN+9zrqqAHkRJhXzJw==} + peerDependencies: + react: '>=16.8.0' + + react-refresh@0.14.0: + resolution: {integrity: sha512-wViHqhAd8OHeLS/IRMJjTSDHF3U9eWi62F/MledQGPdJGDhodXJ9PBLNGr6WWL7qlH12Mt3TyTpbS+hGXMjCzQ==} + engines: {node: '>=0.10.0'} + + react-refresh@0.9.0: + resolution: {integrity: sha512-Gvzk7OZpiqKSkxsQvO/mbTN1poglhmAV7gR/DdIrRrSMXraRQQlfikRJOr3Nb9GTMPC5kof948Zy6jJZIFtDvQ==} + engines: {node: '>=0.10.0'} + + react-remove-scroll-bar@2.3.6: + resolution: {integrity: sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.6.0: + resolution: {integrity: sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.6.3: + resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react-router-dom@6.26.1: + resolution: {integrity: sha512-veut7m41S1fLql4pLhxeSW3jlqs+4MtjRLj0xvuCEXsxusJCbs6I8yn9BxzzDX2XDgafrccY6hwjmd/bL54tFw==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + react-dom: '>=16.8' + + react-router@6.26.1: + resolution: {integrity: sha512-kIwJveZNwp7teQRI5QmwWo39A5bXRyqpH0COKKmPnyD2vBvDwgFXSqDUYtt1h+FEyfnE8eXr7oe0MxRzVwCcvQ==} + engines: {node: '>=14.0.0'} + peerDependencies: + react: '>=16.8' + + react-style-singleton@2.2.1: + resolution: {integrity: sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + react@18.2.0: + resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==} + engines: {node: '>=0.10.0'} + + read-cache@1.0.0: + resolution: {integrity: sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==} + + readable-stream@3.6.2: + resolution: {integrity: sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==} + engines: {node: '>= 6'} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + regenerator-runtime@0.13.11: + resolution: {integrity: sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==} + + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + + registry-auth-token@5.0.2: + resolution: {integrity: sha512-o/3ikDxtXaA59BmZuZrJZDJv8NMDGSj+6j6XaeBmHw8eY1i1qd9+6H+LjVvQXx3HN6aRCGa1cUdJ9RaJZUugnQ==} + engines: {node: '>=14'} + + registry-url@6.0.1: + resolution: {integrity: sha512-+crtS5QjFRqFCoQmvGduwYWEBng99ZvmFvF+cUJkGYF1L1BfU8C6Zp9T7f5vPAwyLkUExpvK+ANVZmGU49qi4Q==} + engines: {node: '>=12'} + + resolve-alpn@1.2.1: + resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + resolve@1.22.8: + resolution: {integrity: sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw==} + hasBin: true + + responselike@3.0.0: + resolution: {integrity: sha512-40yHxbNcl2+rzXvZuVkrYohathsSJlMTXKryG5y8uciHv1+xDLHQpgjG64JUO9nrEq2jGLH6IZ8BcZyw3wrweg==} + engines: {node: '>=14.16'} + + restore-cursor@3.1.0: + resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} + engines: {node: '>=8'} + + reusify@1.0.4: + resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + rollup@3.29.5: + resolution: {integrity: sha512-GVsDdsbJzzy4S/v3dqWPJ7EfvZJfCHiDqe80IyrF59LYuP+e6U1LJoUqeuqRbwAWoMNoXivMNeNAOf5E22VA1w==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + + run-async@3.0.0: + resolution: {integrity: sha512-540WwVDOMxA6dN6We19EcT9sc3hkXPw5mzRNGM3FkdN/vtE9NFvj5lFAPNwUDmJjXidm3v7TC1cTE7t17Ulm1Q==} + engines: {node: '>=0.12.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + rxjs@7.8.1: + resolution: {integrity: sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + + sass@1.77.8: + resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==} + engines: {node: '>=14.0.0'} + hasBin: true + + sax@1.4.1: + resolution: {integrity: sha512-+aWOz7yVScEGoKNd4PA10LZ8sk0A/z5+nXQG5giUO5rprX9jgYsTdov9qCchZiPIZezbZH+jRut8nPodFAX4Jg==} + + scheduler@0.23.2: + resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@6.3.1: + resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} + hasBin: true + + semver@7.5.4: + resolution: {integrity: sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==} + engines: {node: '>=10'} + hasBin: true + + semver@7.6.3: + resolution: {integrity: sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==} + engines: {node: '>=10'} + hasBin: true + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + sharp@0.32.6: + resolution: {integrity: sha512-KyLTWwgcR9Oe4d9HwCwNM2l7+J0dUQwn/yf7S0EnTtb0eVS4RxO0eUSvxPtzT4F3SY+C4K6fqdv/DO27sJ/v/w==} + engines: {node: '>=14.15.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + signal-exit@3.0.7: + resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + simple-concat@1.0.1: + resolution: {integrity: sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==} + + simple-get@4.0.1: + resolution: {integrity: sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==} + + simple-swizzle@0.2.2: + resolution: {integrity: sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==} + + slash@3.0.0: + resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} + engines: {node: '>=8'} + + source-map-js@1.2.0: + resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} + engines: {node: '>=0.10.0'} + + source-map@0.6.1: + resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} + engines: {node: '>=0.10.0'} + + source-map@0.8.0-beta.0: + resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} + engines: {node: '>= 8'} + + srcset@4.0.0: + resolution: {integrity: sha512-wvLeHgcVHKO8Sc/H/5lkGreJQVeYMm9rlmt8PuR1xE31rIuXhuzznUUqAt8MqLhB3MqJdFzlNAfpcWnxiFUcPw==} + engines: {node: '>=12'} + + stable@0.1.8: + resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==} + deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility' + + streamx@2.19.0: + resolution: {integrity: sha512-5z6CNR4gtkPbwlxyEqoDGDmWIzoNJqCBt4Eac1ICP9YaIT08ct712cFj0u1rx4F8luAuL+3Qc+RFIdI4OX00kg==} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string_decoder@1.3.0: + resolution: {integrity: sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-final-newline@2.0.0: + resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} + engines: {node: '>=6'} + + strip-json-comments@2.0.1: + resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} + engines: {node: '>=0.10.0'} + + sucrase@3.35.0: + resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + svelte@4.2.2: + resolution: {integrity: sha512-My2tytF2e2NnHSpn2M7/3VdXT4JdTglYVUuSuK/mXL2XtulPYbeBfl8Dm1QiaKRn0zoULRnL+EtfZHHP0k4H3A==} + engines: {node: '>=16'} + + svg-parser@2.0.4: + resolution: {integrity: sha512-e4hG1hRwoOdRb37cIMSgzNsxyzKfayW6VOflrwvR+/bzrkyxY/31WkbgnQpgtrNp1SdpJvpUAGTa/ZoiPNDuRQ==} + + svgo@2.8.0: + resolution: {integrity: sha512-+N/Q9kV1+F+UeWYoSiULYo4xYSDQlTgb+ayMobAXPwMnLvop7oxKMo9OzIrX5x3eS4L4f2UHhc9axXwY8DpChg==} + engines: {node: '>=10.13.0'} + hasBin: true + + tailwind-merge@2.5.4: + resolution: {integrity: sha512-0q8cfZHMu9nuYP/b5Shb7Y7Sh1B7Nnl5GqNr1U+n2p6+mybvRtayrQ+0042Z5byvTA8ihjlP8Odo8/VnHbZu4Q==} + + tailwindcss-animate@1.0.7: + resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} + peerDependencies: + tailwindcss: '>=3.0.0 || insiders' + + tailwindcss@3.4.10: + resolution: {integrity: sha512-KWZkVPm7yJRhdu4SRSl9d4AK2wM3a50UsvgHZO7xY77NQr2V+fIrEuoDGQcbvswWvFGbS2f6e+jC/6WJm1Dl0w==} + engines: {node: '>=14.0.0'} + hasBin: true + + tar-fs@2.1.1: + resolution: {integrity: sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==} + + tar-fs@3.0.6: + resolution: {integrity: sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==} + + tar-stream@2.2.0: + resolution: {integrity: sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==} + engines: {node: '>=6'} + + tar-stream@3.1.7: + resolution: {integrity: sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==} + + temp-dir@3.0.0: + resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} + engines: {node: '>=14.16'} + + tempy@3.1.0: + resolution: {integrity: sha512-7jDLIdD2Zp0bDe5r3D2qtkd1QOCacylBuL7oa4udvN6v2pqr4+LcCr67C8DR1zkpaZ8XosF5m1yQSabKAW6f2g==} + engines: {node: '>=14.16'} + + text-decoder@1.1.1: + resolution: {integrity: sha512-8zll7REEv4GDD3x4/0pW+ppIxSNs7H1J10IKFZsuOMscumCdM2a+toDGLPA3T+1+fLBql4zbt5z83GEQGGV5VA==} + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + timsort@0.3.0: + resolution: {integrity: sha512-qsdtZH+vMoCARQtyod4imc2nIJwg9Cc7lPRrw9CzF8ZKR0khdr8+2nX80PBhET3tcyTtJDxAffGh2rXH4tyU8A==} + + tmp@0.0.33: + resolution: {integrity: sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==} + engines: {node: '>=0.6.0'} + + to-fast-properties@2.0.0: + resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} + engines: {node: '>=4'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + tr46@1.0.1: + resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-algebra@1.2.2: + resolution: {integrity: sha512-kloPhf1hq3JbCPOTYoOWDKxebWjNb2o/LKnNfkWhxVVisFFmMJPPdJeGoGmM+iRLyoXAR61e08Pb+vUXINg8aA==} + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tslib@2.7.0: + resolution: {integrity: sha512-gLXCKdN1/j47AiHiOkJN69hJmcbGTHI0ImLmbYLHykhgeN0jVGola9yVjFgzCUklsZQMW55o+dW7IXv3RCXDzA==} + + tsup@7.2.0: + resolution: {integrity: sha512-vDHlczXbgUvY3rWvqFEbSqmC1L7woozbzngMqTtL2PGBODTtWlRwGDDawhvWzr5c1QjKe4OAKqJGfE1xeXUvtQ==} + engines: {node: '>=16.14'} + hasBin: true + peerDependencies: + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.1.0' + peerDependenciesMeta: + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + tunnel-agent@0.6.0: + resolution: {integrity: sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==} + + type-fest@0.20.2: + resolution: {integrity: sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==} + engines: {node: '>=10'} + + type-fest@0.21.3: + resolution: {integrity: sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==} + engines: {node: '>=10'} + + type-fest@1.4.0: + resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} + engines: {node: '>=10'} + + type-fest@2.19.0: + resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} + engines: {node: '>=12.20'} + + typescript@5.2.2: + resolution: {integrity: sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==} + engines: {node: '>=14.17'} + hasBin: true + + typescript@5.3.3: + resolution: {integrity: sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==} + engines: {node: '>=14.17'} + hasBin: true + + uhyphen@0.1.0: + resolution: {integrity: sha512-o0QVGuFg24FK765Qdd5kk0zU/U4dEsCtN/GSiwNI9i8xsSVtjIAOdTaVhLwZ1nrbWxFVMxNDDl+9fednsOMsBw==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + unique-string@3.0.0: + resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} + engines: {node: '>=12'} + + universalify@2.0.1: + resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} + engines: {node: '>= 10.0.0'} + + update-browserslist-db@1.1.0: + resolution: {integrity: sha512-EdRAaAyk2cUE1wOf2DkEhzxqOQvFOoRJFNS6NeyJ01Gp2beMRpBAINjM2iDXE3KCuKhwnvHIQCJm6ThL2Z+HzQ==} + hasBin: true + peerDependencies: + browserslist: '>= 4.21.0' + + use-callback-ref@1.3.2: + resolution: {integrity: sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.2: + resolution: {integrity: sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': ^16.9.0 || ^17.0.0 || ^18.0.0 + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sync-external-store@1.2.0: + resolution: {integrity: sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + use-sync-external-store@1.2.2: + resolution: {integrity: sha512-PElTlVMwpblvbNqQ82d2n6RjStvdSoNe9FG28kNfz3WiXilJm4DdNkEzRhCZuIDwY8U08WVihhGR5iRqAwfDiw==} + peerDependencies: + react: ^16.8.0 || ^17.0.0 || ^18.0.0 + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + utility-types@3.11.0: + resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==} + engines: {node: '>= 4'} + + vue@3.3.4: + resolution: {integrity: sha512-VTyEYn3yvIeY1Py0WaYGZsXnz3y5UnGi62GjVEqvEGPl6nxbOrCXbVOTQWBEJUqAyTUk2uJ5JLVnYJ6ZzGbrSw==} + + wcwidth@1.0.1: + resolution: {integrity: sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==} + + weak-lru-cache@1.2.2: + resolution: {integrity: sha512-DEAoo25RfSYMuTGc9vPJzZcZullwIqRDSI9LOy+fkCJPi6hykCnfKaXTuPBDuXAUcqHXyOgFtHNp/kB2FjYHbw==} + + webidl-conversions@4.0.2: + resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} + + whatwg-url@7.1.0: + resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + wrap-ansi@6.2.0: + resolution: {integrity: sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==} + engines: {node: '>=8'} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + xxhash-wasm@0.4.2: + resolution: {integrity: sha512-/eyHVRJQCirEkSZ1agRSCwriMhwlyUcFkXD5TPVSLP+IPzjsqMVzZwdoczLp1SoQU0R3dxz1RpIK+4YNQbCVOA==} + + yallist@3.1.1: + resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + + yallist@4.0.0: + resolution: {integrity: sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==} + + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + + yaml@2.5.0: + resolution: {integrity: sha512-2wWLbGbYDiSqqIKoPjar3MPgB94ErzCtrNE1FdqGuaO0pi2JGjmE8aW8TDZwzU7vuxcGRdL/4gPQwQ7hD5AMSw==} + engines: {node: '>= 14'} + hasBin: true + + zustand@4.0.0: + resolution: {integrity: sha512-OrsfQTnRXF1LZ9/vR/IqN9ws5EXUhb149xmPjErZnUrkgxS/gAHGy2dPNIVkVvoxrVe1sIydn4JjF0dYHmGeeQ==} + engines: {node: '>=12.7.0'} + peerDependencies: + immer: '>=9.0' + react: '>=16.8' + peerDependenciesMeta: + immer: + optional: true + react: + optional: true + +snapshots: + + '@alloc/quick-lru@5.2.0': {} + + '@ampproject/remapping@2.3.0': + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + + '@babel/code-frame@7.24.7': + dependencies: + '@babel/highlight': 7.24.7 + picocolors: 1.0.1 + + '@babel/compat-data@7.25.4': {} + + '@babel/core@7.25.2': + dependencies: + '@ampproject/remapping': 2.3.0 + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.5 + '@babel/helper-compilation-targets': 7.25.2 + '@babel/helper-module-transforms': 7.25.2(@babel/core@7.25.2) + '@babel/helpers': 7.25.0 + '@babel/parser': 7.25.4 + '@babel/template': 7.25.0 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 + convert-source-map: 2.0.0 + debug: 4.3.6 + gensync: 1.0.0-beta.2 + json5: 2.2.3 + semver: 6.3.1 + transitivePeerDependencies: + - supports-color + + '@babel/generator@7.25.5': + dependencies: + '@babel/types': 7.25.4 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 + jsesc: 2.5.2 + + '@babel/helper-compilation-targets@7.25.2': + dependencies: + '@babel/compat-data': 7.25.4 + '@babel/helper-validator-option': 7.24.8 + browserslist: 4.23.3 + lru-cache: 5.1.1 + semver: 6.3.1 + + '@babel/helper-module-imports@7.24.7': + dependencies: + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-module-transforms@7.25.2(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@babel/helper-module-imports': 7.24.7 + '@babel/helper-simple-access': 7.24.7 + '@babel/helper-validator-identifier': 7.24.7 + '@babel/traverse': 7.25.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-simple-access@7.24.7': + dependencies: + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 + transitivePeerDependencies: + - supports-color + + '@babel/helper-string-parser@7.24.8': {} + + '@babel/helper-validator-identifier@7.24.7': {} + + '@babel/helper-validator-option@7.24.8': {} + + '@babel/helpers@7.25.0': + dependencies: + '@babel/template': 7.25.0 + '@babel/types': 7.25.4 + + '@babel/highlight@7.24.7': + dependencies: + '@babel/helper-validator-identifier': 7.24.7 + chalk: 2.4.2 + js-tokens: 4.0.0 + picocolors: 1.0.1 + + '@babel/parser@7.25.4': + dependencies: + '@babel/types': 7.25.4 + + '@babel/runtime@7.25.4': + dependencies: + regenerator-runtime: 0.14.1 + + '@babel/template@7.25.0': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/parser': 7.25.4 + '@babel/types': 7.25.4 + + '@babel/traverse@7.25.4': + dependencies: + '@babel/code-frame': 7.24.7 + '@babel/generator': 7.25.5 + '@babel/parser': 7.25.4 + '@babel/template': 7.25.0 + '@babel/types': 7.25.4 + debug: 4.3.6 + globals: 11.12.0 + transitivePeerDependencies: + - supports-color + + '@babel/types@7.25.4': + dependencies: + '@babel/helper-string-parser': 7.24.8 + '@babel/helper-validator-identifier': 7.24.7 + to-fast-properties: 2.0.0 + + '@esbuild/android-arm64@0.18.20': + optional: true + + '@esbuild/android-arm@0.18.20': + optional: true + + '@esbuild/android-x64@0.18.20': + optional: true + + '@esbuild/darwin-arm64@0.18.20': + optional: true + + '@esbuild/darwin-x64@0.18.20': + optional: true + + '@esbuild/freebsd-arm64@0.18.20': + optional: true + + '@esbuild/freebsd-x64@0.18.20': + optional: true + + '@esbuild/linux-arm64@0.18.20': + optional: true + + '@esbuild/linux-arm@0.18.20': + optional: true + + '@esbuild/linux-ia32@0.18.20': + optional: true + + '@esbuild/linux-loong64@0.18.20': + optional: true + + '@esbuild/linux-mips64el@0.18.20': + optional: true + + '@esbuild/linux-ppc64@0.18.20': + optional: true + + '@esbuild/linux-riscv64@0.18.20': + optional: true + + '@esbuild/linux-s390x@0.18.20': + optional: true + + '@esbuild/linux-x64@0.18.20': + optional: true + + '@esbuild/netbsd-x64@0.18.20': + optional: true + + '@esbuild/openbsd-x64@0.18.20': + optional: true + + '@esbuild/sunos-x64@0.18.20': + optional: true + + '@esbuild/win32-arm64@0.18.20': + optional: true + + '@esbuild/win32-ia32@0.18.20': + optional: true + + '@esbuild/win32-x64@0.18.20': + optional: true + + '@expo/spawn-async@1.7.2': + dependencies: + cross-spawn: 7.0.3 + + '@floating-ui/core@1.6.8': + dependencies: + '@floating-ui/utils': 0.2.8 + + '@floating-ui/dom@1.6.12': + dependencies: + '@floating-ui/core': 1.6.8 + '@floating-ui/utils': 0.2.8 + + '@floating-ui/react-dom@2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@floating-ui/dom': 1.6.12 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + '@floating-ui/utils@0.2.8': {} + + '@ianvs/prettier-plugin-sort-imports@4.1.1(@vue/compiler-sfc@3.3.4)(prettier@3.2.4)': + dependencies: + '@babel/core': 7.25.2 + '@babel/generator': 7.25.5 + '@babel/parser': 7.25.4 + '@babel/traverse': 7.25.4 + '@babel/types': 7.25.4 + prettier: 3.2.4 + semver: 7.6.3 + optionalDependencies: + '@vue/compiler-sfc': 3.3.4 + transitivePeerDependencies: + - supports-color + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@jridgewell/gen-mapping@0.3.5': + dependencies: + '@jridgewell/set-array': 1.2.1 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/set-array@1.2.1': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@lezer/common@0.15.12': {} + + '@lezer/common@1.2.1': {} + + '@lezer/lr@0.15.8': + dependencies: + '@lezer/common': 0.15.12 + + '@lezer/lr@1.4.2': + dependencies: + '@lezer/common': 1.2.1 + + '@ljharb/through@2.3.13': + dependencies: + call-bind: 1.0.7 + + '@lmdb/lmdb-darwin-arm64@2.5.2': + optional: true + + '@lmdb/lmdb-darwin-arm64@2.7.11': + optional: true + + '@lmdb/lmdb-darwin-x64@2.5.2': + optional: true + + '@lmdb/lmdb-darwin-x64@2.7.11': + optional: true + + '@lmdb/lmdb-linux-arm64@2.5.2': + optional: true + + '@lmdb/lmdb-linux-arm64@2.7.11': + optional: true + + '@lmdb/lmdb-linux-arm@2.5.2': + optional: true + + '@lmdb/lmdb-linux-arm@2.7.11': + optional: true + + '@lmdb/lmdb-linux-x64@2.5.2': + optional: true + + '@lmdb/lmdb-linux-x64@2.7.11': + optional: true + + '@lmdb/lmdb-win32-x64@2.5.2': + optional: true + + '@lmdb/lmdb-win32-x64@2.7.11': + optional: true + + '@mischnic/json-sourcemap@0.1.0': + dependencies: + '@lezer/common': 0.15.12 + '@lezer/lr': 0.15.8 + json5: 2.2.3 + + '@mischnic/json-sourcemap@0.1.1': + dependencies: + '@lezer/common': 1.2.1 + '@lezer/lr': 1.4.2 + json5: 2.2.3 + + '@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-arm@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-linux-x64@3.0.3': + optional: true + + '@msgpackr-extract/msgpackr-extract-win32-x64@3.0.3': + optional: true + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.17.1 + + '@parcel/bundler-default@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/cache@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs': 2.8.3(@parcel/core@2.9.3) + '@parcel/logger': 2.8.3 + '@parcel/utils': 2.8.3 + lmdb: 2.5.2 + + '@parcel/cache@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/logger': 2.9.3 + '@parcel/utils': 2.9.3 + lmdb: 2.7.11 + + '@parcel/codeframe@2.8.3': + dependencies: + chalk: 4.1.2 + + '@parcel/codeframe@2.9.3': + dependencies: + chalk: 4.1.2 + + '@parcel/compressor-raw@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/config-default@2.9.3(@parcel/core@2.9.3)(@swc/helpers@0.5.12)(postcss@8.4.41)(typescript@5.2.2)': + dependencies: + '@parcel/bundler-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/compressor-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/core': 2.9.3 + '@parcel/namer-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-htmlnano': 2.9.3(@parcel/core@2.9.3)(postcss@8.4.41)(typescript@5.2.2) + '@parcel/optimizer-image': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-svgo': 2.9.3(@parcel/core@2.9.3) + '@parcel/optimizer-swc': 2.9.3(@parcel/core@2.9.3)(@swc/helpers@0.5.12) + '@parcel/packager-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-html': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/packager-svg': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-dev-server': 2.9.3(@parcel/core@2.9.3) + '@parcel/resolver-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-browser-hmr': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-react-refresh': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-service-worker': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-babel': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-html': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-image': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-json': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-postcss': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-posthtml': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-react-refresh-wrap': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-svg': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@swc/helpers' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/core@2.9.3': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/cache': 2.9.3(@parcel/core@2.9.3) + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/profiler': 2.9.3 + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + abortcontroller-polyfill: 1.7.5 + base-x: 3.0.10 + browserslist: 4.23.3 + clone: 2.1.2 + dotenv: 7.0.0 + dotenv-expand: 5.1.0 + json5: 2.2.3 + msgpackr: 1.11.0 + nullthrows: 1.1.1 + semver: 7.6.3 + + '@parcel/diagnostic@2.8.3': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + nullthrows: 1.1.1 + + '@parcel/diagnostic@2.9.3': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + nullthrows: 1.1.1 + + '@parcel/events@2.8.3': {} + + '@parcel/events@2.9.3': {} + + '@parcel/fs-search@2.8.3': + dependencies: + detect-libc: 1.0.3 + + '@parcel/fs-search@2.9.3': {} + + '@parcel/fs@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs-search': 2.8.3 + '@parcel/types': 2.8.3(@parcel/core@2.9.3) + '@parcel/utils': 2.8.3 + '@parcel/watcher': 2.2.0 + '@parcel/workers': 2.8.3(@parcel/core@2.9.3) + + '@parcel/fs@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/fs-search': 2.9.3 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/watcher': 2.2.0 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + + '@parcel/graph@2.9.3': + dependencies: + nullthrows: 1.1.1 + + '@parcel/hash@2.8.3': + dependencies: + detect-libc: 1.0.3 + xxhash-wasm: 0.4.2 + + '@parcel/hash@2.9.3': + dependencies: + xxhash-wasm: 0.4.2 + + '@parcel/logger@2.8.3': + dependencies: + '@parcel/diagnostic': 2.8.3 + '@parcel/events': 2.8.3 + + '@parcel/logger@2.9.3': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + + '@parcel/markdown-ansi@2.8.3': + dependencies: + chalk: 4.1.2 + + '@parcel/markdown-ansi@2.9.3': + dependencies: + chalk: 4.1.2 + + '@parcel/namer-default@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/node-resolver-core@3.0.3(@parcel/core@2.9.3)': + dependencies: + '@mischnic/json-sourcemap': 0.1.1 + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-css@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.23.3 + lightningcss: 1.26.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-data-url@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + isbinaryfile: 4.0.10 + mime: 2.6.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-htmlnano@2.9.3(@parcel/core@2.9.3)(postcss@8.4.41)(typescript@5.2.2)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + htmlnano: 2.1.1(postcss@8.4.41)(svgo@2.8.0)(typescript@5.2.2) + nullthrows: 1.1.1 + posthtml: 0.16.6 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + - cssnano + - postcss + - purgecss + - relateurl + - srcset + - terser + - typescript + - uncss + + '@parcel/optimizer-image@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + + '@parcel/optimizer-svgo@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + svgo: 2.8.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/optimizer-swc@2.9.3(@parcel/core@2.9.3)(@swc/helpers@0.5.12)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + '@swc/core': 1.7.14(@swc/helpers@0.5.12) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + - '@swc/helpers' + + '@parcel/package-manager@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.8.3 + '@parcel/fs': 2.8.3(@parcel/core@2.9.3) + '@parcel/logger': 2.8.3 + '@parcel/types': 2.8.3(@parcel/core@2.9.3) + '@parcel/utils': 2.8.3 + '@parcel/workers': 2.8.3(@parcel/core@2.9.3) + semver: 5.7.2 + + '@parcel/package-manager@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/logger': 2.9.3 + '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + semver: 7.6.3 + + '@parcel/packager-css@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-html@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-js@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + globals: 13.24.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-raw@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/packager-svg@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + posthtml: 0.16.6 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/plugin@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/types': 2.8.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/plugin@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/profiler@2.9.3': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + chrome-trace-event: 1.0.4 + + '@parcel/reporter-bundle-buddy@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/reporter-dev-server@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/resolver-default@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/node-resolver-core': 3.0.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-browser-hmr@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-js@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.8.3(@parcel/core@2.9.3) + '@parcel/utils': 2.8.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-js@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-react-refresh@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + react-error-overlay: 6.0.9 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/runtime-service-worker@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/source-map@2.1.1': + dependencies: + detect-libc: 1.0.3 + + '@parcel/transformer-babel@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.23.3 + json5: 2.2.3 + nullthrows: 1.1.1 + semver: 7.6.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-css@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + browserslist: 4.23.3 + lightningcss: 1.26.0 + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-graphql@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + graphql: 15.9.0 + graphql-import-macro: 1.0.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-html@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.3 + srcset: 4.0.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-image@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + + '@parcel/transformer-inline-string@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-js@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + '@swc/helpers': 0.5.12 + browserslist: 4.23.3 + nullthrows: 1.1.1 + regenerator-runtime: 0.13.11 + semver: 7.6.3 + + '@parcel/transformer-json@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + json5: 2.2.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-less@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + less: 4.2.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-postcss@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + clone: 2.1.2 + nullthrows: 1.1.1 + postcss-value-parser: 4.2.0 + semver: 7.6.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-posthtml@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-raw@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-react-refresh-wrap@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + react-refresh: 0.9.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-sass@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + sass: 1.77.8 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-svg-react@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@svgr/core': 6.5.1 + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) + '@svgr/plugin-svgo': 6.5.1(@svgr/core@6.5.1) + transitivePeerDependencies: + - '@parcel/core' + - supports-color + + '@parcel/transformer-svg@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + nullthrows: 1.1.1 + posthtml: 0.16.6 + posthtml-parser: 0.10.2 + posthtml-render: 3.0.0 + semver: 7.6.3 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/transformer-worklet@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/types@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/cache': 2.8.3(@parcel/core@2.9.3) + '@parcel/diagnostic': 2.8.3 + '@parcel/fs': 2.8.3(@parcel/core@2.9.3) + '@parcel/package-manager': 2.8.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/workers': 2.8.3(@parcel/core@2.9.3) + utility-types: 3.11.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/types@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/cache': 2.9.3(@parcel/core@2.9.3) + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + utility-types: 3.11.0 + transitivePeerDependencies: + - '@parcel/core' + + '@parcel/utils@2.8.3': + dependencies: + '@parcel/codeframe': 2.8.3 + '@parcel/diagnostic': 2.8.3 + '@parcel/hash': 2.8.3 + '@parcel/logger': 2.8.3 + '@parcel/markdown-ansi': 2.8.3 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + + '@parcel/utils@2.9.3': + dependencies: + '@parcel/codeframe': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/markdown-ansi': 2.9.3 + '@parcel/source-map': 2.1.1 + chalk: 4.1.2 + nullthrows: 1.1.1 + + '@parcel/watcher-android-arm64@2.2.0': + optional: true + + '@parcel/watcher-darwin-arm64@2.2.0': + optional: true + + '@parcel/watcher-darwin-x64@2.2.0': + optional: true + + '@parcel/watcher-linux-arm-glibc@2.2.0': + optional: true + + '@parcel/watcher-linux-arm64-glibc@2.2.0': + optional: true + + '@parcel/watcher-linux-arm64-musl@2.2.0': + optional: true + + '@parcel/watcher-linux-x64-glibc@2.2.0': + optional: true + + '@parcel/watcher-linux-x64-musl@2.2.0': + optional: true + + '@parcel/watcher-win32-arm64@2.2.0': + optional: true + + '@parcel/watcher-win32-x64@2.2.0': + optional: true + + '@parcel/watcher@2.2.0': + dependencies: + detect-libc: 1.0.3 + is-glob: 4.0.3 + micromatch: 4.0.8 + node-addon-api: 7.1.1 + optionalDependencies: + '@parcel/watcher-android-arm64': 2.2.0 + '@parcel/watcher-darwin-arm64': 2.2.0 + '@parcel/watcher-darwin-x64': 2.2.0 + '@parcel/watcher-linux-arm-glibc': 2.2.0 + '@parcel/watcher-linux-arm64-glibc': 2.2.0 + '@parcel/watcher-linux-arm64-musl': 2.2.0 + '@parcel/watcher-linux-x64-glibc': 2.2.0 + '@parcel/watcher-linux-x64-musl': 2.2.0 + '@parcel/watcher-win32-arm64': 2.2.0 + '@parcel/watcher-win32-x64': 2.2.0 + + '@parcel/workers@2.8.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.8.3 + '@parcel/logger': 2.8.3 + '@parcel/types': 2.8.3(@parcel/core@2.9.3) + '@parcel/utils': 2.8.3 + chrome-trace-event: 1.0.4 + nullthrows: 1.1.1 + + '@parcel/workers@2.9.3(@parcel/core@2.9.3)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/profiler': 2.9.3 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@plasmohq/consolidate@0.17.0(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + bluebird: 3.7.2 + optionalDependencies: + lodash: 4.17.21 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + + '@plasmohq/init@0.7.0': {} + + '@plasmohq/messaging@0.6.2(react@18.2.0)': + dependencies: + nanoid: 5.0.3 + optionalDependencies: + react: 18.2.0 + + '@plasmohq/parcel-bundler@0.5.6': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + + '@plasmohq/parcel-compressor-utf8@0.1.1(@parcel/core@2.9.3)': + dependencies: + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + transitivePeerDependencies: + - '@parcel/core' + + '@plasmohq/parcel-config@0.41.1(@swc/core@1.7.14(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(lodash@4.17.21)(postcss@8.4.41)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2)': + dependencies: + '@parcel/compressor-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/config-default': 2.9.3(@parcel/core@2.9.3)(@swc/helpers@0.5.12)(postcss@8.4.41)(typescript@5.2.2) + '@parcel/core': 2.9.3 + '@parcel/optimizer-data-url': 2.9.3(@parcel/core@2.9.3) + '@parcel/reporter-bundle-buddy': 2.9.3(@parcel/core@2.9.3) + '@parcel/resolver-default': 2.9.3(@parcel/core@2.9.3) + '@parcel/runtime-js': 2.8.3(@parcel/core@2.9.3) + '@parcel/runtime-service-worker': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/transformer-babel': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-css': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-graphql': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-inline-string': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-js': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-less': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-postcss': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-raw': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-react-refresh-wrap': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-sass': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-svg-react': 2.9.3(@parcel/core@2.9.3) + '@parcel/transformer-worklet': 2.9.3(@parcel/core@2.9.3) + '@plasmohq/parcel-bundler': 0.5.6 + '@plasmohq/parcel-compressor-utf8': 0.1.1(@parcel/core@2.9.3) + '@plasmohq/parcel-namer-manifest': 0.3.12 + '@plasmohq/parcel-optimizer-encapsulate': 0.0.8 + '@plasmohq/parcel-optimizer-es': 0.4.1(@swc/helpers@0.5.12) + '@plasmohq/parcel-packager': 0.6.15 + '@plasmohq/parcel-resolver': 0.14.1 + '@plasmohq/parcel-resolver-post': 0.4.5(@swc/core@1.7.14(@swc/helpers@0.5.12))(postcss@8.4.41) + '@plasmohq/parcel-runtime': 0.25.1 + '@plasmohq/parcel-transformer-inject-env': 0.2.12 + '@plasmohq/parcel-transformer-inline-css': 0.3.11 + '@plasmohq/parcel-transformer-manifest': 0.20.1 + '@plasmohq/parcel-transformer-svelte': 0.6.0 + '@plasmohq/parcel-transformer-vue': 0.5.0(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - arc-templates + - atpl + - babel-core + - bracket-template + - coffeescript + - cssnano + - dot + - eco + - ect + - ejs + - haml-coffee + - hamlet + - hamljs + - handlebars + - hogan.js + - htmling + - jazz + - jqtpl + - just + - liquid + - liquor + - lodash + - marko + - mote + - mustache + - nunjucks + - plates + - postcss + - pug + - purgecss + - qejs + - ractive + - razor-tmpl + - react + - react-dom + - relateurl + - slm + - squirrelly + - srcset + - supports-color + - teacup + - templayed + - terser + - then-pug + - tinyliquid + - toffee + - ts-node + - twig + - twing + - typescript + - uncss + - underscore + - vash + - velocityjs + - walrus + - whiskers + + '@plasmohq/parcel-core@0.1.10': + dependencies: + '@parcel/cache': 2.9.3(@parcel/core@2.9.3) + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/events': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/graph': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/logger': 2.9.3 + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@parcel/watcher': 2.2.0 + '@parcel/workers': 2.9.3(@parcel/core@2.9.3) + abortcontroller-polyfill: 1.7.5 + nullthrows: 1.1.1 + + '@plasmohq/parcel-namer-manifest@0.3.12': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + + '@plasmohq/parcel-optimizer-encapsulate@0.0.8': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + + '@plasmohq/parcel-optimizer-es@0.4.1(@swc/helpers@0.5.12)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + '@swc/core': 1.3.96(@swc/helpers@0.5.12) + nullthrows: 1.1.1 + transitivePeerDependencies: + - '@swc/helpers' + + '@plasmohq/parcel-packager@0.6.15': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + nullthrows: 1.1.1 + + '@plasmohq/parcel-resolver-post@0.4.5(@swc/core@1.7.14(@swc/helpers@0.5.12))(postcss@8.4.41)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + tsup: 7.2.0(@swc/core@1.7.14(@swc/helpers@0.5.12))(postcss@8.4.41)(typescript@5.2.2) + typescript: 5.2.2 + transitivePeerDependencies: + - '@swc/core' + - postcss + - supports-color + - ts-node + + '@plasmohq/parcel-resolver@0.14.1': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/hash': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + fast-glob: 3.3.2 + fs-extra: 11.1.1 + got: 13.0.0 + + '@plasmohq/parcel-runtime@0.25.1': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@types/trusted-types': 2.0.7 + react-refresh: 0.14.0 + + '@plasmohq/parcel-transformer-inject-env@0.2.12': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + + '@plasmohq/parcel-transformer-inline-css@0.3.11': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + browserslist: 4.22.1 + lightningcss: 1.21.8 + + '@plasmohq/parcel-transformer-manifest@0.20.1': + dependencies: + '@mischnic/json-sourcemap': 0.1.0 + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + content-security-policy-parser: 0.4.1 + json-schema-to-ts: 2.9.2 + nullthrows: 1.1.1 + + '@plasmohq/parcel-transformer-svelte@0.6.0': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/utils': 2.9.3 + svelte: 4.2.2 + + '@plasmohq/parcel-transformer-vue@0.5.0(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@parcel/core': 2.9.3 + '@parcel/diagnostic': 2.9.3 + '@parcel/plugin': 2.9.3(@parcel/core@2.9.3) + '@parcel/source-map': 2.1.1 + '@parcel/types': 2.9.3(@parcel/core@2.9.3) + '@parcel/utils': 2.9.3 + '@plasmohq/consolidate': 0.17.0(lodash@4.17.21)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@vue/compiler-sfc': 3.3.4 + nullthrows: 1.1.1 + semver: 7.5.4 + vue: 3.3.4 + transitivePeerDependencies: + - arc-templates + - atpl + - babel-core + - bracket-template + - coffeescript + - dot + - eco + - ect + - ejs + - haml-coffee + - hamlet + - hamljs + - handlebars + - hogan.js + - htmling + - jazz + - jqtpl + - just + - liquid + - liquor + - lodash + - marko + - mote + - mustache + - nunjucks + - plates + - pug + - qejs + - ractive + - razor-tmpl + - react + - react-dom + - slm + - squirrelly + - teacup + - templayed + - then-pug + - tinyliquid + - toffee + - twig + - twing + - underscore + - vash + - velocityjs + - walrus + - whiskers + + '@plasmohq/storage@1.11.0(react@18.2.0)': + dependencies: + pify: 6.1.0 + optionalDependencies: + react: 18.2.0 + + '@pnpm/config.env-replace@1.1.0': {} + + '@pnpm/network.ca-file@1.0.2': + dependencies: + graceful-fs: 4.2.10 + + '@pnpm/npm-conf@2.3.1': + dependencies: + '@pnpm/config.env-replace': 1.1.0 + '@pnpm/network.ca-file': 1.0.2 + config-chain: 1.1.13 + + '@radix-ui/number@1.1.0': {} + + '@radix-ui/primitive@1.1.0': {} + + '@radix-ui/primitive@1.1.1': {} + + '@radix-ui/react-accessible-icon@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-accordion@1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collapsible': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-alert-dialog@1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dialog': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-arrow@1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-arrow@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-aspect-ratio@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-avatar@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-checkbox@1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-collapsible@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-collection@1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-collection@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-compose-refs@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-compose-refs@1.1.1(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-context-menu@2.2.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-menu': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-context@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-context@1.1.1(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-dialog@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.0(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-dialog@1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-direction@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-dismissable-layer@1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-dismissable-layer@1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-dropdown-menu@2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-menu': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-focus-guards@1.1.1(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-focus-scope@1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-focus-scope@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-hover-card@1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-icons@1.3.2(react@18.2.0)': + dependencies: + react: 18.2.0 + + '@radix-ui/react-id@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-label@2.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-menu@2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-navigation-menu@1.2.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-popover@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.0(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-popover@1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-popper@1.2.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-arrow': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/rect': 1.1.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-popper@1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@floating-ui/react-dom': 2.1.2(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-arrow': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-rect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/rect': 1.1.0 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-portal@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-portal@1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-presence@1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-presence@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-primitive@2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-slot': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-primitive@2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-progress@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-radio-group@1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-roving-focus@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-scroll-area@1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-select@2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-focus-guards': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-focus-scope': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + aria-hidden: 1.2.4 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-remove-scroll: 2.6.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-separator@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-slider@1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/number': 1.1.0 + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-slot@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-slot@1.1.2(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-switch@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-previous': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-size': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-tabs@1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-toast@1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.0 + '@radix-ui/react-collection': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-toast@1.2.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-collection': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-toggle-group@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-toggle@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-toolbar@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-roving-focus': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-separator': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle-group': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-tooltip@1.1.8(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/primitive': 1.1.1 + '@radix-ui/react-compose-refs': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-context': 1.1.1(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dismissable-layer': 1.1.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-popper': 1.2.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-presence': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-use-callback-ref@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-controllable-state@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-layout-effect@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-previous@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-rect@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/rect': 1.1.0 + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-use-size@1.1.0(@types/react@18.2.48)(react@18.2.0)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@18.2.48)(react@18.2.0) + react: 18.2.0 + optionalDependencies: + '@types/react': 18.2.48 + + '@radix-ui/react-visually-hidden@1.1.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/react-visually-hidden@1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)': + dependencies: + '@radix-ui/react-primitive': 2.0.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + '@types/react-dom': 18.2.18 + + '@radix-ui/rect@1.1.0': {} + + '@remix-run/router@1.19.1': {} + + '@sindresorhus/is@5.6.0': {} + + '@svgr/babel-plugin-add-jsx-attribute@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-remove-jsx-attribute@8.0.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-remove-jsx-empty-expression@8.0.0(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-replace-jsx-attribute-value@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-svg-dynamic-title@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-svg-em-dimensions@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-transform-react-native-svg@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-plugin-transform-svg-component@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + + '@svgr/babel-preset@6.5.1(@babel/core@7.25.2)': + dependencies: + '@babel/core': 7.25.2 + '@svgr/babel-plugin-add-jsx-attribute': 6.5.1(@babel/core@7.25.2) + '@svgr/babel-plugin-remove-jsx-attribute': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-remove-jsx-empty-expression': 8.0.0(@babel/core@7.25.2) + '@svgr/babel-plugin-replace-jsx-attribute-value': 6.5.1(@babel/core@7.25.2) + '@svgr/babel-plugin-svg-dynamic-title': 6.5.1(@babel/core@7.25.2) + '@svgr/babel-plugin-svg-em-dimensions': 6.5.1(@babel/core@7.25.2) + '@svgr/babel-plugin-transform-react-native-svg': 6.5.1(@babel/core@7.25.2) + '@svgr/babel-plugin-transform-svg-component': 6.5.1(@babel/core@7.25.2) + + '@svgr/core@6.5.1': + dependencies: + '@babel/core': 7.25.2 + '@svgr/babel-preset': 6.5.1(@babel/core@7.25.2) + '@svgr/plugin-jsx': 6.5.1(@svgr/core@6.5.1) + camelcase: 6.3.0 + cosmiconfig: 7.1.0 + transitivePeerDependencies: + - supports-color + + '@svgr/hast-util-to-babel-ast@6.5.1': + dependencies: + '@babel/types': 7.25.4 + entities: 4.5.0 + + '@svgr/plugin-jsx@6.5.1(@svgr/core@6.5.1)': + dependencies: + '@babel/core': 7.25.2 + '@svgr/babel-preset': 6.5.1(@babel/core@7.25.2) + '@svgr/core': 6.5.1 + '@svgr/hast-util-to-babel-ast': 6.5.1 + svg-parser: 2.0.4 + transitivePeerDependencies: + - supports-color + + '@svgr/plugin-svgo@6.5.1(@svgr/core@6.5.1)': + dependencies: + '@svgr/core': 6.5.1 + cosmiconfig: 7.1.0 + deepmerge: 4.3.1 + svgo: 2.8.0 + + '@swc/core-darwin-arm64@1.3.96': + optional: true + + '@swc/core-darwin-arm64@1.7.14': + optional: true + + '@swc/core-darwin-x64@1.3.96': + optional: true + + '@swc/core-darwin-x64@1.7.14': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.3.96': + optional: true + + '@swc/core-linux-arm-gnueabihf@1.7.14': + optional: true + + '@swc/core-linux-arm64-gnu@1.3.96': + optional: true + + '@swc/core-linux-arm64-gnu@1.7.14': + optional: true + + '@swc/core-linux-arm64-musl@1.3.96': + optional: true + + '@swc/core-linux-arm64-musl@1.7.14': + optional: true + + '@swc/core-linux-x64-gnu@1.3.96': + optional: true + + '@swc/core-linux-x64-gnu@1.7.14': + optional: true + + '@swc/core-linux-x64-musl@1.3.96': + optional: true + + '@swc/core-linux-x64-musl@1.7.14': + optional: true + + '@swc/core-win32-arm64-msvc@1.3.96': + optional: true + + '@swc/core-win32-arm64-msvc@1.7.14': + optional: true + + '@swc/core-win32-ia32-msvc@1.3.96': + optional: true + + '@swc/core-win32-ia32-msvc@1.7.14': + optional: true + + '@swc/core-win32-x64-msvc@1.3.96': + optional: true + + '@swc/core-win32-x64-msvc@1.7.14': + optional: true + + '@swc/core@1.3.96(@swc/helpers@0.5.12)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.12 + optionalDependencies: + '@swc/core-darwin-arm64': 1.3.96 + '@swc/core-darwin-x64': 1.3.96 + '@swc/core-linux-arm-gnueabihf': 1.3.96 + '@swc/core-linux-arm64-gnu': 1.3.96 + '@swc/core-linux-arm64-musl': 1.3.96 + '@swc/core-linux-x64-gnu': 1.3.96 + '@swc/core-linux-x64-musl': 1.3.96 + '@swc/core-win32-arm64-msvc': 1.3.96 + '@swc/core-win32-ia32-msvc': 1.3.96 + '@swc/core-win32-x64-msvc': 1.3.96 + '@swc/helpers': 0.5.12 + + '@swc/core@1.7.14(@swc/helpers@0.5.12)': + dependencies: + '@swc/counter': 0.1.3 + '@swc/types': 0.1.12 + optionalDependencies: + '@swc/core-darwin-arm64': 1.7.14 + '@swc/core-darwin-x64': 1.7.14 + '@swc/core-linux-arm-gnueabihf': 1.7.14 + '@swc/core-linux-arm64-gnu': 1.7.14 + '@swc/core-linux-arm64-musl': 1.7.14 + '@swc/core-linux-x64-gnu': 1.7.14 + '@swc/core-linux-x64-musl': 1.7.14 + '@swc/core-win32-arm64-msvc': 1.7.14 + '@swc/core-win32-ia32-msvc': 1.7.14 + '@swc/core-win32-x64-msvc': 1.7.14 + '@swc/helpers': 0.5.12 + + '@swc/counter@0.1.3': {} + + '@swc/helpers@0.5.12': + dependencies: + tslib: 2.7.0 + + '@swc/types@0.1.12': + dependencies: + '@swc/counter': 0.1.3 + + '@szmarczak/http-timer@5.0.1': + dependencies: + defer-to-connect: 2.0.1 + + '@trysound/sax@0.2.0': {} + + '@types/chrome@0.0.258': + dependencies: + '@types/filesystem': 0.0.36 + '@types/har-format': 1.2.15 + + '@types/estree@1.0.5': {} + + '@types/filesystem@0.0.36': + dependencies: + '@types/filewriter': 0.0.33 + + '@types/filewriter@0.0.33': {} + + '@types/har-format@1.2.15': {} + + '@types/http-cache-semantics@4.0.4': {} + + '@types/json-schema@7.0.15': {} + + '@types/node@20.11.5': + dependencies: + undici-types: 5.26.5 + + '@types/parse-json@4.0.2': {} + + '@types/prop-types@15.7.12': {} + + '@types/react-dom@18.2.18': + dependencies: + '@types/react': 18.2.48 + + '@types/react@18.2.48': + dependencies: + '@types/prop-types': 15.7.12 + '@types/scheduler': 0.23.0 + csstype: 3.1.3 + + '@types/scheduler@0.23.0': {} + + '@types/trusted-types@2.0.7': {} + + '@ungap/event-target@0.2.4': {} + + '@ungap/event@0.2.2': {} + + '@vue/compiler-core@3.3.4': + dependencies: + '@babel/parser': 7.25.4 + '@vue/shared': 3.3.4 + estree-walker: 2.0.2 + source-map-js: 1.2.0 + + '@vue/compiler-dom@3.3.4': + dependencies: + '@vue/compiler-core': 3.3.4 + '@vue/shared': 3.3.4 + + '@vue/compiler-sfc@3.3.4': + dependencies: + '@babel/parser': 7.25.4 + '@vue/compiler-core': 3.3.4 + '@vue/compiler-dom': 3.3.4 + '@vue/compiler-ssr': 3.3.4 + '@vue/reactivity-transform': 3.3.4 + '@vue/shared': 3.3.4 + estree-walker: 2.0.2 + magic-string: 0.30.11 + postcss: 8.4.41 + source-map-js: 1.2.0 + + '@vue/compiler-ssr@3.3.4': + dependencies: + '@vue/compiler-dom': 3.3.4 + '@vue/shared': 3.3.4 + + '@vue/reactivity-transform@3.3.4': + dependencies: + '@babel/parser': 7.25.4 + '@vue/compiler-core': 3.3.4 + '@vue/shared': 3.3.4 + estree-walker: 2.0.2 + magic-string: 0.30.11 + + '@vue/reactivity@3.3.4': + dependencies: + '@vue/shared': 3.3.4 + + '@vue/runtime-core@3.3.4': + dependencies: + '@vue/reactivity': 3.3.4 + '@vue/shared': 3.3.4 + + '@vue/runtime-dom@3.3.4': + dependencies: + '@vue/runtime-core': 3.3.4 + '@vue/shared': 3.3.4 + csstype: 3.1.3 + + '@vue/server-renderer@3.3.4(vue@3.3.4)': + dependencies: + '@vue/compiler-ssr': 3.3.4 + '@vue/shared': 3.3.4 + vue: 3.3.4 + + '@vue/shared@3.3.4': {} + + abortcontroller-polyfill@1.7.5: {} + + acorn@8.12.1: {} + + ansi-escapes@4.3.2: + dependencies: + type-fest: 0.21.3 + + ansi-regex@5.0.1: {} + + ansi-regex@6.0.1: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + any-promise@1.3.0: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + arg@5.0.2: {} + + argparse@2.0.1: {} + + aria-hidden@1.2.4: + dependencies: + tslib: 2.7.0 + + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + + array-union@2.1.0: {} + + autoprefixer@10.4.20(postcss@8.4.41): + dependencies: + browserslist: 4.23.3 + caniuse-lite: 1.0.30001651 + fraction.js: 4.3.7 + normalize-range: 0.1.2 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + + axobject-query@3.2.4: {} + + b4a@1.6.6: {} + + balanced-match@1.0.2: {} + + bare-events@2.4.2: + optional: true + + bare-fs@2.3.1: + dependencies: + bare-events: 2.4.2 + bare-path: 2.1.3 + bare-stream: 2.1.3 + optional: true + + bare-os@2.4.0: + optional: true + + bare-path@2.1.3: + dependencies: + bare-os: 2.4.0 + optional: true + + bare-stream@2.1.3: + dependencies: + streamx: 2.19.0 + optional: true + + base-x@3.0.10: + dependencies: + safe-buffer: 5.2.1 + + base64-js@1.5.1: {} + + binary-extensions@2.3.0: {} + + bl@4.1.0: + dependencies: + buffer: 5.7.1 + inherits: 2.0.4 + readable-stream: 3.6.2 + + bluebird@3.7.2: {} + + boolbase@1.0.0: {} + + brace-expansion@2.0.1: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browserslist@4.22.1: + dependencies: + caniuse-lite: 1.0.30001651 + electron-to-chromium: 1.5.13 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.22.1) + + browserslist@4.23.3: + dependencies: + caniuse-lite: 1.0.30001651 + electron-to-chromium: 1.5.13 + node-releases: 2.0.18 + update-browserslist-db: 1.1.0(browserslist@4.23.3) + + buffer@5.7.1: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + buffer@6.0.3: + dependencies: + base64-js: 1.5.1 + ieee754: 1.2.1 + + bundle-require@4.2.1(esbuild@0.18.20): + dependencies: + esbuild: 0.18.20 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + cacheable-lookup@7.0.0: {} + + cacheable-request@10.2.14: + dependencies: + '@types/http-cache-semantics': 4.0.4 + get-stream: 6.0.1 + http-cache-semantics: 4.1.1 + keyv: 4.5.4 + mimic-response: 4.0.0 + normalize-url: 8.0.1 + responselike: 3.0.0 + + call-bind@1.0.7: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + set-function-length: 1.2.2 + + callsites@3.1.0: {} + + camelcase-css@2.0.1: {} + + camelcase@6.3.0: {} + + caniuse-lite@1.0.30001651: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.3.0: {} + + change-case@5.1.2: {} + + chardet@0.7.0: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + chownr@1.1.4: {} + + chrome-trace-event@1.0.4: {} + + class-variance-authority@0.7.0: + dependencies: + clsx: 2.0.0 + + cli-cursor@3.1.0: + dependencies: + restore-cursor: 3.1.0 + + cli-spinners@2.9.2: {} + + cli-width@4.1.0: {} + + clone@1.0.4: {} + + clone@2.1.2: {} + + clsx@2.0.0: {} + + clsx@2.1.1: {} + + cmdk@1.0.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@radix-ui/react-dialog': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-id': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-primitive': 2.0.0(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + use-sync-external-store: 1.2.2(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + code-red@1.0.4: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + '@types/estree': 1.0.5 + acorn: 8.12.1 + estree-walker: 3.0.3 + periscopic: 3.1.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + color-string@1.9.1: + dependencies: + color-name: 1.1.4 + simple-swizzle: 0.2.2 + + color@4.2.3: + dependencies: + color-convert: 2.0.1 + color-string: 1.9.1 + + commander@4.1.1: {} + + commander@7.2.0: {} + + config-chain@1.1.13: + dependencies: + ini: 1.3.8 + proto-list: 1.2.4 + + content-security-policy-parser@0.4.1: {} + + convert-source-map@2.0.0: {} + + copy-anything@2.0.6: + dependencies: + is-what: 3.14.1 + + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + + cosmiconfig@9.0.0(typescript@5.2.2): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.2.2 + + cosmiconfig@9.0.0(typescript@5.3.3): + dependencies: + env-paths: 2.2.1 + import-fresh: 3.3.0 + js-yaml: 4.1.0 + parse-json: 5.2.0 + optionalDependencies: + typescript: 5.3.3 + + cross-spawn@7.0.3: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + crypto-random-string@4.0.0: + dependencies: + type-fest: 1.4.0 + + css-select@3.1.2: + dependencies: + boolbase: 1.0.0 + css-what: 4.0.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-select@4.3.0: + dependencies: + boolbase: 1.0.0 + css-what: 6.1.0 + domhandler: 4.3.1 + domutils: 2.8.0 + nth-check: 2.1.1 + + css-tree@1.1.3: + dependencies: + mdn-data: 2.0.14 + source-map: 0.6.1 + + css-tree@2.3.1: + dependencies: + mdn-data: 2.0.30 + source-map-js: 1.2.0 + + css-what@4.0.0: {} + + css-what@6.1.0: {} + + cssesc@3.0.0: {} + + csso@4.2.0: + dependencies: + css-tree: 1.1.3 + + csstype@3.1.3: {} + + debug@4.3.6: + dependencies: + ms: 2.1.2 + + decompress-response@6.0.0: + dependencies: + mimic-response: 3.1.0 + + deep-extend@0.6.0: {} + + deepmerge@4.3.1: {} + + defaults@1.0.4: + dependencies: + clone: 1.0.4 + + defer-to-connect@2.0.1: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.0 + es-errors: 1.3.0 + gopd: 1.0.1 + + dequal@2.0.3: {} + + detect-libc@1.0.3: {} + + detect-libc@2.0.3: {} + + detect-node-es@1.1.0: {} + + didyoumean@1.2.2: {} + + dir-glob@3.0.1: + dependencies: + path-type: 4.0.0 + + dlv@1.1.3: {} + + dom-serializer@1.4.1: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + entities: 2.2.0 + + dom-to-semantic-markdown@1.2.11: {} + + domelementtype@2.3.0: {} + + domhandler@4.3.1: + dependencies: + domelementtype: 2.3.0 + + domutils@2.8.0: + dependencies: + dom-serializer: 1.4.1 + domelementtype: 2.3.0 + domhandler: 4.3.1 + + dotenv-expand@10.0.0: {} + + dotenv-expand@5.1.0: {} + + dotenv@16.3.1: {} + + dotenv@7.0.0: {} + + eastasianwidth@0.2.0: {} + + electron-to-chromium@1.5.13: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + end-of-stream@1.4.4: + dependencies: + once: 1.4.0 + + entities@2.2.0: {} + + entities@3.0.1: {} + + entities@4.5.0: {} + + env-paths@2.2.1: {} + + errno@0.1.8: + dependencies: + prr: 1.0.1 + optional: true + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-define-property@1.0.0: + dependencies: + get-intrinsic: 1.2.4 + + es-errors@1.3.0: {} + + esbuild@0.18.20: + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + + escalade@3.1.2: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@5.0.0: {} + + estree-walker@2.0.2: {} + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.5 + + events@3.3.0: {} + + execa@5.1.1: + dependencies: + cross-spawn: 7.0.3 + get-stream: 6.0.1 + human-signals: 2.1.0 + is-stream: 2.0.1 + merge-stream: 2.0.0 + npm-run-path: 4.0.1 + onetime: 5.1.2 + signal-exit: 3.0.7 + strip-final-newline: 2.0.0 + + expand-template@2.0.3: {} + + external-editor@3.1.0: + dependencies: + chardet: 0.7.0 + iconv-lite: 0.4.24 + tmp: 0.0.33 + + fast-fifo@1.3.2: {} + + fast-glob@3.3.2: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fastq@1.17.1: + dependencies: + reusify: 1.0.4 + + fflate@0.8.1: {} + + figures@5.0.0: + dependencies: + escape-string-regexp: 5.0.0 + is-unicode-supported: 1.3.0 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + foreground-child@3.3.0: + dependencies: + cross-spawn: 7.0.3 + signal-exit: 4.1.0 + + form-data-encoder@2.1.4: {} + + fraction.js@4.3.7: {} + + fs-constants@1.0.0: {} + + fs-extra@11.1.1: + dependencies: + graceful-fs: 4.2.11 + jsonfile: 6.1.0 + universalify: 2.0.1 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + gensync@1.0.0-beta.2: {} + + get-intrinsic@1.2.4: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + has-proto: 1.0.3 + has-symbols: 1.0.3 + hasown: 2.0.2 + + get-nonce@1.0.1: {} + + get-port@7.0.0: {} + + get-stream@6.0.1: {} + + github-from-package@0.0.0: {} + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.0 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.0 + path-scurry: 1.11.1 + + globals@11.12.0: {} + + globals@13.24.0: + dependencies: + type-fest: 0.20.2 + + globby@11.1.0: + dependencies: + array-union: 2.1.0 + dir-glob: 3.0.1 + fast-glob: 3.3.2 + ignore: 5.2.4 + merge2: 1.4.1 + slash: 3.0.0 + + gopd@1.0.1: + dependencies: + get-intrinsic: 1.2.4 + + got@12.6.1: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + + got@13.0.0: + dependencies: + '@sindresorhus/is': 5.6.0 + '@szmarczak/http-timer': 5.0.1 + cacheable-lookup: 7.0.0 + cacheable-request: 10.2.14 + decompress-response: 6.0.0 + form-data-encoder: 2.1.4 + get-stream: 6.0.1 + http2-wrapper: 2.2.1 + lowercase-keys: 3.0.0 + p-cancelable: 3.0.0 + responselike: 3.0.0 + + graceful-fs@4.2.10: {} + + graceful-fs@4.2.11: {} + + graphql-import-macro@1.0.0: + dependencies: + graphql: 15.9.0 + + graphql@15.9.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.0 + + has-proto@1.0.3: {} + + has-symbols@1.0.3: {} + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + html-escaper@3.0.3: {} + + htmlnano@2.1.1(postcss@8.4.41)(svgo@2.8.0)(typescript@5.2.2): + dependencies: + cosmiconfig: 9.0.0(typescript@5.2.2) + posthtml: 0.16.6 + timsort: 0.3.0 + optionalDependencies: + postcss: 8.4.41 + svgo: 2.8.0 + transitivePeerDependencies: + - typescript + + htmlparser2@6.1.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 2.2.0 + + htmlparser2@7.2.0: + dependencies: + domelementtype: 2.3.0 + domhandler: 4.3.1 + domutils: 2.8.0 + entities: 3.0.1 + + http-cache-semantics@4.1.1: {} + + http2-wrapper@2.2.1: + dependencies: + quick-lru: 5.1.1 + resolve-alpn: 1.2.1 + + human-signals@2.1.0: {} + + iconv-lite@0.4.24: + dependencies: + safer-buffer: 2.1.2 + + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + optional: true + + ieee754@1.2.1: {} + + ignore@5.2.4: {} + + image-size@0.5.5: + optional: true + + immutable@4.3.7: {} + + import-fresh@3.3.0: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + inherits@2.0.4: {} + + ini@1.3.8: {} + + inquirer@9.2.12: + dependencies: + '@ljharb/through': 2.3.13 + ansi-escapes: 4.3.2 + chalk: 5.3.0 + cli-cursor: 3.1.0 + cli-width: 4.1.0 + external-editor: 3.1.0 + figures: 5.0.0 + lodash: 4.17.21 + mute-stream: 1.0.0 + ora: 5.4.1 + run-async: 3.0.0 + rxjs: 7.8.1 + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 6.2.0 + + invariant@2.2.4: + dependencies: + loose-envify: 1.4.0 + + is-arrayish@0.2.1: {} + + is-arrayish@0.3.2: {} + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-core-module@2.15.1: + dependencies: + hasown: 2.0.2 + + is-extglob@2.1.1: {} + + is-fullwidth-code-point@3.0.0: {} + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-interactive@1.0.0: {} + + is-json@2.0.1: {} + + is-number@7.0.0: {} + + is-path-inside@4.0.0: {} + + is-reference@3.0.2: + dependencies: + '@types/estree': 1.0.5 + + is-stream@2.0.1: {} + + is-stream@3.0.0: {} + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-what@3.14.1: {} + + isbinaryfile@4.0.10: {} + + isexe@2.0.0: {} + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + jiti@1.21.6: {} + + joycon@3.1.1: {} + + js-tokens@4.0.0: {} + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + jsesc@2.5.2: {} + + json-buffer@3.0.1: {} + + json-parse-even-better-errors@2.3.1: {} + + json-schema-to-ts@2.9.2: + dependencies: + '@babel/runtime': 7.25.4 + '@types/json-schema': 7.0.15 + ts-algebra: 1.2.2 + + json5@2.2.3: {} + + jsonfile@6.1.0: + dependencies: + universalify: 2.0.1 + optionalDependencies: + graceful-fs: 4.2.11 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + less@4.2.0: + dependencies: + copy-anything: 2.0.6 + parse-node-version: 1.0.1 + tslib: 2.7.0 + optionalDependencies: + errno: 0.1.8 + graceful-fs: 4.2.11 + image-size: 0.5.5 + make-dir: 2.1.0 + mime: 1.6.0 + needle: 3.3.1 + source-map: 0.6.1 + + lightningcss-darwin-arm64@1.21.8: + optional: true + + lightningcss-darwin-arm64@1.26.0: + optional: true + + lightningcss-darwin-x64@1.21.8: + optional: true + + lightningcss-darwin-x64@1.26.0: + optional: true + + lightningcss-freebsd-x64@1.21.8: + optional: true + + lightningcss-freebsd-x64@1.26.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.21.8: + optional: true + + lightningcss-linux-arm-gnueabihf@1.26.0: + optional: true + + lightningcss-linux-arm64-gnu@1.21.8: + optional: true + + lightningcss-linux-arm64-gnu@1.26.0: + optional: true + + lightningcss-linux-arm64-musl@1.21.8: + optional: true + + lightningcss-linux-arm64-musl@1.26.0: + optional: true + + lightningcss-linux-x64-gnu@1.21.8: + optional: true + + lightningcss-linux-x64-gnu@1.26.0: + optional: true + + lightningcss-linux-x64-musl@1.21.8: + optional: true + + lightningcss-linux-x64-musl@1.26.0: + optional: true + + lightningcss-win32-arm64-msvc@1.26.0: + optional: true + + lightningcss-win32-x64-msvc@1.21.8: + optional: true + + lightningcss-win32-x64-msvc@1.26.0: + optional: true + + lightningcss@1.21.8: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.21.8 + lightningcss-darwin-x64: 1.21.8 + lightningcss-freebsd-x64: 1.21.8 + lightningcss-linux-arm-gnueabihf: 1.21.8 + lightningcss-linux-arm64-gnu: 1.21.8 + lightningcss-linux-arm64-musl: 1.21.8 + lightningcss-linux-x64-gnu: 1.21.8 + lightningcss-linux-x64-musl: 1.21.8 + lightningcss-win32-x64-msvc: 1.21.8 + + lightningcss@1.26.0: + dependencies: + detect-libc: 1.0.3 + optionalDependencies: + lightningcss-darwin-arm64: 1.26.0 + lightningcss-darwin-x64: 1.26.0 + lightningcss-freebsd-x64: 1.26.0 + lightningcss-linux-arm-gnueabihf: 1.26.0 + lightningcss-linux-arm64-gnu: 1.26.0 + lightningcss-linux-arm64-musl: 1.26.0 + lightningcss-linux-x64-gnu: 1.26.0 + lightningcss-linux-x64-musl: 1.26.0 + lightningcss-win32-arm64-msvc: 1.26.0 + lightningcss-win32-x64-msvc: 1.26.0 + + lilconfig@2.1.0: {} + + lilconfig@3.1.2: {} + + lines-and-columns@1.2.4: {} + + linkedom@0.1.34: + dependencies: + '@ungap/event': 0.2.2 + '@ungap/event-target': 0.2.4 + css-select: 3.1.2 + html-escaper: 3.0.3 + htmlparser2: 6.1.0 + uhyphen: 0.1.0 + + lmdb@2.5.2: + dependencies: + msgpackr: 1.11.0 + node-addon-api: 4.3.0 + node-gyp-build-optional-packages: 5.0.3 + ordered-binary: 1.5.1 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 2.5.2 + '@lmdb/lmdb-darwin-x64': 2.5.2 + '@lmdb/lmdb-linux-arm': 2.5.2 + '@lmdb/lmdb-linux-arm64': 2.5.2 + '@lmdb/lmdb-linux-x64': 2.5.2 + '@lmdb/lmdb-win32-x64': 2.5.2 + + lmdb@2.7.11: + dependencies: + msgpackr: 1.8.5 + node-addon-api: 4.3.0 + node-gyp-build-optional-packages: 5.0.6 + ordered-binary: 1.5.1 + weak-lru-cache: 1.2.2 + optionalDependencies: + '@lmdb/lmdb-darwin-arm64': 2.7.11 + '@lmdb/lmdb-darwin-x64': 2.7.11 + '@lmdb/lmdb-linux-arm': 2.7.11 + '@lmdb/lmdb-linux-arm64': 2.7.11 + '@lmdb/lmdb-linux-x64': 2.7.11 + '@lmdb/lmdb-win32-x64': 2.7.11 + + load-tsconfig@0.2.5: {} + + locate-character@3.0.0: {} + + lodash.sortby@4.7.0: {} + + lodash@4.17.21: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + loose-envify@1.4.0: + dependencies: + js-tokens: 4.0.0 + + lowercase-keys@3.0.0: {} + + lru-cache@10.4.3: {} + + lru-cache@5.1.1: + dependencies: + yallist: 3.1.1 + + lru-cache@6.0.0: + dependencies: + yallist: 4.0.0 + + lucide-react@0.454.0(react@18.2.0): + dependencies: + react: 18.2.0 + + magic-string@0.30.11: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.0 + + make-dir@2.1.0: + dependencies: + pify: 4.0.1 + semver: 5.7.2 + optional: true + + mdn-data@2.0.14: {} + + mdn-data@2.0.30: {} + + merge-stream@2.0.0: {} + + merge2@1.4.1: {} + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mime@1.6.0: + optional: true + + mime@2.6.0: {} + + mimic-fn@2.1.0: {} + + mimic-response@3.1.0: {} + + mimic-response@4.0.0: {} + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.1 + + minimist@1.2.8: {} + + minipass@7.1.2: {} + + mkdirp-classic@0.5.3: {} + + mnemonic-id@3.2.7: {} + + ms@2.1.2: {} + + msgpackr-extract@3.0.3: + dependencies: + node-gyp-build-optional-packages: 5.2.2 + optionalDependencies: + '@msgpackr-extract/msgpackr-extract-darwin-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-darwin-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-arm64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-linux-x64': 3.0.3 + '@msgpackr-extract/msgpackr-extract-win32-x64': 3.0.3 + optional: true + + msgpackr@1.11.0: + optionalDependencies: + msgpackr-extract: 3.0.3 + + msgpackr@1.8.5: + optionalDependencies: + msgpackr-extract: 3.0.3 + + mute-stream@1.0.0: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.7: {} + + nanoid@5.0.3: {} + + napi-build-utils@1.0.2: {} + + needle@3.3.1: + dependencies: + iconv-lite: 0.6.3 + sax: 1.4.1 + optional: true + + node-abi@3.67.0: + dependencies: + semver: 7.6.3 + + node-addon-api@4.3.0: {} + + node-addon-api@6.1.0: {} + + node-addon-api@7.1.1: {} + + node-gyp-build-optional-packages@5.0.3: {} + + node-gyp-build-optional-packages@5.0.6: {} + + node-gyp-build-optional-packages@5.2.2: + dependencies: + detect-libc: 2.0.3 + optional: true + + node-object-hash@3.0.0: {} + + node-releases@2.0.18: {} + + normalize-path@3.0.0: {} + + normalize-range@0.1.2: {} + + normalize-url@8.0.1: {} + + npm-run-path@4.0.1: + dependencies: + path-key: 3.1.1 + + nth-check@2.1.1: + dependencies: + boolbase: 1.0.0 + + nullthrows@1.1.1: {} + + object-assign@4.1.1: {} + + object-hash@3.0.0: {} + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@5.1.2: + dependencies: + mimic-fn: 2.1.0 + + ora@5.4.1: + dependencies: + bl: 4.1.0 + chalk: 4.1.2 + cli-cursor: 3.1.0 + cli-spinners: 2.9.2 + is-interactive: 1.0.0 + is-unicode-supported: 0.1.0 + log-symbols: 4.1.0 + strip-ansi: 6.0.1 + wcwidth: 1.0.1 + + ordered-binary@1.5.1: {} + + os-tmpdir@1.0.2: {} + + p-cancelable@3.0.0: {} + + package-json-from-dist@1.0.0: {} + + package-json@8.1.1: + dependencies: + got: 12.6.1 + registry-auth-token: 5.0.2 + registry-url: 6.0.1 + semver: 7.6.3 + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + + parse-node-version@1.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@4.0.0: {} + + periscopic@3.1.0: + dependencies: + '@types/estree': 1.0.5 + estree-walker: 3.0.3 + is-reference: 3.0.2 + + picocolors@1.0.1: {} + + picomatch@2.3.1: {} + + pify@2.3.0: {} + + pify@4.0.1: + optional: true + + pify@6.1.0: {} + + pirates@4.0.6: {} + + plasmo@0.89.4(@swc/core@1.7.14(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(lodash@4.17.21)(postcss@8.4.41)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@expo/spawn-async': 1.7.2 + '@parcel/core': 2.9.3 + '@parcel/fs': 2.9.3(@parcel/core@2.9.3) + '@parcel/package-manager': 2.9.3(@parcel/core@2.9.3) + '@parcel/watcher': 2.2.0 + '@plasmohq/init': 0.7.0 + '@plasmohq/parcel-config': 0.41.1(@swc/core@1.7.14(@swc/helpers@0.5.12))(@swc/helpers@0.5.12)(lodash@4.17.21)(postcss@8.4.41)(react-dom@18.2.0(react@18.2.0))(react@18.2.0)(typescript@5.2.2) + '@plasmohq/parcel-core': 0.1.10 + buffer: 6.0.3 + chalk: 5.3.0 + change-case: 5.1.2 + dotenv: 16.3.1 + dotenv-expand: 10.0.0 + events: 3.3.0 + fast-glob: 3.3.2 + fflate: 0.8.1 + get-port: 7.0.0 + got: 13.0.0 + ignore: 5.2.4 + inquirer: 9.2.12 + is-path-inside: 4.0.0 + json5: 2.2.3 + mnemonic-id: 3.2.7 + node-object-hash: 3.0.0 + package-json: 8.1.1 + process: 0.11.10 + semver: 7.5.4 + sharp: 0.32.6 + tempy: 3.1.0 + typescript: 5.2.2 + transitivePeerDependencies: + - '@swc/core' + - '@swc/helpers' + - arc-templates + - atpl + - babel-core + - bracket-template + - coffeescript + - cssnano + - dot + - eco + - ect + - ejs + - haml-coffee + - hamlet + - hamljs + - handlebars + - hogan.js + - htmling + - jazz + - jqtpl + - just + - liquid + - liquor + - lodash + - marko + - mote + - mustache + - nunjucks + - plates + - postcss + - pug + - purgecss + - qejs + - ractive + - razor-tmpl + - react + - react-dom + - relateurl + - slm + - squirrelly + - srcset + - supports-color + - teacup + - templayed + - terser + - then-pug + - tinyliquid + - toffee + - ts-node + - twig + - twing + - uncss + - underscore + - vash + - velocityjs + - walrus + - whiskers + + postcss-import@15.1.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-value-parser: 4.2.0 + read-cache: 1.0.0 + resolve: 1.22.8 + + postcss-js@4.0.1(postcss@8.4.41): + dependencies: + camelcase-css: 2.0.1 + postcss: 8.4.41 + + postcss-load-config@4.0.2(postcss@8.4.41): + dependencies: + lilconfig: 3.1.2 + yaml: 2.5.0 + optionalDependencies: + postcss: 8.4.41 + + postcss-loader@8.1.1(postcss@8.4.41)(typescript@5.3.3): + dependencies: + cosmiconfig: 9.0.0(typescript@5.3.3) + jiti: 1.21.6 + postcss: 8.4.41 + semver: 7.6.3 + transitivePeerDependencies: + - typescript + + postcss-nested@6.2.0(postcss@8.4.41): + dependencies: + postcss: 8.4.41 + postcss-selector-parser: 6.1.2 + + postcss-selector-parser@6.1.2: + dependencies: + cssesc: 3.0.0 + util-deprecate: 1.0.2 + + postcss-value-parser@4.2.0: {} + + postcss@8.4.41: + dependencies: + nanoid: 3.3.7 + picocolors: 1.0.1 + source-map-js: 1.2.0 + + posthtml-parser@0.10.2: + dependencies: + htmlparser2: 7.2.0 + + posthtml-parser@0.11.0: + dependencies: + htmlparser2: 7.2.0 + + posthtml-render@3.0.0: + dependencies: + is-json: 2.0.1 + + posthtml@0.16.6: + dependencies: + posthtml-parser: 0.11.0 + posthtml-render: 3.0.0 + + prebuild-install@7.1.2: + dependencies: + detect-libc: 2.0.3 + expand-template: 2.0.3 + github-from-package: 0.0.0 + minimist: 1.2.8 + mkdirp-classic: 0.5.3 + napi-build-utils: 1.0.2 + node-abi: 3.67.0 + pump: 3.0.0 + rc: 1.2.8 + simple-get: 4.0.1 + tar-fs: 2.1.1 + tunnel-agent: 0.6.0 + + prettier@3.2.4: {} + + process@0.11.10: {} + + proto-list@1.2.4: {} + + prr@1.0.1: + optional: true + + pump@3.0.0: + dependencies: + end-of-stream: 1.4.4 + once: 1.4.0 + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + queue-tick@1.0.1: {} + + quick-lru@5.1.1: {} + + radix-ui@1.0.1(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@radix-ui/react-accessible-icon': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-accordion': 1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-alert-dialog': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-aspect-ratio': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-avatar': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-checkbox': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-collapsible': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-context-menu': 2.2.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-dialog': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-direction': 1.1.0(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-dropdown-menu': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-hover-card': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-label': 2.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-navigation-menu': 1.2.5(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-popover': 1.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-portal': 1.1.4(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-progress': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-radio-group': 1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-scroll-area': 1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-select': 2.1.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-separator': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slider': 1.2.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-slot': 1.1.2(@types/react@18.2.48)(react@18.2.0) + '@radix-ui/react-switch': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-tabs': 1.1.3(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toast': 1.2.6(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toggle-group': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-toolbar': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-tooltip': 1.1.8(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + '@radix-ui/react-visually-hidden': 1.1.2(@types/react-dom@18.2.18)(@types/react@18.2.48)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + transitivePeerDependencies: + - '@types/react' + - '@types/react-dom' + + rc@1.2.8: + dependencies: + deep-extend: 0.6.0 + ini: 1.3.8 + minimist: 1.2.8 + strip-json-comments: 2.0.1 + + react-dom@18.2.0(react@18.2.0): + dependencies: + loose-envify: 1.4.0 + react: 18.2.0 + scheduler: 0.23.2 + + react-error-overlay@6.0.9: {} + + react-hooks-global-state@2.1.0(react@18.2.0): + dependencies: + react: 18.2.0 + zustand: 4.0.0(react@18.2.0) + transitivePeerDependencies: + - immer + + react-refresh@0.14.0: {} + + react-refresh@0.9.0: {} + + react-remove-scroll-bar@2.3.6(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + react-style-singleton: 2.2.1(@types/react@18.2.48)(react@18.2.0) + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + react-remove-scroll-bar@2.3.8(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + react-style-singleton: 2.2.3(@types/react@18.2.48)(react@18.2.0) + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + react-remove-scroll@2.6.0(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + react-remove-scroll-bar: 2.3.6(@types/react@18.2.48)(react@18.2.0) + react-style-singleton: 2.2.1(@types/react@18.2.48)(react@18.2.0) + tslib: 2.7.0 + use-callback-ref: 1.3.2(@types/react@18.2.48)(react@18.2.0) + use-sidecar: 1.1.2(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + + react-remove-scroll@2.6.3(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + react-remove-scroll-bar: 2.3.8(@types/react@18.2.48)(react@18.2.0) + react-style-singleton: 2.2.3(@types/react@18.2.48)(react@18.2.0) + tslib: 2.7.0 + use-callback-ref: 1.3.3(@types/react@18.2.48)(react@18.2.0) + use-sidecar: 1.1.3(@types/react@18.2.48)(react@18.2.0) + optionalDependencies: + '@types/react': 18.2.48 + + react-router-dom@6.26.1(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + '@remix-run/router': 1.19.1 + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-router: 6.26.1(react@18.2.0) + + react-router@6.26.1(react@18.2.0): + dependencies: + '@remix-run/router': 1.19.1 + react: 18.2.0 + + react-style-singleton@2.2.1(@types/react@18.2.48)(react@18.2.0): + dependencies: + get-nonce: 1.0.1 + invariant: 2.2.4 + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + react-style-singleton@2.2.3(@types/react@18.2.48)(react@18.2.0): + dependencies: + get-nonce: 1.0.1 + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + react@18.2.0: + dependencies: + loose-envify: 1.4.0 + + read-cache@1.0.0: + dependencies: + pify: 2.3.0 + + readable-stream@3.6.2: + dependencies: + inherits: 2.0.4 + string_decoder: 1.3.0 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + regenerator-runtime@0.13.11: {} + + regenerator-runtime@0.14.1: {} + + registry-auth-token@5.0.2: + dependencies: + '@pnpm/npm-conf': 2.3.1 + + registry-url@6.0.1: + dependencies: + rc: 1.2.8 + + resolve-alpn@1.2.1: {} + + resolve-from@4.0.0: {} + + resolve-from@5.0.0: {} + + resolve@1.22.8: + dependencies: + is-core-module: 2.15.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + responselike@3.0.0: + dependencies: + lowercase-keys: 3.0.0 + + restore-cursor@3.1.0: + dependencies: + onetime: 5.1.2 + signal-exit: 3.0.7 + + reusify@1.0.4: {} + + rollup@3.29.5: + optionalDependencies: + fsevents: 2.3.3 + + run-async@3.0.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + rxjs@7.8.1: + dependencies: + tslib: 2.7.0 + + safe-buffer@5.2.1: {} + + safer-buffer@2.1.2: {} + + sass@1.77.8: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.7 + source-map-js: 1.2.0 + + sax@1.4.1: + optional: true + + scheduler@0.23.2: + dependencies: + loose-envify: 1.4.0 + + semver@5.7.2: {} + + semver@6.3.1: {} + + semver@7.5.4: + dependencies: + lru-cache: 6.0.0 + + semver@7.6.3: {} + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.2.4 + gopd: 1.0.1 + has-property-descriptors: 1.0.2 + + sharp@0.32.6: + dependencies: + color: 4.2.3 + detect-libc: 2.0.3 + node-addon-api: 6.1.0 + prebuild-install: 7.1.2 + semver: 7.6.3 + simple-get: 4.0.1 + tar-fs: 3.0.6 + tunnel-agent: 0.6.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@3.0.0: {} + + signal-exit@3.0.7: {} + + signal-exit@4.1.0: {} + + simple-concat@1.0.1: {} + + simple-get@4.0.1: + dependencies: + decompress-response: 6.0.0 + once: 1.4.0 + simple-concat: 1.0.1 + + simple-swizzle@0.2.2: + dependencies: + is-arrayish: 0.3.2 + + slash@3.0.0: {} + + source-map-js@1.2.0: {} + + source-map@0.6.1: {} + + source-map@0.8.0-beta.0: + dependencies: + whatwg-url: 7.1.0 + + srcset@4.0.0: {} + + stable@0.1.8: {} + + streamx@2.19.0: + dependencies: + fast-fifo: 1.3.2 + queue-tick: 1.0.1 + text-decoder: 1.1.1 + optionalDependencies: + bare-events: 2.4.2 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string_decoder@1.3.0: + dependencies: + safe-buffer: 5.2.1 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.0.1 + + strip-final-newline@2.0.0: {} + + strip-json-comments@2.0.1: {} + + sucrase@3.35.0: + dependencies: + '@jridgewell/gen-mapping': 0.3.5 + commander: 4.1.1 + glob: 10.4.5 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.6 + ts-interface-checker: 0.1.13 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-preserve-symlinks-flag@1.0.0: {} + + svelte@4.2.2: + dependencies: + '@ampproject/remapping': 2.3.0 + '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/trace-mapping': 0.3.25 + acorn: 8.12.1 + aria-query: 5.3.0 + axobject-query: 3.2.4 + code-red: 1.0.4 + css-tree: 2.3.1 + estree-walker: 3.0.3 + is-reference: 3.0.2 + locate-character: 3.0.0 + magic-string: 0.30.11 + periscopic: 3.1.0 + + svg-parser@2.0.4: {} + + svgo@2.8.0: + dependencies: + '@trysound/sax': 0.2.0 + commander: 7.2.0 + css-select: 4.3.0 + css-tree: 1.1.3 + csso: 4.2.0 + picocolors: 1.0.1 + stable: 0.1.8 + + tailwind-merge@2.5.4: {} + + tailwindcss-animate@1.0.7(tailwindcss@3.4.10): + dependencies: + tailwindcss: 3.4.10 + + tailwindcss@3.4.10: + dependencies: + '@alloc/quick-lru': 5.2.0 + arg: 5.0.2 + chokidar: 3.6.0 + didyoumean: 1.2.2 + dlv: 1.1.3 + fast-glob: 3.3.2 + glob-parent: 6.0.2 + is-glob: 4.0.3 + jiti: 1.21.6 + lilconfig: 2.1.0 + micromatch: 4.0.8 + normalize-path: 3.0.0 + object-hash: 3.0.0 + picocolors: 1.0.1 + postcss: 8.4.41 + postcss-import: 15.1.0(postcss@8.4.41) + postcss-js: 4.0.1(postcss@8.4.41) + postcss-load-config: 4.0.2(postcss@8.4.41) + postcss-nested: 6.2.0(postcss@8.4.41) + postcss-selector-parser: 6.1.2 + resolve: 1.22.8 + sucrase: 3.35.0 + transitivePeerDependencies: + - ts-node + + tar-fs@2.1.1: + dependencies: + chownr: 1.1.4 + mkdirp-classic: 0.5.3 + pump: 3.0.0 + tar-stream: 2.2.0 + + tar-fs@3.0.6: + dependencies: + pump: 3.0.0 + tar-stream: 3.1.7 + optionalDependencies: + bare-fs: 2.3.1 + bare-path: 2.1.3 + + tar-stream@2.2.0: + dependencies: + bl: 4.1.0 + end-of-stream: 1.4.4 + fs-constants: 1.0.0 + inherits: 2.0.4 + readable-stream: 3.6.2 + + tar-stream@3.1.7: + dependencies: + b4a: 1.6.6 + fast-fifo: 1.3.2 + streamx: 2.19.0 + + temp-dir@3.0.0: {} + + tempy@3.1.0: + dependencies: + is-stream: 3.0.0 + temp-dir: 3.0.0 + type-fest: 2.19.0 + unique-string: 3.0.0 + + text-decoder@1.1.1: + dependencies: + b4a: 1.6.6 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + timsort@0.3.0: {} + + tmp@0.0.33: + dependencies: + os-tmpdir: 1.0.2 + + to-fast-properties@2.0.0: {} + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + tr46@1.0.1: + dependencies: + punycode: 2.3.1 + + tree-kill@1.2.2: {} + + ts-algebra@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + tslib@2.7.0: {} + + tsup@7.2.0(@swc/core@1.7.14(@swc/helpers@0.5.12))(postcss@8.4.41)(typescript@5.2.2): + dependencies: + bundle-require: 4.2.1(esbuild@0.18.20) + cac: 6.7.14 + chokidar: 3.6.0 + debug: 4.3.6 + esbuild: 0.18.20 + execa: 5.1.1 + globby: 11.1.0 + joycon: 3.1.1 + postcss-load-config: 4.0.2(postcss@8.4.41) + resolve-from: 5.0.0 + rollup: 3.29.5 + source-map: 0.8.0-beta.0 + sucrase: 3.35.0 + tree-kill: 1.2.2 + optionalDependencies: + '@swc/core': 1.7.14(@swc/helpers@0.5.12) + postcss: 8.4.41 + typescript: 5.2.2 + transitivePeerDependencies: + - supports-color + - ts-node + + tunnel-agent@0.6.0: + dependencies: + safe-buffer: 5.2.1 + + type-fest@0.20.2: {} + + type-fest@0.21.3: {} + + type-fest@1.4.0: {} + + type-fest@2.19.0: {} + + typescript@5.2.2: {} + + typescript@5.3.3: {} + + uhyphen@0.1.0: {} + + undici-types@5.26.5: {} + + unique-string@3.0.0: + dependencies: + crypto-random-string: 4.0.0 + + universalify@2.0.1: {} + + update-browserslist-db@1.1.0(browserslist@4.22.1): + dependencies: + browserslist: 4.22.1 + escalade: 3.1.2 + picocolors: 1.0.1 + + update-browserslist-db@1.1.0(browserslist@4.23.3): + dependencies: + browserslist: 4.23.3 + escalade: 3.1.2 + picocolors: 1.0.1 + + use-callback-ref@1.3.2(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + use-callback-ref@1.3.3(@types/react@18.2.48)(react@18.2.0): + dependencies: + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + use-sidecar@1.1.2(@types/react@18.2.48)(react@18.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + use-sidecar@1.1.3(@types/react@18.2.48)(react@18.2.0): + dependencies: + detect-node-es: 1.1.0 + react: 18.2.0 + tslib: 2.7.0 + optionalDependencies: + '@types/react': 18.2.48 + + use-sync-external-store@1.2.0(react@18.2.0): + dependencies: + react: 18.2.0 + + use-sync-external-store@1.2.2(react@18.2.0): + dependencies: + react: 18.2.0 + + util-deprecate@1.0.2: {} + + utility-types@3.11.0: {} + + vue@3.3.4: + dependencies: + '@vue/compiler-dom': 3.3.4 + '@vue/compiler-sfc': 3.3.4 + '@vue/runtime-dom': 3.3.4 + '@vue/server-renderer': 3.3.4(vue@3.3.4) + '@vue/shared': 3.3.4 + + wcwidth@1.0.1: + dependencies: + defaults: 1.0.4 + + weak-lru-cache@1.2.2: {} + + webidl-conversions@4.0.2: {} + + whatwg-url@7.1.0: + dependencies: + lodash.sortby: 4.7.0 + tr46: 1.0.1 + webidl-conversions: 4.0.2 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + wrap-ansi@6.2.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + xxhash-wasm@0.4.2: {} + + yallist@3.1.1: {} + + yallist@4.0.0: {} + + yaml@1.10.2: {} + + yaml@2.5.0: {} + + zustand@4.0.0(react@18.2.0): + dependencies: + use-sync-external-store: 1.2.0(react@18.2.0) + optionalDependencies: + react: 18.2.0 diff --git a/surfsense_browser_extension/popup.tsx b/surfsense_browser_extension/popup.tsx new file mode 100644 index 000000000..bf3ab388c --- /dev/null +++ b/surfsense_browser_extension/popup.tsx @@ -0,0 +1,15 @@ +import { MemoryRouter } from "react-router-dom" + +import { Routing } from "~routes" +import { Toaster } from "@/routes/ui/toaster" + +function IndexPopup() { + return ( + + + + + ) +} + +export default IndexPopup \ No newline at end of file diff --git a/surfsense_browser_extension/postcss.config.js b/surfsense_browser_extension/postcss.config.js new file mode 100644 index 000000000..12a703d90 --- /dev/null +++ b/surfsense_browser_extension/postcss.config.js @@ -0,0 +1,6 @@ +module.exports = { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +}; diff --git a/surfsense_browser_extension/routes/index.tsx b/surfsense_browser_extension/routes/index.tsx new file mode 100644 index 000000000..a24885b60 --- /dev/null +++ b/surfsense_browser_extension/routes/index.tsx @@ -0,0 +1,13 @@ +import { Route, Routes } from "react-router-dom" + +import ApiKeyForm from "./pages/ApiKeyForm" +import HomePage from "./pages/HomePage" +import '../tailwind.css' + + +export const Routing = () => ( + + } /> + } /> + +) \ No newline at end of file diff --git a/surfsense_browser_extension/routes/pages/ApiKeyForm.tsx b/surfsense_browser_extension/routes/pages/ApiKeyForm.tsx new file mode 100644 index 000000000..717dc5835 --- /dev/null +++ b/surfsense_browser_extension/routes/pages/ApiKeyForm.tsx @@ -0,0 +1,123 @@ +import React, { useState } from "react"; +import { useNavigate } from "react-router-dom" +import icon from "data-base64:~assets/icon.png" +import { Storage } from "@plasmohq/storage" +import { Button } from "~/routes/ui/button" +import { ReloadIcon } from "@radix-ui/react-icons" + +const ApiKeyForm = () => { + const navigation = useNavigate() + const [apiKey, setApiKey] = useState(''); + const [error, setError] = useState(''); + const [loading, setLoading] = useState(false); + const storage = new Storage({ area: "local" }) + + const validateForm = () => { + if (!apiKey) { + setError('API key is required'); + return false; + } + setError(''); + return true; + }; + + const handleSubmit = async (event: { preventDefault: () => void; }) => { + event.preventDefault(); + if (!validateForm()) return; + setLoading(true); + + try { + // Verify token is valid by making a request to the API + const response = await fetch(`${process.env.PLASMO_PUBLIC_BACKEND_URL}/verify-token`, { + method: 'GET', + headers: { + 'Authorization': `Bearer ${apiKey}`, + } + }); + + setLoading(false); + + if (response.ok) { + // Store the API key as the token + await storage.set('token', apiKey); + navigation("/") + } else { + setError('Invalid API key. Please check and try again.'); + } + } catch (error) { + setLoading(false); + setError('An error occurred. Please try again later.'); + } + }; + + return ( +
+
+
+
+ SurfSense +
+

SurfSense

+
+ +
+
+

Enter your API Key

+

+ Your API key connects this extension to the SurfSense. +

+ +
+
+ + setApiKey(e.target.value)} + className="w-full px-3 py-2 bg-gray-900/50 border border-gray-700 rounded-md focus:outline-none focus:ring-2 focus:ring-teal-500 text-white placeholder:text-gray-500" + placeholder="Enter your API key" + /> + {error && ( +

{error}

+ )} +
+ + +
+ +
+

+ Need an API key?{" "} + + Sign up + +

+
+
+
+
+
+ ); +} + +export default ApiKeyForm diff --git a/surfsense_browser_extension/routes/pages/HomePage.tsx b/surfsense_browser_extension/routes/pages/HomePage.tsx new file mode 100644 index 000000000..0b45a3c4f --- /dev/null +++ b/surfsense_browser_extension/routes/pages/HomePage.tsx @@ -0,0 +1,476 @@ +import React, { useEffect, useState } from "react"; +import { useNavigate } from "react-router-dom" +import icon from "data-base64:~assets/icon.png" +import { convertHtmlToMarkdown } from "dom-to-semantic-markdown"; +import type { WebHistory } from "~utils/interfaces"; +import { getRenderedHtml } from "~utils/commons"; +import Loading from "./Loading"; +import brain from "data-base64:~assets/brain.png" +import { Storage } from "@plasmohq/storage" +import { sendToBackground } from "@plasmohq/messaging" +import { Check, ChevronsUpDown } from "lucide-react" +import { cn } from "~/lib/utils" +import { Button } from "~/routes/ui/button" +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, +} from "~/routes/ui/command" +import { + Popover, + PopoverContent, + PopoverTrigger, +} from "~/routes/ui/popover" +import { useToast } from "~routes/ui/use-toast"; +import { + CircleIcon, + CrossCircledIcon, + DiscIcon, + ExitIcon, + FileIcon, + ReloadIcon, + ResetIcon, + UploadIcon +} from "@radix-ui/react-icons" + +const HomePage = () => { + const { toast } = useToast() + const navigation = useNavigate() + const [noOfWebPages, setNoOfWebPages] = useState(0); + const [loading, setLoading] = useState(true); + const [open, setOpen] = React.useState(false) + const [value, setValue] = React.useState("") + const [searchspaces, setSearchSpaces] = useState([]) + const [isSaving, setIsSaving] = useState(false); + + useEffect(() => { + const checkSearchSpaces = async () => { + const storage = new Storage({ area: "local" }) + const token = await storage.get('token'); + try { + const response = await fetch( + `${process.env.PLASMO_PUBLIC_BACKEND_URL}/api/v1/searchspaces/`, + { + headers: { + 'Authorization': `Bearer ${token}` + } + } + ); + + if (!response.ok) { + throw new Error("Token verification failed"); + } else { + const res = await response.json() + console.log(res) + setSearchSpaces(res) + } + } catch (error) { + await storage.remove('token'); + await storage.remove('showShadowDom'); + navigation("/login") + } + }; + + checkSearchSpaces(); + setLoading(false); + }, []); + + + useEffect(() => { + async function onLoad() { + try { + chrome.storage.onChanged.addListener( + (changes: any, areaName: string) => { + if (changes.webhistory) { + const webhistory = JSON.parse(changes.webhistory.newValue); + console.log("webhistory", webhistory) + + let sum = 0 + webhistory.webhistory.forEach((element: any) => { + sum = sum + element.tabHistory.length + }); + + setNoOfWebPages(sum) + } + } + ); + + const storage = new Storage({ area: "local" }) + const searchspace = await storage.get("search_space"); + + if(searchspace){ + setValue(searchspace) + } + + await storage.set("showShadowDom", true) + + const webhistoryObj: any = await storage.get("webhistory"); + if (webhistoryObj.webhistory.length) { + const webhistory = webhistoryObj.webhistory; + + if (webhistoryObj) { + let sum = 0 + webhistory.forEach((element: any) => { + sum = sum + element.tabHistory.length + }); + setNoOfWebPages(sum) + } + } else { + setNoOfWebPages(0) + } + } catch (error) { + console.log(error); + } + } + + onLoad() + }, []); + + async function clearMem(): Promise { + try { + const storage = new Storage({ area: "local" }) + + let webHistory: any = await storage.get("webhistory"); + let urlQueue: any = await storage.get("urlQueueList"); + let timeQueue: any = await storage.get("timeQueueList"); + + if (!webHistory.webhistory) { + return + } + + //Main Cleanup COde + chrome.tabs.query({}, async (tabs) => { + //Get Active Tabs Ids + let actives = tabs.map((tab) => { + if (tab.id) { + return tab.id + } + }) + + actives = actives.filter((item: any) => item) + + //Only retain which is still active + const newHistory = webHistory.webhistory.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + const newUrlQueue = urlQueue.urlQueueList.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + const newTimeQueue = timeQueue.timeQueueList.map((element: any) => { + //@ts-ignore + if (actives.includes(element.tabsessionId)) { + return element + } + }) + + await storage.set("webhistory", { webhistory: newHistory.filter((item: any) => item) }); + await storage.set("urlQueueList", { urlQueueList: newUrlQueue.filter((item: any) => item) }); + await storage.set("timeQueueList", { timeQueueList: newTimeQueue.filter((item: any) => item) }); + toast({ + title: "History store cleared", + description: "Inactive history sessions have been removed", + variant: "destructive", + }) + }); + } catch (error) { + console.log(error); + } + } + + async function saveCurrSnapShot(): Promise { + chrome.tabs.query({ active: true, currentWindow: true }, async function (tabs) { + const storage = new Storage({ area: "local" }) + const tab = tabs[0]; + if (tab.id) { + const tabId: number = tab.id + const result = await chrome.scripting.executeScript({ + // @ts-ignore + target: { tabId: tab.id }, + // @ts-ignore + func: getRenderedHtml, + }); + + let toPushInTabHistory: any = result[0].result; + + //Updates 'tabhistory' + let webhistoryObj: any = await storage.get("webhistory"); + + const webHistoryOfTabId = webhistoryObj.webhistory.filter( + (data: WebHistory) => { + return data.tabsessionId === tab.id; + } + ); + + toPushInTabHistory.pageContentMarkdown = convertHtmlToMarkdown( + toPushInTabHistory.renderedHtml, + { + extractMainContent: true, + includeMetaData: false, + enableTableColumnTracking: true + } + ) + + delete toPushInTabHistory.renderedHtml + + let tabhistory = webHistoryOfTabId[0].tabHistory; + + const urlQueueListObj: any = await storage.get("urlQueueList"); + const timeQueueListObj: any = await storage.get("timeQueueList"); + + const isUrlQueueThere = urlQueueListObj.urlQueueList.find((data: WebHistory) => data.tabsessionId === tabId) + const isTimeQueueThere = timeQueueListObj.timeQueueList.find((data: WebHistory) => data.tabsessionId === tabId) + + toPushInTabHistory.duration = toPushInTabHistory.entryTime - isTimeQueueThere.timeQueue[isTimeQueueThere.timeQueue.length - 1] + if (isUrlQueueThere.urlQueue.length == 1) { + toPushInTabHistory.reffererUrl = 'START' + } + if (isUrlQueueThere.urlQueue.length > 1) { + toPushInTabHistory.reffererUrl = isUrlQueueThere.urlQueue[isUrlQueueThere.urlQueue.length - 2]; + } + + webHistoryOfTabId[0].tabHistory.push(toPushInTabHistory); + + await storage.set("webhistory", webhistoryObj); + + toast({ + title: "Snapshot saved", + description: `Captured: ${toPushInTabHistory.title}`, + }) + } + + }); + } + + const saveDatamessage = async () => { + if (value === "") { + toast({ + title: "Select a SearchSpace !", + }) + return + } + + const storage = new Storage({ area: "local" }) + const search_space_id = await storage.get("search_space_id"); + + if (!search_space_id) { + toast({ + title: "Invalid SearchSpace selected!", + variant: "destructive", + }) + return + } + + setIsSaving(true); + toast({ + title: "Save job running", + description: "Saving captured content to SurfSense", + }) + + try { + const resp = await sendToBackground({ + // @ts-ignore + name: "savedata", + }) + + toast({ + title: resp.message, + }) + } catch (error) { + toast({ + title: "Error saving data", + description: "Please try again", + variant: "destructive", + }) + } finally { + setIsSaving(false); + } + } + + async function logOut(): Promise { + const storage = new Storage({ area: "local" }) + await storage.remove('token'); + await storage.remove('showShadowDom'); + navigation("/login") + } + + if (loading) { + return ; + } else { + return searchspaces.length === 0 ? ( +
+
+
+
+
+ SurfSense +
+

SurfSense

+
+

Please create a Search Space to continue

+
+
+ +
+ +
+
+
+
+ ) : ( +
+
+
+
+
+ SurfSense +
+

SurfSense

+
+ +
+ +
+
+
+
+ brain + {noOfWebPages} +
+
+

Captured web pages

+
+ +
+ + + + + + + + + + No search spaces found. + + {searchspaces.map((space) => ( + { + const storage = new Storage({ area: "local" }) + if (currentValue === value) { + await storage.set("search_space", ""); + await storage.set("search_space_id", 0); + } else { + const selectedSpace = searchspaces.find((space) => space.name === currentValue); + await storage.set("search_space", currentValue); + await storage.set("search_space_id", selectedSpace.id); + } + setValue(currentValue === value ? "" : currentValue) + setOpen(false) + }} + className="aria-selected:bg-gray-700" + > + +
+ + {space.name} +
+
+ ))} +
+
+
+
+
+
+ +
+ + + + + +
+
+
+
+ ); + } +}; + +export default HomePage \ No newline at end of file diff --git a/surfsense_browser_extension/routes/pages/Loading.tsx b/surfsense_browser_extension/routes/pages/Loading.tsx new file mode 100644 index 000000000..e1bba1b9c --- /dev/null +++ b/surfsense_browser_extension/routes/pages/Loading.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import icon from "data-base64:~assets/icon.png" +import { ReloadIcon } from "@radix-ui/react-icons" + +const Loading = () => { + return ( +
+
+
+
+ SurfSense +
+

SurfSense

+
+ +
+ +
+ {Array.from("LOADING").map((letter, i) => ( + + {letter} + + ))} +
+
+
+
+ ) +} + +export default Loading \ No newline at end of file diff --git a/surfsense_browser_extension/routes/ui/button.tsx b/surfsense_browser_extension/routes/ui/button.tsx new file mode 100644 index 000000000..65729daa1 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/button.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "~/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0", + { + variants: { + variant: { + default: "bg-primary text-primary-foreground hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground hover:bg-destructive/90", + outline: + "border border-input bg-background hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-10 px-4 py-2", + sm: "h-9 rounded-md px-3", + lg: "h-11 rounded-md px-8", + icon: "h-10 w-10", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/surfsense_browser_extension/routes/ui/command.tsx b/surfsense_browser_extension/routes/ui/command.tsx new file mode 100644 index 000000000..15f897110 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/command.tsx @@ -0,0 +1,155 @@ +"use client" + +import * as React from "react" +import { type DialogProps } from "@radix-ui/react-dialog" +import { Command as CommandPrimitive } from "cmdk" +import { Search } from "lucide-react" + +import { cn } from "~/lib/utils" +import { Dialog, DialogContent } from "~/routes/ui/dialog" + +const Command = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Command.displayName = CommandPrimitive.displayName + +interface CommandDialogProps extends DialogProps {} + +const CommandDialog = ({ children, ...props }: CommandDialogProps) => { + return ( + + + + {children} + + + + ) +} + +const CommandInput = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ + +
+)) + +CommandInput.displayName = CommandPrimitive.Input.displayName + +const CommandList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandList.displayName = CommandPrimitive.List.displayName + +const CommandEmpty = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>((props, ref) => ( + +)) + +CommandEmpty.displayName = CommandPrimitive.Empty.displayName + +const CommandGroup = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandGroup.displayName = CommandPrimitive.Group.displayName + +const CommandSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +CommandSeparator.displayName = CommandPrimitive.Separator.displayName + +const CommandItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) + +CommandItem.displayName = CommandPrimitive.Item.displayName + +const CommandShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ) +} +CommandShortcut.displayName = "CommandShortcut" + +export { + Command, + CommandDialog, + CommandInput, + CommandList, + CommandEmpty, + CommandGroup, + CommandItem, + CommandShortcut, + CommandSeparator, +} diff --git a/surfsense_browser_extension/routes/ui/dialog.tsx b/surfsense_browser_extension/routes/ui/dialog.tsx new file mode 100644 index 000000000..9850daa41 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/dialog.tsx @@ -0,0 +1,122 @@ +"use client" + +import * as React from "react" +import * as DialogPrimitive from "@radix-ui/react-dialog" +import { X } from "lucide-react" + +import { cn } from "~/lib/utils" + +const Dialog = DialogPrimitive.Root + +const DialogTrigger = DialogPrimitive.Trigger + +const DialogPortal = DialogPrimitive.Portal + +const DialogClose = DialogPrimitive.Close + +const DialogOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogOverlay.displayName = DialogPrimitive.Overlay.displayName + +const DialogContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)) +DialogContent.displayName = DialogPrimitive.Content.displayName + +const DialogHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogHeader.displayName = "DialogHeader" + +const DialogFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DialogFooter.displayName = "DialogFooter" + +const DialogTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogTitle.displayName = DialogPrimitive.Title.displayName + +const DialogDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DialogDescription.displayName = DialogPrimitive.Description.displayName + +export { + Dialog, + DialogPortal, + DialogOverlay, + DialogClose, + DialogTrigger, + DialogContent, + DialogHeader, + DialogFooter, + DialogTitle, + DialogDescription, +} diff --git a/surfsense_browser_extension/routes/ui/popover.tsx b/surfsense_browser_extension/routes/ui/popover.tsx new file mode 100644 index 000000000..d45a633e7 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/popover.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as PopoverPrimitive from "@radix-ui/react-popover" + +import { cn } from "~/lib/utils" + +const Popover = PopoverPrimitive.Root + +const PopoverTrigger = PopoverPrimitive.Trigger + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + + + +)) +PopoverContent.displayName = PopoverPrimitive.Content.displayName + +export { Popover, PopoverTrigger, PopoverContent } diff --git a/surfsense_browser_extension/routes/ui/toast.tsx b/surfsense_browser_extension/routes/ui/toast.tsx new file mode 100644 index 000000000..521b94b07 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/toast.tsx @@ -0,0 +1,129 @@ +"use client" + +import * as React from "react" +import * as ToastPrimitives from "@radix-ui/react-toast" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const ToastProvider = ToastPrimitives.Provider + +const ToastViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastViewport.displayName = ToastPrimitives.Viewport.displayName + +const toastVariants = cva( + "group pointer-events-auto relative flex w-full items-center justify-between space-x-4 overflow-hidden rounded-md border p-6 pr-8 shadow-lg transition-all data-[swipe=cancel]:translate-x-0 data-[swipe=end]:translate-x-[var(--radix-toast-swipe-end-x)] data-[swipe=move]:translate-x-[var(--radix-toast-swipe-move-x)] data-[swipe=move]:transition-none data-[state=open]:animate-in data-[state=closed]:animate-out data-[swipe=end]:animate-out data-[state=closed]:fade-out-80 data-[state=closed]:slide-out-to-right-full data-[state=open]:slide-in-from-top-full data-[state=open]:sm:slide-in-from-bottom-full", + { + variants: { + variant: { + default: "border bg-background text-foreground", + destructive: + "destructive group border-destructive bg-destructive text-destructive-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Toast = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & + VariantProps +>(({ className, variant, ...props }, ref) => { + return ( + + ) +}) +Toast.displayName = ToastPrimitives.Root.displayName + +const ToastAction = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastAction.displayName = ToastPrimitives.Action.displayName + +const ToastClose = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)) +ToastClose.displayName = ToastPrimitives.Close.displayName + +const ToastTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastTitle.displayName = ToastPrimitives.Title.displayName + +const ToastDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +ToastDescription.displayName = ToastPrimitives.Description.displayName + +type ToastProps = React.ComponentPropsWithoutRef + +type ToastActionElement = React.ReactElement + +export { + type ToastProps, + type ToastActionElement, + ToastProvider, + ToastViewport, + Toast, + ToastTitle, + ToastDescription, + ToastClose, + ToastAction, +} diff --git a/surfsense_browser_extension/routes/ui/toaster.tsx b/surfsense_browser_extension/routes/ui/toaster.tsx new file mode 100644 index 000000000..89077be38 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/toaster.tsx @@ -0,0 +1,35 @@ +"use client" + +import { useToast } from "@/routes/ui/use-toast" +import { + Toast, + ToastClose, + ToastDescription, + ToastProvider, + ToastTitle, + ToastViewport, +} from "@/routes/ui/toast" + +export function Toaster() { + const { toasts } = useToast() + + return ( + + {toasts.map(function ({ id, title, description, action, ...props }) { + return ( + +
+ {title && {title}} + {description && ( + {description} + )} +
+ {action} + +
+ ) + })} + +
+ ) +} diff --git a/surfsense_browser_extension/routes/ui/use-toast.tsx b/surfsense_browser_extension/routes/ui/use-toast.tsx new file mode 100644 index 000000000..fd284dca9 --- /dev/null +++ b/surfsense_browser_extension/routes/ui/use-toast.tsx @@ -0,0 +1,194 @@ +"use client" + +// Inspired by react-hot-toast library +import * as React from "react" + +import type { + ToastActionElement, + ToastProps, +} from "@/routes/ui/toast" + +const TOAST_LIMIT = 1 +const TOAST_REMOVE_DELAY = 1000000 + +type ToasterToast = ToastProps & { + id: string + title?: React.ReactNode + description?: React.ReactNode + action?: ToastActionElement +} + +const actionTypes = { + ADD_TOAST: "ADD_TOAST", + UPDATE_TOAST: "UPDATE_TOAST", + DISMISS_TOAST: "DISMISS_TOAST", + REMOVE_TOAST: "REMOVE_TOAST", +} as const + +let count = 0 + +function genId() { + count = (count + 1) % Number.MAX_SAFE_INTEGER + return count.toString() +} + +type ActionType = typeof actionTypes + +type Action = + | { + type: ActionType["ADD_TOAST"] + toast: ToasterToast + } + | { + type: ActionType["UPDATE_TOAST"] + toast: Partial + } + | { + type: ActionType["DISMISS_TOAST"] + toastId?: ToasterToast["id"] + } + | { + type: ActionType["REMOVE_TOAST"] + toastId?: ToasterToast["id"] + } + +interface State { + toasts: ToasterToast[] +} + +const toastTimeouts = new Map>() + +const addToRemoveQueue = (toastId: string) => { + if (toastTimeouts.has(toastId)) { + return + } + + const timeout = setTimeout(() => { + toastTimeouts.delete(toastId) + dispatch({ + type: "REMOVE_TOAST", + toastId: toastId, + }) + }, TOAST_REMOVE_DELAY) + + toastTimeouts.set(toastId, timeout) +} + +export const reducer = (state: State, action: Action): State => { + switch (action.type) { + case "ADD_TOAST": + return { + ...state, + toasts: [action.toast, ...state.toasts].slice(0, TOAST_LIMIT), + } + + case "UPDATE_TOAST": + return { + ...state, + toasts: state.toasts.map((t) => + t.id === action.toast.id ? { ...t, ...action.toast } : t + ), + } + + case "DISMISS_TOAST": { + const { toastId } = action + + // ! Side effects ! - This could be extracted into a dismissToast() action, + // but I'll keep it here for simplicity + if (toastId) { + addToRemoveQueue(toastId) + } else { + state.toasts.forEach((toast) => { + addToRemoveQueue(toast.id) + }) + } + + return { + ...state, + toasts: state.toasts.map((t) => + t.id === toastId || toastId === undefined + ? { + ...t, + open: false, + } + : t + ), + } + } + case "REMOVE_TOAST": + if (action.toastId === undefined) { + return { + ...state, + toasts: [], + } + } + return { + ...state, + toasts: state.toasts.filter((t) => t.id !== action.toastId), + } + } +} + +const listeners: Array<(state: State) => void> = [] + +let memoryState: State = { toasts: [] } + +function dispatch(action: Action) { + memoryState = reducer(memoryState, action) + listeners.forEach((listener) => { + listener(memoryState) + }) +} + +type Toast = Omit + +function toast({ ...props }: Toast) { + const id = genId() + + const update = (props: ToasterToast) => + dispatch({ + type: "UPDATE_TOAST", + toast: { ...props, id }, + }) + const dismiss = () => dispatch({ type: "DISMISS_TOAST", toastId: id }) + + dispatch({ + type: "ADD_TOAST", + toast: { + ...props, + id, + open: true, + onOpenChange: (open) => { + if (!open) dismiss() + }, + }, + }) + + return { + id: id, + dismiss, + update, + } +} + +function useToast() { + const [state, setState] = React.useState(memoryState) + + React.useEffect(() => { + listeners.push(setState) + return () => { + const index = listeners.indexOf(setState) + if (index > -1) { + listeners.splice(index, 1) + } + } + }, [state]) + + return { + ...state, + toast, + dismiss: (toastId?: string) => dispatch({ type: "DISMISS_TOAST", toastId }), + } +} + +export { useToast, toast } diff --git a/surfsense_browser_extension/tailwind.config.js b/surfsense_browser_extension/tailwind.config.js new file mode 100644 index 000000000..e362d8239 --- /dev/null +++ b/surfsense_browser_extension/tailwind.config.js @@ -0,0 +1,76 @@ +const { fontFamily } = require("tailwindcss/defaultTheme") + +/** @type {import('tailwindcss').Config} */ +module.exports = { + darkMode: ["class"], + content: ["./*.{js,jsx,ts,tsx}","./routes/*.tsx","./routes/**/*.tsx"], + theme: { + container: { + center: true, + padding: "2rem", + screens: { + "2xl": "1400px", + }, + }, + extend: { + colors: { + border: "hsl(var(--border))", + input: "hsl(var(--input))", + ring: "hsl(var(--ring))", + background: "hsl(var(--background))", + foreground: "hsl(var(--foreground))", + primary: { + DEFAULT: "hsl(var(--primary))", + foreground: "hsl(var(--primary-foreground))", + }, + secondary: { + DEFAULT: "hsl(var(--secondary))", + foreground: "hsl(var(--secondary-foreground))", + }, + destructive: { + DEFAULT: "hsl(var(--destructive))", + foreground: "hsl(var(--destructive-foreground))", + }, + muted: { + DEFAULT: "hsl(var(--muted))", + foreground: "hsl(var(--muted-foreground))", + }, + accent: { + DEFAULT: "hsl(var(--accent))", + foreground: "hsl(var(--accent-foreground))", + }, + popover: { + DEFAULT: "hsl(var(--popover))", + foreground: "hsl(var(--popover-foreground))", + }, + card: { + DEFAULT: "hsl(var(--card))", + foreground: "hsl(var(--card-foreground))", + }, + }, + borderRadius: { + lg: `var(--radius)`, + md: `calc(var(--radius) - 2px)`, + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-sans)", ...fontFamily.sans], + }, + keyframes: { + "accordion-down": { + from: { height: "0" }, + to: { height: "var(--radix-accordion-content-height)" }, + }, + "accordion-up": { + from: { height: "var(--radix-accordion-content-height)" }, + to: { height: "0" }, + }, + }, + animation: { + "accordion-down": "accordion-down 0.2s ease-out", + "accordion-up": "accordion-up 0.2s ease-out", + }, + }, + }, + plugins: [require("tailwindcss-animate")], +} diff --git a/surfsense_browser_extension/tailwind.css b/surfsense_browser_extension/tailwind.css new file mode 100644 index 000000000..a95fb751c --- /dev/null +++ b/surfsense_browser_extension/tailwind.css @@ -0,0 +1,98 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + :root { + --background: 240 10% 3.9%; + --foreground: 0 0% 98%; + + --card: 240 10% 3.9%; + --card-foreground: 0 0% 98%; + + --popover: 240 10% 3.9%; + --popover-foreground: 0 0% 98%; + + --primary: 180 100% 37%; + --primary-foreground: 0 0% 98%; + + --secondary: 240 5.9% 10%; + --secondary-foreground: 0 0% 98%; + + --muted: 240 5.9% 10%; + --muted-foreground: 240 5% 64.9%; + + --accent: 169 97% 37%; + --accent-foreground: 0 0% 98%; + + --destructive: 0 84.2% 60.2%; + --destructive-foreground: 0 0% 98%; + + --border: 240 5.9% 24%; + --input: 240 5.9% 10%; + --ring: 180 100% 37%; + + --radius: 0.5rem; + } + + .dark { + --background: 224 71% 4%; + --foreground: 213 31% 91%; + + --muted: 223 47% 11%; + --muted-foreground: 215.4 16.3% 56.9%; + + --accent: 216 34% 17%; + --accent-foreground: 210 40% 98%; + + --popover: 224 71% 4%; + --popover-foreground: 215 20.2% 65.1%; + + --border: 216 34% 17%; + --input: 216 34% 17%; + + --card: 224 71% 4%; + --card-foreground: 213 31% 91%; + + --primary: 210 40% 98%; + --primary-foreground: 222.2 47.4% 1.2%; + + --secondary: 222.2 47.4% 11.2%; + --secondary-foreground: 210 40% 98%; + + --destructive: 0 63% 31%; + --destructive-foreground: 210 40% 98%; + + --ring: 216 34% 17%; + + --radius: 0.5rem; + } +} + +body { + min-width: 380px; + min-height: 580px; +} + +@layer base { + * { + @apply border-border; + } + body { + @apply bg-background text-foreground; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, + Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif; + } + + /* Styling for shadcn/ui components */ + .command-dialog { + @apply dark; + } +} + +/* Popup page dimensions */ +@media (prefers-color-scheme: dark) { + body { + @apply bg-slate-950 text-white; + } +} diff --git a/surfsense_browser_extension/tsconfig.json b/surfsense_browser_extension/tsconfig.json new file mode 100644 index 000000000..88daa5059 --- /dev/null +++ b/surfsense_browser_extension/tsconfig.json @@ -0,0 +1,20 @@ +{ + "extends": "plasmo/templates/tsconfig.base", + "exclude": [ + "node_modules" + ], + "include": [ + ".plasmo/index.d.ts", + "./**/*.ts", + "./**/*.tsx" + ], + "compilerOptions": { + "paths": { + "~*": [ + "./*" + ], + "@/*": ["./*"] + }, + "baseUrl": "." + } +} diff --git a/surfsense_browser_extension/utils/commons.ts b/surfsense_browser_extension/utils/commons.ts new file mode 100644 index 000000000..5056594ee --- /dev/null +++ b/surfsense_browser_extension/utils/commons.ts @@ -0,0 +1,144 @@ +import { Storage } from "@plasmohq/storage" +import type { WebHistory } from "./interfaces" + +export const emptyArr: any[] = [] + +export const initQueues = async (tabId: number) => { + const storage = new Storage({ area: "local" }) + + let urlQueueListObj: any = await storage.get("urlQueueList") + let timeQueueListObj: any = await storage.get("timeQueueList") + + if (!urlQueueListObj && !timeQueueListObj) { + await storage.set("urlQueueList", { + urlQueueList: [{ tabsessionId: tabId, urlQueue: [] }] + }) + await storage.set("timeQueueList", { + timeQueueList: [{ tabsessionId: tabId, timeQueue: [] }] + }) + + return + } + + if (urlQueueListObj.urlQueueList && timeQueueListObj.timeQueueList) { + const isUrlQueueThere = urlQueueListObj.urlQueueList.find( + (data: WebHistory) => data.tabsessionId === tabId + ) + const isTimeQueueThere = timeQueueListObj.timeQueueList.find( + (data: WebHistory) => data.tabsessionId === tabId + ) + + if (!isUrlQueueThere) { + urlQueueListObj.urlQueueList.push({ tabsessionId: tabId, urlQueue: [] }) + + await storage.set("urlQueueList", { + urlQueueList: urlQueueListObj.urlQueueList + }) + } + + if (!isTimeQueueThere) { + timeQueueListObj.timeQueueList.push({ + tabsessionId: tabId, + timeQueue: [] + }) + + await storage.set("timeQueueList", { + timeQueueList: timeQueueListObj.timeQueueList + }) + } + + return + } +} + +export function getRenderedHtml() { + return { + url: window.location.href, + entryTime: Date.now(), + title: document.title, + renderedHtml: document.documentElement.outerHTML + } +} + +export const initWebHistory = async (tabId: number) => { +const storage = new Storage({ area: "local" }) + const result: any = await storage.get("webhistory") + + if (result === undefined) { + await storage.set("webhistory", { webhistory: emptyArr }) + return + } + + const ifIdExists = result.webhistory.find( + (data: WebHistory) => data.tabsessionId === tabId + ) + + if (ifIdExists === undefined) { + let webHistory = result.webhistory + const initData = { + tabsessionId: tabId, + tabHistory: emptyArr + } + + webHistory.push(initData) + + try { + await storage.set("webhistory", { webhistory: webHistory }) + return + } catch (error) { + console.log(error) + } + } else { + return + } +} + +export function toIsoString(date: Date) { + var tzo = -date.getTimezoneOffset(), + dif = tzo >= 0 ? "+" : "-", + pad = function (num: number) { + return (num < 10 ? "0" : "") + num + } + + return ( + date.getFullYear() + + "-" + + pad(date.getMonth() + 1) + + "-" + + pad(date.getDate()) + + "T" + + pad(date.getHours()) + + ":" + + pad(date.getMinutes()) + + ":" + + pad(date.getSeconds()) + + dif + + pad(Math.floor(Math.abs(tzo) / 60)) + + ":" + + pad(Math.abs(tzo) % 60) + ) +} + +export const webhistoryToLangChainDocument = ( + tabId: number, + tabHistory: any[] +) => { + let toSaveFinally = [] + for (let j = 0; j < tabHistory.length; j++) { + const mtadata = { + BrowsingSessionId: `${tabId}`, + VisitedWebPageURL: `${tabHistory[j].url}`, + VisitedWebPageTitle: `${tabHistory[j].title}`, + VisitedWebPageDateWithTimeInISOString: `${toIsoString(new Date(tabHistory[j].entryTime))}`, + VisitedWebPageReffererURL: `${tabHistory[j].reffererUrl}`, + VisitedWebPageVisitDurationInMilliseconds: tabHistory[j].duration + } + + toSaveFinally.push({ + metadata: mtadata, + pageContent: tabHistory[j].pageContentMarkdown + }) + } + + return toSaveFinally +} diff --git a/surfsense_browser_extension/utils/interfaces.ts b/surfsense_browser_extension/utils/interfaces.ts new file mode 100644 index 000000000..ba75e8d4c --- /dev/null +++ b/surfsense_browser_extension/utils/interfaces.ts @@ -0,0 +1,4 @@ +export interface WebHistory { + tabsessionId: number; + tabHistory: any[]; +} \ No newline at end of file diff --git a/surfsense_frontend b/surfsense_frontend deleted file mode 160000 index 1de756133..000000000 --- a/surfsense_frontend +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1de75613320f6d077ca04c6ec7a7441e07536613 diff --git a/surfsense_web/.cursorrules b/surfsense_web/.cursorrules new file mode 100644 index 000000000..0320360a1 --- /dev/null +++ b/surfsense_web/.cursorrules @@ -0,0 +1 @@ +use pnpm as default package manager \ No newline at end of file diff --git a/surfsense_web/.dockerignore b/surfsense_web/.dockerignore new file mode 100644 index 000000000..73361d68a --- /dev/null +++ b/surfsense_web/.dockerignore @@ -0,0 +1,15 @@ +.git +.gitignore +node_modules +.next +out +.DS_Store +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +.env +.env.local +.env.development.local +.env.test.local +.env.production.local \ No newline at end of file diff --git a/surfsense_web/.gitignore b/surfsense_web/.gitignore new file mode 100644 index 000000000..5ef6a5207 --- /dev/null +++ b/surfsense_web/.gitignore @@ -0,0 +1,41 @@ +# See https://help.github.com/articles/ignoring-files/ for more about ignoring files. + +# dependencies +/node_modules +/.pnp +.pnp.* +.yarn/* +!.yarn/patches +!.yarn/plugins +!.yarn/releases +!.yarn/versions + +# testing +/coverage + +# next.js +/.next/ +/out/ + +# production +/build + +# misc +.DS_Store +*.pem + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* +.pnpm-debug.log* + +# env files (can opt-in for committing if needed) +.env* + +# vercel +.vercel + +# typescript +*.tsbuildinfo +next-env.d.ts diff --git a/surfsense_web/.vscode/launch.json b/surfsense_web/.vscode/launch.json new file mode 100644 index 000000000..0f7a24de3 --- /dev/null +++ b/surfsense_web/.vscode/launch.json @@ -0,0 +1,31 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js: debug client-side", + "type": "chrome", + "request": "launch", + "url": "http://localhost:3000", + "webRoot": "${workspaceFolder}" + }, + { + "name": "Next.js: debug server-side", + "type": "node-terminal", + "request": "launch", + "command": "pnpm run debug:server", + "skipFiles": ["/**"] + }, + { + "name": "Next.js: debug full stack", + "type": "node-terminal", + "request": "launch", + "command": "pnpm run debug", + "serverReadyAction": { + "pattern": "- Local:.+(https?://.+)", + "uriFormat": "%s", + "action": "debugWithChrome" + }, + "skipFiles": ["/**"] + } + ] +} \ No newline at end of file diff --git a/surfsense_web/Dockerfile b/surfsense_web/Dockerfile new file mode 100644 index 000000000..2ce0b0f4d --- /dev/null +++ b/surfsense_web/Dockerfile @@ -0,0 +1,25 @@ +FROM node:20-alpine + +WORKDIR /app + +# Install pnpm +RUN npm install -g pnpm + +# Copy package files +COPY package.json pnpm-lock.yaml ./ + +# Install dependencies +RUN pnpm install + +# Copy source code +COPY . . + +# Build app for production +# For development, we'll mount the source code as a volume +# so the build step will be skipped in development mode + +EXPOSE 3000 + +# Start Next.js in development mode by default +# This will be faster for development since we're mounting the code as a volume +CMD ["pnpm", "dev"] \ No newline at end of file diff --git a/surfsense_web/LICENSE b/surfsense_web/LICENSE new file mode 100644 index 000000000..8c19a3a15 --- /dev/null +++ b/surfsense_web/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2025 Rohan Verma + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/surfsense_web/README.md b/surfsense_web/README.md new file mode 100644 index 000000000..244b92bd8 --- /dev/null +++ b/surfsense_web/README.md @@ -0,0 +1,96 @@ +# Next.js Token Handler Component + +This project includes a reusable client component for Next.js that handles token storage from URL parameters. + +## TokenHandler Component + +The `TokenHandler` component is designed to: + +1. Extract a token from URL parameters +2. Store the token in localStorage +3. Redirect the user to a specified path + +### Usage + +```tsx +import TokenHandler from '@/components/TokenHandler'; + +export default function AuthCallbackPage() { + return ( +
+

Authentication Callback

+ +
+ ); +} +``` + +### Props + +The component accepts the following props: + +- `redirectPath` (optional): Path to redirect after storing token (default: '/') +- `tokenParamName` (optional): Name of the URL parameter containing the token (default: 'token') +- `storageKey` (optional): Key to use when storing in localStorage (default: 'auth_token') + +### Example URL + +After authentication, redirect users to: +``` +https://your-domain.com/auth/callback?token=your-auth-token +``` + +## Implementation Details + +- Uses Next.js's `useSearchParams` hook to access URL parameters +- Uses `useRouter` for client-side navigation after token storage +- Includes error handling for localStorage operations +- Displays a loading message while processing + +## Security Considerations + +- This implementation assumes the token is passed securely +- Consider using HTTPS to prevent token interception +- For enhanced security, consider using HTTP-only cookies instead of localStorage +- The token in the URL might be visible in browser history and server logs + +This is a [Next.js](https://nextjs.org) project bootstrapped with [`create-next-app`](https://nextjs.org/docs/app/api-reference/cli/create-next-app). + +## Getting Started + +First, run the development server: + +```bash +npm run dev +# or +yarn dev +# or +pnpm dev +# or +bun dev +``` + +Open [http://localhost:3000](http://localhost:3000) with your browser to see the result. + +You can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file. + +This project uses [`next/font`](https://nextjs.org/docs/app/building-your-application/optimizing/fonts) to automatically optimize and load [Geist](https://vercel.com/font), a new font family for Vercel. + +## Learn More + +To learn more about Next.js, take a look at the following resources: + +- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API. +- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial. + +You can check out [the Next.js GitHub repository](https://github.com/vercel/next.js) - your feedback and contributions are welcome! + +## Deploy on Vercel + +The easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js. + +Check out our [Next.js deployment documentation](https://nextjs.org/docs/app/building-your-application/deploying) for more details. diff --git a/surfsense_web/app/auth/callback/page.tsx b/surfsense_web/app/auth/callback/page.tsx new file mode 100644 index 000000000..d4ec41442 --- /dev/null +++ b/surfsense_web/app/auth/callback/page.tsx @@ -0,0 +1,19 @@ +import { Suspense } from 'react'; +import TokenHandler from '@/components/TokenHandler'; + +export default function AuthCallbackPage() { + return ( +
+

Authentication Callback

+ +
+
}> + + +
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/api-key/api-key-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/api-key/api-key-client.tsx new file mode 100644 index 000000000..4b33fff5e --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/api-key/api-key-client.tsx @@ -0,0 +1,176 @@ +'use client' + +import React from 'react' +import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" +import { Button } from "@/components/ui/button" +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert" +import { motion, AnimatePresence } from "framer-motion" +import { IconCheck, IconCopy, IconKey } from "@tabler/icons-react" +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip" +import { useApiKey } from "@/hooks/use-api-key" + +const fadeIn = { + hidden: { opacity: 0 }, + visible: { opacity: 1, transition: { duration: 0.4 } } +} + +const staggerContainer = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1 + } + } +} + +const ApiKeyClient = () => { + const { + apiKey, + isLoading, + copied, + copyToClipboard + } = useApiKey() + + return ( +
+ + +

API Key

+

+ Your API key for authenticating with the SurfSense API. +

+
+ + + + + Important + + Your API key grants full access to your account. Never share it publicly or with unauthorized users. + + + + + + + + Your API Key + + Use this key to authenticate your API requests. + + + + + {isLoading ? ( + + ) : apiKey ? ( + +
+ + {apiKey} + +
+ + + + + + +

{copied ? "Copied!" : "Copy to clipboard"}

+
+
+
+
+ ) : ( + + No API key found. + + )} +
+
+
+
+ + +

How to use your API key

+ + + + +

Authentication

+

+ Include your API key in the Authorization header of your requests: +

+ + + Authorization: Bearer {apiKey || 'YOUR_API_KEY'} + + +
+
+
+
+
+
+
+ ) +} + +export default ApiKeyClient \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/api-key/client-wrapper.tsx b/surfsense_web/app/dashboard/[search_space_id]/api-key/client-wrapper.tsx new file mode 100644 index 000000000..bbe1d018c --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/api-key/client-wrapper.tsx @@ -0,0 +1,32 @@ +'use client' + +import React, { useEffect, useState } from 'react' +import dynamic from 'next/dynamic' + +// Loading component with animation +const LoadingComponent = () => ( +
+
+

Loading API Key Management...

+
+) + +// Dynamically import the ApiKeyClient component +const ApiKeyClient = dynamic(() => import('./api-key-client'), { + ssr: false, + loading: () => +}) + +export default function ClientWrapper() { + const [isMounted, setIsMounted] = useState(false) + + useEffect(() => { + setIsMounted(true) + }, []) + + if (!isMounted) { + return + } + + return +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/api-key/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/api-key/page.tsx new file mode 100644 index 000000000..957652432 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/api-key/page.tsx @@ -0,0 +1,6 @@ +import React from 'react' +import ClientWrapper from './client-wrapper' + +export default function ApiKeyPage() { + return +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx new file mode 100644 index 000000000..c481bd6ec --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/chats/chats-client.tsx @@ -0,0 +1,510 @@ +'use client'; + +import { useState, useEffect } from 'react'; +import { motion, AnimatePresence } from 'framer-motion'; +import { useSearchParams } from 'next/navigation'; +import { MessageCircleMore, Search, Calendar, Tag, Trash2, ExternalLink, MoreHorizontal } from 'lucide-react'; +import { format } from 'date-fns'; + +// UI Components +import { Input } from '@/components/ui/input'; +import { Button } from '@/components/ui/button'; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from '@/components/ui/card'; +import { Badge } from '@/components/ui/badge'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, + DropdownMenuSeparator +} from '@/components/ui/dropdown-menu'; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from '@/components/ui/pagination'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, +} from "@/components/ui/dialog"; +import { + Select, + SelectContent, + SelectGroup, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; + +interface Chat { + created_at: string; + id: number; + type: string; + title: string; + messages: ChatMessage[]; + search_space_id: number; +} + +interface ChatMessage { + id: string; + createdAt: string; + role: string; + content: string; + parts?: any; +} + +interface ChatsPageClientProps { + searchSpaceId: string; +} + +const pageVariants = { + initial: { opacity: 0 }, + enter: { opacity: 1, transition: { duration: 0.3, ease: 'easeInOut' } }, + exit: { opacity: 0, transition: { duration: 0.3, ease: 'easeInOut' } } +}; + +const chatCardVariants = { + initial: { y: 20, opacity: 0 }, + animate: { y: 0, opacity: 1 }, + exit: { y: -20, opacity: 0 } +}; + +const MotionCard = motion(Card); + +export default function ChatsPageClient({ searchSpaceId }: ChatsPageClientProps) { + const [chats, setChats] = useState([]); + const [filteredChats, setFilteredChats] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [searchQuery, setSearchQuery] = useState(''); + const [currentPage, setCurrentPage] = useState(1); + const [totalPages, setTotalPages] = useState(1); + const [selectedType, setSelectedType] = useState('all'); + const [sortOrder, setSortOrder] = useState('newest'); + const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); + const [chatToDelete, setChatToDelete] = useState<{ id: number, title: string } | null>(null); + const [isDeleting, setIsDeleting] = useState(false); + + const chatsPerPage = 9; + const searchParams = useSearchParams(); + + // Get initial page from URL params if it exists + useEffect(() => { + const pageParam = searchParams.get('page'); + if (pageParam) { + const pageNumber = parseInt(pageParam, 10); + if (!isNaN(pageNumber) && pageNumber > 0) { + setCurrentPage(pageNumber); + } + } + }, [searchParams]); + + // Fetch chats from API + useEffect(() => { + const fetchChats = async () => { + try { + setIsLoading(true); + + // Get token from localStorage + const token = localStorage.getItem('surfsense_bearer_token'); + + if (!token) { + setError('Authentication token not found. Please log in again.'); + setIsLoading(false); + return; + } + + // Fetch all chats for this search space + const response = await fetch( + `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/?search_space_id=${searchSpaceId}`, + { + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + }, + cache: 'no-store', + } + ); + + if (!response.ok) { + const errorData = await response.json().catch(() => null); + throw new Error(`Failed to fetch chats: ${response.status} ${errorData?.error || ''}`); + } + + const data: Chat[] = await response.json(); + setChats(data); + setFilteredChats(data); + setError(null); + } catch (error) { + console.error('Error fetching chats:', error); + setError(error instanceof Error ? error.message : 'Unknown error occurred'); + setChats([]); + setFilteredChats([]); + } finally { + setIsLoading(false); + } + }; + + fetchChats(); + }, [searchSpaceId]); + + // Filter and sort chats based on search query, type, and sort order + useEffect(() => { + let result = [...chats]; + + // Filter by search term + if (searchQuery) { + const query = searchQuery.toLowerCase(); + result = result.filter(chat => + chat.title.toLowerCase().includes(query) + ); + } + + // Filter by type + if (selectedType !== 'all') { + result = result.filter(chat => chat.type === selectedType); + } + + // Sort chats + result.sort((a, b) => { + const dateA = new Date(a.created_at).getTime(); + const dateB = new Date(b.created_at).getTime(); + + return sortOrder === 'newest' ? dateB - dateA : dateA - dateB; + }); + + setFilteredChats(result); + setTotalPages(Math.max(1, Math.ceil(result.length / chatsPerPage))); + + // Reset to first page when filters change + if (currentPage !== 1 && (searchQuery || selectedType !== 'all' || sortOrder !== 'newest')) { + setCurrentPage(1); + } + }, [chats, searchQuery, selectedType, sortOrder, currentPage]); + + // Function to handle chat deletion + const handleDeleteChat = async () => { + if (!chatToDelete) return; + + setIsDeleting(true); + try { + const token = localStorage.getItem('surfsense_bearer_token'); + if (!token) { + setIsDeleting(false); + return; + } + + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${chatToDelete.id}`, { + method: 'DELETE', + headers: { + 'Authorization': `Bearer ${token}`, + 'Content-Type': 'application/json', + } + }); + + if (!response.ok) { + throw new Error(`Failed to delete chat: ${response.statusText}`); + } + + // Close dialog and refresh chats + setDeleteDialogOpen(false); + setChatToDelete(null); + + // Update local state by removing the deleted chat + setChats(prevChats => prevChats.filter(chat => chat.id !== chatToDelete.id)); + } catch (error) { + console.error('Error deleting chat:', error); + } finally { + setIsDeleting(false); + } + }; + + // Calculate pagination + const indexOfLastChat = currentPage * chatsPerPage; + const indexOfFirstChat = indexOfLastChat - chatsPerPage; + const currentChats = filteredChats.slice(indexOfFirstChat, indexOfLastChat); + + // Get unique chat types for filter dropdown + const chatTypes = ['all', ...Array.from(new Set(chats.map(chat => chat.type)))]; + + return ( + +
+
+

All Chats

+

View, search, and manage all your chats.

+
+ + {/* Filter and Search Bar */} +
+
+
+ + setSearchQuery(e.target.value)} + /> +
+ + +
+ +
+ +
+
+ + {/* Status Messages */} + {isLoading && ( +
+
+
+

Loading chats...

+
+
+ )} + + {error && !isLoading && ( +
+

Error loading chats

+

{error}

+
+ )} + + {!isLoading && !error && filteredChats.length === 0 && ( +
+ +

No chats found

+

+ {searchQuery || selectedType !== 'all' + ? 'Try adjusting your search filters' + : 'Start a new chat to get started'} +

+
+ )} + + {/* Chat Grid */} + {!isLoading && !error && filteredChats.length > 0 && ( + +
+ {currentChats.map((chat, index) => ( + + +
+
+ {chat.title || `Chat ${chat.id}`} + + + + {format(new Date(chat.created_at), 'MMM d, yyyy')} + + +
+ + + + + + window.location.href = `/dashboard/${chat.search_space_id}/researcher/${chat.id}`}> + + View Chat + + + { + setChatToDelete({ id: chat.id, title: chat.title || `Chat ${chat.id}` }); + setDeleteDialogOpen(true); + }} + > + + Delete Chat + + + +
+
+ +
+ {chat.messages && chat.messages.length > 0 + ? typeof chat.messages[0] === 'string' + ? chat.messages[0] + : chat.messages[0]?.content || 'No message content' + : 'No messages in this chat.'} +
+
+ +
+ + {chat.messages?.length || 0} messages +
+ + + {chat.type || 'Unknown'} + +
+
+ ))} +
+
+ )} + + {/* Pagination */} + {!isLoading && !error && totalPages > 1 && ( + + + + { + e.preventDefault(); + if (currentPage > 1) setCurrentPage(currentPage - 1); + }} + className={currentPage <= 1 ? 'pointer-events-none opacity-50' : ''} + /> + + + {Array.from({ length: totalPages }).map((_, index) => { + const pageNumber = index + 1; + const isVisible = + pageNumber === 1 || + pageNumber === totalPages || + (pageNumber >= currentPage - 1 && pageNumber <= currentPage + 1); + + if (!isVisible) { + // Show ellipsis at appropriate positions + if (pageNumber === 2 || pageNumber === totalPages - 1) { + return ( + + ... + + ); + } + return null; + } + + return ( + + { + e.preventDefault(); + setCurrentPage(pageNumber); + }} + isActive={pageNumber === currentPage} + > + {pageNumber} + + + ); + })} + + + { + e.preventDefault(); + if (currentPage < totalPages) setCurrentPage(currentPage + 1); + }} + className={currentPage >= totalPages ? 'pointer-events-none opacity-50' : ''} + /> + + + + )} +
+ + {/* Delete Confirmation Dialog */} + + + + + + Delete Chat + + + Are you sure you want to delete {chatToDelete?.title}? This action cannot be undone. + + + + + + + + +
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/chats/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/chats/page.tsx new file mode 100644 index 000000000..d8662829f --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/chats/page.tsx @@ -0,0 +1,18 @@ +import { Suspense } from 'react'; +import ChatsPageClient from './chats-client'; + +interface PageProps { + params: { + search_space_id: string; + }; +} + +export default function ChatsPage({ params }: PageProps) { + return ( + +
+
}> + + + ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx new file mode 100644 index 000000000..c9a768461 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/client-layout.tsx @@ -0,0 +1,44 @@ +'use client'; + +import { + SidebarInset, + SidebarProvider, + SidebarTrigger, +} from "@/components/ui/sidebar" +import { ThemeTogglerComponent } from "@/components/theme/theme-toggle" +import React from 'react' +import { Separator } from "@/components/ui/separator" +import { AppSidebarProvider } from "@/components/sidebar/AppSidebarProvider" + +export function DashboardClientLayout({ + children, + searchSpaceId, + navSecondary, + navMain +}: { + children: React.ReactNode; + searchSpaceId: string; + navSecondary: any[]; + navMain: any[]; +}) { + return ( + + {/* Use AppSidebarProvider which fetches user, search space, and recent chats */} + + +
+
+ + + +
+
+ {children} +
+
+ ) +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx new file mode 100644 index 000000000..dfc8b828a --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/(manage)/page.tsx @@ -0,0 +1,256 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { toast } from "sonner"; +import { Edit, Plus, Search, Trash2, ExternalLink, RefreshCw } from "lucide-react"; + +import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip"; + +// Helper function to get connector type display name +const getConnectorTypeDisplay = (type: string): string => { + const typeMap: Record = { + "SERPER_API": "Serper API", + "TAVILY_API": "Tavily API", + "SLACK_CONNECTOR": "Slack", + "NOTION_CONNECTOR": "Notion", + // Add other connector types here as needed + }; + return typeMap[type] || type; +}; + +// Helper function to format date with time +const formatDateTime = (dateString: string | null): string => { + if (!dateString) return "Never"; + + const date = new Date(dateString); + return new Intl.DateTimeFormat('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit' + }).format(date); +}; + +export default function ConnectorsPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + + const { connectors, isLoading, error, deleteConnector, indexConnector } = useSearchSourceConnectors(); + const [connectorToDelete, setConnectorToDelete] = useState(null); + const [indexingConnectorId, setIndexingConnectorId] = useState(null); + + useEffect(() => { + if (error) { + toast.error("Failed to load connectors"); + console.error("Error fetching connectors:", error); + } + }, [error]); + + // Handle connector deletion + const handleDeleteConnector = async () => { + if (connectorToDelete === null) return; + + try { + await deleteConnector(connectorToDelete); + toast.success("Connector deleted successfully"); + } catch (error) { + console.error("Error deleting connector:", error); + toast.error("Failed to delete connector"); + } finally { + setConnectorToDelete(null); + } + }; + + // Handle connector indexing + const handleIndexConnector = async (connectorId: number) => { + setIndexingConnectorId(connectorId); + try { + await indexConnector(connectorId, searchSpaceId); + toast.success("Connector content indexed successfully"); + } catch (error) { + console.error("Error indexing connector content:", error); + toast.error(error instanceof Error ? error.message : "Failed to index connector content"); + } finally { + setIndexingConnectorId(null); + } + }; + + return ( +
+ +
+

Connectors

+

+ Manage your connected services and data sources. +

+
+ +
+ + + + Your Connectors + + View and manage all your connected services. + + + + {isLoading ? ( +
+
+
+
+
+
+ ) : connectors.length === 0 ? ( +
+

No connectors found

+

+ You haven't added any connectors yet. Add one to enhance your search capabilities. +

+ +
+ ) : ( +
+ + + + Name + Type + Last Indexed + Actions + + + + {connectors.map((connector) => ( + + {connector.name} + {getConnectorTypeDisplay(connector.connector_type)} + + {connector.is_indexable + ? formatDateTime(connector.last_indexed_at) + : "Not indexable"} + + +
+ {connector.is_indexable && ( + + + + + + +

Index Content

+
+
+
+ )} + + + + + + + + Delete Connector + + Are you sure you want to delete this connector? This action cannot be undone. + + + + setConnectorToDelete(null)}> + Cancel + + + Delete + + + + +
+
+
+ ))} +
+
+
+ )} +
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx new file mode 100644 index 000000000..7700bc877 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/[connector_id]/page.tsx @@ -0,0 +1,279 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { toast } from "sonner"; +import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; + +import { useSearchSourceConnectors, SearchSourceConnector } from "@/hooks/useSearchSourceConnectors"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; + +// Define the form schema with Zod +const apiConnectorFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + api_key: z.string().min(10, { + message: "API key is required and must be valid.", + }), +}); + +// Helper function to get connector type display name +const getConnectorTypeDisplay = (type: string): string => { + const typeMap: Record = { + "SERPER_API": "Serper API", + "TAVILY_API": "Tavily API", + "SLACK_CONNECTOR": "Slack Connector", + "NOTION_CONNECTOR": "Notion Connector", + // Add other connector types here as needed + }; + return typeMap[type] || type; +}; + +// Define the type for the form values +type ApiConnectorFormValues = z.infer; + +export default function EditConnectorPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const connectorId = parseInt(params.connector_id as string, 10); + + const { connectors, updateConnector } = useSearchSourceConnectors(); + const [connector, setConnector] = useState(null); + const [isLoading, setIsLoading] = useState(true); + const [isSubmitting, setIsSubmitting] = useState(false); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(apiConnectorFormSchema), + defaultValues: { + name: "", + api_key: "", + }, + }); + + // Get API key field name based on connector type + const getApiKeyFieldName = (connectorType: string): string => { + const fieldMap: Record = { + "SERPER_API": "SERPER_API_KEY", + "TAVILY_API": "TAVILY_API_KEY", + "SLACK_CONNECTOR": "SLACK_BOT_TOKEN", + "NOTION_CONNECTOR": "NOTION_INTEGRATION_TOKEN" + }; + return fieldMap[connectorType] || ""; + }; + + // Find connector in the list + useEffect(() => { + const currentConnector = connectors.find(c => c.id === connectorId); + + if (currentConnector) { + setConnector(currentConnector); + + // Check if connector type is supported + const apiKeyField = getApiKeyFieldName(currentConnector.connector_type); + if (apiKeyField) { + form.reset({ + name: currentConnector.name, + api_key: currentConnector.config[apiKeyField] || "", + }); + } else { + // Redirect if not a supported connector type + toast.error("This connector type is not supported for editing"); + router.push(`/dashboard/${searchSpaceId}/connectors`); + } + + setIsLoading(false); + } else if (!isLoading && connectors.length > 0) { + // If connectors are loaded but this one isn't found + toast.error("Connector not found"); + router.push(`/dashboard/${searchSpaceId}/connectors`); + } + }, [connectors, connectorId, form, router, searchSpaceId, isLoading]); + + // Handle form submission + const onSubmit = async (values: ApiConnectorFormValues) => { + if (!connector) return; + + setIsSubmitting(true); + try { + const apiKeyField = getApiKeyFieldName(connector.connector_type); + + // Only update the API key if a new one was provided + const updatedConfig = { ...connector.config }; + if (values.api_key) { + updatedConfig[apiKeyField] = values.api_key; + } + + await updateConnector(connectorId, { + name: values.name, + connector_type: connector.connector_type, + config: updatedConfig, + }); + + toast.success("Connector updated successfully!"); + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error updating connector:", error); + toast.error(error instanceof Error ? error.message : "Failed to update connector"); + } finally { + setIsSubmitting(false); + } + }; + + if (isLoading) { + return ( +
+
+
+
+
+
+ ); + } + + return ( +
+ + + + + + + Edit {connector ? getConnectorTypeDisplay(connector.connector_type) : ""} Connector + + + Update your connector settings. + + + + + + API Key Security + + Your API key is stored securely. For security reasons, we don't display your existing API key. + If you don't update the API key field, your existing key will be preserved. + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + + {connector?.connector_type === "SLACK_CONNECTOR" + ? "Slack Bot Token" + : connector?.connector_type === "NOTION_CONNECTOR" + ? "Notion Integration Token" + : "API Key"} + + + + + + {connector?.connector_type === "SLACK_CONNECTOR" + ? "Enter a new Slack Bot Token or leave blank to keep your existing token." + : connector?.connector_type === "NOTION_CONNECTOR" + ? "Enter a new Notion Integration Token or leave blank to keep your existing token." + : "Enter a new API key or leave blank to keep your existing key."} + + + + )} + /> + +
+ +
+ + +
+
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx new file mode 100644 index 000000000..1d9e3efa0 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/notion-connector/page.tsx @@ -0,0 +1,317 @@ +"use client"; + +import { useState } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { toast } from "sonner"; +import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; + +import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +// Define the form schema with Zod +const notionConnectorFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + integration_token: z.string().min(10, { + message: "Notion Integration Token is required and must be valid.", + }), +}); + +// Define the type for the form values +type NotionConnectorFormValues = z.infer; + +export default function NotionConnectorPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [isSubmitting, setIsSubmitting] = useState(false); + const { createConnector } = useSearchSourceConnectors(); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(notionConnectorFormSchema), + defaultValues: { + name: "Notion Connector", + integration_token: "", + }, + }); + + // Handle form submission + const onSubmit = async (values: NotionConnectorFormValues) => { + setIsSubmitting(true); + try { + await createConnector({ + name: values.name, + connector_type: "NOTION_CONNECTOR", + config: { + NOTION_INTEGRATION_TOKEN: values.integration_token, + }, + is_indexable: true, + last_indexed_at: null, + }); + + toast.success("Notion connector created successfully!"); + + // Navigate back to connectors page + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error creating connector:", error); + toast.error(error instanceof Error ? error.message : "Failed to create connector"); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + + + + + Connect + Documentation + + + + + + Connect Notion Workspace + + Integrate with Notion to search and retrieve information from your workspace pages and databases. This connector can index your Notion content for search. + + + + + + Notion Integration Token Required + + You'll need a Notion Integration Token to use this connector. You can create a Notion integration and get the token from{" "} + + Notion Integrations Dashboard + + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + Notion Integration Token + + + + + Your Notion Integration Token will be encrypted and stored securely. It typically starts with "ntn_". + + + + )} + /> + +
+ +
+ + +
+ +

What you get with Notion integration:

+
    +
  • Search through your Notion pages and databases
  • +
  • Access documents, wikis, and knowledge bases
  • +
  • Connect your team's knowledge directly to your search space
  • +
  • Keep your search results up-to-date with latest Notion content
  • +
  • Index your Notion documents for enhanced search capabilities
  • +
+
+
+
+ + + + + Notion Connector Documentation + + Learn how to set up and use the Notion connector to index your workspace data. + + + +
+

How it works

+

+ The Notion connector uses the Notion search API to fetch all pages that the connector has access to within a workspace. +

+
    +
  • For follow up indexing runs, the connector only retrieves pages that have been updated since the last indexing attempt.
  • +
  • Indexing is configured to run every 10 minutes, so page updates should appear within 10 minutes.
  • +
+
+ + + + Authorization + + + + No Admin Access Required + + There's no requirement to be an Admin to share information with an integration. Any member can share pages and databases with it. + + + +
+
+

Step 1: Create an integration

+
    +
  1. Visit https://www.notion.com/my-integrations in your browser.
  2. +
  3. Click the + New integration button.
  4. +
  5. Name the integration (something like "Search Connector" could work).
  6. +
  7. Select "Read content" as the only capability required.
  8. +
  9. Click Submit to create the integration.
  10. +
  11. On the next page, you'll find your Notion integration token. Make a copy of it as you'll need it to configure the connector.
  12. +
+
+ +
+

Step 2: Share pages/databases with your integration

+

+ To keep your information secure, integrations don't have access to any pages or databases in the workspace at first. + You must share specific pages with an integration in order for the connector to access those pages. +

+
    +
  1. Go to the page/database in your workspace.
  2. +
  3. Click the ••• on the top right corner of the page.
  4. +
  5. Scroll to the bottom of the pop-up and click Add connections.
  6. +
  7. Search for and select the new integration in the Search for connections... menu.
  8. +
  9. + Important: +
      +
    • If you've added a page, all child pages also become accessible.
    • +
    • If you've added a database, all rows (and their children) become accessible.
    • +
    +
  10. +
+
+
+
+
+ + + Indexing + +
    +
  1. Navigate to the Connector Dashboard and select the Notion Connector.
  2. +
  3. Place the Integration Token under Step 1 Provide Credentials.
  4. +
  5. Click Connect to establish the connection.
  6. +
+ + + + Indexing Behavior + + The Notion connector currently indexes everything it has access to. If you want to limit specific content being indexed, simply unshare the database from Notion with the integration. + + +
+
+
+
+
+
+
+
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx new file mode 100644 index 000000000..d68de69d3 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/page.tsx @@ -0,0 +1,256 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { + IconBrandGoogle, + IconBrandSlack, + IconBrandWindows, + IconBrandDiscord, + IconSearch, + IconMessages, + IconDatabase, + IconCloud, + IconBrandGithub, + IconBrandNotion, + IconMail, + IconBrandZoom, + IconChevronRight, +} from "@tabler/icons-react"; +import { motion, AnimatePresence } from "framer-motion"; +import { useState } from "react"; +import { useParams } from "next/navigation"; +import Link from "next/link"; +import { Button } from "@/components/ui/button"; +import { Separator } from "@/components/ui/separator"; +import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"; + +// Define connector categories and their connectors +const connectorCategories = [ + { + id: "search-engines", + title: "Search Engines", + description: "Connect to search engines to enhance your research capabilities.", + icon: , + connectors: [ + { + id: "tavily-api", + title: "Tavily Search API", + description: "Connect to Tavily Search API to search the web.", + icon: , + status: "available", + }, + { + id: "serper-api", + title: "Serper API", + description: "Connect to Serper API to search the web.", + icon: , + status: "coming-soon", + }, + ], + }, + { + id: "team-chats", + title: "Team Chats", + description: "Connect to your team communication platforms.", + icon: , + connectors: [ + { + id: "slack-connector", + title: "Slack", + description: "Connect to your Slack workspace to access messages and channels.", + icon: , + status: "available", + }, + { + id: "ms-teams", + title: "Microsoft Teams", + description: "Connect to Microsoft Teams to access your team's conversations.", + icon: , + status: "coming-soon", + }, + { + id: "discord", + title: "Discord", + description: "Connect to Discord servers to access messages and channels.", + icon: , + status: "coming-soon", + }, + ], + }, + { + id: "knowledge-bases", + title: "Knowledge Bases", + description: "Connect to your knowledge bases and documentation.", + icon: , + connectors: [ + { + id: "notion-connector", + title: "Notion", + description: "Connect to your Notion workspace to access pages and databases.", + icon: , + status: "available", + }, + { + id: "github", + title: "GitHub", + description: "Connect to GitHub repositories to access code and documentation.", + icon: , + status: "coming-soon", + }, + ], + }, + { + id: "communication", + title: "Communication", + description: "Connect to your email and meeting platforms.", + icon: , + connectors: [ + { + id: "gmail", + title: "Gmail", + description: "Connect to your Gmail account to access emails.", + icon: , + status: "coming-soon", + }, + { + id: "zoom", + title: "Zoom", + description: "Connect to Zoom to access meeting recordings and transcripts.", + icon: , + status: "coming-soon", + }, + ], + }, +]; + +export default function ConnectorsPage() { + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [expandedCategories, setExpandedCategories] = useState(["search-engines"]); + + const toggleCategory = (categoryId: string) => { + setExpandedCategories(prev => + prev.includes(categoryId) + ? prev.filter(id => id !== categoryId) + : [...prev, categoryId] + ); + }; + + return ( +
+ +

Connect Your Tools

+

+ Integrate with your favorite services to enhance your research capabilities. +

+
+ +
+ {connectorCategories.map((category, categoryIndex) => ( + toggleCategory(category.id)} + className="border rounded-lg overflow-hidden bg-card" + > + + +
+
+ {category.icon} +
+
+

{category.title}

+

{category.description}

+
+
+ +
+
+ + +
+ + {category.connectors.map((connector, index) => ( + +
+ +
+ {connector.icon} +
+ +
+

+ {connector.title} +

+ {connector.status === "coming-soon" && ( + Coming soon + )} +
+ +

+ {connector.description} +

+ + {connector.status === "available" ? ( + + + + ) : ( + + )} + + ))} + +
+ + + ))} +
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx new file mode 100644 index 000000000..7ace10d6d --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/serper-api/page.tsx @@ -0,0 +1,207 @@ +"use client"; + +import { useState } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { toast } from "sonner"; +import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; + +import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; + +// Define the form schema with Zod +const serperApiFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + api_key: z.string().min(10, { + message: "API key is required and must be valid.", + }), +}); + +// Define the type for the form values +type SerperApiFormValues = z.infer; + +export default function SerperApiPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [isSubmitting, setIsSubmitting] = useState(false); + const { createConnector } = useSearchSourceConnectors(); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(serperApiFormSchema), + defaultValues: { + name: "Serper API Connector", + api_key: "", + }, + }); + + // Handle form submission + const onSubmit = async (values: SerperApiFormValues) => { + setIsSubmitting(true); + try { + await createConnector({ + name: values.name, + connector_type: "SERPER_API", + config: { + SERPER_API_KEY: values.api_key, + }, + is_indexable: false, + last_indexed_at: null, + }); + + toast.success("Serper API connector created successfully!"); + + // Navigate back to connectors page + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error creating connector:", error); + toast.error(error instanceof Error ? error.message : "Failed to create connector"); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + + + + + Connect Serper API + + Integrate with Serper API to enhance your search capabilities with Google search results. + + + + + + API Key Required + + You'll need a Serper API key to use this connector. You can get one by signing up at{" "} + + serper.dev + + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + Serper API Key + + + + + Your API key will be encrypted and stored securely. + + + + )} + /> + +
+ +
+ + +
+ +

What you get with Serper API:

+
    +
  • Access to Google search results directly in your research
  • +
  • Real-time information from the web
  • +
  • Enhanced search capabilities for your projects
  • +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx new file mode 100644 index 000000000..3eea9d6e8 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/slack-connector/page.tsx @@ -0,0 +1,351 @@ +"use client"; + +import { useState } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { toast } from "sonner"; +import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; + +import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; +import { + Accordion, + AccordionContent, + AccordionItem, + AccordionTrigger, +} from "@/components/ui/accordion"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; + +// Define the form schema with Zod +const slackConnectorFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + bot_token: z.string().min(10, { + message: "Bot User OAuth Token is required and must be valid.", + }), +}); + +// Define the type for the form values +type SlackConnectorFormValues = z.infer; + +export default function SlackConnectorPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [isSubmitting, setIsSubmitting] = useState(false); + const { createConnector } = useSearchSourceConnectors(); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(slackConnectorFormSchema), + defaultValues: { + name: "Slack Connector", + bot_token: "", + }, + }); + + // Handle form submission + const onSubmit = async (values: SlackConnectorFormValues) => { + setIsSubmitting(true); + try { + await createConnector({ + name: values.name, + connector_type: "SLACK_CONNECTOR", + config: { + SLACK_BOT_TOKEN: values.bot_token, + }, + is_indexable: true, + last_indexed_at: null, + }); + + toast.success("Slack connector created successfully!"); + + // Navigate back to connectors page + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error creating connector:", error); + toast.error(error instanceof Error ? error.message : "Failed to create connector"); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + + + + + Connect + Documentation + + + + + + Connect Slack Workspace + + Integrate with Slack to search and retrieve information from your workspace channels and conversations. This connector can index your Slack messages for search. + + + + + + Bot User OAuth Token Required + + You'll need a Slack Bot User OAuth Token to use this connector. You can create a Slack app and get the token from{" "} + + Slack API Dashboard + + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + Slack Bot User OAuth Token + + + + + Your Bot User OAuth Token will be encrypted and stored securely. It typically starts with "xoxb-". + + + + )} + /> + +
+ +
+ + +
+ +

What you get with Slack integration:

+
    +
  • Search through your Slack channels and conversations
  • +
  • Access historical messages and shared files
  • +
  • Connect your team's knowledge directly to your search space
  • +
  • Keep your search results up-to-date with latest communications
  • +
  • Index your Slack messages for enhanced search capabilities
  • +
+
+
+
+ + + + + Slack Connector Documentation + + Learn how to set up and use the Slack connector to index your workspace data. + + + +
+

How it works

+

+ The Slack connector indexes all public channels for a given workspace. +

+
    +
  • Upcoming: Support for private channels by tagging/adding the Slack Bot to private channels.
  • +
+
+ + + + Authorization + + + + Admin Access Required + + You must be an admin of the Slack workspace to set up the connector. + + + +
    +
  1. Navigate and sign in to https://api.slack.com/apps.
  2. +
  3. + Create a new Slack app: +
      +
    • Click the Create New App button in the top right.
    • +
    • Select From an app manifest option.
    • +
    • Select the relevant workspace from the dropdown and click Next.
    • +
    +
  4. +
  5. + Select the "YAML" tab, paste the following manifest into the text box, and click Next: +
    +
    +{`display_information:
    +  name: SlackConnector
    +  description: ReadOnly Connector for indexing
    +features:
    +  bot_user:
    +    display_name: SlackConnector
    +    always_online: false
    +oauth_config:
    +  scopes:
    +    bot:
    +      - channels:history
    +      - channels:read
    +      - groups:history
    +      - groups:read
    +      - channels:join
    +      - im:history
    +      - users:read
    +      - users:read.email
    +      - usergroups:read
    +settings:
    +  org_deploy_enabled: false
    +  socket_mode_enabled: false
    +  token_rotation_enabled: false`}
    +                            
    +
    +
  6. +
  7. Click the Create button.
  8. +
  9. In the app page, navigate to the OAuth & Permissions tab under the Features header.
  10. +
  11. Copy the Bot User OAuth Token, this will be used to access Slack.
  12. +
+
+
+ + + Indexing + +
    +
  1. Navigate to the Connector Dashboard and select the Slack Connector.
  2. +
  3. Place the Bot User OAuth Token under Step 1 Provide Credentials.
  4. +
  5. Click Connect to establish the connection.
  6. +
+ + + + Important: Invite Bot to Channels + + After connecting, you must invite the bot to each channel you want to index. In each Slack channel, type: +
/invite @YourBotName
+

Without this step, you'll get a "not_in_channel" error when the connector tries to access channel messages.

+
+
+ + + + First Indexing + + The first indexing pulls all of the public channels and takes longer than future updates. Only channels where the bot has been invited will be fully indexed. + + + +
+

Troubleshooting:

+
    +
  • + not_in_channel error: If you see this error in logs, it means the bot hasn't been invited to a channel it's trying to access. Use the /invite @YourBotName command in that channel. +
  • +
  • + Alternative approach: You can add the chat:write.public scope to your Slack app to allow it to access public channels without an explicit invitation. +
  • +
  • + For private channels: The bot must always be invited using the /invite command. +
  • +
+
+
+
+
+
+
+
+
+
+
+ ); +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx new file mode 100644 index 000000000..03ef3d160 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/connectors/add/tavily-api/page.tsx @@ -0,0 +1,207 @@ +"use client"; + +import { useState } from "react"; +import { useRouter, useParams } from "next/navigation"; +import { motion } from "framer-motion"; +import { zodResolver } from "@hookform/resolvers/zod"; +import { useForm } from "react-hook-form"; +import * as z from "zod"; +import { toast } from "sonner"; +import { ArrowLeft, Check, Info, Loader2 } from "lucide-react"; + +import { useSearchSourceConnectors } from "@/hooks/useSearchSourceConnectors"; +import { + Form, + FormControl, + FormDescription, + FormField, + FormItem, + FormLabel, + FormMessage, +} from "@/components/ui/form"; +import { Input } from "@/components/ui/input"; +import { Button } from "@/components/ui/button"; +import { + Card, + CardContent, + CardDescription, + CardFooter, + CardHeader, + CardTitle, +} from "@/components/ui/card"; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; + +// Define the form schema with Zod +const tavilyApiFormSchema = z.object({ + name: z.string().min(3, { + message: "Connector name must be at least 3 characters.", + }), + api_key: z.string().min(10, { + message: "API key is required and must be valid.", + }), +}); + +// Define the type for the form values +type TavilyApiFormValues = z.infer; + +export default function TavilyApiPage() { + const router = useRouter(); + const params = useParams(); + const searchSpaceId = params.search_space_id as string; + const [isSubmitting, setIsSubmitting] = useState(false); + const { createConnector } = useSearchSourceConnectors(); + + // Initialize the form + const form = useForm({ + resolver: zodResolver(tavilyApiFormSchema), + defaultValues: { + name: "Tavily API Connector", + api_key: "", + }, + }); + + // Handle form submission + const onSubmit = async (values: TavilyApiFormValues) => { + setIsSubmitting(true); + try { + await createConnector({ + name: values.name, + connector_type: "TAVILY_API", + config: { + TAVILY_API_KEY: values.api_key, + }, + is_indexable: false, + last_indexed_at: null, + }); + + toast.success("Tavily API connector created successfully!"); + + // Navigate back to connectors page + router.push(`/dashboard/${searchSpaceId}/connectors`); + } catch (error) { + console.error("Error creating connector:", error); + toast.error(error instanceof Error ? error.message : "Failed to create connector"); + } finally { + setIsSubmitting(false); + } + }; + + return ( +
+ + + + + + Connect Tavily API + + Integrate with Tavily API to enhance your search capabilities with AI-powered search results. + + + + + + API Key Required + + You'll need a Tavily API key to use this connector. You can get one by signing up at{" "} + + tavily.com + + + + +
+ + ( + + Connector Name + + + + + A friendly name to identify this connector. + + + + )} + /> + + ( + + Tavily API Key + + + + + Your API key will be encrypted and stored securely. + + + + )} + /> + +
+ +
+ + +
+ +

What you get with Tavily API:

+
    +
  • AI-powered search results tailored to your queries
  • +
  • Real-time information from the web
  • +
  • Enhanced search capabilities for your projects
  • +
+
+
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx new file mode 100644 index 000000000..fd2f25bc2 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/(manage)/page.tsx @@ -0,0 +1,1030 @@ +"use client"; + +import { cn } from "@/lib/utils"; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from "@/components/ui/alert-dialog"; +import { Badge } from "@/components/ui/badge"; +import { Button } from "@/components/ui/button"; +import { Checkbox } from "@/components/ui/checkbox"; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/components/ui/dropdown-menu"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Pagination, PaginationContent, PaginationItem } from "@/components/ui/pagination"; +import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; +import { + ColumnDef, + ColumnFiltersState, + FilterFn, + PaginationState, + Row, + SortingState, + VisibilityState, + flexRender, + getCoreRowModel, + getFacetedUniqueValues, + getFilteredRowModel, + getPaginationRowModel, + getSortedRowModel, + useReactTable, +} from "@tanstack/react-table"; +import { + AlertCircle, + ChevronDown, + ChevronFirst, + ChevronLast, + ChevronLeft, + ChevronRight, + ChevronUp, + CircleAlert, + CircleX, + Columns3, + Filter, + ListFilter, + Plus, + FileText, + Globe, + MessageSquare, + FileX, + File, + Trash, + MoreHorizontal, + Webhook, +} from "lucide-react"; +import { useEffect, useId, useMemo, useRef, useState, useContext } from "react"; +import { motion, AnimatePresence } from "framer-motion"; +import { useParams } from "next/navigation"; +import { useDocuments } from "@/hooks/use-documents"; +import React from "react"; +import { toast } from "sonner"; +import ReactMarkdown from "react-markdown"; +import rehypeRaw from "rehype-raw"; +import rehypeSanitize from "rehype-sanitize"; +import remarkGfm from "remark-gfm"; +import { DocumentViewer } from "@/components/document-viewer"; +import { JsonMetadataViewer } from "@/components/json-metadata-viewer"; +import { IconBrandNotion, IconBrandSlack } from "@tabler/icons-react"; + +// Define animation variants for reuse +const fadeInScale = { + hidden: { opacity: 0, scale: 0.95 }, + visible: { + opacity: 1, + scale: 1, + transition: { type: "spring", stiffness: 300, damping: 30 } + }, + exit: { + opacity: 0, + scale: 0.95, + transition: { duration: 0.15 } + } +}; + +type Document = { + id: number; + title: string; + document_type: "EXTENSION" | "CRAWLED_URL" | "SLACK_CONNECTOR" | "NOTION_CONNECTOR" | "FILE"; + document_metadata: any; + content: string; + created_at: string; + search_space_id: number; +}; + +// Custom filter function for multi-column searching +const multiColumnFilterFn: FilterFn = (row, columnId, filterValue) => { + const searchableRowContent = `${row.original.title}`.toLowerCase(); + const searchTerm = (filterValue ?? "").toLowerCase(); + return searchableRowContent.includes(searchTerm); +}; + +const statusFilterFn: FilterFn = (row, columnId, filterValue: string[]) => { + if (!filterValue?.length) return true; + const status = row.getValue(columnId) as string; + return filterValue.includes(status); +}; + +// Add document type icons mapping +const documentTypeIcons = { + EXTENSION: Webhook, + CRAWLED_URL: Globe, + SLACK_CONNECTOR: IconBrandSlack, + NOTION_CONNECTOR: IconBrandNotion, + FILE: File, +} as const; + +const columns: ColumnDef[] = [ + { + id: "select", + header: ({ table }) => ( + table.toggleAllPageRowsSelected(!!value)} + aria-label="Select all" + /> + ), + cell: ({ row }) => ( + row.toggleSelected(!!value)} + aria-label="Select row" + /> + ), + size: 28, + enableSorting: false, + enableHiding: false, + }, + { + header: "Title", + accessorKey: "title", + cell: ({ row }) => { + const Icon = documentTypeIcons[row.original.document_type]; + return ( + + + {row.getValue("title")} + + ); + }, + size: 250, + }, + { + header: "Type", + accessorKey: "document_type", + cell: ({ row }) => { + const type = row.getValue("document_type") as keyof typeof documentTypeIcons; + const Icon = documentTypeIcons[type]; + return ( +
+
+ +
+ + {type.split('_').map(word => word.charAt(0) + word.slice(1).toLowerCase()).join(' ')} + +
+ ); + }, + size: 180, + }, + { + header: "Content Summary", + accessorKey: "content", + cell: ({ row }) => { + const content = row.getValue("content") as string; + const title = row.getValue("title") as string; + + // Create a truncated preview (first 150 characters) + const previewContent = content.length > 150 + ? content.substring(0, 150) + "..." + : content; + + return ( + + ); + }, + size: 300, + }, + { + header: "Created At", + accessorKey: "created_at", + cell: ({ row }) => { + const date = new Date(row.getValue("created_at")); + return date.toLocaleDateString(); + }, + size: 120, + }, + { + id: "actions", + header: () => Actions, + cell: ({ row }) => , + size: 60, + enableHiding: false, + }, +]; + +// Create a context to share the deleteDocument function +const DocumentsContext = React.createContext<{ + deleteDocument: (id: number) => Promise; + refreshDocuments: () => Promise; +} | null>(null); + +export default function DocumentsTable() { + const id = useId(); + const params = useParams(); + const searchSpaceId = Number(params.search_space_id); + const { documents, loading, error, refreshDocuments, deleteDocument } = useDocuments(searchSpaceId); + + console.log("Search Space ID:", searchSpaceId); + console.log("Documents loaded:", documents?.length); + + useEffect(() => { + console.log("Delete document function available:", !!deleteDocument); + }, [deleteDocument]); + + const [columnFilters, setColumnFilters] = useState([]); + const [columnVisibility, setColumnVisibility] = useState({}); + const [pagination, setPagination] = useState({ + pageIndex: 0, + pageSize: 10, + }); + const inputRef = useRef(null); + + const [sorting, setSorting] = useState([ + { + id: "title", + desc: false, + }, + ]); + + const [data, setData] = useState([]); + + useEffect(() => { + if (documents) { + setData(documents); + } + }, [documents]); + + const handleDeleteRows = async () => { + const selectedRows = table.getSelectedRowModel().rows; + console.log("Deleting selected rows:", selectedRows.length); + + if (selectedRows.length === 0) { + toast.error("No rows selected"); + return; + } + + // Create an array of promises for each delete operation + const deletePromises = selectedRows.map(row => { + console.log("Deleting row with ID:", row.original.id); + return deleteDocument(row.original.id); + }); + + try { + // Execute all delete operations + const results = await Promise.all(deletePromises); + console.log("Delete results:", results); + + // Check if all deletions were successful + const allSuccessful = results.every(result => result === true); + + if (allSuccessful) { + toast.success(`Successfully deleted ${selectedRows.length} document(s)`); + } else { + toast.error("Some documents could not be deleted"); + } + + // Refresh the documents list after all deletions + await refreshDocuments(); + table.resetRowSelection(); + } catch (error: any) { + console.error("Error deleting documents:", error); + toast.error("Error deleting documents"); + } + }; + + const table = useReactTable({ + data, + columns, + getCoreRowModel: getCoreRowModel(), + getSortedRowModel: getSortedRowModel(), + onSortingChange: setSorting, + enableSortingRemoval: false, + getPaginationRowModel: getPaginationRowModel(), + onPaginationChange: setPagination, + onColumnFiltersChange: setColumnFilters, + onColumnVisibilityChange: setColumnVisibility, + getFilteredRowModel: getFilteredRowModel(), + getFacetedUniqueValues: getFacetedUniqueValues(), + state: { + sorting, + pagination, + columnFilters, + columnVisibility, + }, + }); + + // Get unique status values + const uniqueStatusValues = useMemo(() => { + const statusColumn = table.getColumn("document_type"); + + if (!statusColumn) return []; + + const values = Array.from(statusColumn.getFacetedUniqueValues().keys()); + + return values.sort(); + }, [table.getColumn("document_type")?.getFacetedUniqueValues()]); + + // Get counts for each status + const statusCounts = useMemo(() => { + const statusColumn = table.getColumn("document_type"); + if (!statusColumn) return new Map(); + return statusColumn.getFacetedUniqueValues(); + }, [table.getColumn("document_type")?.getFacetedUniqueValues()]); + + const selectedStatuses = useMemo(() => { + const filterValue = table.getColumn("document_type")?.getFilterValue() as string[]; + return filterValue ?? []; + }, [table.getColumn("document_type")?.getFilterValue()]); + + const handleStatusChange = (checked: boolean, value: string) => { + const filterValue = table.getColumn("document_type")?.getFilterValue() as string[]; + const newFilterValue = filterValue ? [...filterValue] : []; + + if (checked) { + newFilterValue.push(value); + } else { + const index = newFilterValue.indexOf(value); + if (index > -1) { + newFilterValue.splice(index, 1); + } + } + + table.getColumn("document_type")?.setFilterValue(newFilterValue.length ? newFilterValue : undefined); + }; + + return ( + Promise.resolve(false)), + refreshDocuments: refreshDocuments || (() => Promise.resolve()) + }}> + + {/* Filters */} + +
+ {/* Filter by name or email */} + + table.getColumn("title")?.setFilterValue(e.target.value)} + placeholder="Filter by title..." + type="text" + aria-label="Filter by title" + /> + + + {Boolean(table.getColumn("title")?.getFilterValue()) && ( + { + table.getColumn("title")?.setFilterValue(""); + if (inputRef.current) { + inputRef.current.focus(); + } + }} + initial={{ opacity: 0, rotate: -90 }} + animate={{ opacity: 1, rotate: 0 }} + exit={{ opacity: 0, rotate: 90 }} + whileHover={{ scale: 1.1 }} + whileTap={{ scale: 0.9 }} + > + + )} + + {/* Filter by status */} + + + + + + + + +
+
Filters
+
+ + {uniqueStatusValues.map((value, i) => ( + + handleStatusChange(checked, value)} + /> + + + ))} + +
+
+
+
+
+ {/* Toggle columns visibility */} + + + + + + + + + Toggle columns + {table + .getAllColumns() + .filter((column) => column.getCanHide()) + .map((column) => { + return ( + column.toggleVisibility(!!value)} + onSelect={(event) => event.preventDefault()} + > + {column.id} + + ); + })} + + + +
+
+ {/* Delete button */} + {table.getSelectedRowModel().rows.length > 0 && ( + + + + + +
+ + + Are you absolutely sure? + + This action cannot be undone. This will permanently delete{" "} + {table.getSelectedRowModel().rows.length} selected{" "} + {table.getSelectedRowModel().rows.length === 1 ? "row" : "rows"}. + + +
+ + Cancel + Delete + +
+
+ )} + {/* Add user button */} + {/* */} +
+
+ + {/* Table */} + + {loading ? ( +
+
+
+

Loading documents...

+
+
+ ) : error ? ( +
+
+ +

Error loading documents

+ +
+
+ ) : data.length === 0 ? ( +
+
+ +

No documents found

+
+
+ ) : ( + + + {table.getHeaderGroups().map((headerGroup) => ( + + {headerGroup.headers.map((header) => { + return ( + + {header.isPlaceholder ? null : header.column.getCanSort() ? ( +
{ + // Enhanced keyboard handling for sorting + if ( + header.column.getCanSort() && + (e.key === "Enter" || e.key === " ") + ) { + e.preventDefault(); + header.column.getToggleSortingHandler()?.(e); + } + }} + tabIndex={header.column.getCanSort() ? 0 : undefined} + > + {flexRender(header.column.columnDef.header, header.getContext())} + {{ + asc: ( +
+ ) : ( + flexRender(header.column.columnDef.header, header.getContext()) + )} +
+ ); + })} +
+ ))} +
+ + + {table.getRowModel().rows?.length ? ( + table.getRowModel().rows.map((row, index) => ( + + {row.getVisibleCells().map((cell) => ( + + {flexRender(cell.column.columnDef.cell, cell.getContext())} + + ))} + + )) + ) : ( + + + No documents found. + + + )} + + +
+ )} +
+ + {/* Pagination */} +
+ {/* Results per page */} + + + + + {/* Page number information */} + +

+ + {table.getState().pagination.pageIndex * table.getState().pagination.pageSize + 1}- + {Math.min( + Math.max( + table.getState().pagination.pageIndex * table.getState().pagination.pageSize + + table.getState().pagination.pageSize, + 0, + ), + table.getRowCount(), + )} + {" "} + of {table.getRowCount().toString()} +

+
+ + {/* Pagination buttons */} +
+ + + {/* First page button */} + + + + + + {/* Previous page button */} + + + + + + {/* Next page button */} + + + + + + {/* Last page button */} + + + + + + + +
+
+
+
+ ); +} + +function RowActions({ row }: { row: Row }) { + const [isOpen, setIsOpen] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + const { deleteDocument, refreshDocuments } = useContext(DocumentsContext)!; + const document = row.original; + + const handleDelete = async () => { + setIsDeleting(true); + try { + await deleteDocument(document.id); + toast.success("Document deleted successfully"); + await refreshDocuments(); + } catch (error) { + console.error("Error deleting document:", error); + toast.error("Failed to delete document"); + } finally { + setIsDeleting(false); + setIsOpen(false); + } + }; + + return ( +
+ + + + + + e.preventDefault()}> + View Metadata + + } + /> + + + + { + e.preventDefault(); + setIsOpen(true); + }} + > + Delete + + + + + Are you sure? + + This action cannot be undone. This will permanently delete the document. + + + + Cancel + { + e.preventDefault(); + handleDelete(); + }} + disabled={isDeleting} + > + {isDeleting ? "Deleting..." : "Delete"} + + + + + + +
+ ); +} + +export { DocumentsTable } \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx new file mode 100644 index 000000000..a6fb3d128 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/upload/page.tsx @@ -0,0 +1,463 @@ +"use client" + +import { useState, useCallback, useRef } from "react" +import { useDropzone } from "react-dropzone" +import { Button } from "@/components/ui/button" +import { toast } from "sonner" +import { X, Upload, FileIcon, Tag, AlertCircle, CheckCircle2, Calendar, FileType } from "lucide-react" +import { useRouter, useParams } from "next/navigation" +import { motion, AnimatePresence } from "framer-motion" + +// Grid pattern component inspired by Aceternity UI +function GridPattern() { + const columns = 41; + const rows = 11; + return ( +
+ {Array.from({ length: rows }).map((_, row) => + Array.from({ length: columns }).map((_, col) => { + const index = row * columns + col; + return ( +
+ ); + }) + )} +
+ ); +} + +export default function FileUploader() { + // Use the useParams hook to get the params + const params = useParams(); + const search_space_id = params.search_space_id as string; + + const [files, setFiles] = useState([]) + const [isUploading, setIsUploading] = useState(false) + const router = useRouter(); + const fileInputRef = useRef(null); + + const acceptedFileTypes = { + 'image/bmp': ['.bmp'], + 'text/csv': ['.csv'], + 'application/msword': ['.doc'], + 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': ['.docx'], + 'message/rfc822': ['.eml'], + 'application/epub+zip': ['.epub'], + 'image/heic': ['.heic'], + 'text/html': ['.html'], + 'image/jpeg': ['.jpeg', '.jpg'], + 'image/png': ['.png'], + 'text/markdown': ['.md'], + 'application/vnd.ms-outlook': ['.msg'], + 'application/vnd.oasis.opendocument.text': ['.odt'], + 'text/x-org': ['.org'], + 'application/pkcs7-signature': ['.p7s'], + 'application/pdf': ['.pdf'], + 'application/vnd.ms-powerpoint': ['.ppt'], + 'application/vnd.openxmlformats-officedocument.presentationml.presentation': ['.pptx'], + 'text/x-rst': ['.rst'], + 'application/rtf': ['.rtf'], + 'image/tiff': ['.tiff'], + 'text/plain': ['.txt'], + 'text/tab-separated-values': ['.tsv'], + 'application/vnd.ms-excel': ['.xls'], + 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': ['.xlsx'], + 'application/xml': ['.xml'], + } + + const supportedExtensions = Array.from(new Set(Object.values(acceptedFileTypes).flat())).sort() + + const onDrop = useCallback((acceptedFiles: File[]) => { + setFiles((prevFiles) => [...prevFiles, ...acceptedFiles]) + }, []) + + const { getRootProps, getInputProps, isDragActive } = useDropzone({ + onDrop, + accept: acceptedFileTypes, + maxSize: 50 * 1024 * 1024, // 50MB + }) + + const handleClick = () => { + fileInputRef.current?.click(); + }; + + const removeFile = (index: number) => { + setFiles((prevFiles) => prevFiles.filter((_, i) => i !== index)) + } + + const formatFileSize = (bytes: number) => { + if (bytes === 0) return "0 Bytes" + const k = 1024 + const sizes = ["Bytes", "KB", "MB", "GB", "TB"] + const i = Math.floor(Math.log(bytes) / Math.log(k)) + return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i] + } + + const handleUpload = async () => { + setIsUploading(true) + + const formData = new FormData() + files.forEach((file) => { + formData.append("files", file) + }) + + formData.append('search_space_id', search_space_id) + + try { + toast("File Upload", { + description: "Files Uploading Initiated", + }) + + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL!}/api/v1/documents/fileupload`, { + method: "POST", + headers: { + 'Authorization': `Bearer ${window.localStorage.getItem("surfsense_bearer_token")}` + }, + body: formData, + }) + + if (!response.ok) { + throw new Error("Upload failed") + } + + await response.json() + + toast("Upload Successful", { + description: "Files Uploaded Successfully", + }) + + router.push(`/dashboard/${search_space_id}/documents`); + } catch (error: any) { + setIsUploading(false) + toast("Upload Error", { + description: `Error uploading files: ${error.message}`, + }) + } + } + + const mainVariant = { + initial: { + x: 0, + y: 0, + }, + animate: { + x: 20, + y: -20, + opacity: 0.9, + }, + }; + + const secondaryVariant = { + initial: { + opacity: 0, + }, + animate: { + opacity: 1, + }, + }; + + const containerVariants = { + hidden: { opacity: 0, y: 20 }, + visible: { + opacity: 1, + y: 0, + transition: { + duration: 0.5, + when: "beforeChildren", + staggerChildren: 0.1 + } + } + }; + + const itemVariants = { + hidden: { opacity: 0, y: 10 }, + visible: { opacity: 1, y: 0, transition: { duration: 0.3 } } + }; + + const fileItemVariants = { + hidden: { opacity: 0, x: -20 }, + visible: { opacity: 1, x: 0, transition: { duration: 0.3 } }, + exit: { opacity: 0, x: 20, transition: { duration: 0.2 } } + }; + + return ( +
+ + + + + {/* Grid background pattern */} +
+ +
+ +
+ {/* Dropzone area */} +
+ + +

+ Upload files +

+

+ Drag or drop your files here or click to upload +

+ +
+ {!files.length && ( + + {isDragActive ? ( + + Drop it + + + ) : ( + + )} + + )} + + {!files.length && ( + + )} +
+
+
+
+ + {/* File list section */} + + {files.length > 0 && ( + +
+

Selected Files ({files.length})

+ +
+ +
+ + {files.map((file, index) => ( + +
+ + {file.name} + +
+ + {formatFileSize(file.size)} + + +
+
+ +
+ + + {file.type || 'Unknown type'} + + + + + modified {new Date(file.lastModified).toLocaleDateString()} + +
+
+ ))} +
+
+ + + + +
+ )} +
+ + {/* File type information */} + +
+
+ +

Supported file types:

+
+
+ {supportedExtensions.map((ext) => ( + + {ext} + + ))} +
+
+
+
+
+ + +
+ ) +} diff --git a/surfsense_web/app/dashboard/[search_space_id]/documents/webpage/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/documents/webpage/page.tsx new file mode 100644 index 000000000..60571d7d0 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/documents/webpage/page.tsx @@ -0,0 +1,200 @@ +"use client"; + +import { useState } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { Tag, TagInput } from "emblor"; +import { Button } from "@/components/ui/button"; +import { Label } from "@/components/ui/label"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { toast } from "sonner"; +import { Globe, Loader2 } from "lucide-react"; + +// URL validation regex +const urlRegex = /^(https?:\/\/)?([\da-z.-]+)\.([a-z.]{2,6})([/\w .-]*)*\/?$/; + +export default function WebpageCrawler() { + const params = useParams(); + const router = useRouter(); + const search_space_id = params.search_space_id as string; + + const [urlTags, setUrlTags] = useState([]); + const [activeTagIndex, setActiveTagIndex] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); + const [error, setError] = useState(null); + + // Function to validate a URL + const isValidUrl = (url: string): boolean => { + return urlRegex.test(url); + }; + + // Function to handle URL submission + const handleSubmit = async () => { + // Validate that we have at least one URL + if (urlTags.length === 0) { + setError("Please add at least one URL"); + return; + } + + // Validate all URLs + const invalidUrls = urlTags.filter(tag => !isValidUrl(tag.text)); + if (invalidUrls.length > 0) { + setError(`Invalid URLs detected: ${invalidUrls.map(tag => tag.text).join(', ')}`); + return; + } + + setError(null); + setIsSubmitting(true); + + try { + toast("URL Crawling", { + description: "Starting URL crawling process...", + }); + + // Extract URLs from tags + const urls = urlTags.map(tag => tag.text); + + // Make API call to backend + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/documents/`, { + method: "POST", + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${localStorage.getItem("surfsense_bearer_token")}` + }, + body: JSON.stringify({ + "document_type": "CRAWLED_URL", + "content": urls, + "search_space_id": parseInt(search_space_id) + }), + }); + + if (!response.ok) { + throw new Error("Failed to crawl URLs"); + } + + await response.json(); + + toast("Crawling Successful", { + description: "URLs have been submitted for crawling", + }); + + // Redirect to documents page + router.push(`/dashboard/${search_space_id}/documents`); + } catch (error: any) { + setError(error.message || "An error occurred while crawling URLs"); + toast("Crawling Error", { + description: `Error crawling URLs: ${error.message}`, + }); + } finally { + setIsSubmitting(false); + } + }; + + // Function to add a new URL tag + const handleAddTag = (text: string) => { + // Basic URL validation + if (!isValidUrl(text)) { + toast("Invalid URL", { + description: "Please enter a valid URL", + }); + return; + } + + // Check for duplicates + if (urlTags.some(tag => tag.text === text)) { + toast("Duplicate URL", { + description: "This URL has already been added", + }); + return; + } + + // Add the new tag + const newTag: Tag = { + id: Date.now().toString(), + text: text, + }; + + setUrlTags([...urlTags, newTag]); + }; + + return ( +
+ + + + + Add Webpages for Crawling + + + Enter URLs to crawl and add to your document collection + + + +
+
+ + +

+ Add multiple URLs by pressing Enter after each one +

+
+ + {error && ( +
+ {error} +
+ )} + +
+

Tips for URL crawling:

+
    +
  • Enter complete URLs including http:// or https://
  • +
  • Make sure the websites allow crawling
  • +
  • Public webpages work best
  • +
  • Crawling may take some time depending on the website size
  • +
+
+
+
+ + + + +
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/layout.tsx b/surfsense_web/app/dashboard/[search_space_id]/layout.tsx new file mode 100644 index 000000000..6eeb720d9 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/layout.tsx @@ -0,0 +1,99 @@ +// Server component +import React, { use } from 'react' +import { DashboardClientLayout } from './client-layout' + +export default function DashboardLayout({ + params, + children +}: { + params: Promise<{ search_space_id: string }>, + children: React.ReactNode +}) { + // Use React.use to unwrap the params Promise + const { search_space_id } = use(params); + + // TODO: Get search space name from our FastAPI backend + const customNavSecondary = [ + { + title: `All Search Spaces`, + url: `#`, + icon: "Info", + }, + { + title: `All Search Spaces`, + url: "/dashboard", + icon: "Undo2", + }, + ] + + const customNavMain = [ + { + title: "Researcher", + url: `/dashboard/${search_space_id}/researcher`, + icon: "SquareTerminal", + isActive: true, + items: [], + }, + + { + title: "Documents", + url: "#", + icon: "FileStack", + items: [ + { + title: "Upload Documents", + url: `/dashboard/${search_space_id}/documents/upload`, + }, + { + title: "Add Webpages", + url: `/dashboard/${search_space_id}/documents/webpage`, + }, + { + title: "Manage Documents", + url: `/dashboard/${search_space_id}/documents`, + }, + ], + }, + { + title: "Connectors", + url: `#`, + icon: "Cable", + items: [ + { + title: "Add Connector", + url: `/dashboard/${search_space_id}/connectors/add`, + }, + { + title: "Manage Connectors", + url: `/dashboard/${search_space_id}/connectors`, + }, + ], + }, + // TODO: Add research synthesizer's + // { + // title: "Research Synthesizer's", + // url: `#`, + // icon: "SquareLibrary", + // items: [ + // { + // title: "Podcast Creator", + // url: `/dashboard/${search_space_id}/synthesizer/podcast`, + // }, + // { + // title: "Presentation Creator", + // url: `/dashboard/${search_space_id}/synthesizer/presentation`, + // }, + // ], + // }, + ] + + return ( + + {children} + + ) +} \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/researcher/[chat_id]/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/researcher/[chat_id]/page.tsx new file mode 100644 index 000000000..6d74488da --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/researcher/[chat_id]/page.tsx @@ -0,0 +1,1054 @@ +"use client"; +import React, { useRef, useEffect, useState } from 'react'; +import { useChat } from '@ai-sdk/react'; +import { useParams } from 'next/navigation'; +import { + Loader2, + X, + Search, + ExternalLink, + ChevronLeft, + ChevronRight, + Check, + ArrowDown, + CircleUser, + Database, + SendHorizontal +} from 'lucide-react'; +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; +import { + Dialog, + DialogContent, + DialogDescription, + DialogHeader, + DialogTitle, + DialogTrigger, + DialogFooter +} from "@/components/ui/dialog"; +import { + SegmentedControl, + ConnectorButton as ConnectorButtonComponent, + getConnectorIcon, + getMainViewSources as getMainViewSourcesUtil, + getFilteredSources as getFilteredSourcesUtil, + getPaginatedDialogSources as getPaginatedDialogSourcesUtil, + getSourcesCount as getSourcesCountUtil, + useScrollToBottom, + updateScrollIndicators as updateScrollIndicatorsUtil, + useScrollIndicators, + scrollTabsLeft as scrollTabsLeftUtil, + scrollTabsRight as scrollTabsRightUtil, + Source, + ResearchMode, + researcherOptions +} from '@/components/chat'; +import { MarkdownViewer } from '@/components/markdown-viewer'; +import { connectorSourcesMenu as defaultConnectorSourcesMenu } from '@/components/chat/connector-sources'; +import { Logo } from '@/components/Logo'; +import { useSearchSourceConnectors } from '@/hooks'; + +interface SourceItem { + id: number; + title: string; + description: string; + url: string; + connectorType?: string; +} + +interface ConnectorSource { + id: number; + name: string; + type: string; + sources: SourceItem[]; +} + +/** + * Button that displays selected connectors and opens connector selection dialog + */ +const ConnectorButton = ({ selectedConnectors, onClick }: { selectedConnectors: string[], onClick: () => void }) => { + const { connectorSourceItems } = useSearchSourceConnectors(); + + return ( + + ); +}; + +// Create a wrapper component for the sources dialog content +const SourcesDialogContent = ({ + connector, + sourceFilter, + expandedSources, + sourcesPage, + setSourcesPage, + setSourceFilter, + setExpandedSources, + isLoadingMore +}: { + connector: any; + sourceFilter: string; + expandedSources: boolean; + sourcesPage: number; + setSourcesPage: React.Dispatch>; + setSourceFilter: React.Dispatch>; + setExpandedSources: React.Dispatch>; + isLoadingMore: boolean; +}) => { + // Safely access sources with fallbacks + const sources = connector?.sources || []; + + // Safe versions of utility functions + const getFilteredSourcesSafe = () => { + if (!sources.length) return []; + return getFilteredSourcesUtil(connector, sourceFilter); + }; + + const getPaginatedSourcesSafe = () => { + if (!sources.length) return []; + return getPaginatedDialogSourcesUtil( + connector, + sourceFilter, + expandedSources, + sourcesPage, + 5 // SOURCES_PER_PAGE + ); + }; + + const filteredSources = getFilteredSourcesSafe() || []; + const paginatedSources = getPaginatedSourcesSafe() || []; + + // Description text + const descriptionText = sourceFilter + ? `Found ${filteredSources.length} sources matching "${sourceFilter}"` + : `Viewing ${paginatedSources.length} of ${sources.length} sources`; + + if (paginatedSources.length === 0) { + return ( +
+ +

No sources found matching "{sourceFilter}"

+ +
+ ); + } + + return ( + <> + + + {getConnectorIcon(connector.type)} + {connector.name} Sources + + + {descriptionText} + + + +
+ + { + setSourceFilter(e.target.value); + setSourcesPage(1); + setExpandedSources(false); + }} + /> + {sourceFilter && ( + + )} +
+ +
+ {paginatedSources.map((source: any) => ( + +
+
+ {getConnectorIcon(connector.type)} +
+
+

{source.title}

+

{source.description}

+
+ +
+
+ ))} + + {!expandedSources && paginatedSources.length < filteredSources.length && ( + + )} + + {expandedSources && filteredSources.length > 10 && ( +
+ Showing all {filteredSources.length} sources +
+ )} +
+ + ); +}; + +const ChatPage = () => { + const [token, setToken] = React.useState(null); + const [showAnswer, setShowAnswer] = useState(true); + const [activeTab, setActiveTab] = useState(""); + const [dialogOpen, setDialogOpen] = useState(false); + const [sourcesPage, setSourcesPage] = useState(1); + const [expandedSources, setExpandedSources] = useState(false); + const [isLoadingMore, setIsLoadingMore] = useState(false); + const [canScrollLeft, setCanScrollLeft] = useState(false); + const [canScrollRight, setCanScrollRight] = useState(true); + const [sourceFilter, setSourceFilter] = useState(""); + const tabsListRef = useRef(null); + const [terminalExpanded, setTerminalExpanded] = useState(false); + const [selectedConnectors, setSelectedConnectors] = useState(["CRAWLED_URL"]); + const [researchMode, setResearchMode] = useState("GENERAL"); + const [currentTime, setCurrentTime] = useState(''); + const [currentDate, setCurrentDate] = useState(''); + const [connectorSources, setConnectorSources] = useState([]); + const terminalMessagesRef = useRef(null); + const { connectorSourceItems, isLoading: isLoadingConnectors } = useSearchSourceConnectors(); + + const SOURCES_PER_PAGE = 5; + const INITIAL_SOURCES_DISPLAY = 3; + + const { search_space_id, chat_id } = useParams(); + + // Get token from localStorage on client side only + React.useEffect(() => { + setToken(localStorage.getItem('surfsense_bearer_token')); + }, []); + + // Set the current time only on the client side after initial render + useEffect(() => { + setCurrentDate(new Date().toISOString().split('T')[0]); + setCurrentTime(new Date().toTimeString().split(' ')[0]); + }, []); + + // Add this CSS to remove input shadow and improve the UI + useEffect(() => { + if (typeof document !== 'undefined') { + const style = document.createElement('style'); + style.innerHTML = ` + .no-shadow-input { + box-shadow: none !important; + } + .no-shadow-input:focus-visible { + box-shadow: none !important; + outline: none !important; + } + .shadcn-selector { + transition: all 200ms cubic-bezier(0.4, 0, 0.2, 1); + border: 1px solid hsl(var(--border)); + background-color: transparent; + position: relative; + overflow: hidden; + } + .shadcn-selector:hover { + background-color: hsl(var(--muted)); + border-color: hsl(var(--primary) / 0.5); + } + .shadcn-selector:after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 0; + background: hsl(var(--primary) / 0.1); + transition: height 300ms ease; + } + .shadcn-selector:hover:after { + height: 100%; + } + .shadcn-selector-primary { + color: hsl(var(--primary)); + border-color: hsl(var(--primary) / 0.3); + } + .shadcn-selector-primary:hover { + border-color: hsl(var(--primary)); + background-color: hsl(var(--primary) / 0.1); + } + /* Fix for scrollbar layout shifts */ + html { + overflow-y: scroll; + } + body { + scrollbar-gutter: stable; + } + /* For Firefox */ + * { + scrollbar-width: thin; + } + /* For Webkit browsers */ + ::-webkit-scrollbar { + width: 8px; + height: 8px; + } + ::-webkit-scrollbar-track { + background: transparent; + } + ::-webkit-scrollbar-thumb { + background-color: rgba(155, 155, 155, 0.5); + border-radius: 20px; + } + `; + document.head.appendChild(style); + + return () => { + document.head.removeChild(style); + }; + } + }, []); + + const { messages, input, handleInputChange, handleSubmit: handleChatSubmit, status, setMessages } = useChat({ + api: `${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chat`, + streamProtocol: 'data', + headers: { + ...(token && { Authorization: `Bearer ${token}` }), + }, + body: { + data: { + search_space_id: search_space_id, + selected_connectors: selectedConnectors, + research_mode: researchMode + } + }, + onError: (error) => { + console.error("Chat error:", error); + // You can add additional error handling here if needed + } + }); + + // Fetch chat details when component mounts + useEffect(() => { + const fetchChatDetails = async () => { + try { + if (!token) return; // Wait for token to be set + + console.log('Fetching chat details for chat ID:', chat_id); + + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${Number(chat_id)}`, { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + } + }); + + if (!response.ok) { + throw new Error(`Failed to fetch chat details: ${response.statusText}`); + } + + const chatData = await response.json(); + console.log('Chat details fetched:', chatData); + + // Set research mode from chat data + if (chatData.type) { + setResearchMode(chatData.type as ResearchMode); + } + + // Set connectors from chat data + if (chatData.initial_connectors && Array.isArray(chatData.initial_connectors)) { + setSelectedConnectors(chatData.initial_connectors); + } + + // Set messages from chat data + if (chatData.messages && Array.isArray(chatData.messages)) { + setMessages(chatData.messages); + } + } catch (err) { + console.error('Error fetching chat details:', err); + } + }; + + if (token) { + fetchChatDetails(); + } + }, [token, chat_id, setMessages]); + + // Update chat when a conversation exchange is complete + useEffect(() => { + const updateChat = async () => { + try { + // Only update when: + // 1. Status is ready (not loading) + // 2. We have messages + // 3. Last message is from assistant (completed response) + if ( + status === 'ready' && + messages.length > 0 && + messages[messages.length - 1]?.role === 'assistant' + ) { + const token = localStorage.getItem('surfsense_bearer_token'); + if (!token) return; + + // Find the first user message to use as title + const userMessages = messages.filter(msg => msg.role === 'user'); + if (userMessages.length === 0) return; + + // Use the first user message as the title + const title = userMessages[0].content; + + + console.log('Updating chat with title:', title); + + // Update the chat + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/${Number(chat_id)}`, { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + type: researchMode, + title: title, + initial_connectors: selectedConnectors, + messages: messages, + search_space_id: Number(search_space_id) + }) + }); + + if (!response.ok) { + throw new Error(`Failed to update chat: ${response.statusText}`); + } + + console.log('Chat updated successfully'); + } + } catch (err) { + console.error('Error updating chat:', err); + } + }; + + updateChat(); + }, [messages, status, chat_id, researchMode, selectedConnectors, search_space_id]); + + // Log messages whenever they update and extract annotations from the latest assistant message if available + useEffect(() => { + console.log('Messages updated:', messages); + + // Extract annotations from the latest assistant message if available + const assistantMessages = messages.filter(msg => msg.role === 'assistant'); + if (assistantMessages.length > 0) { + const latestAssistantMessage = assistantMessages[assistantMessages.length - 1]; + if (latestAssistantMessage?.annotations) { + const annotations = latestAssistantMessage.annotations as any[]; + + // Debug log to track streaming annotations + if (process.env.NODE_ENV === 'development') { + console.log('Streaming annotations:', annotations); + + // Log counts of each annotation type + const terminalInfoCount = annotations.filter(a => a.type === 'TERMINAL_INFO').length; + const sourcesCount = annotations.filter(a => a.type === 'SOURCES').length; + const answerCount = annotations.filter(a => a.type === 'ANSWER').length; + + console.log(`Annotation counts - Terminal: ${terminalInfoCount}, Sources: ${sourcesCount}, Answer: ${answerCount}`); + } + + // Process SOURCES annotation - get the last one to ensure we have the latest + const sourcesAnnotations = annotations.filter( + (annotation) => annotation.type === 'SOURCES' + ); + + if (sourcesAnnotations.length > 0) { + // Get the last SOURCES annotation to ensure we have the most recent one + const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1]; + if (latestSourcesAnnotation.content) { + setConnectorSources(latestSourcesAnnotation.content); + } + } + + // Check for terminal info annotations and scroll terminal to bottom if they exist + const terminalInfoAnnotations = annotations.filter( + (annotation) => annotation.type === 'TERMINAL_INFO' + ); + + if (terminalInfoAnnotations.length > 0) { + // Schedule scrolling after the DOM has been updated + setTimeout(scrollTerminalToBottom, 100); + } + } + } + }, [messages]); + + // Custom handleSubmit function to include selected connectors and answer type + const handleSubmit = (e: React.FormEvent) => { + e.preventDefault(); + + if (!input.trim() || status !== 'ready') return; + + // You can add additional logic here if needed + // For example, validation for selected connectors + if (selectedConnectors.length === 0) { + alert("Please select at least one connector"); + return; + } + + // Call the original handleSubmit from useChat + handleChatSubmit(e); + }; + + // Reference to the messages container for auto-scrolling + const messagesEndRef = useRef(null); + + // Function to scroll to bottom + const scrollToBottom = () => { + messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); + }; + + // Function to scroll terminal to bottom + const scrollTerminalToBottom = () => { + if (terminalMessagesRef.current) { + terminalMessagesRef.current.scrollTop = terminalMessagesRef.current.scrollHeight; + } + }; + + // Scroll to bottom when messages change + useEffect(() => { + scrollToBottom(); + }, [messages]); + + // Set activeTab when connectorSources change + useEffect(() => { + if (connectorSources.length > 0) { + setActiveTab(connectorSources[0].type); + } + }, [connectorSources]); + + // Scroll terminal to bottom when expanded + useEffect(() => { + if (terminalExpanded) { + setTimeout(scrollTerminalToBottom, 300); // Wait for transition to complete + } + }, [terminalExpanded]); + + // Get total sources count for a connector type + const getSourcesCount = (connectorType: string) => { + return getSourcesCountUtil(connectorSources, connectorType); + }; + + // Function to check scroll position and update indicators + const updateScrollIndicators = () => { + updateScrollIndicatorsUtil(tabsListRef as React.RefObject, setCanScrollLeft, setCanScrollRight); + }; + + // Initialize scroll indicators + const updateIndicators = useScrollIndicators( + tabsListRef as React.RefObject, + setCanScrollLeft, + setCanScrollRight + ); + + // Function to scroll tabs list left + const scrollTabsLeft = () => { + scrollTabsLeftUtil(tabsListRef as React.RefObject, updateIndicators); + }; + + // Function to scroll tabs list right + const scrollTabsRight = () => { + scrollTabsRightUtil(tabsListRef as React.RefObject, updateIndicators); + }; + + // Use the scroll to bottom hook + useScrollToBottom(messagesEndRef as React.RefObject, [messages]); + + // Function to get sources for the main view + const getMainViewSources = (connector: any) => { + return getMainViewSourcesUtil(connector, INITIAL_SOURCES_DISPLAY); + }; + + // Function to get filtered sources for the dialog with null check + const getFilteredSourcesWithCheck = (connector: any, sourceFilter: string) => { + if (!connector?.sources) return []; + return getFilteredSourcesUtil(connector, sourceFilter); + }; + + // Function to get paginated dialog sources with null check + const getPaginatedDialogSourcesWithCheck = (connector: any, sourceFilter: string, expandedSources: boolean, sourcesPage: number, sourcesPerPage: number) => { + if (!connector?.sources) return []; + return getPaginatedDialogSourcesUtil(connector, sourceFilter, expandedSources, sourcesPage, sourcesPerPage); + }; + + // Function to get a citation source by ID + const getCitationSource = (citationId: number): Source | null => { + if (!messages || messages.length === 0) return null; + + // Find the latest assistant message + const assistantMessages = messages.filter(msg => msg.role === 'assistant'); + if (assistantMessages.length === 0) return null; + + const latestAssistantMessage = assistantMessages[assistantMessages.length - 1]; + if (!latestAssistantMessage?.annotations) return null; + + // Find all SOURCES annotations + const annotations = latestAssistantMessage.annotations as any[]; + const sourcesAnnotations = annotations.filter( + (annotation) => annotation.type === 'SOURCES' + ); + + // Get the latest SOURCES annotation + if (sourcesAnnotations.length === 0) return null; + const latestSourcesAnnotation = sourcesAnnotations[sourcesAnnotations.length - 1]; + + if (!latestSourcesAnnotation.content) return null; + + // Flatten all sources from all connectors + const allSources: Source[] = []; + latestSourcesAnnotation.content.forEach((connector: ConnectorSource) => { + if (connector.sources && Array.isArray(connector.sources)) { + connector.sources.forEach((source: SourceItem) => { + allSources.push({ + id: source.id, + title: source.title, + description: source.description, + url: source.url, + connectorType: connector.type + }); + }); + } + }); + + // Find the source with the matching ID + const foundSource = allSources.find(source => source.id === citationId); + + return foundSource || null; + }; + + return ( + <> +
+ {messages.length === 0 && ( +

+ +
+ Surf{""} +
+
+ Sense +
+
+
+

+ )} + {messages?.map((message, index) => { + if (message.role === 'user') { + return ( +
+ +
+ + + + + +
+
+ ); + } + + if (message.role === 'assistant') { + return ( +
+ + + Answer + + + {/* Status Messages Section */} + +
+
+
+
setTerminalExpanded(false)}>
+
+
setTerminalExpanded(true)}>
+
+ surfsense-research-terminal +
+
+ +
+
Last login: {currentDate} {currentTime}
+
+ researcher@surfsense + : + ~/research + $ + surfsense-researcher +
+ {message.annotations && (() => { + // Get all TERMINAL_INFO annotations + const terminalInfoAnnotations = (message.annotations as any[]) + .filter(a => a.type === 'TERMINAL_INFO'); + + // Get the latest TERMINAL_INFO annotation + const latestTerminalInfo = terminalInfoAnnotations.length > 0 + ? terminalInfoAnnotations[terminalInfoAnnotations.length - 1] + : null; + + // Render the content of the latest TERMINAL_INFO annotation + return latestTerminalInfo?.content.map((item: any, idx: number) => ( +
+ [{String(idx).padStart(2, '0')}:{String(Math.floor(idx * 2)).padStart(2, '0')}] + {'>'} + {item.text} +
+ )); + })()} +
+ [00:13] + researcher@surfsense + : + ~/research + $ +
+
+ + {/* Terminal scroll button */} +
+ +
+
+
+ + {/* Sources Section with Connector Tabs */} +
+
+ + Sources +
+ + 0 ? connectorSources[0].type : "CRAWLED_URL"} + className="w-full" + onValueChange={setActiveTab} + > +
+
+ + +
+
+ + {connectorSources.map((connector) => ( + + {getConnectorIcon(connector.type)} + {connector.name.split(' ')[0]} + + {getSourcesCount(connector.type)} + + + ))} + +
+
+ + +
+
+ + {connectorSources.map(connector => ( + +
+ {getMainViewSources(connector).map((source: any) => ( + +
+
+ {getConnectorIcon(connector.type)} +
+
+

{source.title}

+

{source.description}

+
+ +
+
+ ))} + + {connector.sources.length > INITIAL_SOURCES_DISPLAY && ( + setDialogOpen(open)}> + + + + + + + + )} +
+
+ ))} +
+
+ + {/* Answer Section */} +
+ {showAnswer && ( +
+ {message.annotations && (() => { + // Get all ANSWER annotations + const answerAnnotations = (message.annotations as any[]) + .filter(a => a.type === 'ANSWER'); + + // Get the latest ANSWER annotation + const latestAnswer = answerAnnotations.length > 0 + ? answerAnnotations[answerAnnotations.length - 1] + : null; + + // If we have a latest ANSWER annotation with content, render it + if (latestAnswer?.content && latestAnswer.content.length > 0) { + return ( + + ); + } + + // Fallback to the message content if no ANSWER annotation is available + return ; + })()} +
+ )} +
+ {/* Scroll to bottom button */} +
+ +
+
+
+
+ ); + } + + return null; + })} + + {/* New Chat Input Form */} +
+
+ + {/* Send button */} + +
+
+
+ {/* Connector Selection Dialog */} + + + { }} + /> + + + + Select Connectors + + Choose which data sources to include in your research + + + + {/* Connector selection grid */} +
+ {isLoadingConnectors ? ( +
+ +
+ ) : ( + connectorSourceItems.map((connector) => { + const isSelected = selectedConnectors.includes(connector.type); + + return ( +
{ + setSelectedConnectors( + isSelected + ? selectedConnectors.filter((type) => type !== connector.type) + : [...selectedConnectors, connector.type] + ); + }} + role="checkbox" + aria-checked={isSelected} + tabIndex={0} + > +
+ {getConnectorIcon(connector.type)} +
+ {connector.name} + {isSelected && } +
+ ); + }) + )} +
+ + +
+ + +
+
+
+
+ + {/* Research Mode Segmented Control */} + + value={researchMode} + onChange={setResearchMode} + options={researcherOptions} + /> +
+
+
+ + {/* Reference for auto-scrolling */} +
+
+ + ); +}; + +export default ChatPage; \ No newline at end of file diff --git a/surfsense_web/app/dashboard/[search_space_id]/researcher/page.tsx b/surfsense_web/app/dashboard/[search_space_id]/researcher/page.tsx new file mode 100644 index 000000000..b61229985 --- /dev/null +++ b/surfsense_web/app/dashboard/[search_space_id]/researcher/page.tsx @@ -0,0 +1,80 @@ +"use client"; +import React, { useEffect } from 'react'; +import { useParams, useRouter } from 'next/navigation'; +import { Loader2 } from 'lucide-react'; + +const ResearcherPage = () => { + const router = useRouter(); + const { search_space_id } = useParams(); + const [isCreating, setIsCreating] = React.useState(true); + const [error, setError] = React.useState(null); + + useEffect(() => { + const createChat = async () => { + try { + // Get token from localStorage + const token = localStorage.getItem('surfsense_bearer_token'); + + if (!token) { + setError('Authentication token not found'); + setIsCreating(false); + return; + } + + // Create a new chat + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/chats/`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Authorization': `Bearer ${token}` + }, + body: JSON.stringify({ + type: "GENERAL", + title: "Untitled Chat", // Empty title initially + initial_connectors: ["CRAWLED_URL"], // Default connector + messages: [], + search_space_id: Number(search_space_id) + }) + }); + + if (!response.ok) { + throw new Error(`Failed to create chat: ${response.statusText}`); + } + + const data = await response.json(); + + // Redirect to the new chat page + router.push(`/dashboard/${search_space_id}/researcher/${data.id}`); + } catch (err) { + console.error('Error creating chat:', err); + setError(err instanceof Error ? err.message : 'Failed to create chat'); + setIsCreating(false); + } + }; + + createChat(); + }, [search_space_id, router]); + + if (error) { + return ( +
+
Error: {error}
+ +
+ ); + } + + return ( +
+ +

Creating new research chat...

+
+ ); +}; + +export default ResearcherPage; \ No newline at end of file diff --git a/surfsense_web/app/dashboard/page.tsx b/surfsense_web/app/dashboard/page.tsx new file mode 100644 index 000000000..4e19545a4 --- /dev/null +++ b/surfsense_web/app/dashboard/page.tsx @@ -0,0 +1,353 @@ +"use client"; + +import React from 'react' +import Link from 'next/link' +import { motion } from 'framer-motion' +import { Button } from '@/components/ui/button' +import { Plus, Search, Trash2, AlertCircle, Loader2 } from 'lucide-react' +import { Tilt } from '@/components/ui/tilt' +import { Spotlight } from '@/components/ui/spotlight' +import { Logo } from '@/components/Logo'; +import { ThemeTogglerComponent } from '@/components/theme/theme-toggle'; +import { toast } from 'sonner'; +import { + AlertDialog, + AlertDialogAction, + AlertDialogCancel, + AlertDialogContent, + AlertDialogDescription, + AlertDialogFooter, + AlertDialogHeader, + AlertDialogTitle, + AlertDialogTrigger, +} from '@/components/ui/alert-dialog'; +import { + Alert, + AlertDescription, + AlertTitle, +} from "@/components/ui/alert"; +import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"; +import { useSearchSpaces } from '@/hooks/use-search-spaces'; +import { useRouter } from 'next/navigation'; + +/** + * Formats a date string into a readable format + * @param dateString - The date string to format + * @returns Formatted date string (e.g., "Jan 1, 2023") + */ +const formatDate = (dateString: string): string => { + return new Date(dateString).toLocaleDateString('en-US', { + year: 'numeric', + month: 'short', + day: 'numeric' + }); +}; + +/** + * Loading screen component with animation + */ +const LoadingScreen = () => { + return ( +
+ + + + Loading + Fetching your search spaces... + + + + + + + + This may take a moment + + + +
+ ); +}; + +/** + * Error screen component with animation + */ +const ErrorScreen = ({ message }: { message: string }) => { + const router = useRouter(); + + return ( +
+ + + +
+ + Error +
+ Something went wrong +
+ + + + Error Details + + {message} + + + + + + + +
+
+
+ ); +}; + +const DashboardPage = () => { + // Animation variants + const containerVariants = { + hidden: { opacity: 0 }, + visible: { + opacity: 1, + transition: { + staggerChildren: 0.1, + }, + }, + }; + + const itemVariants = { + hidden: { y: 20, opacity: 0 }, + visible: { + y: 0, + opacity: 1, + transition: { + type: "spring", + stiffness: 300, + damping: 24, + }, + }, + }; + + const { searchSpaces, loading, error, refreshSearchSpaces } = useSearchSpaces(); + + if (loading) return ; + if (error) return ; + + const handleDeleteSearchSpace = async (id: number) => { + // Send DELETE request to the API + try { + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces/${id}`, { + method: 'DELETE', + headers: { + Authorization: `Bearer ${localStorage.getItem('surfsense_bearer_token')}`, + }, + }); + + if (!response.ok) { + toast.error("Failed to delete search space"); + throw new Error("Failed to delete search space"); + } + + // Refresh the search spaces list after successful deletion + refreshSearchSpaces(); + } catch (error) { + console.error('Error deleting search space:', error); + toast.error("An error occurred while deleting the search space"); + return; + } + toast.success("Search space deleted successfully"); + }; + + return ( + + +
+
+ +
+

SurfSense Dashboard

+

+ Welcome to your SurfSense dashboard. +

+
+
+ +
+ +
+
+

Your Search Spaces

+ + + + + +
+ +
+ {searchSpaces && searchSpaces.map((space) => ( + + + + +
+
+ {space.name} +
+
+ + + + + +
+
+ + + + + + + Delete Search Space + + Are you sure you want to delete "{space.name}"? This action cannot be undone. + All documents, chats, and podcasts in this search space will be permanently deleted. + + + + Cancel + handleDeleteSearchSpace(space.id)} + className="bg-destructive hover:bg-destructive/90" + > + Delete + + + + +
+
+ +
+
+

{space.name}

+

{space.description}

+
+
+ {/* {space.title} */} + Created {formatDate(space.created_at)} +
+
+
+ + + + ))} + + {searchSpaces.length === 0 && ( + +
+ +
+

No search spaces found

+

Create your first search space to get started

+ + + +
+ )} + + {searchSpaces.length > 0 && ( + + + +
+ + Add New Search Space +
+ +
+
+ )} +
+
+ + + ) +} + +export default DashboardPage \ No newline at end of file diff --git a/surfsense_web/app/dashboard/searchspaces/page.tsx b/surfsense_web/app/dashboard/searchspaces/page.tsx new file mode 100644 index 000000000..9bade8239 --- /dev/null +++ b/surfsense_web/app/dashboard/searchspaces/page.tsx @@ -0,0 +1,52 @@ +"use client"; + +import { toast } from "sonner"; +import { SearchSpaceForm } from "@/components/search-space-form"; +import { motion } from "framer-motion"; +import { useRouter } from "next/navigation"; +export default function SearchSpacesPage() { + const router = useRouter(); + const handleCreateSearchSpace = async (data: { name: string; description: string }) => { + try { + const response = await fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/api/v1/searchspaces`, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${localStorage.getItem('surfsense_bearer_token')}`, + }, + body: JSON.stringify(data), + }); + + if (!response.ok) { + toast.error("Failed to create search space"); + throw new Error("Failed to create search space"); + } + + const result = await response.json(); + + toast.success("Search space created successfully", { + description: `"${data.name}" has been created.`, + }); + + router.push(`/dashboard`); + + return result; + } catch (error: any) { + console.error('Error creating search space:', error); + throw error; + } + }; + + return ( + +
+ +
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/app/favicon.ico b/surfsense_web/app/favicon.ico new file mode 100644 index 000000000..171ab2c49 Binary files /dev/null and b/surfsense_web/app/favicon.ico differ diff --git a/surfsense_web/app/globals.css b/surfsense_web/app/globals.css new file mode 100644 index 000000000..8fdefeca5 --- /dev/null +++ b/surfsense_web/app/globals.css @@ -0,0 +1,150 @@ +@import "tailwindcss"; + +@plugin "tailwindcss-animate"; + +@custom-variant dark (&:is(.dark *)); + +@theme { + --font-sans: var(--font-geist-sans); + --font-mono: var(--font-geist-mono); +} + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.145 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.145 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.145 0 0); + --primary: oklch(0.205 0 0); + --primary-foreground: oklch(0.985 0 0); + --secondary: oklch(0.97 0 0); + --secondary-foreground: oklch(0.205 0 0); + --muted: oklch(0.97 0 0); + --muted-foreground: oklch(0.556 0 0); + --accent: oklch(0.97 0 0); + --accent-foreground: oklch(0.205 0 0); + --destructive: oklch(0.577 0.245 27.325); + --destructive-foreground: oklch(0.577 0.245 27.325); + --border: oklch(0.922 0 0); + --input: oklch(0.922 0 0); + --ring: oklch(0.708 0 0); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --radius: 0.625rem; + --sidebar: oklch(0.985 0 0); + --sidebar-foreground: oklch(0.145 0 0); + --sidebar-primary: oklch(0.205 0 0); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.97 0 0); + --sidebar-accent-foreground: oklch(0.205 0 0); + --sidebar-border: oklch(0.922 0 0); + --sidebar-ring: oklch(0.708 0 0); +} + +.dark { + --background: oklch(0.145 0 0); + --foreground: oklch(0.985 0 0); + --card: oklch(0.145 0 0); + --card-foreground: oklch(0.985 0 0); + --popover: oklch(0.145 0 0); + --popover-foreground: oklch(0.985 0 0); + --primary: oklch(0.985 0 0); + --primary-foreground: oklch(0.205 0 0); + --secondary: oklch(0.269 0 0); + --secondary-foreground: oklch(0.985 0 0); + --muted: oklch(0.269 0 0); + --muted-foreground: oklch(0.708 0 0); + --accent: oklch(0.269 0 0); + --accent-foreground: oklch(0.985 0 0); + --destructive: oklch(0.396 0.141 25.723); + --destructive-foreground: oklch(0.637 0.237 25.331); + --border: oklch(0.269 0 0); + --input: oklch(0.269 0 0); + --ring: oklch(0.439 0 0); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.205 0 0); + --sidebar-foreground: oklch(0.985 0 0); + --sidebar-primary: oklch(0.488 0.243 264.376); + --sidebar-primary-foreground: oklch(0.985 0 0); + --sidebar-accent: oklch(0.269 0 0); + --sidebar-accent-foreground: oklch(0.985 0 0); + --sidebar-border: oklch(0.269 0 0); + --sidebar-ring: oklch(0.439 0 0); +} + +@theme inline { + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } + :root { + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; + } + + .dark { + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; + } +} diff --git a/surfsense_web/app/layout.tsx b/surfsense_web/app/layout.tsx new file mode 100644 index 000000000..be0b18079 --- /dev/null +++ b/surfsense_web/app/layout.tsx @@ -0,0 +1,73 @@ +import type { Metadata } from "next"; +import "./globals.css"; +import { cn } from "@/lib/utils"; +import { Roboto } from "next/font/google"; + +import { Toaster } from "@/components/ui/sonner"; +import { ThemeProvider } from "@/components/theme/theme-provider"; + +const roboto = Roboto({ + subsets: ["latin"], + weight: ["400", "500", "700"], + display: 'swap', + variable: '--font-roboto', +}); + +export const metadata: Metadata = { + title: "SurfSense - A Personal NotebookLM and Perplexity-like AI Assistant for Everyone.", + description: + "Have your own private NotebookLM and Perplexity with better integrations.", + openGraph: { + images: [ + { + url: "https://surfsense.net/og-image.png", + width: 1200, + height: 630, + alt: "SurfSense - A Personal NotebookLM and Perplexity-like AI Assistant for Everyone.", + }, + ], + }, + twitter: { + card: "summary_large_image", + site: "https://surfsense.net", + creator: "https://surfsense.net", + title: "SurfSense - A Personal NotebookLM and Perplexity-like AI Assistant for Everyone.", + description: + "Have your own private NotebookLM and Perplexity with better integrations.", + images: [ + { + url: "https://surfsense.net/og-image.png", + width: 1200, + height: 630, + alt: "SurfSense - A Personal NotebookLM and Perplexity-like AI Assistant for Everyone.", + }, + ], + }, +}; + +export default async function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + {children} + + + + + ); +} diff --git a/surfsense_web/app/login/GoogleLoginButton.tsx b/surfsense_web/app/login/GoogleLoginButton.tsx new file mode 100644 index 000000000..a4ed4a3a0 --- /dev/null +++ b/surfsense_web/app/login/GoogleLoginButton.tsx @@ -0,0 +1,98 @@ +"use client"; +import React from "react"; +import { IconBrandGoogleFilled } from "@tabler/icons-react"; +import { motion } from "framer-motion"; +import { Logo } from "@/components/Logo"; + +export function GoogleLoginButton() { + const handleGoogleLogin = () => { + // Redirect to Google OAuth authorization URL + fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/auth/google/authorize`) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to get authorization URL'); + } + return response.json(); + }) + .then((data) => { + if (data.authorization_url) { + window.location.href = data.authorization_url; + } else { + console.error('No authorization URL received'); + } + }) + .catch((error) => { + console.error('Error during Google login:', error); + }); + } + return ( +
+ +
+ +

+ Welcome Back +

+ + +
+
+
+
+
+
+ + Continue with Google +
+
+
+ ); +} + + + +const AmbientBackground = () => { + return ( +
+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/surfsense_web/app/login/page.tsx b/surfsense_web/app/login/page.tsx new file mode 100644 index 000000000..ee3b46200 --- /dev/null +++ b/surfsense_web/app/login/page.tsx @@ -0,0 +1,5 @@ +import { GoogleLoginButton } from "./GoogleLoginButton"; + +export default function LoginPage() { + return ; +} \ No newline at end of file diff --git a/surfsense_web/app/page.tsx b/surfsense_web/app/page.tsx new file mode 100644 index 000000000..6e5f6166d --- /dev/null +++ b/surfsense_web/app/page.tsx @@ -0,0 +1,16 @@ +"use client"; +import React from "react"; +import { Navbar } from "@/components/Navbar"; +import { motion } from "framer-motion"; +import { ModernHeroWithGradients } from "@/components/ModernHeroWithGradients"; +import { Footer } from "@/components/Footer"; + +export default function HomePage() { + return ( +
+ + +
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/components.json b/surfsense_web/components.json new file mode 100644 index 000000000..335484f94 --- /dev/null +++ b/surfsense_web/components.json @@ -0,0 +1,21 @@ +{ + "$schema": "https://ui.shadcn.com/schema.json", + "style": "new-york", + "rsc": true, + "tsx": true, + "tailwind": { + "config": "", + "css": "app/globals.css", + "baseColor": "neutral", + "cssVariables": true, + "prefix": "" + }, + "aliases": { + "components": "@/components", + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" +} \ No newline at end of file diff --git a/surfsense_web/components/Footer.tsx b/surfsense_web/components/Footer.tsx new file mode 100644 index 000000000..0439b3540 --- /dev/null +++ b/surfsense_web/components/Footer.tsx @@ -0,0 +1,102 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { + IconBrandGithub, + IconBrandLinkedin, + IconBrandTwitter, +} from "@tabler/icons-react"; +import Link from "next/link"; +import React from "react"; + +export function Footer() { + const pages = [ + { + title: "Privacy", + href: "#", + }, + { + title: "Terms", + href: "#", + }, + ]; + + return ( +
+
+
+
+
+ SurfSense +
+
+ +
    + {pages.map((page, idx) => ( +
  • + + {page.title} + +
  • + ))} +
+ + +
+
+

+ © SurfSense 2025 +

+
+ + + + + + + + + +
+
+
+
+ ); +} + +const GridLineHorizontal = ({ + className, + offset, +}: { + className?: string; + offset?: string; +}) => { + return ( +
+ ); +}; \ No newline at end of file diff --git a/surfsense_web/components/Logo.tsx b/surfsense_web/components/Logo.tsx new file mode 100644 index 000000000..cd2e5aa50 --- /dev/null +++ b/surfsense_web/components/Logo.tsx @@ -0,0 +1,22 @@ +"use client"; +import Link from "next/link"; +import React from "react"; +import Image from "next/image"; +import { cn } from "@/lib/utils"; + +export const Logo = ({ className }: { className?: string }) => { + return ( + + logo + + ); +}; + diff --git a/surfsense_web/components/ModernHeroWithGradients.tsx b/surfsense_web/components/ModernHeroWithGradients.tsx new file mode 100644 index 000000000..649a7a9a5 --- /dev/null +++ b/surfsense_web/components/ModernHeroWithGradients.tsx @@ -0,0 +1,526 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { IconArrowRight, IconBrandGithub } from "@tabler/icons-react"; +import Link from "next/link"; +import React from "react"; +import { motion } from "framer-motion"; +import { Logo } from "./Logo"; + +export function ModernHeroWithGradients() { + return ( +
+
+
+ + + + + + + +
+ + SurfSense v0.0.6 Released + + + {/* Import the Logo component or define it in this file */} +
+
+ +
+

+ SurfSense +

+
+

+ A Customizable AI Research Agent just like NotebookLM or Perplexity, but connected to external sources such as search engines (Tavily), Slack, Notion, and more. +

+
+ + Get Started + + + + GitHub + +
+
+
+
+
+ ); +} + +const TopLines = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const BottomLines = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const SideLines = () => { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + ); +}; + +const BottomGradient = ({ className }: { className?: string }) => { + return ( + + + + + + + + + + + + ); +}; + +const TopGradient = ({ className }: { className?: string }) => { + return ( + + + + + + + + + + + + + + + + + + + ); +}; + +const DarkModeGradient = ({ className }: { className?: string } = {}) => { + return ( +
+
+
+
+
+ ); +}; \ No newline at end of file diff --git a/surfsense_web/components/Navbar.tsx b/surfsense_web/components/Navbar.tsx new file mode 100644 index 000000000..e85453278 --- /dev/null +++ b/surfsense_web/components/Navbar.tsx @@ -0,0 +1,307 @@ +"use client"; +import { cn } from "@/lib/utils"; +import { IconMenu2, IconX, IconBrandGoogleFilled } from "@tabler/icons-react"; +import { + motion, + AnimatePresence, + useScroll, + useMotionValueEvent, +} from "framer-motion"; +import Link from "next/link"; +import React, { useRef, useState } from "react"; +import { Button } from "./ui/button"; +import { Logo } from "./Logo"; +import { ThemeTogglerComponent } from "./theme/theme-toggle"; + +interface NavbarProps { + navItems: { + name: string; + link: string; + }[]; + visible: boolean; +} + +export const Navbar = () => { + const navItems = [ + { + name: "", + link: "/", + }, + // { + // name: "Product", + // link: "/#product", + // }, + // { + // name: "Pricing", + // link: "/#pricing", + // }, + ]; + + const ref = useRef(null); + const { scrollY } = useScroll({ + target: ref, + offset: ["start start", "end start"], + }); + const [visible, setVisible] = useState(false); + + useMotionValueEvent(scrollY, "change", (latest) => { + if (latest > 100) { + setVisible(true); + } else { + setVisible(false); + } + }); + + return ( + + + + + ); +}; + +const DesktopNav = ({ navItems, visible }: NavbarProps) => { + const [hoveredIndex, setHoveredIndex] = useState(null); + + const handleGoogleLogin = () => { + // Redirect to Google OAuth authorization URL + fetch(`${process.env.NEXT_PUBLIC_FASTAPI_BACKEND_URL}/auth/google/authorize`) + .then((response) => { + if (!response.ok) { + throw new Error('Failed to get authorization URL'); + } + return response.json(); + }) + .then((data) => { + if (data.authorization_url) { + window.location.href = data.authorization_url; + } else { + console.error('No authorization URL received'); + } + }) + .catch((error) => { + console.error('Error during Google login:', error); + }); + }; + + return ( + setHoveredIndex(null)} + animate={{ + backdropFilter: "blur(16px)", + background: visible + ? "rgba(var(--background-rgb), 0.8)" + : "rgba(var(--background-rgb), 0.6)", + width: visible ? "38%" : "80%", + height: visible ? "48px" : "64px", + y: visible ? 8 : 0, + }} + initial={{ + width: "80%", + height: "64px", + background: "rgba(var(--background-rgb), 0.6)", + }} + transition={{ + type: "spring", + stiffness: 400, + damping: 30, + }} + className={cn( + "hidden lg:flex flex-row self-center items-center justify-between py-2 mx-auto px-6 rounded-full relative z-[60] backdrop-saturate-[1.8]", + visible ? "border dark:border-white/10 border-gray-300/30" : "border-0" + )} + style={{ + "--background-rgb": "var(--tw-dark) ? '0, 0, 0' : '255, 255, 255'", + } as React.CSSProperties} + > +
+ + SurfSense +
+ + {navItems.map((navItem, idx) => ( + setHoveredIndex(idx)} + className="relative" + > + + {navItem.name} + {hoveredIndex === idx && ( + + )} + + + ))} + +
+ + + {!visible && ( + + + + )} + +
+
+ ); +}; + +const MobileNav = ({ navItems, visible }: NavbarProps) => { + const [open, setOpen] = useState(false); + + const handleGoogleLogin = () => { + // Redirect to the login page + window.location.href = "./login"; + }; + + return ( + <> + +
+ +
+ + {open ? ( + setOpen(!open)} /> + ) : ( + setOpen(!open)} + /> + )} +
+
+ + + {open && ( + + {navItems.map( + (navItem: { link: string; name: string }, idx: number) => ( + setOpen(false)} + className="relative dark:text-white/90 text-gray-800 hover:text-gray-900 dark:hover:text-white transition-colors" + > + {navItem.name} + + ) + )} + + + )} + +
+ + ); +}; \ No newline at end of file diff --git a/surfsense_web/components/TokenHandler.tsx b/surfsense_web/components/TokenHandler.tsx new file mode 100644 index 000000000..26e84441a --- /dev/null +++ b/surfsense_web/components/TokenHandler.tsx @@ -0,0 +1,55 @@ +'use client'; + +import { useEffect } from 'react'; +import { useRouter, useSearchParams } from 'next/navigation'; + +interface TokenHandlerProps { + redirectPath?: string; // Path to redirect after storing token + tokenParamName?: string; // Name of the URL parameter containing the token + storageKey?: string; // Key to use when storing in localStorage +} + +/** + * Client component that extracts a token from URL parameters and stores it in localStorage + * + * @param redirectPath - Path to redirect after storing token (default: '/') + * @param tokenParamName - Name of the URL parameter containing the token (default: 'token') + * @param storageKey - Key to use when storing in localStorage (default: 'auth_token') + */ +const TokenHandler = ({ + redirectPath = '/', + tokenParamName = 'token', + storageKey = 'surfsense_bearer_token' +}: TokenHandlerProps) => { + const router = useRouter(); + const searchParams = useSearchParams(); + + useEffect(() => { + // Only run on client-side + if (typeof window === 'undefined') return; + + // Get token from URL parameters + const token = searchParams.get(tokenParamName); + + if (token) { + try { + // Store token in localStorage + localStorage.setItem(storageKey, token); + console.log(`Token stored in localStorage with key: ${storageKey}`); + + // Redirect to specified path + router.push(redirectPath); + } catch (error) { + console.error('Error storing token in localStorage:', error); + } + } + }, [searchParams, tokenParamName, storageKey, redirectPath, router]); + + return ( +
+

Processing authentication...

+
+ ); +}; + +export default TokenHandler; \ No newline at end of file diff --git a/surfsense_web/components/chat/Citation.tsx b/surfsense_web/components/chat/Citation.tsx new file mode 100644 index 000000000..caaff6e02 --- /dev/null +++ b/surfsense_web/components/chat/Citation.tsx @@ -0,0 +1,112 @@ +import React, { useState } from 'react'; +import { ExternalLink } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Card } from '@/components/ui/card'; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuTrigger, +} from '@/components/ui/dropdown-menu'; +import { getConnectorIcon } from './ConnectorComponents'; +import { Source } from './types'; + +type CitationProps = { + citationId: number; + citationText: string; + position: number; + source: Source | null; +}; + +/** + * Citation component to handle individual citations + */ +export const Citation = ({ citationId, citationText, position, source }: CitationProps) => { + const [open, setOpen] = useState(false); + const citationKey = `citation-${citationId}-${position}`; + + if (!source) return <>{citationText}; + + return ( + + + + + + {citationId} + + + + + +
+
+ {getConnectorIcon(source.connectorType || '')} +
+
+
+

{source.title}

+
+

{source.description}

+
+ {source.url} +
+
+ +
+
+
+
+
+ ); +}; + +/** + * Function to render text with citations + */ +export const renderTextWithCitations = (text: string, getCitationSource: (id: number) => Source | null) => { + // Regular expression to find citation patterns like [1], [2], etc. + const citationRegex = /\[(\d+)\]/g; + const parts = []; + let lastIndex = 0; + let match; + let position = 0; + + while ((match = citationRegex.exec(text)) !== null) { + // Add text before the citation + if (match.index > lastIndex) { + parts.push(text.substring(lastIndex, match.index)); + } + + // Add the citation component + const citationId = parseInt(match[1], 10); + parts.push( + + ); + + lastIndex = match.index + match[0].length; + position++; + } + + // Add any remaining text after the last citation + if (lastIndex < text.length) { + parts.push(text.substring(lastIndex)); + } + + return parts; +}; \ No newline at end of file diff --git a/surfsense_web/components/chat/ConnectorComponents.tsx b/surfsense_web/components/chat/ConnectorComponents.tsx new file mode 100644 index 000000000..b6dad8216 --- /dev/null +++ b/surfsense_web/components/chat/ConnectorComponents.tsx @@ -0,0 +1,162 @@ +import React from 'react'; +import { + ChevronDown, + Plus, + Search, + Globe, + BookOpen, + Sparkles, + Microscope, + Telescope, + File, + Link, + Slack, + Webhook +} from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Connector, ResearchMode } from './types'; + +// Helper function to get connector icon +export const getConnectorIcon = (connectorType: string) => { + const iconProps = { className: "h-4 w-4" }; + + switch(connectorType) { + case 'CRAWLED_URL': + return ; + case 'FILE': + return ; + case 'EXTENSION': + return ; + case 'SERPER_API': + case 'TAVILY_API': + return ; + case 'SLACK_CONNECTOR': + return ; + case 'NOTION_CONNECTOR': + return ; + case 'DEEP': + return ; + case 'DEEPER': + return ; + case 'DEEPEST': + return ; + default: + return ; + } +}; + +export const researcherOptions: { value: ResearchMode; label: string; icon: React.ReactNode }[] = [ + { + value: 'GENERAL', + label: 'General', + icon: getConnectorIcon('GENERAL') + }, + { + value: 'DEEP', + label: 'Deep', + icon: getConnectorIcon('DEEP') + }, + { + value: 'DEEPER', + label: 'Deeper', + icon: getConnectorIcon('DEEPER') + }, +] + +/** + * Displays a small icon for a connector type + */ +export const ConnectorIcon = ({ type, index = 0 }: { type: string; index?: number }) => ( +
+ {getConnectorIcon(type)} +
+); + +/** + * Displays a count indicator for additional connectors + */ +export const ConnectorCountBadge = ({ count }: { count: number }) => ( +
+ +{count} +
+); + +type ConnectorButtonProps = { + selectedConnectors: string[]; + onClick: () => void; + connectorSources: Connector[]; +}; + +/** + * Button that displays selected connectors and opens connector selection dialog + */ +export const ConnectorButton = ({ selectedConnectors, onClick, connectorSources }: ConnectorButtonProps) => { + const totalConnectors = connectorSources.length; + const selectedCount = selectedConnectors.length; + const progressPercentage = (selectedCount / totalConnectors) * 100; + + // Get the name of a single selected connector + const getSingleConnectorName = () => { + const connector = connectorSources.find(c => c.type === selectedConnectors[0]); + return connector?.name || ''; + }; + + // Get display text based on selection count + const getDisplayText = () => { + if (selectedCount === totalConnectors) return "All Connectors"; + if (selectedCount === 1) return getSingleConnectorName(); + return `${selectedCount} Connectors`; + }; + + // Render the empty state (no connectors selected) + const renderEmptyState = () => ( + <> + + Select Connectors + + ); + + // Render the selected connectors preview + const renderSelectedConnectors = () => ( + <> +
+ {/* Show up to 3 connector icons */} + {selectedConnectors.slice(0, 3).map((type, index) => ( + + ))} + + {/* Show count indicator if more than 3 connectors are selected */} + {selectedCount > 3 && } +
+ + {/* Display text */} + {getDisplayText()} + + ); + + return ( + + ); +}; \ No newline at end of file diff --git a/surfsense_web/components/chat/ScrollUtils.tsx b/surfsense_web/components/chat/ScrollUtils.tsx new file mode 100644 index 000000000..8977e9042 --- /dev/null +++ b/surfsense_web/components/chat/ScrollUtils.tsx @@ -0,0 +1,80 @@ +import React, { RefObject, useEffect } from 'react'; + +/** + * Function to scroll to the bottom of a container + */ +export const scrollToBottom = (ref: RefObject) => { + ref.current?.scrollIntoView({ behavior: 'smooth' }); +}; + +/** + * Hook to scroll to bottom when messages change + */ +export const useScrollToBottom = (ref: RefObject, dependencies: any[]) => { + useEffect(() => { + scrollToBottom(ref); + }, dependencies); +}; + +/** + * Function to check scroll position and update indicators + */ +export const updateScrollIndicators = ( + tabsListRef: RefObject, + setCanScrollLeft: (value: boolean) => void, + setCanScrollRight: (value: boolean) => void +) => { + if (tabsListRef.current) { + const { scrollLeft, scrollWidth, clientWidth } = tabsListRef.current; + setCanScrollLeft(scrollLeft > 0); + setCanScrollRight(scrollLeft + clientWidth < scrollWidth - 10); // 10px buffer + } +}; + +/** + * Hook to initialize scroll indicators and add resize listener + */ +export const useScrollIndicators = ( + tabsListRef: RefObject, + setCanScrollLeft: (value: boolean) => void, + setCanScrollRight: (value: boolean) => void +) => { + const updateIndicators = () => updateScrollIndicators(tabsListRef, setCanScrollLeft, setCanScrollRight); + + useEffect(() => { + updateIndicators(); + // Add resize listener to update indicators when window size changes + window.addEventListener('resize', updateIndicators); + return () => window.removeEventListener('resize', updateIndicators); + }, []); + + return updateIndicators; +}; + +/** + * Function to scroll tabs list left + */ +export const scrollTabsLeft = ( + tabsListRef: RefObject, + updateIndicators: () => void +) => { + if (tabsListRef.current) { + tabsListRef.current.scrollBy({ left: -200, behavior: 'smooth' }); + // Update indicators after scrolling + setTimeout(updateIndicators, 300); + } +}; + +/** + * Function to scroll tabs list right + */ +export const scrollTabsRight = ( + tabsListRef: RefObject, + updateIndicators: () => void +) => { + if (tabsListRef.current) { + tabsListRef.current.scrollBy({ left: 200, behavior: 'smooth' }); + // Update indicators after scrolling + setTimeout(updateIndicators, 300); + } +}; \ No newline at end of file diff --git a/surfsense_web/components/chat/SegmentedControl.tsx b/surfsense_web/components/chat/SegmentedControl.tsx new file mode 100644 index 000000000..d886a3b79 --- /dev/null +++ b/surfsense_web/components/chat/SegmentedControl.tsx @@ -0,0 +1,38 @@ +import React from 'react'; + +type SegmentedControlProps = { + value: T; + onChange: (value: T) => void; + options: Array<{ + value: T; + label: string; + icon: React.ReactNode; + }>; +}; + +/** + * A segmented control component for selecting between different options + */ +function SegmentedControl({ value, onChange, options }: SegmentedControlProps) { + return ( +
+ {options.map((option) => ( + + ))} +
+ ); +} + +export default SegmentedControl; \ No newline at end of file diff --git a/surfsense_web/components/chat/SourceUtils.tsx b/surfsense_web/components/chat/SourceUtils.tsx new file mode 100644 index 000000000..84422d190 --- /dev/null +++ b/surfsense_web/components/chat/SourceUtils.tsx @@ -0,0 +1,69 @@ +import React from 'react'; +import { Source, Connector } from './types'; + +/** + * Function to get sources for the main view + */ +export const getMainViewSources = (connector: Connector, initialSourcesDisplay: number) => { + return connector.sources?.slice(0, initialSourcesDisplay); +}; + +/** + * Function to get filtered sources for the dialog + */ +export const getFilteredSources = (connector: Connector, sourceFilter: string) => { + if (!sourceFilter.trim()) { + return connector.sources; + } + + const filter = sourceFilter.toLowerCase().trim(); + return connector.sources?.filter(source => + source.title.toLowerCase().includes(filter) || + source.description.toLowerCase().includes(filter) + ); +}; + +/** + * Function to get paginated and filtered sources for the dialog + */ +export const getPaginatedDialogSources = ( + connector: Connector, + sourceFilter: string, + expandedSources: boolean, + sourcesPage: number, + sourcesPerPage: number +) => { + const filteredSources = getFilteredSources(connector, sourceFilter); + + if (expandedSources) { + return filteredSources; + } + return filteredSources?.slice(0, sourcesPage * sourcesPerPage); +}; + +/** + * Function to get the count of sources for a connector type + */ +export const getSourcesCount = (connectorSources: Connector[], connectorType: string) => { + const connector = connectorSources.find(c => c.type === connectorType); + return connector?.sources?.length || 0; +}; + +/** + * Function to get a citation source by ID + */ +export const getCitationSource = ( + citationId: number, + connectorSources: Connector[] +): Source | null => { + for (const connector of connectorSources) { + const source = connector.sources?.find(s => s.id === citationId); + if (source) { + return { + ...source, + connectorType: connector.type + }; + } + } + return null; +}; \ No newline at end of file diff --git a/surfsense_web/components/chat/connector-sources.ts b/surfsense_web/components/chat/connector-sources.ts new file mode 100644 index 000000000..6f0985730 --- /dev/null +++ b/surfsense_web/components/chat/connector-sources.ts @@ -0,0 +1,18 @@ +// Connector sources +export const connectorSourcesMenu = [ + { + id: 1, + name: "Crawled URL", + type: "CRAWLED_URL", + }, + { + id: 2, + name: "File", + type: "FILE", + }, + { + id: 3, + name: "Extension", + type: "EXTENSION", + }, + ]; \ No newline at end of file diff --git a/surfsense_web/components/chat/index.ts b/surfsense_web/components/chat/index.ts new file mode 100644 index 000000000..55ab716c0 --- /dev/null +++ b/surfsense_web/components/chat/index.ts @@ -0,0 +1,7 @@ +// Export all components and utilities from the chat folder +export { default as SegmentedControl } from './SegmentedControl'; +export * from './ConnectorComponents'; +export * from './Citation'; +export * from './SourceUtils'; +export * from './ScrollUtils'; +export * from './types'; \ No newline at end of file diff --git a/surfsense_web/components/chat/types.ts b/surfsense_web/components/chat/types.ts new file mode 100644 index 000000000..d17d890df --- /dev/null +++ b/surfsense_web/components/chat/types.ts @@ -0,0 +1,51 @@ +/** + * Types for chat components + */ + +export type Source = { + id: number; + title: string; + description: string; + url: string; + connectorType?: string; +}; + +export type Connector = { + id: number; + type: string; + name: string; + sources?: Source[]; +}; + +export type StatusMessage = { + id: number; + message: string; + type: 'info' | 'success' | 'error' | 'warning'; + timestamp: string; +}; + +export type ChatMessage = { + id: string; + role: 'user' | 'assistant'; + content: string; + timestamp?: string; +}; + +// Define message types to match useChat() structure +export type MessageRole = 'user' | 'assistant' | 'system' | 'data'; + +export interface ToolInvocation { + state: 'call' | 'result'; + toolCallId: string; + toolName: string; + args: any; + result?: any; +} + +export interface ToolInvocationUIPart { + type: 'tool-invocation'; + toolInvocation: ToolInvocation; +} + + +export type ResearchMode = 'GENERAL' | 'DEEP' | 'DEEPER' | 'DEEPEST'; \ No newline at end of file diff --git a/surfsense_web/components/document-viewer.tsx b/surfsense_web/components/document-viewer.tsx new file mode 100644 index 000000000..b4fb7a419 --- /dev/null +++ b/surfsense_web/components/document-viewer.tsx @@ -0,0 +1,34 @@ +import React from "react"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { MarkdownViewer } from "@/components/markdown-viewer"; +import { FileText } from "lucide-react"; + +interface DocumentViewerProps { + title: string; + content: string; + trigger?: React.ReactNode; +} + +export function DocumentViewer({ title, content, trigger }: DocumentViewerProps) { + return ( + + + {trigger || ( + + )} + + + + {title} + +
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/components/json-metadata-viewer.tsx b/surfsense_web/components/json-metadata-viewer.tsx new file mode 100644 index 000000000..3c0d8b03a --- /dev/null +++ b/surfsense_web/components/json-metadata-viewer.tsx @@ -0,0 +1,55 @@ +import React from "react"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Button } from "@/components/ui/button"; +import { FileJson } from "lucide-react"; +import { JsonView, defaultStyles } from "react-json-view-lite"; +import "react-json-view-lite/dist/index.css"; + +interface JsonMetadataViewerProps { + title: string; + metadata: any; + trigger?: React.ReactNode; +} + +export function JsonMetadataViewer({ title, metadata, trigger }: JsonMetadataViewerProps) { + // Ensure metadata is a valid object + const jsonData = React.useMemo(() => { + if (!metadata) return {}; + + try { + // If metadata is a string, try to parse it + if (typeof metadata === "string") { + return JSON.parse(metadata); + } + // Otherwise, use it as is + return metadata; + } catch (error) { + console.error("Error parsing JSON metadata:", error); + return { error: "Invalid JSON metadata" }; + } + }, [metadata]); + + return ( + + + {trigger || ( + + )} + + + + {title} - Metadata + +
+ +
+
+
+ ); +} \ No newline at end of file diff --git a/surfsense_web/components/markdown-viewer.tsx b/surfsense_web/components/markdown-viewer.tsx new file mode 100644 index 000000000..4398d1067 --- /dev/null +++ b/surfsense_web/components/markdown-viewer.tsx @@ -0,0 +1,154 @@ +import React from "react"; +import ReactMarkdown from "react-markdown"; +import rehypeRaw from "rehype-raw"; +import rehypeSanitize from "rehype-sanitize"; +import remarkGfm from "remark-gfm"; +import { cn } from "@/lib/utils"; +import { Citation } from "./chat/Citation"; +import { Source } from "./chat/types"; + +interface MarkdownViewerProps { + content: string; + className?: string; + getCitationSource?: (id: number) => Source | null; +} + +export function MarkdownViewer({ content, className, getCitationSource }: MarkdownViewerProps) { + return ( +
+ { + // If there's no getCitationSource function, just render normally + if (!getCitationSource) { + return

{children}

; + } + + // Process citations within paragraph content + return

{processCitationsInReactChildren(children, getCitationSource)}

; + }, + a: ({node, children, ...props}) => { + // Process citations within link content if needed + const processedChildren = getCitationSource + ? processCitationsInReactChildren(children, getCitationSource) + : children; + return
{processedChildren}; + }, + ul: ({node, ...props}) =>