diff --git a/components/RegexHighlightText.tsx b/components/RegexHighlightText.tsx
index 89e1a7d..04b5451 100644
--- a/components/RegexHighlightText.tsx
+++ b/components/RegexHighlightText.tsx
@@ -1,3 +1,9 @@
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/components/ui/hover-card";
+
interface RegexHighlightTextProps {
text: string;
matches: string[];
@@ -16,6 +22,7 @@ export default function RegexHighlightText(props: RegexHighlightTextProps) {
);
let lastIndex = 0;
+ let matchNumber = 0;
props.matches.forEach((match, index) => {
const offset = props.text.indexOf(match, lastIndex);
@@ -28,10 +35,45 @@ export default function RegexHighlightText(props: RegexHighlightTextProps) {
);
}
+ matchNumber++;
+ const currentMatchNumber = matchNumber;
+ const matchLength = match.length;
+ const startPos = offset;
+ const endPos = offset + matchLength;
+
parts.push(
-
- {match === "\n" ? newLine : match}
-
+
+
+
+ {match === "\n" ? newLine : match}
+
+
+
+
+
Match #{currentMatchNumber}
+
+ Position:{" "}
+ {startPos} -{" "}
+ {endPos}
+
+
+ Length:{" "}
+ {matchLength}{" "}
+ character
+ {matchLength !== 1 ? "s" : ""}
+
+ {match.length <= 50 && (
+
+ "{match === "\n" ? "\\n" : match}"
+
+ )}
+
+
+
);
lastIndex = offset + match.length;
diff --git a/components/regex/RegexCaptureGroupVisualizer.tsx b/components/regex/RegexCaptureGroupVisualizer.tsx
new file mode 100644
index 0000000..f3b07c4
--- /dev/null
+++ b/components/regex/RegexCaptureGroupVisualizer.tsx
@@ -0,0 +1,206 @@
+import { useMemo } from "react";
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/components/ui/hover-card";
+import {
+ getDetailedMatches,
+ RegexMatch,
+} from "@/components/utils/regex-tester.utils";
+import { cn } from "@/lib/utils";
+
+interface RegexCaptureGroupVisualizerProps {
+ pattern: string;
+ testString: string;
+}
+
+const GROUP_COLORS = [
+ "border-blue-400 bg-blue-50 dark:bg-blue-950/30",
+ "border-green-400 bg-green-50 dark:bg-green-950/30",
+ "border-purple-400 bg-purple-50 dark:bg-purple-950/30",
+ "border-orange-400 bg-orange-50 dark:bg-orange-950/30",
+ "border-pink-400 bg-pink-50 dark:bg-pink-950/30",
+ "border-cyan-400 bg-cyan-50 dark:bg-cyan-950/30",
+];
+
+export default function RegexCaptureGroupVisualizer({
+ pattern,
+ testString,
+}: RegexCaptureGroupVisualizerProps) {
+ const matches: RegexMatch[] = useMemo(() => {
+ if (!pattern || !testString) return [];
+ try {
+ return getDetailedMatches(pattern, testString);
+ } catch {
+ return [];
+ }
+ }, [pattern, testString]);
+
+ if (matches.length === 0) {
+ return (
+
+ No capture groups found. Add groups with parentheses () to see them
+ here.
+
+ );
+ }
+
+ const hasGroups = matches.some(
+ (m) => m.captureGroups.length > 0 || Object.keys(m.groups).length > 0
+ );
+
+ if (!hasGroups) {
+ return (
+
+ No capture groups in pattern. Use parentheses () to create capture
+ groups.
+
+ );
+ }
+
+ return (
+
+ {matches.map((match, matchIndex) => (
+
+
+ Match {matchIndex + 1} at position {match.index}
+
+
+
Full Match
+
+ {match.fullMatch || (
+ empty
+ )}
+
+
+ {match.captureGroups.length > 0 && (
+
+
+ Capture Groups (CSS Box Model)
+
+
+ {match.captureGroups.map((group, groupIndex) => (
+
+
+ 0 ? "8px" : "0",
+ }}
+ >
+
+
+ Group {groupIndex + 1}
+
+
+ {group ?? (
+
+ undefined
+
+ )}
+
+
+
+
+
+
+ Capture Group {groupIndex + 1}
+
+
+ Captured value:{" "}
+
+ {group ?? "undefined"}
+
+
+
+ Access via:{" "}
+
+ match[{groupIndex + 1}]
+ {" "}
+ or{" "}
+
+ ${groupIndex + 1}
+
+
+
+
+ ))}
+
+
+ )}
+
+ {Object.keys(match.groups).length > 0 && (
+
+
+ Named Groups
+
+
+ {Object.entries(match.groups).map(([name, value], index) => (
+
+
+
+
{name}
+
+ {value ?? (
+
+ undefined
+
+ )}
+
+
+
+
+
+ Named Group: {name}
+
+
+ Captured value:{" "}
+
+ {value ?? "undefined"}
+
+
+
+ Access via:{" "}
+
+ match.groups.{name}
+
+
+
+
+ ))}
+
+
+ )}
+
+
+ ))}
+
+ );
+}
diff --git a/components/regex/RegexCheatSheet.tsx b/components/regex/RegexCheatSheet.tsx
new file mode 100644
index 0000000..20f0534
--- /dev/null
+++ b/components/regex/RegexCheatSheet.tsx
@@ -0,0 +1,97 @@
+import { useState } from "react";
+import { ChevronDown, ChevronUp } from "lucide-react";
+import { CHEAT_SHEET } from "@/components/utils/regex-tester.utils";
+import { cn } from "@/lib/utils";
+
+interface RegexCheatSheetProps {
+ onInsert?: (syntax: string) => void;
+}
+
+export default function RegexCheatSheet({ onInsert }: RegexCheatSheetProps) {
+ const [isExpanded, setIsExpanded] = useState(false);
+ const [expandedSections, setExpandedSections] = useState>(
+ new Set(["Character Classes"])
+ );
+
+ const toggleSection = (section: string) => {
+ setExpandedSections((prev) => {
+ const next = new Set(prev);
+ if (next.has(section)) {
+ next.delete(section);
+ } else {
+ next.add(section);
+ }
+ return next;
+ });
+ };
+
+ return (
+
+
+
+ {isExpanded && (
+
+ {Object.entries(CHEAT_SHEET).map(([category, items]) => (
+
+
+
+ {expandedSections.has(category) && (
+
+ {items.map((item, index) => (
+
+ ))}
+
+ )}
+
+ ))}
+ {onInsert && (
+
+ Click any syntax to insert it into your pattern
+
+ )}
+
+ )}
+
+ );
+}
diff --git a/components/regex/RegexFlagToggle.tsx b/components/regex/RegexFlagToggle.tsx
new file mode 100644
index 0000000..af762b2
--- /dev/null
+++ b/components/regex/RegexFlagToggle.tsx
@@ -0,0 +1,54 @@
+import { cn } from "@/lib/utils";
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/components/ui/hover-card";
+import {
+ RegexFlags,
+ FLAG_DESCRIPTIONS,
+} from "@/components/utils/regex-tester.utils";
+
+interface RegexFlagToggleProps {
+ flags: RegexFlags;
+ onFlagChange: (flag: keyof RegexFlags) => void;
+}
+
+export default function RegexFlagToggle({
+ flags,
+ onFlagChange,
+}: RegexFlagToggleProps) {
+ const flagKeys = Object.keys(flags) as (keyof RegexFlags)[];
+
+ return (
+
+ {flagKeys.map((flag) => (
+
+
+
+
+
+
+ {FLAG_DESCRIPTIONS[flag].name}
+
+
+ {FLAG_DESCRIPTIONS[flag].description}
+
+
+
+ ))}
+
+ );
+}
diff --git a/components/regex/RegexMatchStats.tsx b/components/regex/RegexMatchStats.tsx
new file mode 100644
index 0000000..95ae093
--- /dev/null
+++ b/components/regex/RegexMatchStats.tsx
@@ -0,0 +1,72 @@
+import { useMemo } from "react";
+import {
+ getMatchStats,
+ MatchStats,
+} from "@/components/utils/regex-tester.utils";
+
+interface RegexMatchStatsProps {
+ pattern: string;
+ testString: string;
+}
+
+export default function RegexMatchStats({
+ pattern,
+ testString,
+}: RegexMatchStatsProps) {
+ const stats: MatchStats | null = useMemo(() => {
+ if (!pattern || !testString) return null;
+ try {
+ return getMatchStats(pattern, testString);
+ } catch {
+ return null;
+ }
+ }, [pattern, testString]);
+
+ if (!stats) {
+ return (
+
+ Enter pattern and test string to see statistics
+
+ );
+ }
+
+ return (
+
+ 0}
+ />
+
+
+
+
+ );
+}
+
+interface StatCardProps {
+ label: string;
+ value: string;
+ highlight?: boolean;
+}
+
+function StatCard({ label, value, highlight }: StatCardProps) {
+ return (
+
+
{label}
+
+ {value}
+
+
+ );
+}
diff --git a/components/regex/RegexPatternExplainer.tsx b/components/regex/RegexPatternExplainer.tsx
new file mode 100644
index 0000000..e9f6757
--- /dev/null
+++ b/components/regex/RegexPatternExplainer.tsx
@@ -0,0 +1,124 @@
+import { useMemo } from "react";
+import {
+ HoverCard,
+ HoverCardContent,
+ HoverCardTrigger,
+} from "@/components/ui/hover-card";
+import { explainPattern } from "@/components/utils/regex-tester.utils";
+import { cn } from "@/lib/utils";
+
+interface RegexPatternExplainerProps {
+ pattern: string;
+}
+
+const getComponentColor = (type: string): string => {
+ switch (type) {
+ case "escape":
+ return "bg-blue-100 dark:bg-blue-900/40 text-blue-700 dark:text-blue-300";
+ case "characterClass":
+ return "bg-purple-100 dark:bg-purple-900/40 text-purple-700 dark:text-purple-300";
+ case "groupStart":
+ case "groupEnd":
+ return "bg-green-100 dark:bg-green-900/40 text-green-700 dark:text-green-300";
+ case "quantifier":
+ return "bg-orange-100 dark:bg-orange-900/40 text-orange-700 dark:text-orange-300";
+ case "special":
+ return "bg-red-100 dark:bg-red-900/40 text-red-700 dark:text-red-300";
+ default:
+ return "bg-muted text-foreground";
+ }
+};
+
+export default function RegexPatternExplainer({
+ pattern,
+}: RegexPatternExplainerProps) {
+ const components = useMemo(() => {
+ if (!pattern) return [];
+ try {
+ return explainPattern(pattern);
+ } catch {
+ return [];
+ }
+ }, [pattern]);
+
+ if (components.length === 0) {
+ return (
+
+ Enter a pattern to see explanation
+
+ );
+ }
+
+ return (
+
+
+ {components.map((component, index) => (
+
+
+
+ {component.value}
+
+
+
+
+ {component.type}
+
+
+ {component.explanation}
+
+
+
+ ))}
+
+
+
Legend:
+
+
+ Escape
+
+
+ Character Class
+
+
+ Group
+
+
+ Quantifier
+
+
+ Special
+
+
+ Literal
+
+
+
+
+ );
+}
diff --git a/components/regex/RegexPresetPatterns.tsx b/components/regex/RegexPresetPatterns.tsx
new file mode 100644
index 0000000..0901b45
--- /dev/null
+++ b/components/regex/RegexPresetPatterns.tsx
@@ -0,0 +1,33 @@
+import { PRESET_PATTERNS } from "@/components/utils/regex-tester.utils";
+import { cn } from "@/lib/utils";
+
+interface RegexPresetPatternsProps {
+ onSelect: (pattern: string, testString: string) => void;
+ selectedPattern: string;
+}
+
+export default function RegexPresetPatterns({
+ onSelect,
+ selectedPattern,
+}: RegexPresetPatternsProps) {
+ return (
+
+ {PRESET_PATTERNS.map((preset) => (
+
+ ))}
+
+ );
+}
diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx
new file mode 100644
index 0000000..9ba528f
--- /dev/null
+++ b/components/ui/hover-card.tsx
@@ -0,0 +1,27 @@
+import * as React from "react";
+import * as HoverCardPrimitive from "@radix-ui/react-hover-card";
+
+import { cn } from "@/lib/utils";
+
+const HoverCard = HoverCardPrimitive.Root;
+
+const HoverCardTrigger = HoverCardPrimitive.Trigger;
+
+const HoverCardContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, align = "center", sideOffset = 4, ...props }, ref) => (
+
+));
+HoverCardContent.displayName = HoverCardPrimitive.Content.displayName;
+
+export { HoverCard, HoverCardTrigger, HoverCardContent };
diff --git a/components/ui/tooltip.tsx b/components/ui/tooltip.tsx
new file mode 100644
index 0000000..675f3ad
--- /dev/null
+++ b/components/ui/tooltip.tsx
@@ -0,0 +1,28 @@
+import * as React from "react";
+import * as TooltipPrimitive from "@radix-ui/react-tooltip";
+
+import { cn } from "@/lib/utils";
+
+const TooltipProvider = TooltipPrimitive.Provider;
+
+const Tooltip = TooltipPrimitive.Root;
+
+const TooltipTrigger = TooltipPrimitive.Trigger;
+
+const TooltipContent = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, sideOffset = 4, ...props }, ref) => (
+
+));
+TooltipContent.displayName = TooltipPrimitive.Content.displayName;
+
+export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider };
diff --git a/components/utils/regex-tester.utils.ts b/components/utils/regex-tester.utils.ts
index ef73a5f..3fe178d 100644
--- a/components/utils/regex-tester.utils.ts
+++ b/components/utils/regex-tester.utils.ts
@@ -1,3 +1,120 @@
+export interface RegexFlags {
+ g: boolean;
+ i: boolean;
+ m: boolean;
+ s: boolean;
+ u: boolean;
+ y: boolean;
+}
+
+export interface CaptureGroup {
+ index: number;
+ name: string | null;
+ pattern: string;
+ start: number;
+ end: number;
+ content: string | null;
+ nested: CaptureGroup[];
+}
+
+export interface RegexMatch {
+ fullMatch: string;
+ index: number;
+ groups: { [key: string]: string | undefined };
+ captureGroups: (string | undefined)[];
+}
+
+export interface PatternComponent {
+ type: string;
+ value: string;
+ explanation: string;
+ start: number;
+ end: number;
+}
+
+export interface MatchStats {
+ totalMatches: number;
+ matchPositions: { start: number; end: number; text: string }[];
+ averageMatchLength: number;
+ executionTime: number;
+ uniqueMatches: number;
+}
+
+export const FLAG_DESCRIPTIONS: Record<
+ keyof RegexFlags,
+ { name: string; description: string }
+> = {
+ g: {
+ name: "Global",
+ description: "Find all matches rather than stopping after the first match",
+ },
+ i: {
+ name: "Case Insensitive",
+ description: "Match letters regardless of case (a matches A)",
+ },
+ m: {
+ name: "Multiline",
+ description: "^ and $ match start/end of each line, not just the string",
+ },
+ s: {
+ name: "DotAll",
+ description: "Dot (.) matches newline characters as well",
+ },
+ u: {
+ name: "Unicode",
+ description: "Enable full Unicode support for the pattern",
+ },
+ y: {
+ name: "Sticky",
+ description: "Match only from the lastIndex position in the target string",
+ },
+};
+
+export const PRESET_PATTERNS = [
+ {
+ name: "Email",
+ pattern: "/^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}$/gm",
+ testString: "test@example.com\ninvalid-email\nuser.name+tag@domain.co.uk",
+ },
+ {
+ name: "URL",
+ pattern: "/https?:\\/\\/[\\w\\-]+(\\.[\\w\\-]+)+[/#?]?.*/gi",
+ testString:
+ "Visit https://example.com/path?query=1\nOr http://sub.domain.org/page#section",
+ },
+ {
+ name: "Phone (US)",
+ pattern: "/\\(?\\d{3}\\)?[-.\\s]?\\d{3}[-.\\s]?\\d{4}/g",
+ testString: "(555) 123-4567\n555.123.4567\n555-123-4567",
+ },
+ {
+ name: "Date (YYYY-MM-DD)",
+ pattern: "/\\d{4}-(0[1-9]|1[0-2])-(0[1-9]|[12]\\d|3[01])/g",
+ testString: "2024-01-15\n2023-12-31\n2024-13-45 (invalid)",
+ },
+ {
+ name: "IPv4 Address",
+ pattern:
+ "/\\b(?:(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\.){3}(?:25[0-5]|2[0-4]\\d|[01]?\\d\\d?)\\b/g",
+ testString: "192.168.1.1\n10.0.0.255\n256.1.1.1 (invalid)",
+ },
+ {
+ name: "Hex Color",
+ pattern: "/#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})\\b/g",
+ testString: "#FF5733\n#abc\n#GGGGGG (invalid)",
+ },
+ {
+ name: "HTML Tag",
+ pattern: "/<([a-z]+)([^<]+)*(?:>(.*)<\\/\\1>|\\s+\\/>)/gi",
+ testString: 'Content
\n
',
+ },
+ {
+ name: "Credit Card",
+ pattern: "/\\b(?:\\d{4}[- ]?){3}\\d{4}\\b/g",
+ testString: "4111-1111-1111-1111\n4111 1111 1111 1111\n4111111111111111",
+ },
+];
+
export const createRegex = (pattern: string): RegExp => {
if (typeof pattern !== "string" || pattern.trim() === "") {
throw new Error("Pattern must be a non-empty string");
@@ -40,3 +157,414 @@ export const createRegex = (pattern: string): RegExp => {
throw error;
}
};
+
+export const parseFlags = (pattern: string): RegexFlags => {
+ const flags: RegexFlags = {
+ g: false,
+ i: false,
+ m: false,
+ s: false,
+ u: false,
+ y: false,
+ };
+
+ if (pattern.startsWith("/")) {
+ const lastSlashIndex = pattern.lastIndexOf("/");
+ if (lastSlashIndex > 0) {
+ const flagStr = pattern.slice(lastSlashIndex + 1);
+ for (const flag of flagStr) {
+ if (flag in flags) {
+ flags[flag as keyof RegexFlags] = true;
+ }
+ }
+ }
+ }
+
+ return flags;
+};
+
+export const buildPatternWithFlags = (
+ patternBody: string,
+ flags: RegexFlags
+): string => {
+ const flagStr = Object.entries(flags)
+ .filter(([, enabled]) => enabled)
+ .map(([flag]) => flag)
+ .join("");
+ return `/${patternBody}/${flagStr}`;
+};
+
+export const getPatternBody = (pattern: string): string => {
+ if (pattern.startsWith("/")) {
+ const lastSlashIndex = pattern.lastIndexOf("/");
+ if (lastSlashIndex > 0) {
+ return pattern.slice(1, lastSlashIndex);
+ }
+ }
+ return pattern;
+};
+
+export const findCaptureGroups = (pattern: string): CaptureGroup[] => {
+ const patternBody = getPatternBody(pattern);
+ const groups: CaptureGroup[] = [];
+ const stack: { start: number; name: string | null }[] = [];
+ let i = 0;
+
+ while (i < patternBody.length) {
+ if (patternBody[i] === "\\") {
+ i += 2;
+ continue;
+ }
+
+ if (patternBody[i] === "[") {
+ let j = i + 1;
+ while (j < patternBody.length && patternBody[j] !== "]") {
+ if (patternBody[j] === "\\") j++;
+ j++;
+ }
+ i = j + 1;
+ continue;
+ }
+
+ if (patternBody[i] === "(") {
+ let name: string | null = null;
+ let isCapturing = true;
+
+ if (patternBody.slice(i + 1, i + 3) === "?:") {
+ isCapturing = false;
+ } else if (
+ patternBody.slice(i + 1, i + 4) === "?<" &&
+ patternBody[i + 4] !== "=" &&
+ patternBody[i + 4] !== "!"
+ ) {
+ const nameEnd = patternBody.indexOf(">", i + 4);
+ if (nameEnd !== -1) {
+ name = patternBody.slice(i + 4, nameEnd);
+ }
+ } else if (
+ patternBody[i + 1] === "?" &&
+ (patternBody[i + 2] === "=" ||
+ patternBody[i + 2] === "!" ||
+ patternBody[i + 2] === "<")
+ ) {
+ isCapturing = false;
+ }
+
+ if (isCapturing) {
+ stack.push({ start: i, name });
+ } else {
+ stack.push({ start: -1, name: null });
+ }
+ } else if (patternBody[i] === ")") {
+ const groupInfo = stack.pop();
+ if (groupInfo && groupInfo.start !== -1) {
+ const groupPattern = patternBody.slice(groupInfo.start, i + 1);
+ groups.push({
+ index: groups.length + 1,
+ name: groupInfo.name,
+ pattern: groupPattern,
+ start: groupInfo.start,
+ end: i + 1,
+ content: null,
+ nested: [],
+ });
+ }
+ }
+
+ i++;
+ }
+
+ return groups.sort((a, b) => a.start - b.start);
+};
+
+export const explainPattern = (pattern: string): PatternComponent[] => {
+ const patternBody = getPatternBody(pattern);
+ const components: PatternComponent[] = [];
+ let i = 0;
+
+ const explanations: Record = {
+ "^": "Start of string/line",
+ $: "End of string/line",
+ ".": "Any character (except newline)",
+ "*": "Zero or more of the preceding",
+ "+": "One or more of the preceding",
+ "?": "Zero or one of the preceding (optional)",
+ "\\d": "Any digit (0-9)",
+ "\\D": "Any non-digit",
+ "\\w": "Any word character (a-z, A-Z, 0-9, _)",
+ "\\W": "Any non-word character",
+ "\\s": "Any whitespace character",
+ "\\S": "Any non-whitespace character",
+ "\\b": "Word boundary",
+ "\\B": "Non-word boundary",
+ "\\n": "Newline",
+ "\\t": "Tab",
+ "\\r": "Carriage return",
+ "|": "Alternation (OR)",
+ };
+
+ while (i < patternBody.length) {
+ const char = patternBody[i];
+
+ if (char === "\\") {
+ const escaped = patternBody.slice(i, i + 2);
+ const explanation =
+ explanations[escaped] || `Escaped character: ${patternBody[i + 1]}`;
+ components.push({
+ type: "escape",
+ value: escaped,
+ explanation,
+ start: i,
+ end: i + 2,
+ });
+ i += 2;
+ continue;
+ }
+
+ if (char === "[") {
+ let j = i + 1;
+ const negated = patternBody[j] === "^";
+ while (j < patternBody.length && patternBody[j] !== "]") {
+ if (patternBody[j] === "\\") j++;
+ j++;
+ }
+ const charClass = patternBody.slice(i, j + 1);
+ const explanation = negated
+ ? `Character class (NOT): matches any character NOT in ${charClass}`
+ : `Character class: matches any character in ${charClass}`;
+ components.push({
+ type: "characterClass",
+ value: charClass,
+ explanation,
+ start: i,
+ end: j + 1,
+ });
+ i = j + 1;
+ continue;
+ }
+
+ if (char === "(") {
+ let groupType = "Capturing group";
+ let skipChars = 1;
+
+ if (patternBody.slice(i + 1, i + 3) === "?:") {
+ groupType = "Non-capturing group";
+ skipChars = 3;
+ } else if (patternBody.slice(i + 1, i + 3) === "?=") {
+ groupType = "Positive lookahead";
+ skipChars = 3;
+ } else if (patternBody.slice(i + 1, i + 3) === "?!") {
+ groupType = "Negative lookahead";
+ skipChars = 3;
+ } else if (patternBody.slice(i + 1, i + 4) === "?<=") {
+ groupType = "Positive lookbehind";
+ skipChars = 4;
+ } else if (patternBody.slice(i + 1, i + 4) === "?", i + 3);
+ if (nameEnd !== -1) {
+ const name = patternBody.slice(i + 3, nameEnd);
+ groupType = `Named capturing group: "${name}"`;
+ }
+ }
+
+ components.push({
+ type: "groupStart",
+ value: "(",
+ explanation: groupType,
+ start: i,
+ end: i + skipChars,
+ });
+ i += skipChars;
+ continue;
+ }
+
+ if (char === ")") {
+ components.push({
+ type: "groupEnd",
+ value: ")",
+ explanation: "End of group",
+ start: i,
+ end: i + 1,
+ });
+ i++;
+ continue;
+ }
+
+ if (char === "{") {
+ let j = i + 1;
+ while (j < patternBody.length && patternBody[j] !== "}") j++;
+ const quantifier = patternBody.slice(i, j + 1);
+ const inner = quantifier.slice(1, -1);
+ let explanation = "";
+
+ if (inner.includes(",")) {
+ const [min, max] = inner.split(",");
+ if (max === "") {
+ explanation = `${min} or more of the preceding`;
+ } else {
+ explanation = `Between ${min} and ${max} of the preceding`;
+ }
+ } else {
+ explanation = `Exactly ${inner} of the preceding`;
+ }
+
+ components.push({
+ type: "quantifier",
+ value: quantifier,
+ explanation,
+ start: i,
+ end: j + 1,
+ });
+ i = j + 1;
+ continue;
+ }
+
+ if (explanations[char]) {
+ components.push({
+ type: "special",
+ value: char,
+ explanation: explanations[char],
+ start: i,
+ end: i + 1,
+ });
+ } else {
+ components.push({
+ type: "literal",
+ value: char,
+ explanation: `Literal character: "${char}"`,
+ start: i,
+ end: i + 1,
+ });
+ }
+ i++;
+ }
+
+ return components;
+};
+
+export const getMatchStats = (
+ pattern: string,
+ testString: string
+): MatchStats => {
+ const startTime = performance.now();
+ const stats: MatchStats = {
+ totalMatches: 0,
+ matchPositions: [],
+ averageMatchLength: 0,
+ executionTime: 0,
+ uniqueMatches: 0,
+ };
+
+ try {
+ const regex = createRegex(pattern);
+ const globalRegex = new RegExp(
+ regex.source,
+ regex.flags.includes("g") ? regex.flags : regex.flags + "g"
+ );
+
+ let match;
+ const uniqueSet = new Set();
+ let totalLength = 0;
+
+ while ((match = globalRegex.exec(testString)) !== null) {
+ stats.totalMatches++;
+ stats.matchPositions.push({
+ start: match.index,
+ end: match.index + match[0].length,
+ text: match[0],
+ });
+ uniqueSet.add(match[0]);
+ totalLength += match[0].length;
+
+ if (match[0].length === 0) {
+ globalRegex.lastIndex++;
+ }
+ }
+
+ stats.uniqueMatches = uniqueSet.size;
+ stats.averageMatchLength =
+ stats.totalMatches > 0 ? totalLength / stats.totalMatches : 0;
+ } catch {
+ // Pattern is invalid, return empty stats
+ }
+
+ stats.executionTime = performance.now() - startTime;
+ return stats;
+};
+
+export const getDetailedMatches = (
+ pattern: string,
+ testString: string
+): RegexMatch[] => {
+ const matches: RegexMatch[] = [];
+
+ try {
+ const regex = createRegex(pattern);
+ const globalRegex = new RegExp(
+ regex.source,
+ regex.flags.includes("g") ? regex.flags : regex.flags + "g"
+ );
+
+ let match;
+ while ((match = globalRegex.exec(testString)) !== null) {
+ matches.push({
+ fullMatch: match[0],
+ index: match.index,
+ groups: match.groups || {},
+ captureGroups: match.slice(1),
+ });
+
+ if (match[0].length === 0) {
+ globalRegex.lastIndex++;
+ }
+ }
+ } catch {
+ // Pattern is invalid, return empty matches
+ }
+
+ return matches;
+};
+
+export const CHEAT_SHEET = {
+ "Character Classes": [
+ { syntax: ".", description: "Any character except newline" },
+ { syntax: "\\w", description: "Word character [a-zA-Z0-9_]" },
+ { syntax: "\\W", description: "Non-word character" },
+ { syntax: "\\d", description: "Digit [0-9]" },
+ { syntax: "\\D", description: "Non-digit" },
+ { syntax: "\\s", description: "Whitespace" },
+ { syntax: "\\S", description: "Non-whitespace" },
+ { syntax: "[abc]", description: "Any of a, b, or c" },
+ { syntax: "[^abc]", description: "Not a, b, or c" },
+ { syntax: "[a-z]", description: "Character range a-z" },
+ ],
+ Anchors: [
+ { syntax: "^", description: "Start of string/line" },
+ { syntax: "$", description: "End of string/line" },
+ { syntax: "\\b", description: "Word boundary" },
+ { syntax: "\\B", description: "Non-word boundary" },
+ ],
+ Quantifiers: [
+ { syntax: "*", description: "0 or more" },
+ { syntax: "+", description: "1 or more" },
+ { syntax: "?", description: "0 or 1 (optional)" },
+ { syntax: "{n}", description: "Exactly n" },
+ { syntax: "{n,}", description: "n or more" },
+ { syntax: "{n,m}", description: "Between n and m" },
+ { syntax: "*?", description: "0 or more (lazy)" },
+ { syntax: "+?", description: "1 or more (lazy)" },
+ ],
+ "Groups & Lookaround": [
+ { syntax: "(abc)", description: "Capturing group" },
+ { syntax: "(?:abc)", description: "Non-capturing group" },
+ { syntax: "(?abc)", description: "Named group" },
+ { syntax: "(?=abc)", description: "Positive lookahead" },
+ { syntax: "(?!abc)", description: "Negative lookahead" },
+ { syntax: "(?<=abc)", description: "Positive lookbehind" },
+ { syntax: "(?=10"
- },
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
+ "license": "MIT",
"peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -2394,70 +2438,51 @@
}
}
},
- "node_modules/@radix-ui/react-popper": {
- "version": "1.2.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
- "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "license": "MIT",
"dependencies": {
- "@floating-ui/react-dom": "^2.0.0",
- "@radix-ui/react-arrow": "1.1.0",
- "@radix-ui/react-compose-refs": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-use-callback-ref": "1.1.0",
- "@radix-ui/react-use-layout-effect": "1.1.0",
- "@radix-ui/react-use-rect": "1.1.0",
- "@radix-ui/react-use-size": "1.1.0",
- "@radix-ui/rect": "1.1.0"
+ "@radix-ui/react-use-effect-event": "0.0.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
- },
- "@types/react-dom": {
- "optional": true
}
}
},
- "node_modules/@radix-ui/react-portal": {
- "version": "1.0.4",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
- "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-primitive": "1.0.3"
+ "@radix-ui/react-use-callback-ref": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
- },
- "@types/react-dom": {
- "optional": true
}
}
},
- "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-compose-refs": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
- "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
- "dependencies": {
- "@babel/runtime": "^7.13.10"
- },
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "license": "MIT",
"peerDependencies": {
"@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -2465,40 +2490,35 @@
}
}
},
- "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": {
- "version": "1.0.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
- "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
+ "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-slot": "1.0.2"
+ "@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0",
- "react-dom": "^16.8 || ^17.0 || ^18.0"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
- },
- "@types/react-dom": {
- "optional": true
}
}
},
- "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
- "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "license": "MIT",
"dependencies": {
- "@babel/runtime": "^7.13.10",
- "@radix-ui/react-compose-refs": "1.0.1"
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
@@ -2506,12 +2526,165 @@
}
}
},
- "node_modules/@radix-ui/react-presence": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz",
- "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==",
+ "node_modules/@radix-ui/react-hover-card/node_modules/@radix-ui/rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
+ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-icons": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-icons/-/react-icons-1.3.0.tgz",
+ "integrity": "sha512-jQxj/0LKgp+j9BiTXz3O3sgs26RNet2iLWmsPyRz2SIcR4q/4SbazXfnYwbAr+vLYKSfc7qxzyGQA1HLlYiuNw==",
+ "peerDependencies": {
+ "react": "^16.x || ^17.x || ^18.x"
+ }
+ },
+ "node_modules/@radix-ui/react-id": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.0.tgz",
+ "integrity": "sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-label": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.0.tgz",
+ "integrity": "sha512-peLblDlFw/ngk3UWq0VnYaOLy6agTZZ+MUO/WhVfm14vJGML+xH4FAl2XQGLqdefjNb7ApRg6Yn7U42ZhmYXdw==",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.0.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popover/-/react-popover-1.1.1.tgz",
+ "integrity": "sha512-3y1A3isulwnWhvTTwmIreiB8CF4L+qRjZnK1wYLO7pplddzXKby/GnZ2M7OZY3qgnl6p9AodUIHRYGXNah8Y7g==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-context": "1.1.0",
+ "@radix-ui/react-dismissable-layer": "1.1.0",
+ "@radix-ui/react-focus-guards": "1.1.0",
+ "@radix-ui/react-focus-scope": "1.1.0",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-popper": "1.2.0",
+ "@radix-ui/react-portal": "1.1.1",
+ "@radix-ui/react-presence": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-slot": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0",
+ "aria-hidden": "^1.1.1",
+ "react-remove-scroll": "2.5.7"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.0.tgz",
+ "integrity": "sha512-/UovfmmXGptwGcBQawLzvn2jOfM0t4z3/uKffoBlj724+n3FvBbZ7M0aaBOmkp6pqFYpO4yx8tSVJjx3Fl2jig==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-use-callback-ref": "1.1.0",
+ "@radix-ui/react-use-escape-keydown": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-guards": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.0.tgz",
+ "integrity": "sha512-w6XZNUPVv6xCpZUqb/yN9DL6auvpGX3C/ee6Hdi16v2UUy25HV2Q5bcflsiDyT/g5RwbPQ/GIT1vLkeRb+ITBw==",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-focus-scope": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.0.tgz",
+ "integrity": "sha512-200UD8zylvEyL8Bx+z76RJnASR2gRMuxlgFCPAe/Q/679a/r0eK3MBVYMb7vZODZcffZBdob1EGnky78xmVvcA==",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-use-callback-ref": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-portal": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.1.tgz",
+ "integrity": "sha512-A3UtLk85UtqhzFqtoC8Q0KvR2GbXF3mtPgACSazajqq6A41mEQgo53iPzY4i6BwDxlIFqWIhiQ2G729n+2aw/g==",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.0.0",
"@radix-ui/react-use-layout-effect": "1.1.0"
},
"peerDependencies": {
@@ -2529,12 +2702,62 @@
}
}
},
- "node_modules/@radix-ui/react-primitive": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
- "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
+ "node_modules/@radix-ui/react-popover/node_modules/@radix-ui/react-use-escape-keydown": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.0.tgz",
+ "integrity": "sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==",
"dependencies": {
- "@radix-ui/react-slot": "1.1.0"
+ "@radix-ui/react-use-callback-ref": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popover/node_modules/react-remove-scroll": {
+ "version": "2.5.7",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.5.7.tgz",
+ "integrity": "sha512-FnrTWO4L7/Bhhf3CYBNArEG/yROV0tKmTv7/3h9QCFvH6sndeFf1wPqOcbFVu5VAulS5dV1wGT3GZZ/1GawqiA==",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.4",
+ "react-style-singleton": "^2.2.1",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.0",
+ "use-sidecar": "^1.1.2"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-popper": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.0.tgz",
+ "integrity": "sha512-ZnRMshKF43aBxVWPWvbj21+7TQCvhuULWJ4gNIKYpRlQt5xGRhLx66tMp8pya2UkGHTSlhpXwmjqltDYHhw7Vg==",
+ "dependencies": {
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.1.0",
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-context": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-use-callback-ref": "1.1.0",
+ "@radix-ui/react-use-layout-effect": "1.1.0",
+ "@radix-ui/react-use-rect": "1.1.0",
+ "@radix-ui/react-use-size": "1.1.0",
+ "@radix-ui/rect": "1.1.0"
},
"peerDependencies": {
"@types/react": "*",
@@ -2551,13 +2774,567 @@
}
}
},
- "node_modules/@radix-ui/react-progress": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz",
- "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==",
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.0.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.0.4.tgz",
+ "integrity": "sha512-Qki+C/EuGUVCQTOTD5vzJzJuMUlewbzuKyUy+/iHM2uwGiru9gZeBJtHAPKAEkB5KWGi9mP/CHKcY0wt1aW45Q==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-primitive": "1.0.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz",
+ "integrity": "sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-primitive": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz",
+ "integrity": "sha512-yi58uVyoAcK/Nq1inRY56ZSjKypBNKTa/1mcL8qdl6oJeEaDbOldlzrGn7P6Q3Id5d+SYNGc5AJgc4vGhjs5+g==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-slot": "1.0.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal/node_modules/@radix-ui/react-slot": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.0.2.tgz",
+ "integrity": "sha512-YeTpuq4deV+6DusvVUW4ivBgnkHwECUu0BiN43L5UCDFgdhsRUWAghhTF5MbvNTPzmiFOx90asDSUjWuCNapwg==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/react-compose-refs": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.0.tgz",
+ "integrity": "sha512-Gq6wuRN/asf9H/E/VzdKoUtT8GC9PQc9z40/vEr0VCJ4u5XvvhWIrSsCB6vD2/cH7ugTdSfYq9fLJCcM00acrQ==",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-use-layout-effect": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-primitive": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz",
+ "integrity": "sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw==",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-progress": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-progress/-/react-progress-1.1.7.tgz",
+ "integrity": "sha512-vPdg/tF6YC/ynuBIJlk1mm7Le0VgW6ub6J2UWnTQ7/D23KXcPI1qy+0vBkgKgd38RCMJavBXpB83HPNFMTb0Fg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-roving-focus": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
+ "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-collection": "1.1.0",
+ "@radix-ui/react-compose-refs": "1.1.0",
+ "@radix-ui/react-context": "1.1.0",
+ "@radix-ui/react-direction": "1.1.0",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-use-callback-ref": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz",
+ "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.1",
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-collection": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-use-previous": "1.1.1",
+ "@radix-ui/react-use-size": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/primitive": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
+ "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-collection": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
+ "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-context": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
+ "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-direction": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
+ "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-effect-event": "0.0.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-layout-effect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-previous": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
+ "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-slot": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
+ "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tabs": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
+ "integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.0",
+ "@radix-ui/react-context": "1.1.0",
+ "@radix-ui/react-direction": "1.1.0",
+ "@radix-ui/react-id": "1.1.0",
+ "@radix-ui/react-presence": "1.1.0",
+ "@radix-ui/react-primitive": "2.0.0",
+ "@radix-ui/react-roving-focus": "1.1.0",
+ "@radix-ui/react-use-controllable-state": "1.1.0"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tooltip/-/react-tooltip-1.2.8.tgz",
+ "integrity": "sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-dismissable-layer": "1.1.11",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-popper": "1.2.8",
+ "@radix-ui/react-portal": "1.1.9",
+ "@radix-ui/react-presence": "1.1.5",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "@radix-ui/react-visually-hidden": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/primitive": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.3.tgz",
+ "integrity": "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==",
+ "license": "MIT"
+ },
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-arrow": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-arrow/-/react-arrow-1.1.7.tgz",
+ "integrity": "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-context": "1.1.2",
"@radix-ui/react-primitive": "2.1.3"
},
"peerDependencies": {
@@ -2575,7 +3352,7 @@
}
}
},
- "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-compose-refs": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-compose-refs": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
"integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
@@ -2590,7 +3367,7 @@
}
}
},
- "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-context": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-context": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
"integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
@@ -2605,13 +3382,17 @@
}
}
},
- "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-primitive": {
- "version": "2.1.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
- "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-dismissable-layer": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.11.tgz",
+ "integrity": "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/primitive": "1.1.3",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-escape-keydown": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2628,13 +3409,13 @@
}
}
},
- "node_modules/@radix-ui/react-progress/node_modules/@radix-ui/react-slot": {
- "version": "1.2.3",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
- "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-id": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-id/-/react-id-1.1.1.tgz",
+ "integrity": "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==",
"license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.2"
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2646,20 +3427,22 @@
}
}
},
- "node_modules/@radix-ui/react-roving-focus": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.0.tgz",
- "integrity": "sha512-EA6AMGeq9AEeQDeSH0aZgG198qkfHSbvWTf1HvoDmOB5bBG/qTxjYMWUKMnYiV6J/iP/J8MEFSuB2zRU2n7ODA==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-popper": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.8.tgz",
+ "integrity": "sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==",
+ "license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.0",
- "@radix-ui/react-collection": "1.1.0",
- "@radix-ui/react-compose-refs": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-direction": "1.1.0",
- "@radix-ui/react-id": "1.1.0",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-use-callback-ref": "1.1.0",
- "@radix-ui/react-use-controllable-state": "1.1.0"
+ "@floating-ui/react-dom": "^2.0.0",
+ "@radix-ui/react-arrow": "1.1.7",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-layout-effect": "1.1.1",
+ "@radix-ui/react-use-rect": "1.1.1",
+ "@radix-ui/react-use-size": "1.1.1",
+ "@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2676,23 +3459,14 @@
}
}
},
- "node_modules/@radix-ui/react-slider": {
- "version": "1.3.5",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slider/-/react-slider-1.3.5.tgz",
- "integrity": "sha512-rkfe2pU2NBAYfGaxa3Mqosi7VZEWX5CxKaanRv0vZd4Zhl9fvQrg0VM93dv3xGLGfrHuoTRF3JXH8nb9g+B3fw==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-portal": {
+ "version": "1.1.9",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-portal/-/react-portal-1.1.9.tgz",
+ "integrity": "sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==",
"license": "MIT",
"dependencies": {
- "@radix-ui/number": "1.1.1",
- "@radix-ui/primitive": "1.1.2",
- "@radix-ui/react-collection": "1.1.7",
- "@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-use-controllable-state": "1.2.2",
- "@radix-ui/react-use-layout-effect": "1.1.1",
- "@radix-ui/react-use-previous": "1.1.1",
- "@radix-ui/react-use-size": "1.1.1"
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2709,22 +3483,14 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/primitive": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/primitive/-/primitive-1.1.2.tgz",
- "integrity": "sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==",
- "license": "MIT"
- },
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-collection": {
- "version": "1.1.7",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-collection/-/react-collection-1.1.7.tgz",
- "integrity": "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-presence": {
+ "version": "1.1.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.5.tgz",
+ "integrity": "sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/react-compose-refs": "1.1.2",
- "@radix-ui/react-context": "1.1.2",
- "@radix-ui/react-primitive": "2.1.3",
- "@radix-ui/react-slot": "1.2.3"
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2741,52 +3507,7 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-compose-refs": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
- "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-context": {
- "version": "1.1.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-context/-/react-context-1.1.2.tgz",
- "integrity": "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==",
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-direction": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-direction/-/react-direction-1.1.1.tgz",
- "integrity": "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==",
- "license": "MIT",
- "peerDependencies": {
- "@types/react": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- }
- }
- },
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-primitive": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-primitive": {
"version": "2.1.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
"integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
@@ -2809,7 +3530,7 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-slot": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-slot": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
"integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
@@ -2827,15 +3548,11 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-controllable-state": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
- "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-callback-ref": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.1.tgz",
+ "integrity": "sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==",
"license": "MIT",
- "dependencies": {
- "@radix-ui/react-use-effect-event": "0.0.2",
- "@radix-ui/react-use-layout-effect": "1.1.1"
- },
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2846,11 +3563,15 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-layout-effect": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
- "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-controllable-state": {
+ "version": "1.2.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.2.2.tgz",
+ "integrity": "sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==",
"license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-effect-event": "0.0.2",
+ "@radix-ui/react-use-layout-effect": "1.1.1"
+ },
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2861,11 +3582,14 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-previous": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-escape-keydown": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-previous/-/react-use-previous-1.1.1.tgz",
- "integrity": "sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.1.1.tgz",
+ "integrity": "sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==",
"license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-use-callback-ref": "1.1.1"
+ },
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2876,14 +3600,11 @@
}
}
},
- "node_modules/@radix-ui/react-slider/node_modules/@radix-ui/react-use-size": {
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-layout-effect": {
"version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
- "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.1.tgz",
+ "integrity": "sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==",
"license": "MIT",
- "dependencies": {
- "@radix-ui/react-use-layout-effect": "1.1.1"
- },
"peerDependencies": {
"@types/react": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
@@ -2894,12 +3615,13 @@
}
}
},
- "node_modules/@radix-ui/react-slot": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.1.0.tgz",
- "integrity": "sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-rect/-/react-use-rect-1.1.1.tgz",
+ "integrity": "sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==",
+ "license": "MIT",
"dependencies": {
- "@radix-ui/react-compose-refs": "1.1.0"
+ "@radix-ui/rect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
@@ -2911,35 +3633,30 @@
}
}
},
- "node_modules/@radix-ui/react-tabs": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.0.tgz",
- "integrity": "sha512-bZgOKB/LtZIij75FSuPzyEti/XBhJH52ExgtdVqjCIh+Nx/FW+LhnbXtbCzIi34ccyMsyOja8T0thCzoHFXNKA==",
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/react-use-size": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-size/-/react-use-size-1.1.1.tgz",
+ "integrity": "sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==",
+ "license": "MIT",
"dependencies": {
- "@radix-ui/primitive": "1.1.0",
- "@radix-ui/react-context": "1.1.0",
- "@radix-ui/react-direction": "1.1.0",
- "@radix-ui/react-id": "1.1.0",
- "@radix-ui/react-presence": "1.1.0",
- "@radix-ui/react-primitive": "2.0.0",
- "@radix-ui/react-roving-focus": "1.1.0",
- "@radix-ui/react-use-controllable-state": "1.1.0"
+ "@radix-ui/react-use-layout-effect": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
- "@types/react-dom": "*",
- "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
- "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
- },
- "@types/react-dom": {
- "optional": true
}
}
},
+ "node_modules/@radix-ui/react-tooltip/node_modules/@radix-ui/rect": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.1.tgz",
+ "integrity": "sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==",
+ "license": "MIT"
+ },
"node_modules/@radix-ui/react-use-callback-ref": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz",
@@ -3101,6 +3818,85 @@
}
}
},
+ "node_modules/@radix-ui/react-visually-hidden": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-visually-hidden/-/react-visually-hidden-1.2.3.tgz",
+ "integrity": "sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-primitive": "2.1.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-compose-refs": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.2.tgz",
+ "integrity": "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==",
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-primitive": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-primitive/-/react-primitive-2.1.3.tgz",
+ "integrity": "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-slot": "1.2.3"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-visually-hidden/node_modules/@radix-ui/react-slot": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-slot/-/react-slot-1.2.3.tgz",
+ "integrity": "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/rect": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@radix-ui/rect/-/rect-1.1.0.tgz",
diff --git a/package.json b/package.json
index 3d19d75..e97b11e 100644
--- a/package.json
+++ b/package.json
@@ -15,6 +15,7 @@
"@cloudflare/speedtest": "^1.6.0",
"@monaco-editor/react": "^4.6.0",
"@radix-ui/react-checkbox": "^1.1.1",
+ "@radix-ui/react-hover-card": "^1.1.15",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.1.0",
"@radix-ui/react-popover": "^1.1.1",
@@ -22,6 +23,7 @@
"@radix-ui/react-slider": "^1.3.5",
"@radix-ui/react-slot": "^1.1.0",
"@radix-ui/react-tabs": "^1.1.0",
+ "@radix-ui/react-tooltip": "^1.2.8",
"class-variance-authority": "^0.7.0",
"clsx": "^2.1.1",
"cmdk": "^1.0.0",
diff --git a/pages/utilities/regex-tester.tsx b/pages/utilities/regex-tester.tsx
index c32a13d..b73a440 100644
--- a/pages/utilities/regex-tester.tsx
+++ b/pages/utilities/regex-tester.tsx
@@ -1,21 +1,106 @@
-import { useState, useEffect, useCallback } from "react";
+import { useState, useEffect, useCallback, useRef } from "react";
import { Textarea } from "@/components/ds/TextareaComponent";
import PageHeader from "@/components/PageHeader";
import { Card } from "@/components/ds/CardComponent";
+import { Button } from "@/components/ds/ButtonComponent";
import { Label } from "@/components/ds/LabelComponent";
import Header from "@/components/Header";
import { CMDK } from "@/components/CMDK";
import Meta from "@/components/Meta";
import RegexHighlightText from "@/components/RegexHighlightText";
-import { createRegex } from "@/components/utils/regex-tester.utils";
+import {
+ createRegex,
+ parseFlags,
+ buildPatternWithFlags,
+ getPatternBody,
+ RegexFlags,
+} from "@/components/utils/regex-tester.utils";
import CallToActionGrid from "@/components/CallToActionGrid";
import GitHubContribution from "@/components/GitHubContribution";
+import RegexFlagToggle from "@/components/regex/RegexFlagToggle";
+import RegexPresetPatterns from "@/components/regex/RegexPresetPatterns";
+import RegexPatternExplainer from "@/components/regex/RegexPatternExplainer";
+import RegexMatchStats from "@/components/regex/RegexMatchStats";
+import RegexCheatSheet from "@/components/regex/RegexCheatSheet";
+import RegexCaptureGroupVisualizer from "@/components/regex/RegexCaptureGroupVisualizer";
+import {
+ Tabs,
+ TabsContent,
+ TabsList,
+ TabsTrigger,
+} from "@/components/ds/TabsComponent";
export default function RegexTester() {
const [pattern, setPattern] = useState("");
const [testString, setTestString] = useState("");
const [result, setResult] = useState("Please fill out");
const [matches, setMatches] = useState(null);
+ const [flags, setFlags] = useState({
+ g: false,
+ i: false,
+ m: false,
+ s: false,
+ u: false,
+ y: false,
+ });
+ const [showCaptureGroups, setShowCaptureGroups] = useState(false);
+ const patternInputRef = useRef(null);
+
+ const handleFlagChange = useCallback(
+ (flag: keyof RegexFlags) => {
+ const newFlags = { ...flags, [flag]: !flags[flag] };
+ setFlags(newFlags);
+
+ const patternBody = getPatternBody(pattern) || "";
+ if (patternBody) {
+ const newPattern = buildPatternWithFlags(patternBody, newFlags);
+ setPattern(newPattern);
+ }
+ },
+ [flags, pattern]
+ );
+
+ const handlePatternChange = useCallback((newPattern: string) => {
+ setPattern(newPattern);
+ const parsedFlags = parseFlags(newPattern);
+ setFlags(parsedFlags);
+ }, []);
+
+ const handlePresetSelect = useCallback(
+ (presetPattern: string, presetTestString: string) => {
+ setPattern(presetPattern);
+ setTestString(presetTestString);
+ const parsedFlags = parseFlags(presetPattern);
+ setFlags(parsedFlags);
+ },
+ []
+ );
+
+ const handleCheatSheetInsert = useCallback(
+ (syntax: string) => {
+ const patternBody = getPatternBody(pattern) || "";
+ const newPatternBody = patternBody + syntax;
+ const newPattern = buildPatternWithFlags(newPatternBody, flags);
+ setPattern(newPattern);
+ patternInputRef.current?.focus();
+ },
+ [pattern, flags]
+ );
+
+ const handleClear = useCallback(() => {
+ setPattern("");
+ setTestString("");
+ setResult("");
+ setMatches(null);
+ setFlags({
+ g: false,
+ i: false,
+ m: false,
+ s: false,
+ u: false,
+ y: false,
+ });
+ }, []);
const handleTest = useCallback(() => {
try {
@@ -60,46 +145,77 @@ export default function RegexTester() {
return (
-