diff --git a/.github/workflows/playwright.yml b/.github/workflows/playwright.yml index 3893ea77..06b8b283 100644 --- a/.github/workflows/playwright.yml +++ b/.github/workflows/playwright.yml @@ -33,9 +33,20 @@ jobs: npm install npm run build NEXTAUTH_SECRET=SECRET npm start & npx playwright test --reporter=dot,list + - name: Ensure required directories exist + run: | + mkdir -p playwright-report + mkdir -p playwright-report/artifacts - uses: actions/upload-artifact@v4 - if: ${{ !cancelled() }} + if: always() with: name: playwright-report path: playwright-report/ retention-days: 30 + - name: Upload failed test screenshots + if: always() + uses: actions/upload-artifact@v4 + with: + name: failed-test-screenshots + path: playwright-report/artifacts/ + retention-days: 30 diff --git a/README.md b/README.md index edd18fcb..3d30f415 100644 --- a/README.md +++ b/README.md @@ -1,87 +1,113 @@ +# CodeGraph - Knowledge Graph Visualization Tool + +### Visualize your repository with our graph for code analysis + [![Try Free](https://img.shields.io/badge/Try%20Free-FalkorDB%20Cloud-FF8101?labelColor=FDE900&link=https://app.falkordb.cloud)](https://app.falkordb.cloud) [![Dockerhub](https://img.shields.io/docker/pulls/falkordb/falkordb?label=Docker)](https://hub.docker.com/r/falkordb/falkordb/) [![Discord](https://img.shields.io/discord/1146782921294884966?style=flat-square)](https://discord.com/invite/6M4QwDXn2w) [![Workflow](https://github.com/FalkorDB/code-graph/actions/workflows/nextjs.yml/badge.svg?branch=main)](https://github.com/FalkorDB/code-graph/actions/workflows/nextjs.yml) +- + +![Alt Text](https://res.cloudinary.com/dhd0k02an/image/upload/v1739719361/FalkorDB_-_Github_-_readme_jr6scy.gif) -![image](https://github.com/FalkorDB/code-graph/assets/753206/60f535ed-cf29-44b2-9005-721f11614803) +**👉🏻[Live Demo](https://code-graph.falkordb.com/)** -## Getting Started -[Live Demo](https://code-graph.falkordb.com/) +## Running Locally -## Run locally -This project is composed of three pieces: +This project consists of three core components: -1. FalkorDB Graph DB - this is where your graphs are stored and queried -2. Code-Graph-Backend - backend logic -3. Code-Graph-Frontend - website +1. **FalkorDB Graph DB** – Stores and queries your graphs. +2. **Code-Graph-Backend** – Handles backend logic. +3. **Code-Graph-Frontend** – Provides the web interface. -You'll need to start all three components: +To set up the project, you’ll need to start all three components. -### Run FalkorDB +### 1. Start FalkorDB + +Run the following command to start FalkorDB using Docker: ```bash docker run -p 6379:6379 -it --rm falkordb/falkordb ``` -### Run Code-Graph-Backend +### 2. Start the Backend -#### Clone the Backend +#### Clone the Backend Repository ```bash git clone https://github.com/FalkorDB/code-graph-backend.git +cd code-graph-backend ``` -#### Setup environment variables +#### Set Up Environment Variables -`SECRET_TOKEN` - user defined token used to authorize the request +Define the required environment variables: ```bash export FALKORDB_HOST=localhost FALKORDB_PORT=6379 \ - OPENAI_API_KEY= SECRET_TOKEN= \ + OPENAI_API_KEY= SECRET_TOKEN= \ FLASK_RUN_HOST=0.0.0.0 FLASK_RUN_PORT=5000 ``` -#### Install dependencies & run +`SECRET_TOKEN` is a user-defined token used for request authorization. -```bash -cd code-graph-backend +#### Install Dependencies & Start the Backend +```bash pip install --no-cache-dir -r requirements.txt - flask --app api/index.py run --debug > flask.log 2>&1 & - ``` -### Run Code-Graph-Frontend +### 3. Start the Frontend -#### Clone the Frontend +#### Clone the Frontend Repository ```bash git clone https://github.com/FalkorDB/code-graph.git +cd code-graph ``` -#### Setup environment variables +#### Set Up Environment Variables ```bash export BACKEND_URL=http://${FLASK_RUN_HOST}:${FLASK_RUN_PORT} \ - SECRET_TOKEN= OPENAI_API_KEY= + SECRET_TOKEN= OPENAI_API_KEY= ``` -#### Install dependencies & run +#### Install Dependencies & Start the Frontend ```bash -cd code-graph npm install npm run dev ``` -### Process a local repository +### 4. Process a Local Repository + +Use the following `curl` command to analyze a local repository: + ```bash -curl -X POST http://127.0.0.1:5000/analyze_folder -H "Content-Type: application/json" -d '{"path": "", "ignore": ["./.github", "./sbin", "./.git","./deps", "./bin", "./build"]}' -H "Authorization: " +curl -X POST http://127.0.0.1:5000/analyze_folder \ + -H "Content-Type: application/json" \ + -H "Authorization: " \ + -d '{"path": "", "ignore": ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"]}' ``` -Note: At the moment code-graph can analyze both the C & Python source files. -Support for additional languages e.g. JavaScript, Go, Java is planned to be added -in the future. +**Note:** Currently, Code-Graph supports analyzing C and Python source files. +Support for additional languages (e.g., JavaScript, Go, Java) is planned. + +### 5. Access the Web Interface + +Once everything is running, open your browser and go to: + +[http://localhost:3000](http://localhost:3000) + +## Community + +Have questions or feedback? Reach out via: + +* [GitHub Issues](https://github.com/FalkorDB/GraphRAG-SDK/issues) +* Join our [Discord](https://discord.com/invite/6M4QwDXn2w) + +⭐️ If you find this repository helpful, please consider giving it a star! -Browse to [http://localhost:3000](http://localhost:3000) +Knowledge Graph, Code Analysis, Code Visualization, Dead Code Analysis, Graph Database diff --git a/app/api/repo/route.ts b/app/api/repo/route.ts index 95aa6cfb..421be274 100644 --- a/app/api/repo/route.ts +++ b/app/api/repo/route.ts @@ -1,4 +1,4 @@ -import { NextResponse } from "next/server"; +import { NextRequest, NextResponse } from "next/server"; import { getEnvVariables } from "../utils"; export async function GET() { @@ -22,39 +22,41 @@ export async function GET() { return NextResponse.json({ result: repositories }, { status: 200 }) } catch (err) { console.error(err) - return NextResponse.json((err as Error).message, { status: 400 }) + return NextResponse.json((err as Error).message, { status: 400 }) } } -// export async function POST(request: NextRequest) { - -// const repo_url = request.nextUrl.searchParams.get('url'); - -// try { - -// if (!repo_url) { -// throw new Error("URL parameter is missing"); -// } - -// const { url, token } = getEnvVariables(); - -// const result = await fetch(`${url}/process_repo`, { -// method: 'POST', -// body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), -// headers: { -// "Authorization": token, -// 'Content-Type': 'application/json' -// }, -// cache: 'no-store' -// }); - -// if (!result.ok) { -// throw new Error(await result.text()); -// } - -// return NextResponse.json({ message: "success" }, { status: 200 }); -// } catch (err) { -// console.error(err) -// return NextResponse.json((err as Error).message, { status: 400 }); -// } -// } +export async function POST(request: NextRequest) { + + const repo_url = request.nextUrl.searchParams.get('url'); + + try { + + if (!repo_url) { + throw new Error("URL parameter is missing"); + } + + const { url, token } = getEnvVariables(); + + const isLocal = repo_url.startsWith("file://") + + const result = await fetch(`${url}/${isLocal ? "analyze_folder" : "analyze_repo"}`, { + method: 'POST', + body: JSON.stringify({ repo_url, ignore: ["./.github", "./sbin", "./.git", "./deps", "./bin", "./build"] }), + headers: { + "Authorization": token, + 'Content-Type': 'application/json' + }, + cache: 'no-store' + }); + + if (!result.ok) { + throw new Error(await result.text()); + } + + return NextResponse.json({ message: "success" }, { status: 200 }); + } catch (err) { + console.error(err) + return NextResponse.json((err as Error).message, { status: 400 }); + } +} \ No newline at end of file diff --git a/app/components/Input.tsx b/app/components/Input.tsx index 83ed01be..58847655 100644 --- a/app/components/Input.tsx +++ b/app/components/Input.tsx @@ -1,12 +1,11 @@ import { toast } from "@/components/ui/use-toast" import { getCategoryColorName, getCategoryColorValue, Graph } from "./model" import { useEffect, useRef, useState } from "react" -import { PathNode } from "../page" +import { PathNode } from "@/lib/utils" import { cn } from "@/lib/utils" import { prepareArg } from "../utils" interface Props extends React.InputHTMLAttributes { - value?: string graph: Graph onValueChange: (node: PathNode) => void handleSubmit?: (node: any) => void @@ -16,7 +15,7 @@ interface Props extends React.InputHTMLAttributes { scrollToBottom?: () => void } -export default function Input({ value, onValueChange, handleSubmit, graph, icon, node, className, parentClassName, scrollToBottom, ...props }: Props) { +export default function Input({ onValueChange, handleSubmit, graph, icon, node, className, parentClassName, scrollToBottom, ...props }: Props) { const [open, setOpen] = useState(false) const [options, setOptions] = useState([]) @@ -38,15 +37,13 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, let isLastRequest = true const timeout = setTimeout(async () => { - if (!value || node?.id) { - if (!value) { - setOptions([]) - } + if (!node?.name) { + setOptions([]) setOpen(false) return } - const result = await fetch(`/api/repo/${prepareArg(graph.Id)}/?prefix=${prepareArg(value)}`, { + const result = await fetch(`/api/repo/${prepareArg(graph.Id)}/?prefix=${prepareArg(node?.name)}`, { method: 'POST' }) @@ -66,7 +63,7 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, setOptions(completions || []) - if (completions?.length > 0) { + if (completions?.length > 0 && !node?.id) { setOpen(true) } else { setOpen(false) @@ -77,7 +74,7 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, clearTimeout(timeout) isLastRequest = false } - }, [value, graph.Id]) + }, [node?.name, graph.Id]) const handleKeyDown = (e: React.KeyboardEvent) => { const container = containerRef.current @@ -87,6 +84,7 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, const option = options.find((o, i) => i === selectedOption) if (!option) return if (handleSubmit) { + onValueChange({ name: option.properties.name, id: option.id }) handleSubmit(option) } else { if (!open) return @@ -133,7 +131,7 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, return (
{ const newVal = e.target.value const invalidChars = /[%*()\-\[\]{};:"|~]/; @@ -168,7 +167,7 @@ export default function Input({ value, onValueChange, handleSubmit, graph, icon, open &&
void setData: Dispatch> + chartRef: GraphRef + messages: Message[] + setMessages: Dispatch> + query: string + setQuery: Dispatch> + selectedPath: PathData | undefined + setSelectedPath: Dispatch> + setChatOpen?: Dispatch> + paths: PathData[] + setPaths: Dispatch> } const SUGGESTIONS = [ @@ -105,20 +51,7 @@ const RemoveLastPath = (messages: Message[]) => { return messages } -export function Chat({ repo, path, setPath, graph, selectedPathId, isPathResponse, setIsPathResponse, setData }: Props) { - - // Holds the messages in the chat - const [messages, setMessages] = useState([]); - - // Holds the messages in the chat - const [paths, setPaths] = useState([]); - - const [selectedPath, setSelectedPath] = useState(); - - // Holds the user input while typing - const [query, setQuery] = useState(''); - - const [tipOpen, setTipOpen] = useState(false); +export function Chat({ messages, setMessages, query, setQuery, selectedPath, setSelectedPath, setChatOpen, repo, path, setPath, graph, selectedPathId, isPathResponse, setIsPathResponse, setData, chartRef, paths, setPaths }: Props) { const [sugOpen, setSugOpen] = useState(false); @@ -131,15 +64,19 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons const p = paths.find((path) => [...path.links, ...path.nodes].some((e: any) => e.id === selectedPathId)) if (!p) return - - handelSetSelectedPath(p) + handleSetSelectedPath(p) }, [selectedPathId]) // Scroll to the bottom of the chat on new message useEffect(() => { - setTimeout(() => { + if (messages.length === 0) return + const timeout = setTimeout(() => { containerRef.current?.scrollTo(0, containerRef.current?.scrollHeight); }, 300) + + return () => { + clearTimeout(timeout) + } }, [messages]); useEffect(() => { @@ -153,7 +90,10 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons setPaths([]) }, [isPathResponse]) - const handelSetSelectedPath = (p: PathData) => { + const handleSetSelectedPath = (p: PathData) => { + const chart = chartRef.current + + if (!chart) return setSelectedPath(prev => { if (prev) { if (isPathResponse && paths.some((path) => [...path.nodes, ...path.links].every((e: any) => [...prev.nodes, ...prev.links].some((e: any) => e.id === e.id)))) { @@ -206,6 +146,10 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons }); } setData({ ...graph.Elements }) + setTimeout(() => { + chart.zoomToFit(1000, 150, (n: NodeObject) => p.nodes.some(node => node.id === n.id)); + }, 0) + setChatOpen && setChatOpen(false) } // A function that handles the change event of the url input box @@ -262,10 +206,16 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons } const handleSubmit = async () => { + const chart = chartRef.current + + if (!chart) return + setSelectedPath(undefined) if (!path?.start?.id || !path.end?.id) return + setPath(undefined) + const result = await fetch(`/api/repo/${prepareArg(repo)}/${prepareArg(String(path.start.id))}/?targetId=${prepareArg(String(path.end.id))}`, { method: 'POST' }) @@ -294,18 +244,20 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons setPaths(formattedPaths) setMessages((prev) => [...RemoveLastPath(prev), { type: MessageTypes.PathResponse, paths: formattedPaths, graphName: graph.Id }]); - setPath(undefined) setIsPathResponse(true) setData({ ...graph.Elements }) + setTimeout(() => { + chart.zoomToFit(1000, 150, (n: NodeObject) => formattedPaths.some(p => p.nodes.some(node => node.id === n.id))); + }, 0) } - const getTip = (disabled = false) => + const getTip = (className?: string) => <> + { + SUGGESTIONS.map((s, i) => ( + + )) + } const getMessage = (message: Message, index?: number) => { @@ -373,7 +337,7 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons parentClassName="w-full" graph={graph} onValueChange={({ name, id }) => setPath(prev => ({ start: { name, id }, end: prev?.end }))} - value={path?.start?.name} + value={path?.start?.name || ""} placeholder="Start typing starting point" type="text" icon={} @@ -383,7 +347,7 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons setPath(prev => ({ end: { name, id }, start: prev?.start }))} placeholder="Start typing end point" type="text" @@ -420,12 +384,12 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons } if (selectedPath?.nodes.every(node => p?.nodes.some((n) => n.id === node.id)) && selectedPath.nodes.length === p.nodes.length) return - + if (!isPathResponse) { setIsPathResponse(undefined) - + } - handelSetSelectedPath(p) + handleSetSelectedPath(p) }} >

#{i + 1}

@@ -450,18 +414,15 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons } return ( -
+
{ messages.length === 0 && <> -

WELCOME TO OUR ASSISTANCE SERVICE

- - We can help you access and update only the needed - data via paths, optimizing network requests with - batching and catching for better performance. - - {getTip()} +

What would you like to analyze?

+
+ {getTip()} +
} { @@ -469,54 +430,25 @@ export function Chat({ repo, path, setPath, graph, selectedPathId, isPathRespons return getMessage(message, index) }) } - { - tipOpen && -
ref?.focus()} className="bg-white absolute bottom-24 border rounded-md flex flex-col gap-3 p-2 overflow-y-auto" tabIndex={-1} onMouseDown={(e) => e.preventDefault()} onBlur={() => setTipOpen(false)}> - {getTip(isSendMessage)} -
- }
- -
- { - repo && -
- -
- - - - - -
-
- } -
- - { - SUGGESTIONS.map((s, i) => ( - - )) - } - -
+
+ + + + + + {getTip("!w-full")} + + +
+ + +
+
); } diff --git a/app/components/code-graph.tsx b/app/components/code-graph.tsx index 83d27c88..fdf31e5f 100644 --- a/app/components/code-graph.tsx +++ b/app/components/code-graph.tsx @@ -1,40 +1,51 @@ -import { Dispatch, RefObject, SetStateAction, useContext, useEffect, useRef, useState } from "react"; -import { GraphData, Node } from "./model"; -import { GraphContext } from "./provider"; +import { Dispatch, SetStateAction, useEffect, useRef, useState } from "react"; +import { Graph, GraphData, Node, Link } from "./model"; import { Toolbar } from "./toolbar"; import { Labels } from "./labels"; -import { GitFork, Search, X } from "lucide-react"; +import { Download, GitFork, Search, X } from "lucide-react"; import ElementMenu from "./elementMenu"; import Combobox from "./combobox"; import { toast } from '@/components/ui/use-toast'; -import { Path, PathNode } from '../page'; +import { Path } from "@/lib/utils"; import Input from './Input'; // import CommitList from './commitList'; import { Checkbox } from '@/components/ui/checkbox'; import dynamic from 'next/dynamic'; import { Position } from "./graphView"; import { prepareArg } from '../utils'; +import { GraphRef } from "@/lib/utils"; const GraphView = dynamic(() => import('./graphView')); interface Props { + graph: Graph, data: GraphData, setData: Dispatch>, - onFetchGraph: (graphName: string) => void, + onFetchGraph: (graphName: string) => Promise, onFetchNode: (nodeIds: number[]) => Promise, options: string[] setOptions: Dispatch> isShowPath: boolean setPath: Dispatch> - chartRef: RefObject + chartRef: GraphRef selectedValue: string selectedPathId: number | undefined setSelectedPathId: (selectedPathId: number) => void isPathResponse: boolean | undefined setIsPathResponse: Dispatch> + handleSearchSubmit: (node: any) => void + searchNode: any + setSearchNode: Dispatch> + cooldownTicks: number | undefined + setCooldownTicks: Dispatch> + onCategoryClick: (name: string, show: boolean) => void + handleDownloadImage: () => void + zoomedNodes: Node[] + setZoomedNodes: Dispatch> } export function CodeGraph({ + graph, data, setData, onFetchGraph, @@ -48,24 +59,28 @@ export function CodeGraph({ setSelectedPathId, isPathResponse, setIsPathResponse, - selectedPathId + selectedPathId, + handleSearchSubmit, + searchNode, + setSearchNode, + cooldownTicks, + setCooldownTicks, + onCategoryClick, + handleDownloadImage, + zoomedNodes, + setZoomedNodes }: Props) { - let graph = useContext(GraphContext) - const [url, setURL] = useState(""); - const [selectedObj, setSelectedObj] = useState(); + const [selectedObj, setSelectedObj] = useState(); const [selectedObjects, setSelectedObjects] = useState([]); const [position, setPosition] = useState(); const [graphName, setGraphName] = useState(""); - const [searchNode, setSearchNode] = useState({}); const [commits, setCommits] = useState([]); const [nodesCount, setNodesCount] = useState(0); const [edgesCount, setEdgesCount] = useState(0); const [commitIndex, setCommitIndex] = useState(0); const [currentCommit, setCurrentCommit] = useState(0); - const [cooldownTicks, setCooldownTicks] = useState(0) - const [cooldownTime, setCooldownTime] = useState(0) const containerRef = useRef(null); useEffect(() => { @@ -81,7 +96,7 @@ export function CodeGraph({ const handleKeyDown = (event: KeyboardEvent) => { if (event.key === 'Delete') { if (selectedObj && selectedObjects.length === 0) return - handelRemove([...selectedObjects.map(obj => obj.id), selectedObj?.id].filter(id => id !== undefined)); + handleRemove([...selectedObjects.map(obj => obj.id), selectedObj?.id].filter(id => id !== undefined), "nodes"); } }; @@ -144,50 +159,45 @@ export function CodeGraph({ } run() + }, [graphName]) - function handleSelectedValue(value: string) { + async function handleSelectedValue(value: string) { setGraphName(value) onFetchGraph(value) } - function onCategoryClick(name: string, show: boolean) { - graph.Categories.find(c => c.name === name)!.show = show - - graph.Elements.nodes.forEach(node => { - if (!(node.category === name)) return - node.visible = show - }) - - graph.visibleLinks(show) - - setData({ ...graph.Elements }) - } - const deleteNeighbors = (nodes: Node[]) => { + if (nodes.length === 0) return; + const expandedNodes: Node[] = [] + graph.Elements = { - nodes: graph.Elements.nodes.map(node => { + nodes: graph.Elements.nodes.filter(node => { + if (!node.collapsed) return true + const isTarget = graph.Elements.links.some(link => link.target.id === node.id && nodes.some(n => n.id === link.source.id)); - if (!isTarget || !node.collapsed) return node + if (!isTarget) return true + + const deleted = graph.NodesMap.delete(Number(node.id)) - if (node.expand) { - node.expand = false - deleteNeighbors([node]) + if (deleted && node.expand) { + expandedNodes.push(node) } - graph.NodesMap.delete(Number(node.id)) - }).filter(node => node !== undefined), + return false + }), links: graph.Elements.links } + deleteNeighbors(expandedNodes) + graph.removeLinks() } const handleExpand = async (nodes: Node[], expand: boolean) => { - if (expand) { const elements = await onFetchNode(nodes.map(n => n.id)) @@ -208,51 +218,29 @@ export function CodeGraph({ nodes.forEach((node) => { node.expand = expand }) - - setSelectedObj(undefined) - setData({ ...graph.Elements }) - } - - const handelSearchSubmit = (node: any) => { - const n = { name: node.properties.name, id: node.id } - - let chartNode = graph.Elements.nodes.find(n => n.id == node.id) - - if (!chartNode?.visible) { - if (!chartNode) { - chartNode = graph.extend({ nodes: [node], edges: [] }).nodes[0] - } else { - chartNode.visible = true - setCooldownTicks(undefined) - setCooldownTime(1000) - } - graph.visibleLinks(true, [chartNode.id]) - } - setSearchNode(n) + setSelectedObj(undefined) setData({ ...graph.Elements }) - - const chart = chartRef.current - - if (chart) { - chart.centerAt(chartNode.x, chartNode.y, 1000); - } } - const handelRemove = (ids: number[]) => { - graph.Elements.nodes.forEach(node => { - if (!ids.includes(node.id)) return - node.visible = false + const handleRemove = (ids: number[], type: "nodes" | "links") => { + graph.Elements[type].forEach(element => { + if (!ids.includes(element.id)) return + element.visible = false }) graph.visibleLinks(false, ids) + setSelectedObj(undefined) + setSelectedObjects([]) + setData({ ...graph.Elements }) } return ( -
-
+
+
+
{ graph.Id ? -
-
-
+
+
+
setSearchNode({ name })} + onValueChange={(node) => setSearchNode(node)} icon={} - handleSubmit={handelSearchSubmit} + handleSubmit={(node) => { + handleSearchSubmit(node) + }} node={searchNode} /> @@ -295,15 +284,14 @@ export function CodeGraph({ } { - (graph.Elements.nodes.some(e => !e.visible)) && + (graph.getElements().some(e => !e.visible)) &&
-
-
-

{nodesCount} Nodes

-

{edgesCount} Edges

-
-
- { - commitIndex !== commits.length && -
-
- -

Display Changes

-
-
-
-

Were added

-
-
-
-

Were edited

-
-
- } - -
-
+
+
+

{nodesCount} Nodes

+

|

+

{edgesCount} Edges

+
+
+ { + commitIndex !== commits.length && +
+
+ +

Display Changes

+
+
+
+

Were added

+
+
+
+

Were edited

+
+
+ } + +
+
:
- -

Select a repo to show its graph here

+ +

Select a repo to show its graph here

} diff --git a/app/components/combobox.tsx b/app/components/combobox.tsx index 354fee3f..c22d9334 100644 --- a/app/components/combobox.tsx +++ b/app/components/combobox.tsx @@ -51,7 +51,7 @@ export default function Combobox({ options, setOptions, selectedValue, onSelecte return ( setCreateURL(e.target.value)} - placeholder="Type URL" - /> -
- + DESKTOP_TIPS.map((tip, index) => ( +
+
+

{tip.title}

+

{tip.keyboardCommand}

+
+

{tip.description}

- - : - } - - */} - -
-
-
- - - + )) + } + + + { + process.env.NEXT_PUBLIC_LOCAL_MODE && + + + + + + + {!isSubmit ? "CREATE A NEW PROJECT" : "THANK YOU FOR A NEW REQUEST"} + + + { + !isSubmit + ? "Please provide the URL of the project to connect and start querying data" + : "Processing your graph, this could take a while. We appreciate your patience" + } + + { + !isSubmit ? +
+ setCreateURL(e.target.value)} + placeholder="Type Project URL (File:// or https://)" + /> +
+ +
+
+ : + } +
+
+ } + +
+
+
+ + handleSearchSubmit(node, desktopChartRef)} + searchNode={searchNode} + setSearchNode={setSearchNode} + cooldownTicks={cooldownTicks} + setCooldownTicks={setCooldownTicks} + onCategoryClick={onCategoryClick} + handleDownloadImage={handleDownloadImage} + zoomedNodes={zoomedNodes} + setZoomedNodes={setZoomedNodes} /> - - - - - + + + + + +
+
+
+ + FalkorDB + + +
+ + {menuOpen && ( +
+
    +
  • + +

    Github

    + +
  • +
  • + +

    Discord

    + +
  • +
  • + +

    Main Website

    + +
  • + + + {MOBILE_TIPS.map((tip, index) => ( + +

    {tip}

    +
    + ))} +
    +
    + {MOBILE_TIPS.map((_, index) => ( +
    + ))} +
    + + + +
+
+ )} +
+ handleSearchSubmit(node, mobileChartRef)} + setSearchNode={setSearchNode} + searchNode={searchNode} + cooldownTicks={cooldownTicks} + setCooldownTicks={setCooldownTicks} + onCategoryClick={onCategoryClick} + handleDownloadImage={handleDownloadImage} + zoomedNodes={zoomedNodes} + setZoomedNodes={setZoomedNodes} /> - - - + {graph.Id && ( +
+ + + + + + + + + + + + + + + + + + + + + + + setSearchNode(node)} + icon={} + handleSubmit={(node) => handleSearchSubmit(node, mobileChartRef)} + node={searchNode} + /> + +
+ +

Take Screenshot

+
+
+
+
+ )} +
+
+ ) } diff --git a/components.json b/components.json index 811d2dbc..05fa7462 100644 --- a/components.json +++ b/components.json @@ -12,6 +12,10 @@ }, "aliases": { "components": "@/components", - "utils": "@/lib/utils" - } + "utils": "@/lib/utils", + "ui": "@/components/ui", + "lib": "@/lib", + "hooks": "@/hooks" + }, + "iconLibrary": "lucide" } \ No newline at end of file diff --git a/components/ui/carousel.tsx b/components/ui/carousel.tsx new file mode 100644 index 00000000..2e34403b --- /dev/null +++ b/components/ui/carousel.tsx @@ -0,0 +1,262 @@ +"use client" + +import * as React from "react" +import useEmblaCarousel, { + type UseEmblaCarouselType, +} from "embla-carousel-react" +import { ArrowLeft, ArrowRight } from "lucide-react" + +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" + +type CarouselApi = UseEmblaCarouselType[1] +type UseCarouselParameters = Parameters +type CarouselOptions = UseCarouselParameters[0] +type CarouselPlugin = UseCarouselParameters[1] + +type CarouselProps = { + opts?: CarouselOptions + plugins?: CarouselPlugin + orientation?: "horizontal" | "vertical" + setApi?: (api: CarouselApi) => void +} + +type CarouselContextProps = { + carouselRef: ReturnType[0] + api: ReturnType[1] + scrollPrev: () => void + scrollNext: () => void + canScrollPrev: boolean + canScrollNext: boolean +} & CarouselProps + +const CarouselContext = React.createContext(null) + +function useCarousel() { + const context = React.useContext(CarouselContext) + + if (!context) { + throw new Error("useCarousel must be used within a ") + } + + return context +} + +const Carousel = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & CarouselProps +>( + ( + { + orientation = "horizontal", + opts, + setApi, + plugins, + className, + children, + ...props + }, + ref + ) => { + const [carouselRef, api] = useEmblaCarousel( + { + ...opts, + axis: orientation === "horizontal" ? "x" : "y", + }, + plugins + ) + const [canScrollPrev, setCanScrollPrev] = React.useState(false) + const [canScrollNext, setCanScrollNext] = React.useState(false) + + const onSelect = React.useCallback((api: CarouselApi) => { + if (!api) { + return + } + + setCanScrollPrev(api.canScrollPrev()) + setCanScrollNext(api.canScrollNext()) + }, []) + + const scrollPrev = React.useCallback(() => { + api?.scrollPrev() + }, [api]) + + const scrollNext = React.useCallback(() => { + api?.scrollNext() + }, [api]) + + const handleKeyDown = React.useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "ArrowLeft") { + event.preventDefault() + scrollPrev() + } else if (event.key === "ArrowRight") { + event.preventDefault() + scrollNext() + } + }, + [scrollPrev, scrollNext] + ) + + React.useEffect(() => { + if (!api || !setApi) { + return + } + + setApi(api) + }, [api, setApi]) + + React.useEffect(() => { + if (!api) { + return + } + + onSelect(api) + api.on("reInit", onSelect) + api.on("select", onSelect) + + return () => { + api?.off("select", onSelect) + } + }, [api, onSelect]) + + return ( + +
+ {children} +
+
+ ) + } +) +Carousel.displayName = "Carousel" + +const CarouselContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { carouselRef, orientation } = useCarousel() + + return ( +
+
+
+ ) +}) +CarouselContent.displayName = "CarouselContent" + +const CarouselItem = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => { + const { orientation } = useCarousel() + + return ( +
+ ) +}) +CarouselItem.displayName = "CarouselItem" + +const CarouselPrevious = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollPrev, canScrollPrev } = useCarousel() + + return ( + + ) +}) +CarouselPrevious.displayName = "CarouselPrevious" + +const CarouselNext = React.forwardRef< + HTMLButtonElement, + React.ComponentProps +>(({ className, variant = "outline", size = "icon", ...props }, ref) => { + const { orientation, scrollNext, canScrollNext } = useCarousel() + + return ( + + ) +}) +CarouselNext.displayName = "CarouselNext" + +export { + type CarouselApi, + Carousel, + CarouselContent, + CarouselItem, + CarouselPrevious, + CarouselNext, +} diff --git a/components/ui/drawer.tsx b/components/ui/drawer.tsx new file mode 100644 index 00000000..25a9aa9c --- /dev/null +++ b/components/ui/drawer.tsx @@ -0,0 +1,123 @@ +"use client" + +import * as React from "react" +import { Drawer as DrawerPrimitive } from "vaul" + +import { cn } from "@/lib/utils" + +const Drawer = ({ + shouldScaleBackground = true, + ...props +}: React.ComponentProps) => ( + +) +Drawer.displayName = "Drawer" + +const DrawerTrigger = DrawerPrimitive.Trigger + +const DrawerPortal = DrawerPrimitive.Portal + +const DrawerClose = DrawerPrimitive.Close + +const DrawerOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerOverlay.displayName = DrawerPrimitive.Overlay.displayName + +interface DrawerContentProps extends React.ComponentPropsWithoutRef { + overlayClassName?: string; + handleClassName?: string; +} + +const DrawerContent = React.forwardRef< + React.ElementRef, + DrawerContentProps +>(({ className, overlayClassName, handleClassName, children, ...props }, ref) => ( + + + +
+ {children} + + +)) +DrawerContent.displayName = "DrawerContent" + +const DrawerHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerHeader.displayName = "DrawerHeader" + +const DrawerFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +DrawerFooter.displayName = "DrawerFooter" + +const DrawerTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerTitle.displayName = DrawerPrimitive.Title.displayName + +const DrawerDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +DrawerDescription.displayName = DrawerPrimitive.Description.displayName + +export { + Drawer, + DrawerPortal, + DrawerOverlay, + DrawerTrigger, + DrawerClose, + DrawerContent, + DrawerHeader, + DrawerFooter, + DrawerTitle, + DrawerDescription, +} diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..8fe00e81 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,37 @@ +version: "3.9" + +services: + falkordb: + image: falkordb/falkordb:latest + ports: + - "6379:6379" + - "3001:3000" + volumes: + - ./:/data/ + stdin_open: true # Keep the container's STDIN open + tty: true # Allocate a pseudo-TTY + + # code-graph-frontend: + # image: falkordb/code-graph-frontend:latest + # ports: + # - "3000:3000" + # depends_on: + # - code-graph-backend + # environment: + # - BACKEND_URL=http://code-graph-backend:5000 # Backend service URL + # - SECRET_TOKEN=Vespa + + code-graph-backend: + image: falkordb/code-graph-backend:latest + ports: + - "5000:5000" + depends_on: + - falkordb + environment: + - FALKORDB_HOST=falkordb + - FALKORDB_PORT=6379 + - OPENAI_API_KEY=YOUR_OPENAI_API_KEY + - SECRET_TOKEN=sC0tTerMania + - FLASK_RUN_HOST=0.0.0.0 + - FLASK_RUN_PORT=5000 + - CODE_GRAPH_PUBLIC=1 \ No newline at end of file diff --git a/e2e/config/constants.ts b/e2e/config/constants.ts index 63bcec56..e697c7b5 100644 --- a/e2e/config/constants.ts +++ b/e2e/config/constants.ts @@ -1,5 +1,5 @@ -export const GRAPH_ID = "GraphRAG-SDK"; -export const PROJECT_NAME = "GraphRAG-SDK"; -export const CHAT_OPTTIONS_COUNT = 1; +export const GRAPHRAG_SDK = "GraphRAG-SDK"; +export const FLASK_GRAPH = "flask"; +export const CHAT_OPTTIONS_COUNT = 6; export const Node_Question = "how many nodes do we have?"; export const Edge_Question = "how many edges do we have?"; \ No newline at end of file diff --git a/e2e/config/testData.ts b/e2e/config/testData.ts index dab5ebf7..98a38ba9 100644 --- a/e2e/config/testData.ts +++ b/e2e/config/testData.ts @@ -1,7 +1,7 @@ export const searchData: { searchInput: string; completedSearchInput?: string; }[] = [ { searchInput: "test"}, { searchInput: "set"}, - { searchInput: "low", completedSearchInput: "lower" }, + { searchInput: "low", completedSearchInput: "lower_items" }, { searchInput: "as", completedSearchInput: "ask"}, ]; @@ -20,10 +20,15 @@ export const nodesPath: { firstNode: string; secondNode: string }[] = [ ]; export const nodes: { nodeName: string; }[] = [ - { nodeName: "import_data"}, + // { nodeName: "ask"}, { nodeName: "add_edge" }, { nodeName: "test_kg_delete"}, { nodeName: "list_graphs"} ]; -export const categories: string[] = ['File', 'Class', 'Function']; \ No newline at end of file +export const categories: string[] = ['File', 'Class', 'Function']; + +export const graphs: { graphName: string; }[] = [ + { graphName: "GraphRAG-SDK" }, + { graphName: "click" }, +]; \ No newline at end of file diff --git a/e2e/logic/POM/codeGraph.ts b/e2e/logic/POM/codeGraph.ts index 9910ded3..b7adcf5e 100644 --- a/e2e/logic/POM/codeGraph.ts +++ b/e2e/logic/POM/codeGraph.ts @@ -1,6 +1,6 @@ -import { Locator, Page } from "playwright"; +import { Download, Locator, Page } from "playwright"; import BasePage from "../../infra/ui/basePage"; -import { delay, waitToBeEnabled } from "../utils"; +import { waitForElementToBeVisible, waitForStableText, waitToBeEnabled } from "../utils"; declare global { interface Window { @@ -9,13 +9,28 @@ declare global { } export default class CodeGraph extends BasePage { + + private isMobile: boolean = false; + + public setMobileState(isMobile: boolean): void { + this.isMobile = isMobile; + } + + private get container(): Locator { + return this.page.locator(this.isMobile ? "#mobile" : "#desktop"); + } + + private get scopedLocator(): (selector: string) => Locator { + return (selector: string) => this.container.locator(selector); + } + /* NavBar Locators*/ private get falkorDBLogo(): Locator { - return this.page.locator("//*[img[@alt='FalkorDB']]") + return this.scopedLocator("//*[img[@alt='FalkorDB']]") } private get navBaritem(): (navItem: string) => Locator { - return (navItem: string) => this.page.locator(`//a[p[text() = '${navItem}']]`); + return (navItem: string) => this.scopedLocator(`//a[p[text() = '${navItem}']]`); } private get createNewProjectBtn(): Locator { @@ -23,11 +38,11 @@ export default class CodeGraph extends BasePage { } private get createNewProjectDialog(): Locator { - return this.page.locator("//div[@role='dialog']") + return this.scopedLocator("//div[@role='dialog']") } private get tipBtn(): Locator { - return this.page.locator("//button[@title='Tip']") + return this.scopedLocator("//button[@title='Tip']") } private get genericMenu(): Locator { @@ -40,7 +55,7 @@ export default class CodeGraph extends BasePage { /* CodeGraph Locators*/ private get comboBoxbtn(): Locator { - return this.page.locator("//button[@role='combobox']") + return this.scopedLocator("//button[@role='combobox']") } private get selectGraphInComboBoxByName(): (graph: string) => Locator { @@ -52,84 +67,85 @@ export default class CodeGraph extends BasePage { } private get lastElementInChat(): Locator { - return this.page.locator("//main[@data-name='main-chat']/*[last()]/span"); + return this.scopedLocator("//main[@data-name='main-chat']/*[last()]/span"); } private get typeUrlInput(): Locator { - return this.page.locator("//div[@role='dialog']/form/input"); + return this.scopedLocator("//div[@role='dialog']/form/input"); } private get createBtnInCreateProjectDialog(): Locator { - return this.page.locator("//div[@role='dialog']/form//following::button//p[contains(text(), 'Create')]") + return this.scopedLocator("//div[@role='dialog']/form//following::button//p[contains(text(), 'Create')]") } private get createProjectWaitDialog(): Locator { - return this.page.locator("//div[@role='dialog']//div//h2[contains(text(), 'THANK YOU FOR A NEW REQUEST')]") + return this.scopedLocator("//div[@role='dialog']//div//h2[contains(text(), 'THANK YOU FOR A NEW REQUEST')]") } private get dialogCreatedGraphsList(): (graph: string) => Locator { - return (graph: string) => this.page.locator(`//div[@role='presentation']/div//span[2][contains(text(), '${graph}')]`); + return (graph: string) => this.scopedLocator(`//div[@role='presentation']/div//span[2][contains(text(), '${graph}')]`); } private get searchBarInput(): Locator { - return this.page.locator("//div[@data-name='search-bar']/input"); + return this.scopedLocator("//div[@data-name='search-bar']/input"); } private get searchBarAutoCompleteOptions(): Locator { - return this.page.locator("//div[@data-name='search-bar']/div/button"); + return this.scopedLocator("//div[@data-name='search-bar']/div/button"); } private get searchBarElements(): Locator { - return this.page.locator("//div[@data-name='search-bar']/div/button/div/p[1]"); + return this.scopedLocator("//div[@data-name='search-bar']/div/button/div/p[1]"); } private get searchBarOptionBtn(): (buttonNum: string) => Locator { - return (buttonNum: string) => this.page.locator(`//div[@data-name='search-bar']//button[${buttonNum}]`); + return (buttonNum: string) => this.scopedLocator(`//div[@data-name='search-bar']//button[${buttonNum}]`); } private get searchBarList(): Locator { - return this.page.locator("//div[@data-name='search-bar-list']"); + return this.scopedLocator("//div[@data-name='search-bar-list']"); } /* Chat Locators */ - private get showPathBtn(): Locator { - return this.page.locator("//button[contains(@class, 'Tip')]"); + + private get showPathBtn(): (selection: string) => Locator { + return (selection: string) => this.page.locator(`//button[contains(@class, 'Tip')]//p[contains(text(), '${selection}')]`); } private get askquestionInput(): Locator { - return this.page.locator("//input[contains(@placeholder, 'Ask your question')]"); + return this.scopedLocator("//input[contains(@placeholder, 'Ask your question')]"); } private get askquestionBtn(): Locator { - return this.page.locator("//input[contains(@placeholder, 'Ask your question')]/following::button[1]"); + return this.scopedLocator("//input[contains(@placeholder, 'Ask your question')]/following::button[1]"); } private get lightbulbBtn(): Locator { - return this.page.locator("//button[@data-name='lightbulb']"); + return this.scopedLocator("//button[@data-name='lightbulb']"); } private get lastChatElementButtonCount(): Locator { - return this.page.locator("//main[@data-name='main-chat']/*[last()]/button"); + return this.scopedLocator("//main[@data-name='main-chat']/*[last()]/button"); } private get chatContainer(): Locator { - return this.page.locator("//main[@data-name='main-chat']"); + return this.scopedLocator("//main[@data-name='main-chat']"); } private get previousQuestionLoadingImage(): Locator { - return this.page.locator("//main[@data-name='main-chat']/*[last()-2]//img[@alt='Waiting for response']") + return this.scopedLocator("//main[@data-name='main-chat']/*[last()-2]//img[@alt='Waiting for response']") } private get selectInputForShowPath(): (inputNum: string) => Locator { - return (inputNum: string) => this.page.locator(`(//main[@data-name='main-chat']//input)[${inputNum}]`); + return (inputNum: string) => this.scopedLocator(`(//main[@data-name='main-chat']//input)[${inputNum}]`); } private get locateNodeInLastChatPath(): (node: string) => Locator { - return (node: string) => this.page.locator(`(//main[@data-name='main-chat']//span[contains(text(), '${node}')])[last()]`); + return (node: string) => this.page.locator(`(//main[@data-name='main-chat']//button//span[contains(text(), ${node})])[last()]`); } private get selectFirstPathOption(): (inputNum: string) => Locator { - return (inputNum: string) => this.page.locator(`(//main[@data-name='main-chat']//input)[1]/following::div[${inputNum}]//button[1]`); + return (inputNum: string) => this.scopedLocator(`(//main[@data-name='main-chat']//input)[1]/following::div[${inputNum}]//button[1]`); } private get notificationError(): Locator { @@ -140,46 +156,50 @@ export default class CodeGraph extends BasePage { return this.page.locator("//div[@role='region']//ol//li/button"); } - private get questionOptionsMenu(): Locator { - return this.page.locator("//button[@data-name='questionOptionsMenu']"); - } - private get selectQuestionInMenu(): (questionNumber: string) => Locator { return (questionNumber: string) => this.page.locator(`//div[contains(@role, 'menu')]/button[${questionNumber}]`); } private get lastQuestionInChat(): Locator { - return this.page.locator("//main[@data-name='main-chat']/*[last()-1]/p"); + return this.scopedLocator("//main[@data-name='main-chat']/*[last()-1]/p"); + } + + private get responseLoadingImg(): Locator { + return this.scopedLocator("//img[@alt='Waiting for response']"); + } + + private get waitingForResponseIndicator(): Locator { + return this.scopedLocator('img[alt="Waiting for response"]'); } /* Canvas Locators*/ private get canvasElement(): Locator { - return this.page.locator("//canvas"); + return this.scopedLocator("//canvas"); } private get zoomInBtn(): Locator { - return this.page.locator("//button[@title='Zoom In']"); + return this.scopedLocator("//button[@title='Zoom In']"); } private get zoomOutBtn(): Locator { - return this.page.locator("//button[@title='Zoom Out']"); + return this.scopedLocator("//button[@title='Zoom Out']"); } private get centerBtn(): Locator { - return this.page.locator("//button[@title='Center']"); + return this.scopedLocator("//button[@title='Center']"); } private get codeGraphCheckbox(): (checkbox: string) => Locator { - return (checkbox: string) => this.page.locator(`(//button[@role='checkbox'])[${checkbox}]`); + return (checkbox: string) => this.scopedLocator(`(//button[@role='checkbox'])[${checkbox}]`); } private get clearGraphBtn(): Locator { - return this.page.locator("//button[p[text()='Reset Graph']]"); + return this.scopedLocator("//button[p[text()='Reset Graph']]"); } private get unhideNodesBtn(): Locator { - return this.page.locator("//button[p[text()='Unhide Nodes']]"); + return this.scopedLocator("//button[p[text()='Unhide Nodes']]"); } private get elementMenuButton(): (buttonID: string) => Locator { @@ -187,39 +207,51 @@ export default class CodeGraph extends BasePage { } private get nodeDetailsPanel(): Locator { - return this.page.locator("//div[@data-name='node-details-panel']"); + return this.scopedLocator("//div[@data-name='node-details-panel']"); + } + + private get elementMenu(): Locator { + return this.scopedLocator("//div[@id='elementMenu']"); } private get nodedetailsPanelHeader(): Locator { - return this.page.locator("//div[@data-name='node-details-panel']/header/p"); + return this.scopedLocator("//div[@data-name='node-details-panel']/header/p"); } private get nodedetailsPanelcloseBtn(): Locator { - return this.page.locator("//div[@data-name='node-details-panel']/header/button"); + return this.scopedLocator("//div[@data-name='node-details-panel']/header/button"); } private get canvasMetricsPanel(): (itemId: string) => Locator { - return (itemId: string) => this.page.locator(`//div[@data-name='metrics-panel']/p[${itemId}]`); + return (itemId: string) => this.scopedLocator(`//div[@data-name='metrics-panel']/p[${itemId}]`); } private get nodedetailsPanelID(): Locator { - return this.page.locator("//div[@data-name='node-details-panel']/main/div[1]/p[2]"); + return this.scopedLocator("//div[@data-name='node-details-panel']/main/div[1]/p[2]"); } private get nodedetailsPanelElements(): Locator { - return this.page.locator("//div[@data-name='node-details-panel']/main/div/p[1]"); + return this.scopedLocator("//div[@data-name='node-details-panel']/main/div/p[1]"); } private get canvasElementBeforeGraphSelection(): Locator { - return this.page.locator("//h1[contains(text(), 'Select a repo to show its graph here')]"); + return this.scopedLocator("//h1[contains(text(), 'Select a repo to show its graph here')]"); } private get copyToClipboardNodePanelDetails(): Locator { - return this.page.locator(`//div[@data-name='node-details-panel']//button[@title='Copy src to clipboard']`); + return this.scopedLocator(`//div[@data-name='node-details-panel']//button[@title='Copy src to clipboard']`); + } + + private get nodeToolTip(): (node: string) => Locator { + return (node: string) => this.page.locator(`//div[contains(@class, 'force-graph-container')]/div[contains(text(), '${node}')]`); + } + + private get downloadImageBtn(): Locator { + return this.scopedLocator("//button[@title='downloadImage']"); } /* NavBar functionality */ async clickOnFalkorDbLogo(): Promise { - await this.page.waitForLoadState('networkidle'); + await this.page.waitForLoadState('networkidle'); const [newPage] = await Promise.all([ this.page.waitForEvent('popup'), this.falkorDBLogo.click(), @@ -237,65 +269,79 @@ export default class CodeGraph extends BasePage { } async clickCreateNewProjectBtn(): Promise { + const isVisible = await waitForElementToBeVisible(this.createNewProjectBtn); + if (!isVisible) throw new Error("'Create New Project' button is not visible!"); await this.createNewProjectBtn.click(); } - + async isCreateNewProjectDialog(): Promise { - return await this.createNewProjectDialog.isVisible(); + return await waitForElementToBeVisible(this.createNewProjectDialog); } - - async clickonTipBtn(): Promise { + + async clickOnTipBtn(): Promise { await this.tipBtn.click(); } async isTipMenuVisible(): Promise { - await delay(500); + await this.page.waitForTimeout(500); return await this.genericMenu.isVisible(); } - - async clickonTipMenuCloseBtn(): Promise { + + async clickOnTipMenuCloseBtn(): Promise { + const isVisible = await waitForElementToBeVisible(this.tipMenuCloseBtn); + if (!isVisible) throw new Error("'Tip Menu Close' button is not visible!"); await this.tipMenuCloseBtn.click(); } + /* Chat functionality */ - async clickOnshowPathBtn(): Promise { - await this.showPathBtn.click(); + async clickOnShowPathBtn(selection: string): Promise { + await this.showPathBtn(selection).click(); } - async clickAskquestionBtn(): Promise { + async clickAskQuestionBtn(): Promise { + const isVisible = await waitForElementToBeVisible(this.askquestionBtn); + if (!isVisible) throw new Error("'Ask Question' button is not visible!"); await this.askquestionBtn.click(); } - + async sendMessage(message: string) { - await waitToBeEnabled(this.askquestionInput); + await waitToBeEnabled(this.askquestionBtn); await this.askquestionInput.fill(message); await this.askquestionBtn.click(); } - + async clickOnLightBulbBtn(): Promise { await this.lightbulbBtn.click(); } async getTextInLastChatElement(): Promise{ - await delay(2500); - return (await this.lastElementInChat.textContent())!; + await this.waitingForResponseIndicator.waitFor({ state: 'hidden' }); + return await waitForStableText(this.lastElementInChat); } - async getLastChatElementButtonCount(): Promise{ + async getLastChatElementButtonCount(): Promise { return await this.lastChatElementButtonCount.count(); } - async scrollToTop() { + async scrollToTop(): Promise { + const isVisible = await waitForElementToBeVisible(this.chatContainer); + if (!isVisible) throw new Error("Chat container is not visible!"); + await this.chatContainer.evaluate((chat) => { - chat.scrollTop = 0; + chat.scrollTop = 0; }); } async getScrollMetrics() { - const scrollTop = await this.chatContainer.evaluate((el) => el.scrollTop); - const scrollHeight = await this.chatContainer.evaluate((el) => el.scrollHeight); - const clientHeight = await this.chatContainer.evaluate((el) => el.clientHeight); - return { scrollTop, scrollHeight, clientHeight }; + const isVisible = await waitForElementToBeVisible(this.chatContainer); + if (!isVisible) throw new Error("Chat container is not visible!"); + + return await this.chatContainer.evaluate((el) => ({ + scrollTop: el.scrollTop, + scrollHeight: el.scrollHeight, + clientHeight: el.clientHeight + })); } async isAtBottom(): Promise { @@ -311,33 +357,35 @@ export default class CodeGraph extends BasePage { await this.selectInputForShowPath(inputNum).fill(node); await this.selectFirstPathOption(inputNum).click(); } - + async isNodeVisibleInLastChatPath(node: string): Promise { - await this.locateNodeInLastChatPath(node).waitFor({ state: 'visible' }); - return await this.locateNodeInLastChatPath(node).isVisible(); + await this.page.mouse.click(10, 10); + const nodeLocator = this.locateNodeInLastChatPath(node); + return await waitForElementToBeVisible(nodeLocator); } async isNotificationError(): Promise { - await delay(500); + await this.page.waitForTimeout(500); return await this.notificationError.isVisible(); } async clickOnNotificationErrorCloseBtn(): Promise { + const isVisible = await waitForElementToBeVisible(this.notificationErrorCloseBtn); + if (!isVisible) throw new Error("Notification error close button is not visible!"); await this.notificationErrorCloseBtn.click(); } - - async clickOnQuestionOptionsMenu(): Promise { - await this.questionOptionsMenu.click(); - } - + async selectAndGetQuestionInOptionsMenu(questionNumber: string): Promise { - await this.selectQuestionInMenu(questionNumber).click(); - return await this.selectQuestionInMenu(questionNumber).innerHTML(); + const question = this.selectQuestionInMenu(questionNumber); + await question.click(); + return await question.innerText(); } - + async getLastQuestionInChat(): Promise { - return await this.lastQuestionInChat.innerText(); - } + const isVisible = await waitForElementToBeVisible(this.lastQuestionInChat); + if (!isVisible) throw new Error("Last question in chat is not visible!"); + return (await this.lastQuestionInChat.innerText()) ?? ""; + } /* CodeGraph functionality */ async selectGraph(graph: string | number): Promise { @@ -349,6 +397,7 @@ export default class CodeGraph extends BasePage { await this.selectGraphInComboBoxByName(graph).waitFor({ state : 'visible'}) await this.selectGraphInComboBoxByName(graph).click(); } + await this.page.waitForTimeout(2000); // graph animation delay } async createProject(url : string): Promise { @@ -377,8 +426,9 @@ export default class CodeGraph extends BasePage { } async selectSearchBarOptionBtn(buttonNum: string): Promise { - await delay(1000); - await this.searchBarOptionBtn(buttonNum).click(); + const button = this.searchBarOptionBtn(buttonNum); + await button.waitFor({ state : "visible"}) + await button.click(); } async getSearchBarInputValue(): Promise { @@ -408,23 +458,38 @@ export default class CodeGraph extends BasePage { async clickCenter(): Promise { await this.centerBtn.click(); - await delay(2000); //animation delay + await this.page.waitForTimeout(2000); //animation delay } async clickOnRemoveNodeViaElementMenu(): Promise { - await this.elementMenuButton("Remove").click(); + const button = this.elementMenuButton("Remove"); + const isVisible = await waitForElementToBeVisible(button); + if (!isVisible) throw new Error("'Remove' button is not visible!"); + await button.click(); } async nodeClick(x: number, y: number): Promise { - await this.canvasElement.hover({ position: { x, y } }); - await this.canvasElement.click({ position: { x, y } }); + await this.waitForCanvasAnimationToEnd(); + for (let attempt = 1; attempt <= 3; attempt++) { + await this.canvasElement.hover({ position: { x, y } }); + await this.page.waitForTimeout(500); + await this.canvasElement.click({ position: { x, y }, button: 'right' }); + if (await this.elementMenu.isVisible()) { + return; + } + await this.page.waitForTimeout(1000); + } + + throw new Error(`Failed to click, elementMenu not visible after multiple attempts.`); } + async selectCodeGraphCheckbox(checkbox: string): Promise { await this.codeGraphCheckbox(checkbox).click(); } async clickOnClearGraphBtn(): Promise { + await this.page.mouse.click(10, 10); await this.clearGraphBtn.click(); } @@ -447,17 +512,27 @@ export default class CodeGraph extends BasePage { } async isNodeDetailsPanel(): Promise { + await this.page.waitForTimeout(500); return this.nodeDetailsPanel.isVisible(); } async clickOnViewNode(): Promise { - await this.elementMenuButton("View Node").click(); + const button = this.elementMenuButton("View Node"); + const isButtonVisible = await waitForElementToBeVisible(button); + if (!isButtonVisible) throw new Error("'View Node' button is not visible!"); + await button.click(); } async getNodeDetailsHeader(): Promise { - await this.elementMenuButton("View Node").click(); - const text = await this.nodedetailsPanelHeader.innerHTML(); - return text; + const isMenuVisible = await waitForElementToBeVisible(this.elementMenu); + if (!isMenuVisible) throw new Error("Element menu did not appear!"); + + await this.clickOnViewNode(); + + const isHeaderVisible = await waitForElementToBeVisible(this.nodedetailsPanelHeader); + if (!isHeaderVisible) throw new Error("Node details panel header did not appear!"); + + return this.nodedetailsPanelHeader.innerHTML(); } async clickOnNodeDetailsCloseBtn(): Promise{ @@ -466,19 +541,22 @@ export default class CodeGraph extends BasePage { async getMetricsPanelInfo(): Promise<{nodes: string, edges: string}> { const nodes = await this.canvasMetricsPanel("1").innerHTML(); - const edges = await this.canvasMetricsPanel("2").innerHTML(); + const edges = await this.canvasMetricsPanel("3").innerHTML(); return { nodes, edges } } async clickOnCopyToClipboardNodePanelDetails(): Promise { + const isButtonVisible = await waitForElementToBeVisible(this.copyToClipboardNodePanelDetails); + if (!isButtonVisible) throw new Error("'copy to clipboard button is not visible!"); await this.copyToClipboardNodePanelDetails.click(); - await delay(1000) return await this.page.evaluate(() => navigator.clipboard.readText()); } async clickOnCopyToClipboard(): Promise { - await this.elementMenuButton("Copy src to clipboard").click(); - await delay(1000) + const button = this.elementMenuButton("Copy src to clipboard"); + const isVisible = await waitForElementToBeVisible(button); + if (!isVisible) throw new Error("View Node button is not visible!"); + await button.click(); return await this.page.evaluate(() => navigator.clipboard.readText()); } @@ -487,15 +565,21 @@ export default class CodeGraph extends BasePage { } async getNodeDetailsPanelElements(): Promise { - await this.elementMenuButton("View Node").click(); - await delay(500) + const button = this.elementMenuButton("View Node"); + const isVisible = await waitForElementToBeVisible(button); + if (!isVisible) throw new Error("View Node button is not visible!"); + await button.click(); + + const isPanelVisible = await waitForElementToBeVisible(this.nodedetailsPanelElements.first()); + if (!isPanelVisible) throw new Error("Node details panel did not appear!"); + const elements = await this.nodedetailsPanelElements.all(); return Promise.all(elements.map(element => element.innerHTML())); } async getGraphDetails(): Promise { await this.canvasElementBeforeGraphSelection.waitFor({ state: 'detached' }); - await delay(2000) + await this.waitForCanvasAnimationToEnd(); await this.page.waitForFunction(() => !!window.graph); const graphData = await this.page.evaluate(() => { @@ -504,32 +588,43 @@ export default class CodeGraph extends BasePage { return graphData; } + - async transformNodeCoordinates(graphData: any): Promise { - const { canvasLeft, canvasTop, canvasWidth, canvasHeight, transform } = await this.canvasElement.evaluate((canvas: HTMLCanvasElement) => { - const rect = canvas.getBoundingClientRect(); - const ctx = canvas.getContext('2d'); - const transform = ctx?.getTransform()!; - return { - canvasLeft: rect.left, - canvasTop: rect.top, - canvasWidth: rect.width, - canvasHeight: rect.height, - transform, - }; - }); - - const screenCoordinates = graphData.elements.nodes.map((node: any) => { - const adjustedX = node.x * transform.a + transform.e; - const adjustedY = node.y * transform.d + transform.f; - const screenX = canvasLeft + adjustedX - 35; - const screenY = canvasTop + adjustedY - 190; + async getGraphNodes(): Promise { + await this.waitForCanvasAnimationToEnd(); - return {...node, screenX, screenY,}; + const graphData = await this.page.evaluate(() => { + return (window as any).graph; }); - return screenCoordinates; + let transformData: any = null; + for (let attempt = 0; attempt < 3; attempt++) { + await this.page.waitForTimeout(1000); + + transformData = await this.canvasElement.evaluate((canvas: HTMLCanvasElement) => { + const rect = canvas.getBoundingClientRect(); + const ctx = canvas.getContext('2d'); + return { + left: rect.left, + top: rect.top, + transform: ctx?.getTransform() || null, + }; + }); + + if (transformData.transform) break; + console.warn(`Attempt ${attempt + 1}: Transform data not available, retrying...`); + } + + if (!transformData?.transform) throw new Error("Canvas transform data not available!"); + + const { a, e, d, f } = transformData.transform; + return graphData.elements.nodes.map((node: any) => ({ + ...node, + screenX: transformData.left + node.x * a + e - 35, + screenY: transformData.top + node.y * d + f - 190, + })); } + async getCanvasScaling(): Promise<{ scaleX: number; scaleY: number }> { const { scaleX, scaleY } = await this.canvasElement.evaluate((canvas: HTMLCanvasElement) => { @@ -543,4 +638,81 @@ export default class CodeGraph extends BasePage { return { scaleX, scaleY }; } + async downloadImage(): Promise { + await this.page.waitForLoadState('networkidle'); + const [download] = await Promise.all([ + this.page.waitForEvent('download'), + this.downloadImageBtn.click(), + ]); + + return download; + } + + async rightClickAtCanvasCenter(): Promise { + const boundingBox = await this.canvasElement.boundingBox(); + if (!boundingBox) throw new Error('Canvas bounding box not found'); + const centerX = boundingBox.x + boundingBox.width / 2; + const centerY = boundingBox.y + boundingBox.height / 2; + await this.page.mouse.click(centerX, centerY, { button: 'right' }); + } + + async hoverAtCanvasCenter(): Promise { + const boundingBox = await this.canvasElement.boundingBox(); + if (!boundingBox) throw new Error('Canvas bounding box not found'); + const centerX = boundingBox.x + boundingBox.width / 2; + const centerY = boundingBox.y + boundingBox.height / 2; + await this.page.mouse.move(centerX, centerY); + } + + async isNodeToolTipVisible(node: string): Promise { + await this.page.waitForTimeout(500); + return await this.nodeToolTip(node).isVisible(); + } + + async waitForCanvasAnimationToEnd(timeout = 15000, checkInterval = 500): Promise { + const canvasHandle = await this.canvasElement.elementHandle(); + + if (!canvasHandle) { + throw new Error("Canvas element not found!"); + } + + await this.page.waitForFunction( + async ({ canvas, checkInterval, timeout }) => { + const ctx = canvas.getContext('2d'); + if (!ctx) return false; + + const width = canvas.width; + const height = canvas.height; + + let previousData = ctx.getImageData(0, 0, width, height).data; + const startTime = Date.now(); + + return new Promise((resolve) => { + const checkCanvas = () => { + if (Date.now() - startTime > timeout) { + resolve(true); + return; + } + + setTimeout(() => { + const currentData = ctx.getImageData(0, 0, width, height).data; + if (JSON.stringify(previousData) === JSON.stringify(currentData)) { + resolve(true); + } else { + previousData = currentData; + checkCanvas(); + } + }, checkInterval); + }; + checkCanvas(); + }); + }, + { + canvas: await canvasHandle.evaluateHandle((el) => el as HTMLCanvasElement), + checkInterval, + timeout + }, + { timeout } + ); + } } diff --git a/e2e/logic/utils.ts b/e2e/logic/utils.ts index 6ba3475d..2e3d41f1 100644 --- a/e2e/logic/utils.ts +++ b/e2e/logic/utils.ts @@ -2,19 +2,58 @@ import { Locator } from "@playwright/test"; export const delay = (ms: number): Promise => new Promise(resolve => setTimeout(resolve, ms)); -export const waitToBeEnabled = async (locator: Locator, timeout: number = 5000): Promise => { - const startTime = Date.now(); +export const waitToBeEnabled = async (locator: Locator, timeout: number = 5000): Promise => { + const elementHandle = await locator.elementHandle(); + if (!elementHandle) throw new Error("Element not found"); - while (Date.now() - startTime < timeout) { - if (await locator.isEnabled()) { - return true; + await locator.page().waitForFunction( + (el) => el && !(el as HTMLElement).hasAttribute("disabled"), + elementHandle, + { timeout } + ); +}; + +export const waitForStableText = async (locator: Locator, timeout: number = 5000): Promise => { + const elementHandle = await locator.elementHandle(); + if (!elementHandle) throw new Error("Element not found"); + + let previousText = ""; + let stableText = ""; + const pollingInterval = 300; + const maxChecks = timeout / pollingInterval; + + for (let i = 0; i < maxChecks; i++) { + stableText = await locator.textContent() ?? ""; + if (stableText === previousText && stableText.trim().length > 0) { + return stableText; } - await new Promise(resolve => setTimeout(resolve, 100)); + previousText = stableText; + await locator.page().waitForTimeout(pollingInterval); } + return stableText; +}; + +export const waitForElementToBeVisible = async (locator: Locator, time = 500, retry = 10): Promise => { + for (let i = 0; i < retry; i++) { + try { + if (await locator.isVisible()) { + return true; + } + } catch (error) { + console.error(`Error checking element visibility: ${error}`); + } + await new Promise(resolve => setTimeout(resolve, time)); // Delay + } return false; }; + export function findNodeByName(nodes: { name: string }[], nodeName: string): any { return nodes.find((node) => node.name === nodeName); - } \ No newline at end of file +} + +export function findFirstNodeWithSrc(nodes: { src?: string }[]): any { + return nodes.find((node) => node.src !== undefined); +} + diff --git a/e2e/tests/canvas.spec.ts b/e2e/tests/canvas.spec.ts index 3701ac86..373ad9a9 100644 --- a/e2e/tests/canvas.spec.ts +++ b/e2e/tests/canvas.spec.ts @@ -2,10 +2,11 @@ import { test, expect } from "@playwright/test"; import BrowserWrapper from "../infra/ui/browserWrapper"; import CodeGraph from "../logic/POM/codeGraph"; import urls from "../config/urls.json"; -import { GRAPH_ID, PROJECT_NAME } from "../config/constants"; -import { findNodeByName } from "../logic/utils"; -import { nodesPath, categories, nodes } from "../config/testData"; +import { GRAPHRAG_SDK } from "../config/constants"; +import { delay, findNodeByName } from "../logic/utils"; +import { nodesPath, categories, nodes, graphs } from "../config/testData"; import { ApiCalls } from "../logic/api/apiCalls"; +import fs from 'fs'; test.describe("Canvas tests", () => { let browser: BrowserWrapper; @@ -20,7 +21,7 @@ test.describe("Canvas tests", () => { test(`Verify zoom in functionality on canvas`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); const initialGraph = await codeGraph.getCanvasScaling(); await codeGraph.clickZoomIn(); await codeGraph.clickZoomIn(); @@ -31,7 +32,7 @@ test.describe("Canvas tests", () => { test(`Verify zoom out functionality on canvas`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); const initialGraph = await codeGraph.getCanvasScaling(); await codeGraph.clickZoomOut(); await codeGraph.clickZoomOut(); @@ -42,25 +43,23 @@ test.describe("Canvas tests", () => { test(`Verify center graph button centers nodes in canvas`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.clickCenter(); const initialGraph = await codeGraph.getCanvasScaling(); - await codeGraph.clickZoomOut(); await codeGraph.clickZoomOut(); await codeGraph.clickCenter(); const updatedGraph = await codeGraph.getCanvasScaling(); expect(Math.abs(initialGraph.scaleX - updatedGraph.scaleX)).toBeLessThanOrEqual(0.1); expect(Math.abs(initialGraph.scaleY - updatedGraph.scaleY)).toBeLessThanOrEqual(0.1); - }) nodes.slice(0,2).forEach((node) => { test(`Validate node hide functionality via element menu in canvas for ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const initialGraph = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(initialGraph); - const targetNode = findNodeByName(convertCoordinates, node.nodeName); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const initialGraph = await codeGraph.getGraphNodes(); + const targetNode = findNodeByName(initialGraph, node.nodeName); await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY); await codeGraph.clickOnRemoveNodeViaElementMenu(); const updatedGraph = await codeGraph.getGraphDetails(); @@ -72,10 +71,9 @@ test.describe("Canvas tests", () => { nodes.slice(0,2).forEach((node) => { test(`Validate unhide node functionality after hiding a node in canvas for ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const initialGraph = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(initialGraph); - const targetNode = findNodeByName(convertCoordinates, node.nodeName); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const initialGraph = await codeGraph.getGraphNodes(); + const targetNode = findNodeByName(initialGraph, node.nodeName); await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY); await codeGraph.clickOnRemoveNodeViaElementMenu(); await codeGraph.clickOnUnhideNodesBtn(); @@ -89,7 +87,7 @@ test.describe("Canvas tests", () => { const checkboxIndex = index + 1; test(`Verify that unchecking the ${category} checkbox hides ${category} nodes on the canvas`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.selectCodeGraphCheckbox(checkboxIndex.toString()); const result = await codeGraph.getGraphDetails(); const findItem = result.categories.find((item: { name: string; }) => item.name === category) @@ -100,8 +98,9 @@ test.describe("Canvas tests", () => { nodesPath.forEach((path) => { test(`Verify "Clear graph" button resets canvas view for path ${path.firstNode} and ${path.secondNode}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - await codeGraph.clickOnshowPathBtn(); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.clickOnShowPathBtn("Show the path"); await codeGraph.insertInputForShowPath("1", path.firstNode); await codeGraph.insertInputForShowPath("2", path.secondNode); const initialGraph = await codeGraph.getGraphDetails(); @@ -118,37 +117,35 @@ test.describe("Canvas tests", () => { }); }) - for (let index = 0; index < 2; index++) { - const checkboxIndex = index + 1; - test(`Verify selecting different graphs displays nodes in canvas - Iteration ${index + 1}`, async () => { + graphs.forEach(({graphName}) => { + test(`Verify selecting different graphs displays nodes in canvas - grpah: ${graphName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(checkboxIndex); + await codeGraph.selectGraph(graphName); const result = await codeGraph.getGraphDetails(); expect(result.elements.nodes.length).toBeGreaterThan(1); expect(result.elements.links.length).toBeGreaterThan(1); }); - } - + }) + for (let index = 0; index < 3; index++) { const nodeIndex: number = index + 1; test(`Validate canvas node dragging for node: ${index}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const initialGraph = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(initialGraph); - await codeGraph.changeNodePosition(convertCoordinates[nodeIndex].screenX, convertCoordinates[nodeIndex].screenY); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const initialGraph = await codeGraph.getGraphNodes(); + await codeGraph.changeNodePosition(initialGraph[nodeIndex].screenX, initialGraph[nodeIndex].screenY); const updateGraph = await codeGraph.getGraphDetails(); - expect(updateGraph.elements.nodes[nodeIndex].x).not.toBe(initialGraph.elements.nodes[nodeIndex].x); - expect(updateGraph.elements.nodes[nodeIndex].y).not.toBe(initialGraph.elements.nodes[nodeIndex].y); + expect(updateGraph.elements.nodes[nodeIndex].x).not.toBe(initialGraph[nodeIndex].x); + expect(updateGraph.elements.nodes[nodeIndex].y).not.toBe(initialGraph[nodeIndex].y); }); } test(`Validate node and edge counts in canvas match API data`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); const { nodes, edges } = await codeGraph.getMetricsPanelInfo(); const api = new ApiCalls(); - const response = await api.projectInfo(PROJECT_NAME); + const response = await api.projectInfo(GRAPHRAG_SDK); expect(response.result.info.node_count).toEqual(parseInt(nodes)); expect(response.result.info.edge_count).toEqual(parseInt(edges)); }); @@ -156,37 +153,21 @@ test.describe("Canvas tests", () => { test(`Validate displayed nodes match API response after selecting a graph via UI`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); const graphData = await codeGraph.getGraphDetails(); const api = new ApiCalls(); - const response = await api.getProject(PROJECT_NAME); + const response = await api.getProject(GRAPHRAG_SDK); const isMatching = graphData.elements.nodes.slice(0, 2).every( (node: any, index: number) => node.name === response.result.entities.nodes[index].properties.name ); expect(isMatching).toBe(true) }); - nodes.slice(0,2).forEach((node) => { - test(`Validate copy to clipboard functionality for node and verify with api for ${node.nodeName}`, async () => { - const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const targetNode = findNodeByName(convertCoordinates, node.nodeName); - await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY); - const result = await codeGraph.clickOnCopyToClipboard(); - const api = new ApiCalls(); - const response = await api.getProject(PROJECT_NAME); - const matchingNode = response.result.entities.nodes.find(nod => nod.properties?.name === node.nodeName); - expect(matchingNode?.properties.src).toBe(result); - }); - }) - nodesPath.forEach(({firstNode, secondNode}) => { test(`Verify successful node path connection in canvas between ${firstNode} and ${secondNode} via UI`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - await codeGraph.clickOnshowPathBtn(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.clickOnShowPathBtn("Show the path"); await codeGraph.insertInputForShowPath("1", firstNode); await codeGraph.insertInputForShowPath("2", secondNode); const result = await codeGraph.getGraphDetails(); @@ -200,8 +181,8 @@ test.describe("Canvas tests", () => { nodesPath.forEach((path) => { test(`Validate node path connection in canvas ui and confirm via api for path ${path.firstNode} and ${path.secondNode}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - await codeGraph.clickOnshowPathBtn(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.clickOnShowPathBtn("Show the path"); await codeGraph.insertInputForShowPath("1", path.firstNode); await codeGraph.insertInputForShowPath("2", path.secondNode); const result = await codeGraph.getGraphDetails(); @@ -209,10 +190,33 @@ test.describe("Canvas tests", () => { const secondNodeRes = findNodeByName(result.elements.nodes, path.secondNode); const api = new ApiCalls(); - const response = await api.showPath(PROJECT_NAME ,firstNodeRes.id, secondNodeRes.id); + const response = await api.showPath(GRAPHRAG_SDK ,firstNodeRes.id, secondNodeRes.id); const callsRelationObject = response.result.paths[0].find(item => item.relation === "CALLS") expect(callsRelationObject?.src_node).toBe(firstNodeRes.id); expect(callsRelationObject?.dest_node).toBe(secondNodeRes.id); }); }) + + test(`Verify file download is triggered and saved after clicking download`, async () => { + const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const download = await codeGraph.downloadImage(); + const downloadPath = await download.path(); + expect(fs.existsSync(downloadPath)).toBe(true); + }) + + nodes.forEach((node) => { + test(`Verify tooltip appears when hovering over node: ${node.nodeName}`, async () => { + const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.getGraphDetails(); + await codeGraph.fillSearchBar(node.nodeName); + await codeGraph.selectSearchBarOptionBtn("1"); + await codeGraph.waitForCanvasAnimationToEnd(); + await codeGraph.hoverAtCanvasCenter(); + expect(await codeGraph.isNodeToolTipVisible(node.nodeName)).toBe(true); + }) + }) + }); diff --git a/e2e/tests/chat.spec.ts b/e2e/tests/chat.spec.ts index 1f245f11..5ab104e4 100644 --- a/e2e/tests/chat.spec.ts +++ b/e2e/tests/chat.spec.ts @@ -3,7 +3,7 @@ import BrowserWrapper from "../infra/ui/browserWrapper"; import urls from "../config/urls.json"; import { ApiCalls } from "../logic/api/apiCalls"; import CodeGraph from "../logic/POM/codeGraph"; -import { CHAT_OPTTIONS_COUNT, GRAPH_ID, Node_Question, PROJECT_NAME } from "../config/constants"; +import { CHAT_OPTTIONS_COUNT, GRAPHRAG_SDK, Node_Question, } from "../config/constants"; import { delay } from "../logic/utils"; import { nodesPath } from "../config/testData"; @@ -20,7 +20,7 @@ test.describe("Chat tests", () => { test(`Validate clicking the lightbulb button displays the correct options at the end of the chat`, async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); + await chat.selectGraph(GRAPHRAG_SDK); await chat.clickOnLightBulbBtn(); const count = await chat.getLastChatElementButtonCount(); expect(count).toBe(CHAT_OPTTIONS_COUNT); @@ -28,10 +28,10 @@ test.describe("Chat tests", () => { test(`Validate that multiple consecutive questions receive individual answers`, async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); + await chat.selectGraph(GRAPHRAG_SDK); const isLoadingArray: boolean[] = []; - for (let i = 0; i < 5; i++) { + for (let i = 0; i < 3; i++) { await chat.sendMessage(Node_Question); const isLoading: boolean = await chat.getpreviousQuestionLoadingImage(); isLoadingArray.push(isLoading); @@ -39,29 +39,59 @@ test.describe("Chat tests", () => { const prevIsLoading = isLoadingArray[i - 1]; expect(prevIsLoading).toBe(false); } + await delay(3000); } }); test("Verify auto-scroll and manual scroll in chat", async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - for (let i = 0; i < 5; i++) { + await chat.selectGraph(GRAPHRAG_SDK); + for (let i = 0; i < 3; i++) { await chat.sendMessage(Node_Question); + await delay(3000); } - await delay(500); + await delay(500); // delay for scroll await chat.scrollToTop(); const { scrollTop } = await chat.getScrollMetrics(); expect(scrollTop).toBeLessThanOrEqual(1); - await chat.sendMessage("Latest Message"); - await delay(500); + await chat.sendMessage(Node_Question); + await delay(500); // delay for scroll expect(await chat.isAtBottom()).toBe(true); }); + test(`Validate consistent UI responses for repeated questions in chat`, async () => { + const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); + await chat.selectGraph(GRAPHRAG_SDK); + const responses: string[] = []; + for (let i = 0; i < 3; i++) { + await chat.sendMessage(Node_Question); + const result = await chat.getTextInLastChatElement(); + const number = result.match(/\d+/g)?.[0]!; + responses.push(number); + await delay(3000); //delay before asking next question + } + const identicalResponses = responses.every((value) => value === responses[0]); + expect(identicalResponses).toBe(true); + }); + + test(`Validate UI response matches API response for a given question in chat`, async () => { + const api = new ApiCalls(); + const apiResponse = await api.askQuestion(GRAPHRAG_SDK, Node_Question); + const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); + await chat.selectGraph(GRAPHRAG_SDK); + await delay(3000); + await chat.sendMessage(Node_Question); + const uiResponse = await chat.getTextInLastChatElement(); + const number = uiResponse.match(/\d+/g)?.[0]!; + + expect(number).toEqual(apiResponse.result.response.match(/\d+/g)?.[0]); + }); + nodesPath.forEach((path) => { test(`Verify successful node path connection between two nodes in chat for ${path.firstNode} and ${path.secondNode}`, async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - await chat.clickOnshowPathBtn(); + await chat.selectGraph(GRAPHRAG_SDK); + await chat.clickOnShowPathBtn("Show the path"); await chat.insertInputForShowPath("1", path.firstNode); await chat.insertInputForShowPath("2", path.secondNode); expect(await chat.isNodeVisibleInLastChatPath(path.firstNode)).toBe(true); @@ -72,8 +102,8 @@ test.describe("Chat tests", () => { nodesPath.forEach((path) => { test(`Verify unsuccessful node path connection between two nodes in chat for ${path.firstNode} and ${path.secondNode}`, async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - await chat.clickOnshowPathBtn(); + await chat.selectGraph(GRAPHRAG_SDK); + await chat.clickOnShowPathBtn("Show the path"); await chat.insertInputForShowPath("1", path.secondNode); await chat.insertInputForShowPath("2", path.firstNode); await delay(500); @@ -83,48 +113,23 @@ test.describe("Chat tests", () => { test("Validate error notification and its closure when sending an empty question in chat", async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - await chat.clickAskquestionBtn(); + await chat.selectGraph(GRAPHRAG_SDK); + await chat.clickAskQuestionBtn(); expect(await chat.isNotificationError()).toBe(true); await chat.clickOnNotificationErrorCloseBtn(); expect(await chat.isNotificationError()).toBe(false); }); - for (let index = 0; index < 5; index++) { + for (let index = 1; index < 6; index++) { const questionNumber = index + 1; test(`Validate displaying question ${index} in chat after selection from options menu`, async () => { const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - await chat.clickOnQuestionOptionsMenu(); - const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()); + await chat.selectGraph(GRAPHRAG_SDK); + await chat.clickOnLightBulbBtn(); + const selectedQuestion = await chat.selectAndGetQuestionInOptionsMenu(questionNumber.toString()); expect(selectedQuestion).toEqual(await chat.getLastQuestionInChat()) + const result = await chat.getTextInLastChatElement(); + expect(result).toBeDefined(); }); } - - test(`Validate consistent UI responses for repeated questions in chat`, async () => { - const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - const responses: string[] = []; - for (let i = 0; i < 3; i++) { - await chat.sendMessage(Node_Question); - const result = await chat.getTextInLastChatElement(); - const number = result.match(/\d+/g)?.[0]!; - responses.push(number); - } - const identicalResponses = responses.every((value) => value === responses[0]); - expect(identicalResponses).toBe(true); - }); - - test(`Validate UI response matches API response for a given question in chat`, async () => { - const chat = await browser.createNewPage(CodeGraph, urls.baseUrl); - await chat.selectGraph(GRAPH_ID); - - await chat.sendMessage(Node_Question); - const uiResponse = await chat.getTextInLastChatElement(); - const number = uiResponse.match(/\d+/g)?.[0]!; - - const api = new ApiCalls(); - const apiResponse = await api.askQuestion(PROJECT_NAME, Node_Question); - expect(number).toEqual(apiResponse.result.response.match(/\d+/g)?.[0]); - }); }); diff --git a/e2e/tests/navBar.spec.ts b/e2e/tests/navBar.spec.ts index 9f294a75..313f28be 100644 --- a/e2e/tests/navBar.spec.ts +++ b/e2e/tests/navBar.spec.ts @@ -21,7 +21,7 @@ test.describe(' Navbar tests', () => { }) const navitems: { navItem: string; expectedRes: string }[] = [ - { navItem: "Home", expectedRes: urls.falkorDBUrl }, + { navItem: "Main Website", expectedRes: urls.falkorDBUrl }, { navItem: "Github", expectedRes: urls.falkorDbGithubUrl } ]; @@ -41,9 +41,9 @@ test.describe(' Navbar tests', () => { test("Validate Tip popup visibility and closure functionality", async () => { const navBar = await browser.createNewPage(CodeGraph, urls.baseUrl); - await navBar.clickonTipBtn(); + await navBar.clickOnTipBtn(); expect(await navBar.isTipMenuVisible()).toBe(true); - await navBar.clickonTipMenuCloseBtn(); + await navBar.clickOnTipMenuCloseBtn(); expect(await navBar.isTipMenuVisible()).toBe(false); }); }); diff --git a/e2e/tests/nodeDetailsPanel.spec.ts b/e2e/tests/nodeDetailsPanel.spec.ts index aa59a6fe..16c73224 100644 --- a/e2e/tests/nodeDetailsPanel.spec.ts +++ b/e2e/tests/nodeDetailsPanel.spec.ts @@ -2,10 +2,10 @@ import { test, expect } from "@playwright/test"; import BrowserWrapper from "../infra/ui/browserWrapper"; import CodeGraph from "../logic/POM/codeGraph"; import urls from "../config/urls.json"; -import { GRAPH_ID, PROJECT_NAME } from "../config/constants"; +import { GRAPHRAG_SDK, FLASK_GRAPH } from "../config/constants"; import { nodes } from "../config/testData"; import { ApiCalls } from "../logic/api/apiCalls"; -import { findNodeByName } from "../logic/utils"; +import { findNodeByName, findFirstNodeWithSrc } from "../logic/utils"; test.describe("Node details panel tests", () => { let browser: BrowserWrapper; @@ -18,84 +18,88 @@ test.describe("Node details panel tests", () => { await browser.closeBrowser(); }); - nodes.slice(0,2).forEach((node) => { + nodes.slice(0, 2).forEach((node) => { test(`Validate node details panel displayed on node click for ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const targetNode = findNodeByName(convertCoordinates, node.nodeName); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const graphData = await codeGraph.getGraphNodes(); + + const targetNode = findNodeByName(graphData, node.nodeName); + expect(targetNode).toBeDefined(); await codeGraph.nodeClick(targetNode.screenX, targetNode.screenY); await codeGraph.clickOnViewNode(); - expect(await codeGraph.isNodeDetailsPanel()).toBe(true) - }) - }) + expect(await codeGraph.isNodeDetailsPanel()).toBe(true); + }); + }); - nodes.slice(0,2).forEach((node) => { + nodes.slice(0, 2).forEach((node) => { test(`Validate node details panel is not displayed after close interaction for ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const node1 = findNodeByName(convertCoordinates, node.nodeName); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const graphData = await codeGraph.getGraphNodes(); + const node1 = findNodeByName(graphData, node.nodeName); await codeGraph.nodeClick(node1.screenX, node1.screenY); await codeGraph.clickOnViewNode(); await codeGraph.clickOnNodeDetailsCloseBtn(); - expect(await codeGraph.isNodeDetailsPanel()).toBe(false) - }) - }) + expect(await codeGraph.isNodeDetailsPanel()).toBe(false); + }); + }); nodes.forEach((node) => { test(`Validate node details panel header displays correct node name: ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const node1 = findNodeByName(convertCoordinates, node.nodeName); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const graphData = await codeGraph.getGraphNodes(); + const node1 = findNodeByName(graphData, node.nodeName); await codeGraph.nodeClick(node1.screenX, node1.screenY); - expect(await codeGraph.getNodeDetailsHeader()).toContain(node.nodeName.toUpperCase()) - }) - }) - - nodes.slice(0,2).forEach((node) => { - test(`Validate copy functionality for node inside node details panel and verify with api for ${node.nodeName}`, async () => { - const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const nodeData = findNodeByName(convertCoordinates, node.nodeName); - await codeGraph.nodeClick(nodeData.screenX, nodeData.screenY); - await codeGraph.clickOnViewNode(); - const result = await codeGraph.clickOnCopyToClipboardNodePanelDetails(); - const api = new ApiCalls(); - const response = await api.getProject(PROJECT_NAME); - const foundNode = response.result.entities.nodes.find(nod => nod.properties?.name === node.nodeName); - expect(foundNode?.properties.src).toBe(result); + expect(await codeGraph.getNodeDetailsHeader()).toContain( + node.nodeName.toUpperCase() + ); }); - }) + }); - nodes.slice(0,2).forEach((node) => { + test(`Validate copy functionality for node inside node details panel and verify with api`, async () => { + const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(FLASK_GRAPH); + const graphData = await codeGraph.getGraphNodes(); + const nodeData = findFirstNodeWithSrc(graphData); + await codeGraph.nodeClick(nodeData.screenX, nodeData.screenY); + await codeGraph.clickOnViewNode(); + const result = await codeGraph.clickOnCopyToClipboardNodePanelDetails(); + const api = new ApiCalls(); + const response = await api.getProject(FLASK_GRAPH); + const foundNode = response.result.entities.nodes.find((nod) => nod.properties?.name === nodeData.name); + expect(foundNode?.properties.src).toBe(result); + }); + + nodes.slice(0, 2).forEach((node) => { test(`Validate view node panel keys via api for ${node.nodeName}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); - const graphData = await codeGraph.getGraphDetails(); - const convertCoordinates = await codeGraph.transformNodeCoordinates(graphData); - const node1 = findNodeByName(convertCoordinates, node.nodeName); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + const graphData = await codeGraph.getGraphNodes(); + const node1 = findNodeByName(graphData, node.nodeName); const api = new ApiCalls(); - const response = await api.getProject(PROJECT_NAME); + const response = await api.getProject(GRAPHRAG_SDK); const data: any = response.result.entities.nodes; const findNode = data.find((nod: any) => nod.properties.name === node.nodeName); - await codeGraph.nodeClick(node1.screenX, node1.screenY); let elements = await codeGraph.getNodeDetailsPanelElements(); - elements.splice(2,1) - const apiFields = [...Object.keys(findNode), ...Object.keys(findNode.properties || {})]; - + elements.splice(2, 1); + const apiFields = [ + ...Object.keys(findNode), + ...Object.keys(findNode.properties || {}), + ]; + const isValid = elements.every((field) => { - const cleanedField = field.replace(':', '').trim(); - return apiFields.includes(cleanedField); + const cleanedField = field.replace(":", "").trim(); + return apiFields.includes(cleanedField); }); - expect(isValid).toBe(true) + expect(isValid).toBe(true); }); - }) -}); \ No newline at end of file + }); +}); diff --git a/e2e/tests/searchBar.spec.ts b/e2e/tests/searchBar.spec.ts index a7427d3d..f4ee3307 100644 --- a/e2e/tests/searchBar.spec.ts +++ b/e2e/tests/searchBar.spec.ts @@ -2,9 +2,9 @@ import { test, expect } from "@playwright/test"; import BrowserWrapper from "../infra/ui/browserWrapper"; import CodeGraph from "../logic/POM/codeGraph"; import urls from "../config/urls.json"; -import { GRAPH_ID, PROJECT_NAME } from "../config/constants"; -import { delay } from "../logic/utils"; -import { searchData, specialCharacters } from "../config/testData"; +import { GRAPHRAG_SDK } from "../config/constants"; +import { delay, findFirstNodeWithSrc, findNodeByName } from "../logic/utils"; +import { nodes, searchData, specialCharacters } from "../config/testData"; import { ApiCalls } from "../logic/api/apiCalls"; test.describe("search bar tests", () => { @@ -21,7 +21,7 @@ test.describe("search bar tests", () => { searchData.slice(0, 2).forEach(({ searchInput }) => { test(`Verify search bar auto-complete behavior for input: ${searchInput} via UI`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.fillSearchBar(searchInput); await delay(1000); const textList = await codeGraph.getSearchBarElementsText(); @@ -34,7 +34,7 @@ test.describe("search bar tests", () => { searchData.slice(2, 4).forEach(({ searchInput, completedSearchInput }) => { test(`Validate search bar updates with selected element: ${searchInput}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.fillSearchBar(searchInput); await codeGraph.selectSearchBarOptionBtn("1"); expect(await codeGraph.getSearchBarInputValue()).toBe( @@ -46,7 +46,7 @@ test.describe("search bar tests", () => { searchData.slice(0, 2).forEach(({ searchInput }) => { test(`Verify auto-scroll scroll in search bar list for: ${searchInput}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.fillSearchBar(searchInput); await codeGraph.scrollToBottomInSearchBarList(); expect(await codeGraph.isScrolledToBottomInSearchBarList()).toBe(true); @@ -56,7 +56,7 @@ test.describe("search bar tests", () => { specialCharacters.forEach(({ character, expectedRes }) => { test(`Verify entering special characters behavior in search bar for: ${character}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.fillSearchBar(character); await delay(1000); expect((await codeGraph.getSearchBarInputValue()).includes(character)).toBe(expectedRes); @@ -66,12 +66,26 @@ test.describe("search bar tests", () => { searchData.slice(0, 2).forEach(({ searchInput}) => { test(`search bar auto complete via ui and validating via api for: ${searchInput}`, async () => { const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); - await codeGraph.selectGraph(GRAPH_ID); + await codeGraph.selectGraph(GRAPHRAG_SDK); await codeGraph.fillSearchBar(searchInput); const count = await codeGraph.getSearchAutoCompleteCount(); const api = new ApiCalls(); - const response = await api.searchAutoComplete(PROJECT_NAME, searchInput); + const response = await api.searchAutoComplete(GRAPHRAG_SDK, searchInput); expect(count).toBe(response.result.completions.length); }); }) + + nodes.forEach(({nodeName})=> { + test(`Verify canvas focuses on node ${nodeName} after search`, async () => {//here + const codeGraph = await browser.createNewPage(CodeGraph, urls.baseUrl); + await browser.setPageToFullScreen(); + await codeGraph.selectGraph(GRAPHRAG_SDK); + await codeGraph.getGraphDetails(); + await codeGraph.fillSearchBar(nodeName); + await codeGraph.selectSearchBarOptionBtn("1"); + await codeGraph.waitForCanvasAnimationToEnd(); + await codeGraph.rightClickAtCanvasCenter(); + expect(await codeGraph.getNodeDetailsHeader()).toContain(nodeName.toUpperCase()); + }); + }) }) \ No newline at end of file diff --git a/lib/utils.ts b/lib/utils.ts index d084ccad..2946411b 100644 --- a/lib/utils.ts +++ b/lib/utils.ts @@ -1,6 +1,75 @@ +import { Link, Node } from "@/app/components/model" import { type ClassValue, clsx } from "clsx" +import { MutableRefObject } from "react" import { twMerge } from "tailwind-merge" +import { ForceGraphMethods, NodeObject } from "react-force-graph-2d" + +export type PathData = { + nodes: any[] + links: any[] +} + +export type PathNode = { + id?: number + name?: string +} + +export type Path = { + start?: PathNode, + end?: PathNode +} + +export enum MessageTypes { + Query, + Response, + Path, + PathResponse, + Pending, + Text, +} + +export interface Message { + type: MessageTypes; + text?: string; + paths?: { nodes: any[], links: any[] }[]; + graphName?: string; +} + +export type GraphRef = MutableRefObject | undefined> export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) } + +export function handleZoomToFit(chartRef: GraphRef, paddingMultiplier = 1, filter?: (node: NodeObject) => boolean) { + const chart = chartRef.current + if (chart) { + // Find the currently visible canvas by checking display property + const canvases = document.querySelectorAll('.force-graph-container canvas') as NodeListOf; + const container = Array.from(canvases).find(canvas => { + const container = canvas.parentElement; + + if (!container) return false; + + // Check if element is actually in viewport + const rect = container.getBoundingClientRect(); + const isInViewport = rect.width > 0 && + rect.height > 0 && + rect.top >= 0 && + rect.left >= 0 && + rect.bottom <= window.innerHeight && + rect.right <= window.innerWidth; + + return isInViewport; + })?.parentElement; + + if (!container) return; + + // Calculate padding as 10% of the smallest canvas dimension + const minDimension = Math.min(container.clientWidth, container.clientHeight); + + const padding = minDimension * 0.1 * paddingMultiplier; + + chart.zoomToFit(1000, padding, filter); + } +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 83462b39..a6f6d3ef 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,42 +8,48 @@ "name": "code-graph", "version": "0.2.0", "dependencies": { - "@radix-ui/react-checkbox": "^1.1.2", - "@radix-ui/react-dialog": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.2", - "@radix-ui/react-hover-card": "^1.1.2", - "@radix-ui/react-progress": "^1.1.0", - "@radix-ui/react-select": "^2.1.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-toast": "^1.2.2", - "@radix-ui/react-tooltip": "^1.1.4", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-tooltip": "^1.1.8", "@types/react-gtm-module": "^2.0.4", - "autoprefixer": "^10.4.20", + "autoprefixer": "^10.4.21", + "canvas2svg": "^1.0.16", "class-variance-authority": "^0.7.1", - "clsx": "^2.1.0", - "lucide-react": "^0.441.0", - "next": "^15.1.2", + "clsx": "^2.1.1", + "d3": "^7.9.0", + "embla-carousel-react": "^8.5.2", + "html2canvas": "^1.4.1", + "lucide-react": "^0.486.0", + "next": "^15.1.7", "playwright": "^1.49.1", - "react": "^18", - "react-dom": "^18", - "react-force-graph-2d": "^1.25.8", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-force-graph-2d": "^1.27.0", "react-gtm-module": "^2.0.11", - "react-resizable-panels": "^2.0.20", + "react-json-tree": "^0.19.0", + "react-resizable-panels": "^2.1.7", "react-syntax-highlighter": "^15.6.1", "react-type-animation": "^3.2.0", - "tailwind-merge": "^2.5.5", - "tailwindcss-animate": "^1.0.7" + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2" }, "devDependencies": { - "@playwright/test": "^1.49.1", - "@types/node": "^22.10.2", - "@types/react": "^18", - "@types/react-dom": "^18", + "@playwright/test": "^1.50.1", + "@types/node": "^22.15.26", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@types/react-syntax-highlighter": "^15.5.13", - "eslint": "^9", - "eslint-config-next": "^15.1.2", - "tailwindcss": "^3.4.3", - "typescript": "^5" + "eslint": "^9.23.0", + "eslint-config-next": "^15.2.4", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.3" } }, "node_modules/@alloc/quick-lru": { @@ -69,15 +75,6 @@ "node": ">=6.9.0" } }, - "node_modules/@emnapi/runtime": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.3.1.tgz", - "integrity": "sha512-kEBmG8KyqtxJZv+ygbEim+KCGtIq1fC22Ms3S4ziXmYKm8uyoLX0MHONVKwp+9opg390VaKRNt4a7A9NwmpNhw==", - "optional": true, - "dependencies": { - "tslib": "^2.4.0" - } - }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", @@ -105,13 +102,12 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.0.tgz", - "integrity": "sha512-zdHg2FPIFNKPdcHWtiNT+jEFCHYVplAXRDlQDyqy0zGx/q2parwh7brGJSiTxRk/TSMkbM//zt/f5CHgyTyaSQ==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.19.2.tgz", + "integrity": "sha512-GNKqxfHG2ySmJOBSHg7LxeUx4xpuCoFjacmlCoYWEbaPXLwvfIjixRI12xCQZeULksQb23uiA8F40w5TojpV7w==", "dev": true, - "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.6", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -119,20 +115,32 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@eslint/core": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.9.0.tgz", - "integrity": "sha512-7ATR9F0e4W85D/0w7cU0SNj7qkAexMG+bAHEZOjo9akvGuhHE2m7umzWzfnpa0XAg5Kxc1BWmtPMV67jJ+9VUg==", + "node_modules/@eslint/config-helpers": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.0.tgz", + "integrity": "sha512-yJLLmLexii32mGrhW29qvU3QBVTu0GUmEf/J4XsBtVhp4JkIUFN/BjWqTF63yRvGApIDpZm5fa97LtYtINmfeQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/core": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.12.0.tgz", + "integrity": "sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg==", + "dev": true, + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.2.0.tgz", - "integrity": "sha512-grOjVNN8P3hjJn/eIETF1wwd12DdnwFDoyceUJLYYdkpbwq3nLi+4fqrTAONx7XDALqlL220wC/RHSC/QTI/0w==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", "dev": true, "license": "MIT", "dependencies": { @@ -154,9 +162,9 @@ } }, "node_modules/@eslint/js": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.15.0.tgz", - "integrity": "sha512-tMTqrY+EzbXmKJR5ToI8lxu7jaN5EdmrBFJpQk5JmSlyLsx6o4t27r883K5xsLuCYCpfKBCGswMSWXsM+jB7lg==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.23.0.tgz", + "integrity": "sha512-35MJ8vCPU0ZMxo7zfev2pypqTwWTofFZO6m4KAtdoFhRpLJUpHTZZ+KB3C7Hb1d7bULYwO4lJXGCi5Se+8OMbw==", "dev": true, "license": "MIT", "engines": { @@ -164,22 +172,21 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", "dev": true, - "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.3.tgz", - "integrity": "sha512-2b/g5hRmpbb1o4GnTZax9N9m0FXzz9OV42ZzI4rDDMDuHUqigAiQCEWChBWCY4ztAGVRjoWT19v0yMmc5/L5kA==", + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.7.tgz", + "integrity": "sha512-JubJ5B2pJ4k4yGxaNLdbjrnk9d/iDz6/q8wOilpIowd6PJPgaxCuHBnBszq7Ce2TyMrywm5r4PnKm6V3iiZF+g==", "dev": true, - "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.12.0", "levn": "^0.4.1" }, "engines": { @@ -187,29 +194,26 @@ } }, "node_modules/@floating-ui/core": { - "version": "1.6.7", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.7.tgz", - "integrity": "sha512-yDzVT/Lm101nQ5TCVeK65LtdN7Tj4Qpr9RTXJ2vPFLqtLxwOrpoxAHAJI8J3yYWUc40J0BDBheaitK5SJmno2g==", - "license": "MIT", + "version": "1.6.9", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.9.tgz", + "integrity": "sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==", "dependencies": { - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/dom": { - "version": "1.6.10", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.10.tgz", - "integrity": "sha512-fskgCFv8J8OamCmyun8MfjB1Olfn+uZKjOKZ0vhYF3gRmEUXcGOjxWL8bBr7i4kIuPZ2KD2S3EUIOxnjC8kl2A==", - "license": "MIT", + "version": "1.6.13", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.13.tgz", + "integrity": "sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==", "dependencies": { "@floating-ui/core": "^1.6.0", - "@floating-ui/utils": "^0.2.7" + "@floating-ui/utils": "^0.2.9" } }, "node_modules/@floating-ui/react-dom": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", - "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", - "license": "MIT", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.2.tgz", + "integrity": "sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==", "dependencies": { "@floating-ui/dom": "^1.0.0" }, @@ -219,10 +223,9 @@ } }, "node_modules/@floating-ui/utils": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.7.tgz", - "integrity": "sha512-X8R8Oj771YRl/w+c1HqAC1szL8zWQRwFvgDwT129k9ACdBoud/+/rX9V0qiMl6LWUdP9voC2nDVZYPMQQsb6eA==", - "license": "MIT" + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz", + "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==" }, "node_modules/@humanfs/core": { "version": "0.19.1", @@ -277,11 +280,10 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.1.tgz", - "integrity": "sha512-c7hNEllBlenFTHBky65mhq8WD2kbN9Q6gk0bTk8lSBvc554jpXSkST1iePudpt7+A/AQvuHs9EMqjHDXMY1lrA==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.2.tgz", + "integrity": "sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ==", "dev": true, - "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -290,123 +292,6 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@img/sharp-darwin-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", - "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-darwin-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", - "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.0.4" - } - }, - "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", - "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", - "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", - "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", - "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", - "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", @@ -422,21 +307,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", - "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", @@ -452,69 +322,6 @@ "url": "https://opencollective.com/libvips" } }, - "node_modules/@img/sharp-linux-arm": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", - "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", - "cpu": [ - "arm" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.0.5" - } - }, - "node_modules/@img/sharp-linux-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", - "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.0.4" - } - }, - "node_modules/@img/sharp-linux-s390x": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", - "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", - "cpu": [ - "s390x" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.0.4" - } - }, "node_modules/@img/sharp-linux-x64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", @@ -536,27 +343,6 @@ "@img/sharp-libvips-linux-x64": "1.0.4" } }, - "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", - "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - }, - "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" - } - }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.33.5", "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", @@ -578,60 +364,6 @@ "@img/sharp-libvips-linuxmusl-x64": "1.0.4" } }, - "node_modules/@img/sharp-wasm32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", - "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", - "cpu": [ - "wasm32" - ], - "optional": true, - "dependencies": { - "@emnapi/runtime": "^1.2.0" - }, - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-ia32": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", - "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, - "node_modules/@img/sharp-win32-x64": { - "version": "0.33.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", - "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" - }, - "funding": { - "url": "https://opencollective.com/libvips" - } - }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -725,15 +457,16 @@ } }, "node_modules/@next/env": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/env/-/env-15.1.2.tgz", - "integrity": "sha512-Hm3jIGsoUl6RLB1vzY+dZeqb+/kWPZ+h34yiWxW0dV87l8Im/eMOwpOA+a0L78U0HM04syEjXuRlCozqpwuojQ==" + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.2.1.tgz", + "integrity": "sha512-JmY0qvnPuS2NCWOz2bbby3Pe0VzdAQ7XpEB6uLIHmtXNfAsAO0KLQLkuAoc42Bxbo3/jMC3dcn9cdf+piCcG2Q==" }, "node_modules/@next/eslint-plugin-next": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.1.2.tgz", - "integrity": "sha512-sgfw3+WdaYOGPKCvM1L+UucBmRfh8V2Ygefp7ELON0+0vY7uohQwXXnVWg3rY7mXDKharQR3o7uedpfvnU2hlQ==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-15.2.4.tgz", + "integrity": "sha512-O8ScvKtnxkp8kL9TpJTTKnMqlkZnS+QxwoQnJwPGBxjBbzd6OVVPEJ5/pMNrktSyXQD/chEfzfFzYLM6JANOOQ==", "dev": true, + "license": "MIT", "dependencies": { "fast-glob": "3.3.1" } @@ -743,6 +476,7 @@ "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.1.tgz", "integrity": "sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==", "dev": true, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -759,6 +493,7 @@ "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", "dev": true, + "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, @@ -767,9 +502,9 @@ } }, "node_modules/@next/swc-darwin-arm64": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.1.2.tgz", - "integrity": "sha512-b9TN7q+j5/7+rGLhFAVZiKJGIASuo8tWvInGfAd8wsULjB1uNGRCj1z1WZwwPWzVQbIKWFYqc+9L7W09qwt52w==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.2.1.tgz", + "integrity": "sha512-aWXT+5KEREoy3K5AKtiKwioeblmOvFFjd+F3dVleLvvLiQ/mD//jOOuUcx5hzcO9ISSw4lrqtUPntTpK32uXXQ==", "cpu": [ "arm64" ], @@ -782,9 +517,9 @@ } }, "node_modules/@next/swc-darwin-x64": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.1.2.tgz", - "integrity": "sha512-caR62jNDUCU+qobStO6YJ05p9E+LR0EoXh1EEmyU69cYydsAy7drMcOlUlRtQihM6K6QfvNwJuLhsHcCzNpqtA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.2.1.tgz", + "integrity": "sha512-E/w8ervu4fcG5SkLhvn1NE/2POuDCDEy5gFbfhmnYXkyONZR68qbUlJlZwuN82o7BrBVAw+tkR8nTIjGiMW1jQ==", "cpu": [ "x64" ], @@ -797,9 +532,9 @@ } }, "node_modules/@next/swc-linux-arm64-gnu": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.1.2.tgz", - "integrity": "sha512-fHHXBusURjBmN6VBUtu6/5s7cCeEkuGAb/ZZiGHBLVBXMBy4D5QpM8P33Or8JD1nlOjm/ZT9sEE5HouQ0F+hUA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.2.1.tgz", + "integrity": "sha512-gXDX5lIboebbjhiMT6kFgu4svQyjoSed6dHyjx5uZsjlvTwOAnZpn13w9XDaIMFFHw7K8CpBK7HfDKw0VZvUXQ==", "cpu": [ "arm64" ], @@ -812,9 +547,9 @@ } }, "node_modules/@next/swc-linux-arm64-musl": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.1.2.tgz", - "integrity": "sha512-9CF1Pnivij7+M3G74lxr+e9h6o2YNIe7QtExWq1KUK4hsOLTBv6FJikEwCaC3NeYTflzrm69E5UfwEAbV2U9/g==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.2.1.tgz", + "integrity": "sha512-3v0pF/adKZkBWfUffmB/ROa+QcNTrnmYG4/SS+r52HPwAK479XcWoES2I+7F7lcbqc7mTeVXrIvb4h6rR/iDKg==", "cpu": [ "arm64" ], @@ -827,9 +562,9 @@ } }, "node_modules/@next/swc-linux-x64-gnu": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.1.2.tgz", - "integrity": "sha512-tINV7WmcTUf4oM/eN3Yuu/f8jQ5C6AkueZPKeALs/qfdfX57eNv4Ij7rt0SA6iZ8+fMobVfcFVv664Op0caCCg==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.2.1.tgz", + "integrity": "sha512-RbsVq2iB6KFJRZ2cHrU67jLVLKeuOIhnQB05ygu5fCNgg8oTewxweJE8XlLV+Ii6Y6u4EHwETdUiRNXIAfpBww==", "cpu": [ "x64" ], @@ -842,9 +577,9 @@ } }, "node_modules/@next/swc-linux-x64-musl": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.1.2.tgz", - "integrity": "sha512-jf2IseC4WRsGkzeUw/cK3wci9pxR53GlLAt30+y+B+2qAQxMw6WAC3QrANIKxkcoPU3JFh/10uFfmoMDF9JXKg==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.2.1.tgz", + "integrity": "sha512-QHsMLAyAIu6/fWjHmkN/F78EFPKmhQlyX5C8pRIS2RwVA7z+t9cTb0IaYWC3EHLOTjsU7MNQW+n2xGXr11QPpg==", "cpu": [ "x64" ], @@ -857,9 +592,9 @@ } }, "node_modules/@next/swc-win32-arm64-msvc": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.1.2.tgz", - "integrity": "sha512-wvg7MlfnaociP7k8lxLX4s2iBJm4BrNiNFhVUY+Yur5yhAJHfkS8qPPeDEUH8rQiY0PX3u/P7Q/wcg6Mv6GSAA==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.2.1.tgz", + "integrity": "sha512-Gk42XZXo1cE89i3hPLa/9KZ8OuupTjkDmhLaMKFohjf9brOeZVEa3BQy1J9s9TWUqPhgAEbwv6B2+ciGfe54Vw==", "cpu": [ "arm64" ], @@ -872,9 +607,9 @@ } }, "node_modules/@next/swc-win32-x64-msvc": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.1.2.tgz", - "integrity": "sha512-D3cNA8NoT3aWISWmo7HF5Eyko/0OdOO+VagkoJuiTk7pyX3P/b+n8XA/MYvyR+xSVcbKn68B1rY9fgqjNISqzQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.2.1.tgz", + "integrity": "sha512-YjqXCl8QGhVlMR8uBftWk0iTmvtntr41PhG1kvzGp0sUP/5ehTM+cwx25hKE54J0CRnHYjSGjSH3gkHEaHIN9g==", "cpu": [ "x64" ], @@ -932,12 +667,12 @@ } }, "node_modules/@playwright/test": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.49.1.tgz", - "integrity": "sha512-Ky+BVzPz8pL6PQxHqNRW1k3mIyv933LML7HktS8uik0bUXNCdPhoS/kLihiO1tMf/egaJb4IutXd7UywvXEW+g==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.51.0.tgz", + "integrity": "sha512-dJ0dMbZeHhI+wb77+ljx/FeC8VBP6j/rj9OAojO08JI80wTZy6vRk9KvHKiDCUh4iMpEiseMgqRBIeW+eKX6RA==", "devOptional": true, "dependencies": { - "playwright": "1.49.1" + "playwright": "1.51.0" }, "bin": { "playwright": "cli.js" @@ -953,18 +688,16 @@ "license": "MIT" }, "node_modules/@radix-ui/primitive": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.0.tgz", - "integrity": "sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA==", - "license": "MIT" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", + "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" }, "node_modules/@radix-ui/react-arrow": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz", - "integrity": "sha512-FmlW1rCg7hBpEBwFbjHwCW6AmWLQM6g/v0Sn8XbP9NvmSZ2San1FpQeyPtufzOMSIx7Y4dzjlHoifhp+7NkZhw==", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.2.tgz", + "integrity": "sha512-G+KcpzXHq24iH0uGG/pF8LyzpFJYGD4RfLjCIBfGdSLXvjLHST31RUiRVrupIBMvIppMgSzQ6l66iAxl03tdlg==", "dependencies": { - "@radix-ui/react-primitive": "2.0.0" + "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", @@ -982,15 +715,15 @@ } }, "node_modules/@radix-ui/react-checkbox": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.2.tgz", - "integrity": "sha512-/i0fl686zaJbDQLNKrkCbMyDm6FQMt4jg323k7HuqitoANm9sE23Ql8yOK3Wusk34HSLKDChhMux05FnP6KUkw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.4.tgz", + "integrity": "sha512-wP0CPAHq+P5I4INKe3hJrIa1WoNqqrejzW+zoU0rOvo1b9gDEJJFl2rYfO1PYJUQCc2H1WZxIJmyv9BS8i5fLw==", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-previous": "1.1.0", "@radix-ui/react-use-size": "1.1.0" @@ -1010,53 +743,15 @@ } } }, - "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-checkbox/node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, "node_modules/@radix-ui/react-collection": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.0.tgz", - "integrity": "sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw==", - "license": "MIT", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.2.tgz", + "integrity": "sha512-9z54IEKRxIa9VityapoEYMuByaG42iSy1ZXlY2KcuLSEtq8x4987/N6m15ppoMffgZX72gER2uHe1D9Y6Unlcw==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -1074,10 +769,9 @@ } }, "node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz", - "integrity": "sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", + "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1089,10 +783,9 @@ } }, "node_modules/@radix-ui/react-context": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.0.tgz", - "integrity": "sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A==", - "license": "MIT", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", + "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -1104,110 +797,24 @@ } }, "node_modules/@radix-ui/react-dialog": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.2.tgz", - "integrity": "sha512-Yj4dZtqa2o+kG61fzB0H2qUvmwBA2oyQroGLyNtBj1beo1khoQ3q1a2AO8rrQYjd8256CO9+N8L9tvsS+bnIyA==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.6.tgz", + "integrity": "sha512-/IVhJV5AceX620DUJ4uYVMymzsipdKBzo3edo+omeskCKGm9FRHM0ebIdbPnlQVJqyuHbuBltQUOG2mOTq2IYw==", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", + "@radix-ui/react-focus-scope": "1.1.2", "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-slot": "1.1.0", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-controllable-state": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-portal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", - "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-dialog/node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -1240,13 +847,13 @@ } }, "node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.2.tgz", - "integrity": "sha512-kEHnlhv7wUggvhuJPkyw4qspXLJOdYoAP4dO2c8ngGuXTq1w/HZp1YeVB+NQ2KbH1iEG+pvOCGYSqh9HZOz6hg==", + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.5.tgz", + "integrity": "sha512-E4TywXY6UsXNRhFrECa5HAvE5/4BFcGyfTyK36gP+pAW1ed7UTK4vKwdr53gAJYwqbfCWC6ATvJa3J3R/9+Qrg==", "dependencies": { "@radix-ui/primitive": "1.1.1", "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", "@radix-ui/react-use-escape-keydown": "1.1.0" }, @@ -1265,755 +872,18 @@ } } }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" - }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", - "dependencies": { - "@radix-ui/react-slot": "1.1.1" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-dismissable-layer/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-dropdown-menu": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.2.tgz", - "integrity": "sha512-GVZMR+eqK8/Kes0a36Qrv+i20bAPXSn8rCBTHx30w+3ECnR5o3xixAlqcVaYvLeyKUsm0aqyhWfmUcqufM8nYA==", + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-dropdown-menu/-/react-dropdown-menu-2.1.6.tgz", + "integrity": "sha512-no3X7V5fD487wab/ZYSHXq3H37u4NVeLDKI/Ks724X/eEFSSEFYZxWgsIlr1UBeEyDaM29HM5x9p1Nv8DuTYPA==", "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-menu": "2.1.2", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-dropdown-menu/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-focus-guards": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz", - "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-hover-card": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.2.tgz", - "integrity": "sha512-Y5w0qGhysvmqsIy6nQxaPa6mXNKznfoGjOfBgzOjocLxr2XlSjqBMYQQL+FfyogsMuX+m8cZyQGYhJxvxUzO4w==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-portal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", - "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-id": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", - "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-menu": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.2.tgz", - "integrity": "sha512-lZ0R4qR2Al6fZ4yCCZzu/ReTFrylHFxIqy7OezIpWF4bL0o9biKo0pFIvkaew3TyZ9Fy5gYVrR5zCGZBVbO1zg==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.1", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.0", - "@radix-ui/react-portal": "1.1.2", - "@radix-ui/react-presence": "1.1.1", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-roving-focus": "1.1.0", - "@radix-ui/react-slot": "1.1.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-dismissable-layer": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.1.tgz", - "integrity": "sha512-QSxg29lfr/xcev6kSz7MAlmDnzbP1eI/Dwn3Tp1ip0KT5CUELsxkekFEMVBEoykI3oV39hKT4TKZzBNMbcTZYQ==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-escape-keydown": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-portal": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.2.tgz", - "integrity": "sha512-WeDYLGPxJb/5EGBoedyJbT0MpoULmwnIPMJMSldkuiMsBAv7N1cRdsTWZWht9vpPOiN3qyiGAtbK2is47/uMFg==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-menu/node_modules/@radix-ui/react-presence": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.1.tgz", - "integrity": "sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-popper": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz", - "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==", - "license": "MIT", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-portal": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.3.tgz", - "integrity": "sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", - "dependencies": { - "@radix-ui/react-slot": "1.1.1" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-presence": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", - "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-use-layout-effect": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-presence/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-primitive": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz", - "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==", - "license": "MIT", - "dependencies": { - "@radix-ui/react-slot": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-progress": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.0.tgz", - "integrity": "sha512-aSzvnYpP725CROcxAOEBVZZSIQVQdHgBr2QQFKySsaD14u8dNT0batuXI+AAGDdAHfXH8rbnHmjYFqVJ21KkRg==", - "dependencies": { - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-primitive": "2.0.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-roving-focus": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz", - "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==", - "dependencies": { - "@radix-ui/primitive": "1.1.0", - "@radix-ui/react-collection": "1.1.0", - "@radix-ui/react-compose-refs": "1.1.0", - "@radix-ui/react-context": "1.1.0", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-primitive": "2.0.0", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-select": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.3.tgz", - "integrity": "sha512-tlLwaewTfrKetiex8iW9wwME/qrYlzlH0qcgYmos7xS54MO00SiPHasLoAykg/yVrjf41GQptPPi4oXzrP+sgg==", - "dependencies": { - "@radix-ui/number": "1.1.0", - "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-direction": "1.1.0", - "@radix-ui/react-dismissable-layer": "1.1.2", - "@radix-ui/react-focus-guards": "1.1.1", - "@radix-ui/react-focus-scope": "1.1.1", - "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-previous": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1", - "aria-hidden": "^1.1.1", - "react-remove-scroll": "2.6.0" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-arrow": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", - "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-collection": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", - "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-focus-scope": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.1.tgz", - "integrity": "sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0" + "@radix-ui/react-menu": "2.1.6", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2030,43 +900,28 @@ } } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-popper": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", - "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", - "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.1", - "@radix-ui/react-compose-refs": "1.1.1", - "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" - }, + "node_modules/@radix-ui/react-focus-guards": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.1.tgz", + "integrity": "sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==", "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" + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { "optional": true - }, - "@types/react-dom": { - "optional": true } } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "node_modules/@radix-ui/react-focus-scope": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.2.tgz", + "integrity": "sha512-zxwE80FCU7lcXUGWkdt6XpTTCKPitG1XKOwViTxHVKIJhZl9MvIl2dVHeZENCWD9+EdWv05wlaEkRXUykU27RA==", "dependencies": { - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2083,30 +938,43 @@ } } }, - "node_modules/@radix-ui/react-select/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "node_modules/@radix-ui/react-hover-card": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-hover-card/-/react-hover-card-1.1.6.tgz", + "integrity": "sha512-E4ozl35jq0VRlrdc4dhHrNSV0JqBb4Jy73WAhBEK7JoYnQ83ED5r0Rb/XdVKw89ReAJN38N492BAPBZQ57VmqQ==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@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 } } }, - "node_modules/@radix-ui/react-slot": { + "node_modules/@radix-ui/react-id": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz", - "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==", + "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz", + "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==", "license": "MIT", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.0" + "@radix-ui/react-use-layout-effect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2118,23 +986,29 @@ } } }, - "node_modules/@radix-ui/react-toast": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.3.tgz", - "integrity": "sha512-oB8irs7CGAml6zWbum7MNySTH/sR7PM1ZQyLV8reO946u73sU83yZUKijrMLNbm4hTOrJY4tE8Oa/XUKrOr2Wg==", + "node_modules/@radix-ui/react-menu": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-menu/-/react-menu-2.1.6.tgz", + "integrity": "sha512-tBBb5CXDJW3t2mo9WlO7r6GTmWV0F0uzHZVFmlRmYpiSK1CDU5IKojP1pm7oknpBOrFZx/YgBRW9oorPO2S/Lg==", "dependencies": { "@radix-ui/primitive": "1.1.1", - "@radix-ui/react-collection": "1.1.1", + "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.2", - "@radix-ui/react-portal": "1.1.3", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-roving-focus": "1.1.2", + "@radix-ui/react-slot": "1.1.2", "@radix-ui/react-use-callback-ref": "1.1.0", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1" + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2151,20 +1025,21 @@ } } }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" - }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-collection": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.1.tgz", - "integrity": "sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==", + "node_modules/@radix-ui/react-popper": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.2.tgz", + "integrity": "sha512-Rvqc3nOpwseCyj/rgjlJDYAgyfw7OC1tTkKn2ivhaMGcYt8FSBlahHOZak2i3QwkRXUXgGgzeEe2RuqeEHuHgA==", "dependencies": { + "@floating-ui/react-dom": "^2.0.0", + "@radix-ui/react-arrow": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-rect": "1.1.0", + "@radix-ui/react-use-size": "1.1.0", + "@radix-ui/rect": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2181,40 +1056,58 @@ } } }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", + "node_modules/@radix-ui/react-portal": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.4.tgz", + "integrity": "sha512-sn2O9k1rPFYVyKd5LAJfo96JlSGVFpa1fS6UuBJfrZadudiw5tAmru+n1x7aMRQ84qDM71Zh1+SzK5QwU0tJfA==", + "dependencies": { + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@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 } } }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", + "node_modules/@radix-ui/react-presence": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.2.tgz", + "integrity": "sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@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 } } }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "node_modules/@radix-ui/react-primitive": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.2.tgz", + "integrity": "sha512-Ec/0d38EIuvDF+GZjcMU/Ze6MxntVJYO/fRlCPhCaVUyPY9WTalHJw54tp9sXeJo3tlShWpy41vQRgLRGOuz+w==", "dependencies": { - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/react-slot": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2231,40 +1124,43 @@ } } }, - "node_modules/@radix-ui/react-toast/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", + "node_modules/@radix-ui/react-progress": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.2.tgz", + "integrity": "sha512-u1IgJFQ4zNAUTjGdDL5dcl/U8ntOR6jsnhxKb5RKp5Ozwl88xKR9EqRZOe/Mk8tnx0x5tNUe2F+MzsyjqMg0MA==", "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "@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 } } }, - "node_modules/@radix-ui/react-tooltip": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.5.tgz", - "integrity": "sha512-IucoQPcK5nwUuztaxBQvudvYwH58wtRcJlv1qvaMSyIbL9dEBfFN0vRf/D8xDbu6HmAJLlNGty4z8Na+vIqe9Q==", + "node_modules/@radix-ui/react-roving-focus": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.2.tgz", + "integrity": "sha512-zgMQWkNO169GtGqRvYrzb0Zf8NhMHS2DuEB/TiEmVnpr5OqPU3i8lfbxaAmC2J/KYuIQxyoQQ6DxepyXp61/xw==", "dependencies": { "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-dismissable-layer": "1.1.2", + "@radix-ui/react-direction": "1.1.0", "@radix-ui/react-id": "1.1.0", - "@radix-ui/react-popper": "1.2.1", - "@radix-ui/react-portal": "1.1.3", - "@radix-ui/react-presence": "1.1.2", - "@radix-ui/react-primitive": "2.0.1", - "@radix-ui/react-slot": "1.1.1", - "@radix-ui/react-use-controllable-state": "1.1.0", - "@radix-ui/react-visually-hidden": "1.1.1" + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0" }, "peerDependencies": { "@types/react": "*", @@ -2281,17 +1177,32 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.1.tgz", - "integrity": "sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==" - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.1.tgz", - "integrity": "sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==", + "node_modules/@radix-ui/react-select": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.1.6.tgz", + "integrity": "sha512-T6ajELxRvTuAMWH0YmRJ1qez+x4/7Nq7QIx7zJ0VK3qaEWdnWpNbEDnmWldG1zBDwqrLy5aLMUWcoGirVj5kMg==", "dependencies": { - "@radix-ui/react-primitive": "2.0.1" + "@radix-ui/number": "1.1.0", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-direction": "1.1.0", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-focus-guards": "1.1.1", + "@radix-ui/react-focus-scope": "1.1.2", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0", + "@radix-ui/react-use-previous": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2", + "aria-hidden": "^1.2.4", + "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", @@ -2308,24 +1219,13 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "integrity": "sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==", - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + "node_modules/@radix-ui/react-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.2.tgz", + "integrity": "sha512-YAKxaiGsSQJ38VzKH86/BPRC4rh+b1Jpa+JneA5LRE7skmLPNAyeG8kPJj/oo4STLvlrs8vkf/iYyc3A5stYCQ==", + "dependencies": { + "@radix-ui/react-compose-refs": "1.1.1" }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.1.tgz", - "integrity": "sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==", "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" @@ -2336,21 +1236,23 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.1.tgz", - "integrity": "sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==", + "node_modules/@radix-ui/react-toast": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@radix-ui/react-toast/-/react-toast-1.2.6.tgz", + "integrity": "sha512-gN4dpuIVKEgpLn1z5FhzT9mYRUitbfZq9XqN/7kkBMUgFTzTG8x/KszWJugJXHcwxckY8xcKDZPz7kG3o6DsUA==", "dependencies": { - "@floating-ui/react-dom": "^2.0.0", - "@radix-ui/react-arrow": "1.1.1", + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-collection": "1.1.2", "@radix-ui/react-compose-refs": "1.1.1", "@radix-ui/react-context": "1.1.1", - "@radix-ui/react-primitive": "2.0.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-controllable-state": "1.1.0", "@radix-ui/react-use-layout-effect": "1.1.0", - "@radix-ui/react-use-rect": "1.1.0", - "@radix-ui/react-use-size": "1.1.0", - "@radix-ui/rect": "1.1.0" + "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2367,12 +1269,23 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "node_modules/@radix-ui/react-tooltip": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.1.8.tgz", + "integrity": "sha512-YAA2cu48EkJZdAMHC0dqo9kialOcRStbtiY4nJPaht7Ptrhcvpo+eDChaM6BIs8kL6a8Z5l5poiqLnXcNduOkA==", "dependencies": { - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/primitive": "1.1.1", + "@radix-ui/react-compose-refs": "1.1.1", + "@radix-ui/react-context": "1.1.1", + "@radix-ui/react-dismissable-layer": "1.1.5", + "@radix-ui/react-id": "1.1.0", + "@radix-ui/react-popper": "1.2.2", + "@radix-ui/react-portal": "1.1.4", + "@radix-ui/react-presence": "1.1.2", + "@radix-ui/react-primitive": "2.0.2", + "@radix-ui/react-slot": "1.1.2", + "@radix-ui/react-use-controllable-state": "1.1.0", + "@radix-ui/react-visually-hidden": "1.1.2" }, "peerDependencies": { "@types/react": "*", @@ -2389,23 +1302,6 @@ } } }, - "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/react-use-callback-ref": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz", @@ -2443,7 +1339,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz", "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==", - "license": "MIT", "dependencies": { "@radix-ui/react-use-callback-ref": "1.1.0" }, @@ -2491,7 +1386,6 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.0.tgz", "integrity": "sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==", - "license": "MIT", "dependencies": { "@radix-ui/rect": "1.1.0" }, @@ -2524,47 +1418,11 @@ } }, "node_modules/@radix-ui/react-visually-hidden": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.1.tgz", - "integrity": "sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==", - "dependencies": { - "@radix-ui/react-primitive": "2.0.1" - }, - "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 - } - } - }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-compose-refs": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.1.tgz", - "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 - } - } - }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.1.tgz", - "integrity": "sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.1.2.tgz", + "integrity": "sha512-1SzA4ns2M1aRlvxErqhLHsBHoS5eI5UUcI2awAMgGUp4LoaoWOKYmvqDY2s/tltuPkh3Yk77YF/r3IRj+Amx4Q==", "dependencies": { - "@radix-ui/react-slot": "1.1.1" + "@radix-ui/react-primitive": "2.0.2" }, "peerDependencies": { "@types/react": "*", @@ -2581,28 +1439,10 @@ } } }, - "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.1.tgz", - "integrity": "sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==", - "dependencies": { - "@radix-ui/react-compose-refs": "1.1.1" - }, - "peerDependencies": { - "@types/react": "*", - "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@radix-ui/rect": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz", - "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==", - "license": "MIT" + "integrity": "sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==" }, "node_modules/@rtsao/scc": { "version": "1.1.0", @@ -2664,40 +1504,43 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/lodash": { + "version": "4.17.14", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.14.tgz", + "integrity": "sha512-jsxagdikDiDBeIRaPYtArcT8my4tN1og7MtMRquFT3XNA6axxyHDRUemqDz/taRDdOUn0GnGHRCuff4q48sW9A==" + }, "node_modules/@types/node": { - "version": "22.10.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-22.10.2.tgz", - "integrity": "sha512-Xxr6BBRCAOQixvonOye19wnzyDiUtTeqldOOmj3CkeblonbccA12PFwlufvRdrpjXxqnmUaeiU5EOA+7s5diUQ==", + "version": "22.15.26", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.26.tgz", + "integrity": "sha512-lgISkNrqdQ5DAzjBhnDNGKDuXDNo7/1V4FhNzsKREhWLZTOELQAptuAnJMzHtUl1qyEBBy9lNBKQ9WjyiSloTw==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~6.20.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/prop-types": { "version": "15.7.12", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.12.tgz", "integrity": "sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==", - "devOptional": true, "license": "MIT" }, "node_modules/@types/react": { - "version": "18.3.3", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.3.tgz", - "integrity": "sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==", - "devOptional": true, - "license": "MIT", + "version": "18.3.18", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.18.tgz", + "integrity": "sha512-t4yC+vtgnkYjNSKlFx1jkAhH8LgTo2N/7Qvi83kdEaUtMDiwpbLAktKDaAMlRcJ5eSxZkH74eEGt1ky31d7kfQ==", "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "node_modules/@types/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-qW1Mfv8taImTthu4KoXgDfLuk4bydU6Q/TkADnDWWHwi4NX4BR+LWfTp2sVmTqRrsHvyDDTelgelxJ+SsejKKQ==", + "version": "18.3.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.5.tgz", + "integrity": "sha512-P4t6saawp+b/dFrUr2cvkVsfvPguwsxtH6dNIYRllMsefqFzkZk5UIjzyDOv5g1dXIPdG4Sp1yCR4Z6RCUsG/Q==", "devOptional": true, - "dependencies": { - "@types/react": "*" + "peerDependencies": { + "@types/react": "^18.0.0" } }, "node_modules/@types/react-gtm-module": { @@ -2924,9 +1767,9 @@ } }, "node_modules/acorn": { - "version": "8.14.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", - "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", "dev": true, "license": "MIT", "bin": { @@ -3200,9 +2043,9 @@ "dev": true }, "node_modules/autoprefixer": { - "version": "10.4.20", - "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.20.tgz", - "integrity": "sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==", + "version": "10.4.21", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.4.21.tgz", + "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", "funding": [ { "type": "opencollective", @@ -3217,12 +2060,13 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "browserslist": "^4.23.3", - "caniuse-lite": "^1.0.30001646", + "browserslist": "^4.24.4", + "caniuse-lite": "^1.0.30001702", "fraction.js": "^4.3.7", "normalize-range": "^0.1.2", - "picocolors": "^1.0.1", + "picocolors": "^1.1.1", "postcss-value-parser": "^4.2.0" }, "bin": { @@ -3275,6 +2119,14 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, + "node_modules/base64-arraybuffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", + "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", + "engines": { + "node": ">= 0.6.0" + } + }, "node_modules/bezier-js": { "version": "6.1.4", "resolved": "https://registry.npmjs.org/bezier-js/-/bezier-js-6.1.4.tgz", @@ -3320,9 +2172,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.2", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz", - "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==", + "version": "4.24.4", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", + "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", "funding": [ { "type": "opencollective", @@ -3337,10 +2189,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001669", - "electron-to-chromium": "^1.5.41", - "node-releases": "^2.0.18", + "caniuse-lite": "^1.0.30001688", + "electron-to-chromium": "^1.5.73", + "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { @@ -3428,9 +2281,9 @@ } }, "node_modules/caniuse-lite": { - "version": "1.0.30001680", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001680.tgz", - "integrity": "sha512-rPQy70G6AGUMnbwS1z6Xg+RkHYPAi18ihs47GH0jcxIG7wArmPgY3XbS2sRdBbxJljp3thdT8BIqv9ccCypiPA==", + "version": "1.0.30001707", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001707.tgz", + "integrity": "sha512-3qtRjw/HQSMlDWf+X79N206fepf4SOOU6SQLMaq/0KkZLmSjPxAkBOQQ+FxbHKfHmYLZFfdWsO3KA90ceHPSnw==", "funding": [ { "type": "opencollective", @@ -3444,7 +2297,8 @@ "type": "github", "url": "https://github.com/sponsors/ai" } - ] + ], + "license": "CC-BY-4.0" }, "node_modules/canvas-color-tracker": { "version": "1.3.1", @@ -3457,6 +2311,11 @@ "node": ">=12" } }, + "node_modules/canvas2svg": { + "version": "1.0.16", + "resolved": "https://registry.npmjs.org/canvas2svg/-/canvas2svg-1.0.16.tgz", + "integrity": "sha512-r3ryHprzDOtAsFuczw+/DKkLR3XexwIlJWnJ+71I9QF7V9scYaV5JZgYDoCUlYtT3ARnOpDcm/hDNZYbWMRHqA==" + }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -3566,7 +2425,6 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", - "optional": true, "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" @@ -3597,7 +2455,6 @@ "version": "1.9.1", "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", - "optional": true, "dependencies": { "color-name": "^1.0.0", "simple-swizzle": "^0.2.2" @@ -3629,10 +2486,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.5.tgz", - "integrity": "sha512-ZVJrKKYunU38/76t0RMOulHOnUcbU9GbpWKAOZ0mhjr7CX6FVrH+4FrAapSOekrgFQ3f/8gwMEuIft0aKq6Hug==", - "license": "MIT", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -3642,6 +2498,14 @@ "node": ">= 8" } }, + "node_modules/css-line-break": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", + "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -3658,9 +2522,48 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "devOptional": true, "license": "MIT" }, + "node_modules/d3": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.9.0.tgz", + "integrity": "sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-array": { "version": "3.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz", @@ -3672,11 +2575,45 @@ "node": ">=12" } }, + "node_modules/d3-axis": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-binarytree": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/d3-binarytree/-/d3-binarytree-1.0.2.tgz", "integrity": "sha512-cElUNH+sHu95L04m92pG73t2MEJXKu+GeKUN1TJkFsu93E5W8E9Sc3kHEGJKgenGvj19m6upSn2EunvMgMD2Yw==" }, + "node_modules/d3-brush": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", + "dependencies": { + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-color": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", @@ -3685,6 +2622,28 @@ "node": ">=12" } }, + "node_modules/d3-contour": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.2.tgz", + "integrity": "sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==", + "dependencies": { + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.4", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.4.tgz", + "integrity": "sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-dispatch": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", @@ -3705,6 +2664,38 @@ "node": ">=12" } }, + "node_modules/d3-dsv": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", + "dependencies": { + "commander": "7", + "iconv-lite": "0.6", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, "node_modules/d3-ease": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", @@ -3713,6 +2704,30 @@ "node": ">=12" } }, + "node_modules/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", + "dependencies": { + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-force": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", + "dependencies": { + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-force-3d": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/d3-force-3d/-/d3-force-3d-3.0.5.tgz", @@ -3736,6 +2751,25 @@ "node": ">=12" } }, + "node_modules/d3-geo": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.1.1.tgz", + "integrity": "sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==", + "dependencies": { + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-hierarchy": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-interpolate": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", @@ -3748,9 +2782,25 @@ } }, "node_modules/d3-octree": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.0.2.tgz", - "integrity": "sha512-Qxg4oirJrNXauiuC94uKMbgxwnhdda9xRLl9ihq45srlJ4Ga3CSgqGcAL8iW7N5CIv4Oz8x3E734ulxyvHPvwA==" + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-octree/-/d3-octree-1.1.0.tgz", + "integrity": "sha512-F8gPlqpP+HwRPMO/8uOu5wjH110+6q4cgJvgJT6vlpy3BEaDIKlTZrgHKZSp/i1InRpVfh4puY/kvL6MxK930A==" + }, + "node_modules/d3-path": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz", + "integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==", + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-polygon": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-quadtree": { "version": "3.0.1", @@ -3760,6 +2810,14 @@ "node": ">=12" } }, + "node_modules/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } + }, "node_modules/d3-scale": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", @@ -3795,6 +2853,17 @@ "node": ">=12" } }, + "node_modules/d3-shape": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz", + "integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==", + "dependencies": { + "d3-path": "^3.1.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/d3-time": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz", @@ -3979,6 +3048,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/delaunator": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", + "integrity": "sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==", + "dependencies": { + "robust-predicates": "^3.0.2" + } + }, "node_modules/detect-libc": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.3.tgz", @@ -3991,8 +3068,7 @@ "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", - "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==", - "license": "MIT" + "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==" }, "node_modules/didyoumean": { "version": "1.2.2", @@ -4039,9 +3115,35 @@ "license": "MIT" }, "node_modules/electron-to-chromium": { - "version": "1.5.63", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.63.tgz", - "integrity": "sha512-ddeXKuY9BHo/mw145axlyWjlJ1UBt4WK3AlvkT7W2AbqfRQoacVoRUCF6wL3uIx/8wT9oLKXzI+rFqHHscByaA==" + "version": "1.5.129", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.129.tgz", + "integrity": "sha512-JlXUemX4s0+9f8mLqib/bHH8gOHf5elKS6KeWG3sk3xozb/JTq/RLXIv8OKUWiK4Ah00Wm88EFj5PYkFr4RUPA==", + "license": "ISC" + }, + "node_modules/embla-carousel": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel/-/embla-carousel-8.5.2.tgz", + "integrity": "sha512-xQ9oVLrun/eCG/7ru3R+I5bJ7shsD8fFwLEY7yPe27/+fDHCNj0OT5EoG5ZbFyOxOcG6yTwW8oTz/dWyFnyGpg==" + }, + "node_modules/embla-carousel-react": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel-react/-/embla-carousel-react-8.5.2.tgz", + "integrity": "sha512-Tmx+uY3MqseIGdwp0ScyUuxpBgx5jX1f7od4Cm5mDwg/dptEiTKf9xp6tw0lZN2VA9JbnVMl/aikmbc53c6QFA==", + "dependencies": { + "embla-carousel": "8.5.2", + "embla-carousel-reactive-utils": "8.5.2" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" + } + }, + "node_modules/embla-carousel-reactive-utils": { + "version": "8.5.2", + "resolved": "https://registry.npmjs.org/embla-carousel-reactive-utils/-/embla-carousel-reactive-utils-8.5.2.tgz", + "integrity": "sha512-QC8/hYSK/pEmqEdU1IO5O+XNc/Ptmmq7uCB44vKplgLKhB/l0+yvYx0+Cv0sF6Ena8Srld5vUErZkT+yTahtDg==", + "peerDependencies": { + "embla-carousel": "8.5.2" + } }, "node_modules/emoji-regex": { "version": "9.2.2", @@ -4229,6 +3331,7 @@ "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -4247,30 +3350,31 @@ } }, "node_modules/eslint": { - "version": "9.15.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.15.0.tgz", - "integrity": "sha512-7CrWySmIibCgT1Os28lUU6upBshZ+GxybLOrmRzi08kS8MBuO8QA7pXEgYgY5W8vK3e74xv0lpjo9DbaGU9Rkw==", + "version": "9.23.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.23.0.tgz", + "integrity": "sha512-jV7AbNoFPAY1EkFYpLq5bslU9NLNO8xnEeQXwErNibVryjk67wHVmddTBilc5srIttJDBrB0eMHKZBFbSIABCw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.19.0", - "@eslint/core": "^0.9.0", - "@eslint/eslintrc": "^3.2.0", - "@eslint/js": "9.15.0", - "@eslint/plugin-kit": "^0.2.3", + "@eslint/config-array": "^0.19.2", + "@eslint/config-helpers": "^0.2.0", + "@eslint/core": "^0.12.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.23.0", + "@eslint/plugin-kit": "^0.2.7", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.1", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.5", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.2.0", + "eslint-scope": "^8.3.0", "eslint-visitor-keys": "^4.2.0", "espree": "^10.3.0", "esquery": "^1.5.0", @@ -4307,12 +3411,13 @@ } }, "node_modules/eslint-config-next": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.1.2.tgz", - "integrity": "sha512-PrMm1/4zWSJ689wd/ypWIR5ZF1uvmp3EkgpgBV1Yu6PhEobBjXMGgT8bVNelwl17LXojO8D5ePFRiI4qXjsPRA==", + "version": "15.2.4", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.2.4.tgz", + "integrity": "sha512-v4gYjd4eYIme8qzaJItpR5MMBXJ0/YV07u7eb50kEnlEmX7yhOjdUdzz70v4fiINYRjLf8X8TbogF0k7wlz6sA==", "dev": true, + "license": "MIT", "dependencies": { - "@next/eslint-plugin-next": "15.1.2", + "@next/eslint-plugin-next": "15.2.4", "@rushstack/eslint-patch": "^1.10.3", "@typescript-eslint/eslint-plugin": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", "@typescript-eslint/parser": "^5.4.2 || ^6.0.0 || ^7.0.0 || ^8.0.0", @@ -4561,11 +3666,10 @@ } }, "node_modules/eslint-scope": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.2.0.tgz", - "integrity": "sha512-PHlWUfG6lvPc3yvP5A4PNyBL1W8fkDUccmI21JUu/+GKZBoH/W5u6usENXUrWFRsyoW5ACUjFGgAFQp5gUlb/A==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -4652,7 +3756,6 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -4813,6 +3916,19 @@ "dev": true, "license": "ISC" }, + "node_modules/float-tooltip": { + "version": "1.7.4", + "resolved": "https://registry.npmjs.org/float-tooltip/-/float-tooltip-1.7.4.tgz", + "integrity": "sha512-UUcH+5MHMnHf7a3qF2ZJ7J5PTtTKHRqdaoC3VAHZuX8ooEegNWxpmmHk192lABXw0+O+FzGB4anpEiqe6iv+WA==", + "dependencies": { + "d3-selection": "2 - 3", + "kapsule": "^1.16", + "preact": "10" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -4824,9 +3940,9 @@ } }, "node_modules/force-graph": { - "version": "1.47.1", - "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.47.1.tgz", - "integrity": "sha512-NF0prpR8tNGq7oCE/aFImT/6/3wSk585bcp39UAj6SNSPjvKbX6ktCH6cZnjfsnPNx/DYj1rn+cvvjH814HCFA==", + "version": "1.49.3", + "resolved": "https://registry.npmjs.org/force-graph/-/force-graph-1.49.3.tgz", + "integrity": "sha512-blBqeFq3vdIzqGgvWrML9xA2R0nS5nvjHsEt9lcWVZ29IcdWQ6wa4G0CG/Uv8bP9olwpsJPZSJe3W8vNhiMCnQ==", "dependencies": { "@tweenjs/tween.js": "18 - 25", "accessor-fn": "1", @@ -4839,6 +3955,7 @@ "d3-scale-chromatic": "1 - 3", "d3-selection": "2 - 3", "d3-zoom": "2 - 3", + "float-tooltip": "^1.6", "index-array-by": "1", "kapsule": "^1.16", "lodash-es": "4" @@ -4962,7 +4079,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz", "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==", - "license": "MIT", "engines": { "node": ">=6" } @@ -5238,6 +4354,29 @@ "resolved": "https://registry.npmjs.org/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==" }, + "node_modules/html2canvas": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/html2canvas/-/html2canvas-1.4.1.tgz", + "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", + "dependencies": { + "css-line-break": "^2.1.0", + "text-segmentation": "^1.0.3" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/ignore": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz", @@ -5249,9 +4388,9 @@ } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { @@ -5305,15 +4444,6 @@ "node": ">=12" } }, - "node_modules/invariant": { - "version": "2.2.4", - "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", - "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.0.0" - } - }, "node_modules/is-alphabetical": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-1.0.4.tgz", @@ -5355,8 +4485,7 @@ "node_modules/is-arrayish": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", - "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", - "optional": true + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==" }, "node_modules/is-async-function": { "version": "2.0.0", @@ -5929,12 +5058,14 @@ } }, "node_modules/lilconfig": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz", - "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==", - "license": "MIT", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "engines": { - "node": ">=10" + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" } }, "node_modules/lines-and-columns": { @@ -6003,11 +5134,12 @@ "license": "ISC" }, "node_modules/lucide-react": { - "version": "0.441.0", - "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.441.0.tgz", - "integrity": "sha512-0vfExYtvSDhkC2lqg0zYVW1Uu9GsI4knuV9GP9by5z0Xhc4Zi5RejTxfz9LsjRmCyWVzHCJvxGKZWcRyvQCWVg==", + "version": "0.486.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.486.0.tgz", + "integrity": "sha512-xWop/wMsC1ikiEVLZrxXjPKw4vU/eAip33G2mZHgbWnr4Nr5Rt4Vx4s/q1D3B/rQVbxjOuqASkEZcUxDEKzecw==", + "license": "ISC", "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0-rc" + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "node_modules/math-intrinsics": { @@ -6116,11 +5248,11 @@ "license": "MIT" }, "node_modules/next": { - "version": "15.1.2", - "resolved": "https://registry.npmjs.org/next/-/next-15.1.2.tgz", - "integrity": "sha512-nLJDV7peNy+0oHlmY2JZjzMfJ8Aj0/dd3jCwSZS8ZiO5nkQfcZRqDrRN3U5rJtqVTQneIOGZzb6LCNrk7trMCQ==", + "version": "15.2.1", + "resolved": "https://registry.npmjs.org/next/-/next-15.2.1.tgz", + "integrity": "sha512-zxbsdQv3OqWXybK5tMkPCBKyhIz63RstJ+NvlfkaLMc/m5MwXgz2e92k+hSKcyBpyADhMk2C31RIiaDjUZae7g==", "dependencies": { - "@next/env": "15.1.2", + "@next/env": "15.2.1", "@swc/counter": "0.1.3", "@swc/helpers": "0.5.15", "busboy": "1.6.0", @@ -6135,14 +5267,14 @@ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" }, "optionalDependencies": { - "@next/swc-darwin-arm64": "15.1.2", - "@next/swc-darwin-x64": "15.1.2", - "@next/swc-linux-arm64-gnu": "15.1.2", - "@next/swc-linux-arm64-musl": "15.1.2", - "@next/swc-linux-x64-gnu": "15.1.2", - "@next/swc-linux-x64-musl": "15.1.2", - "@next/swc-win32-arm64-msvc": "15.1.2", - "@next/swc-win32-x64-msvc": "15.1.2", + "@next/swc-darwin-arm64": "15.2.1", + "@next/swc-darwin-x64": "15.2.1", + "@next/swc-linux-arm64-gnu": "15.2.1", + "@next/swc-linux-arm64-musl": "15.2.1", + "@next/swc-linux-x64-gnu": "15.2.1", + "@next/swc-linux-x64-musl": "15.2.1", + "@next/swc-win32-arm64-msvc": "15.2.1", + "@next/swc-win32-x64-msvc": "15.2.1", "sharp": "^0.33.5" }, "peerDependencies": { @@ -6196,9 +5328,10 @@ } }, "node_modules/node-releases": { - "version": "2.0.18", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz", - "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==" + "version": "2.0.19", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", + "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "license": "MIT" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -6490,11 +5623,11 @@ } }, "node_modules/playwright": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.49.1.tgz", - "integrity": "sha512-VYL8zLoNTBxVOrJBbDuRgDWa3i+mfQgDTrL8Ah9QXZ7ax4Dsj0MSq5bYgytRnDVVe+njoKnfsYkH3HzqVj5UZA==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.51.0.tgz", + "integrity": "sha512-442pTfGM0xxfCYxuBa/Pu6B2OqxqqaYq39JS8QDMGThUvIOCd6s0ANDog3uwA0cHavVlnTQzGCN7Id2YekDSXA==", "dependencies": { - "playwright-core": "1.49.1" + "playwright-core": "1.51.0" }, "bin": { "playwright": "cli.js" @@ -6507,9 +5640,9 @@ } }, "node_modules/playwright-core": { - "version": "1.49.1", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.49.1.tgz", - "integrity": "sha512-BzmpVcs4kE2CH15rWfzpjzVGhWERJfmnXmniSyKeRZUs9Ws65m+RGIi7mjJK/euCegfn3i7jvqWeWyHe9y3Vgg==", + "version": "1.51.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.51.0.tgz", + "integrity": "sha512-x47yPE3Zwhlil7wlNU/iktF7t2r/URR3VLbH6EknJd/04Qc/PSJ0EY3CMXipmglLG+zyRxW6HNo2EGbKLHPWMg==", "bin": { "playwright-core": "cli.js" }, @@ -6625,18 +5758,6 @@ } } }, - "node_modules/postcss-load-config/node_modules/lilconfig": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.2.tgz", - "integrity": "sha512-eop+wDAvpItUys0FWkHIKeC9ybYrTGbU41U5K7+bttZZeohvnY7M9dZ5kB21GNWiFT2q1OoPTvncPCgSOVO5ow==", - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, "node_modules/postcss-nested": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", @@ -6680,6 +5801,15 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "license": "MIT" }, + "node_modules/preact": { + "version": "10.26.4", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz", + "integrity": "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", @@ -6763,6 +5893,17 @@ "node": ">=0.10.0" } }, + "node_modules/react-base16-styling": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/react-base16-styling/-/react-base16-styling-0.10.0.tgz", + "integrity": "sha512-H1k2eFB6M45OaiRru3PBXkuCcn2qNmx+gzLb4a9IPMR7tMH8oBRXU5jGbPDYG1Hz+82d88ED0vjR8BmqU3pQdg==", + "dependencies": { + "@types/lodash": "^4.17.0", + "color": "^4.2.3", + "csstype": "^3.1.3", + "lodash-es": "^4.17.21" + } + }, "node_modules/react-dom": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", @@ -6777,11 +5918,11 @@ } }, "node_modules/react-force-graph-2d": { - "version": "1.26.1", - "resolved": "https://registry.npmjs.org/react-force-graph-2d/-/react-force-graph-2d-1.26.1.tgz", - "integrity": "sha512-7dRD0zNjMpeNghc6dwqzKrdWz45kM1/RNQ7OfR/Y4t9cK02NvHjtmA5JeKePAmzZajqmQQFCbTtwxEfhKgcsww==", + "version": "1.27.0", + "resolved": "https://registry.npmjs.org/react-force-graph-2d/-/react-force-graph-2d-1.27.0.tgz", + "integrity": "sha512-NJAc7lWvY8PxBMn2uFXDDaeLcgJp37IYF6r0geOJlmMs5Lsf0IDmr35T7KNszHV3OUmTrZuPlyxrrzwGwyKhRg==", "dependencies": { - "force-graph": "^1.47", + "force-graph": "^1.49", "prop-types": "15", "react-kapsule": "^2.5" }, @@ -6803,6 +5944,19 @@ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "license": "MIT" }, + "node_modules/react-json-tree": { + "version": "0.19.0", + "resolved": "https://registry.npmjs.org/react-json-tree/-/react-json-tree-0.19.0.tgz", + "integrity": "sha512-PqT1WRVcWP+RROsZPQfNEKIC1iM/ZMfY4g5jN6oDnXp5593PPRAYgoHcgYCDjflAHQMtxl8XGdlTwIBdEGUXvw==", + "dependencies": { + "@types/lodash": "^4.17.0", + "react-base16-styling": "^0.10.0" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-kapsule": { "version": "2.5.6", "resolved": "https://registry.npmjs.org/react-kapsule/-/react-kapsule-2.5.6.tgz", @@ -6818,22 +5972,22 @@ } }, "node_modules/react-remove-scroll": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.0.tgz", - "integrity": "sha512-I2U4JVEsQenxDAKaVa3VZ/JeJZe0/2DxPWL8Tj8yLKctQJQiZM52pn/GWFpSp8dftjM3pSAHVJZscAnC/y+ySQ==", + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.6.3.tgz", + "integrity": "sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==", "dependencies": { - "react-remove-scroll-bar": "^2.3.6", - "react-style-singleton": "^2.2.1", + "react-remove-scroll-bar": "^2.3.7", + "react-style-singleton": "^2.2.3", "tslib": "^2.1.0", - "use-callback-ref": "^1.3.0", - "use-sidecar": "^1.1.2" + "use-callback-ref": "^1.3.3", + "use-sidecar": "^1.1.3" }, "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" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -6842,20 +5996,19 @@ } }, "node_modules/react-remove-scroll-bar": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.6.tgz", - "integrity": "sha512-DtSYaao4mBmX+HDo5YWYdBWQwYIQQshUV/dVxFxK+KM26Wjwp1gZ6rv6OC3oujI6Bfu6Xyg3TwK533AQutsn/g==", - "license": "MIT", + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz", + "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==", "dependencies": { - "react-style-singleton": "^2.2.1", + "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "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" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -6864,31 +6017,28 @@ } }, "node_modules/react-resizable-panels": { - "version": "2.0.23", - "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.0.23.tgz", - "integrity": "sha512-8ZKTwTU11t/FYwiwhMdtZYYyFxic5U5ysRu2YwfkAgDbUJXFvnWSJqhnzkSlW+mnDoNAzDCrJhdOSXBPA76wug==", - "license": "MIT", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-2.1.7.tgz", + "integrity": "sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==", "peerDependencies": { - "react": "^16.14.0 || ^17.0.0 || ^18.0.0", - "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0" + "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "node_modules/react-style-singleton": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.1.tgz", - "integrity": "sha512-ZWj0fHEMyWkHzKYUr2Bs/4zU6XLmq9HsgBURm7g5pAVfyn49DgUiNgY2d4lXRlYSiCif9YBGpQleewkcqddc7g==", - "license": "MIT", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz", + "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==", "dependencies": { "get-nonce": "^1.0.0", - "invariant": "^2.2.4", "tslib": "^2.0.0" }, "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" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -7066,6 +6216,11 @@ "node": ">=0.10.0" } }, + "node_modules/robust-predicates": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.2.tgz", + "integrity": "sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7089,6 +6244,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/safe-array-concat": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", @@ -7125,6 +6285,11 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, "node_modules/scheduler": { "version": "0.23.2", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", @@ -7328,7 +6493,6 @@ "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", - "optional": true, "dependencies": { "is-arrayish": "^0.3.1" } @@ -7648,18 +6812,18 @@ } }, "node_modules/tailwind-merge": { - "version": "2.5.5", - "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.5.5.tgz", - "integrity": "sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-2.6.0.tgz", + "integrity": "sha512-P+Vu1qXfzediirmHOC3xKGAYeZtPcV9g76X+xg2FD4tYgR71ewMA35Y3sCz3zhiN/dwefRpJX0yBcgwi1fXNQA==", "funding": { "type": "github", "url": "https://github.com/sponsors/dcastil" } }, "node_modules/tailwindcss": { - "version": "3.4.15", - "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.15.tgz", - "integrity": "sha512-r4MeXnfBmSOuKUWmXe6h2CcyfzJCEk4F0pptO5jlnYSIViUkVmsawj80N5h2lO3gwcmSb4n3PuN+e+GC1Guylw==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.17.tgz", + "integrity": "sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==", "dependencies": { "@alloc/quick-lru": "^5.2.0", "arg": "^5.0.2", @@ -7670,7 +6834,7 @@ "glob-parent": "^6.0.2", "is-glob": "^4.0.3", "jiti": "^1.21.6", - "lilconfig": "^2.1.0", + "lilconfig": "^3.1.3", "micromatch": "^4.0.8", "normalize-path": "^3.0.0", "object-hash": "^3.0.0", @@ -7711,6 +6875,14 @@ "node": ">=6" } }, + "node_modules/text-segmentation": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/text-segmentation/-/text-segmentation-1.0.3.tgz", + "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", + "dependencies": { + "utrie": "^1.0.2" + } + }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", @@ -7876,11 +7048,10 @@ } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.8.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz", + "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7906,15 +7077,16 @@ } }, "node_modules/undici-types": { - "version": "6.20.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", - "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", - "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", + "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "funding": [ { "type": "opencollective", @@ -7929,9 +7101,10 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { "escalade": "^3.2.0", - "picocolors": "^1.1.0" + "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" @@ -7951,10 +7124,9 @@ } }, "node_modules/use-callback-ref": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.2.tgz", - "integrity": "sha512-elOQwe6Q8gqZgDA8mrh44qRTQqpIHDcZ3hXTLjBe1i4ph8XpNJnO+aQf3NaG+lriLopI4HMx9VjQLfPQ6vhnoA==", - "license": "MIT", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz", + "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==", "dependencies": { "tslib": "^2.0.0" }, @@ -7962,8 +7134,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -7972,10 +7144,9 @@ } }, "node_modules/use-sidecar": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.2.tgz", - "integrity": "sha512-epTbsLuzZ7lPClpz2TyryBfztm7m+28DlEv2ZCQ3MDr5ssiwyOwGH/e5F9CkfWjJ1t4clvI58yF822/GUkjjhw==", - "license": "MIT", + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz", + "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==", "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" @@ -7984,8 +7155,8 @@ "node": ">=10" }, "peerDependencies": { - "@types/react": "^16.9.0 || ^17.0.0 || ^18.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + "@types/react": "*", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "peerDependenciesMeta": { "@types/react": { @@ -7999,6 +7170,26 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT" }, + "node_modules/utrie": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", + "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", + "dependencies": { + "base64-arraybuffer": "^1.0.2" + } + }, + "node_modules/vaul": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vaul/-/vaul-1.1.2.tgz", + "integrity": "sha512-ZFkClGpWyI2WUQjdLJ/BaGuV6AVQiJ3uELGk3OYtP+B6yCO7Cmn9vPFXVJkRaGkOJu3m8bQMgtyzNHixULceQA==", + "dependencies": { + "@radix-ui/react-dialog": "^1.1.1" + }, + "peerDependencies": { + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0.0 || ^19.0.0-rc" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 0a789aa3..ffae1d23 100644 --- a/package.json +++ b/package.json @@ -9,41 +9,47 @@ "lint": "next lint" }, "dependencies": { - "@radix-ui/react-checkbox": "^1.1.2", - "@radix-ui/react-dialog": "^1.1.2", - "@radix-ui/react-dropdown-menu": "^2.1.2", - "@radix-ui/react-hover-card": "^1.1.2", - "@radix-ui/react-progress": "^1.1.0", - "@radix-ui/react-select": "^2.1.2", - "@radix-ui/react-slot": "^1.0.2", - "@radix-ui/react-toast": "^1.2.2", - "@radix-ui/react-tooltip": "^1.1.4", + "@radix-ui/react-checkbox": "^1.1.4", + "@radix-ui/react-dialog": "^1.1.6", + "@radix-ui/react-dropdown-menu": "^2.1.6", + "@radix-ui/react-hover-card": "^1.1.6", + "@radix-ui/react-progress": "^1.1.2", + "@radix-ui/react-select": "^2.1.6", + "@radix-ui/react-slot": "^1.1.2", + "@radix-ui/react-toast": "^1.2.6", + "@radix-ui/react-tooltip": "^1.1.8", "@types/react-gtm-module": "^2.0.4", - "autoprefixer": "^10.4.20", + "autoprefixer": "^10.4.21", + "canvas2svg": "^1.0.16", "class-variance-authority": "^0.7.1", - "clsx": "^2.1.0", - "lucide-react": "^0.441.0", - "next": "^15.1.2", + "clsx": "^2.1.1", + "d3": "^7.9.0", + "embla-carousel-react": "^8.5.2", + "html2canvas": "^1.4.1", + "lucide-react": "^0.486.0", + "next": "^15.1.7", "playwright": "^1.49.1", - "react": "^18", - "react-dom": "^18", - "react-force-graph-2d": "^1.25.8", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "react-force-graph-2d": "^1.27.0", "react-gtm-module": "^2.0.11", - "react-resizable-panels": "^2.0.20", + "react-json-tree": "^0.19.0", + "react-resizable-panels": "^2.1.7", "react-syntax-highlighter": "^15.6.1", "react-type-animation": "^3.2.0", - "tailwind-merge": "^2.5.5", - "tailwindcss-animate": "^1.0.7" + "tailwind-merge": "^2.6.0", + "tailwindcss-animate": "^1.0.7", + "vaul": "^1.1.2" }, "devDependencies": { - "@playwright/test": "^1.49.1", - "@types/node": "^22.10.2", - "@types/react": "^18", - "@types/react-dom": "^18", + "@playwright/test": "^1.50.1", + "@types/node": "^22.15.26", + "@types/react": "^18.3.18", + "@types/react-dom": "^18.3.5", "@types/react-syntax-highlighter": "^15.5.13", - "eslint": "^9", - "eslint-config-next": "^15.1.2", - "tailwindcss": "^3.4.3", - "typescript": "^5" + "eslint": "^9.23.0", + "eslint-config-next": "^15.2.4", + "tailwindcss": "^3.4.17", + "typescript": "^5.7.3" } } diff --git a/playwright.config.ts b/playwright.config.ts index 7a5292d5..8b5b1550 100644 --- a/playwright.config.ts +++ b/playwright.config.ts @@ -16,13 +16,14 @@ export default defineConfig({ /* Run tests in files in parallel */ fullyParallel: true, /* Fail the build on CI if you accidentally left test.only in the source code. */ - forbidOnly: !!process.env.CI, + forbidOnly: !!process.env.CI, /* Retry on CI only */ retries: process.env.CI ? 2 : 0, /* Opt out of parallel tests on CI. */ - workers: process.env.CI ? 2 : undefined, + workers: process.env.CI ? 3 : undefined, /* Reporter to use. See https://playwright.dev/docs/test-reporters */ - reporter: 'html', + reporter: [['html', { outputFolder: 'playwright-report' }]], + outputDir: 'playwright-report/artifacts', /* Shared settings for all the projects below. See https://playwright.dev/docs/api/class-testoptions. */ use: { /* Base URL to use in actions like `await page.goto('/')`. */ @@ -30,6 +31,7 @@ export default defineConfig({ /* Collect trace when retrying the failed test. See https://playwright.dev/docs/trace-viewer */ trace: 'on-first-retry', + screenshot: 'only-on-failure', }, /* Configure projects for major browsers */ diff --git a/public/code-graph-logo.svg b/public/code-graph-logo.svg new file mode 100644 index 00000000..f06e5f18 --- /dev/null +++ b/public/code-graph-logo.svg @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tailwind.config.js b/tailwind.config.js index 14d51792..bad191f5 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -14,73 +14,88 @@ module.exports = { ], prefix: "", theme: { - container: { - center: true, - padding: "2rem", - screens: { - "2xl": "1400px", - }, - }, - extend: { - colors: { - blue: "#7466FF", - pink: "#FF66B3", - orange: "#FF804D", - turquoise: "#80E6E6", - 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)", - }, - 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", - }, - }, + container: { + center: true, + padding: '2rem', + screens: { + '2xl': '1400px' + } + }, + extend: { + colors: { + blue: '#7466FF', + pink: '#FF66B3', + orange: '#FF804D', + turquoise: '#80E6E6', + 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))' + }, + chart: { + '1': 'hsl(var(--chart-1))', + '2': 'hsl(var(--chart-2))', + '3': 'hsl(var(--chart-3))', + '4': 'hsl(var(--chart-4))', + '5': 'hsl(var(--chart-5))' + } + }, + borderRadius: { + lg: 'var(--radius)', + md: 'calc(var(--radius) - 2px)', + sm: 'calc(var(--radius) - 4px)' + }, + 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")], } \ No newline at end of file