diff --git a/README.md b/README.md index 70c82fc..967f28c 100644 --- a/README.md +++ b/README.md @@ -1,11 +1,10 @@ -
- Recall logo + CORE logo
-# Recall +# CORE Simple memory management system for AI agents with per-space ingestion and search capabilities. diff --git a/apps/webapp/app/components/graph/graph-popover.tsx b/apps/webapp/app/components/graph/graph-popover.tsx new file mode 100644 index 0000000..e8ce04b --- /dev/null +++ b/apps/webapp/app/components/graph/graph-popover.tsx @@ -0,0 +1,303 @@ +"use client"; + +import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"; +import type { NodePopupContent, EdgePopupContent } from "./type"; +import { getNodeColor } from "./node-colors"; + +import { useMemo } from "react"; + +import { useTheme } from "remix-themes"; + +import dayjs from "dayjs"; + +/** + * Format a date string into a readable format + */ +export function formatDate( + dateString?: string | null, + format: string = "MMM D, YYYY", +): string { + if (!dateString) return "Unknown"; + + try { + return dayjs(dateString).format(format); + } catch (error) { + console.error("Error formatting date:", error); + return "Invalid date"; + } +} + +interface GraphPopoversProps { + showNodePopup: boolean; + showEdgePopup: boolean; + nodePopupContent: NodePopupContent | null; + edgePopupContent: EdgePopupContent | null; + onOpenChange?: (open: boolean) => void; + labelColorMap?: Map; +} + +export function GraphPopovers({ + showNodePopup, + showEdgePopup, + nodePopupContent, + edgePopupContent, + onOpenChange, + labelColorMap, +}: GraphPopoversProps) { + const [resolvedTheme] = useTheme(); + const isDarkMode = resolvedTheme === "dark"; + + const primaryNodeLabel = useMemo((): string | null => { + if (!nodePopupContent) { + return null; + } + + // Check if node has primaryLabel property (GraphNode) + const nodeAny = nodePopupContent.node as any; + if (nodeAny.primaryLabel && typeof nodeAny.primaryLabel === "string") { + return nodeAny.primaryLabel; + } + + // Fall back to original logic with labels + const primaryLabel = nodePopupContent.node.labels?.find( + (label) => label !== "Entity", + ); + return primaryLabel || "Entity"; + }, [nodePopupContent]); + + // Get the color for the primary label + const labelColor = useMemo(() => { + if (!primaryNodeLabel || !labelColorMap) return ""; + return getNodeColor(primaryNodeLabel, isDarkMode, labelColorMap); + }, [primaryNodeLabel, isDarkMode, labelColorMap]); + + const attributesToDisplay = useMemo(() => { + if (!nodePopupContent) { + return []; + } + const entityProperties = Object.fromEntries( + Object.entries(nodePopupContent.node.attributes || {}).filter( + ([key]) => key !== "labels", + ), + ); + + return Object.entries(entityProperties).map(([key, value]) => ({ + key, + value, + })); + }, [nodePopupContent]); + + return ( +
+ + +
+ + e.preventDefault()} + > +
+
+

Node Details

+ {primaryNodeLabel && ( + + {primaryNodeLabel} + + )} +
+
+

+ + Name: + + {nodePopupContent?.node.name || "Unknown"} +

+

+ + UUID: + + {nodePopupContent?.node.uuid || "Unknown"} +

+

+ + Created: + + {nodePopupContent?.node.created_at && + formatDate(nodePopupContent?.node.created_at)} +

+ + {attributesToDisplay.length > 0 && ( +
+

+ Properties: +

+
+ {attributesToDisplay.map(({ key, value }) => ( +

+ + {key}: + {" "} + + {typeof value === "object" + ? JSON.stringify(value) + : String(value)} + +

+ ))} +
+
+ )} + + {nodePopupContent?.node.summary && ( +
+

+ Summary: +

+
{ + e.stopPropagation(); + const target = e.currentTarget; + target.scrollTop += e.deltaY; + }} + > +

+ {nodePopupContent.node.summary} +

+
+
+ )} + + {nodePopupContent?.node.labels?.length ? ( +
+

+ Labels: +

+
+ {nodePopupContent.node.labels.map((label) => ( + + {label} + + ))} +
+
+ ) : null} +
+
+
+ + + + +
+ + e.preventDefault()} + > +
+

+ {edgePopupContent?.source.name || "Unknown"} →{" "} + + {edgePopupContent?.relation.name || "Unknown"} + {" "} + → {edgePopupContent?.target.name || "Unknown"} +

+
+
+

Relationship

+
+

+ + UUID: + + {edgePopupContent?.relation.uuid || "Unknown"} +

+

+ + Type: + + {edgePopupContent?.relation.name || "Unknown"} +

+ {edgePopupContent?.relation.fact && ( +

+ + Fact: + + {edgePopupContent.relation.fact} +

+ )} + {edgePopupContent?.relation.episodes?.length ? ( +
+

+ Episodes: +

+
+ {edgePopupContent.relation.episodes.map((episode) => ( + + {episode} + + ))} +
+
+ ) : null} +

+ + Created: + + {formatDate(edgePopupContent?.relation.created_at)} +

