+```
+## Running
+
+A sample `Dockerfile` is provided in `devboard/` for easy containerization:
+```Dockerfile
+FROM node:18-alpine AS builder
+WORKDIR /app
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
+RUN yarn install --frozen-lockfile || npm install
+COPY . .
+RUN yarn build || npm run build
+
+FROM node:18-alpine AS runner
+WORKDIR /app
+COPY --from=builder /app/.next ./.next
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/package.json ./package.json
+RUN yarn install --production --frozen-lockfile || npm install --production
+ENV NODE_ENV=production
+ENV PORT=3000
+EXPOSE 3000
+CMD ["yarn", "start"]
```
+
## Contributors
+
-
-
-
-
-
-
-
-
-
-
+
+
+
diff --git a/devboard/.env.example b/devboard/.env.example
new file mode 100644
index 0000000..4a70930
--- /dev/null
+++ b/devboard/.env.example
@@ -0,0 +1,2 @@
+GEMINI_API_KEY=your api key
+NEXT_PUBLIC_API_BASE_URL=http://localhost:3000
\ No newline at end of file
diff --git a/devboard/.gitignore b/devboard/.gitignore
new file mode 100644
index 0000000..e72b4d6
--- /dev/null
+++ b/devboard/.gitignore
@@ -0,0 +1,41 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env
+
+# vercel
+.vercel
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
diff --git a/devboard/Dockerfile b/devboard/Dockerfile
new file mode 100644
index 0000000..113b920
--- /dev/null
+++ b/devboard/Dockerfile
@@ -0,0 +1,36 @@
+# DevBoard Frontend Dockerfile
+# Build a production-ready container for Next.js frontend
+
+FROM node:18-alpine AS builder
+WORKDIR /app
+
+# Install dependencies
+COPY package.json yarn.lock* package-lock.json* pnpm-lock.yaml* ./
+RUN yarn install --frozen-lockfile || npm install
+
+# Copy source code
+COPY . .
+
+# Build Next.js app
+RUN yarn build || npm run build
+
+# Production image
+FROM node:18-alpine AS runner
+WORKDIR /app
+
+# Copy built assets from builder
+COPY --from=builder /app/.next ./.next
+COPY --from=builder /app/public ./public
+COPY --from=builder /app/package.json ./package.json
+
+# Install only production dependencies
+RUN yarn install --production --frozen-lockfile || npm install --production
+
+# Set environment variables (override in deployment)
+ENV NODE_ENV=production
+ENV PORT=3000
+
+EXPOSE 3000
+
+# Start Next.js server
+CMD ["yarn", "start"]
diff --git a/devboard/README.md b/devboard/README.md
new file mode 100644
index 0000000..684a44e
--- /dev/null
+++ b/devboard/README.md
@@ -0,0 +1,130 @@
+ # DevBoard Frontend
+
+A modern, feature-rich GitHub widget builder and portfolio dashboard. DevBoard empowers developers to create beautiful, interactive widgets for their GitHub profiles, visualize stats, and showcase their work with premium design tools inspired by Figma, Notion, and Linear.
+
+---
+
+## 🚀 Features
+
+### Widget Builder
+- **Premium Canvas Sizes**: Choose from preset sizes (Badge, Stats Card, Banner, Square, Large Display) or set custom dimensions.
+- **Drag & Drop Elements**: Add text, images, containers, shapes (rectangle, circle, triangle, star), charts, progress bars, badges, buttons, and QR codes.
+- **GitHub Data Integration**: Fetch live GitHub stats (username, name, bio, followers, following, public repos, avatar, creation date, commits) and display them dynamically in widgets.
+- **Inline Editing**: Double-click elements to edit text inline with a beautiful, responsive editor.
+- **Layer Management**: Organize, lock/unlock, and toggle visibility for each element. View all layers in a sidebar.
+- **Grid & Snap**: Toggle grid visibility, adjust grid size, and enable snap-to-grid for precise placement.
+- **Theme Switching**: Instantly switch between dark and light canvas themes.
+- **SVG Export**: Auto-generates production-ready SVG code for your widget. Copy to clipboard with one click.
+- **Save & Privacy**: Save widgets to your dashboard, set them as private, and tag them for easy organization.
+- **Undo/Redo**: Robust history management for all canvas actions.
+
+### Portfolio Dashboard
+- **Profile Integration**: Connect your GitHub account and display widgets on your portfolio page.
+- **Widget Marketplace**: Discover, use, and remix widgets created by the community (coming soon).
+
+### UI/UX
+- **Modern Design**: Inspired by top design tools, with floating toolbars, draggable panels, and smooth transitions.
+- **Accessibility**: Keyboard shortcuts for quick actions, responsive layout for all devices.
+
+---
+
+## 🛠️ Getting Started
+
+### Prerequisites
+- Node.js (v18+ recommended)
+- npm or yarn
+- Docker (optional, for containerized deployment)
+
+### Installation
+
+1. **Clone the repository:**
+ ```sh
+ git clone https://github.com/NitinTheGreat/devboard-frontend.git
+ cd devboard-frontend/devboard
+ ```
+2. **Install dependencies:**
+ ```sh
+ npm install
+ # or
+ yarn install
+ ```
+3. **Run the development server:**
+ ```sh
+ npm run dev
+ # or
+ yarn dev
+ ```
+4. **Open in browser:**
+ Visit [http://localhost:3000](http://localhost:3000)
+
+### Environment Variables
+Create a `.env.local` file in the `devboard` directory and set:
+```
+NEXT_PUBLIC_API_BASE_URL=
+```
+
+---
+
+## 🐳 Docker Deployment
+
+A sample `Dockerfile` is provided for easy containerization. See below for details.
+
+---
+
+## 🧩 Project Structure
+
+- `devboard/app/` — Next.js app routes and pages
+- `devboard/components/` — UI components, widget builder, authentication, marketplace, etc.
+- `devboard/lib/` — Utility functions, context, types
+- `devboard/public/` — Static assets (SVGs, icons)
+- `devboard/types/` — TypeScript types
+
+---
+
+## ✨ Contributing
+
+We welcome contributions to DevBoard! To get started:
+
+1. **Fork the repository** and create your branch:
+ ```sh
+ git checkout -b feature/your-feature-name
+ ```
+2. **Make your changes** (see `devboard/components/widget-builder/index.tsx` for main logic).
+3. **Test locally** and ensure all features work as expected.
+4. **Commit and push**:
+ ```sh
+ git commit -m "Add: your feature description"
+ git push origin feature/your-feature-name
+ ```
+5. **Open a Pull Request** on GitHub. Please describe your changes clearly and reference any related issues.
+
+### Guidelines
+- Write clean, readable code and follow the existing style.
+- Document new features in the README if relevant.
+- Add tests if possible.
+- Be respectful and collaborative.
+
+---
+
+## 👤 Contributor
+
+- **Nitin Kumar Pandey**
+ [LinkedIn](https://www.linkedin.com/in/nitinkrpandey)
+
+---
+
+## 📄 License
+
+This project is licensed under the MIT License. See [LICENSE](../LICENSE) for details.
+
+---
+
+## 💬 Questions & Support
+
+For questions, suggestions, or support, please open an issue or reach out via [LinkedIn](https://www.linkedin.com/in/nitinkrpandey).
+
+---
+
+## 🐳 Dockerfile Example
+
+See below for a ready-to-use Dockerfile to run DevBoard Frontend in a container.
diff --git a/devboard/app/api/llm/generate/route.ts b/devboard/app/api/llm/generate/route.ts
new file mode 100644
index 0000000..d567f63
--- /dev/null
+++ b/devboard/app/api/llm/generate/route.ts
@@ -0,0 +1,5 @@
+import { NextRequest, NextResponse } from 'next/server';
+
+export async function POST(req: NextRequest) {
+ return NextResponse.json({});
+}
\ No newline at end of file
diff --git a/devboard/app/api/readme/generate/route.ts b/devboard/app/api/readme/generate/route.ts
new file mode 100644
index 0000000..a20a032
--- /dev/null
+++ b/devboard/app/api/readme/generate/route.ts
@@ -0,0 +1,103 @@
+import { type NextRequest, NextResponse } from "next/server"
+import { ReadmeInputSchema, type ReadmeState } from "@/lib/types"
+import { createReadmeGraph } from "@/lib/readme-graph"
+
+export async function POST(request: NextRequest) {
+ try {
+ // Validate input
+ const body = await request.json()
+ const validatedInput = ReadmeInputSchema.parse(body)
+
+ const { username, currentContent, isNew, personalInfo } = validatedInput
+
+ // Create streaming response
+ const encoder = new TextEncoder()
+
+ const stream = new ReadableStream({
+ async start(controller) {
+ try {
+ // Initialize the graph
+ const graph = createReadmeGraph()
+
+ // Prepare initial state
+ const initialState: ReadmeState = {
+ username,
+ currentContent,
+ isNew,
+ personalInfo,
+ }
+
+ // Execute the graph
+ const result = await graph.invoke(initialState)
+
+ // Check for errors
+ if (result.error) {
+ const errorData = encoder.encode(
+ `data: ${JSON.stringify({
+ type: "error",
+ error: result.error,
+ })}\n\n`,
+ )
+ controller.enqueue(errorData)
+ controller.close()
+ return
+ }
+
+ // Stream the final content
+ const finalContent = result.finalContent || ""
+ const chunkSize = 10
+
+ for (let i = 0; i < finalContent.length; i += chunkSize) {
+ const chunk = finalContent.slice(i, i + chunkSize)
+ const data = encoder.encode(
+ `data: ${JSON.stringify({
+ type: "content",
+ content: chunk,
+ })}\n\n`,
+ )
+ controller.enqueue(data)
+ // Small delay for streaming effect
+ await new Promise((resolve) => setTimeout(resolve, 50))
+ }
+
+ // Send completion signal
+ const completeData = encoder.encode(
+ `data: ${JSON.stringify({
+ type: "complete",
+ finalContent: finalContent,
+ })}\n\n`,
+ )
+ controller.enqueue(completeData)
+ controller.close()
+ } catch (error) {
+ console.error("Graph execution error:", error)
+ const errorData = encoder.encode(
+ `data: ${JSON.stringify({
+ type: "error",
+ error: error instanceof Error ? error.message : "Unknown error",
+ })}\n\n`,
+ )
+ controller.enqueue(errorData)
+ controller.close()
+ }
+ },
+ })
+
+ return new Response(stream, {
+ headers: {
+ "Content-Type": "text/event-stream",
+ "Cache-Control": "no-cache",
+ Connection: "keep-alive",
+ },
+ })
+ } catch (error) {
+ console.error("API route error:", error)
+
+ // Handle validation errors
+ if (error instanceof Error && error.name === "ZodError") {
+ return NextResponse.json({ error: "Invalid input data", details: error.message }, { status: 400 })
+ }
+
+ return NextResponse.json({ error: "Failed to generate README" }, { status: 500 })
+ }
+}
diff --git a/devboard/app/api/widget/[id]/route.ts b/devboard/app/api/widget/[id]/route.ts
new file mode 100644
index 0000000..6ba5f8a
--- /dev/null
+++ b/devboard/app/api/widget/[id]/route.ts
@@ -0,0 +1,134 @@
+import { type NextRequest, NextResponse } from "next/server"
+
+// GitHub API functions
+async function fetchGitHubUser(username: string) {
+ const response = await fetch(`https://api.github.com/users/${username}`)
+ if (!response.ok) throw new Error("User not found")
+ return response.json()
+}
+
+async function fetchGitHubStats(username: string) {
+ // Mock implementation - replace with actual GitHub GraphQL API
+ return {
+ totalCommitContributions: Math.floor(Math.random() * 2000) + 500,
+ totalPullRequestContributions: Math.floor(Math.random() * 200) + 50,
+ totalIssueContributions: Math.floor(Math.random() * 100) + 20,
+ contributionYears: new Date().getFullYear() - 2018,
+ totalStars: Math.floor(Math.random() * 1000) + 100,
+ }
+}
+
+// Theme definitions
+const THEMES = {
+ dark: {
+ backgroundColor: "#0D1117",
+ primaryColor: "#58A6FF",
+ secondaryColor: "#21262D",
+ textColor: "#F0F6FC",
+ accentColor: "#238636",
+ },
+ light: {
+ backgroundColor: "#FFFFFF",
+ primaryColor: "#0969DA",
+ secondaryColor: "#F6F8FA",
+ textColor: "#24292F",
+ accentColor: "#1A7F37",
+ },
+ github: {
+ backgroundColor: "#0D1117",
+ primaryColor: "#F78166",
+ secondaryColor: "#161B22",
+ textColor: "#E6EDF3",
+ accentColor: "#7C3AED",
+ },
+ ocean: {
+ backgroundColor: "#0F172A",
+ primaryColor: "#0EA5E9",
+ secondaryColor: "#1E293B",
+ textColor: "#F1F5F9",
+ accentColor: "#06B6D4",
+ },
+}
+
+function replaceVariables(content: string, userData: any, stats: any, theme: any) {
+ let processedContent = content
+
+ // Replace GitHub variables
+ processedContent = processedContent.replace(/\{\{username\}\}/g, userData.login)
+ processedContent = processedContent.replace(/\{\{name\}\}/g, userData.name || userData.login)
+ processedContent = processedContent.replace(/\{\{bio\}\}/g, userData.bio || "No bio available")
+ processedContent = processedContent.replace(/\{\{followers\}\}/g, userData.followers.toString())
+ processedContent = processedContent.replace(/\{\{following\}\}/g, userData.following.toString())
+ processedContent = processedContent.replace(/\{\{public_repos\}\}/g, userData.public_repos.toString())
+ processedContent = processedContent.replace(/\{\{public_gists\}\}/g, userData.public_gists.toString())
+ processedContent = processedContent.replace(/\{\{avatar_url\}\}/g, userData.avatar_url)
+ processedContent = processedContent.replace(
+ /\{\{created_at\}\}/g,
+ new Date(userData.created_at).getFullYear().toString(),
+ )
+
+ // Replace stats variables
+ processedContent = processedContent.replace(
+ /\{\{totalCommitContributions\}\}/g,
+ stats.totalCommitContributions.toString(),
+ )
+ processedContent = processedContent.replace(
+ /\{\{totalPullRequestContributions\}\}/g,
+ stats.totalPullRequestContributions.toString(),
+ )
+ processedContent = processedContent.replace(
+ /\{\{totalIssueContributions\}\}/g,
+ stats.totalIssueContributions.toString(),
+ )
+ processedContent = processedContent.replace(/\{\{contributionYears\}\}/g, stats.contributionYears.toString())
+ processedContent = processedContent.replace(/\{\{totalStars\}\}/g, stats.totalStars.toString())
+
+ return processedContent
+}
+
+export async function GET(request: NextRequest, { params }: { params: { id: string } }) {
+ try {
+ const { searchParams } = new URL(request.url)
+ const username = searchParams.get("username")
+ const themeName = searchParams.get("theme") || "dark"
+
+ if (!username) {
+ return new NextResponse("Username parameter is required", { status: 400 })
+ }
+
+ // Fetch widget from your backend
+ const apiUrl = process.env.NEXT_PUBLIC_API_BASE_URL
+ const widgetResponse = await fetch(`${apiUrl}/api/widget/${params.id}`)
+
+ if (!widgetResponse.ok) {
+ return new NextResponse("Widget not found", { status: 404 })
+ }
+
+ const widget = await widgetResponse.json()
+
+ // Fetch GitHub data
+ const [userData, stats] = await Promise.all([fetchGitHubUser(username), fetchGitHubStats(username)])
+
+ // Get theme
+ const theme = THEMES[themeName as keyof typeof THEMES] || THEMES.dark
+
+ // Process widget content
+ const processedContent = replaceVariables(widget.content, userData, stats, theme)
+
+ // Generate final SVG
+ const svg = `
+
+ ${processedContent}
+ `
+
+ return new NextResponse(svg, {
+ headers: {
+ "Content-Type": "image/svg+xml",
+ "Cache-Control": "public, max-age=3600", // Cache for 1 hour
+ },
+ })
+ } catch (error) {
+ console.error("Widget rendering error:", error)
+ return new NextResponse("Internal server error", { status: 500 })
+ }
+}
diff --git a/devboard/app/api/widget/route.ts b/devboard/app/api/widget/route.ts
new file mode 100644
index 0000000..5b4bd02
--- /dev/null
+++ b/devboard/app/api/widget/route.ts
@@ -0,0 +1,61 @@
+import { type NextRequest, NextResponse } from "next/server"
+
+export async function POST(request: NextRequest) {
+ try {
+ const body = await request.json()
+ const authHeader = request.headers.get("authorization")
+
+ if (!authHeader) {
+ return NextResponse.json({ error: "Authorization required" }, { status: 401 })
+ }
+
+ // Forward to your existing backend
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/widget`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: authHeader,
+ },
+ body: JSON.stringify(body),
+ })
+
+ if (!response.ok) {
+ const errorText = await response.text()
+ console.error("Backend error:", errorText)
+ return NextResponse.json({ error: "Failed to save widget" }, { status: response.status })
+ }
+
+ const result = await response.json()
+ return NextResponse.json(result)
+ } catch (error) {
+ console.error("API error:", error)
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 })
+ }
+}
+
+export async function GET(request: NextRequest) {
+ try {
+ const authHeader = request.headers.get("authorization")
+
+ if (!authHeader) {
+ return NextResponse.json({ error: "Authorization required" }, { status: 401 })
+ }
+
+ // Forward to your existing backend
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/widget`, {
+ headers: {
+ Authorization: authHeader,
+ },
+ })
+
+ if (!response.ok) {
+ return NextResponse.json({ error: "Failed to fetch widgets" }, { status: response.status })
+ }
+
+ const result = await response.json()
+ return NextResponse.json(result)
+ } catch (error) {
+ console.error("API error:", error)
+ return NextResponse.json({ error: "Internal server error" }, { status: 500 })
+ }
+}
diff --git a/devboard/app/favicon.ico b/devboard/app/favicon.ico
new file mode 100644
index 0000000..00b9556
Binary files /dev/null and b/devboard/app/favicon.ico differ
diff --git a/devboard/app/globals.css b/devboard/app/globals.css
new file mode 100644
index 0000000..dc98be7
--- /dev/null
+++ b/devboard/app/globals.css
@@ -0,0 +1,122 @@
+@import "tailwindcss";
+@import "tw-animate-css";
+
+@custom-variant dark (&:is(.dark *));
+
+@theme inline {
+ --color-background: var(--background);
+ --color-foreground: var(--foreground);
+ --font-sans: var(--font-geist-sans);
+ --font-mono: var(--font-geist-mono);
+ --color-sidebar-ring: var(--sidebar-ring);
+ --color-sidebar-border: var(--sidebar-border);
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
+ --color-sidebar-accent: var(--sidebar-accent);
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
+ --color-sidebar-primary: var(--sidebar-primary);
+ --color-sidebar-foreground: var(--sidebar-foreground);
+ --color-sidebar: var(--sidebar);
+ --color-chart-5: var(--chart-5);
+ --color-chart-4: var(--chart-4);
+ --color-chart-3: var(--chart-3);
+ --color-chart-2: var(--chart-2);
+ --color-chart-1: var(--chart-1);
+ --color-ring: var(--ring);
+ --color-input: var(--input);
+ --color-border: var(--border);
+ --color-destructive: var(--destructive);
+ --color-accent-foreground: var(--accent-foreground);
+ --color-accent: var(--accent);
+ --color-muted-foreground: var(--muted-foreground);
+ --color-muted: var(--muted);
+ --color-secondary-foreground: var(--secondary-foreground);
+ --color-secondary: var(--secondary);
+ --color-primary-foreground: var(--primary-foreground);
+ --color-primary: var(--primary);
+ --color-popover-foreground: var(--popover-foreground);
+ --color-popover: var(--popover);
+ --color-card-foreground: var(--card-foreground);
+ --color-card: var(--card);
+ --radius-sm: calc(var(--radius) - 4px);
+ --radius-md: calc(var(--radius) - 2px);
+ --radius-lg: var(--radius);
+ --radius-xl: calc(var(--radius) + 4px);
+}
+
+:root {
+ --radius: 0.625rem;
+ --background: oklch(1 0 0);
+ --foreground: oklch(0.145 0 0);
+ --card: oklch(1 0 0);
+ --card-foreground: oklch(0.145 0 0);
+ --popover: oklch(1 0 0);
+ --popover-foreground: oklch(0.145 0 0);
+ --primary: oklch(0.205 0 0);
+ --primary-foreground: oklch(0.985 0 0);
+ --secondary: oklch(0.97 0 0);
+ --secondary-foreground: oklch(0.205 0 0);
+ --muted: oklch(0.97 0 0);
+ --muted-foreground: oklch(0.556 0 0);
+ --accent: oklch(0.97 0 0);
+ --accent-foreground: oklch(0.205 0 0);
+ --destructive: oklch(0.577 0.245 27.325);
+ --border: oklch(0.922 0 0);
+ --input: oklch(0.922 0 0);
+ --ring: oklch(0.708 0 0);
+ --chart-1: oklch(0.646 0.222 41.116);
+ --chart-2: oklch(0.6 0.118 184.704);
+ --chart-3: oklch(0.398 0.07 227.392);
+ --chart-4: oklch(0.828 0.189 84.429);
+ --chart-5: oklch(0.769 0.188 70.08);
+ --sidebar: oklch(0.985 0 0);
+ --sidebar-foreground: oklch(0.145 0 0);
+ --sidebar-primary: oklch(0.205 0 0);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.97 0 0);
+ --sidebar-accent-foreground: oklch(0.205 0 0);
+ --sidebar-border: oklch(0.922 0 0);
+ --sidebar-ring: oklch(0.708 0 0);
+}
+
+.dark {
+ --background: oklch(0.145 0 0);
+ --foreground: oklch(0.985 0 0);
+ --card: oklch(0.205 0 0);
+ --card-foreground: oklch(0.985 0 0);
+ --popover: oklch(0.205 0 0);
+ --popover-foreground: oklch(0.985 0 0);
+ --primary: oklch(0.922 0 0);
+ --primary-foreground: oklch(0.205 0 0);
+ --secondary: oklch(0.269 0 0);
+ --secondary-foreground: oklch(0.985 0 0);
+ --muted: oklch(0.269 0 0);
+ --muted-foreground: oklch(0.708 0 0);
+ --accent: oklch(0.269 0 0);
+ --accent-foreground: oklch(0.985 0 0);
+ --destructive: oklch(0.704 0.191 22.216);
+ --border: oklch(1 0 0 / 10%);
+ --input: oklch(1 0 0 / 15%);
+ --ring: oklch(0.556 0 0);
+ --chart-1: oklch(0.488 0.243 264.376);
+ --chart-2: oklch(0.696 0.17 162.48);
+ --chart-3: oklch(0.769 0.188 70.08);
+ --chart-4: oklch(0.627 0.265 303.9);
+ --chart-5: oklch(0.645 0.246 16.439);
+ --sidebar: oklch(0.205 0 0);
+ --sidebar-foreground: oklch(0.985 0 0);
+ --sidebar-primary: oklch(0.488 0.243 264.376);
+ --sidebar-primary-foreground: oklch(0.985 0 0);
+ --sidebar-accent: oklch(0.269 0 0);
+ --sidebar-accent-foreground: oklch(0.985 0 0);
+ --sidebar-border: oklch(1 0 0 / 10%);
+ --sidebar-ring: oklch(0.556 0 0);
+}
+
+@layer base {
+ * {
+ @apply border-border outline-ring/50;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
diff --git a/devboard/app/layout.tsx b/devboard/app/layout.tsx
new file mode 100644
index 0000000..eb96d56
--- /dev/null
+++ b/devboard/app/layout.tsx
@@ -0,0 +1,44 @@
+import type React from "react"
+import type { Metadata } from "next"
+import { Inter } from "next/font/google"
+import "./globals.css"
+import { AuthProvider } from "@/lib/auth-context"
+import { Toaster } from "sonner"
+import Navbar from "@/components/Navbar"
+
+const inter = Inter({ subsets: ["latin"] })
+
+export const metadata: Metadata = {
+ title: "DevBoard ",
+ description: "Transform your GitHub README into a stunning portfolio website and many more",
+}
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode
+}) {
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+
+ )
+}
diff --git a/devboard/app/login/page.tsx b/devboard/app/login/page.tsx
new file mode 100644
index 0000000..b7899d0
--- /dev/null
+++ b/devboard/app/login/page.tsx
@@ -0,0 +1,27 @@
+import { Suspense } from "react"
+import { Loader2 } from "lucide-react"
+import LoginContent from "@/components/login-content"
+import LoginAuthHandler from "@/components/login-auth-handler"
+
+// Loading component for Suspense fallback
+function LoginLoading() {
+ return (
+
+
+
+ )
+}
+
+export default function LoginPage() {
+ return (
+ <>
+ }>
+
+
+
+ >
+ )
+}
diff --git a/devboard/app/page.tsx b/devboard/app/page.tsx
new file mode 100644
index 0000000..d410e39
--- /dev/null
+++ b/devboard/app/page.tsx
@@ -0,0 +1,28 @@
+import { Suspense } from "react"
+import { Loader2 } from "lucide-react"
+import HeroSection from "@/components/Hero"
+import AuthHandler from "@/components/auth-handler"
+import Marketplace from "@/components/marketplace"
+// Loading component for Suspense fallback
+function PageLoading() {
+ return (
+
+
+
+ )
+}
+
+export default function Home() {
+ return (
+ <>
+ }>
+
+
+
+
+ >
+ )
+}
diff --git a/devboard/app/portfolio/page.tsx b/devboard/app/portfolio/page.tsx
new file mode 100644
index 0000000..e5b97b6
--- /dev/null
+++ b/devboard/app/portfolio/page.tsx
@@ -0,0 +1,398 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useRef, useEffect } from "react"
+import { useRouter } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import { Textarea } from "@/components/ui/textarea"
+import { Label } from "@/components/ui/label"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Progress } from "@/components/ui/progress"
+import { Loader2, Upload, Copy, Download, ArrowLeft } from "lucide-react"
+import { toast } from "sonner"
+import { useAuth } from "@/lib/auth-context"
+
+interface GenerationState {
+ currentStep: string
+ progress: number
+ parsedData: string
+ portfolioCode: string
+ error: string | null
+}
+
+export default function PortfolioGenerator() {
+ const [isUploading, setIsUploading] = useState(false)
+ const [isGenerating, setIsGenerating] = useState(false)
+ const [content, setContent] = useState("")
+ const [customMessage, setCustomMessage] = useState("")
+ const [style, setStyle] = useState("minimal")
+ const [generationState, setGenerationState] = useState({
+ currentStep: "",
+ progress: 0,
+ parsedData: "",
+ portfolioCode: "",
+ error: null,
+ })
+ const [streamingCode, setStreamingCode] = useState("")
+ const fileInputRef = useRef(null)
+ const codeRef = useRef(null)
+ const { isAuthenticated } = useAuth()
+ const router = useRouter()
+
+ // Auto-scroll to bottom of code as it streams
+ useEffect(() => {
+ if (codeRef.current) {
+ codeRef.current.scrollTop = codeRef.current.scrollHeight
+ }
+ }, [streamingCode])
+
+ const handleFileUpload = async (e: React.ChangeEvent) => {
+ const file = e.target.files?.[0]
+ if (!file) return
+
+ setIsUploading(true)
+ try {
+ const text = await file.text()
+ setContent(text)
+ toast.success(`${file.name} uploaded successfully`)
+ } catch (error) {
+ toast.error("Error uploading file")
+ } finally {
+ setIsUploading(false)
+ }
+ }
+
+ const handleGenerate = async () => {
+ if (!isAuthenticated) {
+ toast.error("Please log in to generate your portfolio")
+ router.push("/login")
+ return
+ }
+
+ if (!content) {
+ toast.error("Please upload a README or resume first")
+ return
+ }
+
+ setIsGenerating(true)
+ setStreamingCode("")
+ setGenerationState({
+ currentStep: "starting",
+ progress: 0,
+ parsedData: "",
+ portfolioCode: "",
+ error: null,
+ })
+
+ try {
+ const response = await fetch("/api/portfolio/generate", {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({ content, customMessage, style }),
+ })
+
+ if (!response.ok) {
+ throw new Error("Failed to start generation")
+ }
+
+ const reader = response.body?.getReader()
+ const decoder = new TextDecoder()
+
+ if (reader) {
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+
+ const chunk = decoder.decode(value)
+ const lines = chunk.split("\n")
+
+ for (const line of lines) {
+ if (line.startsWith("data: ")) {
+ try {
+ const data = JSON.parse(line.slice(6))
+
+ if (data.type === "complete") {
+ setIsGenerating(false)
+ toast.success("Portfolio generated successfully!")
+ break
+ }
+
+ if (data.type === "error") {
+ throw new Error(data.error)
+ }
+
+ // Update generation state
+ if (data.currentStep || data.progress !== undefined) {
+ setGenerationState((prev) => ({
+ ...prev,
+ currentStep: data.currentStep || prev.currentStep,
+ progress: data.progress !== undefined ? data.progress : prev.progress,
+ parsedData: data.parsedData || prev.parsedData,
+ portfolioCode: data.portfolioCode || prev.portfolioCode,
+ }))
+ }
+
+ // Handle streaming code generation
+ if (data.currentStep === "generating" && data.portfolioCode) {
+ setStreamingCode(data.portfolioCode)
+ }
+ } catch (e) {
+ // Ignore parsing errors for incomplete chunks
+ }
+ }
+ }
+ }
+ }
+ } catch (error) {
+ console.error("Generation error:", error)
+ toast.error("Failed to generate portfolio")
+ setGenerationState((prev) => ({
+ ...prev,
+ error: error instanceof Error ? error.message : "Unknown error",
+ currentStep: "error",
+ }))
+ } finally {
+ setIsGenerating(false)
+ }
+ }
+
+ const copyToClipboard = async () => {
+ try {
+ await navigator.clipboard.writeText(generationState.portfolioCode || streamingCode)
+ toast.success("Code copied to clipboard!")
+ } catch (error) {
+ toast.error("Failed to copy code")
+ }
+ }
+
+ const downloadCode = () => {
+ const code = generationState.portfolioCode || streamingCode
+ const blob = new Blob([code], { type: "text/javascript" })
+ const url = URL.createObjectURL(blob)
+ const a = document.createElement("a")
+ a.href = url
+ a.download = "Portfolio.jsx"
+ document.body.appendChild(a)
+ a.click()
+ document.body.removeChild(a)
+ URL.revokeObjectURL(url)
+ toast.success("Code downloaded!")
+ }
+
+ const getStepDescription = (step: string) => {
+ switch (step) {
+ case "starting":
+ return "Initializing portfolio generation..."
+ case "parsing":
+ return "Analyzing your content and extracting key information..."
+ case "generating":
+ return "Generating your React portfolio code..."
+ case "complete":
+ return "Portfolio generation completed!"
+ case "error":
+ return "An error occurred during generation"
+ default:
+ return "Processing..."
+ }
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
router.back()}
+ className="bg-[#1A1625] border-[#3F1469] hover:bg-[#211D2E] text-white"
+ >
+
+ Back
+
+
Portfolio Generator
+
+
+
+ {/* Configuration Panel */}
+
+
+ Configuration
+
+
+ {/* File Upload */}
+
+
+ Upload README/Resume
+
+ fileInputRef.current?.click()}
+ disabled={isUploading || isGenerating}
+ >
+ {isUploading ? (
+
+ ) : (
+
+ )}
+ {isUploading ? "Uploading..." : "Choose File"}
+
+
+
+
+ {/* Content Textarea */}
+
+
+ Content
+
+
+
+ {/* Style Selection */}
+
+
+ Design Style
+
+
+
+
+
+
+ Minimal
+ Modern
+ Creative
+ Professional
+ Dark Theme
+
+
+
+
+ {/* Custom Message */}
+
+
+ Custom Requirements
+
+
+
+ {/* Generate Button */}
+
+ {isGenerating ? (
+ <>
+
+ Generating...
+ >
+ ) : (
+ "Generate Portfolio"
+ )}
+
+
+ {/* Progress */}
+ {isGenerating && (
+
+
+ {getStepDescription(generationState.currentStep)}
+ {generationState.progress}%
+
+
+
+ )}
+
+ {/* Error Display */}
+ {generationState.error && (
+
+
{generationState.error}
+
+ )}
+
+
+
+ {/* Code Display Panel */}
+
+
+
+
Generated Code
+ {(generationState.portfolioCode || streamingCode) && (
+
+
+
+ Copy
+
+
+
+ Download
+
+
+ )}
+
+
+
+
+ {generationState.portfolioCode || streamingCode ? (
+
+ {streamingCode || generationState.portfolioCode}
+
+ ) : (
+
+
+
+
Ready to Generate
+
+ Upload your content and click generate to see your React portfolio code appear here in
+ real-time.
+
+
+
+ )}
+
+
+
+
+
+
+ )
+}
diff --git a/devboard/app/profile/page.tsx b/devboard/app/profile/page.tsx
new file mode 100644
index 0000000..a5446ef
--- /dev/null
+++ b/devboard/app/profile/page.tsx
@@ -0,0 +1,515 @@
+"use client"
+
+import { useState, useEffect } from "react"
+import { useRouter } from "next/navigation"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Button } from "@/components/ui/button"
+import { Badge } from "@/components/ui/badge"
+import {
+ User,
+ Star,
+ Clock,
+ ArrowLeft,
+ Trash2,
+ Plus,
+ Eye,
+ EyeOff,
+ Calendar,
+ Code,
+ Activity,
+ GitFork,
+} from "lucide-react"
+import { useAuth } from "@/lib/auth-context"
+import { toast } from "sonner"
+import ProtectedRoute from "@/components/protected-route"
+
+// Cookie utility
+const getCookie = (name: string): string | null => {
+ if (typeof window === "undefined") return null
+ try {
+ const nameEQ = name + "="
+ const ca = document.cookie.split(";")
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i]
+ while (c.charAt(0) === " ") c = c.substring(1, c.length)
+ if (c.indexOf(nameEQ) === 0) {
+ return c.substring(nameEQ.length, c.length)
+ }
+ }
+ return null
+ } catch (error) {
+ console.error(`Failed to retrieve cookie ${name}:`, error)
+ return null
+ }
+}
+
+// API Functions
+const apiCall = async (endpoint: string, options: RequestInit = {}) => {
+ const accessToken = getCookie("devboard_access_token")
+ if (!accessToken) {
+ throw new Error("Please log in to continue")
+ }
+
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${endpoint}`, {
+ ...options,
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${accessToken}`,
+ ...options.headers,
+ },
+ })
+
+ if (!response.ok) {
+ const errorText = await response.text()
+ let errorMessage = "An error occurred"
+ try {
+ const errorJson = JSON.parse(errorText)
+ errorMessage = errorJson.error || errorJson.message || errorMessage
+ } catch {
+ errorMessage = errorText || errorMessage
+ }
+ throw new Error(errorMessage)
+ }
+
+ const contentType = response.headers.get("content-type")
+ if (contentType && contentType.includes("application/json")) {
+ return response.json()
+ }
+ return response.text()
+}
+
+interface Widget {
+ _id: string
+ name: string
+ content: string
+ size: { width: number; height: number }
+ isPrivate: boolean
+ Tags: string[]
+ createdAt: string
+ updatedAt: string
+}
+
+function ProfileContent() {
+ const { user, isAuthenticated } = useAuth()
+ const [githubData, setGithubData] = useState(null)
+ const [userWidgets, setUserWidgets] = useState([])
+ const [isLoading, setIsLoading] = useState(true)
+ const [isLoadingWidgets, setIsLoadingWidgets] = useState(true)
+ const router = useRouter()
+
+ useEffect(() => {
+ const fetchGitHubProfile = async () => {
+ if (!user?.username) return
+ try {
+ const response = await fetch(`https://api.github.com/users/${user.username}`)
+ if (response.ok) {
+ const data = await response.json()
+ setGithubData(data)
+ }
+ } catch (error) {
+ console.error("Failed to fetch GitHub profile:", error)
+ toast.error("Failed to load GitHub profile data")
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ fetchGitHubProfile()
+ }, [user])
+
+ useEffect(() => {
+ const fetchUserWidgets = async () => {
+ try {
+ setIsLoadingWidgets(true)
+ const widgets = await apiCall("/api/widget/all")
+ setUserWidgets(Array.isArray(widgets) ? widgets : [])
+ } catch (error: any) {
+ if (!error.message.includes("No widgets found")) {
+ toast.error(`Failed to load widgets: ${error.message}`)
+ }
+ setUserWidgets([])
+ } finally {
+ setIsLoadingWidgets(false)
+ }
+ }
+
+ if (user) {
+ fetchUserWidgets()
+ }
+ }, [user])
+
+ const handleDeleteWidget = async (widgetId: string) => {
+ if (!confirm("Are you sure you want to delete this widget?")) return
+
+ try {
+ await apiCall(`/api/widget/${widgetId}`, {
+ method: "DELETE",
+ })
+ setUserWidgets((prev) => prev.filter((widget) => widget._id !== widgetId))
+ toast.success("Widget deleted successfully")
+ } catch (error: any) {
+ toast.error(`Failed to delete widget: ${error.message}`)
+ }
+ }
+
+ if (!isAuthenticated || !user) {
+ return null
+ }
+
+ const formatDate = (dateString: string) => {
+ return new Date(dateString).toLocaleDateString("en-US", {
+ year: "numeric",
+ month: "long",
+ day: "numeric",
+ })
+ }
+
+ const formatNumber = (num: number) => {
+ if (num >= 1000) {
+ return (num / 1000).toFixed(1) + "k"
+ }
+ return num.toString()
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
router.back()}
+ className="bg-gray-900 border-gray-700 hover:bg-gray-800 text-white cursor-pointer"
+ >
+
+ Back
+
+
Profile
+
+
+
+ {/* Profile Card */}
+
+
+
+
+ {/* Avatar */}
+
+ {githubData?.avatar_url || user.avatar_url ? (
+
+ ) : (
+
+ )}
+
+ {/* Basic Info */}
+
+ {githubData?.name || user.name || user.username}
+
+
@{user.username}
+ {githubData?.bio &&
{githubData.bio}
}
+
+ {/* GitHub Stats */}
+ {githubData && (
+
+
+
{formatNumber(githubData.followers)}
+
Followers
+
+
+
{formatNumber(githubData.following)}
+
Following
+
+
+
{formatNumber(githubData.public_repos)}
+
Repos
+
+
+ )}
+
+ {/* GitHub Profile Link */}
+ {githubData?.html_url && (
+
window.open(githubData.html_url, "_blank")}
+ className="bg-gray-800 border-gray-600 hover:bg-gray-700 text-white w-full cursor-pointer"
+ >
+
+
+
+ View on GitHub
+
+ )}
+
+
+
+
+
+ {/* Details Cards */}
+
+ {/* Account Details */}
+
+
+
+
+ Account Details
+
+
+
+
+
+
Username
+
{user.username}
+
+ {githubData?.created_at && (
+
+
GitHub Member Since
+
{formatDate(githubData.created_at)}
+
+ )}
+ {user.email && (
+
+ )}
+ {githubData?.location && (
+
+
Location
+
{githubData.location}
+
+ )}
+ {githubData?.company && (
+
+
Company
+
{githubData.company}
+
+ )}
+ {githubData?.blog && (
+
+ )}
+
+
+
+
+ {/* GitHub Activity */}
+ {githubData && (
+
+
+
+
+ GitHub Activity
+
+
+
+
+
+
+ {formatNumber(githubData.public_repos)}
+
+
+
+ Public Repos
+
+
+
+
{formatNumber(githubData.followers)}
+
+
+ Followers
+
+
+
+
+ {formatNumber(githubData.following)}
+
+
+
+ Following
+
+
+ {githubData.public_gists !== undefined && (
+
+
+ {formatNumber(githubData.public_gists)}
+
+
+
+ Public Gists
+
+
+ )}
+
+
+
+ )}
+
+ {/* User Widgets */}
+
+
+
+
+
+ My Widgets
+
+ {userWidgets.length}
+
+
+
router.push("/widget")}
+ className="bg-blue-600 hover:bg-blue-700 text-white cursor-pointer"
+ size="sm"
+ >
+
+ Create Widget
+
+
+
+
+ {isLoadingWidgets ? (
+
+ ) : userWidgets.length === 0 ? (
+
+
+
+
+
No Widgets Yet
+
+ Create your first GitHub widget to showcase your stats and contributions in a beautiful,
+ customizable format.
+
+
router.push("/widget")}
+ className="bg-blue-600 hover:bg-blue-700 text-white cursor-pointer"
+ >
+
+ Create Your First Widget
+
+
+ ) : (
+
+
+ {userWidgets.map((widget) => (
+
+
+
+
+
{widget.name}
+
+
+ {widget.size.width} × {widget.size.height}
+
+
+ {widget.isPrivate ? (
+ <>
+
+ Private
+ >
+ ) : (
+ <>
+
+ Public
+ >
+ )}
+
+
+ {/*
+
+ Created {formatDate(widget.createdAt)}
+
*/}
+
+ {/*
handleDeleteWidget(widget._id)}
+ className="text-gray-400 hover:text-red-400 p-1 h-8 w-8 ml-2 cursor-pointer"
+ >
+
+ */}
+
+
+ {widget.Tags && widget.Tags.length > 0 && (
+
+ {widget.Tags.slice(0, 3).map((tag) => (
+
+ {tag}
+
+ ))}
+ {widget.Tags.length > 3 && (
+
+ +{widget.Tags.length - 3}
+
+ )}
+
+ )}
+
+ {/*
+
+
+ Widget Preview
+
+
+
+
📊
+
+ {widget.size.width} × {widget.size.height}
+
+
+
+
*/}
+
+
+ ))}
+
+
+ )}
+
+
+
+
+
+
+ )
+}
+
+export default function Profile() {
+ return (
+
+
+
+ )
+}
diff --git a/devboard/app/readme/page.tsx b/devboard/app/readme/page.tsx
new file mode 100644
index 0000000..e6f51ae
--- /dev/null
+++ b/devboard/app/readme/page.tsx
@@ -0,0 +1,589 @@
+"use client"
+
+import { useState, useEffect, useCallback } from "react"
+import { useRouter } from "next/navigation"
+import { Button } from "@/components/ui/button"
+import { Textarea } from "@/components/ui/textarea"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { ResizablePanelGroup, ResizablePanel, ResizableHandle } from "@/components/ui/resizable"
+import { ArrowLeft, Loader2, Save, FileText, Eye, Sparkles } from "lucide-react"
+import { toast } from "sonner"
+import { useAuth } from "@/lib/auth-context"
+import ReactMarkdown from "react-markdown"
+import remarkGfm from "remark-gfm"
+import rehypeHighlight from "rehype-highlight"
+import rehypeRaw from "rehype-raw"
+import "highlight.js/styles/github-dark.css"
+import ProtectedRoute from "@/components/protected-route"
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "http://140.245.225.60"
+
+interface ReadmeData {
+ name: string
+ path: string
+ sha: string
+ size: number
+ content: string
+ encoding: string
+}
+
+// Cookie utility functions
+const getCookie = (name: string): string | null => {
+ if (typeof window === "undefined") return null
+
+ try {
+ const nameEQ = name + "="
+ const ca = document.cookie.split(";")
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i]
+ while (c.charAt(0) === " ") c = c.substring(1, c.length)
+ if (c.indexOf(nameEQ) === 0) {
+ return c.substring(nameEQ.length, c.length)
+ }
+ }
+ return null
+ } catch (error) {
+ console.error(`Failed to retrieve cookie ${name}:`, error)
+ return null
+ }
+}
+
+function ReadmeEditorContent() {
+ const [markdownContent, setMarkdownContent] = useState("")
+ const [originalContent, setOriginalContent] = useState("")
+ const [readmeData, setReadmeData] = useState(null)
+ const [isLoading, setIsLoading] = useState(true)
+ const [isSaving, setIsSaving] = useState(false)
+ const [isGenerating, setIsGenerating] = useState(false)
+ const [hasChanges, setHasChanges] = useState(false)
+ const [isNewReadme, setIsNewReadme] = useState(false)
+ const { user } = useAuth()
+ const router = useRouter()
+
+ const [showAIForm, setShowAIForm] = useState(false)
+ const [aiFormData, setAiFormData] = useState({
+ field: "",
+ experience: "",
+ skills: "",
+ interests: "",
+ goals: "",
+ })
+
+ // Decode base64 content
+ const decodeBase64Content = (content: string): string => {
+ try {
+ return atob(content.replace(/\n/g, ""))
+ } catch (error) {
+ console.error("Failed to decode base64 content:", error)
+ return content
+ }
+ }
+
+ // Make authenticated request
+ const makeAuthenticatedRequest = async (url: string, options: RequestInit = {}) => {
+ const accessToken = getCookie("devboard_access_token")
+
+ if (!accessToken) {
+ throw new Error("No access token available")
+ }
+
+ return fetch(url, {
+ ...options,
+ headers: {
+ ...options.headers,
+ Authorization: `Bearer ${accessToken}`,
+ "Content-Type": "application/json",
+ },
+ })
+ }
+
+ // Fetch README from GitHub
+ const fetchReadme = useCallback(async () => {
+ setIsLoading(true)
+ try {
+ const response = await makeAuthenticatedRequest(`${API_BASE_URL}/api/github/readme`)
+
+ if (response.status === 404) {
+ // No README found
+ setIsNewReadme(true)
+ setMarkdownContent("")
+ setOriginalContent("")
+ toast.success("Let's create your first README!")
+ } else if (response.ok) {
+ const data: ReadmeData = await response.json()
+ const decodedContent = decodeBase64Content(data.content)
+ setReadmeData(data)
+ setMarkdownContent(decodedContent)
+ setOriginalContent(decodedContent)
+ setIsNewReadme(false)
+ toast.success("README loaded successfully!")
+ } else {
+ const errorData = await response.json()
+ throw new Error(errorData.error || "Failed to fetch README")
+ }
+ } catch (error) {
+ console.error("Error fetching README:", error)
+ toast.error("Failed to load README")
+ } finally {
+ setIsLoading(false)
+ }
+ }, [])
+
+ // Check for changes
+ useEffect(() => {
+ setHasChanges(markdownContent !== originalContent)
+ }, [markdownContent, originalContent])
+
+ // Load README when component mounts
+ useEffect(() => {
+ fetchReadme()
+ }, [fetchReadme])
+
+ // Replace the btoa line with this function
+ const encodeToBase64 = (str: string): string => {
+ // Convert string to UTF-8 bytes, then to base64
+ return btoa(unescape(encodeURIComponent(str)))
+ }
+
+ // AI Generate README
+ const handleAIGenerate = async () => {
+ if (!showAIForm) {
+ setShowAIForm(true)
+ return
+ }
+
+ setIsGenerating(true)
+ setShowAIForm(false)
+
+ try {
+ const response = await fetch("/api/readme/generate", {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ },
+ body: JSON.stringify({
+ username: user?.username || "developer",
+ currentContent: markdownContent,
+ isNew: isNewReadme,
+ personalInfo: aiFormData,
+ }),
+ })
+
+ if (!response.ok) {
+ throw new Error("Failed to generate README")
+ }
+
+ const reader = response.body?.getReader()
+ const decoder = new TextDecoder()
+ let generatedContent = ""
+
+ if (reader) {
+ while (true) {
+ const { done, value } = await reader.read()
+ if (done) break
+
+ const chunk = decoder.decode(value)
+ const lines = chunk.split("\n")
+
+ for (const line of lines) {
+ if (line.startsWith("data: ")) {
+ try {
+ const data = JSON.parse(line.slice(6))
+
+ if (data.type === "content") {
+ generatedContent += data.content
+ setMarkdownContent(generatedContent)
+ } else if (data.type === "complete") {
+ toast.success("README generated successfully!")
+ break
+ } else if (data.type === "error") {
+ throw new Error(data.error)
+ }
+ } catch (e) {
+ // Ignore parsing errors for incomplete chunks
+ }
+ }
+ }
+ }
+ }
+ } catch (error) {
+ console.error("AI generation error:", error)
+ toast.error("Failed to generate README")
+ } finally {
+ setIsGenerating(false)
+ }
+ }
+
+ // Save/Commit README
+ const handleCommit = async () => {
+ if (!hasChanges) {
+ toast.info("No changes to commit")
+ return
+ }
+
+ setIsSaving(true)
+ try {
+ const method = isNewReadme ? "POST" : "PATCH"
+
+ // Encode content to base64 for the API
+ const encodedContent = encodeToBase64(markdownContent)
+
+ const payload = isNewReadme
+ ? {
+ content: encodedContent,
+ message: "Create README.md",
+ }
+ : {
+ content: encodedContent,
+ message: "Update README.md",
+ sha: readmeData?.sha,
+ }
+
+ console.log("Committing with payload:", { method, payload })
+
+ const response = await makeAuthenticatedRequest(`${API_BASE_URL}/api/github/readme`, {
+ method,
+ body: JSON.stringify(payload),
+ })
+
+ if (response.ok) {
+ const result = await response.json()
+ setOriginalContent(markdownContent)
+ setHasChanges(false)
+ setIsNewReadme(false)
+
+ // Update readme data with new sha
+ if (result.content) {
+ setReadmeData(result.content)
+ }
+
+ toast.success("README committed successfully!")
+ } else {
+ const error = await response.json()
+ console.error("Commit error response:", error)
+ throw new Error(error.error || "Failed to commit README")
+ }
+ } catch (error) {
+ console.error("Error committing README:", error)
+ toast.error("Failed to commit changes")
+ } finally {
+ setIsSaving(false)
+ }
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
+
router.back()}
+ className="bg-[#1A1625] border-[#3F1469] hover:bg-[#211D2E] text-white"
+ >
+
+ Back
+
+
+
README Editor
+
+ {user?.username ? `@${user.username}/${user.username}` : "Loading..."}
+
+
+
+
+
+
+ {isGenerating ? : }
+ {isGenerating ? "Generating..." : "AI Generate"}
+
+
+
+ {isSaving ? : }
+ {isSaving ? "Committing..." : "Commit changes"}
+
+
+
+
+ {showAIForm && (
+
+
+
+ Personalize Your README
+ Tell us about yourself to generate a better README
+
+
+
+ What field are you in?
+ setAiFormData({ ...aiFormData, field: e.target.value })}
+ placeholder="e.g., Frontend Developer, Data Scientist, DevOps Engineer"
+ className="w-full p-2 bg-[#1A1625] border border-[#3F1469] rounded text-white"
+ />
+
+
+
+ Experience level
+ setAiFormData({ ...aiFormData, experience: e.target.value })}
+ className="w-full p-2 bg-[#1A1625] border border-[#3F1469] rounded text-white"
+ >
+ Select experience
+ Student
+ Junior (0-2 years)
+ Mid-level (2-5 years)
+ Senior (5+ years)
+
+
+
+
+ Main skills/technologies
+ setAiFormData({ ...aiFormData, skills: e.target.value })}
+ placeholder="e.g., React, Python, Docker, AWS"
+ className="w-full p-2 bg-[#1A1625] border border-[#3F1469] rounded text-white"
+ />
+
+
+
+ Current interests/learning
+ setAiFormData({ ...aiFormData, interests: e.target.value })}
+ placeholder="e.g., Machine Learning, Web3, Mobile Development"
+ className="w-full p-2 bg-[#1A1625] border border-[#3F1469] rounded text-white"
+ />
+
+
+
+ Goals/What you're looking for
+ setAiFormData({ ...aiFormData, goals: e.target.value })}
+ placeholder="e.g., Open source contributions, Job opportunities, Collaboration"
+ className="w-full p-2 bg-[#1A1625] border border-[#3F1469] rounded text-white"
+ />
+
+
+
+ setShowAIForm(false)}
+ variant="outline"
+ className="flex-1 bg-[#1A1625] border-[#3F1469] hover:bg-[#211D2E] text-white"
+ >
+ Cancel
+
+
+ Generate README
+
+
+
+
+
+ )}
+
+ {/* Loading State */}
+ {isLoading ? (
+
+
+
+
Loading your README...
+
+
+ ) : (
+ /* Editor */
+
+
+
+
+
+ README.md
+ {hasChanges && • Modified }
+
+
+ {isNewReadme ? "New file" : `${markdownContent.length} characters`}
+
+
+
+
+
+ {/* Code Panel */}
+
+
+
+
+
+
+ {/* Preview Panel */}
+
+
+
+
+ Preview
+
+
+ {markdownContent ? (
+
+
(
+
+ {children}
+
+ ),
+ h2: ({ children }) => (
+ {children}
+ ),
+ h3: ({ children }) => (
+ {children}
+ ),
+ p: ({ children }) => {children}
,
+ ul: ({ children }) => (
+
+ ),
+ ol: ({ children }) => (
+ {children}
+ ),
+ li: ({ children }) => {children} ,
+ code: ({ children, className }) => {
+ if (className?.includes("inline")) {
+ return (
+ {children}
+ )
+ }
+ return (
+
+ {children}
+
+ )
+ },
+ pre: ({ children }) => (
+
+ {children}
+
+ ),
+ blockquote: ({ children }) => (
+
+ {children}
+
+ ),
+ a: ({ children, href }) => (
+
+ {children}
+
+ ),
+ img: ({ src, alt }) => (
+
+
{
+ const target = e.target as HTMLImageElement
+ target.style.display = "none"
+ }}
+ />
+
+ ),
+ table: ({ children }) => (
+
+ ),
+ th: ({ children }) => (
+
+ {children}
+
+ ),
+ td: ({ children }) => (
+ {children}
+ ),
+ strong: ({ children }) => (
+ {children}
+ ),
+ em: ({ children }) => {children} ,
+ }}
+ >
+ {markdownContent}
+
+
+ ) : (
+
+
+
+
Start Writing
+
+ {isNewReadme
+ ? "Create your first README to see the preview here"
+ : "Edit your README to see the preview here"}
+
+
+
+ )}
+
+
+
+
+
+
+ )}
+
+
+ )
+}
+
+export default function ReadmeEditor() {
+ return (
+
+
+
+ )
+}
diff --git a/devboard/app/widget/loading.tsx b/devboard/app/widget/loading.tsx
new file mode 100644
index 0000000..f15322a
--- /dev/null
+++ b/devboard/app/widget/loading.tsx
@@ -0,0 +1,3 @@
+export default function Loading() {
+ return null
+}
diff --git a/devboard/app/widget/page.tsx b/devboard/app/widget/page.tsx
new file mode 100644
index 0000000..9a47ae8
--- /dev/null
+++ b/devboard/app/widget/page.tsx
@@ -0,0 +1,2002 @@
+"use client"
+
+import type React from "react"
+
+import { useState, useEffect, useRef, useCallback } from "react"
+import { DndProvider, useDrag, useDrop } from "react-dnd"
+import { HTML5Backend } from "react-dnd-html5-backend"
+import { Button } from "@/components/ui/button"
+import { Card, CardContent } from "@/components/ui/card"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Badge } from "@/components/ui/badge"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { Switch } from "@/components/ui/switch"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
+import { Slider } from "@/components/ui/slider"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible"
+import {
+ Save,
+ Copy,
+ ArrowLeft,
+ RefreshCw,
+ Type,
+ Square,
+ Circle,
+ Star,
+ GitCommit,
+ GitPullRequest,
+ AlertCircle,
+ BookOpen,
+ User,
+ Trash2,
+ X,
+ ZoomIn,
+ ZoomOut,
+ Maximize2,
+ Eye,
+ EyeOff,
+ Lock,
+ Unlock,
+ Grid3X3,
+ Target,
+ ChevronDown,
+ Settings,
+ AlignLeft,
+ AlignCenter,
+ AlignRight,
+ Bold,
+ Italic,
+ Underline,
+ Smile,
+} from "lucide-react"
+import { toast } from "sonner"
+import ProtectedRoute from "@/components/protected-route"
+
+// GitHub Variables with detailed information
+const GITHUB_VARIABLES = [
+ {
+ name: "totalCommitContributions",
+ label: "Total Commits",
+ description: "Total number of commits made by the user",
+ category: "Contributions",
+ icon: GitCommit,
+ example: "1,234",
+ emoji: "💻",
+ },
+ {
+ name: "totalIssueContributions",
+ label: "Total Issues",
+ description: "Total issues created or contributed to",
+ category: "Contributions",
+ icon: AlertCircle,
+ example: "89",
+ emoji: "🐛",
+ },
+ {
+ name: "totalPullRequestContributions",
+ label: "Total Pull Requests",
+ description: "Total pull requests created",
+ category: "Contributions",
+ icon: GitPullRequest,
+ example: "156",
+ emoji: "🔀",
+ },
+ {
+ name: "totalRepositoriesWithContributedCommits",
+ label: "Contributed Repositories",
+ description: "Number of repositories with contributions",
+ category: "Repositories",
+ icon: BookOpen,
+ example: "42",
+ emoji: "📚",
+ },
+ {
+ name: "totalStars",
+ label: "Total Stars",
+ description: "Total stars received across all repositories",
+ category: "Repositories",
+ icon: Star,
+ example: "2,567",
+ emoji: "⭐",
+ },
+ {
+ name: "totalRepositories",
+ label: "Total Repositories",
+ description: "Total number of repositories owned",
+ category: "Repositories",
+ icon: BookOpen,
+ example: "78",
+ emoji: "📁",
+ },
+ {
+ name: "username",
+ label: "Username",
+ description: "GitHub username",
+ category: "Profile",
+ icon: User,
+ example: "johndoe",
+ emoji: "👤",
+ },
+]
+
+// GitHub Emojis for widgets
+const GITHUB_EMOJIS = [
+ { emoji: "💻", label: "Computer", category: "Development" },
+ { emoji: "🚀", label: "Rocket", category: "Development" },
+ { emoji: "⭐", label: "Star", category: "GitHub" },
+ { emoji: "🔀", label: "Merge", category: "GitHub" },
+ { emoji: "🐛", label: "Bug", category: "GitHub" },
+ { emoji: "✨", label: "Sparkles", category: "GitHub" },
+ { emoji: "🔥", label: "Fire", category: "Popular" },
+ { emoji: "💡", label: "Idea", category: "Development" },
+ { emoji: "📚", label: "Books", category: "Learning" },
+ { emoji: "🎯", label: "Target", category: "Goals" },
+ { emoji: "📈", label: "Chart", category: "Stats" },
+ { emoji: "🏆", label: "Trophy", category: "Achievement" },
+ { emoji: "💎", label: "Diamond", category: "Quality" },
+ { emoji: "🌟", label: "Glowing Star", category: "Special" },
+ { emoji: "🎨", label: "Art", category: "Design" },
+ { emoji: "⚡", label: "Lightning", category: "Fast" },
+ { emoji: "🔧", label: "Wrench", category: "Tools" },
+ { emoji: "📊", label: "Bar Chart", category: "Analytics" },
+ { emoji: "🎉", label: "Party", category: "Celebration" },
+ { emoji: "💪", label: "Strong", category: "Power" },
+]
+
+// Canvas preset sizes
+const CANVAS_PRESETS = [
+ { name: "Small Badge", width: 300, height: 120 },
+ { name: "Medium Card", width: 400, height: 200 },
+ { name: "Large Banner", width: 500, height: 150 },
+ { name: "Square Card", width: 300, height: 300 },
+ { name: "Wide Display", width: 600, height: 200 },
+ { name: "Custom", width: 400, height: 200 },
+]
+
+// Element types for the toolbar
+const ELEMENT_TYPES = [
+ { type: "text", label: "Text", icon: Type },
+ { type: "variable", label: "GitHub Variable", icon: GitCommit },
+ { type: "emoji", label: "Emoji", icon: Smile },
+ { type: "rectangle", label: "Rectangle", icon: Square },
+ { type: "circle", label: "Circle", icon: Circle },
+ { type: "star", label: "Star", icon: Star },
+]
+
+// Font options
+const FONT_FAMILIES = ["Inter", "Arial", "Helvetica", "Times New Roman", "Georgia", "Verdana", "Monaco", "Courier New"]
+
+// Widget Element Interface
+interface WidgetElement {
+ id: string
+ type: "text" | "variable" | "emoji" | "rectangle" | "circle" | "star"
+ position: { x: number; y: number }
+ size: { width: number; height: number }
+ rotation: number
+ style: {
+ backgroundColor: string
+ color: string
+ fontSize: number
+ fontWeight: string
+ fontFamily: string
+ fontStyle: string
+ textDecoration: string
+ borderRadius: number
+ borderWidth: number
+ borderColor: string
+ opacity: number
+ textAlign: "left" | "center" | "right"
+ padding: number
+ shadow: boolean
+ shadowColor: string
+ shadowBlur: number
+ shadowOffsetX: number
+ shadowOffsetY: number
+ }
+ content: {
+ text?: string
+ variable?: string
+ emoji?: string
+ }
+ visible: boolean
+ locked: boolean
+ zIndex: number
+}
+
+// Widget Interface
+interface Widget {
+ _id?: string
+ name: string
+ elements: WidgetElement[]
+ canvas: {
+ width: number
+ height: number
+ backgroundColor: string
+ backgroundImage?: string
+ showGrid: boolean
+ gridSize: number
+ snapToGrid: boolean
+ }
+ is_private: boolean
+ tags: string[]
+}
+
+// Utility function to determine if a color is light or dark
+const isLightColor = (color: string): boolean => {
+ // Handle hex colors
+ if (color.startsWith("#")) {
+ const hex = color.replace("#", "")
+ const r = Number.parseInt(hex.substr(0, 2), 16)
+ const g = Number.parseInt(hex.substr(2, 2), 16)
+ const b = Number.parseInt(hex.substr(4, 2), 16)
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000
+ return brightness > 128
+ }
+
+ // Handle rgb colors
+ if (color.startsWith("rgb")) {
+ const matches = color.match(/\d+/g)
+ if (matches && matches.length >= 3) {
+ const r = Number.parseInt(matches[0])
+ const g = Number.parseInt(matches[1])
+ const b = Number.parseInt(matches[2])
+ const brightness = (r * 299 + g * 587 + b * 114) / 1000
+ return brightness > 128
+ }
+ }
+
+ // Default to dark for unknown formats
+ return false
+}
+
+// Get contrasting text color
+const getContrastingTextColor = (backgroundColor: string): string => {
+ if (backgroundColor === "transparent") return "#FFFFFF"
+ return isLightColor(backgroundColor) ? "#000000" : "#FFFFFF"
+}
+
+// Cookie utility
+const getCookie = (name: string): string | null => {
+ if (typeof window === "undefined") return null
+ try {
+ const nameEQ = name + "="
+ const ca = document.cookie.split(";")
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i]
+ while (c.charAt(0) === " ") c = c.substring(1, c.length)
+ if (c.indexOf(nameEQ) === 0) {
+ return c.substring(nameEQ.length, c.length)
+ }
+ }
+ return null
+ } catch (error) {
+ console.error(`Failed to retrieve cookie ${name}:`, error)
+ return null
+ }
+}
+
+// API Functions
+const apiCall = async (endpoint: string, options: RequestInit = {}) => {
+ const accessToken = getCookie("devboard_access_token")
+ if (!accessToken) {
+ throw new Error("Please log in to continue")
+ }
+
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}${endpoint}`, {
+ ...options,
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${accessToken}`,
+ ...options.headers,
+ },
+ })
+
+ if (!response.ok) {
+ const errorText = await response.text()
+ let errorMessage = "An error occurred"
+ try {
+ const errorJson = JSON.parse(errorText)
+ errorMessage = errorJson.error || errorJson.message || errorMessage
+ } catch {
+ errorMessage = errorText || errorMessage
+ }
+ throw new Error(errorMessage)
+ }
+
+ const contentType = response.headers.get("content-type")
+ if (contentType && contentType.includes("application/json")) {
+ return response.json()
+ }
+ return response.text()
+}
+
+// Draggable Element Component
+function DraggableElement({ element }: { element: any }) {
+ const [{ isDragging }, dragRef] = useDrag(() => ({
+ type: "ELEMENT",
+ item: { elementType: element.type },
+ collect: (monitor) => ({
+ isDragging: monitor.isDragging(),
+ }),
+ }))
+
+ const Icon = element.icon
+
+ return (
+
+
+
+
+
{element.label}
+
+ )
+}
+
+// Canvas Component
+function Canvas({
+ widget,
+ selectedElements,
+ onElementAdd,
+ onElementSelect,
+ onElementUpdate,
+ zoom,
+}: {
+ widget: Widget
+ selectedElements: WidgetElement[]
+ onElementAdd: (element: WidgetElement) => void
+ onElementSelect: (elements: WidgetElement[]) => void
+ onElementUpdate: (element: WidgetElement) => void
+ zoom: number
+}) {
+ const canvasRef = useRef(null)
+ const [draggedElement, setDraggedElement] = useState(null)
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
+
+ const [{ isOver }, dropRef] = useDrop(() => ({
+ accept: "ELEMENT",
+ drop: (item: { elementType: string }, monitor) => {
+ const offset = monitor.getClientOffset()
+ if (offset && canvasRef.current) {
+ const canvasRect = canvasRef.current.getBoundingClientRect()
+ let x = Math.max(0, (offset.x - canvasRect.left) / zoom)
+ let y = Math.max(0, (offset.y - canvasRect.top) / zoom)
+
+ if (widget.canvas.snapToGrid) {
+ x = Math.round(x / widget.canvas.gridSize) * widget.canvas.gridSize
+ y = Math.round(y / widget.canvas.gridSize) * widget.canvas.gridSize
+ }
+
+ const newElement = createNewElement(item.elementType, x, y)
+ onElementAdd(newElement)
+ }
+ },
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ }),
+ }))
+
+ const createNewElement = (type: string, x: number, y: number): WidgetElement => {
+ const baseElement = {
+ id: `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+ position: { x, y },
+ rotation: 0,
+ visible: true,
+ locked: false,
+ zIndex: widget.elements.length + 1,
+ style: {
+ backgroundColor: "#3B82F6",
+ color: "#FFFFFF",
+ fontSize: 16,
+ fontWeight: "500",
+ fontFamily: "Inter",
+ fontStyle: "normal",
+ textDecoration: "none",
+ borderRadius: 8,
+ borderWidth: 0,
+ borderColor: "#E5E7EB",
+ opacity: 1,
+ textAlign: "center" as const,
+ padding: 12,
+ shadow: true,
+ shadowColor: "rgba(0, 0, 0, 0.1)",
+ shadowBlur: 8,
+ shadowOffsetX: 0,
+ shadowOffsetY: 2,
+ },
+ content: {},
+ }
+
+ switch (type) {
+ case "text":
+ return {
+ ...baseElement,
+ type: "text" as const,
+ size: { width: 140, height: 40 },
+ content: { text: "Sample Text" },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#3B82F6",
+ },
+ }
+ case "variable":
+ return {
+ ...baseElement,
+ type: "variable" as const,
+ size: { width: 160, height: 40 },
+ content: { variable: "totalCommitContributions", text: "{{totalCommitContributions}}" },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#10B981",
+ },
+ }
+ case "emoji":
+ return {
+ ...baseElement,
+ type: "emoji" as const,
+ size: { width: 60, height: 60 },
+ content: { emoji: "💻" },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "transparent",
+ fontSize: 32,
+ shadow: false,
+ },
+ }
+ case "rectangle":
+ return {
+ ...baseElement,
+ type: "rectangle" as const,
+ size: { width: 120, height: 80 },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#8B5CF6",
+ },
+ }
+ case "circle":
+ return {
+ ...baseElement,
+ type: "circle" as const,
+ size: { width: 80, height: 80 },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#F59E0B",
+ borderRadius: 50,
+ },
+ }
+ case "star":
+ return {
+ ...baseElement,
+ type: "star" as const,
+ size: { width: 80, height: 80 },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#EF4444",
+ },
+ }
+ default:
+ return {
+ ...baseElement,
+ type: type as any,
+ size: { width: 100, height: 40 },
+ }
+ }
+ }
+
+ const handleElementMouseDown = (element: WidgetElement, e: React.MouseEvent) => {
+ if (element.locked) return
+ e.stopPropagation()
+
+ if (!selectedElements.find((el) => el.id === element.id)) {
+ if (e.ctrlKey || e.metaKey) {
+ onElementSelect([...selectedElements, element])
+ } else {
+ onElementSelect([element])
+ }
+ }
+
+ setDraggedElement(element)
+ const rect = canvasRef.current?.getBoundingClientRect()
+ if (rect) {
+ setDragOffset({
+ x: (e.clientX - rect.left) / zoom - element.position.x,
+ y: (e.clientY - rect.top) / zoom - element.position.y,
+ })
+ }
+ }
+
+ const handleMouseMove = useCallback(
+ (e: MouseEvent) => {
+ if (!draggedElement || !canvasRef.current) return
+
+ const rect = canvasRef.current.getBoundingClientRect()
+ let newX = Math.max(0, (e.clientX - rect.left) / zoom - dragOffset.x)
+ let newY = Math.max(0, (e.clientY - rect.top) / zoom - dragOffset.y)
+
+ // Constrain to canvas bounds
+ newX = Math.min(newX, widget.canvas.width - draggedElement.size.width)
+ newY = Math.min(newY, widget.canvas.height - draggedElement.size.height)
+
+ if (widget.canvas.snapToGrid) {
+ newX = Math.round(newX / widget.canvas.gridSize) * widget.canvas.gridSize
+ newY = Math.round(newY / widget.canvas.gridSize) * widget.canvas.gridSize
+ }
+
+ onElementUpdate({
+ ...draggedElement,
+ position: { x: newX, y: newY },
+ })
+ },
+ [
+ draggedElement,
+ dragOffset,
+ zoom,
+ onElementUpdate,
+ widget.canvas.snapToGrid,
+ widget.canvas.gridSize,
+ widget.canvas.width,
+ widget.canvas.height,
+ ],
+ )
+
+ const handleMouseUp = useCallback(() => {
+ setDraggedElement(null)
+ }, [])
+
+ useEffect(() => {
+ if (draggedElement) {
+ document.addEventListener("mousemove", handleMouseMove)
+ document.addEventListener("mouseup", handleMouseUp)
+ return () => {
+ document.removeEventListener("mousemove", handleMouseMove)
+ document.removeEventListener("mouseup", handleMouseUp)
+ }
+ }
+ }, [draggedElement, handleMouseMove, handleMouseUp])
+
+ const renderGrid = () => {
+ if (!widget.canvas.showGrid) return null
+ const gridLines = []
+ const gridSize = widget.canvas.gridSize * zoom
+ const canvasWidth = widget.canvas.width * zoom
+ const canvasHeight = widget.canvas.height * zoom
+
+ for (let x = 0; x <= canvasWidth; x += gridSize) {
+ gridLines.push(
+ ,
+ )
+ }
+
+ for (let y = 0; y <= canvasHeight; y += gridSize) {
+ gridLines.push(
+ ,
+ )
+ }
+
+ return (
+
+ {gridLines}
+
+ )
+ }
+
+ const renderElement = (element: WidgetElement) => {
+ if (!element.visible) return null
+
+ const isSelected = selectedElements.some((el) => el.id === element.id)
+ const transform = `rotate(${element.rotation}deg)`
+ const shadowStyle = element.style.shadow
+ ? `${element.style.shadowOffsetX}px ${element.style.shadowOffsetY}px ${element.style.shadowBlur}px ${element.style.shadowColor}`
+ : "none"
+
+ const elementProps = {
+ key: element.id,
+ className: `absolute cursor-pointer transition-all duration-200 select-none ${
+ isSelected ? "ring-2 ring-blue-500 ring-offset-2 ring-offset-gray-900" : "hover:ring-1 hover:ring-blue-300/50"
+ } ${element.locked ? "cursor-not-allowed opacity-60" : ""}`,
+ style: {
+ left: element.position.x,
+ top: element.position.y,
+ width: element.size.width,
+ height: element.size.height,
+ transform,
+ opacity: element.style.opacity,
+ zIndex: element.zIndex,
+ boxShadow: shadowStyle,
+ },
+ onMouseDown: (e: React.MouseEvent) => handleElementMouseDown(element, e),
+ }
+
+ switch (element.type) {
+ case "text":
+ case "variable":
+ return (
+
+
+ {element.content.text || "Text"}
+
+
+ )
+
+ case "emoji":
+ return (
+
+
+ {element.content.emoji || "💻"}
+
+
+ )
+
+ case "rectangle":
+ return (
+
+ )
+
+ case "circle":
+ return (
+
+ )
+
+ case "star":
+ return (
+
+ )
+
+ default:
+ return null
+ }
+ }
+
+ return (
+
+
+
+
+ {renderGrid()}
+ {widget.elements.sort((a, b) => a.zIndex - b.zIndex).map(renderElement)}
+
+
+
+
+ )
+}
+
+// Generate backend content
+const generateBackendContent = (widget: Widget): string => {
+ let content = ""
+
+ widget.elements
+ .filter((element) => element.visible)
+ .sort((a, b) => a.zIndex - b.zIndex)
+ .forEach((element) => {
+ const transform =
+ element.rotation !== 0
+ ? ` transform="rotate(${element.rotation} ${element.position.x + element.size.width / 2} ${element.position.y + element.size.height / 2})"`
+ : ""
+ const opacity = element.style.opacity !== 1 ? ` opacity="${element.style.opacity}"` : ""
+
+ switch (element.type) {
+ case "text":
+ case "variable":
+ const textContent = element.content.text || ""
+ const isGitHubVariable = GITHUB_VARIABLES.some((variable) => textContent === `{{${variable.name}}}`)
+
+ if (isGitHubVariable) {
+ content += `${textContent} `
+ } else {
+ content += `${textContent} `
+ }
+ break
+
+ case "emoji":
+ content += `${element.content.emoji} `
+ break
+
+ case "rectangle":
+ content += ` `
+ break
+
+ case "circle":
+ const radius = Math.min(element.size.width, element.size.height) / 2
+ content += ` `
+ break
+ }
+ })
+
+ return content
+}
+
+// Main Component
+function GitHubWidgetBuilderContent() {
+ const [showInitialModal, setShowInitialModal] = useState(true)
+ const [showSaveModal, setShowSaveModal] = useState(false)
+ const [selectedCanvasSize, setSelectedCanvasSize] = useState(CANVAS_PRESETS[1])
+ const [customWidth, setCustomWidth] = useState(400)
+ const [customHeight, setCustomHeight] = useState(200)
+ const [isSaving, setIsSaving] = useState(false)
+ const [isLoading, setIsLoading] = useState(false)
+ const [selectedElements, setSelectedElements] = useState([])
+ const [zoom, setZoom] = useState(1)
+
+ // Widget states
+ const [userWidgets, setUserWidgets] = useState([])
+ const [editingWidget, setEditingWidget] = useState(null)
+
+ const [widget, setWidget] = useState({
+ name: "My GitHub Widget",
+ elements: [],
+ canvas: {
+ width: 400,
+ height: 200,
+ backgroundColor: "#0D1117",
+ showGrid: true,
+ gridSize: 10,
+ snapToGrid: false,
+ },
+ is_private: false,
+ tags: [],
+ })
+
+ // Load user widgets
+ const loadUserWidgets = async () => {
+ try {
+ setIsLoading(true)
+ const widgets = await apiCall("/api/widget/all")
+ setUserWidgets(Array.isArray(widgets) ? widgets : [])
+ } catch (error: any) {
+ if (error.message.includes("No widgets found")) {
+ setUserWidgets([])
+ } else {
+ toast.error(`Failed to load widgets: ${error.message}`)
+ }
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ useEffect(() => {
+ loadUserWidgets()
+ }, [])
+
+ // Handle canvas size selection
+ const handleCanvasSizeSelect = (size: (typeof CANVAS_PRESETS)[0]) => {
+ setSelectedCanvasSize(size)
+ if (size.name === "Custom") {
+ setWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, width: customWidth, height: customHeight },
+ }))
+ } else {
+ setWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, width: size.width, height: size.height },
+ }))
+ }
+ setShowInitialModal(false)
+ }
+
+ // Add element to canvas
+ const addElement = (element: WidgetElement) => {
+ setWidget((prev) => ({
+ ...prev,
+ elements: [...prev.elements, element],
+ }))
+ setSelectedElements([element])
+ toast.success(`Added ${element.type} element`)
+ }
+
+ // Update element
+ const updateElement = (updatedElement: WidgetElement) => {
+ setWidget((prev) => ({
+ ...prev,
+ elements: prev.elements.map((el) => (el.id === updatedElement.id ? updatedElement : el)),
+ }))
+ setSelectedElements((prev) => prev.map((el) => (el.id === updatedElement.id ? updatedElement : el)))
+ }
+
+ // Update element with smart text color
+ const updateElementWithSmartColor = (updatedElement: WidgetElement, backgroundChanged = false) => {
+ if (backgroundChanged && (updatedElement.type === "text" || updatedElement.type === "variable")) {
+ updatedElement.style.color = getContrastingTextColor(updatedElement.style.backgroundColor)
+ }
+ updateElement(updatedElement)
+ }
+
+ // Delete element
+ const deleteElement = (elementId: string) => {
+ setWidget((prev) => ({
+ ...prev,
+ elements: prev.elements.filter((el) => el.id !== elementId),
+ }))
+ setSelectedElements((prev) => prev.filter((el) => el.id !== elementId))
+ toast.success("Element deleted")
+ }
+
+ // Save widget
+ const handleSave = async () => {
+ if (!widget.name.trim()) {
+ toast.error("Please provide a name for your widget")
+ return
+ }
+
+ if (widget.elements.length === 0) {
+ toast.error("Please add at least one element to your widget")
+ return
+ }
+
+ setIsSaving(true)
+ try {
+ const saveData = {
+ name: widget.name,
+ content: generateBackendContent(widget),
+ size: { width: widget.canvas.width, height: widget.canvas.height },
+ isPrivate: widget.is_private,
+ Tags: widget.tags,
+ }
+
+ if (editingWidget) {
+ await apiCall("/api/widget/", {
+ method: "PATCH",
+ body: JSON.stringify(saveData),
+ })
+ toast.success("Widget updated successfully!")
+ } else {
+ await apiCall("/api/widget/", {
+ method: "POST",
+ body: JSON.stringify(saveData),
+ })
+ toast.success("Widget created successfully!")
+ }
+
+ setShowSaveModal(false)
+ setEditingWidget(null)
+ loadUserWidgets()
+ } catch (error: any) {
+ toast.error(`Failed to save widget: ${error.message}`)
+ } finally {
+ setIsSaving(false)
+ }
+ }
+
+ // Add/remove tags
+ const addTag = (tag: string) => {
+ if (tag && !widget.tags.includes(tag)) {
+ setWidget((prev) => ({
+ ...prev,
+ tags: [...prev.tags, tag],
+ }))
+ }
+ }
+
+ const removeTag = (tagToRemove: string) => {
+ setWidget((prev) => ({
+ ...prev,
+ tags: prev.tags.filter((tag) => tag !== tagToRemove),
+ }))
+ }
+
+ const selectedElement = selectedElements[0]
+
+ // Group variables by category
+ const variablesByCategory = GITHUB_VARIABLES.reduce(
+ (acc, variable) => {
+ if (!acc[variable.category]) {
+ acc[variable.category] = []
+ }
+ acc[variable.category].push(variable)
+ return acc
+ },
+ {} as Record,
+ )
+
+ // Group emojis by category
+ const emojisByCategory = GITHUB_EMOJIS.reduce(
+ (acc, emoji) => {
+ if (!acc[emoji.category]) {
+ acc[emoji.category] = []
+ }
+ acc[emoji.category].push(emoji)
+ return acc
+ },
+ {} as Record,
+ )
+
+ return (
+
+
+ {/* Initial Canvas Size Modal */}
+
+
+
+ Choose Canvas Size
+
+ Select the dimensions for your GitHub widget
+
+
+
+
+ {CANVAS_PRESETS.map((size) => (
+
setSelectedCanvasSize(size)}
+ >
+
+
+ {size.name}
+
+ {size.width} × {size.height}
+
+
+
+ ))}
+
+
+ {selectedCanvasSize.name === "Custom" && (
+
+ )}
+
+
+
+ Cancel
+
+ handleCanvasSizeSelect(selectedCanvasSize)}
+ className="bg-blue-600 hover:bg-blue-700 cursor-pointer"
+ >
+ Create Widget
+
+
+
+
+
+ {/* Save Modal */}
+
+
+
+ {editingWidget ? "Edit Widget" : "Save Widget"}
+ Configure your widget settings
+
+
+
+
+ Widget Name
+ setWidget((prev) => ({ ...prev, name: e.target.value }))}
+ className="bg-gray-800 border-gray-600 text-white mt-1"
+ placeholder="Enter widget name"
+ />
+
+
+
+
+
Private Widget
+
Only you can see this widget
+
+
setWidget((prev) => ({ ...prev, is_private: checked }))}
+ />
+
+
+
+
Tags
+
+ {widget.tags.map((tag) => (
+ removeTag(tag)}
+ >
+ {tag}
+
+ ))}
+
+
+
+
+
+
+
+ Stats
+
+
+ Contributions
+
+
+ Repositories
+
+
+ Profile
+
+
+ Activity
+
+
+
+
+
+
+
+ setShowSaveModal(false)}
+ className="border-gray-600 text-gray-300 hover:bg-gray-800 cursor-pointer"
+ >
+ Cancel
+
+
+ {isSaving ? (
+ <>
+
+ {editingWidget ? "Updating..." : "Saving..."}
+ >
+ ) : (
+ <>
+
+ {editingWidget ? "Update Widget" : "Save Widget"}
+ >
+ )}
+
+
+
+
+
+ {/* Header */}
+
+
+
+
+ Back
+
+
GitHub Widget Builder
+
+
+
+ navigator.clipboard.writeText(generateBackendContent(widget))}
+ variant="outline"
+ className="border-gray-600 text-gray-300 hover:bg-gray-800 cursor-pointer"
+ >
+
+ Copy Code
+
+ setShowSaveModal(true)} className="bg-blue-600 hover:bg-blue-700 cursor-pointer">
+
+ Save Widget
+
+
+
+
+ {/* Main Content */}
+
+ {/* Left Sidebar - Elements & Variables */}
+
+
+
+
+ Elements
+
+
+ Variables
+
+
+ Emojis
+
+
+
+
+
+
+
+
Drag Elements
+
+ {ELEMENT_TYPES.map((element) => (
+
+ ))}
+
+
+
+
+
Canvas Controls
+
+
+
+ setWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, showGrid: !prev.canvas.showGrid },
+ }))
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+ Grid
+
+
+ setWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, snapToGrid: !prev.canvas.snapToGrid },
+ }))
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+ Snap
+
+
+
+
+
setZoom(Math.max(0.25, zoom - 0.25))}
+ className="border-gray-600 cursor-pointer"
+ >
+
+
+
+ {Math.round(zoom * 100)}%
+
+
setZoom(Math.min(3, zoom + 0.25))}
+ className="border-gray-600 cursor-pointer"
+ >
+
+
+
setZoom(1)}
+ className="border-gray-600 cursor-pointer"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
GitHub Variables
+ {Object.entries(variablesByCategory).map(([category, variables]) => (
+
+
+ {category}
+
+
+
+ {variables.map((variable) => {
+ const Icon = variable.icon
+ return (
+ {
+ const newElement = {
+ id: `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+ type: "variable" as const,
+ position: { x: 50, y: 50 },
+ size: { width: 160, height: 40 },
+ rotation: 0,
+ visible: true,
+ locked: false,
+ zIndex: widget.elements.length + 1,
+ style: {
+ backgroundColor: "#10B981",
+ color: "#FFFFFF",
+ fontSize: 16,
+ fontWeight: "500",
+ fontFamily: "Inter",
+ fontStyle: "normal",
+ textDecoration: "none",
+ borderRadius: 8,
+ borderWidth: 0,
+ borderColor: "#E5E7EB",
+ opacity: 1,
+ textAlign: "center" as const,
+ padding: 12,
+ shadow: true,
+ shadowColor: "rgba(0, 0, 0, 0.1)",
+ shadowBlur: 8,
+ shadowOffsetX: 0,
+ shadowOffsetY: 2,
+ },
+ content: {
+ variable: variable.name,
+ text: `{{${variable.name}}}`,
+ },
+ }
+ addElement(newElement)
+ }}
+ >
+
+
+ {variable.emoji}
+
+
+
{variable.label}
+
+
{variable.description}
+
Example: {variable.example}
+
+ )
+ })}
+
+
+ ))}
+
+
+
+
+
+
+
+
GitHub Emojis
+ {Object.entries(emojisByCategory).map(([category, emojis]) => (
+
+
+ {category}
+
+
+
+
+ {emojis.map((emoji) => (
+
{
+ const newElement = {
+ id: `element-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
+ type: "emoji" as const,
+ position: { x: 50, y: 50 },
+ size: { width: 60, height: 60 },
+ rotation: 0,
+ visible: true,
+ locked: false,
+ zIndex: widget.elements.length + 1,
+ style: {
+ backgroundColor: "transparent",
+ color: "#FFFFFF",
+ fontSize: 32,
+ fontWeight: "normal",
+ fontFamily: "Inter",
+ fontStyle: "normal",
+ textDecoration: "none",
+ borderRadius: 8,
+ borderWidth: 0,
+ borderColor: "#E5E7EB",
+ opacity: 1,
+ textAlign: "center" as const,
+ padding: 0,
+ shadow: false,
+ shadowColor: "rgba(0, 0, 0, 0.1)",
+ shadowBlur: 8,
+ shadowOffsetX: 0,
+ shadowOffsetY: 2,
+ },
+ content: {
+ emoji: emoji.emoji,
+ },
+ }
+ addElement(newElement)
+ }}
+ title={emoji.label}
+ >
+
{emoji.emoji}
+
{emoji.label}
+
+ ))}
+
+
+
+ ))}
+
+
+
+
+
+
+ {/* Center - Canvas */}
+
+
+
+
+ {/* Right Sidebar - Properties */}
+
+
+
Properties
+
+ {selectedElements.length > 0 ? "Edit selected element" : "Select an element to edit"}
+
+
+
+
+
+ {selectedElement ? (
+
+ {/* Element Info */}
+
+
+ {selectedElement.type.charAt(0).toUpperCase() + selectedElement.type.slice(1)}
+
+
+ updateElement({ ...selectedElement, visible: !selectedElement.visible })}
+ className="cursor-pointer"
+ >
+ {selectedElement.visible ? : }
+
+ updateElement({ ...selectedElement, locked: !selectedElement.locked })}
+ className="cursor-pointer"
+ >
+ {selectedElement.locked ? : }
+
+ deleteElement(selectedElement.id)}
+ className="text-red-400 hover:text-red-300 cursor-pointer"
+ >
+
+
+
+
+
+ {/* Position & Size */}
+
+
Position & Size
+
+
+ X
+
+ updateElement({
+ ...selectedElement,
+ position: { ...selectedElement.position, x: Number(e.target.value) || 0 },
+ })
+ }
+ className="bg-gray-800 border-gray-600 text-white h-8 text-xs"
+ />
+
+
+ Y
+
+ updateElement({
+ ...selectedElement,
+ position: { ...selectedElement.position, y: Number(e.target.value) || 0 },
+ })
+ }
+ className="bg-gray-800 border-gray-600 text-white h-8 text-xs"
+ />
+
+
+ Width
+
+ updateElement({
+ ...selectedElement,
+ size: { ...selectedElement.size, width: Number(e.target.value) || 0 },
+ })
+ }
+ className="bg-gray-800 border-gray-600 text-white h-8 text-xs"
+ />
+
+
+ Height
+
+ updateElement({
+ ...selectedElement,
+ size: { ...selectedElement.size, height: Number(e.target.value) || 0 },
+ })
+ }
+ className="bg-gray-800 border-gray-600 text-white h-8 text-xs"
+ />
+
+
+
+
+ {/* Content */}
+ {selectedElement.type === "text" && (
+
+ Content
+
+ updateElement({
+ ...selectedElement,
+ content: { ...selectedElement.content, text: e.target.value },
+ })
+ }
+ placeholder="Enter text"
+ className="bg-gray-800 border-gray-600 text-white"
+ />
+
+ )}
+
+ {selectedElement.type === "variable" && (
+
+ Content
+ {
+ updateElement({
+ ...selectedElement,
+ content: {
+ variable: value,
+ text: `{{${value}}}`,
+ },
+ })
+ }}
+ >
+
+
+
+
+ {GITHUB_VARIABLES.map((variable) => (
+
+ {variable.emoji} {variable.label}
+
+ ))}
+
+
+
+ )}
+
+ {selectedElement.type === "emoji" && (
+
+
Emoji
+
+ {GITHUB_EMOJIS.map((emoji) => (
+
+ updateElement({
+ ...selectedElement,
+ content: { ...selectedElement.content, emoji: emoji.emoji },
+ })
+ }
+ title={emoji.label}
+ >
+ {emoji.emoji}
+
+ ))}
+
+
+ )}
+
+ {/* Typography */}
+ {(selectedElement.type === "text" || selectedElement.type === "variable") && (
+
+
Typography
+
+
+ Font Family
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, fontFamily: value },
+ })
+ }
+ >
+
+
+
+
+ {FONT_FAMILIES.map((font) => (
+
+ {font}
+
+ ))}
+
+
+
+
+
+
Font Size
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, fontSize: value },
+ })
+ }
+ min={8}
+ max={72}
+ step={1}
+ className="[&_[role=slider]]:bg-blue-500"
+ />
+
+ 8px
+ {selectedElement.style.fontSize}px
+ 72px
+
+
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: {
+ ...selectedElement.style,
+ fontWeight: selectedElement.style.fontWeight === "bold" ? "normal" : "bold",
+ },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: {
+ ...selectedElement.style,
+ fontStyle: selectedElement.style.fontStyle === "italic" ? "normal" : "italic",
+ },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: {
+ ...selectedElement.style,
+ textDecoration:
+ selectedElement.style.textDecoration === "underline" ? "none" : "underline",
+ },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, textAlign: "left" },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, textAlign: "center" },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, textAlign: "right" },
+ })
+ }
+ className="flex-1 cursor-pointer"
+ >
+
+
+
+
+
+ )}
+
+ {/* Emoji Size */}
+ {selectedElement.type === "emoji" && (
+
+
Size
+
+
Emoji Size
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, fontSize: value },
+ })
+ }
+ min={16}
+ max={128}
+ step={4}
+ className="[&_[role=slider]]:bg-blue-500"
+ />
+
+ 16px
+ {selectedElement.style.fontSize}px
+ 128px
+
+
+
+ )}
+
+ {/* Colors */}
+
+
Colors
+
+
+
Background Color
+
+
+ updateElementWithSmartColor(
+ {
+ ...selectedElement,
+ style: { ...selectedElement.style, backgroundColor: e.target.value },
+ },
+ true,
+ )
+ }
+ className="w-12 h-8 p-1 bg-gray-800 border-gray-600 cursor-pointer"
+ />
+
+ updateElementWithSmartColor(
+ {
+ ...selectedElement,
+ style: { ...selectedElement.style, backgroundColor: e.target.value },
+ },
+ true,
+ )
+ }
+ className="bg-gray-800 border-gray-600 text-white text-xs h-8"
+ />
+
+
+
+ {(selectedElement.type === "text" || selectedElement.type === "variable") && (
+
+ )}
+
+
+
+ {/* Border & Effects */}
+
+
Border & Effects
+
+
+
Border Radius
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, borderRadius: value },
+ })
+ }
+ min={0}
+ max={50}
+ step={1}
+ className="[&_[role=slider]]:bg-blue-500"
+ />
+
+ 0px
+ {selectedElement.style.borderRadius}px
+ 50px
+
+
+
+
+
Opacity
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, opacity: value },
+ })
+ }
+ min={0}
+ max={1}
+ step={0.1}
+ className="[&_[role=slider]]:bg-blue-500"
+ />
+
+ 0%
+ {Math.round(selectedElement.style.opacity * 100)}%
+ 100%
+
+
+
+
+ Drop Shadow
+
+ updateElement({
+ ...selectedElement,
+ style: { ...selectedElement.style, shadow: checked },
+ })
+ }
+ />
+
+
+
+
+ ) : (
+
+
+
No Element Selected
+
Click on an element in the canvas to edit its properties
+
+ )}
+
+
+
+ {/* Layers Panel */}
+
+
+
Layers
+
+ {widget.elements.length}
+
+
+
+
+ {widget.elements
+ .sort((a, b) => b.zIndex - a.zIndex)
+ .map((element) => (
+
el.id === element.id)
+ ? "bg-blue-600/20 border border-blue-500/30"
+ : "hover:bg-gray-800"
+ }`}
+ onClick={() => setSelectedElements([element])}
+ >
+
+
+
+ {element.type.charAt(0).toUpperCase() + element.type.slice(1)}
+
+
+
+ {
+ e.stopPropagation()
+ updateElement({ ...element, visible: !element.visible })
+ }}
+ className="text-gray-400 hover:text-white p-1 h-6 w-6 cursor-pointer"
+ >
+ {element.visible ? : }
+
+ {
+ e.stopPropagation()
+ updateElement({ ...element, locked: !element.locked })
+ }}
+ className="text-gray-400 hover:text-white p-1 h-6 w-6 cursor-pointer"
+ >
+ {element.locked ? : }
+
+
+
+ ))}
+
+
+
+
+
+
+
+ )
+}
+
+export default function GitHubWidgetBuilder() {
+ return (
+
+
+
+ )
+}
diff --git a/devboard/components.json b/devboard/components.json
new file mode 100644
index 0000000..335484f
--- /dev/null
+++ b/devboard/components.json
@@ -0,0 +1,21 @@
+{
+ "$schema": "https://ui.shadcn.com/schema.json",
+ "style": "new-york",
+ "rsc": true,
+ "tsx": true,
+ "tailwind": {
+ "config": "",
+ "css": "app/globals.css",
+ "baseColor": "neutral",
+ "cssVariables": true,
+ "prefix": ""
+ },
+ "aliases": {
+ "components": "@/components",
+ "utils": "@/lib/utils",
+ "ui": "@/components/ui",
+ "lib": "@/lib",
+ "hooks": "@/hooks"
+ },
+ "iconLibrary": "lucide"
+}
\ No newline at end of file
diff --git a/devboard/components/Hero/index.tsx b/devboard/components/Hero/index.tsx
new file mode 100644
index 0000000..bb4e172
--- /dev/null
+++ b/devboard/components/Hero/index.tsx
@@ -0,0 +1,209 @@
+"use client"
+
+import { useEffect, useRef } from "react"
+import { Poppins } from "next/font/google"
+import { ArrowRight, Plus, FileText } from "lucide-react"
+import { useAuth } from "@/lib/auth-context"
+
+// Initialize the Poppins font
+const poppins = Poppins({
+ subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
+ variable: "--font-poppins",
+})
+
+export default function HeroSection() {
+ const canvasRef = useRef(null)
+ const { isAuthenticated, isLoading } = useAuth()
+
+ useEffect(() => {
+ const canvas = canvasRef.current
+ if (!canvas) return
+
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ // Set canvas dimensions
+ const setCanvasDimensions = () => {
+ canvas.width = window.innerWidth
+ canvas.height = window.innerHeight
+ }
+
+ setCanvasDimensions()
+ window.addEventListener("resize", setCanvasDimensions)
+
+ // Create floating shapes
+ const shapes: Shape[] = []
+ const colors = ["#3F1469"]
+
+ for (let i = 0; i < 15; i++) {
+ shapes.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 80 + 20,
+ dx: (Math.random() - 0.5) * 0.5,
+ dy: (Math.random() - 0.5) * 0.5,
+ color: colors[Math.floor(Math.random() * colors.length)],
+ opacity: Math.random() * 0.3 + 0.1,
+ })
+ }
+
+ // Animation loop
+ const animate = () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+
+ // Draw shapes
+ shapes.forEach((shape) => {
+ ctx.beginPath()
+ ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2)
+ ctx.fillStyle = `${shape.color}${Math.floor(shape.opacity * 255)
+ .toString(16)
+ .padStart(2, "0")}`
+ ctx.fill()
+
+ // Update position
+ shape.x += shape.dx
+ shape.y += shape.dy
+
+ // Bounce off edges with some buffer
+ if (shape.x < -shape.radius || shape.x > canvas.width + shape.radius) {
+ shape.dx = -shape.dx
+ }
+ if (shape.y < -shape.radius || shape.y > canvas.height + shape.radius) {
+ shape.dy = -shape.dy
+ }
+ })
+
+ requestAnimationFrame(animate)
+ }
+
+ animate()
+
+ return () => {
+ window.removeEventListener("resize", setCanvasDimensions)
+ }
+ }, [])
+
+ const handleCreateWidget = () => {
+ window.location.href = "/widget"
+ }
+
+ const handleReadme = () => {
+ window.location.href = "/readme"
+ }
+
+ const handleGetStarted = () => {
+ window.location.href = "/login"
+ }
+
+ return (
+
+ {/* Background canvas for floating shapes */}
+
+
+ {/* Hero content */}
+
+
+ Your Dev Dashboard.
+
+
+ Built Your Way.
+
+
+ Create your own custom widgets. No coding required, just drag and drop!
+ Perfect for developers who want a personalized workspace.
+
+
+ {/* Conditional button rendering */}
+ {!isLoading && (
+ <>
+ {!isAuthenticated ? (
+ // Single "Get Started" button for non-authenticated users
+
+ Get Started
+
+
+
+ ) : (
+ // Two buttons side by side for authenticated users
+
+
+
+ Create Widget
+
+
+
+
+
+ Readme
+
+
+
+ )}
+ >
+ )}
+
+
+
+
+ )
+}
+
+// Types
+interface Shape {
+ x: number
+ y: number
+ radius: number
+ dx: number
+ dy: number
+ color: string
+ opacity: number
+}
diff --git a/devboard/components/Navbar/index.tsx b/devboard/components/Navbar/index.tsx
new file mode 100644
index 0000000..c0ed86f
--- /dev/null
+++ b/devboard/components/Navbar/index.tsx
@@ -0,0 +1,153 @@
+"use client"
+
+import { useState, useRef, useEffect } from "react"
+import Link from "next/link"
+import { useRouter } from "next/navigation"
+import { ChevronDown, LogOut, User, Settings } from "lucide-react"
+import { useAuth } from "@/lib/auth-context"
+import { toast } from "sonner"
+
+export default function Navbar() {
+ const [isDropdownOpen, setIsDropdownOpen] = useState(false)
+ const dropdownRef = useRef(null)
+ const { user, isAuthenticated, logout, isLoading } = useAuth()
+ const router = useRouter()
+
+ // Close dropdown when clicking outside
+ useEffect(() => {
+ function handleClickOutside(event: MouseEvent) {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
+ setIsDropdownOpen(false)
+ }
+ }
+
+ document.addEventListener("mousedown", handleClickOutside)
+ return () => document.removeEventListener("mousedown", handleClickOutside)
+ }, [])
+
+ const handleLogout = async () => {
+ try {
+ await logout()
+ toast.success("Logged out successfully")
+ router.push("/login")
+ } catch (error) {
+ toast.error("Logout failed")
+ }
+ setIsDropdownOpen(false)
+ }
+
+ return (
+
+
+ {/* DevBoard Logo */}
+
+
+ DevBoard
+
+
+
+ {/* User Section */}
+
+ {!isLoading && (
+ <>
+ {isAuthenticated && user ? (
+
+
setIsDropdownOpen(!isDropdownOpen)}
+ className="flex items-center gap-2 p-2 rounded-full hover:bg-white/10 transition-colors group"
+ >
+ {/* User Avatar */}
+
+ {user.avatar_url ? (
+
{
+ const target = e.target as HTMLImageElement
+ target.style.display = "none"
+ const fallback = target.parentElement?.querySelector(".fallback-icon") as HTMLElement
+ if (fallback) {
+ fallback.style.display = "block"
+ }
+ }}
+ />
+ ) : (
+
+
+
+ )}
+
+
+
+
+
+ {/* Username */}
+ {user.username}
+
+ {/* Dropdown Arrow */}
+
+
+
+ {/* Dropdown Menu */}
+ {isDropdownOpen && (
+
+ {/* User Info */}
+
+
{user.name || user.username}
+
@{user.username}
+ {user.email &&
{user.email}
}
+
+
+ {/* Menu Items */}
+
+
{
+ router.push("/profile")
+ setIsDropdownOpen(false)
+ }}
+ className="flex items-center gap-3 w-full px-4 py-2 text-gray-300 hover:bg-[#211D2E] hover:text-white transition-colors"
+ >
+
+ Profile
+
+
+
+
+
+
+
+
+ Logout
+
+
+
+ )}
+
+ ) : (
+
+ Login
+
+ )}
+ >
+ )}
+
+
+
+ )
+}
diff --git a/devboard/components/auth-handler.tsx b/devboard/components/auth-handler.tsx
new file mode 100644
index 0000000..4d0e213
--- /dev/null
+++ b/devboard/components/auth-handler.tsx
@@ -0,0 +1,76 @@
+"use client"
+
+import { useEffect, useState } from "react"
+import { useSearchParams } from "next/navigation"
+import { Loader2 } from "lucide-react"
+import { toast } from "sonner"
+import { useAuth } from "@/lib/auth-context"
+
+export default function AuthHandler() {
+ const [isProcessingAuth, setIsProcessingAuth] = useState(false)
+ const searchParams = useSearchParams()
+ const { login, isAuthenticated } = useAuth()
+
+ // Handle authentication tokens from URL (since backend redirects to home page)
+ useEffect(() => {
+ const handleAuthTokens = async () => {
+ try {
+ const accessToken = searchParams.get("access_token")
+ const refreshToken = searchParams.get("refresh_token")
+ const error = searchParams.get("error")
+
+ console.log("HOME PAGE - Checking for auth tokens...")
+ console.log("URL Tokens:", {
+ hasAccessToken: !!accessToken,
+ hasRefreshToken: !!refreshToken,
+ error,
+ fullURL: window.location.href,
+ })
+
+ if (error) {
+ console.error("Auth error in URL:", error)
+ toast.error("Authentication failed: " + decodeURIComponent(error))
+ return
+ }
+
+ if (accessToken && refreshToken && !isAuthenticated) {
+ console.log("Found tokens in URL, processing login...")
+ setIsProcessingAuth(true)
+ try {
+ await login(accessToken, refreshToken)
+ console.log("Login successful from URL tokens")
+ toast.success("Successfully logged in!")
+ // Clean up URL by removing the tokens
+ const cleanUrl = window.location.pathname
+ window.history.replaceState({}, document.title, cleanUrl)
+ } catch (error) {
+ console.error("Login failed:", error)
+ toast.error("Login failed: " + (error instanceof Error ? error.message : "Unknown error"))
+ } finally {
+ setIsProcessingAuth(false)
+ }
+ }
+ } catch (error) {
+ console.error("Error handling auth tokens:", error)
+ setIsProcessingAuth(false)
+ }
+ }
+
+ handleAuthTokens()
+ }, [searchParams, login, isAuthenticated])
+
+ // Show loading state while processing authentication
+ if (isProcessingAuth) {
+ return (
+
+
+
+
Processing authentication...
+
+
+ )
+ }
+
+ // Return null when not processing auth (component is just for handling auth logic)
+ return null
+}
diff --git a/devboard/components/login-auth-handler/index.tsx b/devboard/components/login-auth-handler/index.tsx
new file mode 100644
index 0000000..0342ce7
--- /dev/null
+++ b/devboard/components/login-auth-handler/index.tsx
@@ -0,0 +1,85 @@
+"use client"
+
+import { useEffect, useState, useRef } from "react"
+import { useRouter, useSearchParams } from "next/navigation"
+import { Loader2 } from "lucide-react"
+import { toast } from "sonner"
+import { useAuth } from "@/lib/auth-context"
+
+export default function LoginAuthHandler() {
+ const [isProcessingAuth, setIsProcessingAuth] = useState(false)
+ const router = useRouter()
+ const searchParams = useSearchParams()
+ const { login, isAuthenticated } = useAuth()
+ const hasProcessedTokens = useRef(false)
+
+ // Redirect if already authenticated - only run once when isAuthenticated changes
+ useEffect(() => {
+ if (isAuthenticated && !isProcessingAuth) {
+ router.push("/")
+ }
+ }, [isAuthenticated, router, isProcessingAuth])
+
+ // Handle tokens when they come back from backend - only run once
+ useEffect(() => {
+ // Prevent multiple executions
+ if (hasProcessedTokens.current) return
+
+ const handleTokens = async () => {
+ const accessToken = searchParams.get("access_token")
+ const refreshToken = searchParams.get("refresh_token")
+ const error = searchParams.get("error")
+
+ console.log("🔐 LOGIN PAGE - Checking for tokens:", {
+ hasAccessToken: !!accessToken,
+ hasRefreshToken: !!refreshToken,
+ error,
+ })
+
+ if (error) {
+ toast.error("Authentication failed: " + decodeURIComponent(error))
+ hasProcessedTokens.current = true
+ return
+ }
+
+ if (accessToken && refreshToken) {
+ console.log("✅ Tokens found, logging in...")
+ hasProcessedTokens.current = true
+ setIsProcessingAuth(true)
+
+ try {
+ await login(accessToken, refreshToken)
+ toast.success("Login successful!")
+ // Clean up URL
+ const cleanUrl = window.location.pathname
+ window.history.replaceState({}, document.title, cleanUrl)
+ router.push("/")
+ } catch (error) {
+ console.error("❌ Login failed:", error)
+ toast.error("Login failed")
+ } finally {
+ setIsProcessingAuth(false)
+ }
+ } else {
+ hasProcessedTokens.current = true
+ }
+ }
+
+ handleTokens()
+ }, []) // Empty dependency array - only run once on mount
+
+ // Show loading overlay while processing authentication
+ if (isProcessingAuth) {
+ return (
+
+
+
+
Processing authentication...
+
+
+ )
+ }
+
+ // Return null when not processing auth (component is just for handling auth logic)
+ return null
+}
diff --git a/devboard/components/login-content/index.tsx b/devboard/components/login-content/index.tsx
new file mode 100644
index 0000000..4392c11
--- /dev/null
+++ b/devboard/components/login-content/index.tsx
@@ -0,0 +1,187 @@
+"use client"
+
+import { useEffect, useRef, useState } from "react"
+import { Poppins } from "next/font/google"
+import Link from "next/link"
+import { Loader2 } from "lucide-react"
+
+// Initialize the Poppins font
+const poppins = Poppins({
+ subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
+ variable: "--font-poppins",
+})
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://devboard.varshith.tech"
+
+// Types
+interface Shape {
+ x: number
+ y: number
+ radius: number
+ dx: number
+ dy: number
+ color: string
+ opacity: number
+}
+
+export default function LoginContent() {
+ const canvasRef = useRef(null)
+ const [isLoading, setIsLoading] = useState(false)
+
+ useEffect(() => {
+ const canvas = canvasRef.current
+ if (!canvas) return
+
+ const ctx = canvas.getContext("2d")
+ if (!ctx) return
+
+ // Set canvas dimensions
+ const setCanvasDimensions = () => {
+ canvas.width = window.innerWidth
+ canvas.height = window.innerHeight
+ }
+
+ setCanvasDimensions()
+ window.addEventListener("resize", setCanvasDimensions)
+
+ // Create floating shapes
+ const shapes: Shape[] = []
+ const colors = ["#3F1469"]
+
+ for (let i = 0; i < 15; i++) {
+ shapes.push({
+ x: Math.random() * canvas.width,
+ y: Math.random() * canvas.height,
+ radius: Math.random() * 80 + 20,
+ dx: (Math.random() - 0.5) * 0.5,
+ dy: (Math.random() - 0.5) * 0.5,
+ color: colors[Math.floor(Math.random() * colors.length)],
+ opacity: Math.random() * 0.3 + 0.1,
+ })
+ }
+
+ // Animation loop
+ const animate = () => {
+ ctx.clearRect(0, 0, canvas.width, canvas.height)
+
+ // Draw shapes
+ shapes.forEach((shape) => {
+ ctx.beginPath()
+ ctx.arc(shape.x, shape.y, shape.radius, 0, Math.PI * 2)
+ ctx.fillStyle = `${shape.color}${Math.floor(shape.opacity * 255)
+ .toString(16)
+ .padStart(2, "0")}`
+ ctx.fill()
+
+ // Update position
+ shape.x += shape.dx
+ shape.y += shape.dy
+
+ // Bounce off edges with some buffer
+ if (shape.x < -shape.radius || shape.x > canvas.width + shape.radius) {
+ shape.dx = -shape.dx
+ }
+ if (shape.y < -shape.radius || shape.y > canvas.height + shape.radius) {
+ shape.dy = -shape.dy
+ }
+ })
+
+ requestAnimationFrame(animate)
+ }
+
+ animate()
+
+ return () => {
+ window.removeEventListener("resize", setCanvasDimensions)
+ }
+ }, [])
+
+ const handleGitHubLogin = () => {
+ if (isLoading) return
+
+ setIsLoading(true)
+
+ // Create the callback URL for the current environment
+ const callbackUrl = `${window.location.origin}/login`
+
+ // Redirect directly to the backend auth endpoint
+ const authUrl = `${API_BASE_URL}/api/auth/login?callback_url=${encodeURIComponent(callbackUrl)}`
+
+ window.location.href = authUrl
+ }
+
+ return (
+
+ {/* Background canvas for floating shapes */}
+
+
+ {/* Login Card */}
+
+
+ {/* Main card content */}
+
+
+
Sign In
+
Welcome back! Please sign in to continue
+
+
+
+
+ {isLoading ? (
+
+ ) : (
+
+
+
+ )}
+
+ {isLoading ? "Processing..." : "Continue with Github"}
+
+ {!isLoading && (
+
+ )}
+
+
+
+
+ {/* Bottom section */}
+
+
+ Don't have an account?{" "}
+
+ Sign Up
+
+
+
+
+
+
+ {/* CSS for the shimmer animation */}
+
+
+ )
+}
diff --git a/devboard/components/marketplace/index.tsx b/devboard/components/marketplace/index.tsx
new file mode 100644
index 0000000..a4f88b2
--- /dev/null
+++ b/devboard/components/marketplace/index.tsx
@@ -0,0 +1,344 @@
+"use client"
+
+import { useState, useEffect, useCallback } from "react"
+import { Poppins } from "next/font/google"
+import { Search, Filter, ChevronLeft, ChevronRight, Eye, User, Tag } from "lucide-react"
+import { useAuth, authenticatedFetch } from "@/lib/auth-context"
+
+// Initialize the Poppins font
+const poppins = Poppins({
+ subsets: ["latin"],
+ weight: ["400", "500", "600", "700"],
+ variable: "--font-poppins",
+})
+
+// Types
+interface Widget {
+ _id: string
+ content: string
+ created_by: string
+ is_private: boolean
+ name: string
+ size: {
+ height: number
+ width: number
+ }
+ tags: string[]
+}
+
+interface MarketplaceFilters {
+ widgetType?: string
+ length: number
+ offset: number
+ search?: string
+}
+
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://devboard.varshith.tech"
+
+export default function Marketplace() {
+ const { isAuthenticated, isLoading: authLoading } = useAuth()
+ const [widgets, setWidgets] = useState([])
+ const [loading, setLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const [filters, setFilters] = useState({
+ widgetType: "totalPullRequestContributions", // Set default widget type
+ length: 10,
+ offset: 0,
+ })
+ const [totalCount, setTotalCount] = useState(0)
+ const [searchTerm, setSearchTerm] = useState("")
+ const [selectedWidgetType, setSelectedWidgetType] = useState("totalPullRequestContributions")
+
+ // Available widget types (updated to match your API)
+ const widgetTypes = [
+ "totalCommitContributions",
+ "totalIssueContributions",
+ "totalPullRequestContributions",
+ "totalRepositoriesWithContributedCommits",
+ "totalStars",
+ "totalRepositories",
+ "username",
+ ]
+
+ const fetchWidgets = useCallback(
+ async (currentFilters: MarketplaceFilters) => {
+ if (!isAuthenticated) return
+
+ setLoading(true)
+ setError(null)
+
+ try {
+ const params = new URLSearchParams()
+ if (currentFilters.widgetType) params.append("widgetType", currentFilters.widgetType)
+ params.append("length", currentFilters.length.toString())
+ params.append("offset", currentFilters.offset.toString())
+ if (currentFilters.search) params.append("search", currentFilters.search)
+
+ const url = `${API_BASE_URL}/api/marketplace/?${params.toString()}`
+ console.log("Fetching from URL:", url)
+ console.log("Filters:", currentFilters)
+
+ const response = await authenticatedFetch(url)
+
+ console.log("Response status:", response.status)
+ console.log("Response headers:", Object.fromEntries(response.headers.entries()))
+
+ if (!response.ok) {
+ // Try to get error details from response
+ let errorMessage = `Failed to fetch widgets: ${response.status}`
+ try {
+ const errorData = await response.json()
+ console.log("Error response data:", errorData)
+ errorMessage = errorData.message || errorData.error || errorMessage
+ } catch (parseError) {
+ // If we can't parse the error response, try to get text
+ try {
+ const errorText = await response.text()
+ console.log("Error response text:", errorText)
+ if (errorText) errorMessage += ` - ${errorText}`
+ } catch (textError) {
+ console.log("Could not parse error response")
+ }
+ }
+ throw new Error(errorMessage)
+ }
+
+ const data = await response.json()
+ console.log("Successful response data:", data)
+ setWidgets(Array.isArray(data) ? data : [])
+ setTotalCount(Array.isArray(data) ? data.length : 0)
+ } catch (err) {
+ const errorMessage = err instanceof Error ? err.message : "Failed to fetch widgets"
+ setError(errorMessage)
+ console.error("Error fetching widgets:", err)
+ } finally {
+ setLoading(false)
+ }
+ },
+ [isAuthenticated],
+ )
+
+ useEffect(() => {
+ if (isAuthenticated && !authLoading) {
+ fetchWidgets(filters)
+ }
+ }, [isAuthenticated, authLoading, fetchWidgets, filters])
+
+ const handleSearch = () => {
+ setFilters((prev) => ({
+ ...prev,
+ search: searchTerm,
+ offset: 0, // Reset to first page
+ }))
+ }
+
+ const handleWidgetTypeChange = (widgetType: string) => {
+ setSelectedWidgetType(widgetType)
+ setFilters((prev) => ({
+ ...prev,
+ widgetType: widgetType || "totalPullRequestContributions", // Use default instead of undefined
+ offset: 0, // Reset to first page
+ }))
+ }
+
+ const handlePrevPage = () => {
+ setFilters((prev) => ({
+ ...prev,
+ offset: Math.max(0, prev.offset - prev.length),
+ }))
+ }
+
+ const handleNextPage = () => {
+ setFilters((prev) => ({
+ ...prev,
+ offset: prev.offset + prev.length,
+ }))
+ }
+
+ const handleWidgetClick = (widget: Widget) => {
+ // Handle widget selection/preview
+ console.log("Widget clicked:", widget)
+ // You can implement widget preview or selection logic here
+ }
+
+ // Don't render if not authenticated
+ if (!isAuthenticated) {
+ return null
+ }
+
+ return (
+
+
+ {/* Header */}
+
+
Widget Marketplace
+
+
+ Discover and use community-created widgets for your developer dashboard
+
+
+
+ {/* Filters */}
+
+ {/* Search Bar */}
+
+
+
+ setSearchTerm(e.target.value)}
+ onKeyPress={(e) => e.key === "Enter" && handleSearch()}
+ className="w-full bg-[#1A1625] border border-[#3F1469] rounded-lg pl-10 pr-4 py-3 text-[#DEC9F0] placeholder-[#DEC9F0] placeholder-opacity-60 focus:outline-none focus:border-[#D3A8FF] focus:ring-1 focus:ring-[#D3A8FF]"
+ />
+
+
+
+ Search
+
+
+
+ {/* Widget Type Filter */}
+
+ handleWidgetTypeChange("totalPullRequestContributions")}
+ className={`px-4 py-2 rounded-full text-sm transition-colors duration-200 ${
+ selectedWidgetType === ""
+ ? "bg-[#3F1469] text-white"
+ : "bg-[#1A1625] text-[#DEC9F0] border border-[#3F1469] hover:border-[#D3A8FF]"
+ }`}
+ >
+ Default
+
+ {widgetTypes.map((type) => (
+ handleWidgetTypeChange(type)}
+ className={`px-4 py-2 rounded-full text-sm transition-colors duration-200 ${
+ selectedWidgetType === type
+ ? "bg-[#3F1469] text-white"
+ : "bg-[#1A1625] text-[#DEC9F0] border border-[#3F1469] hover:border-[#D3A8FF]"
+ }`}
+ >
+ {type}
+
+ ))}
+
+
+
+ {/* Loading State */}
+ {loading && (
+
+ )}
+
+ {/* Error State */}
+ {error && (
+
+ )}
+
+ {/* Widgets Grid */}
+ {!loading && !error && (
+ <>
+
+ {widgets.map((widget) => (
+
handleWidgetClick(widget)}
+ className="bg-[#1A1625] border border-[#3F1469] rounded-lg p-6 hover:border-[#D3A8FF] transition-all duration-200 cursor-pointer group hover:shadow-lg hover:shadow-[#3F1469]/20"
+ >
+ {/* Widget Header */}
+
+
+ {widget.name}
+
+ {!widget.is_private && }
+
+
+ {/* Widget Content Preview */}
+
+
+ {widget.content.length > 100 ? `${widget.content.substring(0, 100)}...` : widget.content}
+
+
+
+ {/* Widget Info */}
+
+
+
+ {widget.created_by}
+
+
+
+
+ Size: {widget.size.width}×{widget.size.height}
+
+
+
+ {/* Tags */}
+ {widget.tags.length > 0 && (
+
+
+ {widget.tags.map((tag, index) => (
+
+ {tag}
+
+ ))}
+
+ )}
+
+
+ ))}
+
+
+ {/* Pagination */}
+ {widgets.length > 0 && (
+
+
+
+ Previous
+
+
+
+ Showing {filters.offset + 1}-
+ {Math.min(filters.offset + filters.length, filters.offset + widgets.length)}
+
+
+
+ Next
+
+
+
+ )}
+
+ {/* Empty State */}
+ {widgets.length === 0 && !loading && (
+
+
+
+
No widgets found
+
Try adjusting your search criteria or filters
+
+
+ )}
+ >
+ )}
+
+
+ )
+}
diff --git a/devboard/components/protected-route/index.tsx b/devboard/components/protected-route/index.tsx
new file mode 100644
index 0000000..08f36f0
--- /dev/null
+++ b/devboard/components/protected-route/index.tsx
@@ -0,0 +1,72 @@
+"use client"
+
+import type React from "react"
+import { useEffect, useState } from "react"
+import { useRouter } from "next/navigation"
+import { Loader2 } from "lucide-react"
+import { useAuth } from "@/lib/auth-context"
+
+interface ProtectedRouteProps {
+ children: React.ReactNode
+ fallback?: React.ReactNode
+}
+
+export default function ProtectedRoute({ children, fallback }: ProtectedRouteProps) {
+ const [isLoading, setIsLoading] = useState(true)
+ const [isAuthed, setIsAuthed] = useState(false)
+ const router = useRouter()
+ const { isAuthenticated, refreshToken, isLoading: authLoading } = useAuth()
+
+ useEffect(() => {
+ const checkAuth = async () => {
+ // Wait for auth context to finish loading
+ if (authLoading) {
+ return
+ }
+
+ try {
+ // First check if we have valid tokens
+ if (isAuthenticated) {
+ setIsAuthed(true)
+ setIsLoading(false)
+ return
+ }
+
+ // Try to refresh the token
+ const refreshResult = await refreshToken()
+ if (refreshResult) {
+ setIsAuthed(true)
+ } else {
+ // No valid authentication, redirect to login
+ router.push("/login")
+ }
+ } catch (error) {
+ console.error("Auth check error:", error)
+ router.push("/login")
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ checkAuth()
+ }, [isAuthenticated, refreshToken, router, authLoading])
+
+ if (isLoading || authLoading) {
+ return (
+ fallback || (
+
+
+
+
Checking authentication...
+
+
+ )
+ )
+ }
+
+ if (!isAuthed) {
+ return null // Will redirect to login
+ }
+
+ return <>{children}>
+}
diff --git a/devboard/components/ui/badge.tsx b/devboard/components/ui/badge.tsx
new file mode 100644
index 0000000..0205413
--- /dev/null
+++ b/devboard/components/ui/badge.tsx
@@ -0,0 +1,46 @@
+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 badgeVariants = cva(
+ "inline-flex items-center justify-center rounded-md border px-2 py-0.5 text-xs font-medium w-fit whitespace-nowrap shrink-0 [&>svg]:size-3 gap-1 [&>svg]:pointer-events-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive transition-[color,box-shadow] overflow-hidden",
+ {
+ variants: {
+ variant: {
+ default:
+ "border-transparent bg-primary text-primary-foreground [a&]:hover:bg-primary/90",
+ secondary:
+ "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/90",
+ destructive:
+ "border-transparent bg-destructive text-white [a&]:hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "text-foreground [a&]:hover:bg-accent [a&]:hover:text-accent-foreground",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Badge({
+ className,
+ variant,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"span"> &
+ VariantProps & { asChild?: boolean }) {
+ const Comp = asChild ? Slot : "span"
+
+ return (
+
+ )
+}
+
+export { Badge, badgeVariants }
diff --git a/devboard/components/ui/button.tsx b/devboard/components/ui/button.tsx
new file mode 100644
index 0000000..a2df8dc
--- /dev/null
+++ b/devboard/components/ui/button.tsx
@@ -0,0 +1,59 @@
+import * as React from "react"
+import { Slot } from "@radix-ui/react-slot"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const buttonVariants = cva(
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
+ {
+ variants: {
+ variant: {
+ default:
+ "bg-primary text-primary-foreground shadow-xs hover:bg-primary/90",
+ destructive:
+ "bg-destructive text-white shadow-xs hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
+ outline:
+ "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
+ secondary:
+ "bg-secondary text-secondary-foreground shadow-xs hover:bg-secondary/80",
+ ghost:
+ "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
+ link: "text-primary underline-offset-4 hover:underline",
+ },
+ size: {
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
+ icon: "size-9",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ size: "default",
+ },
+ }
+)
+
+function Button({
+ className,
+ variant,
+ size,
+ asChild = false,
+ ...props
+}: React.ComponentProps<"button"> &
+ VariantProps & {
+ asChild?: boolean
+ }) {
+ const Comp = asChild ? Slot : "button"
+
+ return (
+
+ )
+}
+
+export { Button, buttonVariants }
diff --git a/devboard/components/ui/card.tsx b/devboard/components/ui/card.tsx
new file mode 100644
index 0000000..d05bbc6
--- /dev/null
+++ b/devboard/components/ui/card.tsx
@@ -0,0 +1,92 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Card({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardDescription({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardAction({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardContent({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function CardFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export {
+ Card,
+ CardHeader,
+ CardFooter,
+ CardTitle,
+ CardAction,
+ CardDescription,
+ CardContent,
+}
diff --git a/devboard/components/ui/collapsible.tsx b/devboard/components/ui/collapsible.tsx
new file mode 100644
index 0000000..67a8c89
--- /dev/null
+++ b/devboard/components/ui/collapsible.tsx
@@ -0,0 +1,10 @@
+"use client"
+import * as CollapsiblePrimitive from "@radix-ui/react-collapsible"
+
+const Collapsible = CollapsiblePrimitive.Root
+
+const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger
+
+const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent
+
+export { Collapsible, CollapsibleTrigger, CollapsibleContent }
diff --git a/devboard/components/ui/dialog.tsx b/devboard/components/ui/dialog.tsx
new file mode 100644
index 0000000..d9ccec9
--- /dev/null
+++ b/devboard/components/ui/dialog.tsx
@@ -0,0 +1,143 @@
+"use client"
+
+import * as React from "react"
+import * as DialogPrimitive from "@radix-ui/react-dialog"
+import { XIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Dialog({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogTrigger({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogPortal({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogClose({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function DialogOverlay({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogContent({
+ className,
+ children,
+ showCloseButton = true,
+ ...props
+}: React.ComponentProps & {
+ showCloseButton?: boolean
+}) {
+ return (
+
+
+
+ {children}
+ {showCloseButton && (
+
+
+ Close
+
+ )}
+
+
+ )
+}
+
+function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function DialogFooter({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function DialogTitle({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function DialogDescription({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export {
+ Dialog,
+ DialogClose,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogOverlay,
+ DialogPortal,
+ DialogTitle,
+ DialogTrigger,
+}
diff --git a/devboard/components/ui/input.tsx b/devboard/components/ui/input.tsx
new file mode 100644
index 0000000..03295ca
--- /dev/null
+++ b/devboard/components/ui/input.tsx
@@ -0,0 +1,21 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Input({ className, type, ...props }: React.ComponentProps<"input">) {
+ return (
+
+ )
+}
+
+export { Input }
diff --git a/devboard/components/ui/label.tsx b/devboard/components/ui/label.tsx
new file mode 100644
index 0000000..fb5fbc3
--- /dev/null
+++ b/devboard/components/ui/label.tsx
@@ -0,0 +1,24 @@
+"use client"
+
+import * as React from "react"
+import * as LabelPrimitive from "@radix-ui/react-label"
+
+import { cn } from "@/lib/utils"
+
+function Label({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Label }
diff --git a/devboard/components/ui/progress.tsx b/devboard/components/ui/progress.tsx
new file mode 100644
index 0000000..df460f2
--- /dev/null
+++ b/devboard/components/ui/progress.tsx
@@ -0,0 +1,24 @@
+"use client"
+
+import * as React from "react"
+import * as ProgressPrimitive from "@radix-ui/react-progress"
+import { cn } from "@/lib/utils"
+
+const Progress = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, value, ...props }, ref) => (
+
+
+
+))
+Progress.displayName = ProgressPrimitive.Root.displayName
+
+export { Progress }
diff --git a/devboard/components/ui/resizable.tsx b/devboard/components/ui/resizable.tsx
new file mode 100644
index 0000000..12bbd0b
--- /dev/null
+++ b/devboard/components/ui/resizable.tsx
@@ -0,0 +1,56 @@
+"use client"
+
+import * as React from "react"
+import { GripVerticalIcon } from "lucide-react"
+import * as ResizablePrimitive from "react-resizable-panels"
+
+import { cn } from "@/lib/utils"
+
+function ResizablePanelGroup({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function ResizablePanel({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function ResizableHandle({
+ withHandle,
+ className,
+ ...props
+}: React.ComponentProps & {
+ withHandle?: boolean
+}) {
+ return (
+ div]:rotate-90",
+ className
+ )}
+ {...props}
+ >
+ {withHandle && (
+
+
+
+ )}
+
+ )
+}
+
+export { ResizablePanelGroup, ResizablePanel, ResizableHandle }
diff --git a/devboard/components/ui/scroll-area.tsx b/devboard/components/ui/scroll-area.tsx
new file mode 100644
index 0000000..8e4fa13
--- /dev/null
+++ b/devboard/components/ui/scroll-area.tsx
@@ -0,0 +1,58 @@
+"use client"
+
+import * as React from "react"
+import * as ScrollAreaPrimitive from "@radix-ui/react-scroll-area"
+
+import { cn } from "@/lib/utils"
+
+function ScrollArea({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function ScrollBar({
+ className,
+ orientation = "vertical",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export { ScrollArea, ScrollBar }
diff --git a/devboard/components/ui/select.tsx b/devboard/components/ui/select.tsx
new file mode 100644
index 0000000..dcbbc0c
--- /dev/null
+++ b/devboard/components/ui/select.tsx
@@ -0,0 +1,185 @@
+"use client"
+
+import * as React from "react"
+import * as SelectPrimitive from "@radix-ui/react-select"
+import { CheckIcon, ChevronDownIcon, ChevronUpIcon } from "lucide-react"
+
+import { cn } from "@/lib/utils"
+
+function Select({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectGroup({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectValue({
+ ...props
+}: React.ComponentProps) {
+ return
+}
+
+function SelectTrigger({
+ className,
+ size = "default",
+ children,
+ ...props
+}: React.ComponentProps & {
+ size?: "sm" | "default"
+}) {
+ return (
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectContent({
+ className,
+ children,
+ position = "popper",
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+ {children}
+
+
+
+
+ )
+}
+
+function SelectLabel({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectItem({
+ className,
+ children,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+
+
+
+ {children}
+
+ )
+}
+
+function SelectSeparator({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function SelectScrollUpButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+function SelectScrollDownButton({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export {
+ Select,
+ SelectContent,
+ SelectGroup,
+ SelectItem,
+ SelectLabel,
+ SelectScrollDownButton,
+ SelectScrollUpButton,
+ SelectSeparator,
+ SelectTrigger,
+ SelectValue,
+}
diff --git a/devboard/components/ui/separator.tsx b/devboard/components/ui/separator.tsx
new file mode 100644
index 0000000..275381c
--- /dev/null
+++ b/devboard/components/ui/separator.tsx
@@ -0,0 +1,28 @@
+"use client"
+
+import * as React from "react"
+import * as SeparatorPrimitive from "@radix-ui/react-separator"
+
+import { cn } from "@/lib/utils"
+
+function Separator({
+ className,
+ orientation = "horizontal",
+ decorative = true,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Separator }
diff --git a/devboard/components/ui/slider.tsx b/devboard/components/ui/slider.tsx
new file mode 100644
index 0000000..09391e8
--- /dev/null
+++ b/devboard/components/ui/slider.tsx
@@ -0,0 +1,63 @@
+"use client"
+
+import * as React from "react"
+import * as SliderPrimitive from "@radix-ui/react-slider"
+
+import { cn } from "@/lib/utils"
+
+function Slider({
+ className,
+ defaultValue,
+ value,
+ min = 0,
+ max = 100,
+ ...props
+}: React.ComponentProps) {
+ const _values = React.useMemo(
+ () =>
+ Array.isArray(value)
+ ? value
+ : Array.isArray(defaultValue)
+ ? defaultValue
+ : [min, max],
+ [value, defaultValue, min, max]
+ )
+
+ return (
+
+
+
+
+ {Array.from({ length: _values.length }, (_, index) => (
+
+ ))}
+
+ )
+}
+
+export { Slider }
diff --git a/devboard/components/ui/sonner.tsx b/devboard/components/ui/sonner.tsx
new file mode 100644
index 0000000..957524e
--- /dev/null
+++ b/devboard/components/ui/sonner.tsx
@@ -0,0 +1,25 @@
+"use client"
+
+import { useTheme } from "next-themes"
+import { Toaster as Sonner, ToasterProps } from "sonner"
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme()
+
+ return (
+
+ )
+}
+
+export { Toaster }
diff --git a/devboard/components/ui/switch.tsx b/devboard/components/ui/switch.tsx
new file mode 100644
index 0000000..6a2b524
--- /dev/null
+++ b/devboard/components/ui/switch.tsx
@@ -0,0 +1,31 @@
+"use client"
+
+import * as React from "react"
+import * as SwitchPrimitive from "@radix-ui/react-switch"
+
+import { cn } from "@/lib/utils"
+
+function Switch({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+
+
+ )
+}
+
+export { Switch }
diff --git a/devboard/components/ui/tabs.tsx b/devboard/components/ui/tabs.tsx
new file mode 100644
index 0000000..497ba5e
--- /dev/null
+++ b/devboard/components/ui/tabs.tsx
@@ -0,0 +1,66 @@
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@/lib/utils"
+
+function Tabs({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function TabsList({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function TabsTrigger({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+function TabsContent({
+ className,
+ ...props
+}: React.ComponentProps) {
+ return (
+
+ )
+}
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
diff --git a/devboard/components/ui/textarea.tsx b/devboard/components/ui/textarea.tsx
new file mode 100644
index 0000000..7f21b5e
--- /dev/null
+++ b/devboard/components/ui/textarea.tsx
@@ -0,0 +1,18 @@
+import * as React from "react"
+
+import { cn } from "@/lib/utils"
+
+function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {
+ return (
+
+ )
+}
+
+export { Textarea }
diff --git a/devboard/components/widget-builder/index.tsx b/devboard/components/widget-builder/index.tsx
new file mode 100644
index 0000000..f1e9dd1
--- /dev/null
+++ b/devboard/components/widget-builder/index.tsx
@@ -0,0 +1,2575 @@
+"use client"
+
+import type React from "react"
+import { useState, useRef, useCallback, useEffect } from "react"
+import { DndProvider, useDrag, useDrop } from "react-dnd"
+import { HTML5Backend } from "react-dnd-html5-backend"
+import { Button } from "@/components/ui/button"
+import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Badge } from "@/components/ui/badge"
+import { ScrollArea } from "@/components/ui/scroll-area"
+import { Switch } from "@/components/ui/switch"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import { Slider } from "@/components/ui/slider"
+import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog"
+import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
+import { Separator } from "@/components/ui/separator"
+import {
+ Save,
+ Copy,
+ Type,
+ ImageIcon,
+ BarChart3,
+ Star,
+ ArrowLeft,
+ Layers,
+ Zap,
+ ZoomIn,
+ ZoomOut,
+ Square,
+ Circle,
+ Triangle,
+ Plus,
+ Hash,
+ Sun,
+ Moon,
+ ChevronDown,
+ Eye,
+ EyeOff,
+ Trash2,
+ ChevronUp,
+ Settings,
+ GripVertical,
+ Github,
+ RefreshCw,
+ Grid3X3,
+ AlignLeft,
+ AlignCenter,
+ AlignRight,
+ Undo,
+ Redo,
+ Maximize2,
+ Lock,
+ Unlock,
+ Target,
+ Sparkles,
+ Palette,
+ Move,
+} from "lucide-react"
+import { toast } from "sonner"
+import { useAuth } from "@/lib/auth-context"
+import { useRouter } from "next/navigation"
+
+// Enhanced Types
+interface WidgetElement {
+ id: string
+ type: "text" | "image" | "chart" | "badge" | "progress" | "container" | "shape" | "icon" | "line" | "button" | "qr"
+ position: { x: number; y: number }
+ size: { width: number; height: number }
+ rotation: number
+ style: {
+ backgroundColor?: string
+ color?: string
+ fontSize?: number
+ fontWeight?: string
+ fontFamily?: string
+ borderRadius?: number
+ borderWidth?: number
+ borderColor?: string
+ borderStyle?: string
+ padding?: number
+ opacity?: number
+ shadow?: string
+ gradient?: string
+ zIndex?: number
+ }
+ content: {
+ text?: string
+ variable?: string
+ imageUrl?: string
+ chartType?: string
+ badgeType?: string
+ progressValue?: number | string
+ shapeType?: string
+ iconType?: string
+ linkUrl?: string
+ githubData?: string
+ }
+ visible: boolean
+ locked: boolean
+}
+
+interface Widget {
+ id?: string
+ name: string
+ elements: WidgetElement[]
+ canvas: {
+ width: number
+ height: number
+ backgroundColor: string
+ theme: "dark" | "light"
+ showGrid: boolean
+ gridSize: number
+ snapToGrid: boolean
+ }
+ isPrivate: boolean
+ tags: string[]
+ githubUsername?: string
+}
+
+interface GitHubUserData {
+ login: string
+ name: string
+ bio: string
+ followers: number
+ following: number
+ public_repos: number
+ avatar_url: string
+ created_at: string
+ updated_at: string
+}
+
+// Premium Canvas Sizes inspired by top design tools
+const CANVAS_SIZES = [
+ { name: "GitHub Badge", width: 300, height: 120, description: "Perfect for profile badges", icon: "🏷️" },
+ { name: "Stats Card", width: 400, height: 200, description: "Comprehensive stats display", icon: "📊" },
+ { name: "Wide Banner", width: 600, height: 150, description: "Horizontal profile banner", icon: "🎯" },
+ { name: "Square Card", width: 300, height: 300, description: "Balanced square format", icon: "⬜" },
+ { name: "Large Display", width: 500, height: 300, description: "Detailed information panel", icon: "📱" },
+ { name: "Custom", width: 400, height: 200, description: "Set your own dimensions", icon: "✨" },
+]
+
+// GitHub Variables
+const GITHUB_VARIABLES = [
+ { name: "username", description: "GitHub username", category: "Profile", placeholder: "{{username}}" },
+ { name: "name", description: "Display name", category: "Profile", placeholder: "{{name}}" },
+ { name: "bio", description: "User bio", category: "Profile", placeholder: "{{bio}}" },
+ { name: "followers", description: "Number of followers", category: "Social", placeholder: "{{followers}}" },
+ { name: "following", description: "Number of following", category: "Social", placeholder: "{{following}}" },
+ {
+ name: "public_repos",
+ description: "Public repositories",
+ category: "Repositories",
+ placeholder: "{{public_repos}}",
+ },
+ { name: "avatar_url", description: "Profile picture URL", category: "Profile", placeholder: "{{avatar_url}}" },
+ { name: "created_at", description: "Account creation date", category: "Profile", placeholder: "{{created_at}}" },
+ {
+ name: "totalCommitContributions",
+ description: "Total commits",
+ category: "Activity",
+ placeholder: "{{totalCommitContributions}}",
+ },
+]
+
+// Premium Element Categories with better organization
+const ELEMENT_CATEGORIES = [
+ {
+ name: "Essentials",
+ icon: "✨",
+ elements: [
+ { type: "text", icon: Type, label: "Text", color: "from-blue-500 to-blue-600", description: "Add dynamic text" },
+ {
+ type: "image",
+ icon: ImageIcon,
+ label: "Image",
+ color: "from-green-500 to-green-600",
+ description: "Insert images",
+ },
+ {
+ type: "container",
+ icon: Layers,
+ label: "Container",
+ color: "from-purple-500 to-purple-600",
+ description: "Group elements",
+ },
+ ],
+ },
+ {
+ name: "Shapes",
+ icon: "🎨",
+ elements: [
+ {
+ type: "shape",
+ icon: Square,
+ label: "Rectangle",
+ color: "from-orange-500 to-orange-600",
+ shapeType: "rectangle",
+ description: "Basic rectangle",
+ },
+ {
+ type: "shape",
+ icon: Circle,
+ label: "Circle",
+ color: "from-pink-500 to-pink-600",
+ shapeType: "circle",
+ description: "Perfect circle",
+ },
+ {
+ type: "shape",
+ icon: Triangle,
+ label: "Triangle",
+ color: "from-yellow-500 to-yellow-600",
+ shapeType: "triangle",
+ description: "Triangle shape",
+ },
+ {
+ type: "shape",
+ icon: Star,
+ label: "Star",
+ color: "from-indigo-500 to-indigo-600",
+ shapeType: "star",
+ description: "Star shape",
+ },
+ ],
+ },
+ {
+ name: "GitHub Data",
+ icon: "📊",
+ elements: [
+ {
+ type: "chart",
+ icon: BarChart3,
+ label: "Stats Chart",
+ color: "from-cyan-500 to-cyan-600",
+ description: "Visual statistics",
+ },
+ {
+ type: "progress",
+ icon: Zap,
+ label: "Progress Bar",
+ color: "from-emerald-500 to-emerald-600",
+ description: "Progress indicator",
+ },
+ {
+ type: "badge",
+ icon: Hash,
+ label: "Data Badge",
+ color: "from-rose-500 to-rose-600",
+ description: "Highlight metrics",
+ },
+ {
+ type: "image",
+ icon: Github,
+ label: "Avatar",
+ color: "from-gray-500 to-gray-600",
+ githubData: "avatar",
+ description: "Profile picture",
+ },
+ ],
+ },
+ {
+ name: "Interactive",
+ icon: "⚡",
+ elements: [
+ {
+ type: "button",
+ icon: Plus,
+ label: "Button",
+ color: "from-violet-500 to-violet-600",
+ description: "Call-to-action",
+ },
+ {
+ type: "qr",
+ icon: Hash,
+ label: "QR Code",
+ color: "from-slate-500 to-slate-600",
+ description: "QR code generator",
+ },
+ ],
+ },
+]
+
+// Premium Draggable Element with Figma-inspired design
+function PremiumDraggableElement({ type, icon: Icon, label, color, shapeType, githubData, description }: any) {
+ const [{ isDragging }, dragRef] = useDrag(() => ({
+ type: "element",
+ item: { elementType: type, shapeType, githubData },
+ collect: (monitor) => ({
+ isDragging: monitor.isDragging(),
+ }),
+ }))
+
+ return (
+
+
+
+
+
+
{label}
+
{description}
+
+
+
+
+
+ )
+}
+
+// Premium Inline Text Editor
+function PremiumInlineTextEditor({
+ element,
+ onUpdate,
+ onFinish,
+}: {
+ element: WidgetElement
+ onUpdate: (element: WidgetElement) => void
+ onFinish: () => void
+}) {
+ const [text, setText] = useState(element.content.text || "")
+ const inputRef = useRef(null)
+
+ useEffect(() => {
+ if (inputRef.current) {
+ inputRef.current.focus()
+ inputRef.current.select()
+ }
+ }, [])
+
+ const handleKeyDown = (e: React.KeyboardEvent) => {
+ if (e.key === "Enter") {
+ onUpdate({
+ ...element,
+ content: { ...element.content, text },
+ })
+ onFinish()
+ } else if (e.key === "Escape") {
+ onFinish()
+ }
+ }
+
+ const handleBlur = () => {
+ onUpdate({
+ ...element,
+ content: { ...element.content, text },
+ })
+ onFinish()
+ }
+
+ return (
+ setText(e.target.value)}
+ onKeyDown={handleKeyDown}
+ onBlur={handleBlur}
+ className="w-full h-full bg-transparent border-2 border-blue-400 rounded-lg px-3 text-center outline-none shadow-2xl shadow-blue-500/20 backdrop-blur-sm"
+ style={{
+ fontSize: element.style.fontSize,
+ fontWeight: element.style.fontWeight,
+ fontFamily: element.style.fontFamily,
+ color: element.style.color,
+ }}
+ />
+ )
+}
+
+// GitHub Data Fetcher
+async function fetchGitHubData(username: string): Promise {
+ try {
+ const response = await fetch(`https://api.github.com/users/${username}`)
+ if (!response.ok) {
+ throw new Error("User not found")
+ }
+ return await response.json()
+ } catch (error) {
+ console.error("Error fetching GitHub data:", error)
+ return null
+ }
+}
+
+// Premium Canvas with Linear-inspired interactions
+function PremiumCanvas({
+ widget,
+ onElementAdd,
+ onElementSelect,
+ onElementUpdate,
+ selectedElements,
+ zoom,
+ onZoomChange,
+ githubData,
+ onWidgetUpdate,
+}: {
+ widget: Widget
+ onElementAdd: (element: WidgetElement) => void
+ onElementSelect: (elements: WidgetElement[]) => void
+ onElementUpdate: (element: WidgetElement) => void
+ selectedElements: WidgetElement[]
+ zoom: number
+ onZoomChange: (zoom: number) => void
+ githubData: GitHubUserData | null
+ onWidgetUpdate: (widget: Widget) => void
+}) {
+ const canvasRef = useRef(null)
+ const [draggedElement, setDraggedElement] = useState(null)
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
+ const [editingElement, setEditingElement] = useState(null)
+ const [isSpacePressed, setIsSpacePressed] = useState(false)
+ const [isPanning, setIsPanning] = useState(false)
+ const [panStart, setPanStart] = useState({ x: 0, y: 0 })
+ const [canvasOffset, setCanvasOffset] = useState({ x: 0, y: 0 })
+ const [selectionBox, setSelectionBox] = useState<{
+ start: { x: number; y: number }
+ end: { x: number; y: number }
+ active: boolean
+ }>({ start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, active: false })
+
+ const [{ isOver }, dropRef] = useDrop(() => ({
+ accept: "element",
+ drop: (item: { elementType: string; shapeType?: string; githubData?: string }, monitor) => {
+ const offset = monitor.getClientOffset()
+ if (offset && canvasRef.current) {
+ const canvasRect = canvasRef.current.getBoundingClientRect()
+ let x = Math.max(0, (offset.x - canvasRect.left - canvasOffset.x) / zoom)
+ let y = Math.max(0, (offset.y - canvasRect.top - canvasOffset.y) / zoom)
+
+ if (widget.canvas.snapToGrid) {
+ x = Math.round(x / widget.canvas.gridSize) * widget.canvas.gridSize
+ y = Math.round(y / widget.canvas.gridSize) * widget.canvas.gridSize
+ }
+
+ const newElement: WidgetElement = createNewElement(item.elementType, x, y, item.shapeType, item.githubData)
+ onElementAdd(newElement)
+ }
+ },
+ collect: (monitor) => ({
+ isOver: monitor.isOver(),
+ }),
+ }))
+
+ // Enhanced keyboard shortcuts
+ useEffect(() => {
+ const handleKeyDown = (e: KeyboardEvent) => {
+ if (e.code === "Space") {
+ e.preventDefault()
+ setIsSpacePressed(true)
+ }
+
+ if (e.key === "Delete" || e.key === "Backspace") {
+ selectedElements.forEach((element) => {
+ onWidgetUpdate({
+ ...widget,
+ elements: widget.elements.filter((el) => el.id !== element.id),
+ })
+ })
+ onElementSelect([])
+ }
+
+ if (e.ctrlKey || e.metaKey) {
+ if (e.key === "c" && selectedElements.length > 0) {
+ localStorage.setItem("copiedElements", JSON.stringify(selectedElements))
+ toast.success(`Copied ${selectedElements.length} element(s)`, {
+ icon: "📋",
+ })
+ }
+ if (e.key === "v") {
+ const copiedElements = localStorage.getItem("copiedElements")
+ if (copiedElements) {
+ const elements = JSON.parse(copiedElements) as WidgetElement[]
+ elements.forEach((element, index) => {
+ const newElement = {
+ ...element,
+ id: `element-${Date.now()}-${index}`,
+ position: {
+ x: element.position.x + 20,
+ y: element.position.y + 20,
+ },
+ }
+ onElementAdd(newElement)
+ })
+ toast.success(`Pasted ${elements.length} element(s)`, {
+ icon: "📌",
+ })
+ }
+ }
+ }
+ }
+
+ const handleKeyUp = (e: KeyboardEvent) => {
+ if (e.code === "Space") {
+ setIsSpacePressed(false)
+ setIsPanning(false)
+ }
+ }
+
+ document.addEventListener("keydown", handleKeyDown)
+ document.addEventListener("keyup", handleKeyUp)
+
+ return () => {
+ document.removeEventListener("keydown", handleKeyDown)
+ document.removeEventListener("keyup", handleKeyUp)
+ }
+ }, [selectedElements, widget, onElementAdd, onElementSelect, onWidgetUpdate])
+
+ // Smooth mouse wheel zoom
+ useEffect(() => {
+ const handleWheel = (e: WheelEvent) => {
+ if (e.ctrlKey || e.metaKey) {
+ e.preventDefault()
+ const delta = e.deltaY > 0 ? -0.1 : 0.1
+ const newZoom = Math.max(0.1, Math.min(5, zoom + delta))
+ onZoomChange(newZoom)
+ }
+ }
+
+ const canvas = canvasRef.current
+ if (canvas) {
+ canvas.addEventListener("wheel", handleWheel, { passive: false })
+ return () => canvas.removeEventListener("wheel", handleWheel)
+ }
+ }, [zoom, onZoomChange])
+
+ const createNewElement = (
+ type: string,
+ x: number,
+ y: number,
+ shapeType?: string,
+ githubData?: string,
+ ): WidgetElement => {
+ const baseElement = {
+ id: `element-${Date.now()}`,
+ position: { x, y },
+ rotation: 0,
+ visible: true,
+ locked: false,
+ style: {
+ backgroundColor: widget.canvas.theme === "dark" ? "#1A1625" : "#F8F9FA",
+ color: widget.canvas.theme === "dark" ? "#E2E8F0" : "#1A202C",
+ fontSize: 14,
+ fontWeight: "normal",
+ fontFamily: "Inter",
+ borderRadius: 4,
+ borderWidth: 0,
+ borderColor: "#E2E8F0",
+ borderStyle: "solid",
+ padding: 8,
+ opacity: 1,
+ zIndex: 1,
+ },
+ content: {
+ githubData,
+ },
+ }
+
+ switch (type) {
+ case "text":
+ return {
+ ...baseElement,
+ type: "text" as const,
+ size: { width: 120, height: 32 },
+ content: {
+ ...baseElement.content,
+ text: githubData ? "{{username}}" : "Sample Text",
+ },
+ }
+ case "container":
+ return {
+ ...baseElement,
+ type: "container" as const,
+ size: { width: 200, height: 100 },
+ style: {
+ ...baseElement.style,
+ backgroundColor: widget.canvas.theme === "dark" ? "#2D3748" : "#EDF2F7",
+ borderWidth: 1,
+ },
+ }
+ case "shape":
+ return {
+ ...baseElement,
+ type: "shape" as const,
+ size: { width: 60, height: 60 },
+ content: {
+ ...baseElement.content,
+ shapeType: shapeType || "rectangle",
+ },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#3B82F6",
+ },
+ }
+ case "progress":
+ return {
+ ...baseElement,
+ type: "progress" as const,
+ size: { width: 200, height: 20 },
+ content: {
+ ...baseElement.content,
+ progressValue: githubData ? "{{followers}}" : 75,
+ },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#E5E7EB",
+ },
+ }
+ case "badge":
+ return {
+ ...baseElement,
+ type: "badge" as const,
+ size: { width: 80, height: 24 },
+ content: {
+ ...baseElement.content,
+ text: githubData ? "{{public_repos}}" : "Badge",
+ },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#3B82F6",
+ color: "#FFFFFF",
+ borderRadius: 12,
+ },
+ }
+ case "button":
+ return {
+ ...baseElement,
+ type: "button" as const,
+ size: { width: 100, height: 36 },
+ content: {
+ ...baseElement.content,
+ text: "Button",
+ },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#3B82F6",
+ color: "#FFFFFF",
+ borderRadius: 6,
+ },
+ }
+ case "image":
+ return {
+ ...baseElement,
+ type: "image" as const,
+ size: { width: 80, height: 80 },
+ content: {
+ ...baseElement.content,
+ imageUrl: githubData === "avatar" ? "{{avatar_url}}" : "/placeholder.svg?height=80&width=80",
+ },
+ style: {
+ ...baseElement.style,
+ borderRadius: githubData === "avatar" ? 40 : 4,
+ },
+ }
+ case "chart":
+ return {
+ ...baseElement,
+ type: "chart" as const,
+ size: { width: 200, height: 120 },
+ content: {
+ ...baseElement.content,
+ chartType: "bar",
+ text: "GitHub Stats",
+ },
+ style: {
+ ...baseElement.style,
+ backgroundColor: "#1F2937",
+ borderRadius: 8,
+ },
+ }
+ default:
+ return {
+ ...baseElement,
+ type: type as any,
+ size: { width: 100, height: 40 },
+ }
+ }
+ }
+
+ const handleCanvasMouseDown = (e: React.MouseEvent) => {
+ if (isSpacePressed) {
+ setIsPanning(true)
+ setPanStart({ x: e.clientX - canvasOffset.x, y: e.clientY - canvasOffset.y })
+ return
+ }
+
+ if (!e.target || (e.target as HTMLElement).closest("[data-element-id]")) return
+
+ const rect = canvasRef.current?.getBoundingClientRect()
+ if (rect) {
+ const startX = (e.clientX - rect.left - canvasOffset.x) / zoom
+ const startY = (e.clientY - rect.top - canvasOffset.y) / zoom
+ setSelectionBox({
+ start: { x: startX, y: startY },
+ end: { x: startX, y: startY },
+ active: true,
+ })
+ }
+ }
+
+ const handleCanvasMouseMove = useCallback(
+ (e: MouseEvent) => {
+ if (isPanning) {
+ setCanvasOffset({
+ x: e.clientX - panStart.x,
+ y: e.clientY - panStart.y,
+ })
+ return
+ }
+
+ if (selectionBox.active && canvasRef.current) {
+ const rect = canvasRef.current.getBoundingClientRect()
+ const endX = (e.clientX - rect.left - canvasOffset.x) / zoom
+ const endY = (e.clientY - rect.top - canvasOffset.y) / zoom
+ setSelectionBox((prev) => ({
+ ...prev,
+ end: { x: endX, y: endY },
+ }))
+ }
+
+ if (!draggedElement || !canvasRef.current) return
+ const rect = canvasRef.current.getBoundingClientRect()
+ let newX = Math.max(0, (e.clientX - rect.left - canvasOffset.x) / zoom - dragOffset.x)
+ let newY = Math.max(0, (e.clientY - rect.top - canvasOffset.y) / zoom - dragOffset.y)
+
+ if (widget.canvas.snapToGrid) {
+ newX = Math.round(newX / widget.canvas.gridSize) * widget.canvas.gridSize
+ newY = Math.round(newY / widget.canvas.gridSize) * widget.canvas.gridSize
+ }
+
+ onElementUpdate({
+ ...draggedElement,
+ position: { x: newX, y: newY },
+ })
+ },
+ [
+ draggedElement,
+ dragOffset,
+ zoom,
+ onElementUpdate,
+ widget.canvas.snapToGrid,
+ widget.canvas.gridSize,
+ isPanning,
+ panStart,
+ canvasOffset,
+ selectionBox.active,
+ ],
+ )
+
+ const handleCanvasMouseUp = useCallback(() => {
+ if (selectionBox.active) {
+ const minX = Math.min(selectionBox.start.x, selectionBox.end.x)
+ const maxX = Math.max(selectionBox.start.x, selectionBox.end.x)
+ const minY = Math.min(selectionBox.start.y, selectionBox.end.y)
+ const maxY = Math.max(selectionBox.start.y, selectionBox.end.y)
+
+ const selectedInBox = widget.elements.filter((element) => {
+ const elementCenterX = element.position.x + element.size.width / 2
+ const elementCenterY = element.position.y + element.size.height / 2
+ return elementCenterX >= minX && elementCenterX <= maxX && elementCenterY >= minY && elementCenterY <= maxY
+ })
+
+ onElementSelect(selectedInBox)
+ setSelectionBox({ start: { x: 0, y: 0 }, end: { x: 0, y: 0 }, active: false })
+ }
+
+ setDraggedElement(null)
+ setIsPanning(false)
+ }, [selectionBox, widget.elements, onElementSelect])
+
+ useEffect(() => {
+ if (draggedElement || isPanning || selectionBox.active) {
+ document.addEventListener("mousemove", handleCanvasMouseMove)
+ document.addEventListener("mouseup", handleCanvasMouseUp)
+ return () => {
+ document.removeEventListener("mousemove", handleCanvasMouseMove)
+ document.removeEventListener("mouseup", handleCanvasMouseUp)
+ }
+ }
+ }, [draggedElement, isPanning, selectionBox.active, handleCanvasMouseMove, handleCanvasMouseUp])
+
+ const handleElementMouseDown = (element: WidgetElement, e: React.MouseEvent) => {
+ if (element.locked) return
+ e.stopPropagation()
+
+ if (!selectedElements.find((el) => el.id === element.id)) {
+ if (e.ctrlKey || e.metaKey) {
+ onElementSelect([...selectedElements, element])
+ } else {
+ onElementSelect([element])
+ }
+ }
+
+ setDraggedElement(element)
+ const rect = canvasRef.current?.getBoundingClientRect()
+ if (rect) {
+ setDragOffset({
+ x: (e.clientX - rect.left - canvasOffset.x) / zoom - element.position.x,
+ y: (e.clientY - rect.top - canvasOffset.y) / zoom - element.position.y,
+ })
+ }
+ }
+
+ const handleElementDoubleClick = (element: WidgetElement, e: React.MouseEvent) => {
+ e.stopPropagation()
+ if (element.type === "text" || element.type === "badge" || element.type === "button") {
+ setEditingElement(element.id)
+ }
+ }
+
+ const getDisplayValue = (element: WidgetElement, field: string) => {
+ if (githubData && element.content.text?.includes("{{")) {
+ const placeholder = element.content.text
+ switch (placeholder) {
+ case "{{username}}":
+ return githubData.login
+ case "{{name}}":
+ return githubData.name || githubData.login
+ case "{{bio}}":
+ return githubData.bio || "No bio available"
+ case "{{followers}}":
+ return githubData.followers.toString()
+ case "{{following}}":
+ return githubData.following.toString()
+ case "{{public_repos}}":
+ return githubData.public_repos.toString()
+ case "{{created_at}}":
+ return new Date(githubData.created_at).getFullYear().toString()
+ case "{{totalCommitContributions}}":
+ return "1,234" // Mock data for now
+ default:
+ return element.content.text
+ }
+ }
+ return element.content.text
+ }
+
+ const getImageUrl = (element: WidgetElement) => {
+ if (githubData && element.content.imageUrl === "{{avatar_url}}") {
+ return githubData.avatar_url
+ }
+ return element.content.imageUrl
+ }
+
+ const getProgressValue = (element: WidgetElement) => {
+ if (
+ githubData &&
+ typeof element.content.progressValue === "string" &&
+ element.content.progressValue.includes("{{")
+ ) {
+ const placeholder = element.content.progressValue
+ switch (placeholder) {
+ case "{{followers}}":
+ return Math.min(100, (githubData.followers / 100) * 100)
+ case "{{public_repos}}":
+ return Math.min(100, (githubData.public_repos / 50) * 100)
+ default:
+ return 75
+ }
+ }
+ return typeof element.content.progressValue === "number" ? element.content.progressValue : 75
+ }
+
+ const renderGrid = () => {
+ if (!widget.canvas.showGrid) return null
+
+ const gridLines = []
+ const gridSize = widget.canvas.gridSize * zoom
+ const canvasWidth = widget.canvas.width * zoom
+ const canvasHeight = widget.canvas.height * zoom
+
+ for (let x = 0; x <= canvasWidth; x += gridSize) {
+ gridLines.push(
+ ,
+ )
+ }
+
+ for (let y = 0; y <= canvasHeight; y += gridSize) {
+ gridLines.push(
+ ,
+ )
+ }
+
+ return (
+
+ {gridLines}
+
+ )
+ }
+
+ const renderSelectionBox = () => {
+ if (!selectionBox.active) return null
+
+ const minX = Math.min(selectionBox.start.x, selectionBox.end.x)
+ const maxX = Math.max(selectionBox.start.x, selectionBox.end.x)
+ const minY = Math.min(selectionBox.start.y, selectionBox.end.y)
+ const maxY = Math.max(selectionBox.start.y, selectionBox.end.y)
+
+ return (
+
+ )
+ }
+
+ const renderElement = (element: WidgetElement) => {
+ if (!element.visible) return null
+
+ const isSelected = selectedElements.some((el) => el.id === element.id)
+ const isEditing = editingElement === element.id
+ const transform = `rotate(${element.rotation}deg)`
+
+ const elementProps = {
+ "data-element-id": element.id,
+ className: `
+ absolute cursor-pointer transition-all duration-200 select-none
+ ${
+ isSelected
+ ? "ring-2 ring-blue-500 ring-offset-2 ring-offset-transparent shadow-2xl shadow-blue-500/20"
+ : "hover:ring-1 hover:ring-blue-300/30"
+ }
+ ${element.locked ? "cursor-not-allowed opacity-60" : "hover:shadow-lg"}
+ `,
+ style: {
+ left: element.position.x,
+ top: element.position.y,
+ width: element.size.width,
+ height: element.size.height,
+ transform,
+ opacity: element.style.opacity,
+ zIndex: element.style.zIndex || 1,
+ ...element.style,
+ },
+ onMouseDown: (e: React.MouseEvent) => handleElementMouseDown(element, e),
+ onDoubleClick: (e: React.MouseEvent) => handleElementDoubleClick(element, e),
+ }
+
+ switch (element.type) {
+ case "text":
+ return (
+
+ {isEditing ? (
+
setEditingElement(null)}
+ />
+ ) : (
+
+ {getDisplayValue(element, "text")}
+
+ )}
+
+ )
+ case "image":
+ return (
+
+
+
+ )
+ case "container":
+ return (
+
+ )
+ case "shape":
+ return (
+
+ {element.content.shapeType === "circle" && (
+
+ )}
+ {element.content.shapeType === "rectangle" && (
+
+ )}
+ {element.content.shapeType === "triangle" && (
+
+ )}
+
+ )
+ case "progress":
+ return (
+
+ )
+ case "badge":
+ return (
+
+ {isEditing ? (
+
setEditingElement(null)}
+ />
+ ) : (
+
+ {getDisplayValue(element, "text")}
+
+ )}
+
+ )
+ case "button":
+ return (
+
+ {isEditing ? (
+
setEditingElement(null)}
+ />
+ ) : (
+
+ {getDisplayValue(element, "text")}
+
+ )}
+
+ )
+ case "chart":
+ return (
+
+
+
+
+
GitHub Stats
+ {githubData && (
+
+
Repos: {githubData.public_repos}
+
Followers: {githubData.followers}
+
+ )}
+
+
+
+ )
+ default:
+ return
+ }
+ }
+
+ return (
+
+ {/* Premium Floating Toolbar */}
+
+
+
+
+ onWidgetUpdate({ ...widget, canvas: { ...widget.canvas, showGrid: !widget.canvas.showGrid } })
+ }
+ className={`text-white hover:bg-white/10 rounded-xl transition-all duration-200 ${widget.canvas.showGrid ? "bg-white/20" : ""}`}
+ >
+
+
+
+ onWidgetUpdate({ ...widget, canvas: { ...widget.canvas, snapToGrid: !widget.canvas.snapToGrid } })
+ }
+ className={`text-white hover:bg-white/10 rounded-xl transition-all duration-200 ${widget.canvas.snapToGrid ? "bg-white/20" : ""}`}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
onZoomChange(Math.max(0.1, zoom - 0.25))}
+ className="text-white hover:bg-white/10 rounded-xl"
+ >
+
+
+
+ {Math.round(zoom * 100)}%
+
+
onZoomChange(Math.min(5, zoom + 0.25))}
+ className="text-white hover:bg-white/10 rounded-xl"
+ >
+
+
+
onZoomChange(1)}
+ className="text-white hover:bg-white/10 rounded-xl"
+ >
+
+
+
+
+
+
+ {/* Canvas */}
+
+
+
+ {renderGrid()}
+ {widget.elements.sort((a, b) => (a.style.zIndex || 1) - (b.style.zIndex || 1)).map(renderElement)}
+ {renderSelectionBox()}
+
+
+
+
+ {/* Premium Help Panel */}
+
+
+
+ Space
+ + drag to pan canvas
+
+
+ ⌘
+ + wheel to zoom
+
+
+ ⌘C/V
+ copy/paste elements
+
+
+ Del
+ delete selected
+
+
+
+
+ )
+}
+
+// Premium Properties Panel with Notion-inspired design
+function PremiumPropertiesPanel({
+ selectedElements,
+ onElementUpdate,
+ onElementDelete,
+ widget,
+ isVisible,
+ onToggleVisibility,
+ onWidgetUpdate,
+ githubData,
+ onFetchGitHubData,
+}: {
+ selectedElements: WidgetElement[]
+ onElementUpdate: (element: WidgetElement) => void
+ onElementDelete: (elementId: string) => void
+ widget: Widget
+ isVisible: boolean
+ onToggleVisibility: () => void
+ onWidgetUpdate: (widget: Widget) => void
+ githubData: GitHubUserData | null
+ onFetchGitHubData: (username: string) => void
+}) {
+ const [panelPosition, setPanelPosition] = useState({ x: 0, y: 0 })
+ const [isDragging, setIsDragging] = useState(false)
+ const [dragOffset, setDragOffset] = useState({ x: 0, y: 0 })
+ const panelRef = useRef(null)
+
+ const selectedElement = selectedElements[0]
+
+ const handleMouseDown = (e: React.MouseEvent) => {
+ if (panelRef.current) {
+ const rect = panelRef.current.getBoundingClientRect()
+ setDragOffset({
+ x: e.clientX - rect.left,
+ y: e.clientY - rect.top,
+ })
+ setIsDragging(true)
+ }
+ }
+
+ const handleMouseMove = useCallback(
+ (e: MouseEvent) => {
+ if (!isDragging) return
+ const newX = e.clientX - dragOffset.x
+ const newY = e.clientY - dragOffset.y
+ const maxX = window.innerWidth - 500
+ const maxY = window.innerHeight - 400
+ setPanelPosition({
+ x: Math.max(0, Math.min(newX, maxX)),
+ y: Math.max(0, Math.min(newY, maxY)),
+ })
+ },
+ [isDragging, dragOffset],
+ )
+
+ const handleMouseUp = useCallback(() => {
+ setIsDragging(false)
+ }, [])
+
+ useEffect(() => {
+ if (isDragging) {
+ document.addEventListener("mousemove", handleMouseMove)
+ document.addEventListener("mouseup", handleMouseUp)
+ return () => {
+ document.removeEventListener("mousemove", handleMouseMove)
+ document.removeEventListener("mouseup", handleMouseUp)
+ }
+ }
+ }, [isDragging, handleMouseMove, handleMouseUp])
+
+ if (!selectedElement) return null
+
+ const updateElement = (updates: Partial) => {
+ onElementUpdate({ ...selectedElement, ...updates })
+ }
+
+ const updateStyle = (styleUpdates: Partial) => {
+ updateElement({
+ style: { ...selectedElement.style, ...styleUpdates },
+ })
+ }
+
+ const updateContent = (contentUpdates: Partial) => {
+ updateElement({
+ content: { ...selectedElement.content, ...contentUpdates },
+ })
+ }
+
+ return (
+ <>
+ {/* Premium Toggle Button */}
+
+
+ {isVisible ? : }
+
+
+
+ {/* Premium Properties Panel */}
+ {isVisible && (
+
+
+
+
+
+
+
+
+
+
+ {selectedElements.length > 1
+ ? `${selectedElements.length} Elements Selected`
+ : `${selectedElement.type.charAt(0).toUpperCase() + selectedElement.type.slice(1)} Properties`}
+
+
Customize your element
+
+
+
+ updateElement({ visible: !selectedElement.visible })}
+ className="text-white/70 hover:text-white hover:bg-white/10 rounded-xl"
+ >
+ {selectedElement.visible ? : }
+
+ updateElement({ locked: !selectedElement.locked })}
+ className="text-white/70 hover:text-white hover:bg-white/10 rounded-xl"
+ >
+ {selectedElement.locked ? : }
+
+ onElementDelete(selectedElement.id)}
+ className="text-red-400 hover:text-red-300 hover:bg-red-500/10 rounded-xl"
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Transform
+
+
+
+ Style
+
+
+
+ Content
+
+
+
+ GitHub
+
+
+
+
+
+
+
Rotation
+
+
updateElement({ rotation: value })}
+ min={-180}
+ max={180}
+ step={1}
+ className="[&_[role=slider]]:bg-blue-500 [&_[role=slider]]:border-blue-400 [&_[role=slider]]:w-5 [&_[role=slider]]:h-5"
+ />
+
+ -180°
+ {selectedElement.rotation}°
+ 180°
+
+
+
+
+ Layer Order
+ updateStyle({ zIndex: Number(e.target.value) || 1 })}
+ className="bg-black/20 border-white/20 text-white h-10 rounded-xl mt-2"
+ min="1"
+ max="100"
+ />
+
+
+
+
+
+
+
Border Radius
+
+
updateStyle({ borderRadius: value })}
+ min={0}
+ max={50}
+ step={1}
+ className="[&_[role=slider]]:bg-blue-500 [&_[role=slider]]:border-blue-400 [&_[role=slider]]:w-5 [&_[role=slider]]:h-5"
+ />
+
+ 0px
+ {selectedElement.style.borderRadius || 0}px
+ 50px
+
+
+
+
+
Opacity
+
+
updateStyle({ opacity: value })}
+ min={0}
+ max={1}
+ step={0.1}
+ className="[&_[role=slider]]:bg-blue-500 [&_[role=slider]]:border-blue-400 [&_[role=slider]]:w-5 [&_[role=slider]]:h-5"
+ />
+
+ 0%
+
+ {Math.round((selectedElement.style.opacity || 1) * 100)}%
+
+ 100%
+
+
+
+ {(selectedElement.type === "text" ||
+ selectedElement.type === "badge" ||
+ selectedElement.type === "button") && (
+
+
Font Size
+
+
updateStyle({ fontSize: value })}
+ min={8}
+ max={72}
+ step={1}
+ className="[&_[role=slider]]:bg-blue-500 [&_[role=slider]]:border-blue-400 [&_[role=slider]]:w-5 [&_[role=slider]]:h-5"
+ />
+
+ 8px
+ {selectedElement.style.fontSize || 14}px
+ 72px
+
+
+
+ )}
+
+
+
+ {(selectedElement.type === "text" ||
+ selectedElement.type === "badge" ||
+ selectedElement.type === "button") && (
+
+
Text Content
+
updateContent({ text: e.target.value })}
+ placeholder="Enter text..."
+ className="bg-black/20 border-white/20 text-white h-10 rounded-xl mt-2"
+ />
+
💡 Tip: Double-click element to edit inline
+
+ )}
+ {selectedElement.type === "image" && (
+
+ Image URL
+ updateContent({ imageUrl: e.target.value })}
+ placeholder="Enter image URL or use {{avatar_url}}"
+ className="bg-black/20 border-white/20 text-white h-10 rounded-xl mt-2"
+ />
+
+ )}
+ {selectedElement.type === "progress" && (
+
+ Progress Value
+
+ updateContent({
+ progressValue: e.target.value.includes("{{") ? e.target.value : Number(e.target.value),
+ })
+ }
+ placeholder="Enter value or use {{followers}}"
+ className="bg-black/20 border-white/20 text-white h-10 rounded-xl mt-2"
+ />
+
+ )}
+
+
+
+
+
+ GitHub Integration
+ {githubData && (
+
+ ✓ Connected: {githubData.login}
+
+ )}
+
+
+ onWidgetUpdate({ ...widget, githubUsername: e.target.value })}
+ placeholder="Enter GitHub username"
+ className="bg-black/20 border-white/20 text-white h-10 rounded-xl"
+ />
+ widget.githubUsername && onFetchGitHubData(widget.githubUsername)}
+ disabled={!widget.githubUsername}
+ className="bg-blue-600 hover:bg-blue-700 text-white px-4 rounded-xl"
+ >
+
+
+
+ {githubData && (
+
+
+
+ Name:
+ {githubData.name || "N/A"}
+
+
+ Username:
+ {githubData.login}
+
+
+ Followers:
+ {githubData.followers}
+
+
+ Following:
+ {githubData.following}
+
+
+ Repos:
+ {githubData.public_repos}
+
+
+ Joined:
+
+ {new Date(githubData.created_at).getFullYear()}
+
+
+
+
+ )}
+
+
GitHub Variables
+
{
+ const variable = GITHUB_VARIABLES.find((v) => v.name === value)
+ if (variable) {
+ if (selectedElement.type === "image" && value === "avatar_url") {
+ updateContent({ imageUrl: variable.placeholder })
+ } else if (
+ selectedElement.type === "progress" &&
+ (value === "followers" || value === "public_repos")
+ ) {
+ updateContent({ progressValue: variable.placeholder })
+ } else {
+ updateContent({ text: variable.placeholder, variable: value })
+ }
+ }
+ }}
+ >
+
+
+
+
+ {GITHUB_VARIABLES.map((variable) => (
+
+
+
{variable.name}
+
{variable.description}
+
+
+ ))}
+
+
+
+
+
+
+
+
+
+ )}
+ >
+ )
+}
+
+// Enhanced SVG Generator with proper content extraction
+function generatePremiumSVG(widget: Widget): string {
+ let svgContent = ""
+
+ // Sort elements by z-index and render
+ widget.elements
+ .filter((element) => element.visible)
+ .sort((a, b) => (a.style.zIndex || 1) - (b.style.zIndex || 1))
+ .forEach((element) => {
+ const transform =
+ element.rotation !== 0
+ ? ` transform="rotate(${element.rotation} ${element.position.x + element.size.width / 2} ${element.position.y + element.size.height / 2})"`
+ : ""
+
+ const opacity = element.style.opacity !== 1 ? ` opacity="${element.style.opacity || 1}"` : ""
+
+ if (element.type === "text" || element.type === "badge" || element.type === "button") {
+ const textContent = element.content.text || ""
+ svgContent += `${textContent} `
+ }
+ })
+
+ return svgContent
+}
+
+// History management for undo/redo
+function useHistory(initialState: T) {
+ const [history, setHistory] = useState([initialState])
+ const [currentIndex, setCurrentIndex] = useState(0)
+
+ const pushState = useCallback(
+ (newState: T) => {
+ setHistory((prev) => {
+ const newHistory = prev.slice(0, currentIndex + 1)
+ newHistory.push(newState)
+ return newHistory.slice(-50)
+ })
+ setCurrentIndex((prev) => Math.min(prev + 1, 49))
+ },
+ [currentIndex],
+ )
+
+ const undo = useCallback(() => {
+ if (currentIndex > 0) {
+ setCurrentIndex((prev) => prev - 1)
+ return history[currentIndex - 1]
+ }
+ return null
+ }, [currentIndex, history])
+
+ const redo = useCallback(() => {
+ if (currentIndex < history.length - 1) {
+ setCurrentIndex((prev) => prev + 1)
+ return history[currentIndex + 1]
+ }
+ return null
+ }, [currentIndex, history])
+
+ const canUndo = currentIndex > 0
+ const canRedo = currentIndex < history.length - 1
+
+ return { pushState, undo, redo, canUndo, canRedo, currentState: history[currentIndex] }
+}
+
+// Cookie utility
+const getCookie = (name: string): string | null => {
+ if (typeof window === "undefined") return null
+ try {
+ const nameEQ = name + "="
+ const ca = document.cookie.split(";")
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i]
+ while (c.charAt(0) === " ") c = c.substring(1, c.length)
+ if (c.indexOf(nameEQ) === 0) {
+ return c.substring(nameEQ.length, c.length)
+ }
+ }
+ return null
+ } catch (error) {
+ console.error(`Failed to retrieve cookie ${name}:`, error)
+ return null
+ }
+}
+
+// Main Premium Widget Builder Component
+export default function PremiumWidgetBuilder() {
+ const [showInitialModal, setShowInitialModal] = useState(true)
+ const [showSaveModal, setShowSaveModal] = useState(false)
+ const [selectedCanvasSize, setSelectedCanvasSize] = useState<(typeof CANVAS_SIZES)[0]>(CANVAS_SIZES[1])
+ const [customWidth, setCustomWidth] = useState(400)
+ const [customHeight, setCustomHeight] = useState(200)
+ const [expandedCategories, setExpandedCategories] = useState(["Essentials"])
+ const [selectedElements, setSelectedElements] = useState([])
+ const [zoom, setZoom] = useState(1)
+ const [showPropertiesPanel, setShowPropertiesPanel] = useState(true)
+ const [githubData, setGithubData] = useState(null)
+ const [isSaving, setIsSaving] = useState(false)
+ const [svgContent, setSvgContent] = useState("")
+
+ const [currentWidget, setCurrentWidget] = useState({
+ name: "My Widget",
+ elements: [],
+ canvas: {
+ width: 400,
+ height: 200,
+ backgroundColor: "#0F0C14",
+ theme: "dark",
+ showGrid: true,
+ gridSize: 10,
+ snapToGrid: false,
+ },
+ isPrivate: false,
+ tags: [],
+ githubUsername: "",
+ })
+
+ const { pushState, undo, redo, canUndo, canRedo } = useHistory({
+ elements: currentWidget.elements,
+ canvas: currentWidget.canvas,
+ })
+
+ const { user } = useAuth()
+ const router = useRouter()
+
+ // Update SVG content in real-time
+ useEffect(() => {
+ setSvgContent(generatePremiumSVG(currentWidget))
+ }, [currentWidget])
+
+ // Save state to history when elements change
+ useEffect(() => {
+ pushState({
+ elements: currentWidget.elements,
+ canvas: currentWidget.canvas,
+ })
+ }, [currentWidget.elements, currentWidget.canvas, pushState])
+
+ const handleUndo = () => {
+ const prevState = undo()
+ if (prevState) {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ elements: prevState.elements,
+ canvas: prevState.canvas,
+ }))
+ }
+ }
+
+ const handleRedo = () => {
+ const nextState = redo()
+ if (nextState) {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ elements: nextState.elements,
+ canvas: nextState.canvas,
+ }))
+ }
+ }
+
+ const handleFetchGitHubData = async (username: string) => {
+ try {
+ const data = await fetchGitHubData(username)
+ if (data) {
+ setGithubData(data)
+ toast.success(`🎉 GitHub data loaded for ${username}`, {
+ description: `Found ${data.public_repos} repos and ${data.followers} followers`,
+ })
+ } else {
+ toast.error("❌ Failed to fetch GitHub data")
+ }
+ } catch (error) {
+ toast.error("❌ Error fetching GitHub data")
+ }
+ }
+
+ const handleCanvasSizeSelect = (size: (typeof CANVAS_SIZES)[0]) => {
+ setSelectedCanvasSize(size)
+ if (size.name !== "Custom") {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, width: size.width, height: size.height },
+ }))
+ }
+ setShowInitialModal(false)
+ }
+
+ const handleCustomSizeApply = () => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ canvas: { ...prev.canvas, width: customWidth, height: customHeight },
+ }))
+ setShowInitialModal(false)
+ }
+
+ const toggleCategory = (categoryName: string) => {
+ setExpandedCategories((prev) =>
+ prev.includes(categoryName) ? prev.filter((c) => c !== categoryName) : [...prev, categoryName],
+ )
+ }
+
+ const addElement = (element: WidgetElement) => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ elements: [...prev.elements, element],
+ }))
+ setSelectedElements([element])
+ toast.success(`✨ Added ${element.type} element`, {
+ description: "Double-click to edit inline",
+ })
+ }
+
+ const updateElement = (updatedElement: WidgetElement) => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ elements: prev.elements.map((el) => (el.id === updatedElement.id ? updatedElement : el)),
+ }))
+ setSelectedElements((prev) => prev.map((el) => (el.id === updatedElement.id ? updatedElement : el)))
+ }
+
+ const deleteElement = (elementId: string) => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ elements: prev.elements.filter((el) => el.id !== elementId),
+ }))
+ setSelectedElements((prev) => prev.filter((el) => el.id !== elementId))
+ toast.success("🗑️ Element deleted")
+ }
+
+ const toggleCanvasTheme = () => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ canvas: {
+ ...prev.canvas,
+ theme: prev.canvas.theme === "dark" ? "light" : "dark",
+ backgroundColor: prev.canvas.theme === "dark" ? "#FFFFFF" : "#0F0C14",
+ },
+ }))
+ }
+
+ const handleElementClick = (elementType: any) => {
+ const centerX = currentWidget.canvas.width / 2 - 50
+ const centerY = currentWidget.canvas.height / 2 - 25
+ const newElement: WidgetElement = {
+ id: `element-${Date.now()}`,
+ type: elementType.type,
+ position: { x: centerX, y: centerY },
+ size: { width: 100, height: 40 },
+ rotation: 0,
+ style: {
+ backgroundColor: currentWidget.canvas.theme === "dark" ? "#1A1625" : "#F8F9FA",
+ color: currentWidget.canvas.theme === "dark" ? "#E2E8F0" : "#1A202C",
+ fontSize: 14,
+ fontWeight: "normal",
+ fontFamily: "Inter",
+ borderRadius: 4,
+ borderWidth: 0,
+ borderColor: "#E2E8F0",
+ borderStyle: "solid",
+ padding: 8,
+ opacity: 1,
+ zIndex: 1,
+ },
+ content: {
+ text:
+ elementType.type === "text"
+ ? elementType.githubData
+ ? "{{username}}"
+ : "Sample Text"
+ : elementType.type === "badge"
+ ? elementType.githubData
+ ? "{{public_repos}}"
+ : "Badge"
+ : elementType.type === "button"
+ ? "Button"
+ : undefined,
+ shapeType: elementType.shapeType,
+ githubData: elementType.githubData,
+ imageUrl: elementType.githubData === "avatar" ? "{{avatar_url}}" : undefined,
+ },
+ visible: true,
+ locked: false,
+ }
+ addElement(newElement)
+ }
+
+ const handleSave = async () => {
+ if (!currentWidget.name.trim() || currentWidget.elements.length === 0) {
+ toast.error("❌ Please provide a name and add elements to your widget")
+ return
+ }
+
+ setIsSaving(true)
+ try {
+ const accessToken = getCookie("devboard_access_token")
+ if (!accessToken) {
+ toast.error("🔐 Please log in to save widgets")
+ return
+ }
+
+ // Format data according to backend expectations
+ const saveData = {
+ name: currentWidget.name,
+ content: svgContent, // Just the SVG content, not full SVG
+ size: {
+ width: currentWidget.canvas.width,
+ height: currentWidget.canvas.height,
+ },
+ isPrivate: currentWidget.isPrivate,
+ Tags: currentWidget.tags, // Capital T as expected by backend
+ }
+
+ const response = await fetch(`${process.env.NEXT_PUBLIC_API_BASE_URL}/api/widget`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${accessToken}`,
+ },
+ body: JSON.stringify(saveData),
+ })
+
+ if (response.ok) {
+ const savedWidget = await response.json()
+ setCurrentWidget((prev) => ({ ...prev, id: savedWidget.id }))
+ toast.success("🎉 Widget saved successfully!", {
+ description: "Your widget is now available in your dashboard",
+ })
+ } else {
+ const errorText = await response.text()
+ console.error("Save error response:", errorText)
+ try {
+ const error = JSON.parse(errorText)
+ toast.error(`❌ ${error.message || "Failed to save widget"}`)
+ } catch {
+ toast.error("❌ Failed to save widget - Invalid response format")
+ }
+ }
+ } catch (error) {
+ console.error("Save error:", error)
+ toast.error("❌ Failed to save widget - Network error")
+ } finally {
+ setIsSaving(false)
+ setShowSaveModal(false)
+ }
+ }
+
+ const copyWidgetCode = () => {
+ const fullSVG = `
+
+ ${svgContent}
+ `
+ navigator.clipboard.writeText(fullSVG)
+ toast.success("📋 SVG code copied to clipboard!", {
+ description: "Ready to use in your projects",
+ })
+ }
+
+ const addTag = (tag: string) => {
+ if (tag && !currentWidget.tags.includes(tag)) {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ tags: [...prev.tags, tag],
+ }))
+ }
+ }
+
+ const removeTag = (tagToRemove: string) => {
+ setCurrentWidget((prev) => ({
+ ...prev,
+ tags: prev.tags.filter((tag) => tag !== tagToRemove),
+ }))
+ }
+
+ return (
+
+
+ {/* Premium Initial Canvas Size Modal */}
+
+
+
+
+ ✨ Create Your Perfect Widget
+
+
+ Choose the ideal canvas size for your GitHub widget masterpiece
+
+
+
+ {CANVAS_SIZES.map((size) => (
+
setSelectedCanvasSize(size)}
+ >
+
+
+
{size.icon}
+
+
{size.name}
+
+ {size.width} × {size.height}
+
+
{size.description}
+
+
+
+ ))}
+
+ {selectedCanvasSize.name === "Custom" && (
+
+
✨ Custom Dimensions
+
+
+ )}
+
+
router.back()}
+ className="bg-black/20 border-white/20 hover:bg-black/40 text-white px-8 py-3 text-lg rounded-xl"
+ >
+
+ Back
+
+
handleCanvasSizeSelect(selectedCanvasSize)
+ }
+ className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white px-12 py-3 text-lg rounded-xl shadow-2xl"
+ >
+
+ Create Widget
+
+
+
+
+
+ {/* Premium Save Modal */}
+
+
+
+ 💾 Save Your Widget
+
+ Configure your widget settings before saving
+
+
+
+
+ Widget Name
+ setCurrentWidget((prev) => ({ ...prev, name: e.target.value }))}
+ className="bg-black/20 border-white/20 text-white mt-3 h-12 text-lg rounded-xl"
+ placeholder="Enter widget name"
+ />
+
+
+
+
🔒 Make Private
+
Only you can see and use this widget
+
+
setCurrentWidget((prev) => ({ ...prev, isPrivate: checked }))}
+ className="data-[state=checked]:bg-blue-600 scale-125"
+ />
+
+
+
🏷️ Tags
+
+ {currentWidget.tags.map((tag) => (
+ removeTag(tag)}
+ >
+ {tag} ×
+
+ ))}
+
+
+
+
+
+
+ 📊 Stats
+ 👥 Social
+ 📈 Contributions
+ 📚 Repositories
+ 👤 Profile
+ ❤️ Followers
+ 💻 Commits
+ ⚡ Activity
+
+
+
+
+
+ setShowSaveModal(false)}
+ className="bg-black/20 border-white/20 hover:bg-black/40 text-white px-6 py-3 rounded-xl"
+ >
+ Cancel
+
+
+ {isSaving ? (
+ <>
+
+ Saving...
+ >
+ ) : (
+ <>
+
+ Save Widget
+ >
+ )}
+
+
+
+
+
+ {/* Premium Main Interface */}
+
+ {/* Premium Left Sidebar */}
+
+
+
+
🎨 Elements
+
+
+ {currentWidget.canvas.theme === "dark" ? : }
+
+
+
+
+
+ {ELEMENT_CATEGORIES.map((category) => (
+
+
toggleCategory(category.name)}
+ className="w-full flex items-center justify-between p-4 hover:bg-white/5 rounded-xl transition-all duration-300"
+ >
+
+ {category.icon}
+ {category.name}
+
+
+
+ {expandedCategories.includes(category.name) && (
+
+ {category.elements.map((elementType) => (
+
+
+
handleElementClick(elementType)}
+ className="text-white/70 hover:text-white hover:bg-white/10 px-3 rounded-xl"
+ >
+
+
+
+ ))}
+
+ )}
+
+ ))}
+
+
+
+
+
+
📚 Layers
+
+ {currentWidget.elements.length} element{currentWidget.elements.length !== 1 ? "s" : ""}
+
+
+
+
+ {currentWidget.elements.map((element, index) => (
+
el.id === element.id)
+ ? "bg-blue-600/20 border border-blue-400/30 shadow-2xl shadow-blue-500/20"
+ : "hover:bg-white/5"
+ }`}
+ onClick={() => setSelectedElements([element])}
+ >
+
+
+
+
+ {element.type.charAt(0).toUpperCase() + element.type.slice(1)} {index + 1}
+
+ {element.content.text && (
+
+ {element.content.text}
+
+ )}
+
+
+
+ {
+ e.stopPropagation()
+ updateElement({ ...element, visible: !element.visible })
+ }}
+ className="text-white/50 hover:text-white p-2 h-8 w-8 rounded-lg"
+ >
+ {element.visible ? : }
+
+ {
+ e.stopPropagation()
+ updateElement({ ...element, locked: !element.locked })
+ }}
+ className="text-white/50 hover:text-white p-2 h-8 w-8 rounded-lg"
+ >
+ {element.locked ? : }
+
+
+
+ ))}
+
+
+
+
+
+ {/* Premium Main Canvas Area */}
+
+ {/* Premium Header */}
+
+
+
router.back()}
+ className="bg-black/20 border-white/20 hover:bg-black/40 text-white rounded-xl"
+ >
+
+ Back
+
+
+ ✨ Premium Widget Builder
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Copy SVG
+
+ setShowSaveModal(true)}
+ className="bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700 text-white rounded-xl shadow-2xl"
+ >
+
+ Save Widget
+
+
+
+
+ {/* Premium Canvas */}
+
+
+
+ {/* Premium Right Sidebar - SVG Code */}
+
+
+
+
💻 SVG Output
+
+
+
+
+
Live preview • Auto-generated • Production ready
+
+
+
+
+ {`
+
+ ${svgContent}
+ `}
+
+
+
+
+
+
+ {/* Premium Floating Properties Panel */}
+ setShowPropertiesPanel(!showPropertiesPanel)}
+ onWidgetUpdate={setCurrentWidget}
+ githubData={githubData}
+ onFetchGitHubData={handleFetchGitHubData}
+ />
+
+
+ )
+}
diff --git a/devboard/lib/auth-context.tsx b/devboard/lib/auth-context.tsx
new file mode 100644
index 0000000..d037d43
--- /dev/null
+++ b/devboard/lib/auth-context.tsx
@@ -0,0 +1,419 @@
+"use client"
+
+import type React from "react"
+import { createContext, useContext, useEffect, useState, useCallback } from "react"
+
+// Types
+export interface User {
+ username: string
+ avatar_url?: string
+ name?: string
+ email?: string
+ exp: number
+ iat: number
+}
+
+export interface AuthState {
+ user: User | null
+ isAuthenticated: boolean
+ isLoading: boolean
+ error: string | null
+}
+
+export interface AuthContextType extends AuthState {
+ login: (accessToken: string, refreshToken: string) => Promise
+ logout: () => Promise
+ refreshToken: () => Promise
+ clearError: () => void
+}
+
+// Constants
+const API_BASE_URL = process.env.NEXT_PUBLIC_API_BASE_URL || "https://devboard.varshith.tech"
+
+// Context
+const AuthContext = createContext(undefined)
+
+// Cookie utilities
+const setCookie = (name: string, value: string, days = 7): boolean => {
+ if (typeof window === "undefined") return false
+
+ try {
+ const expires = new Date()
+ expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000)
+ document.cookie = `${name}=${value};expires=${expires.toUTCString()};path=/;secure;samesite=strict`
+ console.log(`Cookie stored: ${name}`)
+ return true
+ } catch (error) {
+ console.error(`Failed to store cookie ${name}:`, error)
+ return false
+ }
+}
+
+const getCookie = (name: string): string | null => {
+ if (typeof window === "undefined") return null
+
+ try {
+ const nameEQ = name + "="
+ const ca = document.cookie.split(";")
+ for (let i = 0; i < ca.length; i++) {
+ let c = ca[i]
+ while (c.charAt(0) === " ") c = c.substring(1, c.length)
+ if (c.indexOf(nameEQ) === 0) {
+ const value = c.substring(nameEQ.length, c.length)
+ console.log(`Cookie retrieved: ${name} = ${value ? "EXISTS" : "NULL"}`)
+ return value
+ }
+ }
+ console.log(`Cookie retrieved: ${name} = NULL`)
+ return null
+ } catch (error) {
+ console.error(`Failed to retrieve cookie ${name}:`, error)
+ return null
+ }
+}
+
+const clearCookies = (): void => {
+ if (typeof window === "undefined") return
+
+ try {
+ document.cookie = "devboard_access_token=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;secure;samesite=strict"
+ document.cookie = "devboard_refresh_token=;expires=Thu, 01 Jan 1970 00:00:00 UTC;path=/;secure;samesite=strict"
+ console.log("All cookies cleared")
+ } catch (error) {
+ console.error("Failed to clear cookies:", error)
+ }
+}
+
+// JWT utilities
+const parseJWT = (token: string): any => {
+ try {
+ const base64Url = token.split(".")[1]
+ const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/")
+ const jsonPayload = decodeURIComponent(
+ atob(base64)
+ .split("")
+ .map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
+ .join(""),
+ )
+ return JSON.parse(jsonPayload)
+ } catch (error) {
+ console.error("Error parsing JWT:", error)
+ return null
+ }
+}
+
+const isTokenExpired = (token: string): boolean => {
+ const payload = parseJWT(token)
+ if (!payload || !payload.exp) return true
+
+ const currentTime = Math.floor(Date.now() / 1000)
+ const bufferTime = 60
+ return payload.exp <= currentTime + bufferTime
+}
+
+// Auth Provider Component
+export function AuthProvider({ children }: { children: React.ReactNode }) {
+ const [state, setState] = useState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: true,
+ error: null,
+ })
+
+ // Initialize auth state
+ const initializeAuth = useCallback(async () => {
+ console.log("Initializing auth...")
+
+ try {
+ const accessToken = getCookie("devboard_access_token")
+ const refreshToken = getCookie("devboard_refresh_token")
+
+ console.log("Token check:", {
+ hasAccessToken: !!accessToken,
+ hasRefreshToken: !!refreshToken,
+ })
+
+ if (!accessToken || !refreshToken) {
+ console.log("No tokens found, setting unauthenticated")
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ })
+ return
+ }
+
+ // Check if access token is expired
+ if (isTokenExpired(accessToken)) {
+ console.log("Access token expired, attempting refresh...")
+ const refreshSuccess = await performTokenRefresh()
+ if (!refreshSuccess) {
+ console.log("Token refresh failed")
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ })
+ return
+ }
+ }
+
+ // Parse user data from current token
+ const currentToken = getCookie("devboard_access_token")
+ if (currentToken) {
+ const userData = parseJWT(currentToken)
+ console.log("Parsed user data:", userData)
+
+ if (userData) {
+ const user: User = {
+ username: userData.username,
+ avatar_url: userData.avatar_url,
+ name: userData.name,
+ email: userData.email,
+ exp: userData.exp,
+ iat: userData.iat,
+ }
+
+ console.log("Setting authenticated user:", user.username)
+ setState({
+ user,
+ isAuthenticated: true,
+ isLoading: false,
+ error: null,
+ })
+ } else {
+ console.log("Failed to parse user data")
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ })
+ }
+ }
+ } catch (error) {
+ console.error("Auth initialization error:", error)
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: "Failed to initialize authentication",
+ })
+ }
+ }, [])
+
+ // Login function
+ const login = useCallback(async (accessToken: string, refreshToken: string): Promise => {
+ console.log("LOGIN FUNCTION CALLED")
+ console.log("Received tokens:", {
+ accessToken: accessToken ? `${accessToken.substring(0, 20)}...` : "NULL",
+ refreshToken: refreshToken ? `${refreshToken.substring(0, 20)}...` : "NULL",
+ })
+
+ try {
+ // Store tokens in cookies
+ const accessSuccess = setCookie("devboard_access_token", accessToken, 1) // 1 day
+ const refreshSuccess = setCookie("devboard_refresh_token", refreshToken, 7) // 7 days
+
+ if (!accessSuccess || !refreshSuccess) {
+ throw new Error("Failed to store tokens")
+ }
+
+ console.log("Tokens stored successfully")
+
+ // Parse user data
+ const userData = parseJWT(accessToken)
+ console.log("Parsing user data:", userData)
+
+ if (!userData) {
+ throw new Error("Invalid access token")
+ }
+
+ const user: User = {
+ username: userData.username,
+ avatar_url: userData.avatar_url,
+ name: userData.name,
+ email: userData.email,
+ exp: userData.exp,
+ iat: userData.iat,
+ }
+
+ console.log("Setting user state:", user.username)
+
+ // Update state immediately
+ setState({
+ user,
+ isAuthenticated: true,
+ isLoading: false,
+ error: null,
+ })
+
+ console.log("Login completed successfully!")
+ } catch (error) {
+ console.error("Login error:", error)
+ setState((prev) => ({
+ ...prev,
+ error: error instanceof Error ? error.message : "Login failed",
+ isLoading: false,
+ }))
+ throw error
+ }
+ }, [])
+
+ // Token refresh function
+ const performTokenRefresh = useCallback(async (): Promise => {
+ try {
+ const refreshToken = getCookie("devboard_refresh_token")
+ if (!refreshToken) {
+ throw new Error("No refresh token available")
+ }
+
+ console.log("Attempting token refresh...")
+
+ const response = await fetch(`${API_BASE_URL}/api/auth/access-token`, {
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: `Bearer ${refreshToken}`,
+ },
+ })
+
+ if (!response.ok) {
+ const errorData = await response.json()
+ throw new Error(errorData.error || "Token refresh failed")
+ }
+
+ const data = await response.json()
+
+ // Store new access token
+ setCookie("devboard_access_token", data.access_token, 1)
+
+ // Update user data with new token
+ const userData = parseJWT(data.access_token)
+ if (userData) {
+ const user: User = {
+ username: userData.username,
+ avatar_url: userData.avatar_url,
+ name: userData.name,
+ email: userData.email,
+ exp: userData.exp,
+ iat: userData.iat,
+ }
+
+ setState({
+ user,
+ isAuthenticated: true,
+ isLoading: false,
+ error: null,
+ })
+ }
+
+ console.log("Token refresh successful")
+ return true
+ } catch (error) {
+ console.error("Token refresh error:", error)
+ clearCookies()
+
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ })
+
+ return false
+ }
+ }, [])
+
+ // Public refresh function
+ const refreshToken = useCallback(async (): Promise => {
+ return await performTokenRefresh()
+ }, [performTokenRefresh])
+
+ // Logout function
+ const logout = useCallback(async (): Promise => {
+ try {
+ const accessToken = getCookie("devboard_access_token")
+
+ if (accessToken) {
+ console.log("Calling logout API...")
+ try {
+ await fetch(`${API_BASE_URL}/api/auth/logout`, {
+ method: "POST",
+ headers: {
+ Authorization: `Bearer ${accessToken}`,
+ },
+ })
+ console.log("Logout API call successful")
+ } catch (apiError) {
+ console.error("Logout API failed:", apiError)
+ // Continue with local logout even if API fails
+ }
+ }
+ } catch (error) {
+ console.error("Logout error:", error)
+ } finally {
+ console.log("Clearing cookies and logging out")
+ clearCookies()
+
+ setState({
+ user: null,
+ isAuthenticated: false,
+ isLoading: false,
+ error: null,
+ })
+ }
+ }, [])
+
+ // Clear error function
+ const clearError = useCallback(() => {
+ setState((prev) => ({ ...prev, error: null }))
+ }, [])
+
+ // Initialize on mount
+ useEffect(() => {
+ initializeAuth()
+ }, [initializeAuth])
+
+ const contextValue: AuthContextType = {
+ ...state,
+ login,
+ logout,
+ refreshToken,
+ clearError,
+ }
+
+ return {children}
+}
+
+// Custom hook to use auth context
+export function useAuth(): AuthContextType {
+ const context = useContext(AuthContext)
+ if (context === undefined) {
+ throw new Error("useAuth must be used within an AuthProvider")
+ }
+ return context
+}
+
+// Authenticated fetch utility
+export async function authenticatedFetch(url: string, options: RequestInit = {}): Promise {
+ const accessToken = getCookie("devboard_access_token")
+
+ if (!accessToken) {
+ throw new Error("No access token available")
+ }
+
+ if (isTokenExpired(accessToken)) {
+ throw new Error("Access token expired")
+ }
+
+ return fetch(url, {
+ ...options,
+ headers: {
+ ...options.headers,
+ Authorization: `Bearer ${accessToken}`,
+ },
+ })
+}
diff --git a/devboard/lib/portfolio-workflow.ts b/devboard/lib/portfolio-workflow.ts
new file mode 100644
index 0000000..ab90022
--- /dev/null
+++ b/devboard/lib/portfolio-workflow.ts
@@ -0,0 +1,15 @@
+export function getDummyPortfolioWorkflow() {
+ return {
+ id: 'dummy-workflow-1',
+ name: 'Sample Portfolio Workflow',
+ steps: [
+ { id: 'step-1', name: 'Ideation', status: 'completed' },
+ { id: 'step-2', name: 'Design', status: 'in-progress' },
+ { id: 'step-3', name: 'Development', status: 'pending' },
+ { id: 'step-4', name: 'Review', status: 'pending' },
+ { id: 'step-5', name: 'Launch', status: 'pending' }
+ ],
+ createdAt: new Date().toISOString(),
+ updatedAt: new Date().toISOString()
+ };
+}
\ No newline at end of file
diff --git a/devboard/lib/readme-graph.ts b/devboard/lib/readme-graph.ts
new file mode 100644
index 0000000..42fa747
--- /dev/null
+++ b/devboard/lib/readme-graph.ts
@@ -0,0 +1,157 @@
+import { StateGraph, END } from "@langchain/langgraph"
+import { ChatGoogleGenerativeAI } from "@langchain/google-genai"
+import type { ReadmeState } from "./types"
+import { cleanMarkdownContent, createAnalysisPrompt, createGenerationPrompt, createReviewPrompt } from "./utils"
+
+// Initialize the model
+const model = new ChatGoogleGenerativeAI({
+ model: "gemini-2.0-flash-exp",
+ apiKey: process.env.GEMINI_API_KEY,
+ temperature: 0.7,
+})
+
+// Analysis Node
+async function analyzeNode(state: ReadmeState): Promise> {
+ try {
+ const prompt = createAnalysisPrompt(state)
+ const response = await model.invoke(prompt)
+
+ return {
+ analysis: response.content as string,
+ }
+ } catch (error) {
+ console.error("Analysis error:", error)
+ return {
+ error: `Analysis failed: ${error instanceof Error ? error.message : "Unknown error"}`,
+ }
+ }
+}
+
+// Generation Node
+async function generateNode(state: ReadmeState): Promise> {
+ try {
+ if (state.error) {
+ return { error: state.error }
+ }
+
+ const prompt = createGenerationPrompt(state)
+ const response = await model.invoke(prompt)
+ let content = response.content as string
+
+ // Clean the generated content
+ content = cleanMarkdownContent(content)
+
+ return {
+ generatedContent: content,
+ }
+ } catch (error) {
+ console.error("Generation error:", error)
+ return {
+ error: `Generation failed: ${error instanceof Error ? error.message : "Unknown error"}`,
+ }
+ }
+}
+
+// Review Node
+async function reviewNode(state: ReadmeState): Promise> {
+ try {
+ if (state.error) {
+ return { error: state.error }
+ }
+
+ const prompt = createReviewPrompt(state)
+ const response = await model.invoke(prompt)
+ let content = response.content as string
+
+ // Clean the final content
+ content = cleanMarkdownContent(content)
+
+ // Additional validation to ensure content is clean
+ if (content.includes("```")) {
+ // If there are still code blocks, remove them more aggressively
+ content = content.replace(/```[\s\S]*?```/g, "")
+ content = content.replace(/```.*$/gm, "")
+ content = content.trim()
+ }
+
+ return {
+ finalContent: content,
+ }
+ } catch (error) {
+ console.error("Review error:", error)
+ return {
+ error: `Review failed: ${error instanceof Error ? error.message : "Unknown error"}`,
+ }
+ }
+}
+
+// Conditional edge functions
+function shouldContinueFromAnalyze(state: ReadmeState): "continue" | "end" {
+ if (state.error) {
+ return "end"
+ }
+ return "continue"
+}
+
+function shouldContinueFromGenerate(state: ReadmeState): "continue" | "end" {
+ if (state.error) {
+ return "end"
+ }
+ return "continue"
+}
+
+// Create the graph using method chaining
+export function createReadmeGraph() {
+ const graph = new StateGraph({
+ channels: {
+ username: {
+ value: (x: string, y?: string) => y ?? x,
+ default: () => "",
+ },
+ currentContent: {
+ value: (x?: string, y?: string) => y ?? x,
+ default: () => undefined,
+ },
+ isNew: {
+ value: (x: boolean, y?: boolean) => y ?? x,
+ default: () => true,
+ },
+ personalInfo: {
+ value: (x?: any, y?: any) => y ?? x,
+ default: () => undefined,
+ },
+ analysis: {
+ value: (x?: string, y?: string) => y ?? x,
+ default: () => undefined,
+ },
+ generatedContent: {
+ value: (x?: string, y?: string) => y ?? x,
+ default: () => undefined,
+ },
+ finalContent: {
+ value: (x?: string, y?: string) => y ?? x,
+ default: () => undefined,
+ },
+ error: {
+ value: (x?: string, y?: string) => y ?? x,
+ default: () => undefined,
+ },
+ },
+ })
+ .addNode("analyze", analyzeNode)
+ .addNode("generate", generateNode)
+ .addNode("review", reviewNode)
+ .addEdge("__start__", "analyze")
+ .addConditionalEdges("analyze", shouldContinueFromAnalyze, {
+ continue: "generate",
+ end: END,
+ })
+ .addConditionalEdges("generate", shouldContinueFromGenerate, {
+ continue: "review",
+ end: END,
+ })
+ .addEdge("review", END)
+ .compile()
+
+ return graph
+}
diff --git a/devboard/lib/types.ts b/devboard/lib/types.ts
new file mode 100644
index 0000000..3d0feca
--- /dev/null
+++ b/devboard/lib/types.ts
@@ -0,0 +1,37 @@
+import { z } from "zod"
+
+// Input schema
+export const ReadmeInputSchema = z.object({
+ username: z.string().min(1, "Username is required"),
+ currentContent: z.string().optional(),
+ isNew: z.boolean(),
+ personalInfo: z
+ .object({
+ field: z.string().optional(),
+ experience: z.string().optional(),
+ skills: z.string().optional(),
+ interests: z.string().optional(),
+ goals: z.string().optional(),
+ })
+ .optional(),
+})
+
+export type ReadmeInput = z.infer
+
+// State interface for the graph
+export interface ReadmeState {
+ username: string
+ currentContent?: string
+ isNew: boolean
+ personalInfo?: {
+ field?: string
+ experience?: string
+ skills?: string
+ interests?: string
+ goals?: string
+ }
+ analysis?: string
+ generatedContent?: string
+ finalContent?: string
+ error?: string
+}
diff --git a/devboard/lib/utils.ts b/devboard/lib/utils.ts
new file mode 100644
index 0000000..c8486c2
--- /dev/null
+++ b/devboard/lib/utils.ts
@@ -0,0 +1,151 @@
+import { clsx, type ClassValue } from "clsx"
+import { twMerge } from "tailwind-merge"
+
+// Function to clean markdown content
+export function cleanMarkdownContent(content: string): string {
+ // Remove markdown code block syntax from the beginning and end
+ let cleaned = content.trim()
+
+ // Remove \`\`\`markdown or \`\`\`md from the beginning
+ cleaned = cleaned.replace(/^```(?:markdown|md)\s*\n?/i, "")
+
+ // Remove \`\`\` from the end
+ cleaned = cleaned.replace(/\n?```\s*$/i, "")
+
+ // Remove any remaining code block markers that might be at the start
+ cleaned = cleaned.replace(/^```\s*\n?/i, "")
+
+ // Ensure proper spacing and clean up any extra whitespace
+ cleaned = cleaned.trim()
+
+ return cleaned
+}
+
+// Function to create analysis prompt
+export function createAnalysisPrompt(state: any): string {
+ const { username, isNew, currentContent, personalInfo } = state
+
+ if (isNew) {
+ return `Analyze the username "${username}" and create a professional analysis for a GitHub README.
+
+Personal Information:
+- Field: ${personalInfo?.field || "Not specified"}
+- Experience: ${personalInfo?.experience || "Not specified"}
+- Skills: ${personalInfo?.skills || "Not specified"}
+- Interests: ${personalInfo?.interests || "Not specified"}
+- Goals: ${personalInfo?.goals || "Not specified"}
+
+Consider what kind of developer they are and suggest appropriate sections.
+Return a brief analysis of what should be included in their README.`
+ } else {
+ return `Analyze this existing README content and suggest improvements:
+
+${currentContent}
+
+Personal Information for enhancement:
+- Field: ${personalInfo?.field || "Not specified"}
+- Experience: ${personalInfo?.experience || "Not specified"}
+- Skills: ${personalInfo?.skills || "Not specified"}
+- Interests: ${personalInfo?.interests || "Not specified"}
+- Goals: ${personalInfo?.goals || "Not specified"}
+
+Provide analysis on what's good, what's missing, and what could be improved.`
+ }
+}
+
+// Function to create generation prompt
+export function createGenerationPrompt(state: any): string {
+ const { username, isNew, currentContent, personalInfo, analysis } = state
+
+ if (isNew) {
+ return `Based on this analysis: ${analysis}
+
+Create a comprehensive GitHub profile README for username "${username}".
+
+Personal Details:
+- Field: ${personalInfo?.field || "Developer"}
+- Experience Level: ${personalInfo?.experience || "Not specified"}
+- Main Skills: ${personalInfo?.skills || "Various technologies"}
+- Current Interests: ${personalInfo?.interests || "Learning new technologies"}
+- Goals: ${personalInfo?.goals || "Growing as a developer"}
+
+Create a well-formatted markdown README with proper spacing and structure. Include these sections:
+
+# Hi, I'm ${username}! 👋
+
+## About Me
+Write a compelling about section based on their field and experience
+
+## 🚀 Skills & Technologies
+List their skills in a well-organized format using bullet points
+
+## 🌱 Currently Learning
+Based on their interests
+
+## 🎯 Goals
+Based on their goals
+
+## 📊 GitHub Stats
+
+
+
+
+## 💡 Daily Quote
+
+
+## 👀 Profile Views
+
+
+## 📫 Connect with Me
+Add contact information and social links
+
+CRITICAL FORMATTING REQUIREMENTS:
+- Return ONLY the raw markdown content
+- DO NOT wrap the response in code blocks
+- DO NOT include \`\`\`markdown or \`\`\` anywhere in the response
+- Start directly with the # Hi, I'm ${username}! 👋 header
+- Use proper line breaks between sections (double line breaks)
+- Use bullet points with - or * for lists
+- Ensure proper spacing around headers
+- Make sure all markdown syntax is clean and properly formatted
+- Use emojis appropriately for visual appeal
+
+Return ONLY the markdown content with perfect formatting. No code blocks, no explanations.`
+ } else {
+ return `Based on this analysis: ${analysis}
+
+Improve this existing README content:
+${currentContent}
+
+Add missing sections like GitHub stats, profile views, and daily quotes.
+Ensure proper markdown formatting with clean spacing and structure.
+
+CRITICAL: Return ONLY the raw markdown content without any code block wrappers.
+DO NOT include \`\`\`markdown or \`\`\` in your response.
+Return ONLY the improved markdown content.`
+ }
+}
+
+// Function to create review prompt
+export function createReviewPrompt(state: any): string {
+ return `Review and refine this generated README content:
+
+${state.generatedContent}
+
+Ensure:
+- Proper markdown formatting with clean line breaks between sections
+- Well-structured sections with appropriate spacing
+- Professional and engaging tone
+- Proper use of headers, lists, and formatting
+- Clean, readable structure
+- All GitHub stats widgets are properly formatted
+- Profile views and daily quote widgets are included
+
+CRITICAL REQUIREMENT: Return ONLY the raw markdown content.
+DO NOT wrap your response in code blocks.
+DO NOT include \`\`\`markdown or \`\`\` anywhere in the response.
+Return the final, polished README content as plain markdown text only.`
+}
+export function cn(...inputs: ClassValue[]) {
+ return twMerge(clsx(inputs))
+}
diff --git a/devboard/next.config.ts b/devboard/next.config.ts
new file mode 100644
index 0000000..9482a69
--- /dev/null
+++ b/devboard/next.config.ts
@@ -0,0 +1,13 @@
+import type { NextConfig } from "next";
+
+const nextConfig: NextConfig = {
+ /* config options here */
+ typescript: {
+ ignoreBuildErrors: true,
+ },
+ eslint: {
+ ignoreDuringBuilds: true,
+ },
+};
+
+export default nextConfig;
diff --git a/devboard/package-lock.json b/devboard/package-lock.json
new file mode 100644
index 0000000..07113c4
--- /dev/null
+++ b/devboard/package-lock.json
@@ -0,0 +1,5222 @@
+{
+ "name": "devboard",
+ "version": "0.1.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "devboard",
+ "version": "0.1.0",
+ "dependencies": {
+ "@google/generative-ai": "^0.24.1",
+ "@langchain/core": "^0.3.61",
+ "@langchain/google-genai": "^0.2.14",
+ "@langchain/langgraph": "^0.3.6",
+ "@radix-ui/react-collapsible": "^1.1.11",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-scroll-area": "^1.2.9",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "framer-motion": "^12.23.9",
+ "lucide-react": "^0.515.0",
+ "next": "15.3.3",
+ "next-themes": "^0.4.6",
+ "react": "^19.0.0",
+ "react-dnd": "^16.0.1",
+ "react-dnd-html5-backend": "^16.0.1",
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "react-resizable-panels": "^3.0.3",
+ "rehype-highlight": "^7.0.2",
+ "rehype-raw": "^7.0.0",
+ "remark-gfm": "^4.0.1",
+ "sonner": "^2.0.5",
+ "tailwind-merge": "^3.3.1",
+ "uuid": "^11.1.0",
+ "zod": "^3.25.67"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "tw-animate-css": "^1.3.4",
+ "typescript": "^5"
+ }
+ },
+ "node_modules/@alloc/quick-lru": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz",
+ "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/@ampproject/remapping": {
+ "version": "2.3.0",
+ "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
+ "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/runtime": {
+ "version": "7.27.6",
+ "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz",
+ "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@cfworker/json-schema": {
+ "version": "4.1.1",
+ "resolved": "https://registry.npmjs.org/@cfworker/json-schema/-/json-schema-4.1.1.tgz",
+ "integrity": "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==",
+ "license": "MIT"
+ },
+ "node_modules/@emnapi/runtime": {
+ "version": "1.4.3",
+ "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz",
+ "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "tslib": "^2.4.0"
+ }
+ },
+ "node_modules/@floating-ui/core": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.1.tgz",
+ "integrity": "sha512-azI0DrjMMfIug/ExbBaeDVJXcY0a7EPvPjb2xAJPa4HeimBX+Z18HK8QQR3jb6356SnDDdxx+hinMLcJEDdOjw==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/dom": {
+ "version": "1.7.1",
+ "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.1.tgz",
+ "integrity": "sha512-cwsmW/zyw5ltYTUeeYJ60CnQuPqmGwuGVhG9w0PRaRKkAyi38BT5CKrpIbb+jtahSwUl04cWzSx9ZOIxeS6RsQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/core": "^1.7.1",
+ "@floating-ui/utils": "^0.2.9"
+ }
+ },
+ "node_modules/@floating-ui/react-dom": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.3.tgz",
+ "integrity": "sha512-huMBfiU9UnQ2oBwIhgzyIiSpVgvlDstU8CX0AF+wS+KzmYMs0J2a3GwuFHV1Lz+jlrQGeC1fF+Nv0QoumyV0bA==",
+ "license": "MIT",
+ "dependencies": {
+ "@floating-ui/dom": "^1.0.0"
+ },
+ "peerDependencies": {
+ "react": ">=16.8.0",
+ "react-dom": ">=16.8.0"
+ }
+ },
+ "node_modules/@floating-ui/utils": {
+ "version": "0.2.9",
+ "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.9.tgz",
+ "integrity": "sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==",
+ "license": "MIT"
+ },
+ "node_modules/@google/generative-ai": {
+ "version": "0.24.1",
+ "resolved": "https://registry.npmjs.org/@google/generative-ai/-/generative-ai-0.24.1.tgz",
+ "integrity": "sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q==",
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.2.tgz",
+ "integrity": "sha512-OfXHZPppddivUJnqyKoi5YVeHRkkNE2zUFT2gbpKxp/JZCFYEYubnMg+gOp6lWfasPrTS+KPosKqdI+ELYVDtg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.2.tgz",
+ "integrity": "sha512-dYvWqmjU9VxqXmjEtjmvHnGqF8GrVjM2Epj9rJ6BUIXvk8slvNDJbhGFvIoXzkDhrJC2jUxNLz/GUjjvSzfw+g==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz",
+ "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz",
+ "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz",
+ "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz",
+ "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-ppc64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz",
+ "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-s390x": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz",
+ "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linux-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz",
+ "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-arm64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz",
+ "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-libvips-linuxmusl-x64": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz",
+ "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.2.tgz",
+ "integrity": "sha512-0DZzkvuEOqQUP9mo2kjjKNok5AmnOr1jB2XYjkaoNRwpAYMDzRmAqUIa1nRi58S2WswqSfPOWLNOr0FDT3H5RQ==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-arm64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.2.tgz",
+ "integrity": "sha512-D8n8wgWmPDakc83LORcfJepdOSN6MvWNzzz2ux0MnIbOqdieRZwVYY32zxVx+IFUT8er5KPcyU3XXsn+GzG/0Q==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-s390x": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.2.tgz",
+ "integrity": "sha512-EGZ1xwhBI7dNISwxjChqBGELCWMGDvmxZXKjQRuqMrakhO8QoMgqCrdjnAqJq/CScxfRn+Bb7suXBElKQpPDiw==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-s390x": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.2.tgz",
+ "integrity": "sha512-sD7J+h5nFLMMmOXYH4DD9UtSNBD05tWSSdWAcEyzqW8Cn5UxXvsHAxmxSesYUsTOBmUnjtxghKDl15EvfqLFbQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.2.tgz",
+ "integrity": "sha512-NEE2vQ6wcxYav1/A22OOxoSOGiKnNmDzCYFOZ949xFmrWZOVII1Bp3NqVVpvj+3UeHMFyN5eP/V5hzViQ5CZNA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.2.tgz",
+ "integrity": "sha512-DOYMrDm5E6/8bm/yQLCWyuDJwUnlevR8xtF8bs+gjZ7cyUNYXiSf/E8Kp0Ss5xasIaXSHzb888V1BE4i1hFhAA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.1.0"
+ }
+ },
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.2.tgz",
+ "integrity": "sha512-/VI4mdlJ9zkaq53MbIG6rZY+QRN3MLbR6usYlgITEzi4Rpx5S6LFKsycOQjkOGmqTNmkIdLjEvooFKwww6OpdQ==",
+ "cpu": [
+ "wasm32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/runtime": "^1.4.3"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.2.tgz",
+ "integrity": "sha512-cfP/r9FdS63VA5k0xiqaNaEoGxBg9k7uE+RQGzuK9fHt7jib4zAVVseR9LsE4gJcNWgT6APKMNnCcnyOtmSEUQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.2.tgz",
+ "integrity": "sha512-QLjGGvAbj0X/FXl8n1WbtQ6iVBpWU7JO94u/P2M4a8CFYsvQi4GW2mRy/JqkRx0qpBzaOdKJKw8uc930EX2AHw==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.2.tgz",
+ "integrity": "sha512-aUdT6zEYtDKCaxkofmmJDJYGCf0+pJg3eU9/oBuqvEeoB9dKI6ZLc/1iLJCTuJQDO4ptntAlkUmHgGjyuobZbw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ }
+ },
+ "node_modules/@isaacs/fs-minipass": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz",
+ "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "minipass": "^7.0.4"
+ },
+ "engines": {
+ "node": ">=18.0.0"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.8",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz",
+ "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/set-array": "^1.2.1",
+ "@jridgewell/sourcemap-codec": "^1.4.10",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/set-array": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz",
+ "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.0",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz",
+ "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.25",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+ "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@langchain/core": {
+ "version": "0.3.61",
+ "resolved": "https://registry.npmjs.org/@langchain/core/-/core-0.3.61.tgz",
+ "integrity": "sha512-4O7fw5SXNSE+uBnathLQrhm3t+7dZGagt/5kt37A+pXw0AkudxEBvveg73sSnpBd9SIz3/Vc7F4k8rCKXGbEDA==",
+ "license": "MIT",
+ "dependencies": {
+ "@cfworker/json-schema": "^4.0.2",
+ "ansi-styles": "^5.0.0",
+ "camelcase": "6",
+ "decamelize": "1.2.0",
+ "js-tiktoken": "^1.0.12",
+ "langsmith": "^0.3.33",
+ "mustache": "^4.2.0",
+ "p-queue": "^6.6.2",
+ "p-retry": "4",
+ "uuid": "^10.0.0",
+ "zod": "^3.25.32",
+ "zod-to-json-schema": "^3.22.3"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@langchain/core/node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@langchain/google-genai": {
+ "version": "0.2.14",
+ "resolved": "https://registry.npmjs.org/@langchain/google-genai/-/google-genai-0.2.14.tgz",
+ "integrity": "sha512-gKe/T2LNh8wSSMJOaFmYd8cwQnDSXKtVtC6a7CFoq5nWuh0bKzhItM/7bue1aMN8mlKfB2G1HCwxhaZoSpS/DA==",
+ "license": "MIT",
+ "dependencies": {
+ "@google/generative-ai": "^0.24.0",
+ "uuid": "^11.1.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@langchain/core": ">=0.3.58 <0.4.0"
+ }
+ },
+ "node_modules/@langchain/langgraph": {
+ "version": "0.3.6",
+ "resolved": "https://registry.npmjs.org/@langchain/langgraph/-/langgraph-0.3.6.tgz",
+ "integrity": "sha512-TMRUEPb/eC5mS8XdY6gwLGX2druwFDxSWUQDXHHNsbrqhIrL3BPlw+UumjcKBQ8wvhk3gEspg4aHXGq8mAqbRA==",
+ "license": "MIT",
+ "dependencies": {
+ "@langchain/langgraph-checkpoint": "~0.0.18",
+ "@langchain/langgraph-sdk": "~0.0.32",
+ "uuid": "^10.0.0",
+ "zod": "^3.25.32"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@langchain/core": ">=0.3.58 < 0.4.0",
+ "zod-to-json-schema": "^3.x"
+ },
+ "peerDependenciesMeta": {
+ "zod-to-json-schema": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@langchain/langgraph-checkpoint": {
+ "version": "0.0.18",
+ "resolved": "https://registry.npmjs.org/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.18.tgz",
+ "integrity": "sha512-IS7zJj36VgY+4pf8ZjsVuUWef7oTwt1y9ylvwu0aLuOn1d0fg05Om9DLm3v2GZ2Df6bhLV1kfWAM0IAl9O5rQQ==",
+ "license": "MIT",
+ "dependencies": {
+ "uuid": "^10.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "peerDependencies": {
+ "@langchain/core": ">=0.2.31 <0.4.0"
+ }
+ },
+ "node_modules/@langchain/langgraph-checkpoint/node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@langchain/langgraph-sdk": {
+ "version": "0.0.84",
+ "resolved": "https://registry.npmjs.org/@langchain/langgraph-sdk/-/langgraph-sdk-0.0.84.tgz",
+ "integrity": "sha512-l0PFQyJ+6m6aclORNPPWlcRwgKcXVXsPaJCbCUYFABR3yf4cOpsjhUNR0cJ7+2cS400oieHjGRdGGyO/hbSjhg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15",
+ "p-queue": "^6.6.2",
+ "p-retry": "4",
+ "uuid": "^9.0.0"
+ },
+ "peerDependencies": {
+ "@langchain/core": ">=0.2.31 <0.4.0",
+ "react": "^18 || ^19"
+ },
+ "peerDependenciesMeta": {
+ "@langchain/core": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@langchain/langgraph-sdk/node_modules/uuid": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz",
+ "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@langchain/langgraph/node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/@next/env": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.3.tgz",
+ "integrity": "sha512-OdiMrzCl2Xi0VTjiQQUK0Xh7bJHnOuET2s+3V+Y40WJBAXrJeGA3f+I8MZJ/YQ3mVGi5XGR1L66oFlgqXhQ4Vw==",
+ "license": "MIT"
+ },
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.3.tgz",
+ "integrity": "sha512-WRJERLuH+O3oYB4yZNVahSVFmtxRNjNF1I1c34tYMoJb0Pve+7/RaLAJJizyYiFhjYNGHRAE1Ri2Fd23zgDqhg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.3.tgz",
+ "integrity": "sha512-XHdzH/yBc55lu78k/XwtuFR/ZXUTcflpRXcsu0nKmF45U96jt1tsOZhVrn5YH+paw66zOANpOnFQ9i6/j+UYvw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.3.tgz",
+ "integrity": "sha512-VZ3sYL2LXB8znNGcjhocikEkag/8xiLgnvQts41tq6i+wql63SMS1Q6N8RVXHw5pEUjiof+II3HkDd7GFcgkzw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.3.tgz",
+ "integrity": "sha512-h6Y1fLU4RWAp1HPNJWDYBQ+e3G7sLckyBXhmH9ajn8l/RSMnhbuPBV/fXmy3muMcVwoJdHL+UtzRzs0nXOf9SA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.3.tgz",
+ "integrity": "sha512-jJ8HRiF3N8Zw6hGlytCj5BiHyG/K+fnTKVDEKvUCyiQ/0r5tgwO7OgaRiOjjRoIx2vwLR+Rz8hQoPrnmFbJdfw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.3.tgz",
+ "integrity": "sha512-HrUcTr4N+RgiiGn3jjeT6Oo208UT/7BuTr7K0mdKRBtTbT4v9zJqCDKO97DUqqoBK1qyzP1RwvrWTvU6EPh/Cw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.3.tgz",
+ "integrity": "sha512-SxorONgi6K7ZUysMtRF3mIeHC5aA3IQLmKFQzU0OuhuUYwpOBc1ypaLJLP5Bf3M9k53KUUUj4vTPwzGvl/NwlQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.3.tgz",
+ "integrity": "sha512-4QZG6F8enl9/S2+yIiOiju0iCTFd93d8VC1q9LZS4p/Xuk81W2QDjCFeoogmrWWkAD59z8ZxepBQap2dKS5ruw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@radix-ui/number": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz",
+ "integrity": "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==",
+ "license": "MIT"
+ },
+ "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-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-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-collapsible": {
+ "version": "1.1.11",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-collapsible/-/react-collapsible-1.1.11.tgz",
+ "integrity": "sha512-2qrRsVGSCYasSz1RFOorXwl0H7g7J1frQtgpQgYrt+MOidtPAINHn9CPovQXb83r8ahapdx3Tu0fa/pdFFSdPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-presence": "1.1.4",
+ "@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"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-collection": {
+ "version": "1.1.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-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-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-dialog": {
+ "version": "1.1.14",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dialog/-/react-dialog-1.1.14.tgz",
+ "integrity": "sha512-+CpweKjqpzTmwRwcYECQcNYbI8V9VSQt0SNFKeEBLgfucbsLssU6Ppq7wUdNXEGb573bMjFhVjKVll8rmV6zMw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-dismissable-layer": "1.1.10",
+ "@radix-ui/react-focus-guards": "1.1.2",
+ "@radix-ui/react-focus-scope": "1.1.7",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-portal": "1.1.9",
+ "@radix-ui/react-presence": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3",
+ "@radix-ui/react-use-controllable-state": "1.2.2",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.6.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-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-dismissable-layer": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.1.10.tgz",
+ "integrity": "sha512-IM1zzRV4W3HtVgftdQiiOmA0AdJlCtMLe00FXaHwgt3rAnNsIyDqshvkIW3hj/iu5hu8ERP7KIYki6NkqDxAwQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@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": "*",
+ "@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-focus-guards": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-guards/-/react-focus-guards-1.1.2.tgz",
+ "integrity": "sha512-fyjAACV62oPV925xFCrH8DR5xWhg9KYtJT4s3u54jxp+L/hbpTY2kIeEFFbFe+a/HCE94zGQMZLIpVTPVZDhaA==",
+ "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-focus-scope": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-focus-scope/-/react-focus-scope-1.1.7.tgz",
+ "integrity": "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.2",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "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-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-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-label": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-label/-/react-label-2.1.7.tgz",
+ "integrity": "sha512-YT1GqPSL8kJn20djelMX7/cTRp/Y9w5IZHvfxQTVHrOqa2yMl7i/UfMqKRU5V7mEyKTrUVgJXhNQPVCG8PBLoQ==",
+ "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-popper": {
+ "version": "1.2.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-popper/-/react-popper-1.2.7.tgz",
+ "integrity": "sha512-IUFAccz1JyKcf/RjB552PlWwxjeCJB8/4KxT7EhBHOJM+mN7LdW+B3kacJXILm32xawcMMjb2i0cIZpo+f9kiQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@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": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-portal": {
+ "version": "1.1.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/react-primitive": "2.1.3",
+ "@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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-presence": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-presence/-/react-presence-1.1.4.tgz",
+ "integrity": "sha512-ueDqRbdc4/bkaQT3GIpLQssRlFgWaL/U2z/S31qRwwLWoxHLgry3SIfCwhxeQNbirEUXFa+lq3RL3oBYXtcmIA==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/react-compose-refs": "1.1.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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "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": {
+ "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-roving-focus": {
+ "version": "1.1.10",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.10.tgz",
+ "integrity": "sha512-dT9aOXUen9JSsxnMPv/0VqySQf5eDQ6LCk5Sw28kamz8wSOW2bJdlX2Bg5VUIIcV+6XlHpWTIuTPCf/UNIyq8Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@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-id": "1.1.1",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@radix-ui/react-use-controllable-state": "1.2.2"
+ },
+ "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-scroll-area": {
+ "version": "1.2.9",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.9.tgz",
+ "integrity": "sha512-YSjEfBXnhUELsO2VzjdtYYD4CfQjvao+lhhrX5XsHD7/cyUNzljF1FHEbgTPN7LH2MClfwRMIsYlqTYpKTTe2A==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/number": "1.1.1",
+ "@radix-ui/primitive": "1.1.2",
+ "@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-presence": "1.1.4",
+ "@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"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-select": {
+ "version": "2.2.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-select/-/react-select-2.2.5.tgz",
+ "integrity": "sha512-HnMTdXEVuuyzx63ME0ut4+sEMYW6oouHWNGUZc7ddvUWIcfCva/AMoqEW/3wnEllriMWBa0RHspCYnfCWJQYmA==",
+ "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-dismissable-layer": "1.1.10",
+ "@radix-ui/react-focus-guards": "1.1.2",
+ "@radix-ui/react-focus-scope": "1.1.7",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-popper": "1.2.7",
+ "@radix-ui/react-portal": "1.1.9",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-slot": "1.2.3",
+ "@radix-ui/react-use-callback-ref": "1.1.1",
+ "@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-visually-hidden": "1.2.3",
+ "aria-hidden": "^1.2.4",
+ "react-remove-scroll": "^2.6.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-separator": {
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-separator/-/react-separator-1.1.7.tgz",
+ "integrity": "sha512-0HEb8R9E8A+jZjvmFCy/J4xhbXy3TV+9XSnGJ3KvTtjlIUy/YQ/p6UYZvi7YbeoeXdyU9+Y3scizK6hkY37baA==",
+ "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-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-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-switch": {
+ "version": "1.2.5",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-switch/-/react-switch-1.2.5.tgz",
+ "integrity": "sha512-5ijLkak6ZMylXsaImpZ8u4Rlf5grRmoc0p0QeX9VJtlrM4f5m3nCTX8tWga/zOA8PZYIR/t0p2Mnvd7InrJ6yQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@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-controllable-state": "1.2.2",
+ "@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-tabs": {
+ "version": "1.1.12",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-tabs/-/react-tabs-1.1.12.tgz",
+ "integrity": "sha512-GTVAlRVrQrSw3cEARM0nAx73ixrWDPNZAruETn3oHCNP6SbZ/hNxdxp+u7VkIEv3/sFoLq1PfcHrl7Pnp0CDpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@radix-ui/primitive": "1.1.2",
+ "@radix-ui/react-context": "1.1.2",
+ "@radix-ui/react-direction": "1.1.1",
+ "@radix-ui/react-id": "1.1.1",
+ "@radix-ui/react-presence": "1.1.4",
+ "@radix-ui/react-primitive": "2.1.3",
+ "@radix-ui/react-roving-focus": "1.1.10",
+ "@radix-ui/react-use-controllable-state": "1.2.2"
+ },
+ "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-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": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "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-use-effect-event": {
+ "version": "0.0.2",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-use-effect-event/-/react-use-effect-event-0.0.2.tgz",
+ "integrity": "sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==",
+ "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-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": {
+ "@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"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "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-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-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/rect": "1.1.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/@radix-ui/react-use-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-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/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/@react-dnd/asap": {
+ "version": "5.0.2",
+ "resolved": "https://registry.npmjs.org/@react-dnd/asap/-/asap-5.0.2.tgz",
+ "integrity": "sha512-WLyfoHvxhs0V9U+GTsGilGgf2QsPl6ZZ44fnv0/b8T3nQyvzxidxsg/ZltbWssbsRDlYW8UKSQMTGotuTotZ6A==",
+ "license": "MIT"
+ },
+ "node_modules/@react-dnd/invariant": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@react-dnd/invariant/-/invariant-4.0.2.tgz",
+ "integrity": "sha512-xKCTqAK/FFauOM9Ta2pswIyT3D8AQlfrYdOi/toTPEhqCuAs1v5tcJ3Y08Izh1cJ5Jchwy9SeAXmMg6zrKs2iw==",
+ "license": "MIT"
+ },
+ "node_modules/@react-dnd/shallowequal": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/@react-dnd/shallowequal/-/shallowequal-4.0.2.tgz",
+ "integrity": "sha512-/RVXdLvJxLg4QKvMoM5WlwNR9ViO9z8B/qPcc+C0Sa/teJY7QG7kJ441DwzOjMYEY7GmU4dj5EcGHIkKZiQZCA==",
+ "license": "MIT"
+ },
+ "node_modules/@swc/counter": {
+ "version": "0.1.3",
+ "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz",
+ "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==",
+ "license": "Apache-2.0"
+ },
+ "node_modules/@swc/helpers": {
+ "version": "0.5.15",
+ "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz",
+ "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "tslib": "^2.8.0"
+ }
+ },
+ "node_modules/@tailwindcss/node": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.1.10.tgz",
+ "integrity": "sha512-2ACf1znY5fpRBwRhMgj9ZXvb2XZW8qs+oTfotJ2C5xR0/WNL7UHZ7zXl6s+rUqedL1mNi+0O+WQr5awGowS3PQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@ampproject/remapping": "^2.3.0",
+ "enhanced-resolve": "^5.18.1",
+ "jiti": "^2.4.2",
+ "lightningcss": "1.30.1",
+ "magic-string": "^0.30.17",
+ "source-map-js": "^1.2.1",
+ "tailwindcss": "4.1.10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.1.10.tgz",
+ "integrity": "sha512-v0C43s7Pjw+B9w21htrQwuFObSkio2aV/qPx/mhrRldbqxbWJK6KizM+q7BF1/1CmuLqZqX3CeYF7s7P9fbA8Q==",
+ "dev": true,
+ "hasInstallScript": true,
+ "license": "MIT",
+ "dependencies": {
+ "detect-libc": "^2.0.4",
+ "tar": "^7.4.3"
+ },
+ "engines": {
+ "node": ">= 10"
+ },
+ "optionalDependencies": {
+ "@tailwindcss/oxide-android-arm64": "4.1.10",
+ "@tailwindcss/oxide-darwin-arm64": "4.1.10",
+ "@tailwindcss/oxide-darwin-x64": "4.1.10",
+ "@tailwindcss/oxide-freebsd-x64": "4.1.10",
+ "@tailwindcss/oxide-linux-arm-gnueabihf": "4.1.10",
+ "@tailwindcss/oxide-linux-arm64-gnu": "4.1.10",
+ "@tailwindcss/oxide-linux-arm64-musl": "4.1.10",
+ "@tailwindcss/oxide-linux-x64-gnu": "4.1.10",
+ "@tailwindcss/oxide-linux-x64-musl": "4.1.10",
+ "@tailwindcss/oxide-wasm32-wasi": "4.1.10",
+ "@tailwindcss/oxide-win32-arm64-msvc": "4.1.10",
+ "@tailwindcss/oxide-win32-x64-msvc": "4.1.10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-android-arm64": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.1.10.tgz",
+ "integrity": "sha512-VGLazCoRQ7rtsCzThaI1UyDu/XRYVyH4/EWiaSX6tFglE+xZB5cvtC5Omt0OQ+FfiIVP98su16jDVHDEIuH4iQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-arm64": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.1.10.tgz",
+ "integrity": "sha512-ZIFqvR1irX2yNjWJzKCqTCcHZbgkSkSkZKbRM3BPzhDL/18idA8uWCoopYA2CSDdSGFlDAxYdU2yBHwAwx8euQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-darwin-x64": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.1.10.tgz",
+ "integrity": "sha512-eCA4zbIhWUFDXoamNztmS0MjXHSEJYlvATzWnRiTqJkcUteSjO94PoRHJy1Xbwp9bptjeIxxBHh+zBWFhttbrQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-freebsd-x64": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.1.10.tgz",
+ "integrity": "sha512-8/392Xu12R0cc93DpiJvNpJ4wYVSiciUlkiOHOSOQNH3adq9Gi/dtySK7dVQjXIOzlpSHjeCL89RUUI8/GTI6g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.1.10.tgz",
+ "integrity": "sha512-t9rhmLT6EqeuPT+MXhWhlRYIMSfh5LZ6kBrC4FS6/+M1yXwfCtp24UumgCWOAJVyjQwG+lYva6wWZxrfvB+NhQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-gnu": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.1.10.tgz",
+ "integrity": "sha512-3oWrlNlxLRxXejQ8zImzrVLuZ/9Z2SeKoLhtCu0hpo38hTO2iL86eFOu4sVR8cZc6n3z7eRXXqtHJECa6mFOvA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-arm64-musl": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.1.10.tgz",
+ "integrity": "sha512-saScU0cmWvg/Ez4gUmQWr9pvY9Kssxt+Xenfx1LG7LmqjcrvBnw4r9VjkFcqmbBb7GCBwYNcZi9X3/oMda9sqQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-gnu": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.1.10.tgz",
+ "integrity": "sha512-/G3ao/ybV9YEEgAXeEg28dyH6gs1QG8tvdN9c2MNZdUXYBaIY/Gx0N6RlJzfLy/7Nkdok4kaxKPHKJUlAaoTdA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-linux-x64-musl": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.1.10.tgz",
+ "integrity": "sha512-LNr7X8fTiKGRtQGOerSayc2pWJp/9ptRYAa4G+U+cjw9kJZvkopav1AQc5HHD+U364f71tZv6XamaHKgrIoVzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-wasm32-wasi": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.1.10.tgz",
+ "integrity": "sha512-d6ekQpopFQJAcIK2i7ZzWOYGZ+A6NzzvQ3ozBvWFdeyqfOZdYHU66g5yr+/HC4ipP1ZgWsqa80+ISNILk+ae/Q==",
+ "bundleDependencies": [
+ "@napi-rs/wasm-runtime",
+ "@emnapi/core",
+ "@emnapi/runtime",
+ "@tybys/wasm-util",
+ "@emnapi/wasi-threads",
+ "tslib"
+ ],
+ "cpu": [
+ "wasm32"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "@emnapi/core": "^1.4.3",
+ "@emnapi/runtime": "^1.4.3",
+ "@emnapi/wasi-threads": "^1.0.2",
+ "@napi-rs/wasm-runtime": "^0.2.10",
+ "@tybys/wasm-util": "^0.9.0",
+ "tslib": "^2.8.0"
+ },
+ "engines": {
+ "node": ">=14.0.0"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-arm64-msvc": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.1.10.tgz",
+ "integrity": "sha512-i1Iwg9gRbwNVOCYmnigWCCgow8nDWSFmeTUU5nbNx3rqbe4p0kRbEqLwLJbYZKmSSp23g4N6rCDmm7OuPBXhDA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/oxide-win32-x64-msvc": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.1.10.tgz",
+ "integrity": "sha512-sGiJTjcBSfGq2DVRtaSljq5ZgZS2SDHSIfhOylkBvHVjwOsodBhnb3HdmiKkVuUGKD0I7G63abMOVaskj1KpOA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 10"
+ }
+ },
+ "node_modules/@tailwindcss/postcss": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@tailwindcss/postcss/-/postcss-4.1.10.tgz",
+ "integrity": "sha512-B+7r7ABZbkXJwpvt2VMnS6ujcDoR2OOcFaqrLIo1xbcdxje4Vf+VgJdBzNNbrAjBj/rLZ66/tlQ1knIGNLKOBQ==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@alloc/quick-lru": "^5.2.0",
+ "@tailwindcss/node": "4.1.10",
+ "@tailwindcss/oxide": "4.1.10",
+ "postcss": "^8.4.41",
+ "tailwindcss": "4.1.10"
+ }
+ },
+ "node_modules/@types/debug": {
+ "version": "4.1.12",
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/ms": "*"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.8",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
+ "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
+ "license": "MIT"
+ },
+ "node_modules/@types/estree-jsx": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "*"
+ }
+ },
+ "node_modules/@types/hast": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/json-schema": {
+ "version": "7.0.15",
+ "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+ "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/mdast": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "*"
+ }
+ },
+ "node_modules/@types/ms": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/node": {
+ "version": "20.19.0",
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.0.tgz",
+ "integrity": "sha512-hfrc+1tud1xcdVTABC2JiomZJEklMcXYNTVtZLAeqTVWD+qL5jkHKT+1lOtqDdGxt+mB53DTtiz673vfjU8D1Q==",
+ "devOptional": true,
+ "license": "MIT",
+ "dependencies": {
+ "undici-types": "~6.21.0"
+ }
+ },
+ "node_modules/@types/react": {
+ "version": "19.1.8",
+ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
+ "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
+ "license": "MIT",
+ "dependencies": {
+ "csstype": "^3.0.2"
+ }
+ },
+ "node_modules/@types/react-dom": {
+ "version": "19.1.6",
+ "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.6.tgz",
+ "integrity": "sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==",
+ "devOptional": true,
+ "license": "MIT",
+ "peerDependencies": {
+ "@types/react": "^19.0.0"
+ }
+ },
+ "node_modules/@types/retry": {
+ "version": "0.12.0",
+ "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz",
+ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==",
+ "license": "MIT"
+ },
+ "node_modules/@types/unist": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
+ "license": "MIT"
+ },
+ "node_modules/@types/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ==",
+ "license": "MIT"
+ },
+ "node_modules/@ungap/structured-clone": {
+ "version": "1.3.0",
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
+ "license": "ISC"
+ },
+ "node_modules/ansi-styles": {
+ "version": "5.2.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz",
+ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/aria-hidden": {
+ "version": "1.2.6",
+ "resolved": "https://registry.npmjs.org/aria-hidden/-/aria-hidden-1.2.6.tgz",
+ "integrity": "sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/bail": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/base64-js": {
+ "version": "1.5.1",
+ "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
+ "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/feross"
+ },
+ {
+ "type": "patreon",
+ "url": "https://www.patreon.com/feross"
+ },
+ {
+ "type": "consulting",
+ "url": "https://feross.org/support"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/busboy": {
+ "version": "1.6.0",
+ "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
+ "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
+ "dependencies": {
+ "streamsearch": "^1.1.0"
+ },
+ "engines": {
+ "node": ">=10.16.0"
+ }
+ },
+ "node_modules/camelcase": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+ "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001723",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001723.tgz",
+ "integrity": "sha512-1R/elMjtehrFejxwmexeXAtae5UO9iSyFn6G/I806CYC/BLyyBk1EPhrKBkWhy6wM6Xnm47dSJQec+tLJ39WHw==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/ccount": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chalk": {
+ "version": "4.1.2",
+ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+ "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.1.0",
+ "supports-color": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/chalk?sponsor=1"
+ }
+ },
+ "node_modules/chalk/node_modules/ansi-styles": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+ "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+ "license": "MIT",
+ "dependencies": {
+ "color-convert": "^2.0.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+ }
+ },
+ "node_modules/character-entities": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-html4": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-entities-legacy": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/character-reference-invalid": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/chownr": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz",
+ "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/class-variance-authority": {
+ "version": "0.7.1",
+ "resolved": "https://registry.npmjs.org/class-variance-authority/-/class-variance-authority-0.7.1.tgz",
+ "integrity": "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "clsx": "^2.1.1"
+ },
+ "funding": {
+ "url": "https://polar.sh/cva"
+ }
+ },
+ "node_modules/client-only": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz",
+ "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==",
+ "license": "MIT"
+ },
+ "node_modules/clsx": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
+ "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/color": {
+ "version": "4.2.3",
+ "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz",
+ "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-convert": "^2.0.1",
+ "color-string": "^1.9.0"
+ },
+ "engines": {
+ "node": ">=12.5.0"
+ }
+ },
+ "node_modules/color-convert": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+ "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+ "license": "MIT",
+ "dependencies": {
+ "color-name": "~1.1.4"
+ },
+ "engines": {
+ "node": ">=7.0.0"
+ }
+ },
+ "node_modules/color-name": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+ "license": "MIT"
+ },
+ "node_modules/color-string": {
+ "version": "1.9.1",
+ "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz",
+ "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "color-name": "^1.0.0",
+ "simple-swizzle": "^0.2.2"
+ }
+ },
+ "node_modules/comma-separated-tokens": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/console-table-printer": {
+ "version": "2.14.6",
+ "resolved": "https://registry.npmjs.org/console-table-printer/-/console-table-printer-2.14.6.tgz",
+ "integrity": "sha512-MCBl5HNVaFuuHW6FGbL/4fB7N/ormCy+tQ+sxTrF6QtSbSNETvPuOVbkJBhzDgYhvjWGrTma4eYJa37ZuoQsPw==",
+ "license": "MIT",
+ "dependencies": {
+ "simple-wcswidth": "^1.0.1"
+ }
+ },
+ "node_modules/csstype": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
+ "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.1",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
+ "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/decamelize": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
+ "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/decode-named-character-reference": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
+ "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dequal": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/detect-libc": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz",
+ "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==",
+ "devOptional": true,
+ "license": "Apache-2.0",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/detect-node-es": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz",
+ "integrity": "sha512-ypdmJU/TbBby2Dxibuv7ZLW3Bs1QEmM7nHjEANfohJLvE0XVujisn1qPJcZxg+qDucsr+bP6fLD1rPS3AhJ7EQ==",
+ "license": "MIT"
+ },
+ "node_modules/devlop": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
+ "license": "MIT",
+ "dependencies": {
+ "dequal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/dnd-core": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/dnd-core/-/dnd-core-16.0.1.tgz",
+ "integrity": "sha512-HK294sl7tbw6F6IeuK16YSBUoorvHpY8RHO+9yFfaJyCDVb6n7PRcezrOEOa2SBCqiYpemh5Jx20ZcjKdFAVng==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-dnd/asap": "^5.0.1",
+ "@react-dnd/invariant": "^4.0.1",
+ "redux": "^4.2.0"
+ }
+ },
+ "node_modules/enhanced-resolve": {
+ "version": "5.18.1",
+ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.1.tgz",
+ "integrity": "sha512-ZSW3ma5GkcQBIpwZTSRAI8N71Uuwgs93IezB7mf7R60tC8ZbJideoDNKjHn2O9KIlx6rkGTTEk1xUCK2E1Y2Yg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "graceful-fs": "^4.2.4",
+ "tapable": "^2.2.0"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/entities": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz",
+ "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==",
+ "license": "BSD-2-Clause",
+ "engines": {
+ "node": ">=0.12"
+ },
+ "funding": {
+ "url": "https://github.com/fb55/entities?sponsor=1"
+ }
+ },
+ "node_modules/escape-string-regexp": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/estree-util-is-identifier-name": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/eventemitter3": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz",
+ "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==",
+ "license": "MIT"
+ },
+ "node_modules/extend": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+ "license": "MIT"
+ },
+ "node_modules/fast-deep-equal": {
+ "version": "3.1.3",
+ "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+ "license": "MIT"
+ },
+ "node_modules/framer-motion": {
+ "version": "12.23.9",
+ "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.9.tgz",
+ "integrity": "sha512-TqEHXj8LWfQSKqfdr5Y4mYltYLw96deu6/K9kGDd+ysqRJPNwF9nb5mZcrLmybHbU7gcJ+HQar41U3UTGanbbQ==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-dom": "^12.23.9",
+ "motion-utils": "^12.23.6",
+ "tslib": "^2.4.0"
+ },
+ "peerDependencies": {
+ "@emotion/is-prop-valid": "*",
+ "react": "^18.0.0 || ^19.0.0",
+ "react-dom": "^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@emotion/is-prop-valid": {
+ "optional": true
+ },
+ "react": {
+ "optional": true
+ },
+ "react-dom": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/get-nonce": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/get-nonce/-/get-nonce-1.0.1.tgz",
+ "integrity": "sha512-FJhYRoDaiatfEkUK8HKlicmu/3SGFD51q3itKDGoSTysQJBnfOcxU5GxnhE1E6soB76MbT0MBtnKJuXyAx+96Q==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/graceful-fs": {
+ "version": "4.2.11",
+ "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+ "dev": true,
+ "license": "ISC"
+ },
+ "node_modules/has-flag": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/hast-util-from-parse5": {
+ "version": "8.0.3",
+ "resolved": "https://registry.npmjs.org/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz",
+ "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "devlop": "^1.0.0",
+ "hastscript": "^9.0.0",
+ "property-information": "^7.0.0",
+ "vfile": "^6.0.0",
+ "vfile-location": "^5.0.0",
+ "web-namespaces": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-is-element": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-is-element/-/hast-util-is-element-3.0.0.tgz",
+ "integrity": "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-parse-selector": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz",
+ "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-raw": {
+ "version": "9.1.0",
+ "resolved": "https://registry.npmjs.org/hast-util-raw/-/hast-util-raw-9.1.0.tgz",
+ "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "hast-util-from-parse5": "^8.0.0",
+ "hast-util-to-parse5": "^8.0.0",
+ "html-void-elements": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "parse5": "^7.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-jsx-runtime": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "estree-util-is-identifier-name": "^3.0.0",
+ "hast-util-whitespace": "^3.0.0",
+ "mdast-util-mdx-expression": "^2.0.0",
+ "mdast-util-mdx-jsx": "^3.0.0",
+ "mdast-util-mdxjs-esm": "^2.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "style-to-js": "^1.0.0",
+ "unist-util-position": "^5.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5": {
+ "version": "8.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz",
+ "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "devlop": "^1.0.0",
+ "property-information": "^6.0.0",
+ "space-separated-tokens": "^2.0.0",
+ "web-namespaces": "^2.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-to-parse5/node_modules/property-information": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-6.5.0.tgz",
+ "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/hast-util-to-text": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/hast-util-to-text/-/hast-util-to-text-4.0.2.tgz",
+ "integrity": "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/unist": "^3.0.0",
+ "hast-util-is-element": "^3.0.0",
+ "unist-util-find-after": "^5.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hast-util-whitespace": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/hastscript": {
+ "version": "9.0.1",
+ "resolved": "https://registry.npmjs.org/hastscript/-/hastscript-9.0.1.tgz",
+ "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "comma-separated-tokens": "^2.0.0",
+ "hast-util-parse-selector": "^4.0.0",
+ "property-information": "^7.0.0",
+ "space-separated-tokens": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/highlight.js": {
+ "version": "11.11.1",
+ "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz",
+ "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=12.0.0"
+ }
+ },
+ "node_modules/hoist-non-react-statics": {
+ "version": "3.3.2",
+ "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz",
+ "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==",
+ "license": "BSD-3-Clause",
+ "dependencies": {
+ "react-is": "^16.7.0"
+ }
+ },
+ "node_modules/html-url-attributes": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/html-void-elements": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/html-void-elements/-/html-void-elements-3.0.0.tgz",
+ "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/inline-style-parser": {
+ "version": "0.2.4",
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
+ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
+ "license": "MIT"
+ },
+ "node_modules/is-alphabetical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-alphanumerical": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
+ "license": "MIT",
+ "dependencies": {
+ "is-alphabetical": "^2.0.0",
+ "is-decimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-arrayish": {
+ "version": "0.3.2",
+ "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz",
+ "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==",
+ "license": "MIT",
+ "optional": true
+ },
+ "node_modules/is-decimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-hexadecimal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/is-plain-obj": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/jiti": {
+ "version": "2.4.2",
+ "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+ "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "jiti": "lib/jiti-cli.mjs"
+ }
+ },
+ "node_modules/js-tiktoken": {
+ "version": "1.0.20",
+ "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.20.tgz",
+ "integrity": "sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A==",
+ "license": "MIT",
+ "dependencies": {
+ "base64-js": "^1.5.1"
+ }
+ },
+ "node_modules/langsmith": {
+ "version": "0.3.37",
+ "resolved": "https://registry.npmjs.org/langsmith/-/langsmith-0.3.37.tgz",
+ "integrity": "sha512-aDFM+LbT01gP8hsJNs4QJjmbRNfoifqhpCSpk8j4k/V8wejEgvgATbgj9W9DQsfQFdtfwx+8G48sK5/0PqQisg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/uuid": "^10.0.0",
+ "chalk": "^4.1.2",
+ "console-table-printer": "^2.12.1",
+ "p-queue": "^6.6.2",
+ "p-retry": "4",
+ "semver": "^7.6.3",
+ "uuid": "^10.0.0"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "*",
+ "@opentelemetry/exporter-trace-otlp-proto": "*",
+ "@opentelemetry/sdk-trace-base": "*",
+ "openai": "*"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@opentelemetry/exporter-trace-otlp-proto": {
+ "optional": true
+ },
+ "@opentelemetry/sdk-trace-base": {
+ "optional": true
+ },
+ "openai": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/langsmith/node_modules/uuid": {
+ "version": "10.0.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz",
+ "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/bin/uuid"
+ }
+ },
+ "node_modules/lightningcss": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.30.1.tgz",
+ "integrity": "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==",
+ "dev": true,
+ "license": "MPL-2.0",
+ "dependencies": {
+ "detect-libc": "^2.0.3"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ },
+ "optionalDependencies": {
+ "lightningcss-darwin-arm64": "1.30.1",
+ "lightningcss-darwin-x64": "1.30.1",
+ "lightningcss-freebsd-x64": "1.30.1",
+ "lightningcss-linux-arm-gnueabihf": "1.30.1",
+ "lightningcss-linux-arm64-gnu": "1.30.1",
+ "lightningcss-linux-arm64-musl": "1.30.1",
+ "lightningcss-linux-x64-gnu": "1.30.1",
+ "lightningcss-linux-x64-musl": "1.30.1",
+ "lightningcss-win32-arm64-msvc": "1.30.1",
+ "lightningcss-win32-x64-msvc": "1.30.1"
+ }
+ },
+ "node_modules/lightningcss-darwin-arm64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.30.1.tgz",
+ "integrity": "sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-darwin-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.30.1.tgz",
+ "integrity": "sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-freebsd-x64": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.30.1.tgz",
+ "integrity": "sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm-gnueabihf": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.30.1.tgz",
+ "integrity": "sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.30.1.tgz",
+ "integrity": "sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-arm64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.30.1.tgz",
+ "integrity": "sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-gnu": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.30.1.tgz",
+ "integrity": "sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-linux-x64-musl": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.30.1.tgz",
+ "integrity": "sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-arm64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.30.1.tgz",
+ "integrity": "sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/lightningcss-win32-x64-msvc": {
+ "version": "1.30.1",
+ "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.30.1.tgz",
+ "integrity": "sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "MPL-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/parcel"
+ }
+ },
+ "node_modules/longest-streak": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lowlight": {
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/lowlight/-/lowlight-3.3.0.tgz",
+ "integrity": "sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "devlop": "^1.0.0",
+ "highlight.js": "~11.11.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "0.515.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.515.0.tgz",
+ "integrity": "sha512-Sy7bY0MeicRm2pzrnoHm2h6C1iVoeHyBU2fjdQDsXGP51fhkhau1/ZV/dzrcxEmAKsxYb6bGaIsMnGHuQ5s0dw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/magic-string": {
+ "version": "0.30.17",
+ "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.17.tgz",
+ "integrity": "sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0"
+ }
+ },
+ "node_modules/markdown-table": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/mdast-util-find-and-replace": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "escape-string-regexp": "^5.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-from-markdown": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark": "^4.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
+ "license": "MIT",
+ "dependencies": {
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
+ "mdast-util-gfm-footnote": "^2.0.0",
+ "mdast-util-gfm-strikethrough": "^2.0.0",
+ "mdast-util-gfm-table": "^2.0.0",
+ "mdast-util-gfm-task-list-item": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-autolink-literal": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-find-and-replace": "^3.0.0",
+ "micromark-util-character": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-strikethrough": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-table": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "markdown-table": "^3.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-gfm-task-list-item": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-expression": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdx-jsx": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "ccount": "^2.0.0",
+ "devlop": "^1.1.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "parse-entities": "^4.0.0",
+ "stringify-entities": "^4.0.0",
+ "unist-util-stringify-position": "^4.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-mdxjs-esm": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree-jsx": "^1.0.0",
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "mdast-util-to-markdown": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-phrasing": {
+ "version": "4.1.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-hast": {
+ "version": "13.2.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
+ "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "@ungap/structured-clone": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "trim-lines": "^3.0.0",
+ "unist-util-position": "^5.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-markdown": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "@types/unist": "^3.0.0",
+ "longest-streak": "^3.0.0",
+ "mdast-util-phrasing": "^4.0.0",
+ "mdast-util-to-string": "^4.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-decode-string": "^2.0.0",
+ "unist-util-visit": "^5.0.0",
+ "zwitch": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/mdast-util-to-string": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "@types/debug": "^4.0.0",
+ "debug": "^4.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-core-commonmark": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "devlop": "^1.0.0",
+ "micromark-factory-destination": "^2.0.0",
+ "micromark-factory-label": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-factory-title": "^2.0.0",
+ "micromark-factory-whitespace": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-html-tag-name": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-subtokenize": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-extension-gfm": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
+ "micromark-extension-gfm-footnote": "^2.0.0",
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
+ "micromark-extension-gfm-table": "^2.0.0",
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
+ "micromark-util-combine-extensions": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-autolink-literal": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-footnote": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-core-commonmark": "^2.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-normalize-identifier": "^2.0.0",
+ "micromark-util-sanitize-uri": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-strikethrough": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-classify-character": "^2.0.0",
+ "micromark-util-resolve-all": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-table": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-tagfilter": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-extension-gfm-task-list-item": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/micromark-factory-destination": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-label": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-space": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-title": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-factory-whitespace": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-factory-space": "^2.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-character": {
+ "version": "2.1.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-chunked": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-classify-character": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-combine-extensions": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-numeric-character-reference": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-decode-string": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "decode-named-character-reference": "^1.0.0",
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-encode": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-html-tag-name": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-normalize-identifier": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-resolve-all": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-sanitize-uri": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "micromark-util-character": "^2.0.0",
+ "micromark-util-encode": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-subtokenize": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "devlop": "^1.0.0",
+ "micromark-util-chunked": "^2.0.0",
+ "micromark-util-symbol": "^2.0.0",
+ "micromark-util-types": "^2.0.0"
+ }
+ },
+ "node_modules/micromark-util-symbol": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/micromark-util-types": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
+ "funding": [
+ {
+ "type": "GitHub Sponsors",
+ "url": "https://github.com/sponsors/unifiedjs"
+ },
+ {
+ "type": "OpenCollective",
+ "url": "https://opencollective.com/unified"
+ }
+ ],
+ "license": "MIT"
+ },
+ "node_modules/minipass": {
+ "version": "7.1.2",
+ "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz",
+ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==",
+ "dev": true,
+ "license": "ISC",
+ "engines": {
+ "node": ">=16 || 14 >=14.17"
+ }
+ },
+ "node_modules/minizlib": {
+ "version": "3.0.2",
+ "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.0.2.tgz",
+ "integrity": "sha512-oG62iEk+CYt5Xj2YqI5Xi9xWUeZhDI8jjQmC5oThVH5JGCTgIjr7ciJDzC7MBzYd//WvR1OTmP5Q38Q8ShQtVA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "minipass": "^7.1.2"
+ },
+ "engines": {
+ "node": ">= 18"
+ }
+ },
+ "node_modules/mkdirp": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-3.0.1.tgz",
+ "integrity": "sha512-+NsyUUAZDmo6YVHzL/stxSu3t9YS1iljliy3BSDrXJ/dkn1KYdmtZODGGjLcc9XLgVVpH4KshHB8XmZgMhaBXg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "mkdirp": "dist/cjs/src/bin.js"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/isaacs"
+ }
+ },
+ "node_modules/motion-dom": {
+ "version": "12.23.9",
+ "resolved": "https://registry.npmjs.org/motion-dom/-/motion-dom-12.23.9.tgz",
+ "integrity": "sha512-6Sv++iWS8XMFCgU1qwKj9l4xuC47Hp4+2jvPfyTXkqDg2tTzSgX6nWKD4kNFXk0k7llO59LZTPuJigza4A2K1A==",
+ "license": "MIT",
+ "dependencies": {
+ "motion-utils": "^12.23.6"
+ }
+ },
+ "node_modules/motion-utils": {
+ "version": "12.23.6",
+ "resolved": "https://registry.npmjs.org/motion-utils/-/motion-utils-12.23.6.tgz",
+ "integrity": "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ==",
+ "license": "MIT"
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/mustache": {
+ "version": "4.2.0",
+ "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz",
+ "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==",
+ "license": "MIT",
+ "bin": {
+ "mustache": "bin/mustache"
+ }
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.11",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/next": {
+ "version": "15.3.3",
+ "resolved": "https://registry.npmjs.org/next/-/next-15.3.3.tgz",
+ "integrity": "sha512-JqNj29hHNmCLtNvd090SyRbXJiivQ+58XjCcrC50Crb5g5u2zi7Y2YivbsEfzk6AtVI80akdOQbaMZwWB1Hthw==",
+ "license": "MIT",
+ "dependencies": {
+ "@next/env": "15.3.3",
+ "@swc/counter": "0.1.3",
+ "@swc/helpers": "0.5.15",
+ "busboy": "1.6.0",
+ "caniuse-lite": "^1.0.30001579",
+ "postcss": "8.4.31",
+ "styled-jsx": "5.1.6"
+ },
+ "bin": {
+ "next": "dist/bin/next"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^19.8.0 || >= 20.0.0"
+ },
+ "optionalDependencies": {
+ "@next/swc-darwin-arm64": "15.3.3",
+ "@next/swc-darwin-x64": "15.3.3",
+ "@next/swc-linux-arm64-gnu": "15.3.3",
+ "@next/swc-linux-arm64-musl": "15.3.3",
+ "@next/swc-linux-x64-gnu": "15.3.3",
+ "@next/swc-linux-x64-musl": "15.3.3",
+ "@next/swc-win32-arm64-msvc": "15.3.3",
+ "@next/swc-win32-x64-msvc": "15.3.3",
+ "sharp": "^0.34.1"
+ },
+ "peerDependencies": {
+ "@opentelemetry/api": "^1.1.0",
+ "@playwright/test": "^1.41.2",
+ "babel-plugin-react-compiler": "*",
+ "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0",
+ "sass": "^1.3.0"
+ },
+ "peerDependenciesMeta": {
+ "@opentelemetry/api": {
+ "optional": true
+ },
+ "@playwright/test": {
+ "optional": true
+ },
+ "babel-plugin-react-compiler": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/next-themes": {
+ "version": "0.4.6",
+ "resolved": "https://registry.npmjs.org/next-themes/-/next-themes-0.4.6.tgz",
+ "integrity": "sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc",
+ "react-dom": "^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/next/node_modules/postcss": {
+ "version": "8.4.31",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz",
+ "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.6",
+ "picocolors": "^1.0.0",
+ "source-map-js": "^1.0.2"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/p-finally": {
+ "version": "1.0.0",
+ "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
+ "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=4"
+ }
+ },
+ "node_modules/p-queue": {
+ "version": "6.6.2",
+ "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz",
+ "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==",
+ "license": "MIT",
+ "dependencies": {
+ "eventemitter3": "^4.0.4",
+ "p-timeout": "^3.2.0"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
+ }
+ },
+ "node_modules/p-retry": {
+ "version": "4.6.2",
+ "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz",
+ "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/retry": "0.12.0",
+ "retry": "^0.13.1"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/p-timeout": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz",
+ "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==",
+ "license": "MIT",
+ "dependencies": {
+ "p-finally": "^1.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/parse-entities": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^2.0.0",
+ "character-entities-legacy": "^3.0.0",
+ "character-reference-invalid": "^2.0.0",
+ "decode-named-character-reference": "^1.0.0",
+ "is-alphanumerical": "^2.0.0",
+ "is-decimal": "^2.0.0",
+ "is-hexadecimal": "^2.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/parse-entities/node_modules/@types/unist": {
+ "version": "2.0.11",
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
+ "license": "MIT"
+ },
+ "node_modules/parse5": {
+ "version": "7.3.0",
+ "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz",
+ "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==",
+ "license": "MIT",
+ "dependencies": {
+ "entities": "^6.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/inikulin/parse5?sponsor=1"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/postcss": {
+ "version": "8.5.5",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.5.tgz",
+ "integrity": "sha512-d/jtm+rdNT8tpXuHY5MMtcbJFBkhXE6593XVR9UoGCH8jSFGci7jGvMGH5RYd5PBJW+00NZQt6gf7CbagJCrhg==",
+ "dev": true,
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.11",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/property-information": {
+ "version": "7.1.0",
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz",
+ "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dnd": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/react-dnd/-/react-dnd-16.0.1.tgz",
+ "integrity": "sha512-QeoM/i73HHu2XF9aKksIUuamHPDvRglEwdHL4jsp784BgUuWcg6mzfxT0QDdQz8Wj0qyRKx2eMg8iZtWvU4E2Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@react-dnd/invariant": "^4.0.1",
+ "@react-dnd/shallowequal": "^4.0.1",
+ "dnd-core": "^16.0.1",
+ "fast-deep-equal": "^3.1.3",
+ "hoist-non-react-statics": "^3.3.2"
+ },
+ "peerDependencies": {
+ "@types/hoist-non-react-statics": ">= 3.3.1",
+ "@types/node": ">= 12",
+ "@types/react": ">= 16",
+ "react": ">= 16.14"
+ },
+ "peerDependenciesMeta": {
+ "@types/hoist-non-react-statics": {
+ "optional": true
+ },
+ "@types/node": {
+ "optional": true
+ },
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-dnd-html5-backend": {
+ "version": "16.0.1",
+ "resolved": "https://registry.npmjs.org/react-dnd-html5-backend/-/react-dnd-html5-backend-16.0.1.tgz",
+ "integrity": "sha512-Wu3dw5aDJmOGw8WjH1I1/yTH+vlXEL4vmjk5p+MHxP8HuHJS1lAGeIdG/hze1AvNeXWo/JgULV87LyQOr+r5jw==",
+ "license": "MIT",
+ "dependencies": {
+ "dnd-core": "^16.0.1"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.1.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.1.0.tgz",
+ "integrity": "sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.26.0"
+ },
+ "peerDependencies": {
+ "react": "^19.1.0"
+ }
+ },
+ "node_modules/react-is": {
+ "version": "16.13.1",
+ "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
+ "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==",
+ "license": "MIT"
+ },
+ "node_modules/react-markdown": {
+ "version": "10.1.0",
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "devlop": "^1.0.0",
+ "hast-util-to-jsx-runtime": "^2.0.0",
+ "html-url-attributes": "^3.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-rehype": "^11.0.0",
+ "unified": "^11.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ },
+ "peerDependencies": {
+ "@types/react": ">=18",
+ "react": ">=18"
+ }
+ },
+ "node_modules/react-remove-scroll": {
+ "version": "2.7.1",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll/-/react-remove-scroll-2.7.1.tgz",
+ "integrity": "sha512-HpMh8+oahmIdOuS5aFKKY6Pyog+FNaZV/XyJOq7b4YFwsFHe5yYfdbIalI4k3vU2nSDql7YskmUseHsRrJqIPA==",
+ "license": "MIT",
+ "dependencies": {
+ "react-remove-scroll-bar": "^2.3.7",
+ "react-style-singleton": "^2.2.3",
+ "tslib": "^2.1.0",
+ "use-callback-ref": "^1.3.3",
+ "use-sidecar": "^1.1.3"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-remove-scroll-bar": {
+ "version": "2.3.8",
+ "resolved": "https://registry.npmjs.org/react-remove-scroll-bar/-/react-remove-scroll-bar-2.3.8.tgz",
+ "integrity": "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q==",
+ "license": "MIT",
+ "dependencies": {
+ "react-style-singleton": "^2.2.2",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/react-resizable-panels": {
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/react-resizable-panels/-/react-resizable-panels-3.0.3.tgz",
+ "integrity": "sha512-7HA8THVBHTzhDK4ON0tvlGXyMAJN1zBeRpuyyremSikgYh2ku6ltD7tsGQOcXx4NKPrZtYCm/5CBr+dkruTGQw==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/react-style-singleton": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/react-style-singleton/-/react-style-singleton-2.2.3.tgz",
+ "integrity": "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ==",
+ "license": "MIT",
+ "dependencies": {
+ "get-nonce": "^1.0.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/redux": {
+ "version": "4.2.1",
+ "resolved": "https://registry.npmjs.org/redux/-/redux-4.2.1.tgz",
+ "integrity": "sha512-LAUYz4lc+Do8/g7aeRa8JkyDErK6ekstQaqWQrNRW//MY1TvCEpMtpTWvlQ+FPbWCx+Xixu/6SHt5N0HR+SB4w==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/runtime": "^7.9.2"
+ }
+ },
+ "node_modules/rehype-highlight": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/rehype-highlight/-/rehype-highlight-7.0.2.tgz",
+ "integrity": "sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-to-text": "^4.0.0",
+ "lowlight": "^3.0.0",
+ "unist-util-visit": "^5.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/rehype-raw": {
+ "version": "7.0.0",
+ "resolved": "https://registry.npmjs.org/rehype-raw/-/rehype-raw-7.0.0.tgz",
+ "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "hast-util-raw": "^9.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-gfm": {
+ "version": "4.0.1",
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-gfm": "^3.0.0",
+ "micromark-extension-gfm": "^3.0.0",
+ "remark-parse": "^11.0.0",
+ "remark-stringify": "^11.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-parse": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-from-markdown": "^2.0.0",
+ "micromark-util-types": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-rehype": {
+ "version": "11.1.2",
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/hast": "^3.0.0",
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-hast": "^13.0.0",
+ "unified": "^11.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/remark-stringify": {
+ "version": "11.0.0",
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/mdast": "^4.0.0",
+ "mdast-util-to-markdown": "^2.0.0",
+ "unified": "^11.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/retry": {
+ "version": "0.13.1",
+ "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
+ "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.26.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.26.0.tgz",
+ "integrity": "sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "7.7.2",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz",
+ "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
+ "node_modules/sharp": {
+ "version": "0.34.2",
+ "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.2.tgz",
+ "integrity": "sha512-lszvBmB9QURERtyKT2bNmsgxXK0ShJrL/fvqlonCo7e6xBF8nT8xU6pW+PMIbLsz0RxQk3rgH9kd8UmvOzlMJg==",
+ "hasInstallScript": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "dependencies": {
+ "color": "^4.2.3",
+ "detect-libc": "^2.0.4",
+ "semver": "^7.7.2"
+ },
+ "engines": {
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-darwin-arm64": "0.34.2",
+ "@img/sharp-darwin-x64": "0.34.2",
+ "@img/sharp-libvips-darwin-arm64": "1.1.0",
+ "@img/sharp-libvips-darwin-x64": "1.1.0",
+ "@img/sharp-libvips-linux-arm": "1.1.0",
+ "@img/sharp-libvips-linux-arm64": "1.1.0",
+ "@img/sharp-libvips-linux-ppc64": "1.1.0",
+ "@img/sharp-libvips-linux-s390x": "1.1.0",
+ "@img/sharp-libvips-linux-x64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-arm64": "1.1.0",
+ "@img/sharp-libvips-linuxmusl-x64": "1.1.0",
+ "@img/sharp-linux-arm": "0.34.2",
+ "@img/sharp-linux-arm64": "0.34.2",
+ "@img/sharp-linux-s390x": "0.34.2",
+ "@img/sharp-linux-x64": "0.34.2",
+ "@img/sharp-linuxmusl-arm64": "0.34.2",
+ "@img/sharp-linuxmusl-x64": "0.34.2",
+ "@img/sharp-wasm32": "0.34.2",
+ "@img/sharp-win32-arm64": "0.34.2",
+ "@img/sharp-win32-ia32": "0.34.2",
+ "@img/sharp-win32-x64": "0.34.2"
+ }
+ },
+ "node_modules/simple-swizzle": {
+ "version": "0.2.2",
+ "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz",
+ "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==",
+ "license": "MIT",
+ "optional": true,
+ "dependencies": {
+ "is-arrayish": "^0.3.1"
+ }
+ },
+ "node_modules/simple-wcswidth": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/simple-wcswidth/-/simple-wcswidth-1.1.2.tgz",
+ "integrity": "sha512-j7piyCjAeTDSjzTSQ7DokZtMNwNlEAyxqSZeCS+CXH7fJ4jx3FuJ/mTW3mE+6JLs4VJBbcll0Kjn+KXI5t21Iw==",
+ "license": "MIT"
+ },
+ "node_modules/sonner": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/sonner/-/sonner-2.0.5.tgz",
+ "integrity": "sha512-YwbHQO6cSso3HBXlbCkgrgzDNIhws14r4MO87Ofy+cV2X7ES4pOoAK3+veSmVTvqNx1BWUxlhPmZzP00Crk2aQ==",
+ "license": "MIT",
+ "peerDependencies": {
+ "react": "^18.0.0 || ^19.0.0 || ^19.0.0-rc",
+ "react-dom": "^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/space-separated-tokens": {
+ "version": "2.0.2",
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/streamsearch": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
+ "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
+ "engines": {
+ "node": ">=10.0.0"
+ }
+ },
+ "node_modules/stringify-entities": {
+ "version": "4.0.4",
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
+ "license": "MIT",
+ "dependencies": {
+ "character-entities-html4": "^2.0.0",
+ "character-entities-legacy": "^3.0.0"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/style-to-js": {
+ "version": "1.1.17",
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
+ "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
+ "license": "MIT",
+ "dependencies": {
+ "style-to-object": "1.0.9"
+ }
+ },
+ "node_modules/style-to-object": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
+ "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
+ "license": "MIT",
+ "dependencies": {
+ "inline-style-parser": "0.2.4"
+ }
+ },
+ "node_modules/styled-jsx": {
+ "version": "5.1.6",
+ "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz",
+ "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==",
+ "license": "MIT",
+ "dependencies": {
+ "client-only": "0.0.1"
+ },
+ "engines": {
+ "node": ">= 12.0.0"
+ },
+ "peerDependencies": {
+ "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0"
+ },
+ "peerDependenciesMeta": {
+ "@babel/core": {
+ "optional": true
+ },
+ "babel-plugin-macros": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/supports-color": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+ "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+ "license": "MIT",
+ "dependencies": {
+ "has-flag": "^4.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/tailwind-merge": {
+ "version": "3.3.1",
+ "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.3.1.tgz",
+ "integrity": "sha512-gBXpgUm/3rp1lMZZrM/w7D8GKqshif0zAymAhbCyIt8KMe+0v9DQ7cdYLR4FHH/cKpdTXb+A/tKKU3eolfsI+g==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/dcastil"
+ }
+ },
+ "node_modules/tailwindcss": {
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.10.tgz",
+ "integrity": "sha512-P3nr6WkvKV/ONsTzj6Gb57sWPMX29EPNPopo7+FcpkQaNsrNpZ1pv8QmrYI2RqEKD7mlGqLnGovlcYnBK0IqUA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/tapable": {
+ "version": "2.2.2",
+ "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.2.tgz",
+ "integrity": "sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/tar": {
+ "version": "7.4.3",
+ "resolved": "https://registry.npmjs.org/tar/-/tar-7.4.3.tgz",
+ "integrity": "sha512-5S7Va8hKfV7W5U6g3aYxXmlPoZVAwUMy9AOKyF2fVuZa2UD3qZjg578OrLRt8PcNN1PleVaL/5/yYATNL0ICUw==",
+ "dev": true,
+ "license": "ISC",
+ "dependencies": {
+ "@isaacs/fs-minipass": "^4.0.0",
+ "chownr": "^3.0.0",
+ "minipass": "^7.1.2",
+ "minizlib": "^3.0.1",
+ "mkdirp": "^3.0.1",
+ "yallist": "^5.0.0"
+ },
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/trim-lines": {
+ "version": "3.0.1",
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/trough": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/tslib": {
+ "version": "2.8.1",
+ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz",
+ "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
+ "license": "0BSD"
+ },
+ "node_modules/tw-animate-css": {
+ "version": "1.3.4",
+ "resolved": "https://registry.npmjs.org/tw-animate-css/-/tw-animate-css-1.3.4.tgz",
+ "integrity": "sha512-dd1Ht6/YQHcNbq0znIT6dG8uhO7Ce+VIIhZUhjsryXsMPJQz3bZg7Q2eNzLwipb25bRZslGb2myio5mScd1TFg==",
+ "dev": true,
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/Wombosvideo"
+ }
+ },
+ "node_modules/typescript": {
+ "version": "5.8.3",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz",
+ "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "tsc": "bin/tsc",
+ "tsserver": "bin/tsserver"
+ },
+ "engines": {
+ "node": ">=14.17"
+ }
+ },
+ "node_modules/undici-types": {
+ "version": "6.21.0",
+ "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz",
+ "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==",
+ "devOptional": true,
+ "license": "MIT"
+ },
+ "node_modules/unified": {
+ "version": "11.0.5",
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "bail": "^2.0.0",
+ "devlop": "^1.0.0",
+ "extend": "^3.0.0",
+ "is-plain-obj": "^4.0.0",
+ "trough": "^2.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-find-after": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-find-after/-/unist-util-find-after-5.0.0.tgz",
+ "integrity": "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-is": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-position": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-stringify-position": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0",
+ "unist-util-visit-parents": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/unist-util-visit-parents": {
+ "version": "6.0.1",
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-is": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/use-callback-ref": {
+ "version": "1.3.3",
+ "resolved": "https://registry.npmjs.org/use-callback-ref/-/use-callback-ref-1.3.3.tgz",
+ "integrity": "sha512-jQL3lRnocaFtu3V00JToYz/4QkNWswxijDaCVNZRiRTO3HQDLsdu1ZtmIUvV4yPp+rvWm5j0y0TG/S61cuijTg==",
+ "license": "MIT",
+ "dependencies": {
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/use-sidecar": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/use-sidecar/-/use-sidecar-1.1.3.tgz",
+ "integrity": "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ==",
+ "license": "MIT",
+ "dependencies": {
+ "detect-node-es": "^1.1.0",
+ "tslib": "^2.0.0"
+ },
+ "engines": {
+ "node": ">=10"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/uuid": {
+ "version": "11.1.0",
+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-11.1.0.tgz",
+ "integrity": "sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A==",
+ "funding": [
+ "https://github.com/sponsors/broofa",
+ "https://github.com/sponsors/ctavan"
+ ],
+ "license": "MIT",
+ "bin": {
+ "uuid": "dist/esm/bin/uuid"
+ }
+ },
+ "node_modules/vfile": {
+ "version": "6.0.3",
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile-message": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-location": {
+ "version": "5.0.3",
+ "resolved": "https://registry.npmjs.org/vfile-location/-/vfile-location-5.0.3.tgz",
+ "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "vfile": "^6.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/vfile-message": {
+ "version": "4.0.2",
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/unist": "^3.0.0",
+ "unist-util-stringify-position": "^4.0.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/unified"
+ }
+ },
+ "node_modules/web-namespaces": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz",
+ "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ },
+ "node_modules/yallist": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz",
+ "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/zod": {
+ "version": "3.25.67",
+ "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.67.tgz",
+ "integrity": "sha512-idA2YXwpCdqUSKRCACDE6ItZD9TZzy3OZMtpfLoh6oPR47lipysRrJfjzMqFxQ3uJuUPyUeWe1r9vLH33xO/Qw==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://github.com/sponsors/colinhacks"
+ }
+ },
+ "node_modules/zod-to-json-schema": {
+ "version": "3.24.5",
+ "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz",
+ "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==",
+ "license": "ISC",
+ "peerDependencies": {
+ "zod": "^3.24.1"
+ }
+ },
+ "node_modules/zwitch": {
+ "version": "2.0.4",
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
+ "license": "MIT",
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/wooorm"
+ }
+ }
+ }
+}
diff --git a/devboard/package.json b/devboard/package.json
new file mode 100644
index 0000000..14e9f7c
--- /dev/null
+++ b/devboard/package.json
@@ -0,0 +1,56 @@
+{
+ "name": "devboard",
+ "version": "0.1.0",
+ "private": true,
+ "scripts": {
+ "dev": "next dev --turbopack",
+ "build": "next build",
+ "start": "next start",
+ "lint": "next lint"
+ },
+ "dependencies": {
+ "@google/generative-ai": "^0.24.1",
+ "@langchain/core": "^0.3.61",
+ "@langchain/google-genai": "^0.2.14",
+ "@langchain/langgraph": "^0.3.6",
+ "@radix-ui/react-collapsible": "^1.1.11",
+ "@radix-ui/react-dialog": "^1.1.14",
+ "@radix-ui/react-label": "^2.1.7",
+ "@radix-ui/react-progress": "^1.1.7",
+ "@radix-ui/react-scroll-area": "^1.2.9",
+ "@radix-ui/react-select": "^2.2.5",
+ "@radix-ui/react-separator": "^1.1.7",
+ "@radix-ui/react-slider": "^1.3.5",
+ "@radix-ui/react-slot": "^1.2.3",
+ "@radix-ui/react-switch": "^1.2.5",
+ "@radix-ui/react-tabs": "^1.1.12",
+ "class-variance-authority": "^0.7.1",
+ "clsx": "^2.1.1",
+ "framer-motion": "^12.23.9",
+ "lucide-react": "^0.515.0",
+ "next": "15.3.3",
+ "next-themes": "^0.4.6",
+ "react": "^19.0.0",
+ "react-dnd": "^16.0.1",
+ "react-dnd-html5-backend": "^16.0.1",
+ "react-dom": "^19.0.0",
+ "react-markdown": "^10.1.0",
+ "react-resizable-panels": "^3.0.3",
+ "rehype-highlight": "^7.0.2",
+ "rehype-raw": "^7.0.0",
+ "remark-gfm": "^4.0.1",
+ "sonner": "^2.0.5",
+ "tailwind-merge": "^3.3.1",
+ "uuid": "^11.1.0",
+ "zod": "^3.25.67"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4",
+ "@types/node": "^20",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "tailwindcss": "^4",
+ "tw-animate-css": "^1.3.4",
+ "typescript": "^5"
+ }
+}
diff --git a/devboard/postcss.config.mjs b/devboard/postcss.config.mjs
new file mode 100644
index 0000000..c7bcb4b
--- /dev/null
+++ b/devboard/postcss.config.mjs
@@ -0,0 +1,5 @@
+const config = {
+ plugins: ["@tailwindcss/postcss"],
+};
+
+export default config;
diff --git a/devboard/public/file.svg b/devboard/public/file.svg
new file mode 100644
index 0000000..004145c
--- /dev/null
+++ b/devboard/public/file.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/devboard/public/globe.svg b/devboard/public/globe.svg
new file mode 100644
index 0000000..567f17b
--- /dev/null
+++ b/devboard/public/globe.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/devboard/public/next.svg b/devboard/public/next.svg
new file mode 100644
index 0000000..5174b28
--- /dev/null
+++ b/devboard/public/next.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/devboard/public/vercel.svg b/devboard/public/vercel.svg
new file mode 100644
index 0000000..7705396
--- /dev/null
+++ b/devboard/public/vercel.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/devboard/public/window.svg b/devboard/public/window.svg
new file mode 100644
index 0000000..b2b2a44
--- /dev/null
+++ b/devboard/public/window.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/devboard/tsconfig.json b/devboard/tsconfig.json
new file mode 100644
index 0000000..d8b9323
--- /dev/null
+++ b/devboard/tsconfig.json
@@ -0,0 +1,27 @@
+{
+ "compilerOptions": {
+ "target": "ES2017",
+ "lib": ["dom", "dom.iterable", "esnext"],
+ "allowJs": true,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "preserve",
+ "incremental": true,
+ "plugins": [
+ {
+ "name": "next"
+ }
+ ],
+ "paths": {
+ "@/*": ["./*"]
+ }
+ },
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
+ "exclude": ["node_modules"]
+}
diff --git a/devboard/types/widget.ts b/devboard/types/widget.ts
new file mode 100644
index 0000000..f7e31bd
--- /dev/null
+++ b/devboard/types/widget.ts
@@ -0,0 +1,76 @@
+export interface WidgetElement {
+ id: string
+ type: "text" | "image" | "chart" | "stats" | "social" | "badge" | "progress" | "container"
+ position: { x: number; y: number }
+ size: { width: number; height: number }
+ style: {
+ backgroundColor?: string
+ color?: string
+ fontSize?: number
+ fontWeight?: string
+ borderRadius?: number
+ border?: string
+ padding?: number
+ margin?: number
+ textAlign?: "left" | "center" | "right"
+ opacity?: number
+ boxShadow?: string
+ }
+ content: {
+ text?: string
+ imageUrl?: string
+ chartType?: "bar" | "line" | "pie" | "doughnut"
+ chartData?: any
+ socialPlatform?: "github" | "twitter" | "linkedin"
+ badgeType?: "shield" | "flat" | "plastic"
+ progressValue?: number
+ progressMax?: number
+ }
+ animation?: {
+ type: "fade" | "slide" | "bounce" | "pulse"
+ duration: number
+ delay: number
+ }
+ responsive?: {
+ mobile: Partial
+ tablet: Partial
+ }
+}
+
+export interface Widget {
+ id: string
+ name: string
+ description: string
+ category: string
+ elements: WidgetElement[]
+ canvas: {
+ width: number
+ height: number
+ backgroundColor: string
+ }
+ metadata: {
+ createdAt: string
+ updatedAt: string
+ version: string
+ tags: string[]
+ author: string
+ downloads: number
+ rating: number
+ }
+ settings: {
+ isPublic: boolean
+ allowCustomization: boolean
+ requiresAuth: boolean
+ apiEndpoint?: string
+ }
+}
+
+export interface WidgetTemplate {
+ id: string
+ name: string
+ description: string
+ category: string
+ preview: string
+ elements: Omit[]
+ popularity: number
+}