diff --git a/.gitattributes b/.gitattributes index daa9606a6b32d8f00d779e015eb6dc69c4c8f474..c98486e960e59ba7c2a57c0981b9e28123956aae 100644 --- a/.gitattributes +++ b/.gitattributes @@ -2,3 +2,5 @@ *.mp4 filter=lfs diff=lfs merge=lfs -text *.rar filter=lfs diff=lfs merge=lfs -text *.tar filter=lfs diff=lfs merge=lfs -text +workers1/auto3d/node_modules/.bin/esbuild filter=lfs diff=lfs merge=lfs -text +workers1/auto3d/node_modules/.bin/workerd filter=lfs diff=lfs merge=lfs -text diff --git a/workers1/auto3d/CursorLens/src/app/stats/page.tsx b/workers1/auto3d/CursorLens/src/app/stats/page.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e726395e76999f5083e40709377677c314dfe57f --- /dev/null +++ b/workers1/auto3d/CursorLens/src/app/stats/page.tsx @@ -0,0 +1,457 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { getStats, getConfigurations, getConfigurationCosts } from "../actions"; +import type { AIConfiguration } from "@prisma/client"; +import { + BarChart, + Bar, + XAxis, + YAxis, + CartesianGrid, + ResponsiveContainer, + LineChart, + Line, + PieChart, + Pie, + Cell, +} from "recharts"; +import { + ChartContainer, + ChartTooltip, + ChartTooltipContent, + ChartLegend, + ChartLegendContent, +} from "@/components/ui/chart"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/components/ui/select"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Checkbox } from "@/components/ui/checkbox"; + +interface Stats { + totalLogs: number; + totalTokens: number; + totalPromptTokens: number; + totalCompletionTokens: number; + perModelProviderStats: { + [key: string]: { + logs: number; + tokens: number; + promptTokens: number; + completionTokens: number; + cost: number; + provider: string; + model: string; + }; + }; + tokenUsageOverTime: { + date: string; + tokens: number; + promptTokens: number; + completionTokens: number; + }[]; +} + +const chartConfig = { + logs: { + label: "Logs", + color: "hsl(var(--chart-1))", + }, + tokens: { + label: "Total Tokens", + color: "hsl(var(--chart-2))", + }, + promptTokens: { + label: "Input Tokens", + color: "hsl(var(--chart-3))", + }, + completionTokens: { + label: "Output Tokens", + color: "hsl(var(--chart-4))", + }, + cost: { + label: "Cost ($)", + color: "hsl(var(--chart-5))", + }, +}; + +export default function StatsPage() { + const [stats, setStats] = useState(null); + const [configurations, setConfigurations] = useState([]); + const [loading, setLoading] = useState(true); + const [error, setError] = useState(null); + const [timeFilter, setTimeFilter] = useState("all"); + const [tokenUsageOverTime, setTokenUsageOverTime] = useState< + { date: string; tokens: number }[] + >([]); + const [selectedMetrics, setSelectedMetrics] = useState([ + "logs", + "tokens", + "cost", + ]); + + useEffect(() => { + const fetchData = async () => { + try { + const [statsData, configData, costsData] = await Promise.all([ + getStats(timeFilter), + getConfigurations(), + getConfigurationCosts(), + ]); + + const perModelProviderStats: Stats["perModelProviderStats"] = {}; + for (const config of configData) { + perModelProviderStats[`${config.provider}:${config.model}`] = { + logs: 0, + tokens: 0, + promptTokens: 0, + completionTokens: 0, + cost: 0, + provider: config.provider, + model: config.model, + }; + } + + for (const [key, modelStats] of Object.entries( + statsData.perModelProviderStats, + )) { + const [provider, model] = key.split(":"); + const costData = costsData.find( + (c) => c.provider === provider && c.model === model, + ); + const inputTokenCost = costData?.inputTokenCost || 0; + const outputTokenCost = costData?.outputTokenCost || 0; + + perModelProviderStats[key] = { + ...modelStats, + cost: + modelStats.promptTokens * inputTokenCost + + modelStats.completionTokens * outputTokenCost, + }; + } + + setStats({ + totalLogs: statsData.totalLogs, + totalTokens: statsData.totalTokens, + totalPromptTokens: statsData.totalPromptTokens, + totalCompletionTokens: statsData.totalCompletionTokens, + perModelProviderStats, + tokenUsageOverTime: statsData.tokenUsageOverTime, + }); + setTokenUsageOverTime(statsData.tokenUsageOverTime); + setConfigurations(configData); + setLoading(false); + } catch (error) { + console.error("Error fetching data:", error); + setError("Error loading data. Please try again later."); + setLoading(false); + } + }; + fetchData(); + }, [timeFilter]); + + const handleMetricToggle = (metric: string) => { + setSelectedMetrics((prev) => + prev.includes(metric) + ? prev.filter((m) => m !== metric) + : [...prev, metric], + ); + }; + + if (loading) { + return ( +
+ +
+ {["card1", "card2", "card3"].map((key) => ( + + + + + + + + + ))} +
+ {["card1", "card2", "card3"].map((key) => ( + + + + + + + + + ))} +
+ ); + } + + if (error) { + return ( +
+ + + Error + + +

{error}

+
+
+
+ ); + } + + if (!stats) return null; + + const chartData = Object.entries(stats.perModelProviderStats).map( + ([key, data]) => ({ + provider: data.provider, + model: data.model, + logs: data.logs, + tokens: data.tokens, + promptTokens: data.promptTokens, + completionTokens: data.completionTokens, + cost: data.cost, + }), + ); + + const pieChartData = Object.entries(stats.perModelProviderStats).map( + ([key, data]) => ({ + name: key, + value: data.logs, + }), + ); + + const COLORS = ["#0088FE", "#00C49F", "#FFBB28", "#FF8042", "#8884D8"]; + + return ( +
+

Analytics Dashboard

+ +
+ +
+ +
+ + + Total Logs + + +

+ {stats.totalLogs.toLocaleString()} +

+
+
+ + + Total Tokens + + +

+ {stats.totalTokens.toLocaleString()} +

+
+
+ + + Total Cost + + +

+ $ + {Object.values(stats.perModelProviderStats) + .reduce((sum, data) => sum + data.cost, 0) + .toFixed(2)} +

+
+
+
+ +
+ {Object.entries(chartConfig).map(([key, config]) => ( +
+ handleMetricToggle(key)} + /> + +
+ ))} +
+ + + + Per Model and Provider Statistics + + + + + + + + + + { + if (active && payload && payload.length) { + return ( +
+

{`${payload[0].payload.provider}: ${payload[0].payload.model}`}

+ {payload.map((entry) => ( +

+ {`${entry.name}: ${entry.value}`} +

+ ))} +
+ ); + } + return null; + }} + /> + } /> + {selectedMetrics.includes("logs") && ( + + )} + {selectedMetrics.includes("tokens") && ( + + )} + {selectedMetrics.includes("promptTokens") && ( + + )} + {selectedMetrics.includes("completionTokens") && ( + + )} + {selectedMetrics.includes("cost") && ( + + )} +
+
+
+
+
+ + + + Token Usage Over Time + + + + + + + + + } /> + } /> + + + + + + + + + + Model and Provider Usage Distribution + + + + + + + `${name} ${(percent * 100).toFixed(0)}%` + } + > + {pieChartData.map((entry) => ( + + ))} + + } /> + } /> + + + + + +
+ ); +} diff --git a/workers1/auto3d/CursorLens/src/app/twitter-image.png b/workers1/auto3d/CursorLens/src/app/twitter-image.png new file mode 100644 index 0000000000000000000000000000000000000000..9633e94c60339425d6c9d1090989d122b6a3d8be Binary files /dev/null and b/workers1/auto3d/CursorLens/src/app/twitter-image.png differ diff --git a/workers1/auto3d/CursorLens/src/components/LogDetails.tsx b/workers1/auto3d/CursorLens/src/components/LogDetails.tsx new file mode 100644 index 0000000000000000000000000000000000000000..6ccbc67bb5e4758cbe52af24bee3f88e4e6d99af --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/LogDetails.tsx @@ -0,0 +1,427 @@ +"use client"; + +import { useState, useEffect } from "react"; +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { Skeleton } from "@/components/ui/skeleton"; +import { Alert, AlertDescription } from "@/components/ui/alert"; +import { + Prism as SyntaxHighlighter, + SyntaxHighlighterProps, +} from "react-syntax-highlighter"; +import * as themes from "react-syntax-highlighter/dist/esm/styles/prism"; +import { Button } from "@/components/ui/button"; +import { Copy, ChevronDown, ChevronUp } from "lucide-react"; +import ReactMarkdown from "react-markdown"; +import { toast } from "sonner"; +import { useSearchParams } from "next/navigation"; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from "@/components/ui/table"; + +interface Log { + id: number; + method: string; + url: string; + timestamp: string; + headers: string; + body: string; + response: string | null; + metadata: { + inputTokens: number; + outputTokens: number; + totalTokens: number; + inputCost: number; + outputCost: number; + totalCost: number; + }; +} + +interface LogDetailsProps { + logId: string; +} + +export default function LogDetails({ logId }: LogDetailsProps) { + const [log, setLog] = useState(null); + const [error, setError] = useState(null); + const [theme, setTheme] = useState( + null, + ); + const searchParams = useSearchParams(); + const [expandedSections, setExpandedSections] = useState({ + response: true, + body: true, + headers: true, + }); + + useEffect(() => { + const fetchLog = async () => { + if (logId) { + try { + const response = await fetch(`/api/logs/${logId}`); + if (!response.ok) { + throw new Error("Failed to fetch log"); + } + const logData: Log = await response.json(); + setLog(logData); + } catch (err) { + setError("Error fetching log data"); + console.error(err); + } + } + }; + + fetchLog(); + + const loadTheme = () => { + const themeName = "vscDarkPlus"; + setTheme( + themes[ + themeName as keyof typeof themes + ] as SyntaxHighlighterProps["style"], + ); + }; + loadTheme(); + }, [logId, searchParams]); + + const toggleSection = (section: "response" | "body" | "headers") => { + setExpandedSections((prev) => ({ + ...prev, + [section]: !prev[section], + })); + }; + + if (error) { + return ( + + {error} + + ); + } + + if (!log) { + return ( + + + + + + + + + + + + + ); + } + + const parseJSON = (str: string | null) => { + if (!str) return null; + try { + return JSON.parse(str); + } catch { + return str; + } + }; + + const maskSensitiveInfo = (obj: any) => { + const sensitiveKeys = ["authorization", "api-key", "secret"]; + if (typeof obj === "object" && obj !== null) { + Object.keys(obj).forEach((key) => { + if ( + sensitiveKeys.some((sensitiveKey) => + key.toLowerCase().includes(sensitiveKey), + ) + ) { + obj[key] = "************************"; + } else if (typeof obj[key] === "object") { + obj[key] = maskSensitiveInfo(obj[key]); + } + }); + } + return obj; + }; + + const JsonHighlight = ({ + content, + isExpandable = false, + }: { + content: string | null; + isExpandable?: boolean; + }) => { + const [isExpanded, setIsExpanded] = useState(!isExpandable); + const parsedContent = content; + const maskedContent = maskSensitiveInfo(parsedContent); + const jsonString = + JSON.stringify(maskedContent, null, 2) || "No data available"; + + const handleCopy = () => { + navigator.clipboard.writeText(jsonString); + toast.success("Copied to clipboard"); + }; + + const toggleExpand = () => { + setIsExpanded(!isExpanded); + }; + + const renderAIResponse = (response: any) => { + return ( +
+

AI Response

+ + {String(children).replace(/\n$/, "")} + + ) : ( + + {children} + + ); + }, + }} + > + {response.text} + +
+ ); + }; + + const renderMessages = (messages: any[]) => { + return messages + .slice() + .reverse() + .map((message, index) => ( +
+

+ {message.role === "user" + ? "You" + : message.role === "system" + ? "System" + : "Assistant"} +

+ + {String(children).replace(/\n$/, "")} + + ) : ( + + {children} + + ); + }, + }} + > + {message.content} + +
+ )); + }; + + return ( +
+ + {isExpandable && ( + + )} + + {(isExpanded || !isExpandable) && ( + + {jsonString} + + )} + + {parsedContent && parsedContent.text && ( +
+

AI Response

+ {renderAIResponse(parsedContent)} +
+ )} + + {parsedContent && parsedContent.messages && ( +
+

+ Messages (Most recent on top) +

+ {renderMessages(parsedContent.messages)} +
+ )} +
+ ); + }; + + const renderUsageTable = (log: Log) => { + return ( + + + + Input Tokens + Output Tokens + Total Tokens + Input Cost + Output Cost + Total Cost + + + + + {log.metadata.inputTokens} + {log.metadata.outputTokens} + {log.metadata.totalTokens} + ${log.metadata.inputCost.toFixed(4)} + ${log.metadata.outputCost.toFixed(4)} + ${log.metadata.totalCost.toFixed(4)} + + +
+ ); + }; + + return ( + + + Request Details + + +

+ {log.method} {log.url} +

+

{log.timestamp}

+ + {renderUsageTable(log)} + + + toggleSection("response")} + > + Response + + + {expandedSections.response && ( + + + + )} + + + + toggleSection("body")} + > + Body + + + {expandedSections.body && ( + + + + )} + + + + toggleSection("headers")} + > + Headers + + + {expandedSections.headers && ( + + + + )} + +
+
+ ); +} diff --git a/workers1/auto3d/CursorLens/src/components/LogsList.tsx b/workers1/auto3d/CursorLens/src/components/LogsList.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0d318fcc58840e92f16ca16cf322561d295f1155 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/LogsList.tsx @@ -0,0 +1,202 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useRouter } from "next/navigation"; +import { Button } from "@/components/ui/button"; +import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; +import { ScrollArea } from "@/components/ui/scroll-area"; +import { Badge } from "@/components/ui/badge"; +import { DollarSign, Clock, Hash, MessageSquare } from "lucide-react"; +import { getConfigurationCosts } from "@/app/actions"; + +interface LogMetadata { + topP: number; + model: string; + configId: string; + provider: string; + maxTokens: number; + temperature: number; + presencePenalty: number; + frequencyPenalty: number; + totalTokens: number; + totalCost: number; +} + +interface Usage { + promptTokens: number; + completionTokens: number; + totalTokens: number; +} + +interface Message { + role: string; + content: string; + name?: string; + experimental_providerMetadata?: { + anthropic?: { + cacheControl?: { + type: string; + }; + }; + }; +} + +interface RequestBody { + messages: Message[]; + temperature: number; + user: string; + stream: boolean; +} + +interface ResponseData { + text: string; + toolCalls: any[]; + toolResults: any[]; + usage: Usage; + finishReason: string; + rawResponse: { + headers: Record; + }; + warnings: string[]; + experimental_providerMetadata?: { + anthropic?: { + cacheCreationInputTokens?: number; + cacheReadInputTokens?: number; + }; + }; +} + +interface Log { + id: string; + method: string; + url: string; + headers: string; + body: RequestBody; // This will be a JSON string containing RequestBody + response: ResponseData; // This will be a JSON string containing ResponseData + timestamp: string; + metadata: LogMetadata; +} + +interface LogsListProps { + logs: Log[]; + onLogSelect: (logId: string) => void; + selectedLogId?: string; +} + +const LogsListComponent: React.FC = ({ + logs, + onLogSelect, + selectedLogId, +}) => { + const getProviderColor = (provider: string) => { + const colors: Record = { + anthropic: "bg-purple-100 text-purple-800 border-purple-300", + anthropicCached: "bg-indigo-100 text-indigo-800 border-indigo-300", + openai: "bg-green-100 text-green-800 border-green-300", + cohere: "bg-blue-100 text-blue-800 border-blue-300", + mistral: "bg-red-100 text-red-800 border-red-300", + groq: "bg-yellow-100 text-yellow-800 border-yellow-300", + ollama: "bg-orange-100 text-orange-800 border-orange-300", + other: "bg-gray-100 text-gray-800 border-gray-300", + }; + return colors[provider] || "bg-gray-100 text-gray-800 border-gray-300"; + }; + + if (!Array.isArray(logs) || logs.length === 0) { + return

No logs available.

; + } + + return ( +
+ {logs.map((log) => { + const totalTokens = log.metadata.totalTokens || 0; + const totalCost = log.metadata.totalCost || 0; + const firstUserMessage = + log.body.messages.find((m) => m.role === "user" && !("name" in m)) + ?.content || "No message available"; + const truncatedMessage = + firstUserMessage.slice(0, 100) + + (firstUserMessage.length > 100 ? "..." : ""); + const isSelected = selectedLogId === log.id; + const providerColorClass = getProviderColor(log.metadata.provider); + + return ( + onLogSelect(log.id)} + > + + + {truncatedMessage} + + + +
+
+ + {log.metadata.provider} + + + {log.metadata.model} + +
+
+ +
+
+
+ + {new Date(log.timestamp).toLocaleString()} +
+
+ + + {totalCost.toFixed(4)} + +
+
+ + {totalTokens} tokens +
+
+
+
+
+ ); + })} +
+ ); +}; + +export default function LogsList({ + logs, + onLogSelect, + selectedLogId, +}: LogsListProps) { + const router = useRouter(); + + return ( + + + Requests + + + + + + + + ); +} diff --git a/workers1/auto3d/CursorLens/src/components/NavBar.tsx b/workers1/auto3d/CursorLens/src/components/NavBar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..7208c852d16dfe6304c9f0dd14c02cced5d59efc --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/NavBar.tsx @@ -0,0 +1,43 @@ +'use client'; + +import Link from 'next/link'; +import { usePathname } from 'next/navigation'; +import { ThemeToggle } from './theme-toggle'; + +function NavLink({ + href, + children, +}: { + href: string; + children: React.ReactNode; +}) { + const pathname = usePathname(); + const active = pathname === href; + + return ( + + {children} + + ); +} + +export default function NavBar() { + return ( + + ); +} diff --git a/workers1/auto3d/CursorLens/src/components/theme-provider.tsx b/workers1/auto3d/CursorLens/src/components/theme-provider.tsx new file mode 100644 index 0000000000000000000000000000000000000000..530befd6e72948d3cca0e920b5eb9dc5b0a7ffb9 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/theme-provider.tsx @@ -0,0 +1,9 @@ +'use client'; + +import * as React from 'react'; +import { ThemeProvider as NextThemesProvider } from 'next-themes'; +import { type ThemeProviderProps } from 'next-themes/dist/types'; + +export function ThemeProvider({ children, ...props }: ThemeProviderProps) { + return {children}; +} diff --git a/workers1/auto3d/CursorLens/src/components/theme-toggle.tsx b/workers1/auto3d/CursorLens/src/components/theme-toggle.tsx new file mode 100644 index 0000000000000000000000000000000000000000..aeb6e34fb46d5d688d0b1dd842f389c8b14cd687 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/theme-toggle.tsx @@ -0,0 +1,20 @@ +'use client'; + +import * as React from 'react'; +import { Moon, Sun } from 'lucide-react'; +import { useTheme } from 'next-themes'; + +export function ThemeToggle() { + const { setTheme, theme } = useTheme(); + + return ( + + ); +} diff --git a/workers1/auto3d/CursorLens/src/components/ui/alert.tsx b/workers1/auto3d/CursorLens/src/components/ui/alert.tsx new file mode 100644 index 0000000000000000000000000000000000000000..5afd41d142c95c74069ebb5460117e74ead3b9df --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/alert.tsx @@ -0,0 +1,59 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const alertVariants = cva( + "relative w-full rounded-lg border px-4 py-3 text-sm [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes & VariantProps +>(({ className, variant, ...props }, ref) => ( +
+)) +Alert.displayName = "Alert" + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertTitle.displayName = "AlertTitle" + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes +>(({ className, ...props }, ref) => ( +
+)) +AlertDescription.displayName = "AlertDescription" + +export { Alert, AlertTitle, AlertDescription } diff --git a/workers1/auto3d/CursorLens/src/components/ui/badge.tsx b/workers1/auto3d/CursorLens/src/components/ui/badge.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e87d62bf1a2b2a0e1cf0b0dc94c2836525f40b67 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/badge.tsx @@ -0,0 +1,36 @@ +import * as React from "react" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const badgeVariants = cva( + "inline-flex items-center rounded-md border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2", + { + variants: { + variant: { + default: + "border-transparent bg-primary text-primary-foreground shadow hover:bg-primary/80", + secondary: + "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80", + destructive: + "border-transparent bg-destructive text-destructive-foreground shadow hover:bg-destructive/80", + outline: "text-foreground", + }, + }, + defaultVariants: { + variant: "default", + }, + } +) + +export interface BadgeProps + extends React.HTMLAttributes, + VariantProps {} + +function Badge({ className, variant, ...props }: BadgeProps) { + return ( +
+ ) +} + +export { Badge, badgeVariants } diff --git a/workers1/auto3d/CursorLens/src/components/ui/button.tsx b/workers1/auto3d/CursorLens/src/components/ui/button.tsx new file mode 100644 index 0000000000000000000000000000000000000000..0270f644a89ee9736ff0e8000570d55fdc25ee15 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { cva, type VariantProps } from "class-variance-authority" + +import { cn } from "@/lib/utils" + +const buttonVariants = cva( + "inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50", + { + variants: { + variant: { + default: + "bg-primary text-primary-foreground shadow hover:bg-primary/90", + destructive: + "bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90", + outline: + "border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground", + secondary: + "bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80", + ghost: "hover:bg-accent hover:text-accent-foreground", + link: "text-primary underline-offset-4 hover:underline", + }, + size: { + default: "h-9 px-4 py-2", + sm: "h-8 rounded-md px-3 text-xs", + lg: "h-10 rounded-md px-8", + icon: "h-9 w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +) + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button" + return ( + + ) + } +) +Button.displayName = "Button" + +export { Button, buttonVariants } diff --git a/workers1/auto3d/CursorLens/src/components/ui/calendar.tsx b/workers1/auto3d/CursorLens/src/components/ui/calendar.tsx new file mode 100644 index 0000000000000000000000000000000000000000..018fe2644a46f1ed879051fae35c2d2efa14e902 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/calendar.tsx @@ -0,0 +1,72 @@ +"use client" + +import * as React from "react" +import { ChevronLeftIcon, ChevronRightIcon } from "@radix-ui/react-icons" +import { DayPicker } from "react-day-picker" + +import { cn } from "@/lib/utils" +import { buttonVariants } from "@/components/ui/button" + +export type CalendarProps = React.ComponentProps + +function Calendar({ + className, + classNames, + showOutsideDays = true, + ...props +}: CalendarProps) { + return ( + .day-range-end)]:rounded-r-md [&:has(>.day-range-start)]:rounded-l-md first:[&:has([aria-selected])]:rounded-l-md last:[&:has([aria-selected])]:rounded-r-md" + : "[&:has([aria-selected])]:rounded-md" + ), + day: cn( + buttonVariants({ variant: "ghost" }), + "h-8 w-8 p-0 font-normal aria-selected:opacity-100" + ), + day_range_start: "day-range-start", + day_range_end: "day-range-end", + day_selected: + "bg-primary text-primary-foreground hover:bg-primary hover:text-primary-foreground focus:bg-primary focus:text-primary-foreground", + day_today: "bg-accent text-accent-foreground", + day_outside: + "day-outside text-muted-foreground opacity-50 aria-selected:bg-accent/50 aria-selected:text-muted-foreground aria-selected:opacity-30", + day_disabled: "text-muted-foreground opacity-50", + day_range_middle: + "aria-selected:bg-accent aria-selected:text-accent-foreground", + day_hidden: "invisible", + ...classNames, + }} + components={{ + IconLeft: ({ ...props }) => , + IconRight: ({ ...props }) => , + }} + {...props} + /> + ) +} +Calendar.displayName = "Calendar" + +export { Calendar } diff --git a/workers1/auto3d/CursorLens/src/components/ui/card.tsx b/workers1/auto3d/CursorLens/src/components/ui/card.tsx new file mode 100644 index 0000000000000000000000000000000000000000..77e9fb789bf225de3ed80bd10e833ba5495a109d --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/card.tsx @@ -0,0 +1,76 @@ +import * as 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/workers1/auto3d/CursorLens/src/components/ui/chart.tsx b/workers1/auto3d/CursorLens/src/components/ui/chart.tsx new file mode 100644 index 0000000000000000000000000000000000000000..e23eebace13b42ceb53801319ffc22a77c8bde10 --- /dev/null +++ b/workers1/auto3d/CursorLens/src/components/ui/chart.tsx @@ -0,0 +1,370 @@ +'use client'; + +import * as React from 'react'; +import * as RechartsPrimitive from 'recharts'; +import { + NameType, + Payload, + ValueType, +} from 'recharts/types/component/DefaultTooltipContent'; + +import { cn } from '@/lib/utils'; + +// Format: { THEME_NAME: CSS_SELECTOR } +const THEMES = { light: '', dark: '.dark' } as const; + +export type ChartConfig = { + [k in string]: { + label?: React.ReactNode; + icon?: React.ComponentType; + } & ( + | { color?: string; theme?: never } + | { color?: never; theme: Record } + ); +}; + +type ChartContextProps = { + config: ChartConfig; +}; + +const ChartContext = React.createContext(null); + +function useChart() { + const context = React.useContext(ChartContext); + + if (!context) { + throw new Error('useChart must be used within a '); + } + + return context; +} + +const ChartContainer = React.forwardRef< + HTMLDivElement, + React.ComponentProps<'div'> & { + config: ChartConfig; + children: React.ComponentProps< + typeof RechartsPrimitive.ResponsiveContainer + >['children']; + } +>(({ id, className, children, config, ...props }, ref) => { + const uniqueId = React.useId(); + const chartId = `chart-${id || uniqueId.replace(/:/g, '')}`; + + return ( + +
+ + + {children} + +
+
+ ); +}); +ChartContainer.displayName = 'Chart'; + +const ChartStyle = ({ id, config }: { id: string; config: ChartConfig }) => { + const colorConfig = Object.entries(config).filter( + ([_, config]) => config.theme || config.color + ); + + if (!colorConfig.length) { + return null; + } + + return ( +