+ {edgePopupContent?.relation.valid_at && ( +

+ + Valid From: + + {formatDate(edgePopupContent.relation.valid_at)} +

+ )} + {edgePopupContent?.relation.expired_at && ( +

+ + Expired At: + + {formatDate(edgePopupContent.relation.expired_at)} +

+ )} + {edgePopupContent?.relation.invalid_at && ( +

+ + Invalid At: + + {formatDate(edgePopupContent.relation.invalid_at)} +

+ )} +
+
+
+ +
+ ); +} diff --git a/apps/webapp/app/components/graph/graph-visualization.tsx b/apps/webapp/app/components/graph/graph-visualization.tsx new file mode 100644 index 0000000..bd6118e --- /dev/null +++ b/apps/webapp/app/components/graph/graph-visualization.tsx @@ -0,0 +1,185 @@ +"use client"; + +import { useState, useMemo, forwardRef } from "react"; +import { Graph, GraphRef } from "./graph"; +import { GraphPopovers } from "./graph-popover"; +import type { RawTriplet, NodePopupContent, EdgePopupContent } from "./type"; +import { toGraphTriplets } from "./type"; +import { createLabelColorMap, getNodeColor } from "./node-colors"; + +import { + HoverCard, + HoverCardContent, + HoverCardTrigger, +} from "@/components/ui/hover-card"; +import { useTheme } from "remix-themes"; + +interface GraphVisualizationProps { + triplets: RawTriplet[]; + width?: number; + height?: number; + zoomOnMount?: boolean; + className?: string; +} + +export const GraphVisualization = forwardRef( + ( + { + triplets, + width = window.innerWidth * 0.85, + height = window.innerHeight * 0.85, + zoomOnMount = true, + className = "border border-border rounded-md h-[85vh] overflow-hidden relative", + }, + ref, + ) => { + const [resolvedTheme] = useTheme(); + const isDarkMode = resolvedTheme === "dark"; + + // Graph state for popovers + const [showNodePopup, setShowNodePopup] = useState(false); + const [showEdgePopup, setShowEdgePopup] = useState(false); + const [nodePopupContent, setNodePopupContent] = + useState(null); + const [edgePopupContent, setEdgePopupContent] = + useState(null); + + // Convert raw triplets to graph triplets + const graphTriplets = useMemo(() => toGraphTriplets(triplets), [triplets]); + + // Extract all unique labels from triplets + const allLabels = useMemo(() => { + const labels = new Set(); + labels.add("Entity"); // Always include Entity as default + + graphTriplets.forEach((triplet) => { + if (triplet.source.primaryLabel) + labels.add(triplet.source.primaryLabel); + if (triplet.target.primaryLabel) + labels.add(triplet.target.primaryLabel); + }); + + return Array.from(labels).sort((a, b) => { + // Always put "Entity" first + if (a === "Entity") return -1; + if (b === "Entity") return 1; + // Sort others alphabetically + return a.localeCompare(b); + }); + }, [graphTriplets]); + + // Create a shared label color map + const sharedLabelColorMap = useMemo(() => { + return createLabelColorMap(allLabels); + }, [allLabels]); + + // Handle node click + const handleNodeClick = (nodeId: string) => { + // Find the triplet that contains this node + const triplet = triplets.find( + (t) => t.sourceNode.uuid === nodeId || t.targetNode.uuid === nodeId, + ); + + if (!triplet) return; + + // Determine which node was clicked (source or target) + const node = + triplet.sourceNode.uuid === nodeId + ? triplet.sourceNode + : triplet.targetNode; + + // Set popup content and show the popup + setNodePopupContent({ + id: nodeId, + node: node, + }); + setShowNodePopup(true); + setShowEdgePopup(false); + }; + + // Handle edge click + const handleEdgeClick = (edgeId: string) => { + // Find the triplet that contains this edge + const triplet = triplets.find((t) => t.edge.uuid === edgeId); + + if (!triplet) return; + + // Set popup content and show the popup + setEdgePopupContent({ + id: edgeId, + source: triplet.sourceNode, + target: triplet.targetNode, + relation: triplet.edge, + }); + setShowEdgePopup(true); + setShowNodePopup(false); + }; + + // Handle popover close + const handlePopoverClose = () => { + setShowNodePopup(false); + setShowEdgePopup(false); + }; + return ( +
+ {/* Entity Types Legend Button */} +
+ + + + + +
+
+ {allLabels.map((label) => ( +
+
+ {label} +
+ ))} +
+
+ + +
+ + {triplets.length > 0 ? ( + + ) : ( +
+

No graph data to visualize.

+
+ )} + +
+ ); + }, +); diff --git a/apps/webapp/app/components/graph/graph.tsx b/apps/webapp/app/components/graph/graph.tsx new file mode 100644 index 0000000..cfd81fc --- /dev/null +++ b/apps/webapp/app/components/graph/graph.tsx @@ -0,0 +1,1051 @@ +"use client"; + +import { + useEffect, + useRef, + useMemo, + useCallback, + useImperativeHandle, + forwardRef, +} from "react"; +import * as d3 from "d3"; +import colors from "tailwindcss/colors"; + +import type { GraphTriplet, IdValue, GraphNode } from "./type"; +import { + createLabelColorMap, + getNodeColor as getNodeColorByLabel, +} from "./node-colors"; +import { useTheme } from "remix-themes"; + +interface GraphProps { + triplets: GraphTriplet[]; + width?: number; + height?: number; + zoomOnMount?: boolean; + onNodeClick?: (nodeId: string) => void; + onEdgeClick?: (edgeId: string) => void; + onBlur?: () => void; + labelColorMap?: Map; +} + +// Add ref type for zoomToLinkById +export interface GraphRef { + zoomToLinkById: (linkId: string) => void; +} + +export const Graph = forwardRef( + ( + { + triplets, + width = 1000, + height = 800, + zoomOnMount = true, + onNodeClick, + onEdgeClick, + onBlur, + labelColorMap: externalLabelColorMap, + }, + ref, + ) => { + const svgRef = useRef(null); + const [themeMode] = useTheme(); + + // Function refs to keep track of reset functions + const resetLinksRef = useRef<(() => void) | null>(null); + const resetNodesRef = useRef<(() => void) | null>(null); + const handleLinkClickRef = useRef< + ((event: any, d: any, relation: IdValue) => void) | null + >(null); + const simulationRef = useRef | null>(null); + const zoomRef = useRef | null>( + null, + ); + const isInitializedRef = useRef(false); + + // Add ref for zoomToLinkById + const graphRef = useRef({ + zoomToLinkById: (linkId: string) => { + if ( + !svgRef.current || + !resetLinksRef.current || + !resetNodesRef.current || + !handleLinkClickRef.current + ) + return; + const svgElement = d3.select(svgRef.current); + const linkGroups = svgElement.selectAll("g > g"); // Select all link groups + + let found = false; + + // Iterate through link groups to find matching relation + linkGroups.each(function (d: any) { + if (found) return; // Skip if already found + + if (d?.relationData) { + const relation = d.relationData.find( + (r: IdValue) => r.id === linkId, + ); + if (relation) { + found = true; + const resetLinks = resetLinksRef.current; + const resetNodes = resetNodesRef.current; + const handleLinkClick = handleLinkClickRef.current; + + if (resetLinks) resetLinks(); + if (resetNodes) resetNodes(); + if (handleLinkClick) + handleLinkClick({ stopPropagation: () => {} }, d, relation); + } + } + }); + + if (!found) { + console.warn(`Link with id ${linkId} not found`); + } + }, + }); + + // Expose the ref through forwardRef + useImperativeHandle(ref, () => graphRef.current); + + // Memoize theme to prevent unnecessary recreation + const theme = useMemo( + () => ({ + node: { + fill: colors.pink[500], + stroke: themeMode === "dark" ? colors.slate[100] : colors.slate[900], + hover: colors.blue[400], + text: themeMode === "dark" ? colors.slate[100] : colors.slate[900], + selected: colors.blue[500], + dimmed: colors.pink[300], + }, + link: { + stroke: themeMode === "dark" ? colors.slate[600] : colors.slate[400], + selected: colors.blue[400], + dimmed: themeMode === "dark" ? colors.slate[800] : colors.slate[200], + label: { + bg: themeMode === "dark" ? colors.slate[800] : colors.slate[200], + text: themeMode === "dark" ? colors.slate[100] : colors.slate[900], + }, + }, + background: + themeMode === "dark" ? colors.slate[900] : colors.slate[100], + controls: { + bg: themeMode === "dark" ? colors.slate[800] : colors.slate[200], + hover: themeMode === "dark" ? colors.slate[700] : colors.slate[300], + text: themeMode === "dark" ? colors.slate[100] : colors.slate[900], + }, + }), + [themeMode], + ); + + // Extract all unique labels from triplets + const allLabels = useMemo(() => { + // Only calculate if we need to create our own map + if (externalLabelColorMap) return []; + + const labels = new Set(); + labels.add("Entity"); // Always include Entity as default + + triplets.forEach((triplet) => { + if (triplet.source.primaryLabel) + labels.add(triplet.source.primaryLabel); + if (triplet.target.primaryLabel) + labels.add(triplet.target.primaryLabel); + }); + + return Array.from(labels); + }, [triplets, externalLabelColorMap]); + + // Create a mapping of label to color + const labelColorMap = useMemo(() => { + return externalLabelColorMap || createLabelColorMap(allLabels); + }, [allLabels, externalLabelColorMap]); + + // Create a mapping of node IDs to their data + const nodeDataMap = useMemo(() => { + const result = new Map(); + + triplets.forEach((triplet) => { + result.set(triplet.source.id, triplet.source); + result.set(triplet.target.id, triplet.target); + }); + + return result; + }, [triplets]); + + // Function to get node color + const getNodeColor = useCallback( + (node: any): string => { + if (!node) { + return getNodeColorByLabel(null, themeMode === "dark", labelColorMap); + } + + // Get the full node data if we only have an ID + const nodeData = nodeDataMap.get(node.id) || node; + + // Extract primaryLabel from node data + const primaryLabel = nodeData.primaryLabel; + + return getNodeColorByLabel( + primaryLabel, + themeMode === "dark", + labelColorMap, + ); + }, + [labelColorMap, nodeDataMap, themeMode], + ); + + // Process graph data + const { nodes, links } = useMemo(() => { + const nodes = Array.from( + new Set(triplets.flatMap((t) => [t.source.id, t.target.id])), + ).map((id) => { + const nodeData = triplets.find( + (t) => t.source.id === id || t.target.id === id, + ); + const value = nodeData + ? nodeData.source.id === id + ? nodeData.source.value + : nodeData.target.value + : id; + return { + id, + value, + }; + }); + + const linkGroups = triplets.reduce( + (groups, triplet) => { + // Skip isolated node edges (they are just placeholders for showing isolated nodes) + if (triplet.relation.type === "_isolated_node_") { + return groups; + } + + let key = `${triplet.source.id}-${triplet.target.id}`; + const reverseKey = `${triplet.target.id}-${triplet.source.id}`; + + if (groups[reverseKey]) { + key = reverseKey; + } + + if (!groups[key]) { + groups[key] = { + source: triplet.source.id, + target: triplet.target.id, + relations: [], + relationData: [], + curveStrength: 0, + }; + } + groups[key].relations.push(triplet.relation.value); + groups[key].relationData.push(triplet.relation); + return groups; + }, + {} as Record< + string, + { + source: string; + target: string; + relations: string[]; + relationData: IdValue[]; + curveStrength: number; + } + >, + ); + + return { + nodes, + links: Object.values(linkGroups), + }; + }, [triplets]); + + // Initialize or update visualization - This will run only once on mount + useEffect(() => { + // Skip if already initialized or ref not available + if (isInitializedRef.current || !svgRef.current) return; + + // Mark as initialized to prevent re-running + isInitializedRef.current = true; + + const svgElement = d3.select(svgRef.current); + svgElement.selectAll("*").remove(); + + const g = svgElement.append("g"); + + // Drag handler function + const drag = ( + simulation: d3.Simulation, + ) => { + const originalSettings = { + velocityDecay: 0.4, + alphaDecay: 0.05, + }; + + function dragstarted(event: any) { + if (!event.active) { + simulation + .velocityDecay(0.7) + .alphaDecay(0.1) + .alphaTarget(0.1) + .restart(); + } + d3.select(event.sourceEvent.target.parentNode) + .select("circle") + .attr("stroke", theme.node.hover) + .attr("stroke-width", 3); + + event.subject.fx = event.subject.x; + event.subject.fy = event.subject.y; + } + + function dragged(event: any) { + event.subject.x = event.x; + event.subject.y = event.y; + event.subject.fx = event.x; + event.subject.fy = event.y; + } + + function dragended(event: any) { + if (!event.active) { + simulation + .velocityDecay(originalSettings.velocityDecay) + .alphaDecay(originalSettings.alphaDecay) + .alphaTarget(0); + } + + // Keep the node fixed at its final position + event.subject.fx = event.x; + event.subject.fy = event.y; + + d3.select(event.sourceEvent.target.parentNode) + .select("circle") + .attr("stroke", theme.node.stroke) + .attr("stroke-width", 2); + } + + return d3 + .drag() + .on("start", dragstarted) + .on("drag", dragged) + .on("end", dragended); + }; + + // Setup zoom behavior + const zoom = d3 + .zoom() + .scaleExtent([0.1, 4]) + .on("zoom", (event) => { + g.attr("transform", event.transform); + }); + + zoomRef.current = zoom; + // @ts-ignore + svgElement.call(zoom).call(zoom.transform, d3.zoomIdentity.scale(0.8)); + + // Identify which nodes are isolated (not in any links) + const nodeIdSet = new Set(nodes.map((n: any) => n.id)); + const linkedNodeIds = new Set(); + + links.forEach((link: any) => { + const sourceId = + typeof link.source === "string" ? link.source : link.source.id; + const targetId = + typeof link.target === "string" ? link.target : link.target.id; + linkedNodeIds.add(sourceId); + linkedNodeIds.add(targetId); + }); + + // Nodes that don't appear in any link are isolated + const isolatedNodeIds = new Set(); + nodeIdSet.forEach((nodeId: string) => { + if (!linkedNodeIds.has(nodeId)) { + isolatedNodeIds.add(nodeId); + } + }); + + // Create simulation with custom forces + const simulation = d3 + .forceSimulation(nodes as d3.SimulationNodeDatum[]) + .force( + "link", + d3 + .forceLink(links) + .id((d: any) => d.id) + .distance(200) + .strength(0.2), + ) + .force( + "charge", + d3 + .forceManyBody() + .strength((d: any) => { + // Use a less negative strength for isolated nodes + // to pull them closer to the center + return isolatedNodeIds.has(d.id) ? -500 : -3000; + }) + .distanceMin(20) + .distanceMax(500) + .theta(0.8), + ) + .force("center", d3.forceCenter(width / 2, height / 2).strength(0.05)) + .force( + "collide", + d3.forceCollide().radius(50).strength(0.3).iterations(5), + ) + // Add a special gravity force for isolated nodes to pull them toward the center + .force( + "isolatedGravity", + d3 + .forceRadial( + 100, // distance from center + width / 2, // center x + height / 2, // center y + ) + .strength((d: any) => (isolatedNodeIds.has(d.id) ? 0.15 : 0.01)), + ) + .velocityDecay(0.4) + .alphaDecay(0.05) + .alphaMin(0.001); + + simulationRef.current = simulation; + + const link = g.append("g").selectAll("g").data(links).join("g"); + + // Define reset functions + resetLinksRef.current = () => { + // @ts-ignore + link + .selectAll("path") + .attr("stroke", theme.link.stroke) + .attr("stroke-opacity", 0.6) + .attr("stroke-width", 1); + + // @ts-ignore + link.selectAll(".link-label rect").attr("fill", theme.link.label.bg); + // @ts-ignore + link.selectAll(".link-label text").attr("fill", theme.link.label.text); + }; + + // Create node groups + const node = g + .append("g") + .selectAll("g") + .data(nodes) + .join("g") + // @ts-ignore + .call(drag(simulation)) + .attr("cursor", "pointer"); + + resetNodesRef.current = () => { + // @ts-ignore + node + .selectAll("circle") + .attr("fill", (d: any) => getNodeColor(d)) + .attr("stroke", theme.node.stroke) + .attr("stroke-width", 1); + }; + + // Handle link click + handleLinkClickRef.current = (event: any, d: any, relation: IdValue) => { + if (event.stopPropagation) { + event.stopPropagation(); + } + + if (resetLinksRef.current) resetLinksRef.current(); + if (onEdgeClick) onEdgeClick(relation.id); + + // Reset all elements to default state + // @ts-ignore + link + .selectAll("path") + .attr("stroke", theme.link.stroke) + .attr("stroke-opacity", 0.6) + .attr("stroke-width", 1); + + // Reset non-highlighted nodes to their proper colors + // @ts-ignore + node + .selectAll("circle") + .attr("fill", (d: any) => getNodeColor(d)) + .attr("stroke", theme.node.stroke) + .attr("stroke-width", 1); + + // Find and highlight the corresponding path and label + const linkGroup = event.target?.closest("g") + ? d3.select(event.target.closest("g")) + : link.filter((l: any) => l === d); + + // @ts-ignore + linkGroup + // @ts-ignore + .selectAll("path") + .attr("stroke", theme.link.selected) + .attr("stroke-opacity", 1) + .attr("stroke-width", 2); + + // Update label styling + // @ts-ignore + linkGroup.select(".link-label rect").attr("fill", theme.link.selected); + // @ts-ignore + linkGroup.select(".link-label text").attr("fill", theme.node.text); + + // Highlight connected nodes + // @ts-ignore + node + .selectAll("circle") + .filter((n: any) => n.id === d.source.id || n.id === d.target.id) + .attr("fill", theme.node.selected) + .attr("stroke", theme.node.selected) + .attr("stroke-width", 2); + + const sourceNode = d.source; + const targetNode = d.target; + + // Calculate bounding box for the two connected nodes and the edge + if ( + sourceNode && + targetNode && + sourceNode.x !== undefined && + targetNode.x !== undefined + ) { + const padding = 100; // Increased padding for better view + const minX = Math.min(sourceNode.x, targetNode.x) - padding; + const minY = Math.min(sourceNode.y, targetNode.y) - padding; + const maxX = Math.max(sourceNode.x, targetNode.x) + padding; + const maxY = Math.max(sourceNode.y, targetNode.y) + padding; + + // Calculate transform to fit the connected nodes + const boundWidth = maxX - minX; + const boundHeight = maxY - minY; + const scale = + 0.9 * Math.min(width / boundWidth, height / boundHeight); + const midX = (minX + maxX) / 2; + const midY = (minY + maxY) / 2; + + if ( + isFinite(scale) && + isFinite(midX) && + isFinite(midY) && + zoomRef.current + ) { + const transform = d3.zoomIdentity + .translate(width / 2 - midX * scale, height / 2 - midY * scale) + .scale(scale); + + // Animate transition to new view + // @ts-ignore + svgElement + .transition() + .duration(750) + .ease(d3.easeCubicInOut) // Add easing for smoother transitions + .call(zoomRef.current.transform, transform); + } + } + }; + + // Create links with proper curve paths + link.each(function (d: any) { + const linkGroup = d3.select(this); + const relationCount = d.relations.length; + + // Calculate curve strengths based on number of relations + const baseStrength = 0.2; + const strengthStep = + relationCount > 1 ? baseStrength / (relationCount - 1) : 0; + + d.relations.forEach((relation: string, index: number) => { + const curveStrength = + relationCount > 1 ? -baseStrength + index * strengthStep * 2 : 0; + const fullRelation = d.relationData[index]; + + linkGroup + .append("path") + .attr("stroke", theme.link.stroke) + .attr("stroke-opacity", 0.6) + .attr("stroke-width", 1) + .attr("fill", "none") + .attr("data-curve-strength", curveStrength) + .attr("cursor", "pointer") + .attr( + "data-source", + typeof d.source === "object" ? d.source.id : d.source, + ) + .attr( + "data-target", + typeof d.target === "object" ? d.target.id : d.target, + ) + .on("click", (event) => { + if (handleLinkClickRef.current) { + handleLinkClickRef.current(event, d, fullRelation); + } + }); + + const labelGroup = linkGroup + .append("g") + .attr("class", "link-label") + .attr("cursor", "pointer") + .attr("data-curve-strength", curveStrength) + .on("click", (event) => { + if (handleLinkClickRef.current) { + handleLinkClickRef.current(event, d, fullRelation); + } + }); + + labelGroup + .append("rect") + .attr("fill", theme.link.label.bg) + .attr("rx", 4) + .attr("ry", 4) + .attr("opacity", 0.9); + + labelGroup + .append("text") + .attr("fill", theme.link.label.text) + .attr("font-size", "8px") + .attr("text-anchor", "middle") + .attr("dominant-baseline", "middle") + .attr("pointer-events", "none") + .text(relation); + + labelGroup.attr("data-curve-strength", curveStrength); + }); + }); + + // Create node circles + node + .append("circle") + .attr("r", 10) + .attr("fill", (d: any) => getNodeColor(d)) + .attr("stroke", theme.node.stroke) + .attr("stroke-width", 1) + .attr("filter", "drop-shadow(0 2px 4px rgba(0,0,0,0.2))") + .attr("data-id", (d: any) => d.id) + .attr("cursor", "pointer"); + + // Add node labels + node + .append("text") + .attr("x", 15) + .attr("y", "0.3em") + .attr("text-anchor", "start") + .attr("fill", theme.node.text) + .attr("font-weight", "500") + .attr("font-size", "12px") + .text((d: any) => d.value) + .attr("cursor", "pointer"); + + // Handle node clicks + function handleNodeClick(event: any, d: any) { + event.stopPropagation(); // Ensure the event doesn't bubble up + + if (resetLinksRef.current) resetLinksRef.current(); + if (resetNodesRef.current) resetNodesRef.current(); + + const selectedNodeId = d?.id; + + if (selectedNodeId && onNodeClick) { + onNodeClick(selectedNodeId); + + // Highlight the selected node + // @ts-ignore + node + .selectAll("circle") + .filter((n: any) => n.id === selectedNodeId) + .attr("fill", theme.node.selected) + .attr("stroke", theme.node.selected) + .attr("stroke-width", 2); + + // Find connected nodes and links + const connectedLinks: any[] = []; + const connectedNodes = new Set(); + + // Add the selected node to the connected nodes + const selectedNode = nodes.find((n: any) => n.id === selectedNodeId); + if (selectedNode) { + connectedNodes.add(selectedNode); + } + + // @ts-ignore + link.selectAll("path").each(function () { + const path = d3.select(this); + const source = path.attr("data-source"); + const target = path.attr("data-target"); + + if (source === selectedNodeId || target === selectedNodeId) { + const sourceNode = nodes.find((n: any) => n.id === source); + const targetNode = nodes.find((n: any) => n.id === target); + + if (sourceNode && targetNode) { + connectedLinks.push({ source: sourceNode, target: targetNode }); + connectedNodes.add(sourceNode); + connectedNodes.add(targetNode); + } + } + }); + + // Calculate bounding box of connected nodes + if (connectedNodes.size > 0 && zoomRef.current) { + let minX = Infinity, + minY = Infinity; + let maxX = -Infinity, + maxY = -Infinity; + + connectedNodes.forEach((node: any) => { + if (node.x !== undefined && node.y !== undefined) { + minX = Math.min(minX, node.x); + minY = Math.min(minY, node.y); + maxX = Math.max(maxX, node.x); + maxY = Math.max(maxY, node.y); + } + }); + + // Add padding + const padding = 50; + minX -= padding; + minY -= padding; + maxX += padding; + maxY += padding; + + // Calculate transform to fit connected nodes + const boundWidth = maxX - minX; + const boundHeight = maxY - minY; + const scale = + 0.9 * Math.min(width / boundWidth, height / boundHeight); + const midX = (minX + maxX) / 2; + const midY = (minY + maxY) / 2; + + if (isFinite(scale) && isFinite(midX) && isFinite(midY)) { + const transform = d3.zoomIdentity + .translate(width / 2 - midX * scale, height / 2 - midY * scale) + .scale(scale); + + // Animate transition to new view + // @ts-ignore + svgElement + .transition() + .duration(750) + .ease(d3.easeCubicInOut) // Add easing for smoother transitions + .call(zoomRef.current.transform, transform); + } + } + + // Highlight connected links + // @ts-ignore + link + .selectAll("path") + .attr("stroke", theme.link.stroke) + .attr("stroke-opacity", 0.6) + .attr("stroke-width", 1) + .filter(function () { + const path = d3.select(this); + return ( + path.attr("data-source") === selectedNodeId || + path.attr("data-target") === selectedNodeId + ); + }) + .attr("stroke", themeMode === "dark" ? "#ffffff" : colors.pink[600]) + .attr("stroke-width", 2); + } + } + + // Attach click handler to nodes + node.on("click", handleNodeClick); + + // Store a reference to the current SVG element + const svgRefCurrent = svgRef.current; + + // Add blur handler + svgElement.on("click", function (event) { + // Make sure we only handle clicks directly on the SVG element, not on its children + if (event.target === svgRefCurrent) { + if (onBlur) onBlur(); + if (resetLinksRef.current) resetLinksRef.current(); + if (resetNodesRef.current) resetNodesRef.current(); + } + }); + + // Update positions on simulation tick + simulation.on("tick", () => { + // Update link paths and labels + link.each(function (d: any) { + // Make sure d.source and d.target have x and y properties + if (!d.source.x && typeof d.source === "string") { + const sourceNode = nodes.find((n: any) => n.id === d.source); + // @ts-ignore - Node will have x,y properties from d3 simulation + if (sourceNode && sourceNode.x) { + d.source = sourceNode; + } + } + + if (!d.target.x && typeof d.target === "string") { + const targetNode = nodes.find((n: any) => n.id === d.target); + // @ts-ignore - Node will have x,y properties from d3 simulation + if (targetNode && targetNode.x) { + d.target = targetNode; + } + } + + const linkGroup = d3.select(this); + linkGroup.selectAll("path").each(function () { + const path = d3.select(this); + const curveStrength = +path.attr("data-curve-strength") || 0; + + // Handle self-referencing nodes + if (d.source.id === d.target.id) { + // Create an elliptical path for self-references + const radiusX = 40; + const radiusY = 90; + const offset = radiusY + 20; + + const cx = d.source.x; + const cy = d.source.y - offset; + const path_d = `M${d.source.x},${d.source.y} + C${cx - radiusX},${cy} + ${cx + radiusX},${cy} + ${d.source.x},${d.source.y}`; + path.attr("d", path_d); + + // Position the label + // @ts-ignore + const labelGroup = linkGroup + .selectAll(".link-label") + .filter(function () { + return ( + d3.select(this).attr("data-curve-strength") === + String(curveStrength) + ); + }); + + // Update both the group position and the rectangle/text within it + labelGroup.attr("transform", `translate(${cx}, ${cy - 10})`); + + // Update the rectangle and text positioning + // @ts-ignore + const text = labelGroup.select("text"); + // @ts-ignore + const rect = labelGroup.select("rect"); + const textBBox = (text.node() as SVGTextElement)?.getBBox(); + + if (textBBox) { + rect + .attr("x", -textBBox.width / 2 - 6) + .attr("y", -textBBox.height / 2 - 4) + .attr("width", textBBox.width + 12) + .attr("height", textBBox.height + 8); + + text.attr("x", 0).attr("y", 0); + } + } else { + const dx = d.target.x - d.source.x; + const dy = d.target.y - d.source.y; + const dr = Math.sqrt(dx * dx + dy * dy); + + const midX = (d.source.x + d.target.x) / 2; + const midY = (d.source.y + d.target.y) / 2; + const normalX = -dy / dr; + const normalY = dx / dr; + const curveMagnitude = dr * curveStrength; + const controlX = midX + normalX * curveMagnitude; + const controlY = midY + normalY * curveMagnitude; + + const path_d = `M${d.source.x},${d.source.y} Q${controlX},${controlY} ${d.target.x},${d.target.y}`; + path.attr("d", path_d); + + const pathNode = path.node() as SVGPathElement; + if (pathNode) { + const pathLength = pathNode.getTotalLength(); + const midPoint = pathNode.getPointAtLength(pathLength / 2); + + // @ts-ignore - Intentionally ignoring d3 selection type issues as in the Svelte version + const labelGroup = linkGroup + .selectAll(".link-label") + .filter(function () { + return ( + d3.select(this).attr("data-curve-strength") === + String(curveStrength) + ); + }); + + if (midPoint) { + // @ts-ignore - Intentionally ignoring d3 selection type issues as in the Svelte version + const text = labelGroup.select("text"); + // @ts-ignore - Intentionally ignoring d3 selection type issues as in the Svelte version + const rect = labelGroup.select("rect"); + const textBBox = (text.node() as SVGTextElement)?.getBBox(); + + if (textBBox) { + const angle = + (Math.atan2( + d.target.y - d.source.y, + d.target.x - d.source.x, + ) * + 180) / + Math.PI; + const rotationAngle = + angle > 90 || angle < -90 ? angle - 180 : angle; + + labelGroup.attr( + "transform", + `translate(${midPoint.x}, ${midPoint.y}) rotate(${rotationAngle})`, + ); + + rect + .attr("x", -textBBox.width / 2 - 6) + .attr("y", -textBBox.height / 2 - 4) + .attr("width", textBBox.width + 12) + .attr("height", textBBox.height + 8); + + text.attr("x", 0).attr("y", 0); + } + } + } + } + }); + }); + + // Update node positions + node.attr("transform", (d: any) => `translate(${d.x},${d.y})`); + }); + + // Handle zoom-to-fit on mount + let hasInitialized = false; + + simulation.on("end", () => { + if (hasInitialized || !zoomOnMount || !zoomRef.current) return; + hasInitialized = true; + + const bounds = g.node()?.getBBox(); + if (bounds) { + const fullWidth = width; + const fullHeight = height; + const currentWidth = bounds.width || 1; + const currentHeight = bounds.height || 1; + + // Only proceed if we have valid dimensions + if ( + currentWidth > 0 && + currentHeight > 0 && + fullWidth > 0 && + fullHeight > 0 + ) { + const midX = bounds.x + currentWidth / 2; + const midY = bounds.y + currentHeight / 2; + + // Calculate scale to fit with padding + const scale = + 0.8 * + Math.min(fullWidth / currentWidth, fullHeight / currentHeight); + + // Ensure we have valid numbers before creating transform + if (isFinite(midX) && isFinite(midY) && isFinite(scale)) { + const transform = d3.zoomIdentity + .translate( + fullWidth / 2 - midX * scale, + fullHeight / 2 - midY * scale, + ) + .scale(scale); + + // Smoothly animate to the new transform + // @ts-ignore + svgElement + .transition() + .duration(750) + .ease(d3.easeCubicInOut) // Add easing for smoother transitions + .call(zoomRef.current.transform, transform); + } else { + console.warn("Invalid transform values:", { midX, midY, scale }); + // Fallback to a simple center transform + const transform = d3.zoomIdentity + .translate(fullWidth / 2, fullHeight / 2) + .scale(0.8); + svgElement.call(zoomRef.current.transform, transform); + } + } + } + }); + + // Cleanup function - only called when component unmounts + return () => { + simulation.stop(); + // Save the ref to a variable before using it in cleanup + const currentSvgRef = svgRef.current; + if (currentSvgRef) { + d3.select(currentSvgRef).on("click", null); + } + isInitializedRef.current = false; + }; + // We're keeping the dependency array empty to ensure initialization runs only once on mount + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []); + + // This effect updates the graph theme colors when the theme changes + useEffect(() => { + // Skip if not initialized + if (!svgRef.current || !isInitializedRef.current) return; + + const svgElement = d3.select(svgRef.current); + + // Update background + svgElement.style("background-color", theme.background); + + // Update nodes - use getNodeColor for proper color assignment + svgElement + .selectAll("circle") + .attr("fill", (d: any) => getNodeColor(d)) + .attr("stroke", theme.node.stroke); + + // Update node labels + // @ts-ignore + svgElement.selectAll("text").attr("fill", theme.node.text); + + // Update links + // @ts-ignore + svgElement + .selectAll("path") + .attr("stroke", theme.link.stroke) + .attr("stroke-opacity", 0.6); + + // Update selected links if any + // @ts-ignore + svgElement + .selectAll("path.selected") + .attr("stroke", theme.link.selected) + .attr("stroke-opacity", 1); + + // Update link labels + // @ts-ignore + svgElement + .selectAll(".link-label rect") + .attr("fill", theme.link.label.bg); + // @ts-ignore + svgElement + .selectAll(".link-label text") + .attr("fill", theme.link.label.text); + + // This effect has many dependencies that would cause frequent re-renders + // We're disabling the exhaustive deps rule to prevent unnecessary re-renders + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [themeMode]); + + return ( + + ); + }, +); diff --git a/apps/webapp/app/components/graph/node-colors.ts b/apps/webapp/app/components/graph/node-colors.ts new file mode 100644 index 0000000..e1f8745 --- /dev/null +++ b/apps/webapp/app/components/graph/node-colors.ts @@ -0,0 +1,84 @@ +import colors from "tailwindcss/colors"; + +// Define a color palette for node coloring +export const nodeColorPalette = { + light: [ + colors.pink[500], // Entity (default) + colors.blue[500], + colors.emerald[500], + colors.amber[500], + colors.indigo[500], + colors.orange[500], + colors.teal[500], + colors.purple[500], + colors.cyan[500], + colors.lime[500], + colors.rose[500], + colors.violet[500], + colors.green[500], + colors.red[500], + ], + dark: [ + colors.pink[400], // Entity (default) + colors.blue[400], + colors.emerald[400], + colors.amber[400], + colors.indigo[400], + colors.orange[400], + colors.teal[400], + colors.purple[400], + colors.cyan[400], + colors.lime[400], + colors.rose[400], + colors.violet[400], + colors.green[400], + colors.red[400], + ], +}; + +// Function to create a map of label to color index +export function createLabelColorMap(labels: string[]) { + // Start with Entity mapped to first color + const result = new Map(); + result.set("Entity", 0); + + // Sort all non-Entity labels alphabetically for consistent color assignment + const sortedLabels = labels + .filter((label) => label !== "Entity") + .sort((a, b) => a.localeCompare(b)); + + // Map each unique label to a color index + let nextIndex = 1; + sortedLabels.forEach((label) => { + if (!result.has(label)) { + result.set(label, nextIndex % nodeColorPalette.light.length); + nextIndex++; + } + }); + + return result; +} + +// Get color for a label directly +export function getNodeColor( + label: string | null | undefined, + isDarkMode: boolean, + labelColorMap: Map, +): string { + if (!label) { + return isDarkMode ? nodeColorPalette.dark[0] : nodeColorPalette.light[0]; + } + + // If label is "Entity" or not found in the map, return default color + if (label === "Entity" || !labelColorMap.has(label)) { + return isDarkMode ? nodeColorPalette.dark[0] : nodeColorPalette.light[0]; + } + + // Get the color index for this label + const colorIndex = labelColorMap.get(label) || 0; + + // Return the color from the appropriate theme palette + return isDarkMode + ? nodeColorPalette.dark[colorIndex] + : nodeColorPalette.light[colorIndex]; +} diff --git a/apps/webapp/app/components/graph/type.ts b/apps/webapp/app/components/graph/type.ts new file mode 100644 index 0000000..98ffdcc --- /dev/null +++ b/apps/webapp/app/components/graph/type.ts @@ -0,0 +1,82 @@ +export interface Node { + uuid: string; + name: string; + summary?: string; + labels?: string[]; + attributes?: Record; + created_at: string; + updated_at: string; +} + +export interface Edge { + uuid: string; + source_node_uuid: string; + target_node_uuid: string; + type: string; + name: string; + fact?: string; + episodes?: string[]; + created_at: string; + updated_at: string; + valid_at?: string; + expired_at?: string; + invalid_at?: string; +} + +export interface RawTriplet { + sourceNode: Node; + edge: Edge; + targetNode: Node; +} + +export interface GraphNode extends Node { + id: string; + value: string; + primaryLabel?: string; +} + +export interface GraphEdge extends Edge { + id: string; + value: string; +} + +export interface GraphTriplet { + source: GraphNode; + relation: GraphEdge; + target: GraphNode; +} + +export interface IdValue { + id: string; + value: string; +} + +// Graph visualization types +export interface GraphNode extends Node { + id: string; + value: string; +} + +export interface GraphEdge extends Edge { + id: string; + value: string; +} + +export interface GraphTriplet { + source: GraphNode; + relation: GraphEdge; + target: GraphNode; +} + +// Popup content types for UI +export interface NodePopupContent { + id: string; + node: Node; +} + +export interface EdgePopupContent { + id: string; + source: Node; + relation: Edge; + target: Node; +} diff --git a/apps/webapp/app/components/layout/LoginPageLayout.tsx b/apps/webapp/app/components/layout/LoginPageLayout.tsx index eb946f1..6462bb1 100644 --- a/apps/webapp/app/components/layout/LoginPageLayout.tsx +++ b/apps/webapp/app/components/layout/LoginPageLayout.tsx @@ -7,9 +7,9 @@ export function LoginPageLayout({ children }: { children: React.ReactNode }) { return (
-
- - +
+ + C.O.R.E
diff --git a/apps/webapp/app/components/logo/logo.tsx b/apps/webapp/app/components/logo/logo.tsx index 95fc0b7..a854395 100644 --- a/apps/webapp/app/components/logo/logo.tsx +++ b/apps/webapp/app/components/logo/logo.tsx @@ -9,59 +9,89 @@ export interface LogoProps { export default function StaticLogo({ width, height }: LogoProps) { const [theme] = useTheme(); - if (theme === Theme.LIGHT) { + if (theme === Theme.DARK) { return ( - - - - - - - - + + + + + + + + + + ); } return ( - - - - - - - - - + + + + + + + + + + ); } diff --git a/apps/webapp/app/components/sidebar/app-sidebar.tsx b/apps/webapp/app/components/sidebar/app-sidebar.tsx new file mode 100644 index 0000000..20aaac2 --- /dev/null +++ b/apps/webapp/app/components/sidebar/app-sidebar.tsx @@ -0,0 +1,70 @@ +import * as React from "react"; + +import { + Sidebar, + SidebarContent, + SidebarFooter, + SidebarHeader, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "../ui/sidebar"; +import { DashboardIcon } from "@radix-ui/react-icons"; +import { Code, LucideFileStack } from "lucide-react"; +import { NavMain } from "./nav-main"; +import { useUser } from "~/hooks/useUser"; +import { NavUser } from "./nav-user"; +import { useWorkspace } from "~/hooks/useWorkspace"; + +const data = { + user: { + name: "shadcn", + email: "m@example.com", + avatar: "/avatars/shadcn.jpg", + }, + navMain: [ + { + title: "Dashboard", + url: "#", + icon: DashboardIcon, + }, + { + title: "API", + url: "#", + icon: Code, + }, + ], +}; + +export function AppSidebar({ ...props }: React.ComponentProps) { + const user = useUser(); + const workspace = useWorkspace(); + + return ( + + + + + + + + {workspace.name} + + + + + + + + + + + + + + + ); +} diff --git a/apps/webapp/app/components/sidebar/nav-main.tsx b/apps/webapp/app/components/sidebar/nav-main.tsx new file mode 100644 index 0000000..f7703ff --- /dev/null +++ b/apps/webapp/app/components/sidebar/nav-main.tsx @@ -0,0 +1,38 @@ +import { useUser } from "~/hooks/useUser"; +import { + SidebarFooter, + SidebarGroup, + SidebarGroupContent, + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, +} from "../ui/sidebar"; +import { NavUser } from "./nav-user"; + +export const NavMain = ({ + items, +}: { + items: { + title: string; + url: string; + icon?: any; + }[]; +}) => { + return ( + + + + + {items.map((item) => ( + + + {item.icon && } + {item.title} + + + ))} + + + + ); +}; diff --git a/apps/webapp/app/components/sidebar/nav-user.tsx b/apps/webapp/app/components/sidebar/nav-user.tsx new file mode 100644 index 0000000..086cddc --- /dev/null +++ b/apps/webapp/app/components/sidebar/nav-user.tsx @@ -0,0 +1,83 @@ +import { DotIcon, LogOut, User as UserI } from "lucide-react"; +import { Avatar, AvatarFallback, AvatarImage, AvatarText } from "../ui/avatar"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuGroup, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "../ui/dropdown-menu"; +import { + SidebarMenu, + SidebarMenuButton, + SidebarMenuItem, + useSidebar, +} from "../ui/sidebar"; +import type { User } from "~/models/user.server"; +import { useUser } from "~/hooks/useUser"; + +export function NavUser({ user }: { user: User }) { + const { isMobile } = useSidebar(); + + return ( + + + + + + +
+ {user.name} + + {user.email} + +
+
+
+ + +
+ + + {user.name} + + +
+ {user.name} + + {user.email} + +
+
+
+ + + + + Account + + + + + + Log out + +
+
+
+
+ ); +} diff --git a/apps/webapp/app/components/ui/avatar.tsx b/apps/webapp/app/components/ui/avatar.tsx new file mode 100644 index 0000000..e08f8b3 --- /dev/null +++ b/apps/webapp/app/components/ui/avatar.tsx @@ -0,0 +1,87 @@ +import * as AvatarPrimitive from "@radix-ui/react-avatar"; +import React from "react"; + +import { getTailwindColor } from "./color-utils"; +import { cn } from "../../lib/utils"; + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +Avatar.displayName = AvatarPrimitive.Root.displayName; + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarImage.displayName = AvatarPrimitive.Image.displayName; + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName; + +// Function to get the first two letters +export const getInitials = (name: string, noOfChar?: number | undefined) => { + if (!name) { + return ""; + } + const words = name.split(" "); + return words + .map((word) => word.charAt(0)) + .filter((char) => char !== "") + .slice(0, noOfChar ? noOfChar : 2) + .join("") + .toUpperCase(); +}; + +const AvatarText = ({ + text, + className, + noOfChar, +}: { + text: string; + className?: string; + noOfChar?: number; +}) => { + return ( + + + + {getInitials(text, noOfChar)} + + + ); +}; + +export { Avatar, AvatarImage, AvatarFallback, AvatarText }; diff --git a/apps/webapp/app/components/ui/button.tsx b/apps/webapp/app/components/ui/button.tsx index 4235f9f..06eeb5e 100644 --- a/apps/webapp/app/components/ui/button.tsx +++ b/apps/webapp/app/components/ui/button.tsx @@ -11,7 +11,8 @@ const buttonVariants = cva( { variants: { variant: { - default: "bg-primary text-white shadow hover:bg-primary/90 dark:hover:bg-primary/90", + default: + "bg-primary text-white shadow hover:bg-primary/90 dark:hover:bg-primary/90", destructive: "text-red-500 bg-grayAlpha-100 border-none", outline: "border border-border shadow-sm hover:bg-gray-100 shadow-none", secondary: "bg-grayAlpha-100 border-none", @@ -37,7 +38,7 @@ const buttonVariants = cva( size: "default", full: false, }, - } + }, ); export interface ButtonProps @@ -62,7 +63,7 @@ const Button = React.forwardRef( disabled, ...props }, - ref + ref, ) => { const Comp = asChild ? Slot : "button"; @@ -70,7 +71,7 @@ const Button = React.forwardRef( ( {children} ); - } + }, ); Button.displayName = "Button"; diff --git a/apps/webapp/app/components/ui/card.tsx b/apps/webapp/app/components/ui/card.tsx new file mode 100644 index 0000000..68b766e --- /dev/null +++ b/apps/webapp/app/components/ui/card.tsx @@ -0,0 +1,80 @@ +import React from "react"; + +import { cn } from "../../lib/utils"; + +const Card = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +Card.displayName = "Card"; + +const CardHeader = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardHeader.displayName = "CardHeader"; + +const CardTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardTitle.displayName = "CardTitle"; + +const CardDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardDescription.displayName = "CardDescription"; + +const CardContent = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +

+)); +CardContent.displayName = "CardContent"; + +const CardFooter = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)); +CardFooter.displayName = "CardFooter"; + +export { + Card, + CardHeader, + CardFooter, + CardTitle, + CardDescription, + CardContent, +}; diff --git a/apps/webapp/app/components/ui/color-utils.ts b/apps/webapp/app/components/ui/color-utils.ts new file mode 100644 index 0000000..df06ff9 --- /dev/null +++ b/apps/webapp/app/components/ui/color-utils.ts @@ -0,0 +1,43 @@ +/** + * Generates an OKLCH color string with fixed lightness, chroma, and a random hue. + * @returns {string} - The generated OKLCH color string. + */ +export function generateOklchColor(): string { + // Generate a random number between 30 and 360 for the hue + const hue = Math.floor(Math.random() * (360 - 30 + 1)) + 30; + + // Fixed lightness and chroma values + const lightness = 66; + const chroma = 0.1835; + + // Construct the OKLCH color string + const oklchColor = `oklch(${lightness}% ${chroma} ${hue})`; + + return oklchColor; +} + +export function getTailwindColor(name: string): string { + // Generate a hash value for the input name + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + // Ensure hash value is within the range of colors array + const index = Math.abs(hash) % 12; + + return `var(--custom-color-${index + 1})`; +} + +export function getTeamColor(name: string): string { + // Generate a hash value for the input name + let hash = 0; + for (let i = 0; i < name.length; i++) { + hash = name.charCodeAt(i) + ((hash << 5) - hash); + } + + // Ensure hash value is within the range of colors array + const index = Math.abs(hash) % 3; + + return `var(--team-color-${index + 1})`; +} diff --git a/apps/webapp/app/components/ui/dropdown-menu.tsx b/apps/webapp/app/components/ui/dropdown-menu.tsx new file mode 100644 index 0000000..7a57cbc --- /dev/null +++ b/apps/webapp/app/components/ui/dropdown-menu.tsx @@ -0,0 +1,204 @@ +import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu"; +import { + CheckIcon, + ChevronRightIcon, + DotFilledIcon, +} from "@radix-ui/react-icons"; + +import React from "react"; + +import { cn } from "../../lib/utils"; + +const DropdownMenu = DropdownMenuPrimitive.Root; + +const DropdownMenuTrigger = DropdownMenuPrimitive.Trigger; + +const DropdownMenuGroup = DropdownMenuPrimitive.Group; + +const DropdownMenuPortal = DropdownMenuPrimitive.Portal; + +const DropdownMenuSub = DropdownMenuPrimitive.Sub; + +const DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup; + +const DropdownMenuSubTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, children, ...props }, ref) => ( + + {children} + + +)); +DropdownMenuSubTrigger.displayName = + DropdownMenuPrimitive.SubTrigger.displayName; + +const DropdownMenuSubContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSubContent.displayName = + DropdownMenuPrimitive.SubContent.displayName; + +const DropdownMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, sideOffset = 4, ...props }, ref) => ( + + + +)); +DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName; + +const DropdownMenuItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName; + +const DropdownMenuCheckboxItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, checked, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuCheckboxItem.displayName = + DropdownMenuPrimitive.CheckboxItem.displayName; + +const DropdownMenuRadioItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + {children} + +)); +DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName; + +const DropdownMenuLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef & { + inset?: boolean; + } +>(({ className, inset, ...props }, ref) => ( + +)); +DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName; + +const DropdownMenuSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName; + +const DropdownMenuShortcut = ({ + className, + ...props +}: React.HTMLAttributes) => { + return ( + + ); +}; +DropdownMenuShortcut.displayName = "DropdownMenuShortcut"; + +export { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuCheckboxItem, + DropdownMenuRadioItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuShortcut, + DropdownMenuGroup, + DropdownMenuPortal, + DropdownMenuSub, + DropdownMenuSubContent, + DropdownMenuSubTrigger, + DropdownMenuRadioGroup, +}; diff --git a/apps/webapp/app/components/ui/input.tsx b/apps/webapp/app/components/ui/input.tsx new file mode 100644 index 0000000..6a891a8 --- /dev/null +++ b/apps/webapp/app/components/ui/input.tsx @@ -0,0 +1,25 @@ +import React from "react"; + +import { cn } from "../../lib/utils"; + +export interface InputProps + extends React.InputHTMLAttributes {} + +const Input = React.forwardRef( + ({ className, type, ...props }, ref) => { + return ( + + ); + }, +); +Input.displayName = "Input"; + +export { Input }; diff --git a/apps/webapp/app/components/ui/multi-select.tsx b/apps/webapp/app/components/ui/multi-select.tsx new file mode 100644 index 0000000..6d1c4ab --- /dev/null +++ b/apps/webapp/app/components/ui/multi-select.tsx @@ -0,0 +1,76 @@ +"use client"; + +import { Button } from "./button"; +import { + DropdownMenu, + DropdownMenuCheckboxItem, + DropdownMenuContent, + DropdownMenuTrigger, +} from "./dropdown-menu"; +import { ChevronDown } from "lucide-react"; +import { type Dispatch, type SetStateAction } from "react"; + +type Option = { label: string; value: string }; + +interface ISelectProps { + placeholder: string; + options: Option[]; + selectedOptions: string[]; + setSelectedOptions: Dispatch>; +} +const MultiSelect = ({ + placeholder, + options: values, + selectedOptions: selectedItems, + setSelectedOptions: setSelectedItems, +}: ISelectProps) => { + const handleSelectChange = (value: string) => { + if (!selectedItems.includes(value)) { + setSelectedItems((prev) => [...prev, value]); + } else { + const referencedArray = [...selectedItems]; + const indexOfItemToBeRemoved = referencedArray.indexOf(value); + referencedArray.splice(indexOfItemToBeRemoved, 1); + setSelectedItems(referencedArray); + } + }; + + const isOptionSelected = (value: string): boolean => { + return selectedItems.includes(value) ? true : false; + }; + + return ( + <> + + + + + e.preventDefault()} + > + {values.map((value: ISelectProps["options"][0], index: number) => { + return ( + e.preventDefault()} + key={index} + checked={isOptionSelected(value.value)} + onCheckedChange={() => handleSelectChange(value.value)} + > + {value.label} + + ); + })} + + + + ); +}; + +export default MultiSelect; diff --git a/apps/webapp/app/components/ui/popover.tsx b/apps/webapp/app/components/ui/popover.tsx new file mode 100644 index 0000000..fc296fa --- /dev/null +++ b/apps/webapp/app/components/ui/popover.tsx @@ -0,0 +1,38 @@ +import * as PopoverPrimitive from "@radix-ui/react-popover"; +import React from "react"; + +import { cn } from "../../lib/utils"; + +const Popover = PopoverPrimitive.Root; + +const PopoverTrigger = PopoverPrimitive.Trigger; + +const PopoverAnchor = PopoverPrimitive.Anchor; +const PopoverPortal = PopoverPrimitive.Portal; + +const PopoverContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, align = "center", sideOffset = 4, ...props }, ref) => ( + <> + + +)); +PopoverContent.displayName = PopoverPrimitive.Content.displayName; + +export { + Popover, + PopoverTrigger, + PopoverContent, + PopoverAnchor, + PopoverPortal, +}; diff --git a/apps/webapp/app/components/ui/select.tsx b/apps/webapp/app/components/ui/select.tsx new file mode 100644 index 0000000..0e9dd9d --- /dev/null +++ b/apps/webapp/app/components/ui/select.tsx @@ -0,0 +1,174 @@ +import { + CheckIcon, + ChevronDownIcon, + ChevronUpIcon, +} from "@radix-ui/react-icons"; +import * as SelectPrimitive from "@radix-ui/react-select"; +import React from "react"; + +import { cn } from "../../lib/utils"; +import { ChevronDown } from "lucide-react"; + +const Select = SelectPrimitive.Root; + +const SelectGroup = SelectPrimitive.Group; + +const SelectValue = SelectPrimitive.Value; + +interface SelectTriggerProps + extends React.ComponentPropsWithoutRef { + showIcon: boolean; +} + +const SelectTrigger = React.forwardRef< + React.ElementRef, + SelectTriggerProps +>(({ className, children, showIcon = true, ...props }, ref) => ( + span]:line-clamp-1", + className, + )} + {...props} + > + {children} + {showIcon && ( + + + + )} + +)); +SelectTrigger.displayName = SelectPrimitive.Trigger.displayName; + +const SelectScrollUpButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName; + +const SelectScrollDownButton = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + + + +)); +SelectScrollDownButton.displayName = + SelectPrimitive.ScrollDownButton.displayName; + +const SelectContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, position = "popper", ...props }, ref) => ( + + + + + {children} + + + + +)); +SelectContent.displayName = SelectPrimitive.Content.displayName; + +const SelectLabel = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectLabel.displayName = SelectPrimitive.Label.displayName; + +const SelectItem = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + + + + + + + {children} + + +)); +SelectItem.displayName = SelectPrimitive.Item.displayName; + +const SelectSeparator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SelectSeparator.displayName = SelectPrimitive.Separator.displayName; + +export { + Select, + SelectGroup, + SelectValue, + SelectTrigger, + SelectContent, + SelectLabel, + SelectItem, + SelectSeparator, + SelectScrollUpButton, + SelectScrollDownButton, +}; diff --git a/apps/webapp/app/components/ui/separator.tsx b/apps/webapp/app/components/ui/separator.tsx new file mode 100644 index 0000000..9a3fb96 --- /dev/null +++ b/apps/webapp/app/components/ui/separator.tsx @@ -0,0 +1,29 @@ +import * as SeparatorPrimitive from "@radix-ui/react-separator"; +import React from "react"; + +import { cn } from "../../lib/utils"; + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref, + ) => ( + + ), +); +Separator.displayName = SeparatorPrimitive.Root.displayName; + +export { Separator }; diff --git a/apps/webapp/app/components/ui/sheet.tsx b/apps/webapp/app/components/ui/sheet.tsx new file mode 100644 index 0000000..104a951 --- /dev/null +++ b/apps/webapp/app/components/ui/sheet.tsx @@ -0,0 +1,138 @@ +import * as SheetPrimitive from "@radix-ui/react-dialog"; +import { Cross2Icon } from "@radix-ui/react-icons"; +import { cva, type VariantProps } from "class-variance-authority"; +import React from "react"; + +import { cn } from "../../lib/utils"; + +const Sheet = SheetPrimitive.Root; + +const SheetTrigger = SheetPrimitive.Trigger; + +const SheetClose = SheetPrimitive.Close; + +const SheetPortal = SheetPrimitive.Portal; + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName; + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:duration-300 data-[state=open]:duration-500", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + }, +); + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + {children} + + + Close + + + +)); +SheetContent.displayName = SheetPrimitive.Content.displayName; + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetHeader.displayName = "SheetHeader"; + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+); +SheetFooter.displayName = "SheetFooter"; + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetTitle.displayName = SheetPrimitive.Title.displayName; + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)); +SheetDescription.displayName = SheetPrimitive.Description.displayName; + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +}; diff --git a/apps/webapp/app/components/ui/sidebar.tsx b/apps/webapp/app/components/ui/sidebar.tsx new file mode 100644 index 0000000..d4d62eb --- /dev/null +++ b/apps/webapp/app/components/ui/sidebar.tsx @@ -0,0 +1,724 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, VariantProps } from "class-variance-authority"; +import { PanelLeftIcon } from "lucide-react"; + +import { useIsMobile } from "~/hooks/use-mobile"; +import { cn } from "~/lib/utils"; +import { Button } from "~/components/ui/button"; +import { Input } from "~/components/ui/input"; +import { Separator } from "~/components/ui/separator"; +import { + Sheet, + SheetContent, + SheetDescription, + SheetHeader, + SheetTitle, +} from "~/components/ui/sheet"; +import { Skeleton } from "~/components/ui/skeleton"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "~/components/ui/tooltip"; + +const SIDEBAR_COOKIE_NAME = "sidebar_state"; +const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7; +const SIDEBAR_WIDTH = "16rem"; +const SIDEBAR_WIDTH_MOBILE = "18rem"; +const SIDEBAR_WIDTH_ICON = "3rem"; +const SIDEBAR_KEYBOARD_SHORTCUT = "b"; + +type SidebarContextProps = { + state: "expanded" | "collapsed"; + open: boolean; + setOpen: (open: boolean) => void; + openMobile: boolean; + setOpenMobile: (open: boolean) => void; + isMobile: boolean; + toggleSidebar: () => void; +}; + +const SidebarContext = React.createContext(null); + +function useSidebar() { + const context = React.useContext(SidebarContext); + if (!context) { + throw new Error("useSidebar must be used within a SidebarProvider."); + } + + return context; +} + +function SidebarProvider({ + defaultOpen = true, + open: openProp, + onOpenChange: setOpenProp, + className, + style, + children, + ...props +}: React.ComponentProps<"div"> & { + defaultOpen?: boolean; + open?: boolean; + onOpenChange?: (open: boolean) => void; +}) { + const isMobile = useIsMobile(); + const [openMobile, setOpenMobile] = React.useState(false); + + // This is the internal state of the sidebar. + // We use openProp and setOpenProp for control from outside the component. + const [_open, _setOpen] = React.useState(defaultOpen); + const open = openProp ?? _open; + const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === "function" ? value(open) : value; + if (setOpenProp) { + setOpenProp(openState); + } else { + _setOpen(openState); + } + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}`; + }, + [setOpenProp, open], + ); + + // Helper to toggle the sidebar. + const toggleSidebar = React.useCallback(() => { + return isMobile ? setOpenMobile((open) => !open) : setOpen((open) => !open); + }, [isMobile, setOpen, setOpenMobile]); + + // Adds a keyboard shortcut to toggle the sidebar. + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ( + event.key === SIDEBAR_KEYBOARD_SHORTCUT && + (event.metaKey || event.ctrlKey) + ) { + event.preventDefault(); + toggleSidebar(); + } + }; + + window.addEventListener("keydown", handleKeyDown); + return () => window.removeEventListener("keydown", handleKeyDown); + }, [toggleSidebar]); + + // We add a state so that we can do data-state="expanded" or "collapsed". + // This makes it easier to style the sidebar with Tailwind classes. + const state = open ? "expanded" : "collapsed"; + + const contextValue = React.useMemo( + () => ({ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + }), + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar], + ); + + return ( + + +
+ {children} +
+
+
+ ); +} + +function Sidebar({ + side = "left", + variant = "sidebar", + collapsible = "offcanvas", + className, + children, + ...props +}: React.ComponentProps<"div"> & { + side?: "left" | "right"; + variant?: "sidebar" | "floating" | "inset"; + collapsible?: "offcanvas" | "icon" | "none"; +}) { + const { isMobile, state, openMobile, setOpenMobile } = useSidebar(); + + if (collapsible === "none") { + return ( +
+ {children} +
+ ); + } + + if (isMobile) { + return ( + + + + Sidebar + Displays the mobile sidebar. + +
{children}
+
+
+ ); + } + + return ( +
+ {/* This is what handles the sidebar gap on desktop */} +
+ +
+ ); +} + +function SidebarTrigger({ + className, + onClick, + ...props +}: React.ComponentProps) { + const { toggleSidebar } = useSidebar(); + + return ( + + ); +} + +function SidebarRail({ className, ...props }: React.ComponentProps<"button">) { + const { toggleSidebar } = useSidebar(); + + return ( + +
+ + + + + ); +} diff --git a/apps/webapp/app/routes/ingest.tsx b/apps/webapp/app/routes/ingest.tsx index eaf06ea..dba820c 100644 --- a/apps/webapp/app/routes/ingest.tsx +++ b/apps/webapp/app/routes/ingest.tsx @@ -1,10 +1,10 @@ -import { EpisodeType } from "@recall/types"; -import { json } from "@remix-run/node"; +import { EpisodeType } from "@core/types"; +import { json, LoaderFunctionArgs } from "@remix-run/node"; import { z } from "zod"; import { createActionApiRoute } from "~/services/routeBuilders/apiBuilder.server"; import { getUserQueue } from "~/lib/ingest.queue"; import { prisma } from "~/db.server"; -import { IngestionStatus } from "@recall/database"; +import { IngestionStatus } from "@core/database"; export const IngestBodyRequest = z.object({ name: z.string(), diff --git a/apps/webapp/app/routes/login.tsx b/apps/webapp/app/routes/login.tsx index f4ed066..a622d57 100644 --- a/apps/webapp/app/routes/login.tsx +++ b/apps/webapp/app/routes/login.tsx @@ -1,10 +1,8 @@ import { type LoaderFunctionArgs } from "@remix-run/node"; -import { Form } from "@remix-run/react"; + import { redirect, typedjson, useTypedLoaderData } from "remix-typedjson"; import { LoginPageLayout } from "~/components/layout/LoginPageLayout"; import { Fieldset } from "~/components/ui/Fieldset"; -import { Header1 } from "~/components/ui/Headers"; -import { Paragraph } from "~/components/ui/Paragraph"; import { isGoogleAuthSupported } from "~/services/auth.server"; import { setRedirectTo } from "~/services/redirectTo.server"; import { getUserId } from "~/services/session.server"; @@ -12,6 +10,14 @@ import { commitSession } from "~/services/sessionStorage.server"; import { requestUrl } from "~/utils/requestUrl.server"; import { RiGoogleLine } from "@remixicon/react"; +import { + Card, + CardContent, + CardDescription, + CardHeader, + CardTitle, +} from "~/components/ui/card"; +import { Button } from "~/components/ui"; export async function loader({ request }: LoaderFunctionArgs) { const userId = await getUserId(request); @@ -43,29 +49,33 @@ export default function LoginPage() { const data = useTypedLoaderData(); return ( -
-
- - Welcome - - - Create an account or login - -
-
- {data.showGoogleAuth && ( - - )} -
-
-
-
+ + + + Login to your account + Create an account or login + + + +
+
+ {data.showGoogleAuth && ( + + )} +
+
+
+
+
); } diff --git a/apps/webapp/app/routes/logout.tsx b/apps/webapp/app/routes/logout.tsx new file mode 100644 index 0000000..48af79f --- /dev/null +++ b/apps/webapp/app/routes/logout.tsx @@ -0,0 +1,18 @@ +import { type ActionFunction, type LoaderFunction } from "@remix-run/node"; +import { redirect } from "remix-typedjson"; + +import { sessionStorage } from "~/services/sessionStorage.server"; + +export const action: ActionFunction = async ({ request }) => { + let session = await sessionStorage.getSession(request.headers.get("cookie")); + return redirect("/login", { + headers: { "Set-Cookie": await sessionStorage.destroySession(session) }, + }); +}; + +export const loader: LoaderFunction = async ({ request }) => { + let session = await sessionStorage.getSession(request.headers.get("cookie")); + return redirect("/login", { + headers: { "Set-Cookie": await sessionStorage.destroySession(session) }, + }); +}; diff --git a/apps/webapp/app/services/graphModels/entity.ts b/apps/webapp/app/services/graphModels/entity.ts index 0fc3b8f..975b2f8 100644 --- a/apps/webapp/app/services/graphModels/entity.ts +++ b/apps/webapp/app/services/graphModels/entity.ts @@ -1,4 +1,4 @@ -import type { EntityNode } from "@recall/types"; +import type { EntityNode } from "@core/types"; import { runQuery } from "~/lib/neo4j.server"; export async function saveEntity(entity: EntityNode): Promise { diff --git a/apps/webapp/app/services/graphModels/episode.ts b/apps/webapp/app/services/graphModels/episode.ts index b6f8da3..6ec7464 100644 --- a/apps/webapp/app/services/graphModels/episode.ts +++ b/apps/webapp/app/services/graphModels/episode.ts @@ -1,5 +1,5 @@ import { runQuery } from "~/lib/neo4j.server"; -import type { EpisodicNode } from "@recall/types"; +import type { EpisodicNode } from "@core/types"; export async function saveEpisode(episode: EpisodicNode): Promise { const query = ` diff --git a/apps/webapp/app/services/graphModels/statement.ts b/apps/webapp/app/services/graphModels/statement.ts index fb20f2e..b982ebc 100644 --- a/apps/webapp/app/services/graphModels/statement.ts +++ b/apps/webapp/app/services/graphModels/statement.ts @@ -3,7 +3,7 @@ import type { EpisodicNode, StatementNode, Triple, -} from "@recall/types"; +} from "@core/types"; import { runQuery } from "~/lib/neo4j.server"; import { saveEntity } from "./entity"; import { saveEpisode } from "./episode"; diff --git a/apps/webapp/app/services/knowledgeGraph.server.ts b/apps/webapp/app/services/knowledgeGraph.server.ts index 58acce4..8ccc586 100644 --- a/apps/webapp/app/services/knowledgeGraph.server.ts +++ b/apps/webapp/app/services/knowledgeGraph.server.ts @@ -9,7 +9,7 @@ import { type EpisodicNode, type StatementNode, type Triple, -} from "@recall/types"; +} from "@core/types"; import { logger } from "./logger.service"; import crypto from "crypto"; import { dedupeNodes, extractMessage, extractText } from "./prompts/nodes"; diff --git a/apps/webapp/app/services/personalAccessToken.server.ts b/apps/webapp/app/services/personalAccessToken.server.ts index fb054cd..21d6174 100644 --- a/apps/webapp/app/services/personalAccessToken.server.ts +++ b/apps/webapp/app/services/personalAccessToken.server.ts @@ -1,4 +1,4 @@ -import { type PersonalAccessToken } from "@recall/database"; +import { type PersonalAccessToken } from "@core/database"; import { customAlphabet, nanoid } from "nanoid"; import nodeCrypto from "node:crypto"; import { z } from "zod"; diff --git a/apps/webapp/app/services/prompts/statements.ts b/apps/webapp/app/services/prompts/statements.ts index 1101c88..a03054e 100644 --- a/apps/webapp/app/services/prompts/statements.ts +++ b/apps/webapp/app/services/prompts/statements.ts @@ -1,4 +1,4 @@ -import { type Triple } from "@recall/types"; +import { type Triple } from "@core/types"; import { type CoreMessage } from "ai"; /** diff --git a/apps/webapp/app/services/session.server.ts b/apps/webapp/app/services/session.server.ts index b841be4..309c2c9 100644 --- a/apps/webapp/app/services/session.server.ts +++ b/apps/webapp/app/services/session.server.ts @@ -2,6 +2,7 @@ import { redirect } from "@remix-run/node"; import { getUserById } from "~/models/user.server"; import { sessionStorage } from "./sessionStorage.server"; import { getImpersonationId } from "./impersonation.server"; +import { getWorkspaceByUser } from "~/models/workspace.server"; export async function getUserId(request: Request): Promise { const impersonatedUserId = await getImpersonationId(request); @@ -24,6 +25,46 @@ export async function getUser(request: Request) { throw await logout(request); } +export async function requireUserId(request: Request, redirectTo?: string) { + const userId = await getUserId(request); + if (!userId) { + const url = new URL(request.url); + const searchParams = new URLSearchParams([ + ["redirectTo", redirectTo ?? `${url.pathname}${url.search}`], + ]); + throw redirect(`/login?${searchParams}`); + } + return userId; +} + +export async function requireUser(request: Request) { + const userId = await requireUserId(request); + + const impersonationId = await getImpersonationId(request); + const user = await getUserById(userId); + if (user) { + return { + id: user.id, + email: user.email, + name: user.name, + displayName: user.displayName, + avatarUrl: user.avatarUrl, + admin: user.admin, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + confirmedBasicDetails: user.confirmedBasicDetails, + isImpersonating: !!impersonationId, + }; + } + + throw await logout(request); +} + +export async function requireWorkpace(request: Request) { + const userId = await requireUserId(request); + return getWorkspaceByUser(userId); +} + export async function logout(request: Request) { return redirect("/logout"); } diff --git a/apps/webapp/app/tailwind.css b/apps/webapp/app/tailwind.css index 605af84..ec7189e 100644 --- a/apps/webapp/app/tailwind.css +++ b/apps/webapp/app/tailwind.css @@ -3,6 +3,7 @@ @import "tailwindcss"; +@custom-variant dark (&:is(.dark *)); :root { --background: oklch(91.28% 0 0); @@ -30,6 +31,14 @@ --input: oklch(0% 0 0 / 6.27%); --ring: 221.2 83.2% 53.3%; --radius: 8px; + --sidebar: hsl(0 0% 98%); + --sidebar-foreground: hsl(240 5.3% 26.1%); + --sidebar-primary: hsl(240 5.9% 10%); + --sidebar-primary-foreground: hsl(0 0% 98%); + --sidebar-accent: hsl(240 4.8% 95.9%); + --sidebar-accent-foreground: hsl(240 5.9% 10%); + --sidebar-border: hsl(220 13% 91%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); } .dark, @@ -60,7 +69,261 @@ --ring: 221.2 83.2% 53.3%; } +:root { + --gray-50: #f9f9f9; + --gray-100: #efefef; + --gray-200: #e8e8e8; + --gray-300: #e0e0e0; + --gray-400: #cecece; + --gray-500: #d8d8d8; + --gray-600: #bbbbbb; + --gray-700: #8d8d8d; + --gray-800: #838383; + --gray-900: #646464; + --gray-950: #202020; + + --grayAlpha-50: 0% 0 0 / 2.35%; + --grayAlpha-100: 0% 0 0 / 6.27%; + --grayAlpha-200: 0% 0 0 / 9.02%; + --grayAlpha-300: 0% 0 0 / 12.16%; + --grayAlpha-400: 0% 0 0 / 15.29%; + --grayAlpha-500: 0% 0 0 / 19.22%; + --grayAlpha-600: 0% 0 0 / 26.67%; + --grayAlpha-700: 0% 0 0 / 44.71%; + --grayAlpha-800: 0% 0 0 / 48.63%; + --grayAlpha-900: 0% 0 0 / 60.78%; + --grayAlpha-950: 0% 0 0 / 87.45%; + + /* Colors for different status */ + --status-pill-0: #d94b0e26; + --status-icon-0: #d94b0e; + + --status-pill-1: #9f3def26; + --status-icon-1: #9f3def; + + --status-pill-2: #8e862c26; + --status-icon-2: #8e862c; + + --status-pill-3: #5c5c5c26; + --status-icon-3: #5c5c5c; + + --status-pill-4: #c28c1126; + --status-icon-4: #c28c11; + + --status-pill-5: #3f8ef726; + --status-icon-5: #3f8ef7; + + --status-pill-6: #3caf2026; + --status-icon-6: #3caf20; + + /* Avatar colors */ + --custom-color-1: #b56455; + --custom-color-2: #7b8a34; + --custom-color-3: #1c91a8; + --custom-color-4: #886dbc; + --custom-color-5: #ad6e30; + --custom-color-6: #54935b; + --custom-color-7: #4187c0; + --custom-color-8: #a165a1; + --custom-color-9: #997d1d; + --custom-color-10: #2b9684; + --custom-color-11: #2b9684; + --custom-color-12: #b0617c; + + /* Team Colors */ + --team-color-1: #89c794; + --team-color-2: #d2a1bb; + --team-color-3: #90c5d6; +} + +.dark { + --gray-50: #191919; + --gray-100: #222222; + --gray-200: #2a2a2a; + --gray-300: #313131; + --gray-400: #3a3a3a; + --gray-500: #484848; + --gray-600: #606060; + --gray-700: #6e6e6e; + --gray-800: #7b7b7b; + --gray-900: #b4b4b4; + --gray-950: #eeeeee; + + --grayAlpha-50: 100% 0 0 / 3.53%; + --grayAlpha-100: 100% 0 0 / 7.06%; + --grayAlpha-200: 100% 0 0 / 10.59%; + --grayAlpha-300: 100% 0 0 / 13.33%; + --grayAlpha-400: 100% 0 0 / 17.25%; + --grayAlpha-500: 100% 0 0 / 23.14%; + --grayAlpha-600: 100% 0 0 / 33.33%; + --grayAlpha-700: 100% 0 0 / 39.22%; + --grayAlpha-800: 100% 0 0 / 44.31%; + --grayAlpha-900: 100% 0 0 / 68.63%; + --grayAlpha-950: 100% 0 0 / 92.94%; + + /* Colors for different status */ + --status-pill-0: #c06142; + --status-icon-0: #c06142; + + --status-pill-1: #886dbc; + --status-icon-1: #886dbc; + + --status-pill-2: #83872c; + --status-icon-2: #83872c; + + --status-pill-3: #b4b4b4; + --status-icon-3: #5c5c5c; + + --status-pill-4: #c28c11; + --status-icon-4: #c28c11; + + --status-pill-5: #4187c0; + --status-icon-5: #4187c0; + + --status-pill-6: #5d9151; + --status-icon-6: #5d9151; + + /* Team Colors */ + --team-color-1: #54935b; + --team-color-2: #a165a1; + --team-color-3: #4187c0; + --sidebar: hsl(240 5.9% 10%); + --sidebar-foreground: hsl(240 4.8% 95.9%); + --sidebar-primary: hsl(224.3 76.3% 48%); + --sidebar-primary-foreground: hsl(0 0% 100%); + --sidebar-accent: hsl(240 3.7% 15.9%); + --sidebar-accent-foreground: hsl(240 4.8% 95.9%); + --sidebar-border: hsl(240 3.7% 15.9%); + --sidebar-ring: hsl(217.2 91.2% 59.8%); +} + @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); + + --color-border: var(--border); + --color-border-dark: var(--border-dark); + --color-input: var(--input); + --color-ring: var(--ring); + --color-background-2: var(--background-2); + --color-background-3: var(--background-3); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-destructive: var(--destructive); + --color-destructive-foreground: var(--destructive-foreground); + --color-warning: var(--warning); + --color-warning-foreground: var(--warning-foreground); + --color-success: var(--success); + --color-success-foreground: var(--success-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + + --color-gray-50: var(--gray-50); + --color-gray-100: var(--gray-100); + --color-gray-200: var(--gray-200); + --color-gray-300: var(--gray-300); + --color-gray-400: var(--gray-400); + --color-gray-500: var(--gray-500); + --color-gray-600: var(--gray-600); + --color-gray-700: var(--gray-700); + --color-gray-800: var(--gray-800); + --color-gray-900: var(--gray-900); + --color-gray-950: var(--gray-950); + + --color-grayAlpha-50: oklch(var(--grayAlpha-50)); + --color-grayAlpha-100: oklch(var(--grayAlpha-100)); + --color-grayAlpha-200: oklch(var(--grayAlpha-200)); + --color-grayAlpha-300: oklch(var(--grayAlpha-300)); + --color-grayAlpha-400: oklch(var(--grayAlpha-400)); + --color-grayAlpha-500: oklch(var(--grayAlpha-500)); + --color-grayAlpha-600: oklch(var(--grayAlpha-600)); + --color-grayAlpha-700: oklch(var(--grayAlpha-700)); + --color-grayAlpha-800: oklch(var(--grayAlpha-800)); + --color-grayAlpha-900: oklch(var(--grayAlpha-900)); + --color-grayAlpha-950: oklch(var(--grayAlpha-950)); + + --color-red-50: #fdf3f3; + --color-red-100: #fbe9e8; + --color-red-200: #f7d4d4; + --color-red-300: #f0b1b1; + --color-red-400: #e78587; + --color-red-500: #d75056; + --color-red-600: #c43a46; + --color-red-700: #a52b3a; + --color-red-800: #8a2735; + --color-red-900: #772433; + --color-red-950: #420f18; + + --color-orange-50: #fdf6ef; + --color-orange-100: #fbead9; + --color-orange-200: #f7d2b1; + --color-orange-300: #f1b480; + --color-orange-400: #ea8c4d; + --color-orange-500: #e67333; + --color-orange-600: #d65520; + --color-orange-700: #b2401c; + --color-orange-800: #8e341e; + --color-orange-900: #732d1b; + --color-orange-950: #3e140c; + + --color-yellow-50: #fdfbe9; + --color-yellow-100: #faf7c7; + --color-yellow-200: #f7ec91; + --color-yellow-300: #f1db53; + --color-yellow-400: #ebc724; + --color-yellow-500: #dcb016; + --color-yellow-600: #c28c11; + --color-yellow-700: #976211; + --color-yellow-800: #7d4f16; + --color-yellow-900: #6b4118; + --color-yellow-950: #3e220a; + + --text-xs: 12px; + --text-sm: 13px; + --text-base: 14px; + --text-md: 15px; + --text-lg: 17px; + --text-xl: 22px; + --text-2xl: 26px; + + --radius-none: 0px; + --radius: 0.375rem; + --radius-sm: 0.125rem; + --radius-md: 0.375rem; + --radius-lg: 0.5rem; + --radius-xl: 0.75rem; + --radius-2xl: 1rem; + --radius-3xl: 1.5rem; + --radius-4xl: 2rem; + --radius-5xl: 3rem; + --radius-full: 9999px; + + --shadow: 0px 6px 20px 0px rgba(0, 0, 0, 0.15), 0px 0px 2px 0px rgba(0, 0, 0, 0.2); + --shadow-1: 0px 6px 20px 0px rgba(0, 0, 0, 0.15), 0px 0px 2px 0px rgba(0, 0, 0, 0.2); + + --font-sans: "Geist Variable", "Helvetica Neue", "Helvetica", "Arial", sans-serif; + --font-mono: "Geist Mono Variable", monaco, Consolas, "Lucida Console", monospace; + --color-sidebar-ring: var(--sidebar-ring); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar: var(--sidebar); +} + +@layer base { + * { + @apply border-border outline-ring/50; + } + body { + @apply bg-background text-foreground; + } } \ No newline at end of file diff --git a/apps/webapp/app/utils/pathBuilder.ts b/apps/webapp/app/utils/pathBuilder.ts new file mode 100644 index 0000000..23f0ffb --- /dev/null +++ b/apps/webapp/app/utils/pathBuilder.ts @@ -0,0 +1,7 @@ +export function confirmBasicDetailsPath() { + return `/confirm-basic-details`; +} + +export function rootPath() { + return `/`; +} diff --git a/apps/webapp/app/utils/presets/apps.ts b/apps/webapp/app/utils/presets/apps.ts new file mode 100644 index 0000000..2be8912 --- /dev/null +++ b/apps/webapp/app/utils/presets/apps.ts @@ -0,0 +1,100 @@ +export enum Apps { + LINEAR = "LINEAR", + SLACK = "SLACK", +} + +export const AppNames = { + [Apps.LINEAR]: "Linear", + [Apps.SLACK]: "Slack", +} as const; + +// General node types that are common across all apps +export const GENERAL_NODE_TYPES = { + PERSON: { + name: "Person", + description: "Represents an individual, like a team member or contact", + }, + APP: { + name: "App", + description: "A software application or service that's integrated", + }, + PLACE: { + name: "Place", + description: "A physical location like an office, meeting room, or city", + }, + ORGANIZATION: { + name: "Organization", + description: "A company, team, or any formal group of people", + }, + EVENT: { + name: "Event", + description: "A meeting, deadline, or any time-based occurrence", + }, +} as const; + +// App-specific node types +export const APP_NODE_TYPES = { + [Apps.LINEAR]: { + ISSUE: { + name: "Linear Issue", + description: "A task, bug report, or feature request tracked in Linear", + }, + PROJECT: { + name: "Linear Project", + description: "A collection of related issues and work items in Linear", + }, + CYCLE: { + name: "Linear Cycle", + description: "A time-boxed iteration of work in Linear", + }, + TEAM: { + name: "Linear Team", + description: "A group of people working together in Linear", + }, + LABEL: { + name: "Linear Label", + description: "A tag used to categorize and organize issues in Linear", + }, + }, + [Apps.SLACK]: { + CHANNEL: { + name: "Slack Channel", + description: "A dedicated space for team communication in Slack", + }, + THREAD: { + name: "Slack Thread", + description: "A focused conversation branch within a Slack channel", + }, + MESSAGE: { + name: "Slack Message", + description: "A single communication sent in a Slack channel or thread", + }, + REACTION: { + name: "Slack Reaction", + description: "An emoji response to a message in Slack", + }, + FILE: { + name: "Slack File", + description: "A document, image or other file shared in Slack", + }, + }, +} as const; + +/** + * Returns both general node types and app-specific node types for given apps + * @param apps Array of app names to get node types for + * @returns Object containing general and app-specific node types + */ +export function getNodeTypes(apps: Array) { + const appSpecificTypes = apps.reduce((acc, appName) => { + return { + ...acc, + [appName]: APP_NODE_TYPES[appName], + }; + }, {}); + + return { + general: GENERAL_NODE_TYPES, + appSpecific: appSpecificTypes, + }; +} diff --git a/apps/webapp/app/utils/singleton.ts b/apps/webapp/app/utils/singleton.ts index 9b28165..4d49eb8 100644 --- a/apps/webapp/app/utils/singleton.ts +++ b/apps/webapp/app/utils/singleton.ts @@ -1,6 +1,6 @@ export function singleton(name: string, getValue: () => T): T { const thusly = globalThis as any; - thusly.__recall_singletons ??= {}; - thusly.__recall_singletons[name] ??= getValue(); - return thusly.__recall_singletons[name]; + thusly.__core_singletons ??= {}; + thusly.__core_singletons[name] ??= getValue(); + return thusly.__core_singletons[name]; } diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 363b8a4..ad61b1d 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -13,10 +13,29 @@ "dependencies": { "@ai-sdk/openai": "^1.3.21", "@coji/remix-auth-google": "^4.2.0", + "@conform-to/react": "^0.6.1", + "@conform-to/zod": "^0.6.1", + "@core/database": "workspace:*", + "@core/types": "workspace:*", "@opentelemetry/api": "1.9.0", + "@radix-ui/react-accordion": "^1.1.2", + "@radix-ui/react-alert-dialog": "^1.0.5", + "@radix-ui/react-avatar": "^1.0.4", + "@radix-ui/react-checkbox": "^1.0.4", + "@radix-ui/react-collapsible": "^1.0.3", + "@radix-ui/react-dialog": "^1.1.14", + "@radix-ui/react-dropdown-menu": "^2.0.6", + "@radix-ui/react-icons": "^1.3.0", + "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-popover": "^1.0.7", + "@radix-ui/react-scroll-area": "^1.0.5", + "@radix-ui/react-select": "^2.0.0", + "@radix-ui/react-separator": "^1.1.7", "@radix-ui/react-slot": "^1.2.3", - "@recall/database": "workspace:*", - "@recall/types": "workspace:*", + "@radix-ui/react-switch": "^1.0.3", + "@radix-ui/react-tabs": "^1.0.4", + "@radix-ui/react-toast": "^1.1.5", + "@radix-ui/react-tooltip": "^1.2.7", "@remix-run/express": "2.16.7", "@remix-run/node": "2.1.0", "@remix-run/react": "2.16.7", @@ -33,6 +52,9 @@ "clsx": "^2.1.1", "compression": "^1.7.4", "cross-env": "^7.0.3", + "d3": "^7.9.0", + "dayjs": "^1.11.10", + "express": "^4.18.1", "ioredis": "^5.6.1", "isbot": "^4.1.0", @@ -67,6 +89,7 @@ "@tailwindcss/typography": "^0.5.16", "@tailwindcss/vite": "^4.1.7", "@types/compression": "^1.7.2", + "@types/d3": "^7.4.3", "@types/express": "^4.17.13", "@types/morgan": "^1.9.3", "@types/react": "^18.2.20", diff --git a/apps/webapp/public/favicon.ico b/apps/webapp/public/favicon.ico index 8830cf6..87c813a 100644 Binary files a/apps/webapp/public/favicon.ico and b/apps/webapp/public/favicon.ico differ diff --git a/apps/webapp/public/logo-dark.png b/apps/webapp/public/logo-dark.png index b24c7ae..f282d02 100644 Binary files a/apps/webapp/public/logo-dark.png and b/apps/webapp/public/logo-dark.png differ diff --git a/apps/webapp/public/logo-dark.svg b/apps/webapp/public/logo-dark.svg new file mode 100644 index 0000000..3f96169 --- /dev/null +++ b/apps/webapp/public/logo-dark.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/webapp/public/logo-light.png b/apps/webapp/public/logo-light.png index 4490ae7..3caf602 100644 Binary files a/apps/webapp/public/logo-light.png and b/apps/webapp/public/logo-light.png differ diff --git a/apps/webapp/public/logo-light.svg b/apps/webapp/public/logo-light.svg new file mode 100644 index 0000000..cdac18b --- /dev/null +++ b/apps/webapp/public/logo-light.svg @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/apps/webapp/tailwind.config.ts b/apps/webapp/tailwind.config.ts index fee4ac5..cee353c 100644 --- a/apps/webapp/tailwind.config.ts +++ b/apps/webapp/tailwind.config.ts @@ -27,134 +27,6 @@ export default { "monospace", ], }, - borderRadius: { - lg: "var(--radius)", - md: "calc(var(--radius) - 2px)", - sm: "calc(var(--radius) - 4px)", - }, - colors: { - border: { - DEFAULT: "oklch(var(--border))", - dark: "oklch(var(--border-dark))", - }, - input: "oklch(var(--input))", - ring: "oklch(var(--ring))", - background: { - 2: "oklch(var(--background-2) / )", - 3: "oklch(var(--background-3) / )", - DEFAULT: "oklch(var(--background) / )", - }, - foreground: "oklch(var(--foreground) / )", - primary: { - DEFAULT: "oklch(var(--primary) / )", - foreground: "oklch(var(--primary-foreground) / )", - }, - secondary: { - DEFAULT: "hsl(var(--secondary))", - foreground: "hsl(var(--secondary-foreground))", - }, - destructive: { - DEFAULT: "oklch(var(--destructive) / )", - foreground: "oklch(var(--destructive-foreground) / )", - }, - warning: { - DEFAULT: "oklch(var(--warning) / )", - foreground: "oklch(var(--warning-foreground) / )", - }, - success: { - DEFAULT: "oklch(var(--success) / )", - foreground: "oklch(var(--success-foreground) / )", - }, - muted: { - DEFAULT: "oklch(var(--muted))", - foreground: "oklch(var(--muted-foreground) / )", - }, - accent: { - DEFAULT: "oklch(var(--accent) / )", - foreground: "oklch(var(--accent-foreground) / )", - }, - popover: { - DEFAULT: "oklch(var(--popover) / )", - foreground: "oklch(var(--popover-foreground) / )", - }, - gray: { - 50: "var(--gray-50)", - 100: "var(--gray-100)", - 200: "var(--gray-200)", - 300: "var(--gray-300)", - 400: "var(--gray-400)", - 500: "var(--gray-500)", - 600: "var(--gray-600)", - 700: "var(--gray-700)", - 800: "var(--gray-800)", - 900: "var(--gray-900)", - 950: "var(--gray-950)", - }, - grayAlpha: { - 50: "oklch(var(--grayAlpha-50))", - 100: "oklch(var(--grayAlpha-100))", - 200: "oklch(var(--grayAlpha-200))", - 300: "oklch(var(--grayAlpha-300))", - 400: "oklch(var(--grayAlpha-400))", - 500: "oklch(var(--grayAlpha-500))", - 600: "oklch(var(--grayAlpha-600))", - 700: "oklch(var(--grayAlpha-700))", - 800: "oklch(var(--grayAlpha-800))", - 900: "oklch(var(--grayAlpha-900))", - 950: "oklch(var(--grayAlpha-950))", - }, - red: { - 50: "#fdf3f3", - 100: "#fbe9e8", - 200: "#f7d4d4", - 300: "#f0b1b1", - 400: "#e78587", - 500: "#d75056", - 600: "#c43a46", - 700: "#a52b3a", - 800: "#8a2735", - 900: "#772433", - 950: "#420f18", - }, - orange: { - 50: "#fdf6ef", - 100: "#fbead9", - 200: "#f7d2b1", - 300: "#f1b480", - 400: "#ea8c4d", - 500: "#e67333", - 600: "#d65520", - 700: "#b2401c", - 800: "#8e341e", - 900: "#732d1b", - 950: "#3e140c", - }, - yellow: { - 50: "#fdfbe9", - 100: "#faf7c7", - 200: "#f7ec91", - 300: "#f1db53", - 400: "#ebc724", - 500: "#dcb016", - 600: "#c28c11", - 700: "#976211", - 800: "#7d4f16", - 900: "#6b4118", - 950: "#3e220a", - }, - sidebar: { - DEFAULT: "oklch(var(--background-2) / )", - foreground: "oklch(var(--foreground) / )", - primary: "oklch(var(--primary) / )", - "primary-foreground": - "oklch(var(--primary-foreground) / )", - accent: "oklch(var(--accent) / )", - "accent-foreground": - "oklch(var(--accent-foreground) / )", - border: "oklch(var(--border))", - ring: "oklch(var(--ring))", - }, - }, }, }, plugins: [ diff --git a/package.json b/package.json index 1eedf2c..21789f0 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "recall", + "name": "core", "private": true, "workspaces": [ "apps/*", "packages/*" ] diff --git a/packages/database/package.json b/packages/database/package.json index af2516f..832878d 100644 --- a/packages/database/package.json +++ b/packages/database/package.json @@ -1,5 +1,5 @@ { - "name": "@recall/database", + "name": "@core/database", "private": true, "version": "0.0.1", "main": "./dist/index.js", diff --git a/packages/database/prisma/migrations/20250605181456_add_integrations/migration.sql b/packages/database/prisma/migrations/20250605181456_add_integrations/migration.sql new file mode 100644 index 0000000..103cfe6 --- /dev/null +++ b/packages/database/prisma/migrations/20250605181456_add_integrations/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Workspace" ADD COLUMN "integrations" TEXT[]; diff --git a/packages/database/prisma/schema.prisma b/packages/database/prisma/schema.prisma index 9a3f085..513ccd1 100644 --- a/packages/database/prisma/schema.prisma +++ b/packages/database/prisma/schema.prisma @@ -52,6 +52,8 @@ model Workspace { slug String @unique icon String? + integrations String[] + userId String? @unique user User? @relation(fields: [userId], references: [id]) } diff --git a/packages/types/package.json b/packages/types/package.json index 98f45c5..7096f50 100644 --- a/packages/types/package.json +++ b/packages/types/package.json @@ -1,5 +1,5 @@ { - "name": "@recall/types", + "name": "@core/types", "private": true, "version": "0.0.1", "main": "./dist/index.js", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3a3fc8e..ca3a362 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -36,18 +36,75 @@ importers: '@coji/remix-auth-google': specifier: ^4.2.0 version: 4.2.0 + '@conform-to/react': + specifier: ^0.6.1 + version: 0.6.3(react@18.3.1) + '@conform-to/zod': + specifier: ^0.6.1 + version: 0.6.3(@conform-to/dom@0.6.3)(zod@3.23.8) + '@core/database': + specifier: workspace:* + version: link:../../packages/database + '@core/types': + specifier: workspace:* + version: link:../../packages/types '@opentelemetry/api': specifier: 1.9.0 version: 1.9.0 + '@radix-ui/react-accordion': + specifier: ^1.1.2 + version: 1.2.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-alert-dialog': + specifier: ^1.0.5 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-avatar': + specifier: ^1.0.4 + version: 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-checkbox': + specifier: ^1.0.4 + version: 1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collapsible': + specifier: ^1.0.3 + version: 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dialog': + specifier: ^1.1.14 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-dropdown-menu': + specifier: ^2.0.6 + version: 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-icons': + specifier: ^1.3.0 + version: 1.3.2(react@18.3.1) + '@radix-ui/react-label': + specifier: ^2.0.2 + version: 2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-popover': + specifier: ^1.0.7 + version: 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-scroll-area': + specifier: ^1.0.5 + version: 1.2.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-select': + specifier: ^2.0.0 + version: 2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-separator': + specifier: ^1.1.7 + version: 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@radix-ui/react-slot': specifier: ^1.2.3 version: 1.2.3(@types/react@18.3.23)(react@18.3.1) - '@recall/database': - specifier: workspace:* - version: link:../../packages/database - '@recall/types': - specifier: workspace:* - version: link:../../packages/types + '@radix-ui/react-switch': + specifier: ^1.0.3 + version: 1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tabs': + specifier: ^1.0.4 + version: 1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-toast': + specifier: ^1.1.5 + version: 1.2.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-tooltip': + specifier: ^1.2.7 + version: 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@remix-run/express': specifier: 2.16.7 version: 2.16.7(express@4.21.2)(typescript@5.8.3) @@ -96,6 +153,12 @@ importers: cross-env: specifier: ^7.0.3 version: 7.0.3 + d3: + specifier: ^7.9.0 + version: 7.9.0 + dayjs: + specifier: ^1.11.10 + version: 1.11.13 express: specifier: ^4.18.1 version: 4.21.2 @@ -193,6 +256,9 @@ importers: '@types/compression': specifier: ^1.7.2 version: 1.8.0 + '@types/d3': + specifier: ^7.4.3 + version: 7.4.3 '@types/express': specifier: ^4.17.13 version: 4.17.22 @@ -585,6 +651,20 @@ packages: '@coji/remix-auth-google@4.2.0': resolution: {integrity: sha512-H9i3fvVz0GE18GUZHpz7p7FQjuiuloTIBAPjW7cfv7lUEk+mI6WRTVLEHJBLLuTlAF1+0EbzvPRYKutxZiFdfw==} + '@conform-to/dom@0.6.3': + resolution: {integrity: sha512-8UbO8vTP0lwpJjzo0TIrQMqWQb65Gj6WpNEVfhHouTEkFTDCP9mND2kO6w1e4TgucRfIDKleYxcFee3lNZsBcw==} + + '@conform-to/react@0.6.3': + resolution: {integrity: sha512-s20D5nRDCa5JkkI+zMyHTaPqqaGgL+y8avenq8fr0r3keCxly/EcrbNH0aAr91OiTdiBD3+ar63XZLZbtx8hDQ==} + peerDependencies: + react: '>=16.8' + + '@conform-to/zod@0.6.3': + resolution: {integrity: sha512-x8HmWuYt/qJVZBB+EG5k0BKCgRUJLVsvy+d6Foqg/Ih/G1xcyPesv7YX/AborurbIiZEuO75Tws1yuZ1AAaIUw==} + peerDependencies: + '@conform-to/dom': 0.6.3 + zod: ^3.21.0 + '@edgefirst-dev/data@0.0.4': resolution: {integrity: sha512-VLhlvEPDJ0Sd0pE6sAYTQkIqZCXVonaWlgRJIQQHzfjTXCadF77qqHj5NxaPSc4wCul0DJO/0MnejVqJAXUiRg==} engines: {node: '>=20.0.0'} @@ -1051,6 +1131,21 @@ packages: resolution: {integrity: sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + '@floating-ui/core@1.7.1': + resolution: {integrity: sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==} + + '@floating-ui/dom@1.7.1': + resolution: {integrity: sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==} + + '@floating-ui/react-dom@2.1.3': + resolution: {integrity: sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==} + peerDependencies: + react: '>=16.8.0' + react-dom: '>=16.8.0' + + '@floating-ui/utils@0.2.9': + resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@fullhuman/postcss-purgecss@2.3.0': resolution: {integrity: sha512-qnKm5dIOyPGJ70kPZ5jiz0I9foVOic0j+cOzNDoo8KoCf6HjicIZ99UfO2OmE7vCYSKAAepEwJtNzpiiZAh9xw==} @@ -1220,6 +1315,103 @@ packages: '@prisma/engines@5.4.1': resolution: {integrity: sha512-vJTdY4la/5V3N7SFvWRmSMUh4mIQnyb/MNoDjzVbh9iLmEC+uEykj/1GPviVsorvfz7DbYSQC4RiwmlEpTEvGA==} + '@radix-ui/number@1.1.1': + resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + + '@radix-ui/primitive@1.1.2': + resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==} + + '@radix-ui/react-accordion@1.2.11': + resolution: {integrity: sha512-l3W5D54emV2ues7jjeG1xcyN7S3jnK3zE2zHqgn0CmMsy9lNJwmgcrmaxS+7ipw15FAivzKNzH3d5EcGoFKw0A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-alert-dialog@1.1.14': + resolution: {integrity: sha512-IOZfZ3nPvN6lXpJTBCunFQPRSvK8MDgSc1FB85xnIpUKOw9en0dJj8JmCAxV7BiZdtYlUpmrQjoTFkVYtdoWzQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-arrow@1.1.7': + resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-avatar@1.1.10': + resolution: {integrity: sha512-V8piFfWapM5OmNCXTzVQY+E1rDa53zY+MQ4Y7356v4fFz6vqCyUtIz2rUD44ZEdwg78/jKmMJHj07+C/Z/rcog==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-checkbox@1.3.2': + resolution: {integrity: sha512-yd+dI56KZqawxKZrJ31eENUwqc1QSqg4OZ15rybGjF2ZNwMO+wCyHzAVLRp9qoYJf7kYy0YpZ2b0JCzJ42HZpA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collapsible@1.1.11': + resolution: {integrity: sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-collection@1.1.7': + resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-compose-refs@1.1.2': resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} peerDependencies: @@ -1229,6 +1421,242 @@ packages: '@types/react': optional: true + '@radix-ui/react-context@1.1.2': + resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dialog@1.1.14': + resolution: {integrity: sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-direction@1.1.1': + resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-dismissable-layer@1.1.10': + resolution: {integrity: sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-dropdown-menu@2.1.15': + resolution: {integrity: sha512-mIBnOjgwo9AH3FyKaSWoSu/dYj6VdhJ7frEPiGTeXCdUFHjl9h3mFh2wwhEtINOmYXWhdpf1rY2minFsmaNgVQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-focus-guards@1.1.2': + resolution: {integrity: sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-focus-scope@1.1.7': + resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-icons@1.3.2': + resolution: {integrity: sha512-fyQIhGDhzfc9pK2kH6Pl9c4BDJGfMkPqkyIgYDthyNYoNg3wVhoJMMh19WS4Up/1KMPFVpNsT2q3WmXn2N1m6g==} + peerDependencies: + react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc + + '@radix-ui/react-id@1.1.1': + resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-label@2.1.7': + resolution: {integrity: sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-menu@2.1.15': + resolution: {integrity: sha512-tVlmA3Vb9n8SZSd+YSbuFR66l87Wiy4du+YE+0hzKQEANA+7cWKH1WgqcEX4pXqxUFQKrWQGHdvEfw00TjFiew==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popover@1.1.14': + resolution: {integrity: sha512-ODz16+1iIbGUfFEfKx2HTPKizg2MN39uIOV8MXeHnmdd3i/N9Wt7vU46wbHsqA0xoaQyXVcs0KIlBdOA2Y95bw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-popper@1.2.7': + resolution: {integrity: sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-portal@1.1.9': + resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-presence@1.1.4': + resolution: {integrity: sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-primitive@2.1.3': + resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-roving-focus@1.1.10': + resolution: {integrity: sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-scroll-area@1.2.9': + resolution: {integrity: sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-select@2.2.5': + resolution: {integrity: sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-separator@1.1.7': + resolution: {integrity: sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + '@radix-ui/react-slot@1.2.3': resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} peerDependencies: @@ -1238,6 +1666,155 @@ packages: '@types/react': optional: true + '@radix-ui/react-switch@1.2.5': + resolution: {integrity: sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tabs@1.1.12': + resolution: {integrity: sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-toast@1.2.14': + resolution: {integrity: sha512-nAP5FBxBJGQ/YfUB+r+O6USFVkWq3gAInkxyEnmvEV5jtSbfDhfa4hwX8CraCnbjMLsE7XSf/K75l9xXY7joWg==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-tooltip@1.2.7': + resolution: {integrity: sha512-Ap+fNYwKTYJ9pzqW+Xe2HtMRbQ/EeWkj2qykZ6SuEV4iS/o1bZI5ssJbk4D2r8XuDuOBVz/tIx2JObtuqU+5Zw==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/react-use-callback-ref@1.1.1': + resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.2': + resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.2': + resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.1': + resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.0': + resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.1': + resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.1': + resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.1': + resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.1': + resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.3': + resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} + peerDependencies: + '@types/react': '*' + '@types/react-dom': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + '@types/react-dom': + optional: true + + '@radix-ui/rect@1.1.1': + resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} + '@remix-run/changelog-github@0.0.5': resolution: {integrity: sha512-43tqwUqWqirbv6D9uzo55ASPsCJ61Ein1k/M8qn+Qpros0MmbmuzjLVPmtaxfxfe2ANX0LefLvCD0pAgr1tp4g==} @@ -1621,6 +2198,99 @@ packages: '@types/cookie@0.6.0': resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/d3-array@3.2.1': + resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + + '@types/d3-axis@3.0.6': + resolution: {integrity: sha512-pYeijfZuBd87T0hGn0FO1vQ/cgLk6E1ALJjfkC0oJ8cbwkZl3TpgS8bVBLZN+2jjGgg38epgxb2zmoGtSfvgMw==} + + '@types/d3-brush@3.0.6': + resolution: {integrity: sha512-nH60IZNNxEcrh6L1ZSMNA28rj27ut/2ZmI3r96Zd+1jrZD++zD3LsMIjWlvg4AYrHn/Pqz4CF3veCxGjtbqt7A==} + + '@types/d3-chord@3.0.6': + resolution: {integrity: sha512-LFYWWd8nwfwEmTZG9PfQxd17HbNPksHBiJHaKuY1XeqscXacsS2tyoo6OdRsjf+NQYeB6XrNL3a25E3gH69lcg==} + + '@types/d3-color@3.1.3': + resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} + + '@types/d3-contour@3.0.6': + resolution: {integrity: sha512-BjzLgXGnCWjUSYGfH1cpdo41/hgdWETu4YxpezoztawmqsvCeep+8QGfiY6YbDvfgHz/DkjeIkkZVJavB4a3rg==} + + '@types/d3-delaunay@6.0.4': + resolution: {integrity: sha512-ZMaSKu4THYCU6sV64Lhg6qjf1orxBthaC161plr5KuPHo3CNm8DTHiLw/5Eq2b6TsNP0W0iJrUOFscY6Q450Hw==} + + '@types/d3-dispatch@3.0.6': + resolution: {integrity: sha512-4fvZhzMeeuBJYZXRXrRIQnvUYfyXwYmLsdiN7XXmVNQKKw1cM8a5WdID0g1hVFZDqT9ZqZEY5pD44p24VS7iZQ==} + + '@types/d3-drag@3.0.7': + resolution: {integrity: sha512-HE3jVKlzU9AaMazNufooRJ5ZpWmLIoc90A37WU2JMmeq28w1FQqCZswHZ3xR+SuxYftzHq6WU6KJHvqxKzTxxQ==} + + '@types/d3-dsv@3.0.7': + resolution: {integrity: sha512-n6QBF9/+XASqcKK6waudgL0pf/S5XHPPI8APyMLLUHd8NqouBGLsU8MgtO7NINGtPBtk9Kko/W4ea0oAspwh9g==} + + '@types/d3-ease@3.0.2': + resolution: {integrity: sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==} + + '@types/d3-fetch@3.0.7': + resolution: {integrity: sha512-fTAfNmxSb9SOWNB9IoG5c8Hg6R+AzUHDRlsXsDZsNp6sxAEOP0tkP3gKkNSO/qmHPoBFTxNrjDprVHDQDvo5aA==} + + '@types/d3-force@3.0.10': + resolution: {integrity: sha512-ZYeSaCF3p73RdOKcjj+swRlZfnYpK1EbaDiYICEEp5Q6sUiqFaFQ9qgoshp5CzIyyb/yD09kD9o2zEltCexlgw==} + + '@types/d3-format@3.0.4': + resolution: {integrity: sha512-fALi2aI6shfg7vM5KiR1wNJnZ7r6UuggVqtDA+xiEdPZQwy/trcQaHnwShLuLdta2rTymCNpxYTiMZX/e09F4g==} + + '@types/d3-geo@3.1.0': + resolution: {integrity: sha512-856sckF0oP/diXtS4jNsiQw/UuK5fQG8l/a9VVLeSouf1/PPbBE1i1W852zVwKwYCBkFJJB7nCFTbk6UMEXBOQ==} + + '@types/d3-hierarchy@3.1.7': + resolution: {integrity: sha512-tJFtNoYBtRtkNysX1Xq4sxtjK8YgoWUNpIiUee0/jHGRwqvzYxkq0hGVbbOGSz+JgFxxRu4K8nb3YpG3CMARtg==} + + '@types/d3-interpolate@3.0.4': + resolution: {integrity: sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==} + + '@types/d3-path@3.1.1': + resolution: {integrity: sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==} + + '@types/d3-polygon@3.0.2': + resolution: {integrity: sha512-ZuWOtMaHCkN9xoeEMr1ubW2nGWsp4nIql+OPQRstu4ypeZ+zk3YKqQT0CXVe/PYqrKpZAi+J9mTs05TKwjXSRA==} + + '@types/d3-quadtree@3.0.6': + resolution: {integrity: sha512-oUzyO1/Zm6rsxKRHA1vH0NEDG58HrT5icx/azi9MF1TWdtttWl0UIUsjEQBBh+SIkrpd21ZjEv7ptxWys1ncsg==} + + '@types/d3-random@3.0.3': + resolution: {integrity: sha512-Imagg1vJ3y76Y2ea0871wpabqp613+8/r0mCLEBfdtqC7xMSfj9idOnmBYyMoULfHePJyxMAw3nWhJxzc+LFwQ==} + + '@types/d3-scale-chromatic@3.1.0': + resolution: {integrity: sha512-iWMJgwkK7yTRmWqRB5plb1kadXyQ5Sj8V/zYlFGMUBbIPKQScw+Dku9cAAMgJG+z5GYDoMjWGLVOvjghDEFnKQ==} + + '@types/d3-scale@4.0.9': + resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} + + '@types/d3-selection@3.0.11': + resolution: {integrity: sha512-bhAXu23DJWsrI45xafYpkQ4NtcKMwWnAC/vKrd2l+nxMFuvOT3XMYTIj2opv8vq8AO5Yh7Qac/nSeP/3zjTK0w==} + + '@types/d3-shape@3.1.7': + resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + + '@types/d3-time-format@4.0.3': + resolution: {integrity: sha512-5xg9rC+wWL8kdDj153qZcsJ0FWiFt0J5RB6LYUNZjwSnesfblqrI/bJ1wBdJ8OQfncgbJG5+2F+qfqnqyzYxyg==} + + '@types/d3-time@3.0.4': + resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} + + '@types/d3-timer@3.0.2': + resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} + + '@types/d3-transition@3.0.9': + resolution: {integrity: sha512-uZS5shfxzO3rGlu0cC3bjmMFKsXv+SmZZcgp0KD22ts4uGXp5EVYGzu/0YdwZeKmddhcAccYtREJKkPfXkZuCg==} + + '@types/d3-zoom@3.0.8': + resolution: {integrity: sha512-iqMC4/YlFCSlO8+2Ii1GGGliCAY4XdeG748w5vQUbevlbDu0zSjH/+jojorQVBK/se0j6DUFNPBGSqD3YWYnDw==} + + '@types/d3@7.4.3': + resolution: {integrity: sha512-lZXZ9ckh5R8uiFVt8ogUNf+pIrK4EsWrx2Np75WvF/eTpJ0FMHNhjXk8CKEx/+gpHbNQyJWehbFaTvqmHWB3ww==} + '@types/debug@4.1.12': resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} @@ -1639,6 +2309,9 @@ packages: '@types/express@4.17.22': resolution: {integrity: sha512-eZUmSnhRX9YRSkplpz0N+k6NljUUn5l3EWZIKZvYzhvMphEuNiyyy1viH/ejgt66JWgALwC/gtSUAeQKtSwW/w==} + '@types/geojson@7946.0.16': + resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==} + '@types/hast@2.3.10': resolution: {integrity: sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==} @@ -2026,6 +2699,10 @@ packages: argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} + engines: {node: '>=10'} + aria-query@5.1.3: resolution: {integrity: sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==} @@ -2323,6 +3000,10 @@ packages: resolution: {integrity: sha512-P0CysNDQ7rtVw4QIQtm+MRxV66vKFSvlsQvGYXZWR3qFU0jlMKHZZZgw8e+8DSah4UDKMqnknRDQz+xuQXQ/Zg==} engines: {node: '>= 6'} + commander@7.2.0: + resolution: {integrity: sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==} + engines: {node: '>= 10'} + compressible@2.0.18: resolution: {integrity: sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==} engines: {node: '>= 0.6'} @@ -2441,6 +3122,133 @@ packages: resolution: {integrity: sha512-QTaY0XjjhTQOdguARF0lGKm5/mEq9PD9/VhZZegHDIBq2tQwgNpHc3dneD4mGo2iJs+fTKv5Bp0fZ+BRuY3Z0g==} engines: {node: '>= 0.1.90'} + d3-array@3.2.4: + resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} + engines: {node: '>=12'} + + d3-axis@3.0.0: + resolution: {integrity: sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==} + engines: {node: '>=12'} + + d3-brush@3.0.0: + resolution: {integrity: sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==} + engines: {node: '>=12'} + + d3-chord@3.0.1: + resolution: {integrity: sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==} + engines: {node: '>=12'} + + d3-color@3.1.0: + resolution: {integrity: sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==} + engines: {node: '>=12'} + + d3-contour@4.0.2: + resolution: {integrity: sha512-4EzFTRIikzs47RGmdxbeUvLWtGedDUNkTcmzoeyg4sP/dvCexO47AaQL7VKy/gul85TOxw+IBgA8US2xwbToNA==} + engines: {node: '>=12'} + + d3-delaunay@6.0.4: + resolution: {integrity: sha512-mdjtIZ1XLAM8bm/hx3WwjfHt6Sggek7qH043O8KEjDXN40xi3vx/6pYSVTwLjEgiXQTbvaouWKynLBiUZ6SK6A==} + engines: {node: '>=12'} + + d3-dispatch@3.0.1: + resolution: {integrity: sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==} + engines: {node: '>=12'} + + d3-drag@3.0.0: + resolution: {integrity: sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==} + engines: {node: '>=12'} + + d3-dsv@3.0.1: + resolution: {integrity: sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==} + engines: {node: '>=12'} + hasBin: true + + d3-ease@3.0.1: + resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} + engines: {node: '>=12'} + + d3-fetch@3.0.1: + resolution: {integrity: sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==} + engines: {node: '>=12'} + + d3-force@3.0.0: + resolution: {integrity: sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==} + engines: {node: '>=12'} + + d3-format@3.1.0: + resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + engines: {node: '>=12'} + + d3-geo@3.1.1: + resolution: {integrity: sha512-637ln3gXKXOwhalDzinUgY83KzNWZRKbYubaG+fGVuc/dxO64RRljtCTnf5ecMyE1RIdtqpkVcq0IbtU2S8j2Q==} + engines: {node: '>=12'} + + d3-hierarchy@3.1.2: + resolution: {integrity: sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==} + engines: {node: '>=12'} + + d3-interpolate@3.0.1: + resolution: {integrity: sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==} + engines: {node: '>=12'} + + d3-path@3.1.0: + resolution: {integrity: sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==} + engines: {node: '>=12'} + + d3-polygon@3.0.1: + resolution: {integrity: sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==} + engines: {node: '>=12'} + + d3-quadtree@3.0.1: + resolution: {integrity: sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==} + engines: {node: '>=12'} + + d3-random@3.0.1: + resolution: {integrity: sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==} + engines: {node: '>=12'} + + d3-scale-chromatic@3.1.0: + resolution: {integrity: sha512-A3s5PWiZ9YCXFye1o246KoscMWqf8BsD9eRiJ3He7C9OBaxKhAd5TFCdEx/7VbKtxxTsu//1mMJFrEt572cEyQ==} + engines: {node: '>=12'} + + d3-scale@4.0.2: + resolution: {integrity: sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==} + engines: {node: '>=12'} + + d3-selection@3.0.0: + resolution: {integrity: sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==} + engines: {node: '>=12'} + + d3-shape@3.2.0: + resolution: {integrity: sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==} + engines: {node: '>=12'} + + d3-time-format@4.1.0: + resolution: {integrity: sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==} + engines: {node: '>=12'} + + d3-time@3.1.0: + resolution: {integrity: sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==} + engines: {node: '>=12'} + + d3-timer@3.0.1: + resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} + engines: {node: '>=12'} + + d3-transition@3.0.1: + resolution: {integrity: sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==} + engines: {node: '>=12'} + peerDependencies: + d3-selection: 2 - 3 + + d3-zoom@3.0.0: + resolution: {integrity: sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==} + engines: {node: '>=12'} + + d3@7.9.0: + resolution: {integrity: sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA==} + engines: {node: '>=12'} + damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} @@ -2463,6 +3271,9 @@ packages: dataloader@1.4.0: resolution: {integrity: sha512-68s5jYdlvasItOJnCuI2Q9s4q98g0pCyL3HrcKJu8KNugUl8ahgmZYg38ysLTgQjjXX3H8CJLkAvWrclWfcalw==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@2.6.9: resolution: {integrity: sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==} peerDependencies: @@ -2535,6 +3346,9 @@ packages: defined@1.0.1: resolution: {integrity: sha512-hsBd2qSVCRE+5PmNdHt1uzyrFu5d3RwmFDKzyNZMFq/EwDNJF7Ee5+D5oEKF0hU6LhtoUF1macFvOe4AskQC1Q==} + delaunator@5.0.1: + resolution: {integrity: sha512-8nvh+XBe96aCESrGOqMp/84b13H9cdKbG5P2ejQCh4d4sK9RL4371qou9drQjMhvnPmhWl5hnmqbEE0fXr9Xnw==} + denque@2.1.0: resolution: {integrity: sha512-HVQE3AAb/pxF8fQAoiqpvg9i3evqug3hoiwakOyZAwJm+6vZehbkYXZ0l4JxS+I3QxM97v5aaRNhj8v5oBhekw==} engines: {node: '>=0.10'} @@ -2559,6 +3373,9 @@ packages: resolution: {integrity: sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==} engines: {node: '>=8'} + detect-node-es@1.1.0: + resolution: {integrity: sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==} + detective@5.2.1: resolution: {integrity: sha512-v9XE1zRnz1wRtgurGu0Bs8uHKFSTdteYZNbIPFVhUZ39L/S79ppMpdmVOZAnoz1jfEFodc48n6MX483Xo3t1yw==} engines: {node: '>=0.8.0'} @@ -3233,6 +4050,10 @@ packages: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} + get-nonce@1.0.1: + resolution: {integrity: sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==} + engines: {node: '>=6'} + get-port@5.1.1: resolution: {integrity: sha512-g/Q1aTSDOxFpchXC4i8ZWvxA1lnPqx/JHqcpIw0/LX9T8x/GBbi6YnlN5nhaKIFkT8oFsscUKgDJYxfwfS6QsQ==} engines: {node: '>=8'} @@ -3376,6 +4197,10 @@ packages: resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==} engines: {node: '>=0.10.0'} + iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + icss-utils@5.1.0: resolution: {integrity: sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==} engines: {node: ^10 || ^12 || >= 14} @@ -3415,6 +4240,10 @@ packages: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} + internmap@2.0.3: + resolution: {integrity: sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==} + engines: {node: '>=12'} + ioredis@5.6.1: resolution: {integrity: sha512-UxC0Yv1Y4WRJiGQxQkP0hfdL0/5/6YvdfOOClRgJ0qppSarkhneSa6UvkMkms0AkdGimSH3Ikqm+6mkMmX7vGA==} engines: {node: '>=12.22.0'} @@ -4807,6 +5636,26 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-remove-scroll-bar@2.3.8: + resolution: {integrity: sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + react-remove-scroll@2.7.1: + resolution: {integrity: sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react-router-dom@6.30.0: resolution: {integrity: sha512-x30B78HV5tFk8ex0ITwzC9TTZMua4jGyA9IUlH1JLQYQTFyxr/ZxwOJq7evg1JX1qGVUcvhsmQSKdPncQrjTgA==} engines: {node: '>=14.0.0'} @@ -4820,6 +5669,16 @@ packages: peerDependencies: react: '>=16.8' + react-style-singleton@2.2.3: + resolution: {integrity: sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -5009,6 +5868,9 @@ packages: engines: {node: 20 || >=22} hasBin: true + robust-predicates@3.0.2: + resolution: {integrity: sha512-IXgzBWvWQwE6PrDI05OvmXUIruQTcoMDzRsOd5CDvHCVLcLHMTSYvOK5Cm46kWqlV3yAbuSpBZdJ5oP5OUoStg==} + rollup@4.41.1: resolution: {integrity: sha512-cPmwD3FnFv8rKMBc1MxWCwVQFxwf1JEmSX3iQXrRVVG15zerAIXRjMFVWnd5Q5QvgKF7Aj+5ykXFhUl+QGnyOw==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} @@ -5017,6 +5879,9 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + rw@1.3.3: + resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==} + rxjs@7.8.2: resolution: {integrity: sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA==} @@ -5600,6 +6465,26 @@ packages: uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + use-callback-ref@1.3.3: + resolution: {integrity: sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + use-sidecar@1.1.3: + resolution: {integrity: sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==} + engines: {node: '>=10'} + peerDependencies: + '@types/react': '*' + react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + use-sync-external-store@1.5.0: resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} peerDependencies: @@ -6342,6 +7227,18 @@ snapshots: '@coji/remix-auth-google@4.2.0': {} + '@conform-to/dom@0.6.3': {} + + '@conform-to/react@0.6.3(react@18.3.1)': + dependencies: + '@conform-to/dom': 0.6.3 + react: 18.3.1 + + '@conform-to/zod@0.6.3(@conform-to/dom@0.6.3)(zod@3.23.8)': + dependencies: + '@conform-to/dom': 0.6.3 + zod: 3.23.8 + '@edgefirst-dev/data@0.0.4': {} '@emnapi/core@1.4.3': @@ -6601,6 +7498,23 @@ snapshots: '@eslint/js@8.57.1': {} + '@floating-ui/core@1.7.1': + dependencies: + '@floating-ui/utils': 0.2.9 + + '@floating-ui/dom@1.7.1': + dependencies: + '@floating-ui/core': 1.7.1 + '@floating-ui/utils': 0.2.9 + + '@floating-ui/react-dom@2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/dom': 1.7.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + + '@floating-ui/utils@0.2.9': {} + '@fullhuman/postcss-purgecss@2.3.0': dependencies: postcss: 7.0.32 @@ -6802,12 +7716,380 @@ snapshots: '@prisma/engines@5.4.1': {} + '@radix-ui/number@1.1.1': {} + + '@radix-ui/primitive@1.1.2': {} + + '@radix-ui/react-accordion@1.2.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collapsible': 1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-alert-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dialog': 1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-arrow@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-avatar@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-checkbox@1.3.2(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-collapsible@1.1.11(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-collection@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@radix-ui/react-compose-refs@1.1.2(@types/react@18.3.23)(react@18.3.1)': dependencies: react: 18.3.1 optionalDependencies: '@types/react': 18.3.23 + '@radix-ui/react-context@1.1.2(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-dialog@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-direction@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-dismissable-layer@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-dropdown-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-menu': 2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-focus-guards@1.1.2(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-icons@1.3.2(react@18.3.1)': + dependencies: + react: 18.3.1 + + '@radix-ui/react-id@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-label@2.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-menu@2.1.15(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-popover@1.1.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-popper@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@floating-ui/react-dom': 2.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-arrow': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-rect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-portal@1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-presence@1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-primitive@2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-roving-focus@1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-scroll-area@1.2.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-select@2.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/number': 1.1.1 + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-focus-guards': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + aria-hidden: 1.2.6 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-remove-scroll: 2.7.1(@types/react@18.3.23)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-separator@1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + '@radix-ui/react-slot@1.2.3(@types/react@18.3.23)(react@18.3.1)': dependencies: '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) @@ -6815,6 +8097,149 @@ snapshots: optionalDependencies: '@types/react': 18.3.23 + '@radix-ui/react-switch@1.2.5(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-previous': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-size': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-tabs@1.1.12(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-direction': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-roving-focus': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-toast@1.2.14(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-collection': 1.1.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-tooltip@1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/primitive': 1.1.2 + '@radix-ui/react-compose-refs': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-context': 1.1.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-dismissable-layer': 1.1.10(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-id': 1.1.1(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-popper': 1.2.7(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-portal': 1.1.9(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-presence': 1.1.4(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@radix-ui/react-slot': 1.2.3(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/react-use-callback-ref@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-controllable-state@1.2.2(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-effect-event': 0.0.2(@types/react@18.3.23)(react@18.3.1) + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-effect-event@0.0.2(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + use-sync-external-store: 1.5.0(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-layout-effect@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-previous@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-rect@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/rect': 1.1.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-use-size@1.1.1(@types/react@18.3.23)(react@18.3.1)': + dependencies: + '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@18.3.23)(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.23 + + '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@radix-ui/react-primitive': 2.1.3(@types/react-dom@18.3.7(@types/react@18.3.23))(@types/react@18.3.23)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + '@types/react-dom': 18.3.7(@types/react@18.3.23) + + '@radix-ui/rect@1.1.1': {} + '@remix-run/changelog-github@0.0.5': dependencies: '@changesets/errors': 0.1.4 @@ -7268,6 +8693,123 @@ snapshots: '@types/cookie@0.6.0': {} + '@types/d3-array@3.2.1': {} + + '@types/d3-axis@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-brush@3.0.6': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-chord@3.0.6': {} + + '@types/d3-color@3.1.3': {} + + '@types/d3-contour@3.0.6': + dependencies: + '@types/d3-array': 3.2.1 + '@types/geojson': 7946.0.16 + + '@types/d3-delaunay@6.0.4': {} + + '@types/d3-dispatch@3.0.6': {} + + '@types/d3-drag@3.0.7': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-dsv@3.0.7': {} + + '@types/d3-ease@3.0.2': {} + + '@types/d3-fetch@3.0.7': + dependencies: + '@types/d3-dsv': 3.0.7 + + '@types/d3-force@3.0.10': {} + + '@types/d3-format@3.0.4': {} + + '@types/d3-geo@3.1.0': + dependencies: + '@types/geojson': 7946.0.16 + + '@types/d3-hierarchy@3.1.7': {} + + '@types/d3-interpolate@3.0.4': + dependencies: + '@types/d3-color': 3.1.3 + + '@types/d3-path@3.1.1': {} + + '@types/d3-polygon@3.0.2': {} + + '@types/d3-quadtree@3.0.6': {} + + '@types/d3-random@3.0.3': {} + + '@types/d3-scale-chromatic@3.1.0': {} + + '@types/d3-scale@4.0.9': + dependencies: + '@types/d3-time': 3.0.4 + + '@types/d3-selection@3.0.11': {} + + '@types/d3-shape@3.1.7': + dependencies: + '@types/d3-path': 3.1.1 + + '@types/d3-time-format@4.0.3': {} + + '@types/d3-time@3.0.4': {} + + '@types/d3-timer@3.0.2': {} + + '@types/d3-transition@3.0.9': + dependencies: + '@types/d3-selection': 3.0.11 + + '@types/d3-zoom@3.0.8': + dependencies: + '@types/d3-interpolate': 3.0.4 + '@types/d3-selection': 3.0.11 + + '@types/d3@7.4.3': + dependencies: + '@types/d3-array': 3.2.1 + '@types/d3-axis': 3.0.6 + '@types/d3-brush': 3.0.6 + '@types/d3-chord': 3.0.6 + '@types/d3-color': 3.1.3 + '@types/d3-contour': 3.0.6 + '@types/d3-delaunay': 6.0.4 + '@types/d3-dispatch': 3.0.6 + '@types/d3-drag': 3.0.7 + '@types/d3-dsv': 3.0.7 + '@types/d3-ease': 3.0.2 + '@types/d3-fetch': 3.0.7 + '@types/d3-force': 3.0.10 + '@types/d3-format': 3.0.4 + '@types/d3-geo': 3.1.0 + '@types/d3-hierarchy': 3.1.7 + '@types/d3-interpolate': 3.0.4 + '@types/d3-path': 3.1.1 + '@types/d3-polygon': 3.0.2 + '@types/d3-quadtree': 3.0.6 + '@types/d3-random': 3.0.3 + '@types/d3-scale': 4.0.9 + '@types/d3-scale-chromatic': 3.1.0 + '@types/d3-selection': 3.0.11 + '@types/d3-shape': 3.1.7 + '@types/d3-time': 3.0.4 + '@types/d3-time-format': 4.0.3 + '@types/d3-timer': 3.0.2 + '@types/d3-transition': 3.0.9 + '@types/d3-zoom': 3.0.8 + '@types/debug@4.1.12': dependencies: '@types/ms': 2.1.0 @@ -7294,6 +8836,8 @@ snapshots: '@types/qs': 6.14.0 '@types/serve-static': 1.15.7 + '@types/geojson@7946.0.16': {} + '@types/hast@2.3.10': dependencies: '@types/unist': 2.0.11 @@ -7732,6 +9276,10 @@ snapshots: argparse@2.0.1: {} + aria-hidden@1.2.6: + dependencies: + tslib: 2.8.1 + aria-query@5.1.3: dependencies: deep-equal: 2.2.3 @@ -8085,6 +9633,8 @@ snapshots: commander@5.1.0: {} + commander@7.2.0: {} + compressible@2.0.18: dependencies: mime-db: 1.54.0 @@ -8190,6 +9740,158 @@ snapshots: csv-stringify: 5.6.5 stream-transform: 2.1.3 + d3-array@3.2.4: + dependencies: + internmap: 2.0.3 + + d3-axis@3.0.0: {} + + d3-brush@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3-chord@3.0.1: + dependencies: + d3-path: 3.1.0 + + d3-color@3.1.0: {} + + d3-contour@4.0.2: + dependencies: + d3-array: 3.2.4 + + d3-delaunay@6.0.4: + dependencies: + delaunator: 5.0.1 + + d3-dispatch@3.0.1: {} + + d3-drag@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-selection: 3.0.0 + + d3-dsv@3.0.1: + dependencies: + commander: 7.2.0 + iconv-lite: 0.6.3 + rw: 1.3.3 + + d3-ease@3.0.1: {} + + d3-fetch@3.0.1: + dependencies: + d3-dsv: 3.0.1 + + d3-force@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-quadtree: 3.0.1 + d3-timer: 3.0.1 + + d3-format@3.1.0: {} + + d3-geo@3.1.1: + dependencies: + d3-array: 3.2.4 + + d3-hierarchy@3.1.2: {} + + d3-interpolate@3.0.1: + dependencies: + d3-color: 3.1.0 + + d3-path@3.1.0: {} + + d3-polygon@3.0.1: {} + + d3-quadtree@3.0.1: {} + + d3-random@3.0.1: {} + + d3-scale-chromatic@3.1.0: + dependencies: + d3-color: 3.1.0 + d3-interpolate: 3.0.1 + + d3-scale@4.0.2: + dependencies: + d3-array: 3.2.4 + d3-format: 3.1.0 + d3-interpolate: 3.0.1 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + + d3-selection@3.0.0: {} + + d3-shape@3.2.0: + dependencies: + d3-path: 3.1.0 + + d3-time-format@4.1.0: + dependencies: + d3-time: 3.1.0 + + d3-time@3.1.0: + dependencies: + d3-array: 3.2.4 + + d3-timer@3.0.1: {} + + d3-transition@3.0.1(d3-selection@3.0.0): + dependencies: + d3-color: 3.1.0 + d3-dispatch: 3.0.1 + d3-ease: 3.0.1 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-timer: 3.0.1 + + d3-zoom@3.0.0: + dependencies: + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-interpolate: 3.0.1 + d3-selection: 3.0.0 + d3-transition: 3.0.1(d3-selection@3.0.0) + + d3@7.9.0: + dependencies: + d3-array: 3.2.4 + d3-axis: 3.0.0 + d3-brush: 3.0.0 + d3-chord: 3.0.1 + d3-color: 3.1.0 + d3-contour: 4.0.2 + d3-delaunay: 6.0.4 + d3-dispatch: 3.0.1 + d3-drag: 3.0.0 + d3-dsv: 3.0.1 + d3-ease: 3.0.1 + d3-fetch: 3.0.1 + d3-force: 3.0.0 + d3-format: 3.1.0 + d3-geo: 3.1.1 + d3-hierarchy: 3.1.2 + d3-interpolate: 3.0.1 + d3-path: 3.1.0 + d3-polygon: 3.0.1 + d3-quadtree: 3.0.1 + d3-random: 3.0.1 + d3-scale: 4.0.2 + d3-scale-chromatic: 3.1.0 + d3-selection: 3.0.0 + d3-shape: 3.2.0 + d3-time: 3.1.0 + d3-time-format: 4.1.0 + d3-timer: 3.0.1 + d3-transition: 3.0.1(d3-selection@3.0.0) + d3-zoom: 3.0.0 + damerau-levenshtein@1.0.8: {} data-uri-to-buffer@3.0.1: {} @@ -8214,6 +9916,8 @@ snapshots: dataloader@1.4.0: {} + dayjs@1.11.13: {} + debug@2.6.9: dependencies: ms: 2.0.0 @@ -8284,6 +9988,10 @@ snapshots: defined@1.0.1: {} + delaunator@5.0.1: + dependencies: + robust-predicates: 3.0.2 + denque@2.1.0: {} depd@2.0.0: {} @@ -8296,6 +10004,8 @@ snapshots: detect-libc@2.0.4: {} + detect-node-es@1.1.0: {} + detective@5.2.1: dependencies: acorn-node: 1.8.2 @@ -9226,6 +10936,8 @@ snapshots: hasown: 2.0.2 math-intrinsics: 1.1.0 + get-nonce@1.0.1: {} + get-port@5.1.1: {} get-proto@1.0.1: @@ -9391,6 +11103,10 @@ snapshots: dependencies: safer-buffer: 2.1.2 + iconv-lite@0.6.3: + dependencies: + safer-buffer: 2.1.2 + icss-utils@5.1.0(postcss@8.5.3): dependencies: postcss: 8.5.3 @@ -9423,6 +11139,8 @@ snapshots: hasown: 2.0.2 side-channel: 1.1.0 + internmap@2.0.3: {} + ioredis@5.6.1: dependencies: '@ioredis/commands': 1.2.0 @@ -10892,6 +12610,25 @@ snapshots: react-refresh@0.14.2: {} + react-remove-scroll-bar@2.3.8(@types/react@18.3.23)(react@18.3.1): + dependencies: + react: 18.3.1 + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + react-remove-scroll@2.7.1(@types/react@18.3.23)(react@18.3.1): + dependencies: + react: 18.3.1 + react-remove-scroll-bar: 2.3.8(@types/react@18.3.23)(react@18.3.1) + react-style-singleton: 2.2.3(@types/react@18.3.23)(react@18.3.1) + tslib: 2.8.1 + use-callback-ref: 1.3.3(@types/react@18.3.23)(react@18.3.1) + use-sidecar: 1.1.3(@types/react@18.3.23)(react@18.3.1) + optionalDependencies: + '@types/react': 18.3.23 + react-router-dom@6.30.0(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@remix-run/router': 1.23.0 @@ -10904,6 +12641,14 @@ snapshots: '@remix-run/router': 1.23.0 react: 18.3.1 + react-style-singleton@2.2.3(@types/react@18.3.23)(react@18.3.1): + dependencies: + get-nonce: 1.0.1 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -11102,6 +12847,8 @@ snapshots: glob: 11.0.2 package-json-from-dist: 1.0.1 + robust-predicates@3.0.2: {} + rollup@4.41.1: dependencies: '@types/estree': 1.0.7 @@ -11132,6 +12879,8 @@ snapshots: dependencies: queue-microtask: 1.2.3 + rw@1.3.3: {} + rxjs@7.8.2: dependencies: tslib: 2.8.1 @@ -11816,6 +13565,21 @@ snapshots: dependencies: punycode: 2.3.1 + use-callback-ref@1.3.3(@types/react@18.3.23)(react@18.3.1): + dependencies: + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + + use-sidecar@1.1.3(@types/react@18.3.23)(react@18.3.1): + dependencies: + detect-node-es: 1.1.0 + react: 18.3.1 + tslib: 2.8.1 + optionalDependencies: + '@types/react': 18.3.23 + use-sync-external-store@1.5.0(react@18.3.1): dependencies: react: 18.3.1