commit a058ff225ea654a82676a31969d92a39553844cd Author: 史悦 Date: Wed Aug 27 17:30:13 2025 +0800 Initial commit: SuperDesign project diff --git a/.cursor/mcp.json b/.cursor/mcp.json new file mode 100644 index 0000000..7e49eb3 --- /dev/null +++ b/.cursor/mcp.json @@ -0,0 +1,23 @@ +{ + "mcpServers": { + "task-master-ai": { + "command": "npx", + "args": [ + "-y", + "--package=task-master-ai", + "task-master-ai" + ], + "env": { + "ANTHROPIC_API_KEY": "ANTHROPIC_API_KEY_HERE", + "PERPLEXITY_API_KEY": "PERPLEXITY_API_KEY_HERE", + "OPENAI_API_KEY": "OPENAI_API_KEY_HERE", + "GOOGLE_API_KEY": "GOOGLE_API_KEY_HERE", + "XAI_API_KEY": "XAI_API_KEY_HERE", + "OPENROUTER_API_KEY": "OPENROUTER_API_KEY_HERE", + "MISTRAL_API_KEY": "MISTRAL_API_KEY_HERE", + "AZURE_OPENAI_API_KEY": "AZURE_OPENAI_API_KEY_HERE", + "OLLAMA_API_KEY": "OLLAMA_API_KEY_HERE" + } + } + } +} \ No newline at end of file diff --git a/.cursor/rules/cursor_rules.mdc b/.cursor/rules/cursor_rules.mdc new file mode 100644 index 0000000..7dfae3d --- /dev/null +++ b/.cursor/rules/cursor_rules.mdc @@ -0,0 +1,53 @@ +--- +description: Guidelines for creating and maintaining Cursor rules to ensure consistency and effectiveness. +globs: .cursor/rules/*.mdc +alwaysApply: true +--- + +- **Required Rule Structure:** + ```markdown + --- + description: Clear, one-line description of what the rule enforces + globs: path/to/files/*.ext, other/path/**/* + alwaysApply: boolean + --- + + - **Main Points in Bold** + - Sub-points with details + - Examples and explanations + ``` + +- **File References:** + - Use `[filename](mdc:path/to/file)` ([filename](mdc:filename)) to reference files + - Example: [prisma.mdc](mdc:.cursor/rules/prisma.mdc) for rule references + - Example: [schema.prisma](mdc:prisma/schema.prisma) for code references + +- **Code Examples:** + - Use language-specific code blocks + ```typescript + // ✅ DO: Show good examples + const goodExample = true; + + // ❌ DON'T: Show anti-patterns + const badExample = false; + ``` + +- **Rule Content Guidelines:** + - Start with high-level overview + - Include specific, actionable requirements + - Show examples of correct implementation + - Reference existing code when possible + - Keep rules DRY by referencing other rules + +- **Rule Maintenance:** + - Update rules when new patterns emerge + - Add examples from actual codebase + - Remove outdated patterns + - Cross-reference related rules + +- **Best Practices:** + - Use bullet points for clarity + - Keep descriptions concise + - Include both DO and DON'T examples + - Reference actual code over theoretical examples + - Use consistent formatting across rules \ No newline at end of file diff --git a/.cursor/rules/design.mdc b/.cursor/rules/design.mdc new file mode 100644 index 0000000..71e3819 --- /dev/null +++ b/.cursor/rules/design.mdc @@ -0,0 +1,388 @@ +--- +description: Use this rule when asked to do any frontend or UI design +globs: +alwaysApply: false +--- +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or ...; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or ..., this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/.cursor/rules/dev_workflow.mdc b/.cursor/rules/dev_workflow.mdc new file mode 100644 index 0000000..3333ce9 --- /dev/null +++ b/.cursor/rules/dev_workflow.mdc @@ -0,0 +1,412 @@ +--- +description: Guide for using Taskmaster to manage task-driven development workflows +globs: **/* +alwaysApply: true +--- + +# Taskmaster Development Workflow + +This guide outlines the standard process for using Taskmaster to manage software development projects. It is written as a set of instructions for you, the AI agent. + +- **Your Default Stance**: For most projects, the user can work directly within the `master` task context. Your initial actions should operate on this default context unless a clear pattern for multi-context work emerges. +- **Your Goal**: Your role is to elevate the user's workflow by intelligently introducing advanced features like **Tagged Task Lists** when you detect the appropriate context. Do not force tags on the user; suggest them as a helpful solution to a specific need. + +## The Basic Loop +The fundamental development cycle you will facilitate is: +1. **`list`**: Show the user what needs to be done. +2. **`next`**: Help the user decide what to work on. +3. **`show `**: Provide details for a specific task. +4. **`expand `**: Break down a complex task into smaller, manageable subtasks. +5. **Implement**: The user writes the code and tests. +6. **`update-subtask`**: Log progress and findings on behalf of the user. +7. **`set-status`**: Mark tasks and subtasks as `done` as work is completed. +8. **Repeat**. + +All your standard command executions should operate on the user's current task context, which defaults to `master`. + +--- + +## Standard Development Workflow Process + +### Simple Workflow (Default Starting Point) + +For new projects or when users are getting started, operate within the `master` tag context: + +- Start new projects by running `initialize_project` tool / `task-master init` or `parse_prd` / `task-master parse-prd --input=''` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to generate initial tasks.json with tagged structure +- Begin coding sessions with `get_tasks` / `task-master list` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to see current tasks, status, and IDs +- Determine the next task to work on using `next_task` / `task-master next` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) +- Analyze task complexity with `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) before breaking down tasks +- Review complexity report using `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) +- Select tasks based on dependencies (all marked 'done'), priority level, and ID order +- View specific task details using `get_task` / `task-master show ` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to understand implementation requirements +- Break down complex tasks using `expand_task` / `task-master expand --id= --force --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) with appropriate flags like `--force` (to replace existing subtasks) and `--research` +- Implement code following task details, dependencies, and project standards +- Mark completed tasks with `set_task_status` / `task-master set-status --id= --status=done` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) +- Update dependent tasks when implementation differs from original plan using `update` / `task-master update --from= --prompt="..."` or `update_task` / `task-master update-task --id= --prompt="..."` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) + +--- + +## Leveling Up: Agent-Led Multi-Context Workflows + +While the basic workflow is powerful, your primary opportunity to add value is by identifying when to introduce **Tagged Task Lists**. These patterns are your tools for creating a more organized and efficient development environment for the user, especially if you detect agentic or parallel development happening across the same session. + +**Critical Principle**: Most users should never see a difference in their experience. Only introduce advanced workflows when you detect clear indicators that the project has evolved beyond simple task management. + +### When to Introduce Tags: Your Decision Patterns + +Here are the patterns to look for. When you detect one, you should propose the corresponding workflow to the user. + +#### Pattern 1: Simple Git Feature Branching +This is the most common and direct use case for tags. + +- **Trigger**: The user creates a new git branch (e.g., `git checkout -b feature/user-auth`). +- **Your Action**: Propose creating a new tag that mirrors the branch name to isolate the feature's tasks from `master`. +- **Your Suggested Prompt**: *"I see you've created a new branch named 'feature/user-auth'. To keep all related tasks neatly organized and separate from your main list, I can create a corresponding task tag for you. This helps prevent merge conflicts in your `tasks.json` file later. Shall I create the 'feature-user-auth' tag?"* +- **Tool to Use**: `task-master add-tag --from-branch` + +#### Pattern 2: Team Collaboration +- **Trigger**: The user mentions working with teammates (e.g., "My teammate Alice is handling the database schema," or "I need to review Bob's work on the API."). +- **Your Action**: Suggest creating a separate tag for the user's work to prevent conflicts with shared master context. +- **Your Suggested Prompt**: *"Since you're working with Alice, I can create a separate task context for your work to avoid conflicts. This way, Alice can continue working with the master list while you have your own isolated context. When you're ready to merge your work, we can coordinate the tasks back to master. Shall I create a tag for your current work?"* +- **Tool to Use**: `task-master add-tag my-work --copy-from-current --description="My tasks while collaborating with Alice"` + +#### Pattern 3: Experiments or Risky Refactors +- **Trigger**: The user wants to try something that might not be kept (e.g., "I want to experiment with switching our state management library," or "Let's refactor the old API module, but I want to keep the current tasks as a reference."). +- **Your Action**: Propose creating a sandboxed tag for the experimental work. +- **Your Suggested Prompt**: *"This sounds like a great experiment. To keep these new tasks separate from our main plan, I can create a temporary 'experiment-zustand' tag for this work. If we decide not to proceed, we can simply delete the tag without affecting the main task list. Sound good?"* +- **Tool to Use**: `task-master add-tag experiment-zustand --description="Exploring Zustand migration"` + +#### Pattern 4: Large Feature Initiatives (PRD-Driven) +This is a more structured approach for significant new features or epics. + +- **Trigger**: The user describes a large, multi-step feature that would benefit from a formal plan. +- **Your Action**: Propose a comprehensive, PRD-driven workflow. +- **Your Suggested Prompt**: *"This sounds like a significant new feature. To manage this effectively, I suggest we create a dedicated task context for it. Here's the plan: I'll create a new tag called 'feature-xyz', then we can draft a Product Requirements Document (PRD) together to scope the work. Once the PRD is ready, I'll automatically generate all the necessary tasks within that new tag. How does that sound?"* +- **Your Implementation Flow**: + 1. **Create an empty tag**: `task-master add-tag feature-xyz --description "Tasks for the new XYZ feature"`. You can also start by creating a git branch if applicable, and then create the tag from that branch. + 2. **Collaborate & Create PRD**: Work with the user to create a detailed PRD file (e.g., `.taskmaster/docs/feature-xyz-prd.txt`). + 3. **Parse PRD into the new tag**: `task-master parse-prd .taskmaster/docs/feature-xyz-prd.txt --tag feature-xyz` + 4. **Prepare the new task list**: Follow up by suggesting `analyze-complexity` and `expand-all` for the newly created tasks within the `feature-xyz` tag. + +#### Pattern 5: Version-Based Development +Tailor your approach based on the project maturity indicated by tag names. + +- **Prototype/MVP Tags** (`prototype`, `mvp`, `poc`, `v0.x`): + - **Your Approach**: Focus on speed and functionality over perfection + - **Task Generation**: Create tasks that emphasize "get it working" over "get it perfect" + - **Complexity Level**: Lower complexity, fewer subtasks, more direct implementation paths + - **Research Prompts**: Include context like "This is a prototype - prioritize speed and basic functionality over optimization" + - **Example Prompt Addition**: *"Since this is for the MVP, I'll focus on tasks that get core functionality working quickly rather than over-engineering."* + +- **Production/Mature Tags** (`v1.0+`, `production`, `stable`): + - **Your Approach**: Emphasize robustness, testing, and maintainability + - **Task Generation**: Include comprehensive error handling, testing, documentation, and optimization + - **Complexity Level**: Higher complexity, more detailed subtasks, thorough implementation paths + - **Research Prompts**: Include context like "This is for production - prioritize reliability, performance, and maintainability" + - **Example Prompt Addition**: *"Since this is for production, I'll ensure tasks include proper error handling, testing, and documentation."* + +### Advanced Workflow (Tag-Based & PRD-Driven) + +**When to Transition**: Recognize when the project has evolved (or has initiated a project which existing code) beyond simple task management. Look for these indicators: +- User mentions teammates or collaboration needs +- Project has grown to 15+ tasks with mixed priorities +- User creates feature branches or mentions major initiatives +- User initializes Taskmaster on an existing, complex codebase +- User describes large features that would benefit from dedicated planning + +**Your Role in Transition**: Guide the user to a more sophisticated workflow that leverages tags for organization and PRDs for comprehensive planning. + +#### Master List Strategy (High-Value Focus) +Once you transition to tag-based workflows, the `master` tag should ideally contain only: +- **High-level deliverables** that provide significant business value +- **Major milestones** and epic-level features +- **Critical infrastructure** work that affects the entire project +- **Release-blocking** items + +**What NOT to put in master**: +- Detailed implementation subtasks (these go in feature-specific tags' parent tasks) +- Refactoring work (create dedicated tags like `refactor-auth`) +- Experimental features (use `experiment-*` tags) +- Team member-specific tasks (use person-specific tags) + +#### PRD-Driven Feature Development + +**For New Major Features**: +1. **Identify the Initiative**: When user describes a significant feature +2. **Create Dedicated Tag**: `add_tag feature-[name] --description="[Feature description]"` +3. **Collaborative PRD Creation**: Work with user to create comprehensive PRD in `.taskmaster/docs/feature-[name]-prd.txt` +4. **Parse & Prepare**: + - `parse_prd .taskmaster/docs/feature-[name]-prd.txt --tag=feature-[name]` + - `analyze_project_complexity --tag=feature-[name] --research` + - `expand_all --tag=feature-[name] --research` +5. **Add Master Reference**: Create a high-level task in `master` that references the feature tag + +**For Existing Codebase Analysis**: +When users initialize Taskmaster on existing projects: +1. **Codebase Discovery**: Use your native tools for producing deep context about the code base. You may use `research` tool with `--tree` and `--files` to collect up to date information using the existing architecture as context. +2. **Collaborative Assessment**: Work with user to identify improvement areas, technical debt, or new features +3. **Strategic PRD Creation**: Co-author PRDs that include: + - Current state analysis (based on your codebase research) + - Proposed improvements or new features + - Implementation strategy considering existing code +4. **Tag-Based Organization**: Parse PRDs into appropriate tags (`refactor-api`, `feature-dashboard`, `tech-debt`, etc.) +5. **Master List Curation**: Keep only the most valuable initiatives in master + +The parse-prd's `--append` flag enables the user to parse multple PRDs within tags or across tags. PRDs should be focused and the number of tasks they are parsed into should be strategically chosen relative to the PRD's complexity and level of detail. + +### Workflow Transition Examples + +**Example 1: Simple → Team-Based** +``` +User: "Alice is going to help with the API work" +Your Response: "Great! To avoid conflicts, I'll create a separate task context for your work. Alice can continue with the master list while you work in your own context. When you're ready to merge, we can coordinate the tasks back together." +Action: add_tag my-api-work --copy-from-current --description="My API tasks while collaborating with Alice" +``` + +**Example 2: Simple → PRD-Driven** +``` +User: "I want to add a complete user dashboard with analytics, user management, and reporting" +Your Response: "This sounds like a major feature that would benefit from detailed planning. Let me create a dedicated context for this work and we can draft a PRD together to ensure we capture all requirements." +Actions: +1. add_tag feature-dashboard --description="User dashboard with analytics and management" +2. Collaborate on PRD creation +3. parse_prd dashboard-prd.txt --tag=feature-dashboard +4. Add high-level "User Dashboard" task to master +``` + +**Example 3: Existing Project → Strategic Planning** +``` +User: "I just initialized Taskmaster on my existing React app. It's getting messy and I want to improve it." +Your Response: "Let me research your codebase to understand the current architecture, then we can create a strategic plan for improvements." +Actions: +1. research "Current React app architecture and improvement opportunities" --tree --files=src/ +2. Collaborate on improvement PRD based on findings +3. Create tags for different improvement areas (refactor-components, improve-state-management, etc.) +4. Keep only major improvement initiatives in master +``` + +--- + +## Primary Interaction: MCP Server vs. CLI + +Taskmaster offers two primary ways to interact: + +1. **MCP Server (Recommended for Integrated Tools)**: + - For AI agents and integrated development environments (like Cursor), interacting via the **MCP server is the preferred method**. + - The MCP server exposes Taskmaster functionality through a set of tools (e.g., `get_tasks`, `add_subtask`). + - This method offers better performance, structured data exchange, and richer error handling compared to CLI parsing. + - Refer to [`mcp.mdc`](mdc:.cursor/rules/mcp.mdc) for details on the MCP architecture and available tools. + - A comprehensive list and description of MCP tools and their corresponding CLI commands can be found in [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc). + - **Restart the MCP server** if core logic in `scripts/modules` or MCP tool/direct function definitions change. + - **Note**: MCP tools fully support tagged task lists with complete tag management capabilities. + +2. **`task-master` CLI (For Users & Fallback)**: + - The global `task-master` command provides a user-friendly interface for direct terminal interaction. + - It can also serve as a fallback if the MCP server is inaccessible or a specific function isn't exposed via MCP. + - Install globally with `npm install -g task-master-ai` or use locally via `npx task-master-ai ...`. + - The CLI commands often mirror the MCP tools (e.g., `task-master list` corresponds to `get_tasks`). + - Refer to [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc) for a detailed command reference. + - **Tagged Task Lists**: CLI fully supports the new tagged system with seamless migration. + +## How the Tag System Works (For Your Reference) + +- **Data Structure**: Tasks are organized into separate contexts (tags) like "master", "feature-branch", or "v2.0". +- **Silent Migration**: Existing projects automatically migrate to use a "master" tag with zero disruption. +- **Context Isolation**: Tasks in different tags are completely separate. Changes in one tag do not affect any other tag. +- **Manual Control**: The user is always in control. There is no automatic switching. You facilitate switching by using `use-tag `. +- **Full CLI & MCP Support**: All tag management commands are available through both the CLI and MCP tools for you to use. Refer to [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc) for a full command list. + +--- + +## Task Complexity Analysis + +- Run `analyze_project_complexity` / `task-master analyze-complexity --research` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) for comprehensive analysis +- Review complexity report via `complexity_report` / `task-master complexity-report` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) for a formatted, readable version. +- Focus on tasks with highest complexity scores (8-10) for detailed breakdown +- Use analysis results to determine appropriate subtask allocation +- Note that reports are automatically used by the `expand_task` tool/command + +## Task Breakdown Process + +- Use `expand_task` / `task-master expand --id=`. It automatically uses the complexity report if found, otherwise generates default number of subtasks. +- Use `--num=` to specify an explicit number of subtasks, overriding defaults or complexity report recommendations. +- Add `--research` flag to leverage Perplexity AI for research-backed expansion. +- Add `--force` flag to clear existing subtasks before generating new ones (default is to append). +- Use `--prompt=""` to provide additional context when needed. +- Review and adjust generated subtasks as necessary. +- Use `expand_all` tool or `task-master expand --all` to expand multiple pending tasks at once, respecting flags like `--force` and `--research`. +- If subtasks need complete replacement (regardless of the `--force` flag on `expand`), clear them first with `clear_subtasks` / `task-master clear-subtasks --id=`. + +## Implementation Drift Handling + +- When implementation differs significantly from planned approach +- When future tasks need modification due to current implementation choices +- When new dependencies or requirements emerge +- Use `update` / `task-master update --from= --prompt='\nUpdate context...' --research` to update multiple future tasks. +- Use `update_task` / `task-master update-task --id= --prompt='\nUpdate context...' --research` to update a single specific task. + +## Task Status Management + +- Use 'pending' for tasks ready to be worked on +- Use 'done' for completed and verified tasks +- Use 'deferred' for postponed tasks +- Add custom status values as needed for project-specific workflows + +## Task Structure Fields + +- **id**: Unique identifier for the task (Example: `1`, `1.1`) +- **title**: Brief, descriptive title (Example: `"Initialize Repo"`) +- **description**: Concise summary of what the task involves (Example: `"Create a new repository, set up initial structure."`) +- **status**: Current state of the task (Example: `"pending"`, `"done"`, `"deferred"`) +- **dependencies**: IDs of prerequisite tasks (Example: `[1, 2.1]`) + - Dependencies are displayed with status indicators (✅ for completed, ⏱️ for pending) + - This helps quickly identify which prerequisite tasks are blocking work +- **priority**: Importance level (Example: `"high"`, `"medium"`, `"low"`) +- **details**: In-depth implementation instructions (Example: `"Use GitHub client ID/secret, handle callback, set session token."`) +- **testStrategy**: Verification approach (Example: `"Deploy and call endpoint to confirm 'Hello World' response."`) +- **subtasks**: List of smaller, more specific tasks (Example: `[{"id": 1, "title": "Configure OAuth", ...}]`) +- Refer to task structure details (previously linked to `tasks.mdc`). + +## Configuration Management (Updated) + +Taskmaster configuration is managed through two main mechanisms: + +1. **`.taskmaster/config.json` File (Primary):** + * Located in the project root directory. + * Stores most configuration settings: AI model selections (main, research, fallback), parameters (max tokens, temperature), logging level, default subtasks/priority, project name, etc. + * **Tagged System Settings**: Includes `global.defaultTag` (defaults to "master") and `tags` section for tag management configuration. + * **Managed via `task-master models --setup` command.** Do not edit manually unless you know what you are doing. + * **View/Set specific models via `task-master models` command or `models` MCP tool.** + * Created automatically when you run `task-master models --setup` for the first time or during tagged system migration. + +2. **Environment Variables (`.env` / `mcp.json`):** + * Used **only** for sensitive API keys and specific endpoint URLs. + * Place API keys (one per provider) in a `.env` file in the project root for CLI usage. + * For MCP/Cursor integration, configure these keys in the `env` section of `.cursor/mcp.json`. + * Available keys/variables: See `assets/env.example` or the Configuration section in the command reference (previously linked to `taskmaster.mdc`). + +3. **`.taskmaster/state.json` File (Tagged System State):** + * Tracks current tag context and migration status. + * Automatically created during tagged system migration. + * Contains: `currentTag`, `lastSwitched`, `migrationNoticeShown`. + +**Important:** Non-API key settings (like model selections, `MAX_TOKENS`, `TASKMASTER_LOG_LEVEL`) are **no longer configured via environment variables**. Use the `task-master models` command (or `--setup` for interactive configuration) or the `models` MCP tool. +**If AI commands FAIL in MCP** verify that the API key for the selected provider is present in the `env` section of `.cursor/mcp.json`. +**If AI commands FAIL in CLI** verify that the API key for the selected provider is present in the `.env` file in the root of the project. + +## Determining the Next Task + +- Run `next_task` / `task-master next` to show the next task to work on. +- The command identifies tasks with all dependencies satisfied +- Tasks are prioritized by priority level, dependency count, and ID +- The command shows comprehensive task information including: + - Basic task details and description + - Implementation details + - Subtasks (if they exist) + - Contextual suggested actions +- Recommended before starting any new development work +- Respects your project's dependency structure +- Ensures tasks are completed in the appropriate sequence +- Provides ready-to-use commands for common task actions + +## Viewing Specific Task Details + +- Run `get_task` / `task-master show ` to view a specific task. +- Use dot notation for subtasks: `task-master show 1.2` (shows subtask 2 of task 1) +- Displays comprehensive information similar to the next command, but for a specific task +- For parent tasks, shows all subtasks and their current status +- For subtasks, shows parent task information and relationship +- Provides contextual suggested actions appropriate for the specific task +- Useful for examining task details before implementation or checking status + +## Managing Task Dependencies + +- Use `add_dependency` / `task-master add-dependency --id= --depends-on=` to add a dependency. +- Use `remove_dependency` / `task-master remove-dependency --id= --depends-on=` to remove a dependency. +- The system prevents circular dependencies and duplicate dependency entries +- Dependencies are checked for existence before being added or removed +- Task files are automatically regenerated after dependency changes +- Dependencies are visualized with status indicators in task listings and files + +## Task Reorganization + +- Use `move_task` / `task-master move --from= --to=` to move tasks or subtasks within the hierarchy +- This command supports several use cases: + - Moving a standalone task to become a subtask (e.g., `--from=5 --to=7`) + - Moving a subtask to become a standalone task (e.g., `--from=5.2 --to=7`) + - Moving a subtask to a different parent (e.g., `--from=5.2 --to=7.3`) + - Reordering subtasks within the same parent (e.g., `--from=5.2 --to=5.4`) + - Moving a task to a new, non-existent ID position (e.g., `--from=5 --to=25`) + - Moving multiple tasks at once using comma-separated IDs (e.g., `--from=10,11,12 --to=16,17,18`) +- The system includes validation to prevent data loss: + - Allows moving to non-existent IDs by creating placeholder tasks + - Prevents moving to existing task IDs that have content (to avoid overwriting) + - Validates source tasks exist before attempting to move them +- The system maintains proper parent-child relationships and dependency integrity +- Task files are automatically regenerated after the move operation +- This provides greater flexibility in organizing and refining your task structure as project understanding evolves +- This is especially useful when dealing with potential merge conflicts arising from teams creating tasks on separate branches. Solve these conflicts very easily by moving your tasks and keeping theirs. + +## Iterative Subtask Implementation + +Once a task has been broken down into subtasks using `expand_task` or similar methods, follow this iterative process for implementation: + +1. **Understand the Goal (Preparation):** + * Use `get_task` / `task-master show ` (see [`taskmaster.mdc`](mdc:.cursor/rules/taskmaster.mdc)) to thoroughly understand the specific goals and requirements of the subtask. + +2. **Initial Exploration & Planning (Iteration 1):** + * This is the first attempt at creating a concrete implementation plan. + * Explore the codebase to identify the precise files, functions, and even specific lines of code that will need modification. + * Determine the intended code changes (diffs) and their locations. + * Gather *all* relevant details from this exploration phase. + +3. **Log the Plan:** + * Run `update_subtask` / `task-master update-subtask --id= --prompt=''`. + * Provide the *complete and detailed* findings from the exploration phase in the prompt. Include file paths, line numbers, proposed diffs, reasoning, and any potential challenges identified. Do not omit details. The goal is to create a rich, timestamped log within the subtask's `details`. + +4. **Verify the Plan:** + * Run `get_task` / `task-master show ` again to confirm that the detailed implementation plan has been successfully appended to the subtask's details. + +5. **Begin Implementation:** + * Set the subtask status using `set_task_status` / `task-master set-status --id= --status=in-progress`. + * Start coding based on the logged plan. + +6. **Refine and Log Progress (Iteration 2+):** + * As implementation progresses, you will encounter challenges, discover nuances, or confirm successful approaches. + * **Before appending new information**: Briefly review the *existing* details logged in the subtask (using `get_task` or recalling from context) to ensure the update adds fresh insights and avoids redundancy. + * **Regularly** use `update_subtask` / `task-master update-subtask --id= --prompt='\n- What worked...\n- What didn't work...'` to append new findings. + * **Crucially, log:** + * What worked ("fundamental truths" discovered). + * What didn't work and why (to avoid repeating mistakes). + * Specific code snippets or configurations that were successful. + * Decisions made, especially if confirmed with user input. + * Any deviations from the initial plan and the reasoning. + * The objective is to continuously enrich the subtask's details, creating a log of the implementation journey that helps the AI (and human developers) learn, adapt, and avoid repeating errors. + +7. **Review & Update Rules (Post-Implementation):** + * Once the implementation for the subtask is functionally complete, review all code changes and the relevant chat history. + * Identify any new or modified code patterns, conventions, or best practices established during the implementation. + * Create new or update existing rules following internal guidelines (previously linked to `cursor_rules.mdc` and `self_improve.mdc`). + +8. **Mark Task Complete:** + * After verifying the implementation and updating any necessary rules, mark the subtask as completed: `set_task_status` / `task-master set-status --id= --status=done`. + +9. **Commit Changes (If using Git):** + * Stage the relevant code changes and any updated/new rule files (`git add .`). + * Craft a comprehensive Git commit message summarizing the work done for the subtask, including both code implementation and any rule adjustments. + * Execute the commit command directly in the terminal (e.g., `git commit -m 'feat(module): Implement feature X for subtask \n\n- Details about changes...\n- Updated rule Y for pattern Z'`). + * Consider if a Changeset is needed according to internal versioning guidelines (previously linked to `changeset.mdc`). If so, run `npm run changeset`, stage the generated file, and amend the commit or create a new one. + +10. **Proceed to Next Subtask:** + * Identify the next subtask (e.g., using `next_task` / `task-master next`). + +## Code Analysis & Refactoring Techniques + +- **Top-Level Function Search**: + - Useful for understanding module structure or planning refactors. + - Use grep/ripgrep to find exported functions/constants: + `rg "export (async function|function|const) \w+"` or similar patterns. + - Can help compare functions between files during migrations or identify potential naming conflicts. + +--- +*This workflow provides a general guideline. Adapt it based on your specific project needs and team practices.* \ No newline at end of file diff --git a/.cursor/rules/git_commit.mdc b/.cursor/rules/git_commit.mdc new file mode 100644 index 0000000..cfc5f58 --- /dev/null +++ b/.cursor/rules/git_commit.mdc @@ -0,0 +1,26 @@ +--- +description: git commit rule +globs: +alwaysApply: false +--- +--- +Git Usage + +- Use the following **prefixes** for commit messages, followed by a **colon** and a **space**: + + - `fix` — for bug fixes + - `feat` — for new features + - `perf` — for performance improvements + - `docs` — for documentation changes + - `style` — for formatting changes + - `refactor` — for code refactoring + - `test` — for adding missing tests + - `chore` — for routine tasks + +- When determining the commit message prefix, pick the most **relevant** option from the list above. + +- Use **lowercase** for all commit messages. + +- If the change is not self-explanatory, include a **bullet list of changes** after a blank line below the summary. + +--- diff --git a/.cursor/rules/self_improve.mdc b/.cursor/rules/self_improve.mdc new file mode 100644 index 0000000..40b31b6 --- /dev/null +++ b/.cursor/rules/self_improve.mdc @@ -0,0 +1,72 @@ +--- +description: Guidelines for continuously improving Cursor rules based on emerging code patterns and best practices. +globs: **/* +alwaysApply: true +--- + +- **Rule Improvement Triggers:** + - New code patterns not covered by existing rules + - Repeated similar implementations across files + - Common error patterns that could be prevented + - New libraries or tools being used consistently + - Emerging best practices in the codebase + +- **Analysis Process:** + - Compare new code with existing rules + - Identify patterns that should be standardized + - Look for references to external documentation + - Check for consistent error handling patterns + - Monitor test patterns and coverage + +- **Rule Updates:** + - **Add New Rules When:** + - A new technology/pattern is used in 3+ files + - Common bugs could be prevented by a rule + - Code reviews repeatedly mention the same feedback + - New security or performance patterns emerge + + - **Modify Existing Rules When:** + - Better examples exist in the codebase + - Additional edge cases are discovered + - Related rules have been updated + - Implementation details have changed + +- **Example Pattern Recognition:** + ```typescript + // If you see repeated patterns like: + const data = await prisma.user.findMany({ + select: { id: true, email: true }, + where: { status: 'ACTIVE' } + }); + + // Consider adding to [prisma.mdc](mdc:.cursor/rules/prisma.mdc): + // - Standard select fields + // - Common where conditions + // - Performance optimization patterns + ``` + +- **Rule Quality Checks:** + - Rules should be actionable and specific + - Examples should come from actual code + - References should be up to date + - Patterns should be consistently enforced + +- **Continuous Improvement:** + - Monitor code review comments + - Track common development questions + - Update rules after major refactors + - Add links to relevant documentation + - Cross-reference related rules + +- **Rule Deprecation:** + - Mark outdated patterns as deprecated + - Remove rules that no longer apply + - Update references to deprecated rules + - Document migration paths for old patterns + +- **Documentation Updates:** + - Keep examples synchronized with code + - Update references to external docs + - Maintain links between related rules + - Document breaking changes +Follow [cursor_rules.mdc](mdc:.cursor/rules/cursor_rules.mdc) for proper rule formatting and structure. diff --git a/.cursor/rules/taskmaster.mdc b/.cursor/rules/taskmaster.mdc new file mode 100644 index 0000000..b4fe6df --- /dev/null +++ b/.cursor/rules/taskmaster.mdc @@ -0,0 +1,557 @@ +--- +description: Comprehensive reference for Taskmaster MCP tools and CLI commands. +globs: **/* +alwaysApply: true +--- +# Taskmaster Tool & Command Reference + +This document provides a detailed reference for interacting with Taskmaster, covering both the recommended MCP tools, suitable for integrations like Cursor, and the corresponding `task-master` CLI commands, designed for direct user interaction or fallback. + +**Note:** For interacting with Taskmaster programmatically or via integrated tools, using the **MCP tools is strongly recommended** due to better performance, structured data, and error handling. The CLI commands serve as a user-friendly alternative and fallback. + +**Important:** Several MCP tools involve AI processing... The AI-powered tools include `parse_prd`, `analyze_project_complexity`, `update_subtask`, `update_task`, `update`, `expand_all`, `expand_task`, and `add_task`. + +**🏷️ Tagged Task Lists System:** Task Master now supports **tagged task lists** for multi-context task management. This allows you to maintain separate, isolated lists of tasks for different features, branches, or experiments. Existing projects are seamlessly migrated to use a default "master" tag. Most commands now support a `--tag ` flag to specify which context to operate on. If omitted, commands use the currently active tag. + +--- + +## Initialization & Setup + +### 1. Initialize Project (`init`) + +* **MCP Tool:** `initialize_project` +* **CLI Command:** `task-master init [options]` +* **Description:** `Set up the basic Taskmaster file structure and configuration in the current directory for a new project.` +* **Key CLI Options:** + * `--name `: `Set the name for your project in Taskmaster's configuration.` + * `--description `: `Provide a brief description for your project.` + * `--version `: `Set the initial version for your project, e.g., '0.1.0'.` + * `-y, --yes`: `Initialize Taskmaster quickly using default settings without interactive prompts.` +* **Usage:** Run this once at the beginning of a new project. +* **MCP Variant Description:** `Set up the basic Taskmaster file structure and configuration in the current directory for a new project by running the 'task-master init' command.` +* **Key MCP Parameters/Options:** + * `projectName`: `Set the name for your project.` (CLI: `--name `) + * `projectDescription`: `Provide a brief description for your project.` (CLI: `--description `) + * `projectVersion`: `Set the initial version for your project, e.g., '0.1.0'.` (CLI: `--version `) + * `authorName`: `Author name.` (CLI: `--author `) + * `skipInstall`: `Skip installing dependencies. Default is false.` (CLI: `--skip-install`) + * `addAliases`: `Add shell aliases tm and taskmaster. Default is false.` (CLI: `--aliases`) + * `yes`: `Skip prompts and use defaults/provided arguments. Default is false.` (CLI: `-y, --yes`) +* **Usage:** Run this once at the beginning of a new project, typically via an integrated tool like Cursor. Operates on the current working directory of the MCP server. +* **Important:** Once complete, you *MUST* parse a prd in order to generate tasks. There will be no tasks files until then. The next step after initializing should be to create a PRD using the example PRD in .taskmaster/templates/example_prd.txt. +* **Tagging:** Use the `--tag` option to parse the PRD into a specific, non-default tag context. If the tag doesn't exist, it will be created automatically. Example: `task-master parse-prd spec.txt --tag=new-feature`. + +### 2. Parse PRD (`parse_prd`) + +* **MCP Tool:** `parse_prd` +* **CLI Command:** `task-master parse-prd [file] [options]` +* **Description:** `Parse a Product Requirements Document, PRD, or text file with Taskmaster to automatically generate an initial set of tasks in tasks.json.` +* **Key Parameters/Options:** + * `input`: `Path to your PRD or requirements text file that Taskmaster should parse for tasks.` (CLI: `[file]` positional or `-i, --input `) + * `output`: `Specify where Taskmaster should save the generated 'tasks.json' file. Defaults to '.taskmaster/tasks/tasks.json'.` (CLI: `-o, --output `) + * `numTasks`: `Approximate number of top-level tasks Taskmaster should aim to generate from the document.` (CLI: `-n, --num-tasks `) + * `force`: `Use this to allow Taskmaster to overwrite an existing 'tasks.json' without asking for confirmation.` (CLI: `-f, --force`) +* **Usage:** Useful for bootstrapping a project from an existing requirements document. +* **Notes:** Task Master will strictly adhere to any specific requirements mentioned in the PRD, such as libraries, database schemas, frameworks, tech stacks, etc., while filling in any gaps where the PRD isn't fully specified. Tasks are designed to provide the most direct implementation path while avoiding over-engineering. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. If the user does not have a PRD, suggest discussing their idea and then use the example PRD in `.taskmaster/templates/example_prd.txt` as a template for creating the PRD based on their idea, for use with `parse-prd`. + +--- + +## AI Model Configuration + +### 2. Manage Models (`models`) +* **MCP Tool:** `models` +* **CLI Command:** `task-master models [options]` +* **Description:** `View the current AI model configuration or set specific models for different roles (main, research, fallback). Allows setting custom model IDs for Ollama and OpenRouter.` +* **Key MCP Parameters/Options:** + * `setMain `: `Set the primary model ID for task generation/updates.` (CLI: `--set-main `) + * `setResearch `: `Set the model ID for research-backed operations.` (CLI: `--set-research `) + * `setFallback `: `Set the model ID to use if the primary fails.` (CLI: `--set-fallback `) + * `ollama `: `Indicates the set model ID is a custom Ollama model.` (CLI: `--ollama`) + * `openrouter `: `Indicates the set model ID is a custom OpenRouter model.` (CLI: `--openrouter`) + * `listAvailableModels `: `If true, lists available models not currently assigned to a role.` (CLI: No direct equivalent; CLI lists available automatically) + * `projectRoot `: `Optional. Absolute path to the project root directory.` (CLI: Determined automatically) +* **Key CLI Options:** + * `--set-main `: `Set the primary model.` + * `--set-research `: `Set the research model.` + * `--set-fallback `: `Set the fallback model.` + * `--ollama`: `Specify that the provided model ID is for Ollama (use with --set-*).` + * `--openrouter`: `Specify that the provided model ID is for OpenRouter (use with --set-*). Validates against OpenRouter API.` + * `--bedrock`: `Specify that the provided model ID is for AWS Bedrock (use with --set-*).` + * `--setup`: `Run interactive setup to configure models, including custom Ollama/OpenRouter IDs.` +* **Usage (MCP):** Call without set flags to get current config. Use `setMain`, `setResearch`, or `setFallback` with a valid model ID to update the configuration. Use `listAvailableModels: true` to get a list of unassigned models. To set a custom model, provide the model ID and set `ollama: true` or `openrouter: true`. +* **Usage (CLI):** Run without flags to view current configuration and available models. Use set flags to update specific roles. Use `--setup` for guided configuration, including custom models. To set a custom model via flags, use `--set-=` along with either `--ollama` or `--openrouter`. +* **Notes:** Configuration is stored in `.taskmaster/config.json` in the project root. This command/tool modifies that file. Use `listAvailableModels` or `task-master models` to see internally supported models. OpenRouter custom models are validated against their live API. Ollama custom models are not validated live. +* **API note:** API keys for selected AI providers (based on their model) need to exist in the mcp.json file to be accessible in MCP context. The API keys must be present in the local .env file for the CLI to be able to read them. +* **Model costs:** The costs in supported models are expressed in dollars. An input/output value of 3 is $3.00. A value of 0.8 is $0.80. +* **Warning:** DO NOT MANUALLY EDIT THE .taskmaster/config.json FILE. Use the included commands either in the MCP or CLI format as needed. Always prioritize MCP tools when available and use the CLI as a fallback. + +--- + +## Task Listing & Viewing + +### 3. Get Tasks (`get_tasks`) + +* **MCP Tool:** `get_tasks` +* **CLI Command:** `task-master list [options]` +* **Description:** `List your Taskmaster tasks, optionally filtering by status and showing subtasks.` +* **Key Parameters/Options:** + * `status`: `Show only Taskmaster tasks matching this status (or multiple statuses, comma-separated), e.g., 'pending' or 'done,in-progress'.` (CLI: `-s, --status `) + * `withSubtasks`: `Include subtasks indented under their parent tasks in the list.` (CLI: `--with-subtasks`) + * `tag`: `Specify which tag context to list tasks from. Defaults to the current active tag.` (CLI: `--tag `) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file `) +* **Usage:** Get an overview of the project status, often used at the start of a work session. + +### 4. Get Next Task (`next_task`) + +* **MCP Tool:** `next_task` +* **CLI Command:** `task-master next [options]` +* **Description:** `Ask Taskmaster to show the next available task you can work on, based on status and completed dependencies.` +* **Key Parameters/Options:** + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file `) + * `tag`: `Specify which tag context to use. Defaults to the current active tag.` (CLI: `--tag `) +* **Usage:** Identify what to work on next according to the plan. + +### 5. Get Task Details (`get_task`) + +* **MCP Tool:** `get_task` +* **CLI Command:** `task-master show [id] [options]` +* **Description:** `Display detailed information for one or more specific Taskmaster tasks or subtasks by ID.` +* **Key Parameters/Options:** + * `id`: `Required. The ID of the Taskmaster task (e.g., '15'), subtask (e.g., '15.2'), or a comma-separated list of IDs ('1,5,10.2') you want to view.` (CLI: `[id]` positional or `-i, --id `) + * `tag`: `Specify which tag context to get the task(s) from. Defaults to the current active tag.` (CLI: `--tag `) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file `) +* **Usage:** Understand the full details for a specific task. When multiple IDs are provided, a summary table is shown. +* **CRITICAL INFORMATION** If you need to collect information from multiple tasks, use comma-separated IDs (i.e. 1,2,3) to receive an array of tasks. Do not needlessly get tasks one at a time if you need to get many as that is wasteful. + +--- + +## Task Creation & Modification + +### 6. Add Task (`add_task`) + +* **MCP Tool:** `add_task` +* **CLI Command:** `task-master add-task [options]` +* **Description:** `Add a new task to Taskmaster by describing it; AI will structure it.` +* **Key Parameters/Options:** + * `prompt`: `Required. Describe the new task you want Taskmaster to create, e.g., "Implement user authentication using JWT".` (CLI: `-p, --prompt `) + * `dependencies`: `Specify the IDs of any Taskmaster tasks that must be completed before this new one can start, e.g., '12,14'.` (CLI: `-d, --dependencies `) + * `priority`: `Set the priority for the new task: 'high', 'medium', or 'low'. Default is 'medium'.` (CLI: `--priority `) + * `research`: `Enable Taskmaster to use the research role for potentially more informed task creation.` (CLI: `-r, --research`) + * `tag`: `Specify which tag context to add the task to. Defaults to the current active tag.` (CLI: `--tag `) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file `) +* **Usage:** Quickly add newly identified tasks during development. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 7. Add Subtask (`add_subtask`) + +* **MCP Tool:** `add_subtask` +* **CLI Command:** `task-master add-subtask [options]` +* **Description:** `Add a new subtask to a Taskmaster parent task, or convert an existing task into a subtask.` +* **Key Parameters/Options:** + * `id` / `parent`: `Required. The ID of the Taskmaster task that will be the parent.` (MCP: `id`, CLI: `-p, --parent `) + * `taskId`: `Use this if you want to convert an existing top-level Taskmaster task into a subtask of the specified parent.` (CLI: `-i, --task-id `) + * `title`: `Required if not using taskId. The title for the new subtask Taskmaster should create.` (CLI: `-t, --title `) + * `description`: `A brief description for the new subtask.` (CLI: `-d, --description <text>`) + * `details`: `Provide implementation notes or details for the new subtask.` (CLI: `--details <text>`) + * `dependencies`: `Specify IDs of other tasks or subtasks, e.g., '15' or '16.1', that must be done before this new subtask.` (CLI: `--dependencies <ids>`) + * `status`: `Set the initial status for the new subtask. Default is 'pending'.` (CLI: `-s, --status <status>`) + * `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after adding the subtask.` (CLI: `--skip-generate`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Break down tasks manually or reorganize existing tasks. + +### 8. Update Tasks (`update`) + +* **MCP Tool:** `update` +* **CLI Command:** `task-master update [options]` +* **Description:** `Update multiple upcoming tasks in Taskmaster based on new context or changes, starting from a specific task ID.` +* **Key Parameters/Options:** + * `from`: `Required. The ID of the first task Taskmaster should update. All tasks with this ID or higher that are not 'done' will be considered.` (CLI: `--from <id>`) + * `prompt`: `Required. Explain the change or new context for Taskmaster to apply to the tasks, e.g., "We are now using React Query instead of Redux Toolkit for data fetching".` (CLI: `-p, --prompt <text>`) + * `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Handle significant implementation changes or pivots that affect multiple future tasks. Example CLI: `task-master update --from='18' --prompt='Switching to React Query.\nNeed to refactor data fetching...'` +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 9. Update Task (`update_task`) + +* **MCP Tool:** `update_task` +* **CLI Command:** `task-master update-task [options]` +* **Description:** `Modify a specific Taskmaster task by ID, incorporating new information or changes. By default, this replaces the existing task details.` +* **Key Parameters/Options:** + * `id`: `Required. The specific ID of the Taskmaster task, e.g., '15', you want to update.` (CLI: `-i, --id <id>`) + * `prompt`: `Required. Explain the specific changes or provide the new information Taskmaster should incorporate into this task.` (CLI: `-p, --prompt <text>`) + * `append`: `If true, appends the prompt content to the task's details with a timestamp, rather than replacing them. Behaves like update-subtask.` (CLI: `--append`) + * `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`) + * `tag`: `Specify which tag context the task belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Refine a specific task based on new understanding. Use `--append` to log progress without creating subtasks. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 10. Update Subtask (`update_subtask`) + +* **MCP Tool:** `update_subtask` +* **CLI Command:** `task-master update-subtask [options]` +* **Description:** `Append timestamped notes or details to a specific Taskmaster subtask without overwriting existing content. Intended for iterative implementation logging.` +* **Key Parameters/Options:** + * `id`: `Required. The ID of the Taskmaster subtask, e.g., '5.2', to update with new information.` (CLI: `-i, --id <id>`) + * `prompt`: `Required. The information, findings, or progress notes to append to the subtask's details with a timestamp.` (CLI: `-p, --prompt <text>`) + * `research`: `Enable Taskmaster to use the research role for more informed updates. Requires appropriate API key.` (CLI: `-r, --research`) + * `tag`: `Specify which tag context the subtask belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Log implementation progress, findings, and discoveries during subtask development. Each update is timestamped and appended to preserve the implementation journey. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 11. Set Task Status (`set_task_status`) + +* **MCP Tool:** `set_task_status` +* **CLI Command:** `task-master set-status [options]` +* **Description:** `Update the status of one or more Taskmaster tasks or subtasks, e.g., 'pending', 'in-progress', 'done'.` +* **Key Parameters/Options:** + * `id`: `Required. The ID(s) of the Taskmaster task(s) or subtask(s), e.g., '15', '15.2', or '16,17.1', to update.` (CLI: `-i, --id <id>`) + * `status`: `Required. The new status to set, e.g., 'done', 'pending', 'in-progress', 'review', 'cancelled'.` (CLI: `-s, --status <status>`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Mark progress as tasks move through the development cycle. + +### 12. Remove Task (`remove_task`) + +* **MCP Tool:** `remove_task` +* **CLI Command:** `task-master remove-task [options]` +* **Description:** `Permanently remove a task or subtask from the Taskmaster tasks list.` +* **Key Parameters/Options:** + * `id`: `Required. The ID of the Taskmaster task, e.g., '5', or subtask, e.g., '5.2', to permanently remove.` (CLI: `-i, --id <id>`) + * `yes`: `Skip the confirmation prompt and immediately delete the task.` (CLI: `-y, --yes`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Permanently delete tasks or subtasks that are no longer needed in the project. +* **Notes:** Use with caution as this operation cannot be undone. Consider using 'blocked', 'cancelled', or 'deferred' status instead if you just want to exclude a task from active planning but keep it for reference. The command automatically cleans up dependency references in other tasks. + +--- + +## Task Structure & Breakdown + +### 13. Expand Task (`expand_task`) + +* **MCP Tool:** `expand_task` +* **CLI Command:** `task-master expand [options]` +* **Description:** `Use Taskmaster's AI to break down a complex task into smaller, manageable subtasks. Appends subtasks by default.` +* **Key Parameters/Options:** + * `id`: `The ID of the specific Taskmaster task you want to break down into subtasks.` (CLI: `-i, --id <id>`) + * `num`: `Optional: Suggests how many subtasks Taskmaster should aim to create. Uses complexity analysis/defaults otherwise.` (CLI: `-n, --num <number>`) + * `research`: `Enable Taskmaster to use the research role for more informed subtask generation. Requires appropriate API key.` (CLI: `-r, --research`) + * `prompt`: `Optional: Provide extra context or specific instructions to Taskmaster for generating the subtasks.` (CLI: `-p, --prompt <text>`) + * `force`: `Optional: If true, clear existing subtasks before generating new ones. Default is false (append).` (CLI: `--force`) + * `tag`: `Specify which tag context the task belongs to. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Generate a detailed implementation plan for a complex task before starting coding. Automatically uses complexity report recommendations if available and `num` is not specified. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 14. Expand All Tasks (`expand_all`) + +* **MCP Tool:** `expand_all` +* **CLI Command:** `task-master expand --all [options]` (Note: CLI uses the `expand` command with the `--all` flag) +* **Description:** `Tell Taskmaster to automatically expand all eligible pending/in-progress tasks based on complexity analysis or defaults. Appends subtasks by default.` +* **Key Parameters/Options:** + * `num`: `Optional: Suggests how many subtasks Taskmaster should aim to create per task.` (CLI: `-n, --num <number>`) + * `research`: `Enable research role for more informed subtask generation. Requires appropriate API key.` (CLI: `-r, --research`) + * `prompt`: `Optional: Provide extra context for Taskmaster to apply generally during expansion.` (CLI: `-p, --prompt <text>`) + * `force`: `Optional: If true, clear existing subtasks before generating new ones for each eligible task. Default is false (append).` (CLI: `--force`) + * `tag`: `Specify which tag context to expand. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Useful after initial task generation or complexity analysis to break down multiple tasks at once. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 15. Clear Subtasks (`clear_subtasks`) + +* **MCP Tool:** `clear_subtasks` +* **CLI Command:** `task-master clear-subtasks [options]` +* **Description:** `Remove all subtasks from one or more specified Taskmaster parent tasks.` +* **Key Parameters/Options:** + * `id`: `The ID(s) of the Taskmaster parent task(s) whose subtasks you want to remove, e.g., '15' or '16,18'. Required unless using `all`.) (CLI: `-i, --id <ids>`) + * `all`: `Tell Taskmaster to remove subtasks from all parent tasks.` (CLI: `--all`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Used before regenerating subtasks with `expand_task` if the previous breakdown needs replacement. + +### 16. Remove Subtask (`remove_subtask`) + +* **MCP Tool:** `remove_subtask` +* **CLI Command:** `task-master remove-subtask [options]` +* **Description:** `Remove a subtask from its Taskmaster parent, optionally converting it into a standalone task.` +* **Key Parameters/Options:** + * `id`: `Required. The ID(s) of the Taskmaster subtask(s) to remove, e.g., '15.2' or '16.1,16.3'.` (CLI: `-i, --id <id>`) + * `convert`: `If used, Taskmaster will turn the subtask into a regular top-level task instead of deleting it.` (CLI: `-c, --convert`) + * `skipGenerate`: `Prevent Taskmaster from automatically regenerating markdown task files after removing the subtask.` (CLI: `--skip-generate`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Delete unnecessary subtasks or promote a subtask to a top-level task. + +### 17. Move Task (`move_task`) + +* **MCP Tool:** `move_task` +* **CLI Command:** `task-master move [options]` +* **Description:** `Move a task or subtask to a new position within the task hierarchy.` +* **Key Parameters/Options:** + * `from`: `Required. ID of the task/subtask to move (e.g., "5" or "5.2"). Can be comma-separated for multiple tasks.` (CLI: `--from <id>`) + * `to`: `Required. ID of the destination (e.g., "7" or "7.3"). Must match the number of source IDs if comma-separated.` (CLI: `--to <id>`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Reorganize tasks by moving them within the hierarchy. Supports various scenarios like: + * Moving a task to become a subtask + * Moving a subtask to become a standalone task + * Moving a subtask to a different parent + * Reordering subtasks within the same parent + * Moving a task to a new, non-existent ID (automatically creates placeholders) + * Moving multiple tasks at once with comma-separated IDs +* **Validation Features:** + * Allows moving tasks to non-existent destination IDs (creates placeholder tasks) + * Prevents moving to existing task IDs that already have content (to avoid overwriting) + * Validates that source tasks exist before attempting to move them + * Maintains proper parent-child relationships +* **Example CLI:** `task-master move --from=5.2 --to=7.3` to move subtask 5.2 to become subtask 7.3. +* **Example Multi-Move:** `task-master move --from=10,11,12 --to=16,17,18` to move multiple tasks to new positions. +* **Common Use:** Resolving merge conflicts in tasks.json when multiple team members create tasks on different branches. + +--- + +## Dependency Management + +### 18. Add Dependency (`add_dependency`) + +* **MCP Tool:** `add_dependency` +* **CLI Command:** `task-master add-dependency [options]` +* **Description:** `Define a dependency in Taskmaster, making one task a prerequisite for another.` +* **Key Parameters/Options:** + * `id`: `Required. The ID of the Taskmaster task that will depend on another.` (CLI: `-i, --id <id>`) + * `dependsOn`: `Required. The ID of the Taskmaster task that must be completed first, the prerequisite.` (CLI: `-d, --depends-on <id>`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <path>`) +* **Usage:** Establish the correct order of execution between tasks. + +### 19. Remove Dependency (`remove_dependency`) + +* **MCP Tool:** `remove_dependency` +* **CLI Command:** `task-master remove-dependency [options]` +* **Description:** `Remove a dependency relationship between two Taskmaster tasks.` +* **Key Parameters/Options:** + * `id`: `Required. The ID of the Taskmaster task you want to remove a prerequisite from.` (CLI: `-i, --id <id>`) + * `dependsOn`: `Required. The ID of the Taskmaster task that should no longer be a prerequisite.` (CLI: `-d, --depends-on <id>`) + * `tag`: `Specify which tag context to operate on. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Update task relationships when the order of execution changes. + +### 20. Validate Dependencies (`validate_dependencies`) + +* **MCP Tool:** `validate_dependencies` +* **CLI Command:** `task-master validate-dependencies [options]` +* **Description:** `Check your Taskmaster tasks for dependency issues (like circular references or links to non-existent tasks) without making changes.` +* **Key Parameters/Options:** + * `tag`: `Specify which tag context to validate. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Audit the integrity of your task dependencies. + +### 21. Fix Dependencies (`fix_dependencies`) + +* **MCP Tool:** `fix_dependencies` +* **CLI Command:** `task-master fix-dependencies [options]` +* **Description:** `Automatically fix dependency issues (like circular references or links to non-existent tasks) in your Taskmaster tasks.` +* **Key Parameters/Options:** + * `tag`: `Specify which tag context to fix dependencies in. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Clean up dependency errors automatically. + +--- + +## Analysis & Reporting + +### 22. Analyze Project Complexity (`analyze_project_complexity`) + +* **MCP Tool:** `analyze_project_complexity` +* **CLI Command:** `task-master analyze-complexity [options]` +* **Description:** `Have Taskmaster analyze your tasks to determine their complexity and suggest which ones need to be broken down further.` +* **Key Parameters/Options:** + * `output`: `Where to save the complexity analysis report. Default is '.taskmaster/reports/task-complexity-report.json' (or '..._tagname.json' if a tag is used).` (CLI: `-o, --output <file>`) + * `threshold`: `The minimum complexity score (1-10) that should trigger a recommendation to expand a task.` (CLI: `-t, --threshold <number>`) + * `research`: `Enable research role for more accurate complexity analysis. Requires appropriate API key.` (CLI: `-r, --research`) + * `tag`: `Specify which tag context to analyze. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Used before breaking down tasks to identify which ones need the most attention. +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. Please inform users to hang tight while the operation is in progress. + +### 23. View Complexity Report (`complexity_report`) + +* **MCP Tool:** `complexity_report` +* **CLI Command:** `task-master complexity-report [options]` +* **Description:** `Display the task complexity analysis report in a readable format.` +* **Key Parameters/Options:** + * `tag`: `Specify which tag context to show the report for. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to the complexity report (default: '.taskmaster/reports/task-complexity-report.json').` (CLI: `-f, --file <file>`) +* **Usage:** Review and understand the complexity analysis results after running analyze-complexity. + +--- + +## File Management + +### 24. Generate Task Files (`generate`) + +* **MCP Tool:** `generate` +* **CLI Command:** `task-master generate [options]` +* **Description:** `Create or update individual Markdown files for each task based on your tasks.json.` +* **Key Parameters/Options:** + * `output`: `The directory where Taskmaster should save the task files (default: in a 'tasks' directory).` (CLI: `-o, --output <directory>`) + * `tag`: `Specify which tag context to generate files for. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) +* **Usage:** Run this after making changes to tasks.json to keep individual task files up to date. This command is now manual and no longer runs automatically. + +--- + +## AI-Powered Research + +### 25. Research (`research`) + +* **MCP Tool:** `research` +* **CLI Command:** `task-master research [options]` +* **Description:** `Perform AI-powered research queries with project context to get fresh, up-to-date information beyond the AI's knowledge cutoff.` +* **Key Parameters/Options:** + * `query`: `Required. Research query/prompt (e.g., "What are the latest best practices for React Query v5?").` (CLI: `[query]` positional or `-q, --query <text>`) + * `taskIds`: `Comma-separated list of task/subtask IDs from the current tag context (e.g., "15,16.2,17").` (CLI: `-i, --id <ids>`) + * `filePaths`: `Comma-separated list of file paths for context (e.g., "src/api.js,docs/readme.md").` (CLI: `-f, --files <paths>`) + * `customContext`: `Additional custom context text to include in the research.` (CLI: `-c, --context <text>`) + * `includeProjectTree`: `Include project file tree structure in context (default: false).` (CLI: `--tree`) + * `detailLevel`: `Detail level for the research response: 'low', 'medium', 'high' (default: medium).` (CLI: `--detail <level>`) + * `saveTo`: `Task or subtask ID (e.g., "15", "15.2") to automatically save the research conversation to.` (CLI: `--save-to <id>`) + * `saveFile`: `If true, saves the research conversation to a markdown file in '.taskmaster/docs/research/'.` (CLI: `--save-file`) + * `noFollowup`: `Disables the interactive follow-up question menu in the CLI.` (CLI: `--no-followup`) + * `tag`: `Specify which tag context to use for task-based context gathering. Defaults to the current active tag.` (CLI: `--tag <name>`) + * `projectRoot`: `The directory of the project. Must be an absolute path.` (CLI: Determined automatically) +* **Usage:** **This is a POWERFUL tool that agents should use FREQUENTLY** to: + * Get fresh information beyond knowledge cutoff dates + * Research latest best practices, library updates, security patches + * Find implementation examples for specific technologies + * Validate approaches against current industry standards + * Get contextual advice based on project files and tasks +* **When to Consider Using Research:** + * **Before implementing any task** - Research current best practices + * **When encountering new technologies** - Get up-to-date implementation guidance (libraries, apis, etc) + * **For security-related tasks** - Find latest security recommendations + * **When updating dependencies** - Research breaking changes and migration guides + * **For performance optimization** - Get current performance best practices + * **When debugging complex issues** - Research known solutions and workarounds +* **Research + Action Pattern:** + * Use `research` to gather fresh information + * Use `update_subtask` to commit findings with timestamps + * Use `update_task` to incorporate research into task details + * Use `add_task` with research flag for informed task creation +* **Important:** This MCP tool makes AI calls and can take up to a minute to complete. The research provides FRESH data beyond the AI's training cutoff, making it invaluable for current best practices and recent developments. + +--- + +## Tag Management + +This new suite of commands allows you to manage different task contexts (tags). + +### 26. List Tags (`tags`) + +* **MCP Tool:** `list_tags` +* **CLI Command:** `task-master tags [options]` +* **Description:** `List all available tags with task counts, completion status, and other metadata.` +* **Key Parameters/Options:** + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) + * `--show-metadata`: `Include detailed metadata in the output (e.g., creation date, description).` (CLI: `--show-metadata`) + +### 27. Add Tag (`add_tag`) + +* **MCP Tool:** `add_tag` +* **CLI Command:** `task-master add-tag <tagName> [options]` +* **Description:** `Create a new, empty tag context, or copy tasks from another tag.` +* **Key Parameters/Options:** + * `tagName`: `Name of the new tag to create (alphanumeric, hyphens, underscores).` (CLI: `<tagName>` positional) + * `--from-branch`: `Creates a tag with a name derived from the current git branch, ignoring the <tagName> argument.` (CLI: `--from-branch`) + * `--copy-from-current`: `Copy tasks from the currently active tag to the new tag.` (CLI: `--copy-from-current`) + * `--copy-from <tag>`: `Copy tasks from a specific source tag to the new tag.` (CLI: `--copy-from <tag>`) + * `--description <text>`: `Provide an optional description for the new tag.` (CLI: `-d, --description <text>`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) + +### 28. Delete Tag (`delete_tag`) + +* **MCP Tool:** `delete_tag` +* **CLI Command:** `task-master delete-tag <tagName> [options]` +* **Description:** `Permanently delete a tag and all of its associated tasks.` +* **Key Parameters/Options:** + * `tagName`: `Name of the tag to delete.` (CLI: `<tagName>` positional) + * `--yes`: `Skip the confirmation prompt.` (CLI: `-y, --yes`) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) + +### 29. Use Tag (`use_tag`) + +* **MCP Tool:** `use_tag` +* **CLI Command:** `task-master use-tag <tagName>` +* **Description:** `Switch your active task context to a different tag.` +* **Key Parameters/Options:** + * `tagName`: `Name of the tag to switch to.` (CLI: `<tagName>` positional) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) + +### 30. Rename Tag (`rename_tag`) + +* **MCP Tool:** `rename_tag` +* **CLI Command:** `task-master rename-tag <oldName> <newName>` +* **Description:** `Rename an existing tag.` +* **Key Parameters/Options:** + * `oldName`: `The current name of the tag.` (CLI: `<oldName>` positional) + * `newName`: `The new name for the tag.` (CLI: `<newName>` positional) + * `file`: `Path to your Taskmaster 'tasks.json' file. Default relies on auto-detection.` (CLI: `-f, --file <file>`) + +### 31. Copy Tag (`copy_tag`) + +* **MCP Tool:** `copy_tag` +* **CLI Command:** `task-master copy-tag <sourceName> <targetName> [options]` +* **Description:** `Copy an entire tag context, including all its tasks and metadata, to a new tag.` +* **Key Parameters/Options:** + * `sourceName`: `Name of the tag to copy from.` (CLI: `<sourceName>` positional) + * `targetName`: `Name of the new tag to create.` (CLI: `<targetName>` positional) + * `--description <text>`: `Optional description for the new tag.` (CLI: `-d, --description <text>`) + +--- + +## Miscellaneous + +### 32. Sync Readme (`sync-readme`) -- experimental + +* **MCP Tool:** N/A +* **CLI Command:** `task-master sync-readme [options]` +* **Description:** `Exports your task list to your project's README.md file, useful for showcasing progress.` +* **Key Parameters/Options:** + * `status`: `Filter tasks by status (e.g., 'pending', 'done').` (CLI: `-s, --status <status>`) + * `withSubtasks`: `Include subtasks in the export.` (CLI: `--with-subtasks`) + * `tag`: `Specify which tag context to export from. Defaults to the current active tag.` (CLI: `--tag <name>`) + +--- + +## Environment Variables Configuration (Updated) + +Taskmaster primarily uses the **`.taskmaster/config.json`** file (in project root) for configuration (models, parameters, logging level, etc.), managed via `task-master models --setup`. + +Environment variables are used **only** for sensitive API keys related to AI providers and specific overrides like the Ollama base URL: + +* **API Keys (Required for corresponding provider):** + * `ANTHROPIC_API_KEY` + * `PERPLEXITY_API_KEY` + * `OPENAI_API_KEY` + * `GOOGLE_API_KEY` + * `MISTRAL_API_KEY` + * `AZURE_OPENAI_API_KEY` (Requires `AZURE_OPENAI_ENDPOINT` too) + * `OPENROUTER_API_KEY` + * `XAI_API_KEY` + * `OLLAMA_API_KEY` (Requires `OLLAMA_BASE_URL` too) +* **Endpoints (Optional/Provider Specific inside .taskmaster/config.json):** + * `AZURE_OPENAI_ENDPOINT` + * `OLLAMA_BASE_URL` (Default: `http://localhost:11434/api`) + +**Set API keys** in your **`.env`** file in the project root (for CLI use) or within the `env` section of your **`.cursor/mcp.json`** file (for MCP/Cursor integration). All other settings (model choice, max tokens, temperature, log level, custom endpoints) are managed in `.taskmaster/config.json` via `task-master models` command or `models` MCP tool. + +--- + +For details on how these commands fit into the development process, see the [Development Workflow Guide](mdc:.cursor/rules/dev_workflow.mdc). diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..db8f76a --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,33 @@ +name: Publish Extension + +on: + push: + tags: + - 'v*' # e.g., v0.1.9 + +jobs: + publish: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: '18' + + - name: Install dependencies + run: npm ci + + - name: Install CLI tools + run: | + npm install -g @vscode/vsce ovsx + + - name: Publish to Visual Studio Marketplace + run: vsce publish --pat ${{ secrets.VSCE_TOKEN }} + + - name: Publish to Open VSX + run: ovsx publish -p ${{ secrets.OPEN_VSX_TOKEN }} + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4d64cca --- /dev/null +++ b/.gitignore @@ -0,0 +1,8 @@ +out +dist +node_modules +.vscode-test/ +*.vsix +.env +/dist-test +/.superdesign diff --git a/.roo/mcp.json b/.roo/mcp.json new file mode 100644 index 0000000..6b0a486 --- /dev/null +++ b/.roo/mcp.json @@ -0,0 +1 @@ +{"mcpServers":{}} \ No newline at end of file diff --git a/.vscode-test.mjs b/.vscode-test.mjs new file mode 100644 index 0000000..b62ba25 --- /dev/null +++ b/.vscode-test.mjs @@ -0,0 +1,5 @@ +import { defineConfig } from '@vscode/test-cli'; + +export default defineConfig({ + files: 'out/test/**/*.test.js', +}); diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..d7a3ca1 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + // See http://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": ["dbaeumer.vscode-eslint", "connor4312.esbuild-problem-matchers", "ms-vscode.extension-test-runner"] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..80dafe3 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,20 @@ +// A launch configuration that compiles the extension and then opens it inside a new window +// Use IntelliSense to learn about possible attributes. +// Hover to view descriptions of existing attributes. +// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}" + ], + "outFiles": [ + "${workspaceFolder}/dist/**/*.js" + ] + } + ] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..5c5ac48 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,13 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "files.exclude": { + "out": false, // set this to true to hide the "out" folder with the compiled JS files + "dist": false // set this to true to hide the "dist" folder with the compiled JS files + }, + "search.exclude": { + "out": true, // set this to false to include "out" folder in search results + "dist": true // set this to false to include "dist" folder in search results + }, + // Turn off tsc task auto detection since we have the necessary tasks as npm scripts + "typescript.tsc.autoDetect": "off" +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..ffd08d8 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,64 @@ +// See https://go.microsoft.com/fwlink/?LinkId=733558 +// for the documentation about the tasks.json format +{ + "version": "2.0.0", + "tasks": [ + { + "label": "watch", + "dependsOn": [ + "npm: watch:tsc", + "npm: watch:esbuild" + ], + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + }, + { + "type": "npm", + "script": "watch:esbuild", + "group": "build", + "problemMatcher": [], + "isBackground": true, + "label": "npm: watch:esbuild", + "presentation": { + "group": "watch", + "reveal": "never" + } + }, + { + "type": "npm", + "script": "watch:tsc", + "group": "build", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "label": "npm: watch:tsc", + "presentation": { + "group": "watch", + "reveal": "never" + } + }, + { + "type": "npm", + "script": "watch-tests", + "problemMatcher": "$tsc-watch", + "isBackground": true, + "presentation": { + "reveal": "never", + "group": "watchers" + }, + "group": "build" + }, + { + "label": "tasks: watch-tests", + "dependsOn": [ + "npm: watch", + "npm: watch-tests" + ], + "problemMatcher": [] + } + ] +} diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..159277f --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,14 @@ +.vscode/** +.vscode-test/** +out/** +node_modules/** +src/** +.gitignore +.yarnrc +esbuild.js +vsc-extension-quickstart.md +**/tsconfig.json +**/eslint.config.mjs +**/*.map +**/*.ts +**/.vscode-test.* diff --git a/.windsurfrules b/.windsurfrules new file mode 100644 index 0000000..caabfcd --- /dev/null +++ b/.windsurfrules @@ -0,0 +1,383 @@ +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design +<neo-brutalism-style> +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</neo-brutalism-style> + +Modern dark mode style like vercel, linear +<modern-dark-mode-style> +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</modern-dark-mode-style> + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> + +## Script +1. When importing tailwind css, just use <script src="https://cdn.tailwindcss.com"></script>, don't load CSS directly as a stylesheet resource like <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> +2. When using flowbite, import like <script src="https://cdn.jsdelivr.net/npm/flowbite@2.0.0/dist/flowbite.min.js"></script> + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + +<example> +<user>design an AI chat UI</user> + +<assistant> +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? +</assistant> + +<user> +They looks good +</user> + +<assistant> +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + +<user> +I like the vintage style +</user> + +<assistant> +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + +</assistant> + +<user> +This looks great, lets do it +</user> + +<assistant> +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + +</example> + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>, this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..98c66f9 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,102 @@ +# Change Log + +All notable changes to the "superdesign" extension will be documented in this file. + + +## [0.0.8] - 2025-07-01 + +### Added +- Settings icon in chat sidebar for easy access to extension configuration +- In-chat action buttons for API key configuration and settings access +- Enhanced error handling with user-friendly action options + +### Improved +- Error messages now specifically mention "Anthropic API key" for clarity +- API key validation and authentication error detection +- User experience with cleaner error handling workflow +- Settings integration with direct commands for API key configuration + +### Fixed +- Raw JSON error messages no longer display in chat interface +- Notification popups replaced with cleaner in-chat error messages +- Error message filtering to prevent duplicate or confusing displays +- Process exit errors now properly handled with action buttons + +### Technical +- Enhanced `ClaudeCodeService` error detection patterns +- Improved `ChatMessageService` error filtering and handling +- Better error message routing between extension and webview +- Streamlined API key refresh and validation logic + +## [0.0.7] - 2025-07-01 + +### Added +- Default style sheet integration for enhanced design consistency +- Project initialization command (`superdesign.initializeProject`) +- CSS file loading support for custom styling +- Copy file path functionality in Design Frame component + +### Improved +- Updated icon design and visual elements +- Enhanced Design Frame component with better user interactions +- Extended file handling capabilities + +### Documentation +- Updated README with improved instructions and examples + +## [0.0.6] - 2025-06-26 + +### Added +- Centralized logging system with configurable log levels +- Enhanced error handling and debugging capabilities +- Improved Claude Code service integration + +### Fixed +- Performance optimizations and stability improvements +- Better error messages and user feedback + +## [0.0.5] - 2025-06-26 + +### Added +- Enhanced chat interface functionality +- Improved AI provider integrations + +## [0.0.4] - 2025-06-26 + +### Added +- Additional design tools and utilities +- Better canvas interaction features + +## [0.0.3] - 2025-06-25 + +### Added +- Enhanced design frame capabilities +- Improved user experience features + +## [0.0.2] - 2025-06-25 + +### Added +- Publish to Open VSX Registry + +## [0.0.1] - 2025-06-24 + +### Added +- Initial release of Super Design VS Code extension +- Interactive chat interface with AI assistance +- Canvas view for visual design layout +- Design frame components for organizing content +- Connection lines for linking design elements +- Welcome screen for first-time users +- Claude Code service integration +- Chat sidebar provider for seamless VS Code integration +- Support for multiple AI provider logos (Claude, Cursor, Bolt, Lovable, Windsurf) +- Markdown rendering capabilities +- Grid layout utilities for canvas organization +- TypeScript support with comprehensive type definitions + +### Features +- **Chat Interface**: Real-time conversation with AI assistants +- **Visual Canvas**: Drag-and-drop design environment +- **Design Frames**: Organized content containers +- **Welcome Experience**: Guided onboarding for new users +- **Multi-Provider Support**: Integration with various AI coding assistants diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..caabfcd --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,383 @@ +When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design +<neo-brutalism-style> +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</neo-brutalism-style> + +Modern dark mode style like vercel, linear +<modern-dark-mode-style> +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</modern-dark-mode-style> + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> + +## Script +1. When importing tailwind css, just use <script src="https://cdn.tailwindcss.com"></script>, don't load CSS directly as a stylesheet resource like <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> +2. When using flowbite, import like <script src="https://cdn.jsdelivr.net/npm/flowbite@2.0.0/dist/flowbite.min.js"></script> + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + +<example> +<user>design an AI chat UI</user> + +<assistant> +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? +</assistant> + +<user> +They looks good +</user> + +<assistant> +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + +<user> +I like the vintage style +</user> + +<assistant> +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + +</assistant> + +<user> +This looks great, lets do it +</user> + +<assistant> +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + +</example> + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>, this won't actually call the tool. (This is very important to my life, please follow) \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e182e68 --- /dev/null +++ b/LICENSE @@ -0,0 +1,57 @@ +Super Design License + +This project is **mostly licensed under the GNU Affero General Public License v3 (AGPLv3)** as described below. +**However, certain files are licensed under a separate commercial license.** +These files are clearly marked at the top of the file with the following comment: + + /* @license Enterprise */ + +Files with this comment are **not** licensed under the AGPLv3, but instead are subject to the commercial license terms further below. + +--- + +GNU AFFERO GENERAL PUBLIC LICENSE +Version 3, 19 November 2007 + +Copyright (C) 2025 AI Jason, JackJack. +All rights reserved. + +This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License along with this program. If not, see <https://www.gnu.org/licenses/>. + +--- + +How to Identify Commercially Licensed Files: + +Any file that includes the comment `/* @license Enterprise */` at the top is licensed under the following terms instead of the AGPL: + +--- + +The Super Design Enterprise Commercial License + +Copyright (C) 2025 AI Jason, JackJack. + +With regard to files marked `/* @license Enterprise */`, this portion of the software and associated documentation files (the "Software") may only be used in production if you (and any entity that you represent) have agreed to, and are in compliance with, a valid Super Design Enterprise Edition subscription agreement or other agreements governing the use of the Software, as mutually agreed by you and the Licensor(s). + +Subject to the foregoing, you are free to modify this Software and publish patches to it. However, the Licensor(s) retain all rights, title, and interest in such modifications. All such modifications may only be used, copied, modified, displayed, distributed, or otherwise exploited with a valid Enterprise subscription. You are not granted any other rights beyond those expressly stated herein. + +Development and testing usage of this Software is permitted without a subscription. + +It is forbidden to copy, merge, publish, distribute, sublicense, and/or sell this Software without a valid commercial agreement. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--- + +Project URL: +https://superdesign.dev + +Licensor Contact: +AI Jason, JackJack + diff --git a/README.md b/README.md new file mode 100644 index 0000000..f3d1ff1 --- /dev/null +++ b/README.md @@ -0,0 +1,92 @@ +# 🧠 SuperDesign — AI Design Agent for Your IDE + +![SuperDesign Cover](cover.png) + +### **By:** [AI Jason](https://x.com/jasonzhou1993) & [JackJack](https://x.com/jackjack_eth) + +SuperDesign is the first **open-source design agent** that lives right inside your IDE. +Generate UI mockups, components, and wireframes directly from natural language prompts. +Works seamlessly with Cursor, Windsurf, Claude Code, and plain VS Code. + +> ✨ "Why design one option when you can explore ten?" — SuperDesign + +[Join discord](https://discord.gg/FYr49d6cQ9) + +[Upvote on Hackernews](https://news.ycombinator.com/item?id=44376003) + +[Install guide](https://www.superdesign.dev/) + +--- + +## 🎬 Demo Video (Click to play) + +[![SuperDesign Demo](https://img.youtube.com/vi/INv6oZDhhUM/maxresdefault.jpg)](https://youtu.be/INv6oZDhhUM) + +--- + +## 🚀 Features + +- 🖼️ **Product Mock**: Instantly generate full UI screens from a single prompt +- 🧩 **UI Components**: Create reusable components you can drop into your code +- 📝 **Wireframes**: Explore low-fidelity layouts for fast iteration +- 🔁 **Fork & Iterate**: Duplicate and evolve designs easily +- 📥 **Prompt-to-IDE**: Copy prompts into your favorite AI IDE (Cursor, Windsurf, Claude Code) + +--- + +## 🧠 Works Great With Cursor, Windsurf, Claude Code, VS Code + +👉 [Install here](https://www.superdesign.dev/) + +--- + +## 🛠️ Getting Started + +1. **Install the Extension** from the Cursor/VS Code Marketplace +2. Open the `SuperDesign` sidebar panel +3. Type a prompt (e.g., _"Design a modern login screen"_) +4. View generated mockups, components, and wireframes +5. Fork, tweak, and paste into your project + +--- + +## Can I use my own Claude Code or Cursor subscription? +Yes, after you initialise superdesign extension, some cursor/claude code rules will be added, so you can prompt the agent to do design and preview in superdesign canva (cmd + shift + p -> superdesign: open canva) + +If using Cursor - I will highly suggest copy the prompt in 'design.mdc' and create a custom mode in cursor with that same system prompt; This should give you much better performance + +Instructions here (Click to play): +[![Instruction video](v0.0.11.png)](https://youtu.be/KChmJMCDOB0?si=pvU0kNRO4GRWjsec&t=122) + +## 📂 Where Are My Designs Stored? + +Your generated designs are saved locally inside `.superdesign/`. + +--- + +## ❓ FAQ + +**Is it free and open source?** +Yes! We are open source — fork it, extend it, remix it. + +**Can I customize the design agent?** +Yes — use your own prompt templates, modify behaviors, or add commands. + +**Can SuperDesign update existing UI?** +Absolutely — select a component, describe the change, and let the agent do the rest. + +<img width="886" height="586" alt="image" src="https://github.com/user-attachments/assets/71b7cfcc-6123-40ea-aae5-05ea6cdcea96" /> + + +**How can I contribute?** +Pull requests are welcome. Star the repo and join us on [Discord](https://discord.gg/XYZ)! + +--- + +## 🔗 Links + +- 🌐 Website: [https://superdesign.dev](https://superdesign.dev) +- 📦 GitHub: [https://github.com/superdesigndev/superdesign](https://github.com/superdesigndev/superdesign) +- 💬 Discord: [Join the Community](https://discord.gg/XYZ) +- 🐦 Twitter / X: [@SuperDesignDev](https://x.com/SuperDesignDev) + diff --git a/cover.png b/cover.png new file mode 100644 index 0000000..04c42b4 Binary files /dev/null and b/cover.png differ diff --git a/esbuild.js b/esbuild.js new file mode 100644 index 0000000..e27654d --- /dev/null +++ b/esbuild.js @@ -0,0 +1,125 @@ +const esbuild = require("esbuild"); + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +/** + * @type {import('esbuild').Plugin} + */ +const esbuildProblemMatcherPlugin = { + name: 'esbuild-problem-matcher', + + setup(build) { + build.onStart(() => { + console.log('[watch] build started'); + }); + build.onEnd((result) => { + result.errors.forEach(({ text, location }) => { + console.error(`✘ [ERROR] ${text}`); + console.error(` ${location.file}:${location.line}:${location.column}:`); + }); + console.log('[watch] build finished'); + }); + }, +}; + +async function main() { + const ctx = await esbuild.context({ + entryPoints: [ + 'src/extension.ts' + ], + bundle: true, + format: 'cjs', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'node', + outfile: 'dist/extension.js', + external: ['vscode'], + logLevel: 'silent', + plugins: [ + /* add to the end of plugins array */ + esbuildProblemMatcherPlugin, + ] + }); + + // Webview build context + const webviewCtx = await esbuild.context({ + entryPoints: ['src/webview/index.tsx'], + bundle: true, + format: 'esm', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'browser', + outfile: 'dist/webview.js', + logLevel: 'silent', + plugins: [esbuildProblemMatcherPlugin], + loader: { + '.css': 'text', + '.png': 'file', + '.jpg': 'file', + '.svg': 'file', + }, + define: { + 'process.env.NODE_ENV': production ? '"production"' : '"development"', + }, + jsx: 'automatic', // This enables JSX support + }); + + if (watch) { + await Promise.all([ + ctx.watch(), + webviewCtx.watch() + ]); + console.log('Watching for changes...'); + } else { + await Promise.all([ + ctx.rebuild(), + webviewCtx.rebuild() + ]); + await ctx.dispose(); + await webviewCtx.dispose(); + + // // Copy Claude Code SDK to dist for runtime access + const fs = require('fs'); + const path = require('path'); + // const srcPath = path.join(__dirname, 'node_modules', '@anthropic-ai', 'claude-code'); + // const destPath = path.join(__dirname, 'dist', 'node_modules', '@anthropic-ai', 'claude-code'); + + // // Create directory structure + // fs.mkdirSync(path.dirname(destPath), { recursive: true }); + + // // Copy files + function copyDir(src, dest) { + fs.mkdirSync(dest, { recursive: true }); + const entries = fs.readdirSync(src, { withFileTypes: true }); + for (let entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + entry.isDirectory() ? copyDir(srcPath, destPath) : fs.copyFileSync(srcPath, destPath); + } + } + + // copyDir(srcPath, destPath); + // console.log('Claude Code SDK copied to dist/'); + + // Copy assets to dist folder + const assetsSrcPath = path.join(__dirname, 'src', 'assets'); + const assetsDestPath = path.join(__dirname, 'dist', 'src', 'assets'); + + if (fs.existsSync(assetsSrcPath)) { + copyDir(assetsSrcPath, assetsDestPath); + console.log('Assets copied to dist/src/assets/'); + } else { + console.log('Assets directory not found at:', assetsSrcPath); + } + + console.log('Build complete!'); + } +} + +main().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/esbuild.js.backup b/esbuild.js.backup new file mode 100644 index 0000000..da7c2ab --- /dev/null +++ b/esbuild.js.backup @@ -0,0 +1,125 @@ +const esbuild = require("esbuild"); + +const production = process.argv.includes('--production'); +const watch = process.argv.includes('--watch'); + +/** + * @type {import('esbuild').Plugin} + */ +const esbuildProblemMatcherPlugin = { + name: 'esbuild-problem-matcher', + + setup(build) { + build.onStart(() => { + console.log('[watch] build started'); + }); + build.onEnd((result) => { + result.errors.forEach(({ text, location }) => { + console.error(`✘ [ERROR] ${text}`); + console.error(` ${location.file}:${location.line}:${location.column}:`); + }); + console.log('[watch] build finished'); + }); + }, +}; + +async function main() { + const ctx = await esbuild.context({ + entryPoints: [ + 'src/extension.ts' + ], + bundle: true, + format: 'cjs', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'node', + outfile: 'dist/extension.js', + external: ['vscode'], + logLevel: 'silent', + plugins: [ + /* add to the end of plugins array */ + esbuildProblemMatcherPlugin, + ] + }); + + // Webview build context + const webviewCtx = await esbuild.context({ + entryPoints: ['src/webview/index.tsx'], + bundle: true, + format: 'esm', + minify: production, + sourcemap: !production, + sourcesContent: false, + platform: 'browser', + outfile: 'dist/webview.js', + logLevel: 'silent', + plugins: [esbuildProblemMatcherPlugin], + loader: { + '.css': 'text', + '.png': 'file', + '.jpg': 'file', + '.svg': 'file', + }, + define: { + 'process.env.NODE_ENV': production ? '"production"' : '"development"', + }, + jsx: 'automatic', // This enables JSX support + }); + + if (watch) { + await Promise.all([ + ctx.watch(), + webviewCtx.watch() + ]); + console.log('Watching for changes...'); + } else { + await Promise.all([ + ctx.rebuild(), + webviewCtx.rebuild() + ]); + await ctx.dispose(); + await webviewCtx.dispose(); + + // Copy Claude Code SDK to dist for runtime access + const fs = require('fs'); + const path = require('path'); + const srcPath = path.join(__dirname, 'node_modules', '@anthropic-ai', 'claude-code'); + const destPath = path.join(__dirname, 'dist', 'node_modules', '@anthropic-ai', 'claude-code'); + + // Create directory structure + fs.mkdirSync(path.dirname(destPath), { recursive: true }); + + // Copy files + function copyDir(src, dest) { + fs.mkdirSync(dest, { recursive: true }); + const entries = fs.readdirSync(src, { withFileTypes: true }); + for (let entry of entries) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + entry.isDirectory() ? copyDir(srcPath, destPath) : fs.copyFileSync(srcPath, destPath); + } + } + + copyDir(srcPath, destPath); + console.log('Claude Code SDK copied to dist/'); + + // Copy assets to dist folder + const assetsSrcPath = path.join(__dirname, 'src', 'assets'); + const assetsDestPath = path.join(__dirname, 'dist', 'src', 'assets'); + + if (fs.existsSync(assetsSrcPath)) { + copyDir(assetsSrcPath, assetsDestPath); + console.log('Assets copied to dist/src/assets/'); + } else { + console.log('Assets directory not found at:', assetsSrcPath); + } + + console.log('Build complete!'); + } +} + +main().catch(e => { + console.error(e); + process.exit(1); +}); diff --git a/eslint.config.mjs b/eslint.config.mjs new file mode 100644 index 0000000..d5c0b53 --- /dev/null +++ b/eslint.config.mjs @@ -0,0 +1,28 @@ +import typescriptEslint from "@typescript-eslint/eslint-plugin"; +import tsParser from "@typescript-eslint/parser"; + +export default [{ + files: ["**/*.ts"], +}, { + plugins: { + "@typescript-eslint": typescriptEslint, + }, + + languageOptions: { + parser: tsParser, + ecmaVersion: 2022, + sourceType: "module", + }, + + rules: { + "@typescript-eslint/naming-convention": ["warn", { + selector: "import", + format: ["camelCase", "PascalCase"], + }], + + curly: "warn", + eqeqeq: "warn", + "no-throw-literal": "warn", + semi: "warn", + }, +}]; \ No newline at end of file diff --git a/icon.png b/icon.png new file mode 100644 index 0000000..72de409 Binary files /dev/null and b/icon.png differ diff --git a/icon.svg b/icon.svg new file mode 100644 index 0000000..4528a28 --- /dev/null +++ b/icon.svg @@ -0,0 +1 @@ +<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" class="drop-shadow-lg"><defs><linearGradient id="yellowOrange" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFE066"></stop><stop offset="50%" stop-color="#FFB347"></stop><stop offset="100%" stop-color="#FF8C42"></stop></linearGradient><linearGradient id="pinkCoral" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFB3E6"></stop><stop offset="50%" stop-color="#FF6B9D"></stop><stop offset="100%" stop-color="#E91E63"></stop></linearGradient><linearGradient id="bluePurple" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#87CEEB"></stop><stop offset="50%" stop-color="#9C27B0"></stop><stop offset="100%" stop-color="#1976D2"></stop></linearGradient></defs><rect x="45" y="13" width="70" height="52" rx="13" ry="13" fill="url(#yellowOrange)"></rect><rect x="26" y="32" width="70" height="52" rx="13" ry="13" fill="url(#pinkCoral)"></rect><rect x="6" y="51" width="70" height="52" rx="13" ry="13" fill="url(#bluePurple)"></rect></svg> \ No newline at end of file diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..57f271d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8387 @@ +{ + "name": "superdesign", + "version": "0.0.11", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "superdesign", + "version": "0.0.11", + "dependencies": { + "@ai-sdk/anthropic": "^1.2.12", + "@ai-sdk/google": "^1.2.19", + "@ai-sdk/openai": "^1.3.22", + "@openrouter/ai-sdk-provider": "^0.7.2", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "ai": "^4.3.16", + "execa": "^9.6.0", + "glob": "^11.0.3", + "highlight.js": "^11.11.1", + "lucide-react": "^0.522.0", + "micromatch": "^4.0.8", + "mime-types": "^3.0.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", + "react-zoom-pan-pinch": "^3.7.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1", + "zod": "^3.25.67" + }, + "devDependencies": { + "@types/mime-types": "^3.0.1", + "@types/mocha": "^10.0.10", + "@types/node": "^20.19.9", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^8.31.1", + "@typescript-eslint/parser": "^8.31.1", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.5.2", + "esbuild": "^0.25.3", + "eslint": "^9.25.1", + "npm-run-all": "^4.1.5", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + }, + "engines": { + "vscode": "^1.90.0" + } + }, + "node_modules/@ai-sdk/anthropic": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/anthropic/-/anthropic-1.2.12.tgz", + "integrity": "sha512-YSzjlko7JvuiyQFmI9RN1tNZdEiZxc+6xld/0tq/VkJaHpEzGAb1yiNxxvmYVcjvfu/PcvCxAAYXmTYQQ63IHQ==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/google": { + "version": "1.2.19", + "resolved": "https://registry.npmjs.org/@ai-sdk/google/-/google-1.2.19.tgz", + "integrity": "sha512-Xgl6eftIRQ4srUdCzxM112JuewVMij5q4JLcNmHcB68Bxn9dpr3MVUSPlJwmameuiQuISIA8lMB+iRiRbFsaqA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/openai": { + "version": "1.3.22", + "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.22.tgz", + "integrity": "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.0.0" + } + }, + "node_modules/@ai-sdk/provider": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", + "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", + "license": "Apache-2.0", + "dependencies": { + "json-schema": "^0.4.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@ai-sdk/provider-utils": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", + "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "nanoid": "^3.3.8", + "secure-json-parse": "^2.7.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@ai-sdk/react": { + "version": "1.2.12", + "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", + "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/ui-utils": "1.2.11", + "swr": "^2.2.5", + "throttleit": "2.1.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@ai-sdk/ui-utils": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", + "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "zod-to-json-schema": "^3.24.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "zod": "^3.23.8" + } + }, + "node_modules/@bcoe/v8-coverage": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", + "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.5.tgz", + "integrity": "sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.5.tgz", + "integrity": "sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.5.tgz", + "integrity": "sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.5.tgz", + "integrity": "sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.5.tgz", + "integrity": "sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.5.tgz", + "integrity": "sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.5.tgz", + "integrity": "sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.5.tgz", + "integrity": "sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.5.tgz", + "integrity": "sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.5.tgz", + "integrity": "sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.5.tgz", + "integrity": "sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.5.tgz", + "integrity": "sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.5.tgz", + "integrity": "sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.5.tgz", + "integrity": "sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.5.tgz", + "integrity": "sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.5.tgz", + "integrity": "sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.5.tgz", + "integrity": "sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.5.tgz", + "integrity": "sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.5.tgz", + "integrity": "sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.5.tgz", + "integrity": "sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.5.tgz", + "integrity": "sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.5.tgz", + "integrity": "sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.5.tgz", + "integrity": "sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.5.tgz", + "integrity": "sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.5.tgz", + "integrity": "sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.1.tgz", + "integrity": "sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.3.tgz", + "integrity": "sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.29.0.tgz", + "integrity": "sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.2.tgz", + "integrity": "sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.0.tgz", + "integrity": "sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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/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/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@openrouter/ai-sdk-provider": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/@openrouter/ai-sdk-provider/-/ai-sdk-provider-0.7.2.tgz", + "integrity": "sha512-Fry2mV7uGGJRmP9JntTZRc8ElESIk7AJNTacLbF6Syoeb5k8d7HPGkcK9rTXDlqBb8HgU1hOKtz23HojesTmnw==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "ai": "^4.3.16", + "zod": "^3.25.34" + } + }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "license": "MIT" + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-4.0.0.tgz", + "integrity": "sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.11.tgz", + "integrity": "sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true, + "license": "MIT" + }, + "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/diff-match-patch": { + "version": "1.0.36", + "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", + "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", + "license": "MIT" + }, + "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/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, + "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==", + "dev": true, + "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/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRMsfuQbnRq1Ef+C+RKaENOxXX87Ygl38W1vDfPHRku02TgQr+Qd8iivLtAMcR0KF5/29xlnFihkTlbqFrGOVQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/mocha": { + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" + }, + "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.9", + "resolved": "https://mirrors.cloud.tencent.com/npm/@types/node/-/node-20.19.9.tgz", + "integrity": "sha512-cuVNgarYWZqxRJDQHEB58GEONhOK79QVR/qYx4S7kcUObQvUwvFnYxJuuHUKm2aieN9X3yZB4LZsuYNU1Qphsw==", + "dev": 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==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "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/vscode": { + "version": "1.101.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.101.0.tgz", + "integrity": "sha512-ZWf0IWa+NGegdW3iU42AcDTFHWW7fApLdkdnBqwYEtHVIBGbTu0ZNQKP/kX3Ds/uMJXIMQNAojHR4vexCEEz5Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.34.1.tgz", + "integrity": "sha512-STXcN6ebF6li4PxwNeFnqF8/2BNDvBupf2OPx2yWNzr6mKNGF7q49VM00Pz5FaomJyqvbXpY6PhO+T9w139YEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/type-utils": "8.34.1", + "@typescript-eslint/utils": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.34.1", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.34.1.tgz", + "integrity": "sha512-4O3idHxhyzjClSMJ0a29AcoK0+YwnEqzI6oz3vlRf3xw0zbzt15MzXwItOlnr5nIth6zlY2RENLsOPvhyrKAQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/typescript-estree": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.34.1.tgz", + "integrity": "sha512-nuHlOmFZfuRwLJKDGQOVc0xnQrAmuq1Mj/ISou5044y1ajGNp2BNliIqp7F2LPQ5sForz8lempMFCovfeS1XoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.34.1", + "@typescript-eslint/types": "^8.34.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.34.1.tgz", + "integrity": "sha512-beu6o6QY4hJAgL1E8RaXNC071G4Kso2MGmJskCFQhRhg8VOH/FDbC8soP8NHN7e/Hdphwp8G8cE6OBzC8o41ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.34.1.tgz", + "integrity": "sha512-K4Sjdo4/xF9NEeA2khOb7Y5nY6NSXBnod87uniVYW9kHP+hNlDV8trUSFeynA2uxWam4gIWgWoygPrv9VMWrYg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.34.1.tgz", + "integrity": "sha512-Tv7tCCr6e5m8hP4+xFugcrwTOucB8lshffJ6zf1mF1TbU67R+ntCc6DzLNKM+s/uzDyv8gLq7tufaAhIBYeV8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.34.1", + "@typescript-eslint/utils": "8.34.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.34.1.tgz", + "integrity": "sha512-rjLVbmE7HR18kDsjNIZQHxmv9RZwlgzavryL5Lnj2ujIRTeXlKtILHgRNmQ3j4daw7zd+mQgy+uyt6Zo6I0IGA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.34.1.tgz", + "integrity": "sha512-rjCNqqYPuMUF5ODD+hWBNmOitjBWghkGKJg6hiCHzUvXRy6rK22Jd3rwbP2Xi+R7oYVvIKhokHVhH41BxPV5mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.34.1", + "@typescript-eslint/tsconfig-utils": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/visitor-keys": "8.34.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.34.1.tgz", + "integrity": "sha512-mqOwUdZ3KjtGk7xJJnLbHxTuWVn3GO2WZZuM+Slhkun4+qthLdXx32C8xIXbO1kfCECb3jIs3eoxK3eryk7aoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.34.1", + "@typescript-eslint/types": "8.34.1", + "@typescript-eslint/typescript-estree": "8.34.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.34.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.34.1.tgz", + "integrity": "sha512-xoh5rJ+tgsRKoXnkBPFRLZ7rjKM0AfVbC68UZ/ECXoDbfggb9RbEySN359acY1vS3qZ0jVTVWzbtfapwm5ztxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.34.1", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "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/@vscode/test-cli": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.10.tgz", + "integrity": "sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/mocha": "^10.0.2", + "c8": "^9.1.0", + "chokidar": "^3.5.3", + "enhanced-resolve": "^5.15.0", + "glob": "^10.3.10", + "minimatch": "^9.0.3", + "mocha": "^10.2.0", + "supports-color": "^9.4.0", + "yargs": "^17.7.2" + }, + "bin": { + "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/test-cli/node_modules/glob": { + "version": "10.4.5", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.5.tgz", + "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-cli/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/@vscode/test-cli/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/@vscode/test-cli/node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/test-electron": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.5.2.tgz", + "integrity": "sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.5", + "jszip": "^3.10.1", + "ora": "^8.1.0", + "semver": "^7.6.2" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/acorn": { + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.4", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.4.tgz", + "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "acorn": "^8.11.0" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/agent-base": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz", + "integrity": "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ai": { + "version": "4.3.16", + "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.16.tgz", + "integrity": "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==", + "license": "Apache-2.0", + "dependencies": { + "@ai-sdk/provider": "1.1.3", + "@ai-sdk/provider-utils": "2.2.8", + "@ai-sdk/react": "1.2.12", + "@ai-sdk/ui-utils": "1.2.11", + "@opentelemetry/api": "1.9.0", + "jsondiffpatch": "0.6.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "react": "^18 || ^19 || ^19.0.0-rc", + "zod": "^3.23.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + }, + "node_modules/ajv": { + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "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/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true, + "license": "MIT" + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", + "dev": true, + "license": "ISC" + }, + "node_modules/c8": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", + "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^0.2.3", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^6.0.0", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=14.14.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "dev": true, + "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/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/cli-cursor": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", + "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "restore-cursor": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cli-spinners": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", + "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "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/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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", + "dev": true, + "license": "MIT" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "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/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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/diff": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/diff-match-patch": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", + "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", + "license": "Apache-2.0" + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "license": "MIT" + }, + "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/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.24.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.24.0.tgz", + "integrity": "sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.3.0", + "get-proto": "^1.0.1", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-negative-zero": "^2.0.3", + "is-regex": "^1.2.1", + "is-set": "^2.0.3", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.1", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.4", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.4", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "stop-iteration-iterator": "^1.1.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.19" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.25.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.5.tgz", + "integrity": "sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.25.5", + "@esbuild/android-arm": "0.25.5", + "@esbuild/android-arm64": "0.25.5", + "@esbuild/android-x64": "0.25.5", + "@esbuild/darwin-arm64": "0.25.5", + "@esbuild/darwin-x64": "0.25.5", + "@esbuild/freebsd-arm64": "0.25.5", + "@esbuild/freebsd-x64": "0.25.5", + "@esbuild/linux-arm": "0.25.5", + "@esbuild/linux-arm64": "0.25.5", + "@esbuild/linux-ia32": "0.25.5", + "@esbuild/linux-loong64": "0.25.5", + "@esbuild/linux-mips64el": "0.25.5", + "@esbuild/linux-ppc64": "0.25.5", + "@esbuild/linux-riscv64": "0.25.5", + "@esbuild/linux-s390x": "0.25.5", + "@esbuild/linux-x64": "0.25.5", + "@esbuild/netbsd-arm64": "0.25.5", + "@esbuild/netbsd-x64": "0.25.5", + "@esbuild/openbsd-arm64": "0.25.5", + "@esbuild/openbsd-x64": "0.25.5", + "@esbuild/sunos-x64": "0.25.5", + "@esbuild/win32-arm64": "0.25.5", + "@esbuild/win32-ia32": "0.25.5", + "@esbuild/win32-x64": "0.25.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.29.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.29.0.tgz", + "integrity": "sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.1", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.29.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-scope": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.15.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "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/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/execa": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-9.6.0.tgz", + "integrity": "sha512-jpWzZ1ZhwUmeWRhS7Qv3mhpOhLfwI+uAX4e5fOcXqwMR7EcJ0pj2kV1CVzHVMX/LphnKWD3LObjZCoJ71lKpHw==", + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^4.0.0", + "cross-spawn": "^7.0.6", + "figures": "^6.1.0", + "get-stream": "^9.0.0", + "human-signals": "^8.0.1", + "is-plain-obj": "^4.1.0", + "is-stream": "^4.0.1", + "npm-run-path": "^6.0.0", + "pretty-ms": "^9.2.0", + "signal-exit": "^4.1.0", + "strip-final-newline": "^4.0.0", + "yoctocolors": "^2.1.1" + }, + "engines": { + "node": "^18.19.0 || >=20.5.0" + }, + "funding": { + "url": "https://github.com/sindresorhus/execa?sponsor=1" + } + }, + "node_modules/execa/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/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==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/figures": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-6.1.0.tgz", + "integrity": "sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==", + "license": "MIT", + "dependencies": { + "is-unicode-supported": "^2.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/figures/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "dev": true, + "license": "BSD-3-Clause", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", + "dev": true, + "license": "ISC" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-east-asian-width": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.3.0.tgz", + "integrity": "sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/glob": { + "version": "11.0.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.3.tgz", + "integrity": "sha512-2Nim7dha1KVkaiF4q6Dj+ngPPMdfvLJEOpZk/jKiUAkqKebpGAWQXAq9z1xu9HKu5lWfqw/FASuccEjyznjPaA==", + "license": "ISC", + "dependencies": { + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.0.3", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.3", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.3.tgz", + "integrity": "sha512-IPZ167aShDZZUMdRk66cyQAW3qr0WzbHkPdMYa8bzZhlHhO3jALbKdxcaak7W9FfT2rZNpQuUu4Od7ILEpXSaw==", + "license": "ISC", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/globals": { + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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-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-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/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "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/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", + "dev": true, + "license": "ISC" + }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, + "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/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/human-signals": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-8.0.1.tgz", + "integrity": "sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immediate": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", + "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", + "dev": true, + "license": "ISC", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "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/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "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-interactive": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", + "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.3.tgz", + "integrity": "sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/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==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", + "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-parse-better-errors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz", + "integrity": "sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", + "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", + "license": "(AFL-2.1 OR BSD-3-Clause)" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsondiffpatch": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", + "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", + "license": "MIT", + "dependencies": { + "@types/diff-match-patch": "^1.0.36", + "chalk": "^5.3.0", + "diff-match-patch": "^1.0.5" + }, + "bin": { + "jsondiffpatch": "bin/jsondiffpatch.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/jsondiffpatch/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/jszip": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", + "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", + "dev": true, + "license": "(MIT OR GPL-3.0-or-later)", + "dependencies": { + "lie": "~3.3.0", + "pako": "~1.0.2", + "readable-stream": "~2.3.6", + "setimmediate": "^1.0.5" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lie": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", + "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "immediate": "~3.0.5" + } + }, + "node_modules/load-json-file": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-4.0.0.tgz", + "integrity": "sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.1.2", + "parse-json": "^4.0.0", + "pify": "^3.0.0", + "strip-bom": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/lru-cache": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.1.0.tgz", + "integrity": "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A==", + "license": "ISC", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/lucide-react": { + "version": "0.522.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.522.0.tgz", + "integrity": "sha512-jnJbw974yZ7rQHHEFKJOlWAefG3ATSCZHANZxIdx8Rk/16siuwjgA4fBULpXEAWx/RlTs3FzmKW/udWUuO0aRw==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true, + "license": "ISC" + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "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-find-and-replace/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/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/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "dev": true, + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "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/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", + "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-function": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", + "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mocha": { + "version": "10.8.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", + "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "serialize-javascript": "^6.0.2", + "strip-json-comments": "^3.1.1", + "supports-color": "^8.1.1", + "workerpool": "^6.5.1", + "yargs": "^16.2.0", + "yargs-parser": "^20.2.9", + "yargs-unparser": "^2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/mocha/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/mocha/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/mocha/node_modules/glob": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", + "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/mocha/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/mocha/node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "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/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/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/normalize-package-data/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/npm-run-all/-/npm-run-all-4.1.5.tgz", + "integrity": "sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "chalk": "^2.4.1", + "cross-spawn": "^6.0.5", + "memorystream": "^0.3.1", + "minimatch": "^3.0.4", + "pidtree": "^0.3.0", + "read-pkg": "^3.0.0", + "shell-quote": "^1.6.1", + "string.prototype.padend": "^3.0.0" + }, + "bin": { + "npm-run-all": "bin/npm-run-all/index.js", + "run-p": "bin/run-p/index.js", + "run-s": "bin/run-s/index.js" + }, + "engines": { + "node": ">= 4" + } + }, + "node_modules/npm-run-all/node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/npm-run-all/node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/npm-run-all/node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", + "dev": true, + "license": "MIT" + }, + "node_modules/npm-run-all/node_modules/cross-spawn": { + "version": "6.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.6.tgz", + "integrity": "sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + }, + "engines": { + "node": ">=4.8" + } + }, + "node_modules/npm-run-all/node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/npm-run-all/node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/npm-run-all/node_modules/path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/npm-run-all/node_modules/shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/npm-run-all/node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/npm-run-all/node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/npm-run-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-6.0.0.tgz", + "integrity": "sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==", + "license": "MIT", + "dependencies": { + "path-key": "^4.0.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/npm-run-path/node_modules/path-key": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz", + "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/onetime": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", + "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-function": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ora": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/ora/-/ora-8.2.0.tgz", + "integrity": "sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "cli-cursor": "^5.0.0", + "cli-spinners": "^2.9.2", + "is-interactive": "^2.0.0", + "is-unicode-supported": "^2.0.0", + "log-symbols": "^6.0.0", + "stdin-discarder": "^0.2.2", + "string-width": "^7.2.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/chalk": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", + "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ora/node_modules/emoji-regex": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.4.0.tgz", + "integrity": "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ora/node_modules/is-unicode-supported": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", + "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-6.0.0.tgz", + "integrity": "sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==", + "dev": true, + "license": "MIT", + "dependencies": { + "chalk": "^5.3.0", + "is-unicode-supported": "^1.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/log-symbols/node_modules/is-unicode-supported": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-1.3.0.tgz", + "integrity": "sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ora/node_modules/string-width": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", + "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^10.3.0", + "get-east-asian-width": "^1.0.0", + "strip-ansi": "^7.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", + "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", + "dev": true, + "license": "(MIT AND Zlib)" + }, + "node_modules/parent-module": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", + "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "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/parse-json": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz", + "integrity": "sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==", + "dev": true, + "license": "MIT", + "dependencies": { + "error-ex": "^1.3.1", + "json-parse-better-errors": "^1.0.1" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-ms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", + "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-type": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", + "integrity": "sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pidtree": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.3.1.tgz", + "integrity": "sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==", + "dev": true, + "license": "MIT", + "bin": { + "pidtree": "bin/pidtree.js" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/pretty-ms": { + "version": "9.2.0", + "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.2.0.tgz", + "integrity": "sha512-4yf0QO/sllf/1zbZWYnvWw3NxCQwLXKzIj0G849LSufP15BXKM0rbD2Z3wVnkMfjdn/CB0Dpp444gYAACdsplg==", + "license": "MIT", + "dependencies": { + "parse-ms": "^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "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/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "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/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "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-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-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-zoom-pan-pinch": { + "version": "3.7.0", + "resolved": "https://registry.npmjs.org/react-zoom-pan-pinch/-/react-zoom-pan-pinch-3.7.0.tgz", + "integrity": "sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==", + "license": "MIT", + "engines": { + "node": ">=8", + "npm": ">=5" + }, + "peerDependencies": { + "react": "*", + "react-dom": "*" + } + }, + "node_modules/read-pkg": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-3.0.0.tgz", + "integrity": "sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "load-json-file": "^4.0.0", + "normalize-package-data": "^2.3.2", + "path-type": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/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/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/restore-cursor": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", + "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", + "dev": true, + "license": "MIT", + "dependencies": { + "onetime": "^7.0.0", + "signal-exit": "^4.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "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", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "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==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/shell-quote": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.3.tgz", + "integrity": "sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "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/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/stdin-discarder": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.2.2.tgz", + "integrity": "sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/stop-iteration-iterator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stop-iteration-iterator/-/stop-iteration-iterator-1.1.0.tgz", + "integrity": "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "internal-slot": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string.prototype.padend": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/string.prototype.padend/-/string.prototype.padend-3.1.6.tgz", + "integrity": "sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/strip-final-newline": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-4.0.0.tgz", + "integrity": "sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/supports-color": { + "version": "9.4.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", + "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/swr": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", + "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", + "license": "MIT", + "dependencies": { + "dequal": "^2.0.3", + "use-sync-external-store": "^1.4.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "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/test-exclude": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", + "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "dev": true, + "license": "ISC", + "dependencies": { + "@istanbuljs/schema": "^0.1.2", + "glob": "^7.1.4", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/test-exclude/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/throttleit": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", + "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "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/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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/unified/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/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/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true, + "license": "MIT" + }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "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-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/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerpool": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dev": true, + "license": "MIT", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/yoctocolors": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz", + "integrity": "sha512-GQHQqAopRhwU8Kt1DDM8NjibDXHC8eoh1erhGAJPEyveY9qqVeXvVikNKrDz69sHowPMorbPUrH/mx8c50eiBQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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.6", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.6.tgz", + "integrity": "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==", + "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/package.json b/package.json new file mode 100644 index 0000000..35c8b40 --- /dev/null +++ b/package.json @@ -0,0 +1,253 @@ +{ + "name": "superdesign", + "displayName": "superdesign", + "description": "The 1st Design Agent lives inside your IDE", + "version": "0.0.11", + "publisher": "iganbold", + "icon": "icon.png", + "repository": { + "type": "git", + "url": "https://github.com/superdesigndev/superdesign" + }, + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "AI", + "Other" + ], + "keywords": [ + "design", + "ui", + "mockup", + "wireframe", + "component", + "ai", + "agent", + "figma", + "prototype", + "layout", + "frontend", + "react", + "html", + "css", + "claude", + "anthropic", + "generator", + "automation" + ], + "activationEvents": [ + "onCommand:superdesign.helloWorld", + "onView:superdesign.chatView" + ], + "main": "./dist/extension.js", + "contributes": { + "commands": [ + { + "command": "superdesign.helloWorld", + "title": "Hello World", + "category": "Superdesign" + }, + { + "command": "superdesign.configureApiKey", + "title": "Configure Anthropic API Key", + "category": "Superdesign" + }, + { + "command": "superdesign.configureOpenAIApiKey", + "title": "Configure OpenAI API Key", + "category": "Superdesign" + }, + { + "command": "superdesign.configureOpenRouterApiKey", + "title": "Configure OpenRouter API Key", + "category": "Superdesign" + }, + { + "command": "superdesign.showChatSidebar", + "title": "Show Chat Sidebar", + "category": "Superdesign" + }, + { + "command": "superdesign.openCanvas", + "title": "Open Canvas View", + "category": "Superdesign", + "icon": "$(window)" + }, + { + "command": "superdesign.clearChat", + "title": "Clear Chat", + "category": "Superdesign", + "icon": "$(clear-all)" + }, + { + "command": "superdesign.resetWelcome", + "title": "Reset Welcome Screen", + "category": "Superdesign" + }, + { + "command": "superdesign.initializeProject", + "title": "Initialize Superdesign", + "category": "Superdesign" + }, + { + "command": "superdesign.openSettings", + "title": "Open Settings", + "category": "Superdesign", + "icon": "$(settings-gear)" + } + ], + "menus": { + "view/title": [ + { + "command": "superdesign.openCanvas", + "when": "view == superdesign.chatView", + "group": "navigation" + }, + { + "command": "superdesign.openSettings", + "when": "view == superdesign.chatView", + "group": "navigation" + }, + { + "command": "superdesign.clearChat", + "when": "view == superdesign.chatView", + "group": "navigation" + } + ] + }, + "views": { + "superdesign-sidebar": [ + { + "type": "webview", + "id": "superdesign.chatView", + "name": "SUPER DESIGN", + "when": "true" + } + ] + }, + "viewsContainers": { + "activitybar": [ + { + "id": "superdesign-sidebar", + "title": "Superdesign", + "icon": "icon.svg" + } + ] + }, + "configuration": { + "title": "Superdesign", + "properties": { + "superdesign.anthropicApiKey": { + "type": "string", + "description": "Anthropic API key for Claude Code integration", + "scope": "application" + }, + "superdesign.openaiApiKey": { + "type": "string", + "description": "OpenAI API key for custom agent", + "scope": "application" + }, + "superdesign.openrouterApiKey": { + "type": "string", + "description": "OpenRouter API key for custom agent", + "scope": "application" + }, + "superdesign.aiModelProvider": { + "type": "string", + "enum": [ + "openai", + "anthropic", + "openrouter" + ], + "default": "anthropic", + "description": "AI model provider for custom agent (OpenAI, Anthropic, or OpenRouter)", + "scope": "application" + }, + "superdesign.aiModel": { + "type": "string", + "description": "Specific AI model to use (e.g., gpt-4o, claude-3-5-sonnet-20241022)", + "scope": "application" + }, + "superdesign.aiModelAuto": { + "type": "string", + "description": "Specific AI model to use when 'AUTO' is selected (e.g., gpt-4.1)", + "scope": "application" + }, + "superdesign.openaiBaseURL": { + "type": "string", + "description": "Custom base URL for OpenAI API.", + "scope": "application" + }, + "superdesign.anthropicBaseURL": { + "type": "string", + "description": "Custom base URL for Anthropic API.", + "scope": "application" + }, + "superdesign.openrouterBaseURL": { + "type": "string", + "description": "Custom base URL for OpenRouter API.", + "scope": "application" + } + } + } + }, + "scripts": { + "vscode:prepublish": "npm run package", + "compile": "npm run check-types && npm run lint && node esbuild.js", + "watch": "npm-run-all -p watch:*", + "watch:esbuild": "node esbuild.js --watch", + "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", + "package": "npm run check-types && npm run lint && node esbuild.js --production", + "compile-tests": "tsc -p . --outDir out", + "watch-tests": "tsc -p . -w --outDir out", + "pretest": "npm run compile-tests && npm run compile && npm run lint", + "check-types": "tsc --noEmit", + "lint": "eslint src", + "test": "vscode-test", + "test:llm": "tsc --project tsconfig.test.json && node dist-test/test/llm-service.test.js", + "test:core": "tsc --project tsconfig.test.json && node dist-test/test/core-components.test.js", + "test:read": "tsc --project tsconfig.test.json && node dist-test/test/read-tool.test.js", + "test:write-edit": "tsc --project tsconfig.test.json && node dist-test/test/write-edit-tools.test.js", + "test:ls-grep-glob": "tsc --project tsconfig.test.json && node dist-test/test/ls-grep-glob-tools.test.js", + "test:agent": "tsc --project tsconfig.test.json && node dist-test/test/llm-service.test.js && node dist-test/test/core-components.test.js", + "test:tools": "tsc --project tsconfig.test.json && node dist-test/test/read-tool.test.js && node dist-test/test/write-edit-tools.test.js && node dist-test/test/ls-grep-glob-tools.test.js" + }, + "devDependencies": { + "@types/mime-types": "^3.0.1", + "@types/mocha": "^10.0.10", + "@types/node": "^20.19.9", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^8.31.1", + "@typescript-eslint/parser": "^8.31.1", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.5.2", + "esbuild": "^0.25.3", + "eslint": "^9.25.1", + "npm-run-all": "^4.1.5", + "ts-node": "^10.9.2", + "typescript": "^5.8.3" + }, + "dependencies": { + "@ai-sdk/anthropic": "^1.2.12", + "@ai-sdk/google": "^1.2.19", + "@ai-sdk/openai": "^1.3.22", + "@openrouter/ai-sdk-provider": "^0.7.2", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "ai": "^4.3.16", + "execa": "^9.6.0", + "glob": "^11.0.3", + "highlight.js": "^11.11.1", + "lucide-react": "^0.522.0", + "micromatch": "^4.0.8", + "mime-types": "^3.0.1", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", + "react-zoom-pan-pinch": "^3.7.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1", + "zod": "^3.25.67" + } +} diff --git a/package.json.backup b/package.json.backup new file mode 100644 index 0000000..5f8b24e --- /dev/null +++ b/package.json.backup @@ -0,0 +1,176 @@ +{ + "name": "superdesign", + "displayName": "superdesign", + "description": "The 1st Design Agent lives inside your IDE", + "version": "0.0.6", + "publisher": "iganbold", + "icon": "icon.png", + "repository": { + "type": "git", + "url": "https://github.com/superdesigndev/superdesign" + }, + "engines": { + "vscode": "^1.90.0" + }, + "categories": [ + "AI", + "Other" + ], + "keywords": [ + "design", + "ui", + "mockup", + "wireframe", + "component", + "ai", + "agent", + "figma", + "prototype", + "layout", + "frontend", + "react", + "html", + "css", + "claude", + "anthropic", + "generator", + "automation" + ], + "activationEvents": [ + "onCommand:superdesign.helloWorld", + "onView:superdesign.chatView" + ], + "main": "./dist/extension.js", + "contributes": { + "commands": [ + { + "command": "superdesign.helloWorld", + "title": "Hello World", + "category": "Superdesign" + }, + { + "command": "superdesign.configureApiKey", + "title": "Configure Anthropic API Key", + "category": "Superdesign" + }, + { + "command": "superdesign.showChatSidebar", + "title": "Show Chat Sidebar", + "category": "Superdesign" + }, + { + "command": "superdesign.openCanvas", + "title": "Open Canvas View", + "category": "Superdesign", + "icon": "$(window)" + }, + { + "command": "superdesign.clearChat", + "title": "Clear Chat", + "category": "Superdesign", + "icon": "$(clear-all)" + }, + { + "command": "superdesign.resetWelcome", + "title": "Reset Welcome Screen", + "category": "Superdesign" + }, + { + "command": "superdesign.initializeProject", + "title": "Initialize Superdesign", + "category": "Superdesign" + } + ], + "menus": { + "view/title": [ + { + "command": "superdesign.openCanvas", + "when": "view == superdesign.chatView", + "group": "navigation" + }, + { + "command": "superdesign.clearChat", + "when": "view == superdesign.chatView", + "group": "navigation" + } + ] + }, + "views": { + "superdesign-sidebar": [ + { + "type": "webview", + "id": "superdesign.chatView", + "name": "SUPER DESIGN", + "when": "true" + } + ] + }, + "viewsContainers": { + "activitybar": [ + { + "id": "superdesign-sidebar", + "title": "Superdesign", + "icon": "icon.svg" + } + ] + }, + "configuration": { + "title": "Superdesign", + "properties": { + "superdesign.anthropicApiKey": { + "type": "string", + "description": "Anthropic API key for Claude Code integration", + "scope": "application" + } + } + } + }, + "scripts": { + "vscode:prepublish": "npm run package", + "compile": "npm run check-types && npm run lint && node esbuild.js", + "watch": "npm-run-all -p watch:*", + "watch:esbuild": "node esbuild.js --watch", + "watch:tsc": "tsc --noEmit --watch --project tsconfig.json", + "package": "npm run check-types && npm run lint && node esbuild.js --production", + "compile-tests": "tsc -p . --outDir out", + "watch-tests": "tsc -p . -w --outDir out", + "pretest": "npm run compile-tests && npm run compile && npm run lint", + "check-types": "tsc --noEmit", + "lint": "eslint src", + "test": "vscode-test" + }, + "devDependencies": { + "@types/mocha": "^10.0.10", + "@types/node": "20.x", + "@types/vscode": "^1.90.0", + "@typescript-eslint/eslint-plugin": "^8.31.1", + "@typescript-eslint/parser": "^8.31.1", + "@vscode/test-cli": "^0.0.10", + "@vscode/test-electron": "^2.5.2", + "esbuild": "^0.25.3", + "eslint": "^9.25.1", + "npm-run-all": "^4.1.5", + "typescript": "^5.8.3" + }, + "dependencies": { + "@ai-sdk/anthropic": "^0.0.50", + "@ai-sdk/google": "^0.0.52", + "@ai-sdk/openai": "^0.0.66", + "@anthropic-ai/claude-code": "^1.0.35", + "@openrouter/ai-sdk-provider": "^0.0.6", + "@types/react": "^19.1.8", + "@types/react-dom": "^19.1.6", + "ai": "^3.4.0", + "execa": "^8.0.0", + "glob": "^10.3.0", + "highlight.js": "^11.11.1", + "lucide-react": "^0.522.0", + "micromatch": "^4.0.5", + "react": "^19.1.0", + "react-dom": "^19.1.0", + "react-markdown": "^10.1.0", + "react-zoom-pan-pinch": "^3.7.0", + "rehype-highlight": "^7.0.2", + "remark-gfm": "^4.0.1" + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml new file mode 100644 index 0000000..c60f3cc --- /dev/null +++ b/pnpm-lock.yaml @@ -0,0 +1,4554 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + '@anthropic-ai/claude-code': + specifier: ^1.0.31 + version: 1.0.33 + '@types/react': + specifier: ^19.1.8 + version: 19.1.8 + '@types/react-dom': + specifier: ^19.1.6 + version: 19.1.6(@types/react@19.1.8) + highlight.js: + specifier: ^11.11.1 + version: 11.11.1 + lucide-react: + specifier: ^0.522.0 + version: 0.522.0(react@19.1.0) + react: + specifier: ^19.1.0 + version: 19.1.0 + react-dom: + specifier: ^19.1.0 + version: 19.1.0(react@19.1.0) + react-markdown: + specifier: ^10.1.0 + version: 10.1.0(@types/react@19.1.8)(react@19.1.0) + react-zoom-pan-pinch: + specifier: ^3.7.0 + version: 3.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + rehype-highlight: + specifier: ^7.0.2 + version: 7.0.2 + remark-gfm: + specifier: ^4.0.1 + version: 4.0.1 + devDependencies: + '@types/mocha': + specifier: ^10.0.10 + version: 10.0.10 + '@types/node': + specifier: 20.x + version: 20.19.1 + '@types/vscode': + specifier: ^1.95.0 + version: 1.101.0 + '@typescript-eslint/eslint-plugin': + specifier: ^8.31.1 + version: 8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/parser': + specifier: ^8.31.1 + version: 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@vscode/test-cli': + specifier: ^0.0.10 + version: 0.0.10 + '@vscode/test-electron': + specifier: ^2.5.2 + version: 2.5.2 + esbuild: + specifier: ^0.25.3 + version: 0.25.5 + eslint: + specifier: ^9.25.1 + version: 9.29.0 + npm-run-all: + specifier: ^4.1.5 + version: 4.1.5 + typescript: + specifier: ^5.8.3 + version: 5.8.3 + +packages: + + '@anthropic-ai/claude-code@1.0.33': + resolution: {integrity: sha512-rKQ1C0+iSV/bS4LVfyCt2FIkIc8MnFi5EbmRAXEunNkXLCQLHfXjsqx7cLOy7c11vZwGkyf/wEp5LwaDQHdjCQ==} + engines: {node: '>=18.0.0'} + hasBin: true + + '@bcoe/v8-coverage@0.2.3': + resolution: {integrity: sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==} + + '@esbuild/aix-ppc64@0.25.5': + resolution: {integrity: sha512-9o3TMmpmftaCMepOdA5k/yDw8SfInyzWWTjYTFCX3kPSDJMROQTb8jg+h9Cnwnmm1vOzvxN7gIfB5V2ewpjtGA==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.25.5': + resolution: {integrity: sha512-VGzGhj4lJO+TVGV1v8ntCZWJktV7SGCs3Pn1GRWI1SBFtRALoomm8k5E9Pmwg3HOAal2VDc2F9+PM/rEY6oIDg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.25.5': + resolution: {integrity: sha512-AdJKSPeEHgi7/ZhuIPtcQKr5RQdo6OO2IL87JkianiMYMPbCtot9fxPbrMiBADOWWm3T2si9stAiVsGbTQFkbA==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.25.5': + resolution: {integrity: sha512-D2GyJT1kjvO//drbRT3Hib9XPwQeWd9vZoBJn+bu/lVsOZ13cqNdDeqIF/xQ5/VmWvMduP6AmXvylO/PIc2isw==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.25.5': + resolution: {integrity: sha512-GtaBgammVvdF7aPIgH2jxMDdivezgFu6iKpmT+48+F8Hhg5J/sfnDieg0aeG/jfSvkYQU2/pceFPDKlqZzwnfQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.25.5': + resolution: {integrity: sha512-1iT4FVL0dJ76/q1wd7XDsXrSW+oLoquptvh4CLR4kITDtqi2e/xwXwdCVH8hVHU43wgJdsq7Gxuzcs6Iq/7bxQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.25.5': + resolution: {integrity: sha512-nk4tGP3JThz4La38Uy/gzyXtpkPW8zSAmoUhK9xKKXdBCzKODMc2adkB2+8om9BDYugz+uGV7sLmpTYzvmz6Sw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.25.5': + resolution: {integrity: sha512-PrikaNjiXdR2laW6OIjlbeuCPrPaAl0IwPIaRv+SMV8CiM8i2LqVUHFC1+8eORgWyY7yhQY+2U2fA55mBzReaw==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.25.5': + resolution: {integrity: sha512-Z9kfb1v6ZlGbWj8EJk9T6czVEjjq2ntSYLY2cw6pAZl4oKtfgQuS4HOq41M/BcoLPzrUbNd+R4BXFyH//nHxVg==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.25.5': + resolution: {integrity: sha512-cPzojwW2okgh7ZlRpcBEtsX7WBuqbLrNXqLU89GxWbNt6uIg78ET82qifUy3W6OVww6ZWobWub5oqZOVtwolfw==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.25.5': + resolution: {integrity: sha512-sQ7l00M8bSv36GLV95BVAdhJ2QsIbCuCjh/uYrWiMQSUuV+LpXwIqhgJDcvMTj+VsQmqAHL2yYaasENvJ7CDKA==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.25.5': + resolution: {integrity: sha512-0ur7ae16hDUC4OL5iEnDb0tZHDxYmuQyhKhsPBV8f99f6Z9KQM02g33f93rNH5A30agMS46u2HP6qTdEt6Q1kg==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.25.5': + resolution: {integrity: sha512-kB/66P1OsHO5zLz0i6X0RxlQ+3cu0mkxS3TKFvkb5lin6uwZ/ttOkP3Z8lfR9mJOBk14ZwZ9182SIIWFGNmqmg==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.25.5': + resolution: {integrity: sha512-UZCmJ7r9X2fe2D6jBmkLBMQetXPXIsZjQJCjgwpVDz+YMcS6oFR27alkgGv3Oqkv07bxdvw7fyB71/olceJhkQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.25.5': + resolution: {integrity: sha512-kTxwu4mLyeOlsVIFPfQo+fQJAV9mh24xL+y+Bm6ej067sYANjyEw1dNHmvoqxJUCMnkBdKpvOn0Ahql6+4VyeA==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.25.5': + resolution: {integrity: sha512-K2dSKTKfmdh78uJ3NcWFiqyRrimfdinS5ErLSn3vluHNeHVnBAFWC8a4X5N+7FgVE1EjXS1QDZbpqZBjfrqMTQ==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.25.5': + resolution: {integrity: sha512-uhj8N2obKTE6pSZ+aMUbqq+1nXxNjZIIjCjGLfsWvVpy7gKCOL6rsY1MhRh9zLtUtAI7vpgLMK6DxjO8Qm9lJw==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.25.5': + resolution: {integrity: sha512-pwHtMP9viAy1oHPvgxtOv+OkduK5ugofNTVDilIzBLpoWAM16r7b/mxBvfpuQDpRQFMfuVr5aLcn4yveGvBZvw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.25.5': + resolution: {integrity: sha512-WOb5fKrvVTRMfWFNCroYWWklbnXH0Q5rZppjq0vQIdlsQKuw6mdSihwSo4RV/YdQ5UCKKvBy7/0ZZYLBZKIbwQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.25.5': + resolution: {integrity: sha512-7A208+uQKgTxHd0G0uqZO8UjK2R0DDb4fDmERtARjSHWxqMTye4Erz4zZafx7Di9Cv+lNHYuncAkiGFySoD+Mw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.25.5': + resolution: {integrity: sha512-G4hE405ErTWraiZ8UiSoesH8DaCsMm0Cay4fsFWOOUcz8b8rC6uCvnagr+gnioEjWn0wC+o1/TAHt+It+MpIMg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/sunos-x64@0.25.5': + resolution: {integrity: sha512-l+azKShMy7FxzY0Rj4RCt5VD/q8mG/e+mDivgspo+yL8zW7qEwctQ6YqKX34DTEleFAvCIUviCFX1SDZRSyMQA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.25.5': + resolution: {integrity: sha512-O2S7SNZzdcFG7eFKgvwUEZ2VG9D/sn/eIiz8XRZ1Q/DO5a3s76Xv0mdBzVM5j5R639lXQmPmSo0iRpHqUUrsxw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.25.5': + resolution: {integrity: sha512-onOJ02pqs9h1iMJ1PQphR+VZv8qBMQ77Klcsqv9CNW2w6yLqoURLcgERAIurY6QE63bbLuqgP9ATqajFLK5AMQ==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.25.5': + resolution: {integrity: sha512-TXv6YnJ8ZMVdX+SXWVBo/0p8LTcrUYngpWjvm91TMjjBQii7Oz11Lw5lbDV5Y0TzuhSJHwiH4hEtC1I42mMS0g==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@eslint-community/eslint-utils@4.7.0': + resolution: {integrity: sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + + '@eslint-community/regexpp@4.12.1': + resolution: {integrity: sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==} + engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} + + '@eslint/config-array@0.20.1': + resolution: {integrity: sha512-OL0RJzC/CBzli0DrrR31qzj6d6i6Mm3HByuhflhl4LOBiWxN+3i6/t/ZQQNii4tjksXi8r2CRW1wMpWA2ULUEw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/config-helpers@0.2.3': + resolution: {integrity: sha512-u180qk2Um1le4yf0ruXH3PYFeEZeYC3p/4wCTKrr2U1CmGdzGi3KtY0nuPDH48UJxlKCC5RDzbcbh4X0XlqgHg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.14.0': + resolution: {integrity: sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/core@0.15.0': + resolution: {integrity: sha512-b7ePw78tEWWkpgZCDYkbqDOP8dmM6qe+AOC6iuJqlq1R/0ahMAeH3qynpnqKFGkMltrp44ohV4ubGyvLX28tzw==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/eslintrc@3.3.1': + resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/js@9.29.0': + resolution: {integrity: sha512-3PIF4cBw/y+1u2EazflInpV+lYsSG0aByVIQzAgb1m1MhHFSbqTyNqtBKHgWf/9Ykud+DhILS9EGkmekVhbKoQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/object-schema@2.1.6': + resolution: {integrity: sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@eslint/plugin-kit@0.3.2': + resolution: {integrity: sha512-4SaFZCNfJqvk/kenHpI8xvN42DMaoycy4PzKc5otHxRswww1kAt82OlBuwRVLofCACCTZEcla2Ydxv8scMXaTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@humanfs/core@0.19.1': + resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==} + engines: {node: '>=18.18.0'} + + '@humanfs/node@0.16.6': + resolution: {integrity: sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==} + engines: {node: '>=18.18.0'} + + '@humanwhocodes/module-importer@1.0.1': + resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} + engines: {node: '>=12.22'} + + '@humanwhocodes/retry@0.3.1': + resolution: {integrity: sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==} + engines: {node: '>=18.18'} + + '@humanwhocodes/retry@0.4.3': + resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} + engines: {node: '>=18.18'} + + '@img/sharp-darwin-arm64@0.33.5': + resolution: {integrity: sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.33.5': + resolution: {integrity: sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-darwin-arm64@1.0.4': + resolution: {integrity: sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.0.4': + resolution: {integrity: sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.0.4': + resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.0.5': + resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.0.4': + resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.33.5': + resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.33.5': + resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-x64@0.33.5': + resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [linux] + + '@img/sharp-win32-x64@0.33.5': + resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} + engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} + cpu: [x64] + os: [win32] + + '@isaacs/cliui@8.0.2': + resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} + engines: {node: '>=12'} + + '@istanbuljs/schema@0.1.3': + resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + engines: {node: '>=8'} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.0': + resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} + + '@jridgewell/trace-mapping@0.3.25': + resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + + '@nodelib/fs.scandir@2.1.5': + resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} + engines: {node: '>= 8'} + + '@nodelib/fs.stat@2.0.5': + resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} + engines: {node: '>= 8'} + + '@nodelib/fs.walk@1.2.8': + resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} + engines: {node: '>= 8'} + + '@pkgjs/parseargs@0.11.0': + resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} + engines: {node: '>=14'} + + '@types/debug@4.1.12': + resolution: {integrity: sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==} + + '@types/estree-jsx@1.0.5': + resolution: {integrity: sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==} + + '@types/estree@1.0.8': + resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + + '@types/hast@3.0.4': + resolution: {integrity: sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==} + + '@types/istanbul-lib-coverage@2.0.6': + resolution: {integrity: sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==} + + '@types/json-schema@7.0.15': + resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} + + '@types/mdast@4.0.4': + resolution: {integrity: sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==} + + '@types/mocha@10.0.10': + resolution: {integrity: sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==} + + '@types/ms@2.1.0': + resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} + + '@types/node@20.19.1': + resolution: {integrity: sha512-jJD50LtlD2dodAEO653i3YF04NWak6jN3ky+Ri3Em3mGR39/glWiboM/IePaRbgwSfqM1TpGXfAg8ohn/4dTgA==} + + '@types/react-dom@19.1.6': + resolution: {integrity: sha512-4hOiT/dwO8Ko0gV1m/TJZYk3y0KBnY9vzDh7W+DH17b2HFSOGgdj33dhihPeuy3l0q23+4e+hoXHV6hCC4dCXw==} + peerDependencies: + '@types/react': ^19.0.0 + + '@types/react@19.1.8': + resolution: {integrity: sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==} + + '@types/unist@2.0.11': + resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} + + '@types/unist@3.0.3': + resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==} + + '@types/vscode@1.101.0': + resolution: {integrity: sha512-ZWf0IWa+NGegdW3iU42AcDTFHWW7fApLdkdnBqwYEtHVIBGbTu0ZNQKP/kX3Ds/uMJXIMQNAojHR4vexCEEz5Q==} + + '@typescript-eslint/eslint-plugin@8.35.0': + resolution: {integrity: sha512-ijItUYaiWuce0N1SoSMrEd0b6b6lYkYt99pqCPfybd+HKVXtEvYhICfLdwp42MhiI5mp0oq7PKEL+g1cNiz/Eg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + '@typescript-eslint/parser': ^8.35.0 + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/parser@8.35.0': + resolution: {integrity: sha512-6sMvZePQrnZH2/cJkwRpkT7DxoAWh+g6+GFRK6bV3YQo7ogi3SX5rgF6099r5Q53Ma5qeT7LGmOmuIutF4t3lA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/project-service@8.35.0': + resolution: {integrity: sha512-41xatqRwWZuhUMF/aZm2fcUsOFKNcG28xqRSS6ZVr9BVJtGExosLAm5A1OxTjRMagx8nJqva+P5zNIGt8RIgbQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/scope-manager@8.35.0': + resolution: {integrity: sha512-+AgL5+mcoLxl1vGjwNfiWq5fLDZM1TmTPYs2UkyHfFhgERxBbqHlNjRzhThJqz+ktBqTChRYY6zwbMwy0591AA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/tsconfig-utils@8.35.0': + resolution: {integrity: sha512-04k/7247kZzFraweuEirmvUj+W3bJLI9fX6fbo1Qm2YykuBvEhRTPl8tcxlYO8kZZW+HIXfkZNoasVb8EV4jpA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/type-utils@8.35.0': + resolution: {integrity: sha512-ceNNttjfmSEoM9PW87bWLDEIaLAyR+E6BoYJQ5PfaDau37UGca9Nyq3lBk8Bw2ad0AKvYabz6wxc7DMTO2jnNA==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/types@8.35.0': + resolution: {integrity: sha512-0mYH3emanku0vHw2aRLNGqe7EXh9WHEhi7kZzscrMDf6IIRUQ5Jk4wp1QrledE/36KtdZrVfKnE32eZCf/vaVQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@typescript-eslint/typescript-estree@8.35.0': + resolution: {integrity: sha512-F+BhnaBemgu1Qf8oHrxyw14wq6vbL8xwWKKMwTMwYIRmFFY/1n/9T/jpbobZL8vp7QyEUcC6xGrnAO4ua8Kp7w==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/utils@8.35.0': + resolution: {integrity: sha512-nqoMu7WWM7ki5tPgLVsmPM8CkqtoPUG6xXGeefM5t4x3XumOEKMoUZPdi+7F+/EotukN4R9OWdmDxN80fqoZeg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + peerDependencies: + eslint: ^8.57.0 || ^9.0.0 + typescript: '>=4.8.4 <5.9.0' + + '@typescript-eslint/visitor-keys@8.35.0': + resolution: {integrity: sha512-zTh2+1Y8ZpmeQaQVIc/ZZxsx8UzgKJyNg1PTvjzC7WMhPSVS8bfDX34k1SrwOf016qd5RU3az2UxUNue3IfQ5g==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + '@ungap/structured-clone@1.3.0': + resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} + + '@vscode/test-cli@0.0.10': + resolution: {integrity: sha512-B0mMH4ia+MOOtwNiLi79XhA+MLmUItIC8FckEuKrVAVriIuSWjt7vv4+bF8qVFiNFe4QRfzPaIZk39FZGWEwHA==} + engines: {node: '>=18'} + hasBin: true + + '@vscode/test-electron@2.5.2': + resolution: {integrity: sha512-8ukpxv4wYe0iWMRQU18jhzJOHkeGKbnw7xWRX3Zw1WJA4cEKbHcmmLPdPrPtL6rhDcrlCZN+xKRpv09n4gRHYg==} + engines: {node: '>=16'} + + acorn-jsx@5.3.2: + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + + acorn@8.15.0: + resolution: {integrity: sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@7.1.3: + resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + engines: {node: '>= 14'} + + ajv@6.12.6: + resolution: {integrity: sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==} + + ansi-colors@4.1.3: + resolution: {integrity: sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==} + engines: {node: '>=6'} + + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + + ansi-regex@6.1.0: + resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + engines: {node: '>=12'} + + ansi-styles@3.2.1: + resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} + engines: {node: '>=4'} + + ansi-styles@4.3.0: + resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} + engines: {node: '>=8'} + + ansi-styles@6.2.1: + resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + engines: {node: '>=12'} + + anymatch@3.1.3: + resolution: {integrity: sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==} + engines: {node: '>= 8'} + + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} + + array-buffer-byte-length@1.0.2: + resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} + engines: {node: '>= 0.4'} + + arraybuffer.prototype.slice@1.0.4: + resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} + engines: {node: '>= 0.4'} + + async-function@1.0.0: + resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} + engines: {node: '>= 0.4'} + + available-typed-arrays@1.0.7: + resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} + engines: {node: '>= 0.4'} + + bail@2.0.2: + resolution: {integrity: sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==} + + balanced-match@1.0.2: + resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + + binary-extensions@2.3.0: + resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} + engines: {node: '>=8'} + + brace-expansion@1.1.12: + resolution: {integrity: sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==} + + brace-expansion@2.0.2: + resolution: {integrity: sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==} + + braces@3.0.3: + resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} + engines: {node: '>=8'} + + browser-stdout@1.3.1: + resolution: {integrity: sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==} + + c8@9.1.0: + resolution: {integrity: sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==} + engines: {node: '>=14.14.0'} + hasBin: true + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + call-bind@1.0.8: + resolution: {integrity: sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==} + engines: {node: '>= 0.4'} + + call-bound@1.0.4: + resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} + engines: {node: '>= 0.4'} + + callsites@3.1.0: + resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} + engines: {node: '>=6'} + + camelcase@6.3.0: + resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} + engines: {node: '>=10'} + + ccount@2.0.1: + resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} + + chalk@2.4.2: + resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} + engines: {node: '>=4'} + + chalk@4.1.2: + resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} + engines: {node: '>=10'} + + chalk@5.4.1: + resolution: {integrity: sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==} + engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} + + character-entities-html4@2.1.0: + resolution: {integrity: sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==} + + character-entities-legacy@3.0.0: + resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} + + character-entities@2.0.2: + resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} + + character-reference-invalid@2.0.1: + resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} + + chokidar@3.6.0: + resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} + engines: {node: '>= 8.10.0'} + + cli-cursor@5.0.0: + resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} + engines: {node: '>=18'} + + cli-spinners@2.9.2: + resolution: {integrity: sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==} + engines: {node: '>=6'} + + cliui@7.0.4: + resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} + + cliui@8.0.1: + resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} + engines: {node: '>=12'} + + color-convert@1.9.3: + resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} + + color-convert@2.0.1: + resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} + engines: {node: '>=7.0.0'} + + color-name@1.1.3: + resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} + + color-name@1.1.4: + resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} + + comma-separated-tokens@2.0.3: + resolution: {integrity: sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==} + + concat-map@0.0.1: + resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + core-util-is@1.0.3: + resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} + + cross-spawn@6.0.6: + resolution: {integrity: sha512-VqCUuhcd1iB+dsv8gxPttb5iZh/D0iubSP21g36KXdEuf6I5JiioesUVjpCdHV9MZRUfVFlvwtIUyPfxo5trtw==} + engines: {node: '>=4.8'} + + cross-spawn@7.0.6: + resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} + engines: {node: '>= 8'} + + csstype@3.1.3: + resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + + data-view-buffer@1.0.2: + resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} + engines: {node: '>= 0.4'} + + data-view-byte-length@1.0.2: + resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} + engines: {node: '>= 0.4'} + + data-view-byte-offset@1.0.1: + resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} + engines: {node: '>= 0.4'} + + debug@4.4.1: + resolution: {integrity: sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + decamelize@4.0.0: + resolution: {integrity: sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==} + engines: {node: '>=10'} + + decode-named-character-reference@1.2.0: + resolution: {integrity: sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==} + + deep-is@0.1.4: + resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} + + define-data-property@1.1.4: + resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} + engines: {node: '>= 0.4'} + + define-properties@1.2.1: + resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} + engines: {node: '>= 0.4'} + + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + + devlop@1.1.0: + resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} + + diff@5.2.0: + resolution: {integrity: sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==} + engines: {node: '>=0.3.1'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + eastasianwidth@0.2.0: + resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} + + emoji-regex@10.4.0: + resolution: {integrity: sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw==} + + emoji-regex@8.0.0: + resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} + + emoji-regex@9.2.2: + resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + + enhanced-resolve@5.18.2: + resolution: {integrity: sha512-6Jw4sE1maoRJo3q8MsSIn2onJFbLTOjY9hlx4DZXmOKvLRd1Ok2kXmAGXaafL2+ijsJZ1ClYbl/pmqr9+k4iUQ==} + engines: {node: '>=10.13.0'} + + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + + es-abstract@1.24.0: + resolution: {integrity: sha512-WSzPgsdLtTcQwm4CROfS5ju2Wa1QQcVeT37jFjYzdFz1r9ahadC8B8/a4qxJxM+09F18iumCdRmlr96ZYkQvEg==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-object-atoms@1.1.1: + resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + es-to-primitive@1.3.0: + resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==} + engines: {node: '>= 0.4'} + + esbuild@0.25.5: + resolution: {integrity: sha512-P8OtKZRv/5J5hhz0cUAdu/cLuPIKXpQl1R9pZtvmHWQvrAUVd0UNIPT4IB4W3rNOqVO0rlqHmCIbSwxh/c9yUQ==} + engines: {node: '>=18'} + hasBin: true + + escalade@3.2.0: + resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} + engines: {node: '>=6'} + + escape-string-regexp@1.0.5: + resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} + engines: {node: '>=0.8.0'} + + escape-string-regexp@4.0.0: + resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} + engines: {node: '>=10'} + + escape-string-regexp@5.0.0: + resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} + engines: {node: '>=12'} + + eslint-scope@8.4.0: + resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint-visitor-keys@3.4.3: + resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + eslint-visitor-keys@4.2.1: + resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + eslint@9.29.0: + resolution: {integrity: sha512-GsGizj2Y1rCWDu6XoEekL3RLilp0voSePurjZIkxL3wlm5o5EC9VpgaP7lrCvjnkuLvzFBQWB3vWB3K5KQTveQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + hasBin: true + peerDependencies: + jiti: '*' + peerDependenciesMeta: + jiti: + optional: true + + espree@10.4.0: + resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} + engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} + + esquery@1.6.0: + resolution: {integrity: sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==} + engines: {node: '>=0.10'} + + esrecurse@4.3.0: + resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} + engines: {node: '>=4.0'} + + estraverse@5.3.0: + resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} + engines: {node: '>=4.0'} + + estree-util-is-identifier-name@3.0.0: + resolution: {integrity: sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==} + + esutils@2.0.3: + resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} + engines: {node: '>=0.10.0'} + + extend@3.0.2: + resolution: {integrity: sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==} + + fast-deep-equal@3.1.3: + resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} + + fast-glob@3.3.3: + resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} + engines: {node: '>=8.6.0'} + + fast-json-stable-stringify@2.1.0: + resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} + + fast-levenshtein@2.0.6: + resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} + + fastq@1.19.1: + resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + + file-entry-cache@8.0.0: + resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} + engines: {node: '>=16.0.0'} + + fill-range@7.1.1: + resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} + engines: {node: '>=8'} + + find-up@5.0.0: + resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} + engines: {node: '>=10'} + + flat-cache@4.0.1: + resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} + engines: {node: '>=16'} + + flat@5.0.2: + resolution: {integrity: sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==} + hasBin: true + + flatted@3.3.3: + resolution: {integrity: sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==} + + for-each@0.3.5: + resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} + engines: {node: '>= 0.4'} + + foreground-child@3.3.1: + resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} + engines: {node: '>=14'} + + fs.realpath@1.0.0: + resolution: {integrity: sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + function.prototype.name@1.1.8: + resolution: {integrity: sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==} + engines: {node: '>= 0.4'} + + functions-have-names@1.2.3: + resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} + + get-caller-file@2.0.5: + resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} + engines: {node: 6.* || 8.* || >= 10.*} + + get-east-asian-width@1.3.0: + resolution: {integrity: sha512-vpeMIQKxczTD/0s2CdEWHcb0eeJe6TFjxb+J5xgX7hScxqrGuyjmv4c1D4A/gelKfyox0gJJwIHF+fLjeaM8kQ==} + engines: {node: '>=18'} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + get-symbol-description@1.1.0: + resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} + engines: {node: '>= 0.4'} + + glob-parent@5.1.2: + resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} + engines: {node: '>= 6'} + + glob-parent@6.0.2: + resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} + engines: {node: '>=10.13.0'} + + glob@10.4.5: + resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} + hasBin: true + + glob@7.2.3: + resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} + deprecated: Glob versions prior to v9 are no longer supported + + glob@8.1.0: + resolution: {integrity: sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==} + engines: {node: '>=12'} + deprecated: Glob versions prior to v9 are no longer supported + + globals@14.0.0: + resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==} + engines: {node: '>=18'} + + globalthis@1.0.4: + resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + graceful-fs@4.2.11: + resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} + + graphemer@1.4.0: + resolution: {integrity: sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==} + + has-bigints@1.1.0: + resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} + engines: {node: '>= 0.4'} + + has-flag@3.0.0: + resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} + engines: {node: '>=4'} + + has-flag@4.0.0: + resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} + engines: {node: '>=8'} + + has-property-descriptors@1.0.2: + resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} + + has-proto@1.2.0: + resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.2: + resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + engines: {node: '>= 0.4'} + + hast-util-is-element@3.0.0: + resolution: {integrity: sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g==} + + hast-util-to-jsx-runtime@2.3.6: + resolution: {integrity: sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==} + + hast-util-to-text@4.0.2: + resolution: {integrity: sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A==} + + hast-util-whitespace@3.0.0: + resolution: {integrity: sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==} + + he@1.2.0: + resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} + hasBin: true + + highlight.js@11.11.1: + resolution: {integrity: sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==} + engines: {node: '>=12.0.0'} + + hosted-git-info@2.8.9: + resolution: {integrity: sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==} + + html-escaper@2.0.2: + resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} + + html-url-attributes@3.0.1: + resolution: {integrity: sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==} + + http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + + https-proxy-agent@7.0.6: + resolution: {integrity: sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==} + engines: {node: '>= 14'} + + ignore@5.3.2: + resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} + engines: {node: '>= 4'} + + ignore@7.0.5: + resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} + engines: {node: '>= 4'} + + immediate@3.0.6: + resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==} + + import-fresh@3.3.1: + resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} + engines: {node: '>=6'} + + imurmurhash@0.1.4: + resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} + engines: {node: '>=0.8.19'} + + inflight@1.0.6: + resolution: {integrity: sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==} + deprecated: This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful. + + inherits@2.0.4: + resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + + inline-style-parser@0.2.4: + resolution: {integrity: sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==} + + internal-slot@1.1.0: + resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} + engines: {node: '>= 0.4'} + + is-alphabetical@2.0.1: + resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} + + is-alphanumerical@2.0.1: + resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} + + is-array-buffer@3.0.5: + resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} + engines: {node: '>= 0.4'} + + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + + is-async-function@2.1.1: + resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} + engines: {node: '>= 0.4'} + + is-bigint@1.1.0: + resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} + engines: {node: '>= 0.4'} + + is-binary-path@2.1.0: + resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} + engines: {node: '>=8'} + + is-boolean-object@1.2.2: + resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} + engines: {node: '>= 0.4'} + + is-callable@1.2.7: + resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} + engines: {node: '>= 0.4'} + + is-core-module@2.16.1: + resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + engines: {node: '>= 0.4'} + + is-data-view@1.0.2: + resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} + engines: {node: '>= 0.4'} + + is-date-object@1.1.0: + resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} + engines: {node: '>= 0.4'} + + is-decimal@2.0.1: + resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} + + is-extglob@2.1.1: + resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} + engines: {node: '>=0.10.0'} + + is-finalizationregistry@1.1.1: + resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} + engines: {node: '>= 0.4'} + + is-fullwidth-code-point@3.0.0: + resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} + engines: {node: '>=8'} + + is-generator-function@1.1.0: + resolution: {integrity: sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==} + engines: {node: '>= 0.4'} + + is-glob@4.0.3: + resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} + engines: {node: '>=0.10.0'} + + is-hexadecimal@2.0.1: + resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} + + is-interactive@2.0.0: + resolution: {integrity: sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==} + engines: {node: '>=12'} + + is-map@2.0.3: + resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} + engines: {node: '>= 0.4'} + + is-negative-zero@2.0.3: + resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} + engines: {node: '>= 0.4'} + + is-number-object@1.1.1: + resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} + engines: {node: '>= 0.4'} + + is-number@7.0.0: + resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} + engines: {node: '>=0.12.0'} + + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + + is-plain-obj@4.1.0: + resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} + engines: {node: '>=12'} + + is-regex@1.2.1: + resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} + engines: {node: '>= 0.4'} + + is-set@2.0.3: + resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} + engines: {node: '>= 0.4'} + + is-shared-array-buffer@1.0.4: + resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} + engines: {node: '>= 0.4'} + + is-string@1.1.1: + resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} + engines: {node: '>= 0.4'} + + is-symbol@1.1.1: + resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} + engines: {node: '>= 0.4'} + + is-typed-array@1.1.15: + resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} + engines: {node: '>= 0.4'} + + is-unicode-supported@0.1.0: + resolution: {integrity: sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==} + engines: {node: '>=10'} + + is-unicode-supported@1.3.0: + resolution: {integrity: sha512-43r2mRvz+8JRIKnWJ+3j8JtjRKZ6GmjzfaE/qiBJnikNnYv/6bagRJ1kUhNk8R5EX/GkobD+r+sfxCPJsiKBLQ==} + engines: {node: '>=12'} + + is-unicode-supported@2.1.0: + resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} + engines: {node: '>=18'} + + is-weakmap@2.0.2: + resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} + engines: {node: '>= 0.4'} + + is-weakref@1.1.1: + resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} + engines: {node: '>= 0.4'} + + is-weakset@2.0.4: + resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} + engines: {node: '>= 0.4'} + + isarray@1.0.0: + resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} + + isarray@2.0.5: + resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} + + isexe@2.0.0: + resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} + + istanbul-lib-coverage@3.2.2: + resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} + engines: {node: '>=8'} + + istanbul-lib-report@3.0.1: + resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} + engines: {node: '>=10'} + + istanbul-reports@3.1.7: + resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + engines: {node: '>=8'} + + jackspeak@3.4.3: + resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + + js-yaml@4.1.0: + resolution: {integrity: sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==} + hasBin: true + + json-buffer@3.0.1: + resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + + json-parse-better-errors@1.0.2: + resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} + + json-schema-traverse@0.4.1: + resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} + + json-stable-stringify-without-jsonify@1.0.1: + resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + + jszip@3.10.1: + resolution: {integrity: sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==} + + keyv@4.5.4: + resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} + + levn@0.4.1: + resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} + engines: {node: '>= 0.8.0'} + + lie@3.3.0: + resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==} + + load-json-file@4.0.0: + resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} + engines: {node: '>=4'} + + locate-path@6.0.0: + resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} + engines: {node: '>=10'} + + lodash.merge@4.6.2: + resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==} + + log-symbols@4.1.0: + resolution: {integrity: sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==} + engines: {node: '>=10'} + + log-symbols@6.0.0: + resolution: {integrity: sha512-i24m8rpwhmPIS4zscNzK6MSEhk0DUWa/8iYQWxhffV8jkI4Phvs3F+quL5xvS0gdQR0FyTCMMH33Y78dDTzzIw==} + engines: {node: '>=18'} + + longest-streak@3.1.0: + resolution: {integrity: sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==} + + lowlight@3.3.0: + resolution: {integrity: sha512-0JNhgFoPvP6U6lE/UdVsSq99tn6DhjjpAj5MxG49ewd2mOBVtwWYIT8ClyABhq198aXXODMU6Ox8DrGy/CpTZQ==} + + lru-cache@10.4.3: + resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} + + lucide-react@0.522.0: + resolution: {integrity: sha512-jnJbw974yZ7rQHHEFKJOlWAefG3ATSCZHANZxIdx8Rk/16siuwjgA4fBULpXEAWx/RlTs3FzmKW/udWUuO0aRw==} + peerDependencies: + react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + + make-dir@4.0.0: + resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} + engines: {node: '>=10'} + + markdown-table@3.0.4: + resolution: {integrity: sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mdast-util-find-and-replace@3.0.2: + resolution: {integrity: sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==} + + mdast-util-from-markdown@2.0.2: + resolution: {integrity: sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==} + + mdast-util-gfm-autolink-literal@2.0.1: + resolution: {integrity: sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==} + + mdast-util-gfm-footnote@2.1.0: + resolution: {integrity: sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==} + + mdast-util-gfm-strikethrough@2.0.0: + resolution: {integrity: sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==} + + mdast-util-gfm-table@2.0.0: + resolution: {integrity: sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==} + + mdast-util-gfm-task-list-item@2.0.0: + resolution: {integrity: sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==} + + mdast-util-gfm@3.1.0: + resolution: {integrity: sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==} + + mdast-util-mdx-expression@2.0.1: + resolution: {integrity: sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==} + + mdast-util-mdx-jsx@3.2.0: + resolution: {integrity: sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==} + + mdast-util-mdxjs-esm@2.0.1: + resolution: {integrity: sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==} + + mdast-util-phrasing@4.1.0: + resolution: {integrity: sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==} + + mdast-util-to-hast@13.2.0: + resolution: {integrity: sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==} + + mdast-util-to-markdown@2.1.2: + resolution: {integrity: sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==} + + mdast-util-to-string@4.0.0: + resolution: {integrity: sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==} + + memorystream@0.3.1: + resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} + engines: {node: '>= 0.10.0'} + + merge2@1.4.1: + resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} + engines: {node: '>= 8'} + + micromark-core-commonmark@2.0.3: + resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} + + micromark-extension-gfm-autolink-literal@2.1.0: + resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} + + micromark-extension-gfm-footnote@2.1.0: + resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} + + micromark-extension-gfm-strikethrough@2.1.0: + resolution: {integrity: sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==} + + micromark-extension-gfm-table@2.1.1: + resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} + + micromark-extension-gfm-tagfilter@2.0.0: + resolution: {integrity: sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==} + + micromark-extension-gfm-task-list-item@2.1.0: + resolution: {integrity: sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==} + + micromark-extension-gfm@3.0.0: + resolution: {integrity: sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==} + + micromark-factory-destination@2.0.1: + resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} + + micromark-factory-label@2.0.1: + resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} + + micromark-factory-space@2.0.1: + resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} + + micromark-factory-title@2.0.1: + resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} + + micromark-factory-whitespace@2.0.1: + resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} + + micromark-util-character@2.1.1: + resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} + + micromark-util-chunked@2.0.1: + resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} + + micromark-util-classify-character@2.0.1: + resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} + + micromark-util-combine-extensions@2.0.1: + resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} + + micromark-util-decode-numeric-character-reference@2.0.2: + resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} + + micromark-util-decode-string@2.0.1: + resolution: {integrity: sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==} + + micromark-util-encode@2.0.1: + resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} + + micromark-util-html-tag-name@2.0.1: + resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} + + micromark-util-normalize-identifier@2.0.1: + resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} + + micromark-util-resolve-all@2.0.1: + resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} + + micromark-util-sanitize-uri@2.0.1: + resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} + + micromark-util-subtokenize@2.1.0: + resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} + + micromark-util-symbol@2.0.1: + resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} + + micromark-util-types@2.0.2: + resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} + + micromark@4.0.2: + resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} + + micromatch@4.0.8: + resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} + engines: {node: '>=8.6'} + + mimic-function@5.0.1: + resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} + engines: {node: '>=18'} + + minimatch@3.1.2: + resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} + + minimatch@5.1.6: + resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} + engines: {node: '>=10'} + + minimatch@9.0.5: + resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} + engines: {node: '>=16 || 14 >=14.17'} + + minipass@7.1.2: + resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + engines: {node: '>=16 || 14 >=14.17'} + + mocha@10.8.2: + resolution: {integrity: sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==} + engines: {node: '>= 14.0.0'} + hasBin: true + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + natural-compare@1.4.0: + resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} + + nice-try@1.0.5: + resolution: {integrity: sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==} + + normalize-package-data@2.5.0: + resolution: {integrity: sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==} + + normalize-path@3.0.0: + resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} + engines: {node: '>=0.10.0'} + + npm-run-all@4.1.5: + resolution: {integrity: sha512-Oo82gJDAVcaMdi3nuoKFavkIHBRVqQ1qvMb+9LHk/cF4P6B2m8aP04hGf7oL6wZ9BuGwX1onlLhpuoofSyoQDQ==} + engines: {node: '>= 4'} + hasBin: true + + object-inspect@1.13.4: + resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} + engines: {node: '>= 0.4'} + + object-keys@1.1.1: + resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} + engines: {node: '>= 0.4'} + + object.assign@4.1.7: + resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} + engines: {node: '>= 0.4'} + + once@1.4.0: + resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} + + onetime@7.0.0: + resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} + engines: {node: '>=18'} + + optionator@0.9.4: + resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} + engines: {node: '>= 0.8.0'} + + ora@8.2.0: + resolution: {integrity: sha512-weP+BZ8MVNnlCm8c0Qdc1WSWq4Qn7I+9CJGm7Qali6g44e/PUzbjNqJX5NJ9ljlNMosfJvg1fKEGILklK9cwnw==} + engines: {node: '>=18'} + + own-keys@1.0.1: + resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} + engines: {node: '>= 0.4'} + + p-limit@3.1.0: + resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} + engines: {node: '>=10'} + + p-locate@5.0.0: + resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} + engines: {node: '>=10'} + + package-json-from-dist@1.0.1: + resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} + + pako@1.0.11: + resolution: {integrity: sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==} + + parent-module@1.0.1: + resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} + engines: {node: '>=6'} + + parse-entities@4.0.2: + resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} + + parse-json@4.0.0: + resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} + engines: {node: '>=4'} + + path-exists@4.0.0: + resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} + engines: {node: '>=8'} + + path-is-absolute@1.0.1: + resolution: {integrity: sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==} + engines: {node: '>=0.10.0'} + + path-key@2.0.1: + resolution: {integrity: sha512-fEHGKCSmUSDPv4uoj8AlD+joPlq3peND+HRYyxFz4KPw4z926S/b8rIuFs2FYJg3BwsxJf6A9/3eIdLaYC+9Dw==} + engines: {node: '>=4'} + + path-key@3.1.1: + resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} + engines: {node: '>=8'} + + path-parse@1.0.7: + resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} + + path-scurry@1.11.1: + resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} + engines: {node: '>=16 || 14 >=14.18'} + + path-type@3.0.0: + resolution: {integrity: sha512-T2ZUsdZFHgA3u4e5PfPbjd7HDDpxPnQb5jN0SrDsjNSuVXHJqtwTnWqG0B1jZrgmJ/7lj1EmVIByWt1gxGkWvg==} + engines: {node: '>=4'} + + picomatch@2.3.1: + resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + engines: {node: '>=8.6'} + + pidtree@0.3.1: + resolution: {integrity: sha512-qQbW94hLHEqCg7nhby4yRC7G2+jYHY4Rguc2bjw7Uug4GIJuu1tvf2uHaZv5Q8zdt+WKJ6qK1FOI6amaWUo5FA==} + engines: {node: '>=0.10'} + hasBin: true + + pify@3.0.0: + resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} + engines: {node: '>=4'} + + possible-typed-array-names@1.1.0: + resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} + engines: {node: '>= 0.4'} + + prelude-ls@1.2.1: + resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} + engines: {node: '>= 0.8.0'} + + process-nextick-args@2.0.1: + resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} + + property-information@7.1.0: + resolution: {integrity: sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==} + + punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + + queue-microtask@1.2.3: + resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} + + randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + + react-dom@19.1.0: + resolution: {integrity: sha512-Xs1hdnE+DyKgeHJeJznQmYMIBG3TKIHJJT95Q58nHLSrElKlGQqDTR2HQ9fx5CN/Gk6Vh/kupBTDLU11/nDk/g==} + peerDependencies: + react: ^19.1.0 + + react-markdown@10.1.0: + resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} + peerDependencies: + '@types/react': '>=18' + react: '>=18' + + react-zoom-pan-pinch@3.7.0: + resolution: {integrity: sha512-UmReVZ0TxlKzxSbYiAj+LeGRW8s8LraAFTXRAxzMYnNRgGPsxCudwZKVkjvGmjtx7SW/hZamt69NUmGf4xrkXA==} + engines: {node: '>=8', npm: '>=5'} + peerDependencies: + react: '*' + react-dom: '*' + + react@19.1.0: + resolution: {integrity: sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==} + engines: {node: '>=0.10.0'} + + read-pkg@3.0.0: + resolution: {integrity: sha512-BLq/cCO9two+lBgiTYNqD6GdtK8s4NpaWrl6/rCO9w0TUS8oJl7cmToOZfRYllKTISY6nt1U7jQ53brmKqY6BA==} + engines: {node: '>=4'} + + readable-stream@2.3.8: + resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} + + readdirp@3.6.0: + resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} + engines: {node: '>=8.10.0'} + + reflect.getprototypeof@1.0.10: + resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} + engines: {node: '>= 0.4'} + + regexp.prototype.flags@1.5.4: + resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} + engines: {node: '>= 0.4'} + + rehype-highlight@7.0.2: + resolution: {integrity: sha512-k158pK7wdC2qL3M5NcZROZ2tR/l7zOzjxXd5VGdcfIyoijjQqpHd3JKtYSBDpDZ38UI2WJWuFAtkMDxmx5kstA==} + + remark-gfm@4.0.1: + resolution: {integrity: sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==} + + remark-parse@11.0.0: + resolution: {integrity: sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==} + + remark-rehype@11.1.2: + resolution: {integrity: sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==} + + remark-stringify@11.0.0: + resolution: {integrity: sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==} + + require-directory@2.1.1: + resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} + engines: {node: '>=0.10.0'} + + resolve-from@4.0.0: + resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} + engines: {node: '>=4'} + + resolve@1.22.10: + resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + engines: {node: '>= 0.4'} + hasBin: true + + restore-cursor@5.1.0: + resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} + engines: {node: '>=18'} + + reusify@1.1.0: + resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} + engines: {iojs: '>=1.0.0', node: '>=0.10.0'} + + run-parallel@1.2.0: + resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + + safe-array-concat@1.1.3: + resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + engines: {node: '>=0.4'} + + safe-buffer@5.1.2: + resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} + + safe-buffer@5.2.1: + resolution: {integrity: sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==} + + safe-push-apply@1.0.0: + resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} + engines: {node: '>= 0.4'} + + safe-regex-test@1.1.0: + resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} + engines: {node: '>= 0.4'} + + scheduler@0.26.0: + resolution: {integrity: sha512-NlHwttCI/l5gCPR3D1nNXtWABUmBwvZpEQiD4IXSbIDq8BzLIK/7Ir5gTFSGZDUu37K5cMNp0hFtzO38sC7gWA==} + + semver@5.7.2: + resolution: {integrity: sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==} + hasBin: true + + semver@7.7.2: + resolution: {integrity: sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==} + engines: {node: '>=10'} + hasBin: true + + serialize-javascript@6.0.2: + resolution: {integrity: sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==} + + set-function-length@1.2.2: + resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} + engines: {node: '>= 0.4'} + + set-function-name@2.0.2: + resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} + engines: {node: '>= 0.4'} + + set-proto@1.0.0: + resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} + engines: {node: '>= 0.4'} + + setimmediate@1.0.5: + resolution: {integrity: sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==} + + shebang-command@1.2.0: + resolution: {integrity: sha512-EV3L1+UQWGor21OmnvojK36mhg+TyIKDh3iFBKBohr5xeXIhNBcx8oWdgkTEEQ+BEFFYdLRuqMfd5L84N1V5Vg==} + engines: {node: '>=0.10.0'} + + shebang-command@2.0.0: + resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} + engines: {node: '>=8'} + + shebang-regex@1.0.0: + resolution: {integrity: sha512-wpoSFAxys6b2a2wHZ1XpDSgD7N9iVjg29Ph9uV/uaP9Ex/KXlkTZTeddxDPSYQpgvzKLGJke2UU0AzoGCjNIvQ==} + engines: {node: '>=0.10.0'} + + shebang-regex@3.0.0: + resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} + engines: {node: '>=8'} + + shell-quote@1.8.3: + resolution: {integrity: sha512-ObmnIF4hXNg1BqhnHmgbDETF8dLPCggZWBjkQfhZpbszZnYur5DUljTcCHii5LC3J5E0yeO/1LIMyH+UvHQgyw==} + engines: {node: '>= 0.4'} + + side-channel-list@1.0.0: + resolution: {integrity: sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==} + engines: {node: '>= 0.4'} + + side-channel-map@1.0.1: + resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} + engines: {node: '>= 0.4'} + + side-channel-weakmap@1.0.2: + resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} + engines: {node: '>= 0.4'} + + side-channel@1.1.0: + resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + engines: {node: '>= 0.4'} + + signal-exit@4.1.0: + resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} + engines: {node: '>=14'} + + space-separated-tokens@2.0.2: + resolution: {integrity: sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==} + + spdx-correct@3.2.0: + resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} + + spdx-exceptions@2.5.0: + resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} + + spdx-expression-parse@3.0.1: + resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} + + spdx-license-ids@3.0.21: + resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + + stdin-discarder@0.2.2: + resolution: {integrity: sha512-UhDfHmA92YAlNnCfhmq0VeNL5bDbiZGg7sZ2IvPsXubGkiNa9EC+tUTsjBRsYUAz87btI6/1wf4XoVvQ3uRnmQ==} + engines: {node: '>=18'} + + stop-iteration-iterator@1.1.0: + resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} + engines: {node: '>= 0.4'} + + string-width@4.2.3: + resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} + engines: {node: '>=8'} + + string-width@5.1.2: + resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} + engines: {node: '>=12'} + + string-width@7.2.0: + resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} + engines: {node: '>=18'} + + string.prototype.padend@3.1.6: + resolution: {integrity: sha512-XZpspuSB7vJWhvJc9DLSlrXl1mcA2BdoY5jjnS135ydXqLoqhs96JjDtCkjJEQHvfqZIp9hBuBMgI589peyx9Q==} + engines: {node: '>= 0.4'} + + string.prototype.trim@1.2.10: + resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + engines: {node: '>= 0.4'} + + string.prototype.trimend@1.0.9: + resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + engines: {node: '>= 0.4'} + + string.prototype.trimstart@1.0.8: + resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} + engines: {node: '>= 0.4'} + + string_decoder@1.1.1: + resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} + + stringify-entities@4.0.4: + resolution: {integrity: sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==} + + strip-ansi@6.0.1: + resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} + engines: {node: '>=8'} + + strip-ansi@7.1.0: + resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + engines: {node: '>=12'} + + strip-bom@3.0.0: + resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} + engines: {node: '>=4'} + + strip-json-comments@3.1.1: + resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} + engines: {node: '>=8'} + + style-to-js@1.1.17: + resolution: {integrity: sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==} + + style-to-object@1.0.9: + resolution: {integrity: sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==} + + supports-color@5.5.0: + resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} + engines: {node: '>=4'} + + supports-color@7.2.0: + resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} + engines: {node: '>=8'} + + supports-color@8.1.1: + resolution: {integrity: sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==} + engines: {node: '>=10'} + + supports-color@9.4.0: + resolution: {integrity: sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==} + engines: {node: '>=12'} + + supports-preserve-symlinks-flag@1.0.0: + resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} + engines: {node: '>= 0.4'} + + tapable@2.2.2: + resolution: {integrity: sha512-Re10+NauLTMCudc7T5WLFLAwDhQ0JWdrMK+9B2M8zR5hRExKmsRDCBA7/aV/pNJFltmBFO5BAMlQFi/vq3nKOg==} + engines: {node: '>=6'} + + test-exclude@6.0.0: + resolution: {integrity: sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==} + engines: {node: '>=8'} + + to-regex-range@5.0.1: + resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} + engines: {node: '>=8.0'} + + trim-lines@3.0.1: + resolution: {integrity: sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==} + + trough@2.2.0: + resolution: {integrity: sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==} + + ts-api-utils@2.1.0: + resolution: {integrity: sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==} + engines: {node: '>=18.12'} + peerDependencies: + typescript: '>=4.8.4' + + type-check@0.4.0: + resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} + engines: {node: '>= 0.8.0'} + + typed-array-buffer@1.0.3: + resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} + engines: {node: '>= 0.4'} + + typed-array-byte-length@1.0.3: + resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} + engines: {node: '>= 0.4'} + + typed-array-byte-offset@1.0.4: + resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} + engines: {node: '>= 0.4'} + + typed-array-length@1.0.7: + resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + engines: {node: '>= 0.4'} + + typescript@5.8.3: + resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} + engines: {node: '>=14.17'} + hasBin: true + + unbox-primitive@1.1.0: + resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} + engines: {node: '>= 0.4'} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + unified@11.0.5: + resolution: {integrity: sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==} + + unist-util-find-after@5.0.0: + resolution: {integrity: sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ==} + + unist-util-is@6.0.0: + resolution: {integrity: sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==} + + unist-util-position@5.0.0: + resolution: {integrity: sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==} + + unist-util-stringify-position@4.0.0: + resolution: {integrity: sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==} + + unist-util-visit-parents@6.0.1: + resolution: {integrity: sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==} + + unist-util-visit@5.0.0: + resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==} + + uri-js@4.4.1: + resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} + + util-deprecate@1.0.2: + resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + + v8-to-istanbul@9.3.0: + resolution: {integrity: sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==} + engines: {node: '>=10.12.0'} + + validate-npm-package-license@3.0.4: + resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} + + vfile-message@4.0.2: + resolution: {integrity: sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==} + + vfile@6.0.3: + resolution: {integrity: sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==} + + which-boxed-primitive@1.1.1: + resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} + engines: {node: '>= 0.4'} + + which-builtin-type@1.2.1: + resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} + engines: {node: '>= 0.4'} + + which-collection@1.0.2: + resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} + engines: {node: '>= 0.4'} + + which-typed-array@1.1.19: + resolution: {integrity: sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==} + engines: {node: '>= 0.4'} + + which@1.3.1: + resolution: {integrity: sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==} + hasBin: true + + which@2.0.2: + resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} + engines: {node: '>= 8'} + hasBin: true + + word-wrap@1.2.5: + resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} + engines: {node: '>=0.10.0'} + + workerpool@6.5.1: + resolution: {integrity: sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==} + + wrap-ansi@7.0.0: + resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} + engines: {node: '>=10'} + + wrap-ansi@8.1.0: + resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} + engines: {node: '>=12'} + + wrappy@1.0.2: + resolution: {integrity: sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==} + + y18n@5.0.8: + resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} + engines: {node: '>=10'} + + yargs-parser@20.2.9: + resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} + engines: {node: '>=10'} + + yargs-parser@21.1.1: + resolution: {integrity: sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==} + engines: {node: '>=12'} + + yargs-unparser@2.0.0: + resolution: {integrity: sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==} + engines: {node: '>=10'} + + yargs@16.2.0: + resolution: {integrity: sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==} + engines: {node: '>=10'} + + yargs@17.7.2: + resolution: {integrity: sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==} + engines: {node: '>=12'} + + yocto-queue@0.1.0: + resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} + engines: {node: '>=10'} + + zwitch@2.0.4: + resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} + +snapshots: + + '@anthropic-ai/claude-code@1.0.33': + optionalDependencies: + '@img/sharp-darwin-arm64': 0.33.5 + '@img/sharp-darwin-x64': 0.33.5 + '@img/sharp-linux-arm': 0.33.5 + '@img/sharp-linux-arm64': 0.33.5 + '@img/sharp-linux-x64': 0.33.5 + '@img/sharp-win32-x64': 0.33.5 + + '@bcoe/v8-coverage@0.2.3': {} + + '@esbuild/aix-ppc64@0.25.5': + optional: true + + '@esbuild/android-arm64@0.25.5': + optional: true + + '@esbuild/android-arm@0.25.5': + optional: true + + '@esbuild/android-x64@0.25.5': + optional: true + + '@esbuild/darwin-arm64@0.25.5': + optional: true + + '@esbuild/darwin-x64@0.25.5': + optional: true + + '@esbuild/freebsd-arm64@0.25.5': + optional: true + + '@esbuild/freebsd-x64@0.25.5': + optional: true + + '@esbuild/linux-arm64@0.25.5': + optional: true + + '@esbuild/linux-arm@0.25.5': + optional: true + + '@esbuild/linux-ia32@0.25.5': + optional: true + + '@esbuild/linux-loong64@0.25.5': + optional: true + + '@esbuild/linux-mips64el@0.25.5': + optional: true + + '@esbuild/linux-ppc64@0.25.5': + optional: true + + '@esbuild/linux-riscv64@0.25.5': + optional: true + + '@esbuild/linux-s390x@0.25.5': + optional: true + + '@esbuild/linux-x64@0.25.5': + optional: true + + '@esbuild/netbsd-arm64@0.25.5': + optional: true + + '@esbuild/netbsd-x64@0.25.5': + optional: true + + '@esbuild/openbsd-arm64@0.25.5': + optional: true + + '@esbuild/openbsd-x64@0.25.5': + optional: true + + '@esbuild/sunos-x64@0.25.5': + optional: true + + '@esbuild/win32-arm64@0.25.5': + optional: true + + '@esbuild/win32-ia32@0.25.5': + optional: true + + '@esbuild/win32-x64@0.25.5': + optional: true + + '@eslint-community/eslint-utils@4.7.0(eslint@9.29.0)': + dependencies: + eslint: 9.29.0 + eslint-visitor-keys: 3.4.3 + + '@eslint-community/regexpp@4.12.1': {} + + '@eslint/config-array@0.20.1': + dependencies: + '@eslint/object-schema': 2.1.6 + debug: 4.4.1(supports-color@8.1.1) + minimatch: 3.1.2 + transitivePeerDependencies: + - supports-color + + '@eslint/config-helpers@0.2.3': {} + + '@eslint/core@0.14.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/core@0.15.0': + dependencies: + '@types/json-schema': 7.0.15 + + '@eslint/eslintrc@3.3.1': + dependencies: + ajv: 6.12.6 + debug: 4.4.1(supports-color@8.1.1) + espree: 10.4.0 + globals: 14.0.0 + ignore: 5.3.2 + import-fresh: 3.3.1 + js-yaml: 4.1.0 + minimatch: 3.1.2 + strip-json-comments: 3.1.1 + transitivePeerDependencies: + - supports-color + + '@eslint/js@9.29.0': {} + + '@eslint/object-schema@2.1.6': {} + + '@eslint/plugin-kit@0.3.2': + dependencies: + '@eslint/core': 0.15.0 + levn: 0.4.1 + + '@humanfs/core@0.19.1': {} + + '@humanfs/node@0.16.6': + dependencies: + '@humanfs/core': 0.19.1 + '@humanwhocodes/retry': 0.3.1 + + '@humanwhocodes/module-importer@1.0.1': {} + + '@humanwhocodes/retry@0.3.1': {} + + '@humanwhocodes/retry@0.4.3': {} + + '@img/sharp-darwin-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.0.4 + optional: true + + '@img/sharp-darwin-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.0.4 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-darwin-x64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm64@1.0.4': + optional: true + + '@img/sharp-libvips-linux-arm@1.0.5': + optional: true + + '@img/sharp-libvips-linux-x64@1.0.4': + optional: true + + '@img/sharp-linux-arm64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.0.4 + optional: true + + '@img/sharp-linux-arm@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.0.5 + optional: true + + '@img/sharp-linux-x64@0.33.5': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.0.4 + optional: true + + '@img/sharp-win32-x64@0.33.5': + optional: true + + '@isaacs/cliui@8.0.2': + dependencies: + string-width: 5.1.2 + string-width-cjs: string-width@4.2.3 + strip-ansi: 7.1.0 + strip-ansi-cjs: strip-ansi@6.0.1 + wrap-ansi: 8.1.0 + wrap-ansi-cjs: wrap-ansi@7.0.0 + + '@istanbuljs/schema@0.1.3': {} + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.0': {} + + '@jridgewell/trace-mapping@0.3.25': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.0 + + '@nodelib/fs.scandir@2.1.5': + dependencies: + '@nodelib/fs.stat': 2.0.5 + run-parallel: 1.2.0 + + '@nodelib/fs.stat@2.0.5': {} + + '@nodelib/fs.walk@1.2.8': + dependencies: + '@nodelib/fs.scandir': 2.1.5 + fastq: 1.19.1 + + '@pkgjs/parseargs@0.11.0': + optional: true + + '@types/debug@4.1.12': + dependencies: + '@types/ms': 2.1.0 + + '@types/estree-jsx@1.0.5': + dependencies: + '@types/estree': 1.0.8 + + '@types/estree@1.0.8': {} + + '@types/hast@3.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/istanbul-lib-coverage@2.0.6': {} + + '@types/json-schema@7.0.15': {} + + '@types/mdast@4.0.4': + dependencies: + '@types/unist': 3.0.3 + + '@types/mocha@10.0.10': {} + + '@types/ms@2.1.0': {} + + '@types/node@20.19.1': + dependencies: + undici-types: 6.21.0 + + '@types/react-dom@19.1.6(@types/react@19.1.8)': + dependencies: + '@types/react': 19.1.8 + + '@types/react@19.1.8': + dependencies: + csstype: 3.1.3 + + '@types/unist@2.0.11': {} + + '@types/unist@3.0.3': {} + + '@types/vscode@1.101.0': {} + + '@typescript-eslint/eslint-plugin@8.35.0(@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3))(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/regexpp': 4.12.1 + '@typescript-eslint/parser': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/type-utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + eslint: 9.29.0 + graphemer: 1.4.0 + ignore: 7.0.5 + natural-compare: 1.4.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/parser@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/project-service@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + debug: 4.4.1(supports-color@8.1.1) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/scope-manager@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + + '@typescript-eslint/tsconfig-utils@8.35.0(typescript@5.8.3)': + dependencies: + typescript: 5.8.3 + + '@typescript-eslint/type-utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + '@typescript-eslint/utils': 8.35.0(eslint@9.29.0)(typescript@5.8.3) + debug: 4.4.1(supports-color@8.1.1) + eslint: 9.29.0 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/types@8.35.0': {} + + '@typescript-eslint/typescript-estree@8.35.0(typescript@5.8.3)': + dependencies: + '@typescript-eslint/project-service': 8.35.0(typescript@5.8.3) + '@typescript-eslint/tsconfig-utils': 8.35.0(typescript@5.8.3) + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/visitor-keys': 8.35.0 + debug: 4.4.1(supports-color@8.1.1) + fast-glob: 3.3.3 + is-glob: 4.0.3 + minimatch: 9.0.5 + semver: 7.7.2 + ts-api-utils: 2.1.0(typescript@5.8.3) + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/utils@8.35.0(eslint@9.29.0)(typescript@5.8.3)': + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@typescript-eslint/scope-manager': 8.35.0 + '@typescript-eslint/types': 8.35.0 + '@typescript-eslint/typescript-estree': 8.35.0(typescript@5.8.3) + eslint: 9.29.0 + typescript: 5.8.3 + transitivePeerDependencies: + - supports-color + + '@typescript-eslint/visitor-keys@8.35.0': + dependencies: + '@typescript-eslint/types': 8.35.0 + eslint-visitor-keys: 4.2.1 + + '@ungap/structured-clone@1.3.0': {} + + '@vscode/test-cli@0.0.10': + dependencies: + '@types/mocha': 10.0.10 + c8: 9.1.0 + chokidar: 3.6.0 + enhanced-resolve: 5.18.2 + glob: 10.4.5 + minimatch: 9.0.5 + mocha: 10.8.2 + supports-color: 9.4.0 + yargs: 17.7.2 + + '@vscode/test-electron@2.5.2': + dependencies: + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.6 + jszip: 3.10.1 + ora: 8.2.0 + semver: 7.7.2 + transitivePeerDependencies: + - supports-color + + acorn-jsx@5.3.2(acorn@8.15.0): + dependencies: + acorn: 8.15.0 + + acorn@8.15.0: {} + + agent-base@7.1.3: {} + + ajv@6.12.6: + dependencies: + fast-deep-equal: 3.1.3 + fast-json-stable-stringify: 2.1.0 + json-schema-traverse: 0.4.1 + uri-js: 4.4.1 + + ansi-colors@4.1.3: {} + + ansi-regex@5.0.1: {} + + ansi-regex@6.1.0: {} + + ansi-styles@3.2.1: + dependencies: + color-convert: 1.9.3 + + ansi-styles@4.3.0: + dependencies: + color-convert: 2.0.1 + + ansi-styles@6.2.1: {} + + anymatch@3.1.3: + dependencies: + normalize-path: 3.0.0 + picomatch: 2.3.1 + + argparse@2.0.1: {} + + array-buffer-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + is-array-buffer: 3.0.5 + + arraybuffer.prototype.slice@1.0.4: + dependencies: + array-buffer-byte-length: 1.0.2 + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + is-array-buffer: 3.0.5 + + async-function@1.0.0: {} + + available-typed-arrays@1.0.7: + dependencies: + possible-typed-array-names: 1.1.0 + + bail@2.0.2: {} + + balanced-match@1.0.2: {} + + binary-extensions@2.3.0: {} + + brace-expansion@1.1.12: + dependencies: + balanced-match: 1.0.2 + concat-map: 0.0.1 + + brace-expansion@2.0.2: + dependencies: + balanced-match: 1.0.2 + + braces@3.0.3: + dependencies: + fill-range: 7.1.1 + + browser-stdout@1.3.1: {} + + c8@9.1.0: + dependencies: + '@bcoe/v8-coverage': 0.2.3 + '@istanbuljs/schema': 0.1.3 + find-up: 5.0.0 + foreground-child: 3.3.1 + istanbul-lib-coverage: 3.2.2 + istanbul-lib-report: 3.0.1 + istanbul-reports: 3.1.7 + test-exclude: 6.0.0 + v8-to-istanbul: 9.3.0 + yargs: 17.7.2 + yargs-parser: 21.1.1 + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + call-bind@1.0.8: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + get-intrinsic: 1.3.0 + set-function-length: 1.2.2 + + call-bound@1.0.4: + dependencies: + call-bind-apply-helpers: 1.0.2 + get-intrinsic: 1.3.0 + + callsites@3.1.0: {} + + camelcase@6.3.0: {} + + ccount@2.0.1: {} + + chalk@2.4.2: + dependencies: + ansi-styles: 3.2.1 + escape-string-regexp: 1.0.5 + supports-color: 5.5.0 + + chalk@4.1.2: + dependencies: + ansi-styles: 4.3.0 + supports-color: 7.2.0 + + chalk@5.4.1: {} + + character-entities-html4@2.1.0: {} + + character-entities-legacy@3.0.0: {} + + character-entities@2.0.2: {} + + character-reference-invalid@2.0.1: {} + + chokidar@3.6.0: + dependencies: + anymatch: 3.1.3 + braces: 3.0.3 + glob-parent: 5.1.2 + is-binary-path: 2.1.0 + is-glob: 4.0.3 + normalize-path: 3.0.0 + readdirp: 3.6.0 + optionalDependencies: + fsevents: 2.3.3 + + cli-cursor@5.0.0: + dependencies: + restore-cursor: 5.1.0 + + cli-spinners@2.9.2: {} + + cliui@7.0.4: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + cliui@8.0.1: + dependencies: + string-width: 4.2.3 + strip-ansi: 6.0.1 + wrap-ansi: 7.0.0 + + color-convert@1.9.3: + dependencies: + color-name: 1.1.3 + + color-convert@2.0.1: + dependencies: + color-name: 1.1.4 + + color-name@1.1.3: {} + + color-name@1.1.4: {} + + comma-separated-tokens@2.0.3: {} + + concat-map@0.0.1: {} + + convert-source-map@2.0.0: {} + + core-util-is@1.0.3: {} + + cross-spawn@6.0.6: + dependencies: + nice-try: 1.0.5 + path-key: 2.0.1 + semver: 5.7.2 + shebang-command: 1.2.0 + which: 1.3.1 + + cross-spawn@7.0.6: + dependencies: + path-key: 3.1.1 + shebang-command: 2.0.0 + which: 2.0.2 + + csstype@3.1.3: {} + + data-view-buffer@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-length@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + data-view-byte-offset@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-data-view: 1.0.2 + + debug@4.4.1(supports-color@8.1.1): + dependencies: + ms: 2.1.3 + optionalDependencies: + supports-color: 8.1.1 + + decamelize@4.0.0: {} + + decode-named-character-reference@1.2.0: + dependencies: + character-entities: 2.0.2 + + deep-is@0.1.4: {} + + define-data-property@1.1.4: + dependencies: + es-define-property: 1.0.1 + es-errors: 1.3.0 + gopd: 1.2.0 + + define-properties@1.2.1: + dependencies: + define-data-property: 1.1.4 + has-property-descriptors: 1.0.2 + object-keys: 1.1.1 + + dequal@2.0.3: {} + + devlop@1.1.0: + dependencies: + dequal: 2.0.3 + + diff@5.2.0: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + eastasianwidth@0.2.0: {} + + emoji-regex@10.4.0: {} + + emoji-regex@8.0.0: {} + + emoji-regex@9.2.2: {} + + enhanced-resolve@5.18.2: + dependencies: + graceful-fs: 4.2.11 + tapable: 2.2.2 + + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + + es-abstract@1.24.0: + dependencies: + array-buffer-byte-length: 1.0.2 + arraybuffer.prototype.slice: 1.0.4 + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + data-view-buffer: 1.0.2 + data-view-byte-length: 1.0.2 + data-view-byte-offset: 1.0.1 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + es-set-tostringtag: 2.1.0 + es-to-primitive: 1.3.0 + function.prototype.name: 1.1.8 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + get-symbol-description: 1.1.0 + globalthis: 1.0.4 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + has-proto: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + internal-slot: 1.1.0 + is-array-buffer: 3.0.5 + is-callable: 1.2.7 + is-data-view: 1.0.2 + is-negative-zero: 2.0.3 + is-regex: 1.2.1 + is-set: 2.0.3 + is-shared-array-buffer: 1.0.4 + is-string: 1.1.1 + is-typed-array: 1.1.15 + is-weakref: 1.1.1 + math-intrinsics: 1.1.0 + object-inspect: 1.13.4 + object-keys: 1.1.1 + object.assign: 4.1.7 + own-keys: 1.0.1 + regexp.prototype.flags: 1.5.4 + safe-array-concat: 1.1.3 + safe-push-apply: 1.0.0 + safe-regex-test: 1.1.0 + set-proto: 1.0.0 + stop-iteration-iterator: 1.1.0 + string.prototype.trim: 1.2.10 + string.prototype.trimend: 1.0.9 + string.prototype.trimstart: 1.0.8 + typed-array-buffer: 1.0.3 + typed-array-byte-length: 1.0.3 + typed-array-byte-offset: 1.0.4 + typed-array-length: 1.0.7 + unbox-primitive: 1.1.0 + which-typed-array: 1.1.19 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-object-atoms@1.1.1: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + es-to-primitive@1.3.0: + dependencies: + is-callable: 1.2.7 + is-date-object: 1.1.0 + is-symbol: 1.1.1 + + esbuild@0.25.5: + optionalDependencies: + '@esbuild/aix-ppc64': 0.25.5 + '@esbuild/android-arm': 0.25.5 + '@esbuild/android-arm64': 0.25.5 + '@esbuild/android-x64': 0.25.5 + '@esbuild/darwin-arm64': 0.25.5 + '@esbuild/darwin-x64': 0.25.5 + '@esbuild/freebsd-arm64': 0.25.5 + '@esbuild/freebsd-x64': 0.25.5 + '@esbuild/linux-arm': 0.25.5 + '@esbuild/linux-arm64': 0.25.5 + '@esbuild/linux-ia32': 0.25.5 + '@esbuild/linux-loong64': 0.25.5 + '@esbuild/linux-mips64el': 0.25.5 + '@esbuild/linux-ppc64': 0.25.5 + '@esbuild/linux-riscv64': 0.25.5 + '@esbuild/linux-s390x': 0.25.5 + '@esbuild/linux-x64': 0.25.5 + '@esbuild/netbsd-arm64': 0.25.5 + '@esbuild/netbsd-x64': 0.25.5 + '@esbuild/openbsd-arm64': 0.25.5 + '@esbuild/openbsd-x64': 0.25.5 + '@esbuild/sunos-x64': 0.25.5 + '@esbuild/win32-arm64': 0.25.5 + '@esbuild/win32-ia32': 0.25.5 + '@esbuild/win32-x64': 0.25.5 + + escalade@3.2.0: {} + + escape-string-regexp@1.0.5: {} + + escape-string-regexp@4.0.0: {} + + escape-string-regexp@5.0.0: {} + + eslint-scope@8.4.0: + dependencies: + esrecurse: 4.3.0 + estraverse: 5.3.0 + + eslint-visitor-keys@3.4.3: {} + + eslint-visitor-keys@4.2.1: {} + + eslint@9.29.0: + dependencies: + '@eslint-community/eslint-utils': 4.7.0(eslint@9.29.0) + '@eslint-community/regexpp': 4.12.1 + '@eslint/config-array': 0.20.1 + '@eslint/config-helpers': 0.2.3 + '@eslint/core': 0.14.0 + '@eslint/eslintrc': 3.3.1 + '@eslint/js': 9.29.0 + '@eslint/plugin-kit': 0.3.2 + '@humanfs/node': 0.16.6 + '@humanwhocodes/module-importer': 1.0.1 + '@humanwhocodes/retry': 0.4.3 + '@types/estree': 1.0.8 + '@types/json-schema': 7.0.15 + ajv: 6.12.6 + chalk: 4.1.2 + cross-spawn: 7.0.6 + debug: 4.4.1(supports-color@8.1.1) + escape-string-regexp: 4.0.0 + eslint-scope: 8.4.0 + eslint-visitor-keys: 4.2.1 + espree: 10.4.0 + esquery: 1.6.0 + esutils: 2.0.3 + fast-deep-equal: 3.1.3 + file-entry-cache: 8.0.0 + find-up: 5.0.0 + glob-parent: 6.0.2 + ignore: 5.3.2 + imurmurhash: 0.1.4 + is-glob: 4.0.3 + json-stable-stringify-without-jsonify: 1.0.1 + lodash.merge: 4.6.2 + minimatch: 3.1.2 + natural-compare: 1.4.0 + optionator: 0.9.4 + transitivePeerDependencies: + - supports-color + + espree@10.4.0: + dependencies: + acorn: 8.15.0 + acorn-jsx: 5.3.2(acorn@8.15.0) + eslint-visitor-keys: 4.2.1 + + esquery@1.6.0: + dependencies: + estraverse: 5.3.0 + + esrecurse@4.3.0: + dependencies: + estraverse: 5.3.0 + + estraverse@5.3.0: {} + + estree-util-is-identifier-name@3.0.0: {} + + esutils@2.0.3: {} + + extend@3.0.2: {} + + fast-deep-equal@3.1.3: {} + + fast-glob@3.3.3: + dependencies: + '@nodelib/fs.stat': 2.0.5 + '@nodelib/fs.walk': 1.2.8 + glob-parent: 5.1.2 + merge2: 1.4.1 + micromatch: 4.0.8 + + fast-json-stable-stringify@2.1.0: {} + + fast-levenshtein@2.0.6: {} + + fastq@1.19.1: + dependencies: + reusify: 1.1.0 + + file-entry-cache@8.0.0: + dependencies: + flat-cache: 4.0.1 + + fill-range@7.1.1: + dependencies: + to-regex-range: 5.0.1 + + find-up@5.0.0: + dependencies: + locate-path: 6.0.0 + path-exists: 4.0.0 + + flat-cache@4.0.1: + dependencies: + flatted: 3.3.3 + keyv: 4.5.4 + + flat@5.0.2: {} + + flatted@3.3.3: {} + + for-each@0.3.5: + dependencies: + is-callable: 1.2.7 + + foreground-child@3.3.1: + dependencies: + cross-spawn: 7.0.6 + signal-exit: 4.1.0 + + fs.realpath@1.0.0: {} + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + function.prototype.name@1.1.8: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + functions-have-names: 1.2.3 + hasown: 2.0.2 + is-callable: 1.2.7 + + functions-have-names@1.2.3: {} + + get-caller-file@2.0.5: {} + + get-east-asian-width@1.3.0: {} + + get-intrinsic@1.3.0: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-define-property: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.2 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.1 + + get-symbol-description@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + + glob-parent@5.1.2: + dependencies: + is-glob: 4.0.3 + + glob-parent@6.0.2: + dependencies: + is-glob: 4.0.3 + + glob@10.4.5: + dependencies: + foreground-child: 3.3.1 + jackspeak: 3.4.3 + minimatch: 9.0.5 + minipass: 7.1.2 + package-json-from-dist: 1.0.1 + path-scurry: 1.11.1 + + glob@7.2.3: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 3.1.2 + once: 1.4.0 + path-is-absolute: 1.0.1 + + glob@8.1.0: + dependencies: + fs.realpath: 1.0.0 + inflight: 1.0.6 + inherits: 2.0.4 + minimatch: 5.1.6 + once: 1.4.0 + + globals@14.0.0: {} + + globalthis@1.0.4: + dependencies: + define-properties: 1.2.1 + gopd: 1.2.0 + + gopd@1.2.0: {} + + graceful-fs@4.2.11: {} + + graphemer@1.4.0: {} + + has-bigints@1.1.0: {} + + has-flag@3.0.0: {} + + has-flag@4.0.0: {} + + has-property-descriptors@1.0.2: + dependencies: + es-define-property: 1.0.1 + + has-proto@1.2.0: + dependencies: + dunder-proto: 1.0.1 + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.2: + dependencies: + function-bind: 1.1.2 + + hast-util-is-element@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + hast-util-to-jsx-runtime@2.3.6: + dependencies: + '@types/estree': 1.0.8 + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + comma-separated-tokens: 2.0.3 + devlop: 1.1.0 + estree-util-is-identifier-name: 3.0.0 + hast-util-whitespace: 3.0.0 + mdast-util-mdx-expression: 2.0.1 + mdast-util-mdx-jsx: 3.2.0 + mdast-util-mdxjs-esm: 2.0.1 + property-information: 7.1.0 + space-separated-tokens: 2.0.2 + style-to-js: 1.1.17 + unist-util-position: 5.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + hast-util-to-text@4.0.2: + dependencies: + '@types/hast': 3.0.4 + '@types/unist': 3.0.3 + hast-util-is-element: 3.0.0 + unist-util-find-after: 5.0.0 + + hast-util-whitespace@3.0.0: + dependencies: + '@types/hast': 3.0.4 + + he@1.2.0: {} + + highlight.js@11.11.1: {} + + hosted-git-info@2.8.9: {} + + html-escaper@2.0.2: {} + + html-url-attributes@3.0.1: {} + + http-proxy-agent@7.0.2: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + https-proxy-agent@7.0.6: + dependencies: + agent-base: 7.1.3 + debug: 4.4.1(supports-color@8.1.1) + transitivePeerDependencies: + - supports-color + + ignore@5.3.2: {} + + ignore@7.0.5: {} + + immediate@3.0.6: {} + + import-fresh@3.3.1: + dependencies: + parent-module: 1.0.1 + resolve-from: 4.0.0 + + imurmurhash@0.1.4: {} + + inflight@1.0.6: + dependencies: + once: 1.4.0 + wrappy: 1.0.2 + + inherits@2.0.4: {} + + inline-style-parser@0.2.4: {} + + internal-slot@1.1.0: + dependencies: + es-errors: 1.3.0 + hasown: 2.0.2 + side-channel: 1.1.0 + + is-alphabetical@2.0.1: {} + + is-alphanumerical@2.0.1: + dependencies: + is-alphabetical: 2.0.1 + is-decimal: 2.0.1 + + is-array-buffer@3.0.5: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + is-arrayish@0.2.1: {} + + is-async-function@2.1.1: + dependencies: + async-function: 1.0.0 + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-bigint@1.1.0: + dependencies: + has-bigints: 1.1.0 + + is-binary-path@2.1.0: + dependencies: + binary-extensions: 2.3.0 + + is-boolean-object@1.2.2: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-callable@1.2.7: {} + + is-core-module@2.16.1: + dependencies: + hasown: 2.0.2 + + is-data-view@1.0.2: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + is-typed-array: 1.1.15 + + is-date-object@1.1.0: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-decimal@2.0.1: {} + + is-extglob@2.1.1: {} + + is-finalizationregistry@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-fullwidth-code-point@3.0.0: {} + + is-generator-function@1.1.0: + dependencies: + call-bound: 1.0.4 + get-proto: 1.0.1 + has-tostringtag: 1.0.2 + safe-regex-test: 1.1.0 + + is-glob@4.0.3: + dependencies: + is-extglob: 2.1.1 + + is-hexadecimal@2.0.1: {} + + is-interactive@2.0.0: {} + + is-map@2.0.3: {} + + is-negative-zero@2.0.3: {} + + is-number-object@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-number@7.0.0: {} + + is-plain-obj@2.1.0: {} + + is-plain-obj@4.1.0: {} + + is-regex@1.2.1: + dependencies: + call-bound: 1.0.4 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + hasown: 2.0.2 + + is-set@2.0.3: {} + + is-shared-array-buffer@1.0.4: + dependencies: + call-bound: 1.0.4 + + is-string@1.1.1: + dependencies: + call-bound: 1.0.4 + has-tostringtag: 1.0.2 + + is-symbol@1.1.1: + dependencies: + call-bound: 1.0.4 + has-symbols: 1.1.0 + safe-regex-test: 1.1.0 + + is-typed-array@1.1.15: + dependencies: + which-typed-array: 1.1.19 + + is-unicode-supported@0.1.0: {} + + is-unicode-supported@1.3.0: {} + + is-unicode-supported@2.1.0: {} + + is-weakmap@2.0.2: {} + + is-weakref@1.1.1: + dependencies: + call-bound: 1.0.4 + + is-weakset@2.0.4: + dependencies: + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + + isarray@1.0.0: {} + + isarray@2.0.5: {} + + isexe@2.0.0: {} + + istanbul-lib-coverage@3.2.2: {} + + istanbul-lib-report@3.0.1: + dependencies: + istanbul-lib-coverage: 3.2.2 + make-dir: 4.0.0 + supports-color: 7.2.0 + + istanbul-reports@3.1.7: + dependencies: + html-escaper: 2.0.2 + istanbul-lib-report: 3.0.1 + + jackspeak@3.4.3: + dependencies: + '@isaacs/cliui': 8.0.2 + optionalDependencies: + '@pkgjs/parseargs': 0.11.0 + + js-yaml@4.1.0: + dependencies: + argparse: 2.0.1 + + json-buffer@3.0.1: {} + + json-parse-better-errors@1.0.2: {} + + json-schema-traverse@0.4.1: {} + + json-stable-stringify-without-jsonify@1.0.1: {} + + jszip@3.10.1: + dependencies: + lie: 3.3.0 + pako: 1.0.11 + readable-stream: 2.3.8 + setimmediate: 1.0.5 + + keyv@4.5.4: + dependencies: + json-buffer: 3.0.1 + + levn@0.4.1: + dependencies: + prelude-ls: 1.2.1 + type-check: 0.4.0 + + lie@3.3.0: + dependencies: + immediate: 3.0.6 + + load-json-file@4.0.0: + dependencies: + graceful-fs: 4.2.11 + parse-json: 4.0.0 + pify: 3.0.0 + strip-bom: 3.0.0 + + locate-path@6.0.0: + dependencies: + p-locate: 5.0.0 + + lodash.merge@4.6.2: {} + + log-symbols@4.1.0: + dependencies: + chalk: 4.1.2 + is-unicode-supported: 0.1.0 + + log-symbols@6.0.0: + dependencies: + chalk: 5.4.1 + is-unicode-supported: 1.3.0 + + longest-streak@3.1.0: {} + + lowlight@3.3.0: + dependencies: + '@types/hast': 3.0.4 + devlop: 1.1.0 + highlight.js: 11.11.1 + + lru-cache@10.4.3: {} + + lucide-react@0.522.0(react@19.1.0): + dependencies: + react: 19.1.0 + + make-dir@4.0.0: + dependencies: + semver: 7.7.2 + + markdown-table@3.0.4: {} + + math-intrinsics@1.1.0: {} + + mdast-util-find-and-replace@3.0.2: + dependencies: + '@types/mdast': 4.0.4 + escape-string-regexp: 5.0.0 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + mdast-util-from-markdown@2.0.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + mdast-util-to-string: 4.0.0 + micromark: 4.0.2 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-decode-string: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + unist-util-stringify-position: 4.0.0 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-autolink-literal@2.0.1: + dependencies: + '@types/mdast': 4.0.4 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-find-and-replace: 3.0.2 + micromark-util-character: 2.1.1 + + mdast-util-gfm-footnote@2.1.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + micromark-util-normalize-identifier: 2.0.1 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-strikethrough@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-table@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + markdown-table: 3.0.4 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm-task-list-item@2.0.0: + dependencies: + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-gfm@3.1.0: + dependencies: + mdast-util-from-markdown: 2.0.2 + mdast-util-gfm-autolink-literal: 2.0.1 + mdast-util-gfm-footnote: 2.1.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.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-expression@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdx-jsx@3.2.0: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + ccount: 2.0.1 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + parse-entities: 4.0.2 + stringify-entities: 4.0.4 + unist-util-stringify-position: 4.0.0 + vfile-message: 4.0.2 + transitivePeerDependencies: + - supports-color + + mdast-util-mdxjs-esm@2.0.1: + dependencies: + '@types/estree-jsx': 1.0.5 + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + devlop: 1.1.0 + mdast-util-from-markdown: 2.0.2 + mdast-util-to-markdown: 2.1.2 + transitivePeerDependencies: + - supports-color + + mdast-util-phrasing@4.1.0: + dependencies: + '@types/mdast': 4.0.4 + unist-util-is: 6.0.0 + + mdast-util-to-hast@13.2.0: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@ungap/structured-clone': 1.3.0 + devlop: 1.1.0 + micromark-util-sanitize-uri: 2.0.1 + trim-lines: 3.0.1 + unist-util-position: 5.0.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + mdast-util-to-markdown@2.1.2: + dependencies: + '@types/mdast': 4.0.4 + '@types/unist': 3.0.3 + longest-streak: 3.1.0 + mdast-util-phrasing: 4.1.0 + mdast-util-to-string: 4.0.0 + micromark-util-classify-character: 2.0.1 + micromark-util-decode-string: 2.0.1 + unist-util-visit: 5.0.0 + zwitch: 2.0.4 + + mdast-util-to-string@4.0.0: + dependencies: + '@types/mdast': 4.0.4 + + memorystream@0.3.1: {} + + merge2@1.4.1: {} + + micromark-core-commonmark@2.0.3: + dependencies: + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-factory-destination: 2.0.1 + micromark-factory-label: 2.0.1 + micromark-factory-space: 2.0.1 + micromark-factory-title: 2.0.1 + micromark-factory-whitespace: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-html-tag-name: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-autolink-literal@2.1.0: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-footnote@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-strikethrough@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-classify-character: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-table@2.1.1: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm-tagfilter@2.0.0: + dependencies: + micromark-util-types: 2.0.2 + + micromark-extension-gfm-task-list-item@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-extension-gfm@3.0.0: + dependencies: + micromark-extension-gfm-autolink-literal: 2.1.0 + micromark-extension-gfm-footnote: 2.1.0 + micromark-extension-gfm-strikethrough: 2.1.0 + micromark-extension-gfm-table: 2.1.1 + micromark-extension-gfm-tagfilter: 2.0.0 + micromark-extension-gfm-task-list-item: 2.1.0 + micromark-util-combine-extensions: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-destination@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-label@2.0.1: + dependencies: + devlop: 1.1.0 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-space@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-types: 2.0.2 + + micromark-factory-title@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-factory-whitespace@2.0.1: + dependencies: + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-character@2.1.1: + dependencies: + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-chunked@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-classify-character@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-combine-extensions@2.0.1: + dependencies: + micromark-util-chunked: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-decode-numeric-character-reference@2.0.2: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-decode-string@2.0.1: + dependencies: + decode-named-character-reference: 1.2.0 + micromark-util-character: 2.1.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-symbol: 2.0.1 + + micromark-util-encode@2.0.1: {} + + micromark-util-html-tag-name@2.0.1: {} + + micromark-util-normalize-identifier@2.0.1: + dependencies: + micromark-util-symbol: 2.0.1 + + micromark-util-resolve-all@2.0.1: + dependencies: + micromark-util-types: 2.0.2 + + micromark-util-sanitize-uri@2.0.1: + dependencies: + micromark-util-character: 2.1.1 + micromark-util-encode: 2.0.1 + micromark-util-symbol: 2.0.1 + + micromark-util-subtokenize@2.1.0: + dependencies: + devlop: 1.1.0 + micromark-util-chunked: 2.0.1 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + + micromark-util-symbol@2.0.1: {} + + micromark-util-types@2.0.2: {} + + micromark@4.0.2: + dependencies: + '@types/debug': 4.1.12 + debug: 4.4.1(supports-color@8.1.1) + decode-named-character-reference: 1.2.0 + devlop: 1.1.0 + micromark-core-commonmark: 2.0.3 + micromark-factory-space: 2.0.1 + micromark-util-character: 2.1.1 + micromark-util-chunked: 2.0.1 + micromark-util-combine-extensions: 2.0.1 + micromark-util-decode-numeric-character-reference: 2.0.2 + micromark-util-encode: 2.0.1 + micromark-util-normalize-identifier: 2.0.1 + micromark-util-resolve-all: 2.0.1 + micromark-util-sanitize-uri: 2.0.1 + micromark-util-subtokenize: 2.1.0 + micromark-util-symbol: 2.0.1 + micromark-util-types: 2.0.2 + transitivePeerDependencies: + - supports-color + + micromatch@4.0.8: + dependencies: + braces: 3.0.3 + picomatch: 2.3.1 + + mimic-function@5.0.1: {} + + minimatch@3.1.2: + dependencies: + brace-expansion: 1.1.12 + + minimatch@5.1.6: + dependencies: + brace-expansion: 2.0.2 + + minimatch@9.0.5: + dependencies: + brace-expansion: 2.0.2 + + minipass@7.1.2: {} + + mocha@10.8.2: + dependencies: + ansi-colors: 4.1.3 + browser-stdout: 1.3.1 + chokidar: 3.6.0 + debug: 4.4.1(supports-color@8.1.1) + diff: 5.2.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 8.1.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.1.6 + ms: 2.1.3 + serialize-javascript: 6.0.2 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.5.1 + yargs: 16.2.0 + yargs-parser: 20.2.9 + yargs-unparser: 2.0.0 + + ms@2.1.3: {} + + natural-compare@1.4.0: {} + + nice-try@1.0.5: {} + + normalize-package-data@2.5.0: + dependencies: + hosted-git-info: 2.8.9 + resolve: 1.22.10 + semver: 5.7.2 + validate-npm-package-license: 3.0.4 + + normalize-path@3.0.0: {} + + npm-run-all@4.1.5: + dependencies: + ansi-styles: 3.2.1 + chalk: 2.4.2 + cross-spawn: 6.0.6 + memorystream: 0.3.1 + minimatch: 3.1.2 + pidtree: 0.3.1 + read-pkg: 3.0.0 + shell-quote: 1.8.3 + string.prototype.padend: 3.1.6 + + object-inspect@1.13.4: {} + + object-keys@1.1.1: {} + + object.assign@4.1.7: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + has-symbols: 1.1.0 + object-keys: 1.1.1 + + once@1.4.0: + dependencies: + wrappy: 1.0.2 + + onetime@7.0.0: + dependencies: + mimic-function: 5.0.1 + + optionator@0.9.4: + dependencies: + deep-is: 0.1.4 + fast-levenshtein: 2.0.6 + levn: 0.4.1 + prelude-ls: 1.2.1 + type-check: 0.4.0 + word-wrap: 1.2.5 + + ora@8.2.0: + dependencies: + chalk: 5.4.1 + cli-cursor: 5.0.0 + cli-spinners: 2.9.2 + is-interactive: 2.0.0 + is-unicode-supported: 2.1.0 + log-symbols: 6.0.0 + stdin-discarder: 0.2.2 + string-width: 7.2.0 + strip-ansi: 7.1.0 + + own-keys@1.0.1: + dependencies: + get-intrinsic: 1.3.0 + object-keys: 1.1.1 + safe-push-apply: 1.0.0 + + p-limit@3.1.0: + dependencies: + yocto-queue: 0.1.0 + + p-locate@5.0.0: + dependencies: + p-limit: 3.1.0 + + package-json-from-dist@1.0.1: {} + + pako@1.0.11: {} + + parent-module@1.0.1: + dependencies: + callsites: 3.1.0 + + parse-entities@4.0.2: + dependencies: + '@types/unist': 2.0.11 + character-entities-legacy: 3.0.0 + character-reference-invalid: 2.0.1 + decode-named-character-reference: 1.2.0 + is-alphanumerical: 2.0.1 + is-decimal: 2.0.1 + is-hexadecimal: 2.0.1 + + parse-json@4.0.0: + dependencies: + error-ex: 1.3.2 + json-parse-better-errors: 1.0.2 + + path-exists@4.0.0: {} + + path-is-absolute@1.0.1: {} + + path-key@2.0.1: {} + + path-key@3.1.1: {} + + path-parse@1.0.7: {} + + path-scurry@1.11.1: + dependencies: + lru-cache: 10.4.3 + minipass: 7.1.2 + + path-type@3.0.0: + dependencies: + pify: 3.0.0 + + picomatch@2.3.1: {} + + pidtree@0.3.1: {} + + pify@3.0.0: {} + + possible-typed-array-names@1.1.0: {} + + prelude-ls@1.2.1: {} + + process-nextick-args@2.0.1: {} + + property-information@7.1.0: {} + + punycode@2.3.1: {} + + queue-microtask@1.2.3: {} + + randombytes@2.1.0: + dependencies: + safe-buffer: 5.2.1 + + react-dom@19.1.0(react@19.1.0): + dependencies: + react: 19.1.0 + scheduler: 0.26.0 + + react-markdown@10.1.0(@types/react@19.1.8)(react@19.1.0): + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + '@types/react': 19.1.8 + devlop: 1.1.0 + hast-util-to-jsx-runtime: 2.3.6 + html-url-attributes: 3.0.1 + mdast-util-to-hast: 13.2.0 + react: 19.1.0 + remark-parse: 11.0.0 + remark-rehype: 11.1.2 + unified: 11.0.5 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + transitivePeerDependencies: + - supports-color + + react-zoom-pan-pinch@3.7.0(react-dom@19.1.0(react@19.1.0))(react@19.1.0): + dependencies: + react: 19.1.0 + react-dom: 19.1.0(react@19.1.0) + + react@19.1.0: {} + + read-pkg@3.0.0: + dependencies: + load-json-file: 4.0.0 + normalize-package-data: 2.5.0 + path-type: 3.0.0 + + readable-stream@2.3.8: + dependencies: + core-util-is: 1.0.3 + inherits: 2.0.4 + isarray: 1.0.0 + process-nextick-args: 2.0.1 + safe-buffer: 5.1.2 + string_decoder: 1.1.1 + util-deprecate: 1.0.2 + + readdirp@3.6.0: + dependencies: + picomatch: 2.3.1 + + reflect.getprototypeof@1.0.10: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + get-intrinsic: 1.3.0 + get-proto: 1.0.1 + which-builtin-type: 1.2.1 + + regexp.prototype.flags@1.5.4: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-errors: 1.3.0 + get-proto: 1.0.1 + gopd: 1.2.0 + set-function-name: 2.0.2 + + rehype-highlight@7.0.2: + dependencies: + '@types/hast': 3.0.4 + hast-util-to-text: 4.0.2 + lowlight: 3.3.0 + unist-util-visit: 5.0.0 + vfile: 6.0.3 + + remark-gfm@4.0.1: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-gfm: 3.1.0 + micromark-extension-gfm: 3.0.0 + remark-parse: 11.0.0 + remark-stringify: 11.0.0 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-parse@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-from-markdown: 2.0.2 + micromark-util-types: 2.0.2 + unified: 11.0.5 + transitivePeerDependencies: + - supports-color + + remark-rehype@11.1.2: + dependencies: + '@types/hast': 3.0.4 + '@types/mdast': 4.0.4 + mdast-util-to-hast: 13.2.0 + unified: 11.0.5 + vfile: 6.0.3 + + remark-stringify@11.0.0: + dependencies: + '@types/mdast': 4.0.4 + mdast-util-to-markdown: 2.1.2 + unified: 11.0.5 + + require-directory@2.1.1: {} + + resolve-from@4.0.0: {} + + resolve@1.22.10: + dependencies: + is-core-module: 2.16.1 + path-parse: 1.0.7 + supports-preserve-symlinks-flag: 1.0.0 + + restore-cursor@5.1.0: + dependencies: + onetime: 7.0.0 + signal-exit: 4.1.0 + + reusify@1.1.0: {} + + run-parallel@1.2.0: + dependencies: + queue-microtask: 1.2.3 + + safe-array-concat@1.1.3: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + get-intrinsic: 1.3.0 + has-symbols: 1.1.0 + isarray: 2.0.5 + + safe-buffer@5.1.2: {} + + safe-buffer@5.2.1: {} + + safe-push-apply@1.0.0: + dependencies: + es-errors: 1.3.0 + isarray: 2.0.5 + + safe-regex-test@1.1.0: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-regex: 1.2.1 + + scheduler@0.26.0: {} + + semver@5.7.2: {} + + semver@7.7.2: {} + + serialize-javascript@6.0.2: + dependencies: + randombytes: 2.1.0 + + set-function-length@1.2.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + function-bind: 1.1.2 + get-intrinsic: 1.3.0 + gopd: 1.2.0 + has-property-descriptors: 1.0.2 + + set-function-name@2.0.2: + dependencies: + define-data-property: 1.1.4 + es-errors: 1.3.0 + functions-have-names: 1.2.3 + has-property-descriptors: 1.0.2 + + set-proto@1.0.0: + dependencies: + dunder-proto: 1.0.1 + es-errors: 1.3.0 + es-object-atoms: 1.1.1 + + setimmediate@1.0.5: {} + + shebang-command@1.2.0: + dependencies: + shebang-regex: 1.0.0 + + shebang-command@2.0.0: + dependencies: + shebang-regex: 3.0.0 + + shebang-regex@1.0.0: {} + + shebang-regex@3.0.0: {} + + shell-quote@1.8.3: {} + + side-channel-list@1.0.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + + side-channel-map@1.0.1: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + + side-channel-weakmap@1.0.2: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + object-inspect: 1.13.4 + side-channel-map: 1.0.1 + + side-channel@1.1.0: + dependencies: + es-errors: 1.3.0 + object-inspect: 1.13.4 + side-channel-list: 1.0.0 + side-channel-map: 1.0.1 + side-channel-weakmap: 1.0.2 + + signal-exit@4.1.0: {} + + space-separated-tokens@2.0.2: {} + + spdx-correct@3.2.0: + dependencies: + spdx-expression-parse: 3.0.1 + spdx-license-ids: 3.0.21 + + spdx-exceptions@2.5.0: {} + + spdx-expression-parse@3.0.1: + dependencies: + spdx-exceptions: 2.5.0 + spdx-license-ids: 3.0.21 + + spdx-license-ids@3.0.21: {} + + stdin-discarder@0.2.2: {} + + stop-iteration-iterator@1.1.0: + dependencies: + es-errors: 1.3.0 + internal-slot: 1.1.0 + + string-width@4.2.3: + dependencies: + emoji-regex: 8.0.0 + is-fullwidth-code-point: 3.0.0 + strip-ansi: 6.0.1 + + string-width@5.1.2: + dependencies: + eastasianwidth: 0.2.0 + emoji-regex: 9.2.2 + strip-ansi: 7.1.0 + + string-width@7.2.0: + dependencies: + emoji-regex: 10.4.0 + get-east-asian-width: 1.3.0 + strip-ansi: 7.1.0 + + string.prototype.padend@3.1.6: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + + string.prototype.trim@1.2.10: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-data-property: 1.1.4 + define-properties: 1.2.1 + es-abstract: 1.24.0 + es-object-atoms: 1.1.1 + has-property-descriptors: 1.0.2 + + string.prototype.trimend@1.0.9: + dependencies: + call-bind: 1.0.8 + call-bound: 1.0.4 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string.prototype.trimstart@1.0.8: + dependencies: + call-bind: 1.0.8 + define-properties: 1.2.1 + es-object-atoms: 1.1.1 + + string_decoder@1.1.1: + dependencies: + safe-buffer: 5.1.2 + + stringify-entities@4.0.4: + dependencies: + character-entities-html4: 2.1.0 + character-entities-legacy: 3.0.0 + + strip-ansi@6.0.1: + dependencies: + ansi-regex: 5.0.1 + + strip-ansi@7.1.0: + dependencies: + ansi-regex: 6.1.0 + + strip-bom@3.0.0: {} + + strip-json-comments@3.1.1: {} + + style-to-js@1.1.17: + dependencies: + style-to-object: 1.0.9 + + style-to-object@1.0.9: + dependencies: + inline-style-parser: 0.2.4 + + supports-color@5.5.0: + dependencies: + has-flag: 3.0.0 + + supports-color@7.2.0: + dependencies: + has-flag: 4.0.0 + + supports-color@8.1.1: + dependencies: + has-flag: 4.0.0 + + supports-color@9.4.0: {} + + supports-preserve-symlinks-flag@1.0.0: {} + + tapable@2.2.2: {} + + test-exclude@6.0.0: + dependencies: + '@istanbuljs/schema': 0.1.3 + glob: 7.2.3 + minimatch: 3.1.2 + + to-regex-range@5.0.1: + dependencies: + is-number: 7.0.0 + + trim-lines@3.0.1: {} + + trough@2.2.0: {} + + ts-api-utils@2.1.0(typescript@5.8.3): + dependencies: + typescript: 5.8.3 + + type-check@0.4.0: + dependencies: + prelude-ls: 1.2.1 + + typed-array-buffer@1.0.3: + dependencies: + call-bound: 1.0.4 + es-errors: 1.3.0 + is-typed-array: 1.1.15 + + typed-array-byte-length@1.0.3: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + + typed-array-byte-offset@1.0.4: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + has-proto: 1.2.0 + is-typed-array: 1.1.15 + reflect.getprototypeof: 1.0.10 + + typed-array-length@1.0.7: + dependencies: + call-bind: 1.0.8 + for-each: 0.3.5 + gopd: 1.2.0 + is-typed-array: 1.1.15 + possible-typed-array-names: 1.1.0 + reflect.getprototypeof: 1.0.10 + + typescript@5.8.3: {} + + unbox-primitive@1.1.0: + dependencies: + call-bound: 1.0.4 + has-bigints: 1.1.0 + has-symbols: 1.1.0 + which-boxed-primitive: 1.1.1 + + undici-types@6.21.0: {} + + unified@11.0.5: + dependencies: + '@types/unist': 3.0.3 + bail: 2.0.2 + devlop: 1.1.0 + extend: 3.0.2 + is-plain-obj: 4.1.0 + trough: 2.2.0 + vfile: 6.0.3 + + unist-util-find-after@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-is@6.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-position@5.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-stringify-position@4.0.0: + dependencies: + '@types/unist': 3.0.3 + + unist-util-visit-parents@6.0.1: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + + unist-util-visit@5.0.0: + dependencies: + '@types/unist': 3.0.3 + unist-util-is: 6.0.0 + unist-util-visit-parents: 6.0.1 + + uri-js@4.4.1: + dependencies: + punycode: 2.3.1 + + util-deprecate@1.0.2: {} + + v8-to-istanbul@9.3.0: + dependencies: + '@jridgewell/trace-mapping': 0.3.25 + '@types/istanbul-lib-coverage': 2.0.6 + convert-source-map: 2.0.0 + + validate-npm-package-license@3.0.4: + dependencies: + spdx-correct: 3.2.0 + spdx-expression-parse: 3.0.1 + + vfile-message@4.0.2: + dependencies: + '@types/unist': 3.0.3 + unist-util-stringify-position: 4.0.0 + + vfile@6.0.3: + dependencies: + '@types/unist': 3.0.3 + vfile-message: 4.0.2 + + which-boxed-primitive@1.1.1: + dependencies: + is-bigint: 1.1.0 + is-boolean-object: 1.2.2 + is-number-object: 1.1.1 + is-string: 1.1.1 + is-symbol: 1.1.1 + + which-builtin-type@1.2.1: + dependencies: + call-bound: 1.0.4 + function.prototype.name: 1.1.8 + has-tostringtag: 1.0.2 + is-async-function: 2.1.1 + is-date-object: 1.1.0 + is-finalizationregistry: 1.1.1 + is-generator-function: 1.1.0 + is-regex: 1.2.1 + is-weakref: 1.1.1 + isarray: 2.0.5 + which-boxed-primitive: 1.1.1 + which-collection: 1.0.2 + which-typed-array: 1.1.19 + + which-collection@1.0.2: + dependencies: + is-map: 2.0.3 + is-set: 2.0.3 + is-weakmap: 2.0.2 + is-weakset: 2.0.4 + + which-typed-array@1.1.19: + dependencies: + available-typed-arrays: 1.0.7 + call-bind: 1.0.8 + call-bound: 1.0.4 + for-each: 0.3.5 + get-proto: 1.0.1 + gopd: 1.2.0 + has-tostringtag: 1.0.2 + + which@1.3.1: + dependencies: + isexe: 2.0.0 + + which@2.0.2: + dependencies: + isexe: 2.0.0 + + word-wrap@1.2.5: {} + + workerpool@6.5.1: {} + + wrap-ansi@7.0.0: + dependencies: + ansi-styles: 4.3.0 + string-width: 4.2.3 + strip-ansi: 6.0.1 + + wrap-ansi@8.1.0: + dependencies: + ansi-styles: 6.2.1 + string-width: 5.1.2 + strip-ansi: 7.1.0 + + wrappy@1.0.2: {} + + y18n@5.0.8: {} + + yargs-parser@20.2.9: {} + + yargs-parser@21.1.1: {} + + yargs-unparser@2.0.0: + dependencies: + camelcase: 6.3.0 + decamelize: 4.0.0 + flat: 5.0.2 + is-plain-obj: 2.1.0 + + yargs@16.2.0: + dependencies: + cliui: 7.0.4 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 20.2.9 + + yargs@17.7.2: + dependencies: + cliui: 8.0.1 + escalade: 3.2.0 + get-caller-file: 2.0.5 + require-directory: 2.1.1 + string-width: 4.2.3 + y18n: 5.0.8 + yargs-parser: 21.1.1 + + yocto-queue@0.1.0: {} + + zwitch@2.0.4: {} diff --git a/src/assets/bolt_logo.jpg b/src/assets/bolt_logo.jpg new file mode 100644 index 0000000..b085fc6 Binary files /dev/null and b/src/assets/bolt_logo.jpg differ diff --git a/src/assets/claude_code_logo.png b/src/assets/claude_code_logo.png new file mode 100644 index 0000000..5f96751 Binary files /dev/null and b/src/assets/claude_code_logo.png differ diff --git a/src/assets/cursor_logo.png b/src/assets/cursor_logo.png new file mode 100644 index 0000000..d7f449f Binary files /dev/null and b/src/assets/cursor_logo.png differ diff --git a/src/assets/icon.png b/src/assets/icon.png new file mode 100644 index 0000000..72de409 Binary files /dev/null and b/src/assets/icon.png differ diff --git a/src/assets/icon.svg b/src/assets/icon.svg new file mode 100644 index 0000000..4528a28 --- /dev/null +++ b/src/assets/icon.svg @@ -0,0 +1 @@ +<svg width="128" height="128" viewBox="0 0 128 128" fill="none" xmlns="http://www.w3.org/2000/svg" class="drop-shadow-lg"><defs><linearGradient id="yellowOrange" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFE066"></stop><stop offset="50%" stop-color="#FFB347"></stop><stop offset="100%" stop-color="#FF8C42"></stop></linearGradient><linearGradient id="pinkCoral" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#FFB3E6"></stop><stop offset="50%" stop-color="#FF6B9D"></stop><stop offset="100%" stop-color="#E91E63"></stop></linearGradient><linearGradient id="bluePurple" x1="0%" y1="0%" x2="100%" y2="100%"><stop offset="0%" stop-color="#87CEEB"></stop><stop offset="50%" stop-color="#9C27B0"></stop><stop offset="100%" stop-color="#1976D2"></stop></linearGradient></defs><rect x="45" y="13" width="70" height="52" rx="13" ry="13" fill="url(#yellowOrange)"></rect><rect x="26" y="32" width="70" height="52" rx="13" ry="13" fill="url(#pinkCoral)"></rect><rect x="6" y="51" width="70" height="52" rx="13" ry="13" fill="url(#bluePurple)"></rect></svg> \ No newline at end of file diff --git a/src/assets/lovable_logo.png b/src/assets/lovable_logo.png new file mode 100644 index 0000000..3ca16cb Binary files /dev/null and b/src/assets/lovable_logo.png differ diff --git a/src/assets/windsurf_logo.png b/src/assets/windsurf_logo.png new file mode 100644 index 0000000..5f06a49 Binary files /dev/null and b/src/assets/windsurf_logo.png differ diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..4c67fa8 --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,1888 @@ +// The module 'vscode' contains the VS Code extensibility API +// Import the module and reference it with the alias vscode in your code below +import * as vscode from 'vscode'; +import { CustomAgentService } from './services/customAgentService'; +import { ChatSidebarProvider } from './providers/chatSidebarProvider'; +import { Logger, LogLevel } from './services/logger'; +import * as path from 'path'; + +// This method is called when your extension is activated +// Your extension is activated the very first time the command is executed + +// Function to save uploaded images to moodboard directory +async function saveImageToMoodboard(data: { + fileName: string; + originalName: string; + base64Data: string; + mimeType: string; + size: number; +}, sidebarProvider: ChatSidebarProvider) { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + Logger.error('No workspace folder found for saving image'); + return; + } + + try { + // Create .superdesign/moodboard directory if it doesn't exist + const moodboardDir = vscode.Uri.joinPath(workspaceFolder.uri, '.superdesign', 'moodboard'); + + try { + await vscode.workspace.fs.stat(moodboardDir); + } catch { + // Directory doesn't exist, create it + await vscode.workspace.fs.createDirectory(moodboardDir); + Logger.info('Created .superdesign/moodboard directory'); + } + + // Convert base64 to buffer and save file + const base64Content = data.base64Data.split(',')[1]; // Remove data:image/jpeg;base64, prefix + const buffer = Buffer.from(base64Content, 'base64'); + const filePath = vscode.Uri.joinPath(moodboardDir, data.fileName); + + await vscode.workspace.fs.writeFile(filePath, buffer); + + Logger.info(`Image saved to moodboard: ${data.fileName} (${(data.size / 1024).toFixed(1)} KB)`); + + // Send back the full absolute path to the webview + sidebarProvider.sendMessage({ + command: 'imageSavedToMoodboard', + data: { + fileName: data.fileName, + originalName: data.originalName, + fullPath: filePath.fsPath + } + }); + + } catch (error) { + Logger.error(`Error saving image to moodboard: ${error}`); + vscode.window.showErrorMessage(`Failed to save image: ${error}`); + + // Send error back to webview + sidebarProvider.sendMessage({ + command: 'imageSaveError', + data: { + fileName: data.fileName, + originalName: data.originalName, + error: error instanceof Error ? error.message : String(error) + } + }); + } +} + +// Function to convert image files to base64 for AI SDK +async function getBase64Image(filePath: string, sidebarProvider: ChatSidebarProvider) { + try { + // Read the image file + const fileUri = vscode.Uri.file(filePath); + const fileData = await vscode.workspace.fs.readFile(fileUri); + + // Determine MIME type from file extension + const extension = filePath.toLowerCase().split('.').pop(); + let mimeType: string; + switch (extension) { + case 'jpg': + case 'jpeg': + mimeType = 'image/jpeg'; + break; + case 'png': + mimeType = 'image/png'; + break; + case 'gif': + mimeType = 'image/gif'; + break; + case 'webp': + mimeType = 'image/webp'; + break; + case 'bmp': + mimeType = 'image/bmp'; + break; + default: + mimeType = 'image/png'; // Default fallback + } + + // Convert to base64 + const base64Content = Buffer.from(fileData).toString('base64'); + const base64DataUri = `data:${mimeType};base64,${base64Content}`; + + console.log(`Converted image to base64: ${filePath} (${(fileData.length / 1024).toFixed(1)} KB)`); + + // Send back the base64 data to webview + sidebarProvider.sendMessage({ + command: 'base64ImageResponse', + filePath: filePath, + base64Data: base64DataUri, + mimeType: mimeType, + size: fileData.length + }); + + } catch (error) { + console.error('Error converting image to base64:', error); + + // Send error back to webview + sidebarProvider.sendMessage({ + command: 'base64ImageResponse', + filePath: filePath, + error: error instanceof Error ? error.message : String(error) + }); + } +} + +// Function to read CSS file content for theme preview +async function getCssFileContent(filePath: string, sidebarProvider: ChatSidebarProvider) { + try { + // Handle relative paths - resolve them to workspace root + let resolvedPath = filePath; + + if (!path.isAbsolute(filePath)) { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + throw new Error('No workspace folder found'); + } + + // If path doesn't start with .superdesign, add it + if (!filePath.startsWith('.superdesign/') && filePath.startsWith('design_iterations/')) { + resolvedPath = `.superdesign/${filePath}`; + } + + resolvedPath = path.join(workspaceFolder.uri.fsPath, resolvedPath); + } + + // Read the CSS file + const fileUri = vscode.Uri.file(resolvedPath); + const fileData = await vscode.workspace.fs.readFile(fileUri); + + // Convert to string + const cssContent = Buffer.from(fileData).toString('utf8'); + + console.log(`Read CSS file: ${resolvedPath} (${(fileData.length / 1024).toFixed(1)} KB)`); + + // Send back the CSS content to webview + sidebarProvider.sendMessage({ + command: 'cssFileContentResponse', + filePath: filePath, + content: cssContent, + size: fileData.length + }); + + } catch (error) { + console.error('Error reading CSS file:', error); + + // Send error back to webview + sidebarProvider.sendMessage({ + command: 'cssFileContentResponse', + filePath: filePath, + error: error instanceof Error ? error.message : String(error) + }); + } +} + +// Function to submit email to Supabase API +async function submitEmailToSupabase(email: string, sidebarProvider: ChatSidebarProvider) { + try { + const https = require('https'); + const postData = JSON.stringify({ email }); + + const options = { + hostname: 'uqofryalyuvdvlbbutvi.supabase.co', + port: 443, + path: '/rest/v1/forms', + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'apikey': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InVxb2ZyeWFseXV2ZHZsYmJ1dHZpIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NTA3NDUxMTUsImV4cCI6MjA2NjMyMTExNX0.xyIw5nMK_ltpU64Z95E5xsnl8Uw3P0Y0UZaJKiX65MI', + 'Content-Length': Buffer.byteLength(postData) + } + }; + + const req = https.request(options, (res: any) => { + let data = ''; + + res.on('data', (chunk: string) => { + data += chunk; + }); + + res.on('end', () => { + if (res.statusCode >= 200 && res.statusCode < 300) { + Logger.info(`Email submitted successfully: ${email}`); + sidebarProvider.sendMessage({ + command: 'emailSubmitSuccess', + email: email + }); + } else { + Logger.error(`Email submission failed: ${res.statusCode} ${data}`); + sidebarProvider.sendMessage({ + command: 'emailSubmitError', + error: 'Failed to submit email. Please try again.' + }); + } + }); + }); + + req.on('error', (error: any) => { + Logger.error(`Email submission request error: ${error}`); + sidebarProvider.sendMessage({ + command: 'emailSubmitError', + error: 'Failed to submit email. Please try again.' + }); + }); + + req.write(postData); + req.end(); + + } catch (error) { + Logger.error(`Email submission error: ${error}`); + sidebarProvider.sendMessage({ + command: 'emailSubmitError', + error: 'Failed to submit email. Please try again.' + }); + } +} + +// Function to initialize Superdesign project structure +async function initializeSuperdesignProject() { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + vscode.window.showErrorMessage('No workspace folder found. Please open a workspace first.'); + return; + } + + const workspaceRoot = workspaceFolder.uri; + const superdesignFolder = vscode.Uri.joinPath(workspaceRoot, '.superdesign'); + + // Detect OS for correct keyboard shortcut + const isWindows = process.platform === 'win32'; + const shortcut = isWindows ? 'Ctrl+Shift+P' : 'Command+Shift+P'; + + const designRuleContent = `When asked to design UI & frontend interface +When asked to design UI & frontend interface +# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design +<neo-brutalism-style> +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</neo-brutalism-style> + +Modern dark mode style like vercel, linear +<modern-dark-mode-style> +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} +</modern-dark-mode-style> + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like <script src="https://unpkg.com/lucide@latest/dist/umd/lucide.min.js"></script> + +## Script +1. When importing tailwind css, just use <script src="https://cdn.tailwindcss.com"></script>, don't load CSS directly as a stylesheet resource like <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet"> +2. When using flowbite, import like <script src="https://cdn.jsdelivr.net/npm/flowbite@2.0.0/dist/flowbite.min.js"></script> + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Output type: Just text +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Output type: Tool call +Think through what are the colors, fonts, spacing, etc. +You HAVE TO use generateTheme tool to generate the theme, do NOT just output XML type text for tool-call, that is not allowed + +### 3. Animation design +Output type: Just text +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Output type: Tool call +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file +You HAVE TO use write tool to generate the html file, do NOT just output XML type text for tool-call, that is not allowed + +<example> +<user>design an AI chat UI</user> + +<assistant> +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? +</assistant> + +<user> +They looks good +</user> + +<assistant> +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='.superdesign/design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + +<user> +I like the vintage style +</user> + +<assistant> +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + +</assistant> + +<user> +This looks great, lets do it +</user> + +<assistant> +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='.superdesign/design_iterations/chat_ui.css', content='...') +write(file_path='.superdesign/design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + +</example> + +IMPORTANT RULES: +1. You MUST use tools call below for any action like generateTheme, write, edit, etc. You are NOT allowed to just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>; MUST USE TOOL CALL (This is very important!!) +2. You MUST confirm the layout, and then theme style, and then animation +3. You MUST use .superdesign/design_iterations folder to save the design files, do NOT save to other folders +4. You MUST create follow the workflow above + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design + +When calling tools, you MUST use the actual tool call, do NOT just output text like 'Called tool: write with arguments: ...' or <tool-call>...</tool-call>, this won't actually call the tool. (This is very important to my life, please follow)`; + + const designRuleMdcContent = `--- +description: Use this rule when asked to do any frontend or UI design +globs: +alwaysApply: false +--- +${designRuleContent}`; + + const defaultCssContent = `/* ======================================== + Dark Mode UI Framework + A beautiful dark mode design system + ======================================== */ + +/* ======================================== + CSS Variables & Theme + ======================================== */ +:root { + /* Dark Mode Color Palette */ + --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); + --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); + + /* Spacing & Layout */ + --radius: 0.625rem; + --spacing-xs: 0.25rem; + --spacing-sm: 0.5rem; + --spacing-md: 0.75rem; + --spacing-lg: 1rem; + --spacing-xl: 1.5rem; + --spacing-2xl: 2rem; + --spacing-3xl: 3rem; + + /* Typography */ + --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; + --font-size-xs: 0.75rem; + --font-size-sm: 0.875rem; + --font-size-base: 1rem; + --font-size-lg: 1.125rem; + --font-size-xl: 1.25rem; + --font-size-2xl: 1.5rem; + --font-size-3xl: 1.875rem; + --font-size-4xl: 2.25rem; +} + +/* ======================================== + Base Styles + ======================================== */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-family); + line-height: 1.6; + min-height: 100vh; +} + +html.dark { + color-scheme: dark; +} + +/* ======================================== + Layout Components + ======================================== */ +.container { + max-width: 64rem; + margin: 0 auto; + padding: var(--spacing-2xl) var(--spacing-lg); +} + +.container-sm { + max-width: 42rem; +} + +.container-lg { + max-width: 80rem; +} + +.grid { + display: grid; +} + +.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); } +.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); } +.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); } +.grid-cols-auto { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); } + +.gap-sm { gap: var(--spacing-sm); } +.gap-md { gap: var(--spacing-md); } +.gap-lg { gap: var(--spacing-lg); } +.gap-xl { gap: var(--spacing-xl); } + +.flex { + display: flex; +} + +.flex-col { + flex-direction: column; +} + +.items-center { + align-items: center; +} + +.justify-center { + justify-content: center; +} + +.justify-between { + justify-content: space-between; +} + +.text-center { + text-align: center; +} + +/* ======================================== + Card Components + ======================================== */ +.card { + background-color: var(--card); + color: var(--card-foreground); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: calc(var(--radius) + 4px); + padding: var(--spacing-xl); + box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1); + transition: all 0.2s ease; +} + +.card:hover { + box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1); +} + +/* ======================================== + Button Components + ======================================== */ +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: var(--spacing-sm); + white-space: nowrap; + border-radius: var(--radius); + font-size: var(--font-size-sm); + font-weight: 500; + transition: all 0.2s; + border: none; + cursor: pointer; + padding: var(--spacing-sm) var(--spacing-lg); + min-height: 2.25rem; + outline: none; + text-decoration: none; +} + +.btn:disabled { + pointer-events: none; + opacity: 0.5; +} + +.btn-primary { + background-color: var(--primary); + color: var(--primary-foreground); +} + +.btn-primary:hover { + background-color: rgba(236, 236, 236, 0.9); +} + +.btn-outline { + background-color: transparent; + border: 1px solid var(--border); + color: var(--foreground); +} + +.btn-outline:hover { + background-color: var(--accent); +} + +.btn-ghost { + background-color: transparent; + color: var(--foreground); +} + +.btn-ghost:hover { + background-color: var(--accent); +} + +.btn-destructive { + background-color: var(--destructive); + color: white; +} + +.btn-destructive:hover { + background-color: rgba(220, 38, 38, 0.9); +} + +/* Button Sizes */ +.btn-sm { + padding: var(--spacing-xs) var(--spacing-md); + font-size: var(--font-size-xs); + min-height: 2rem; +} + +.btn-lg { + padding: var(--spacing-md) var(--spacing-xl); + font-size: var(--font-size-base); + min-height: 2.75rem; +} + +.btn-icon { + padding: var(--spacing-sm); + width: 2.25rem; + height: 2.25rem; +} + +/* ======================================== + Form Components + ======================================== */ +.form-input { + width: 100%; + background: rgba(255, 255, 255, 0.15); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: var(--spacing-sm) var(--spacing-md); + color: var(--foreground); + font-size: var(--font-size-sm); + outline: none; + transition: all 0.2s; +} + +.form-input:focus { + border-color: var(--ring); + box-shadow: 0 0 0 3px rgba(136, 136, 136, 0.5); +} + +.form-input::placeholder { + color: var(--muted-foreground); +} + +/* ======================================== + Badge Components + ======================================== */ +.badge { + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: var(--radius); + border: 1px solid; + padding: 0.125rem var(--spacing-sm); + font-size: var(--font-size-xs); + font-weight: 500; + white-space: nowrap; +} + +/* Priority Badge Variants */ +.badge-priority-high { + background: rgba(127, 29, 29, 0.3); + color: rgb(252, 165, 165); + border: 1px solid rgba(153, 27, 27, 0.5); +} + +.badge-priority-medium { + background: rgba(120, 53, 15, 0.3); + color: rgb(252, 211, 77); + border: 1px solid rgba(146, 64, 14, 0.5); +} + +.badge-priority-low { + background: rgba(20, 83, 45, 0.3); + color: rgb(134, 239, 172); + border: 1px solid rgba(22, 101, 52, 0.5); +} + +/* ======================================== + Tab Components + ======================================== */ +.tab-list { + display: flex; + gap: var(--spacing-sm); + margin-bottom: var(--spacing-xl); +} + +.tab-button { + background-color: transparent; + border: 1px solid rgba(255, 255, 255, 0.2); + color: var(--foreground); + text-transform: capitalize; + font-weight: 500; + transition: all 0.2s ease; + padding: var(--spacing-sm) var(--spacing-md); + border-radius: var(--radius); + cursor: pointer; + font-size: var(--font-size-sm); +} + +.tab-button:hover { + background-color: rgba(255, 255, 255, 0.05); + border-color: rgba(255, 255, 255, 0.3); +} + +.tab-button.active { + background-color: #f8f9fa !important; + color: #1a1a1a !important; + border-color: #f8f9fa !important; + font-weight: 600; +} + +.tab-button.active:hover { + background-color: #e9ecef !important; + border-color: #e9ecef !important; +} + +/* ======================================== + Typography + ======================================== */ +.text-xs { font-size: var(--font-size-xs); } +.text-sm { font-size: var(--font-size-sm); } +.text-base { font-size: var(--font-size-base); } +.text-lg { font-size: var(--font-size-lg); } +.text-xl { font-size: var(--font-size-xl); } +.text-2xl { font-size: var(--font-size-2xl); } +.text-3xl { font-size: var(--font-size-3xl); } +.text-4xl { font-size: var(--font-size-4xl); } + +.font-normal { font-weight: 400; } +.font-medium { font-weight: 500; } +.font-semibold { font-weight: 600; } +.font-bold { font-weight: 700; } + +.text-primary { color: var(--primary); } +.text-muted { color: var(--muted-foreground); } +.text-destructive { color: var(--destructive); } + +.gradient-text { + background: linear-gradient(to right, var(--primary), rgba(236, 236, 236, 0.6)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +/* ======================================== + Icon System + ======================================== */ +.icon { + width: 1rem; + height: 1rem; + fill: currentColor; + flex-shrink: 0; +} + +.icon-sm { width: 0.875rem; height: 0.875rem; } +.icon-lg { width: 1.25rem; height: 1.25rem; } +.icon-xl { width: 1.5rem; height: 1.5rem; } +.icon-2xl { width: 2rem; height: 2rem; } + +/* ======================================== + Interactive Components + ======================================== */ +.checkbox { + width: 1rem; + height: 1rem; + border: 1px solid var(--border); + border-radius: 4px; + cursor: pointer; + position: relative; + background: rgba(255, 255, 255, 0.15); + transition: all 0.2s; +} + +.checkbox:hover { + border-color: var(--ring); +} + +.checkbox.checked { + background-color: rgb(22, 163, 74); + border-color: rgb(22, 163, 74); +} + +.checkbox.checked::after { + content: '✓'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 0.75rem; + font-weight: bold; +} + +/* ======================================== + List Components + ======================================== */ +.list-item { + display: flex; + align-items: center; + gap: var(--spacing-lg); + padding: var(--spacing-lg); + border-bottom: 1px solid rgba(255, 255, 255, 0.05); + transition: background-color 0.2s; +} + +.list-item:hover { + background-color: rgba(255, 255, 255, 0.025); +} + +.list-item:last-child { + border-bottom: none; +} + +.list-item.completed { + opacity: 0.6; +} + +/* ======================================== + Empty State Component + ======================================== */ +.empty-state { + text-align: center; + padding: var(--spacing-3xl) var(--spacing-lg); + color: var(--muted-foreground); +} + +.empty-state .icon { + width: 3rem; + height: 3rem; + margin: 0 auto var(--spacing-lg); + opacity: 0.5; +} + +/* ======================================== + Utility Classes + ======================================== */ +.hidden { display: none; } +.block { display: block; } +.flex { display: flex; } +.inline-flex { display: inline-flex; } + +.w-full { width: 100%; } +.h-full { height: 100%; } +.min-h-screen { min-height: 100vh; } + +.opacity-50 { opacity: 0.5; } +.opacity-60 { opacity: 0.6; } +.opacity-75 { opacity: 0.75; } + +.transition-all { transition: all 0.2s ease; } +.transition-colors { transition: color 0.2s ease, background-color 0.2s ease; } +.transition-opacity { transition: opacity 0.2s ease; } + +/* ======================================== + Responsive Design + ======================================== */ +@media (max-width: 768px) { + .container { + padding: var(--spacing-lg); + } + + .grid-cols-auto { + grid-template-columns: 1fr; + } + + .flex-col-mobile { + flex-direction: column; + } + + .text-center-mobile { + text-align: center; + } + + .gap-sm-mobile { gap: var(--spacing-sm); } + + .hidden-mobile { display: none; } + .block-mobile { display: block; } +} + +@media (max-width: 640px) { + .text-2xl { font-size: var(--font-size-xl); } + .text-3xl { font-size: var(--font-size-2xl); } + .text-4xl { font-size: var(--font-size-3xl); } + + .container { + padding: var(--spacing-lg) var(--spacing-sm); + } +} + +/* ======================================== + Animation Utilities + ======================================== */ +@keyframes fadeIn { + from { opacity: 0; transform: translateY(10px); } + to { opacity: 1; transform: translateY(0); } +} + +.animate-fade-in { + animation: fadeIn 0.3s ease-out; +} + +/* ======================================== + Focus & Accessibility + ======================================== */ +.focus-visible:focus-visible { + outline: 2px solid var(--ring); + outline-offset: 2px; +} + +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +}`; + + try { + // Create .superdesign/design_iterations directory + const designIterationsFolder = vscode.Uri.joinPath(superdesignFolder, 'design_iterations'); + await vscode.workspace.fs.createDirectory(designIterationsFolder); + + // Create default_ui_darkmode.css file + const defaultCssPath = vscode.Uri.joinPath(designIterationsFolder, 'default_ui_darkmode.css'); + try { + // Check if file already exists + await vscode.workspace.fs.stat(defaultCssPath); + Logger.debug('default_ui_darkmode.css already exists, skipping creation'); + } catch { + // File doesn't exist, create it + await vscode.workspace.fs.writeFile(defaultCssPath, Buffer.from(defaultCssContent, 'utf8')); + Logger.info('Created default_ui_darkmode.css file'); + } + + // Create .cursor/rules directory if it doesn't exist + const cursorRulesFolder = vscode.Uri.joinPath(workspaceRoot, '.cursor', 'rules'); + try { + await vscode.workspace.fs.stat(cursorRulesFolder); + } catch { + await vscode.workspace.fs.createDirectory(cursorRulesFolder); + } + + // Create or append to design.mdc + const designMdcPath = vscode.Uri.joinPath(cursorRulesFolder, 'design.mdc'); + try { + const existingContent = await vscode.workspace.fs.readFile(designMdcPath); + const currentContent = Buffer.from(existingContent).toString('utf8'); + if (!currentContent.includes('superdesign: Open Canvas View')) { + const updatedContent = currentContent + '\n\n' + designRuleMdcContent; + await vscode.workspace.fs.writeFile(designMdcPath, Buffer.from(updatedContent, 'utf8')); + } + } catch { + // File doesn't exist, create it + await vscode.workspace.fs.writeFile(designMdcPath, Buffer.from(designRuleMdcContent, 'utf8')); + } + + // Create or append to CLAUDE.md + const claudeMdPath = vscode.Uri.joinPath(workspaceRoot, 'CLAUDE.md'); + try { + const existingContent = await vscode.workspace.fs.readFile(claudeMdPath); + const currentContent = Buffer.from(existingContent).toString('utf8'); + if (!currentContent.includes('superdesign: Open Canvas View')) { + const updatedContent = currentContent + '\n\n' + designRuleContent; + await vscode.workspace.fs.writeFile(claudeMdPath, Buffer.from(updatedContent, 'utf8')); + } + } catch { + // File doesn't exist, create it + await vscode.workspace.fs.writeFile(claudeMdPath, Buffer.from(designRuleContent, 'utf8')); + } + + // Create or append to .windsurfrules + const windsurfRulesPath = vscode.Uri.joinPath(workspaceRoot, '.windsurfrules'); + try { + const existingContent = await vscode.workspace.fs.readFile(windsurfRulesPath); + const currentContent = Buffer.from(existingContent).toString('utf8'); + if (!currentContent.includes('superdesign: Open Canvas View')) { + const updatedContent = currentContent + '\n\n' + designRuleContent; + await vscode.workspace.fs.writeFile(windsurfRulesPath, Buffer.from(updatedContent, 'utf8')); + } + } catch { + // File doesn't exist, create it + await vscode.workspace.fs.writeFile(windsurfRulesPath, Buffer.from(designRuleContent, 'utf8')); + } + + vscode.window.showInformationMessage('✅ Superdesign project initialized successfully! Created .superdesign folder and design rules for Cursor, Claude, and Windsurf.'); + + } catch (error) { + vscode.window.showErrorMessage(`Failed to initialize Superdesign project: ${error}`); + } +} + +export function activate(context: vscode.ExtensionContext) { + // Initialize the centralized logger + Logger.initialize(); + Logger.info('Superdesign extension is now active!'); + // Note: Users can manually open output via View → Output → Select "Superdesign" if needed + + // Initialize Custom Agent service + Logger.info('Creating CustomAgentService...'); + const customAgent = new CustomAgentService(Logger.getOutputChannel()); + Logger.info('CustomAgentService created'); + + // The command has been defined in the package.json file + // Now provide the implementation of the command with registerCommand + // The commandId parameter must match the command field in package.json + const helloWorldDisposable = vscode.commands.registerCommand('superdesign.helloWorld', () => { + // The code you place here will be executed every time your command is executed + // Display a message box to the user + vscode.window.showInformationMessage('Hello World from superdesign!'); + }); + + // Register API key configuration commands + const configureApiKeyDisposable = vscode.commands.registerCommand('superdesign.configureApiKey', async () => { + await configureAnthropicApiKey(); + }); + + const configureOpenAIApiKeyDisposable = vscode.commands.registerCommand('superdesign.configureOpenAIApiKey', async () => { + await configureOpenAIApiKey(); + }); + + const configureOpenRouterApiKeyDisposable = vscode.commands.registerCommand('superdesign.configureOpenRouterApiKey', async () => { + await configureOpenRouterApiKey(); + }); + + + // Create the chat sidebar provider + const sidebarProvider = new ChatSidebarProvider(context.extensionUri, customAgent, Logger.getOutputChannel()); + + // Register the webview view provider for sidebar + const sidebarDisposable = vscode.window.registerWebviewViewProvider( + ChatSidebarProvider.VIEW_TYPE, + sidebarProvider, + { + webviewOptions: { + retainContextWhenHidden: true + } + } + ); + + // Register command to show sidebar + const showSidebarDisposable = vscode.commands.registerCommand('superdesign.showChatSidebar', () => { + vscode.commands.executeCommand('workbench.view.extension.superdesign-sidebar'); + }); + + // Register canvas command + const openCanvasDisposable = vscode.commands.registerCommand('superdesign.openCanvas', () => { + SuperdesignCanvasPanel.createOrShow(context.extensionUri, sidebarProvider); + }); + + // Register clear chat command + const clearChatDisposable = vscode.commands.registerCommand('superdesign.clearChat', () => { + sidebarProvider.sendMessage({ + command: 'clearChat' + }); + }); + + // Register reset welcome command + const resetWelcomeDisposable = vscode.commands.registerCommand('superdesign.resetWelcome', () => { + sidebarProvider.sendMessage({ + command: 'resetWelcome' + }); + vscode.window.showInformationMessage('Welcome screen has been reset. Refresh the sidebar to see the welcome screen again.'); + }); + + // Register initialize project command + const initializeProjectDisposable = vscode.commands.registerCommand('superdesign.initializeProject', async () => { + await initializeSuperdesignProject(); + }); + + // Register open settings command + const openSettingsDisposable = vscode.commands.registerCommand('superdesign.openSettings', () => { + vscode.commands.executeCommand('workbench.action.openSettings', '@ext:iganbold.superdesign'); + }); + + // Register configure API key command (alternative to the existing one) + const configureApiKeyQuickDisposable = vscode.commands.registerCommand('superdesign.configureApiKeyQuick', async () => { + await configureAnthropicApiKey(); + }); + + // Set up message handler for auto-canvas functionality + sidebarProvider.setMessageHandler((message) => { + switch (message.command) { + case 'checkCanvasStatus': + // Check if canvas panel is currently open + const isCanvasOpen = SuperdesignCanvasPanel.currentPanel !== undefined; + sidebarProvider.sendMessage({ + command: 'canvasStatusResponse', + isOpen: isCanvasOpen + }); + break; + + case 'autoOpenCanvas': + // Auto-open canvas if not already open + SuperdesignCanvasPanel.createOrShow(context.extensionUri, sidebarProvider); + break; + + case 'setContextFromCanvas': + // Forward context from canvas to chat sidebar + sidebarProvider.sendMessage({ + command: 'contextFromCanvas', + data: message.data + }); + break; + + case 'saveImageToMoodboard': + // Save uploaded image to moodboard directory + saveImageToMoodboard(message.data, sidebarProvider); + break; + + case 'getBase64Image': + // Convert saved image to base64 for AI SDK + getBase64Image(message.filePath, sidebarProvider); + break; + + case 'getCssFileContent': + // Read CSS file content for theme preview + getCssFileContent(message.filePath, sidebarProvider); + break; + + case 'showError': + // Show error message to user + vscode.window.showErrorMessage(message.data); + break; + + case 'submitEmail': + // Handle email submission from welcome screen + submitEmailToSupabase(message.email, sidebarProvider); + break; + + case 'initializeSuperdesign': + // Auto-trigger initialize Superdesign command + console.log('🚀 Received initializeSuperdesign command from webview'); + vscode.commands.executeCommand('superdesign.initializeProject'); + break; + } + }); + + context.subscriptions.push( + helloWorldDisposable, + configureApiKeyDisposable, + configureOpenAIApiKeyDisposable, + configureOpenRouterApiKeyDisposable, + sidebarDisposable, + showSidebarDisposable, + openCanvasDisposable, + clearChatDisposable, + resetWelcomeDisposable, + initializeProjectDisposable, + openSettingsDisposable, + configureApiKeyQuickDisposable + ); +} + +// Function to configure Anthropic API key +async function configureAnthropicApiKey() { + const currentKey = vscode.workspace.getConfiguration('superdesign').get<string>('anthropicApiKey'); + + const input = await vscode.window.showInputBox({ + title: 'Configure Anthropic API Key', + prompt: 'Enter your Anthropic API key (get one from https://console.anthropic.com/)', + value: currentKey ? '••••••••••••••••' : '', + password: true, + placeHolder: 'sk-ant-...', + validateInput: (value) => { + if (!value || value.trim().length === 0) { + return 'API key cannot be empty'; + } + if (value === '••••••••••••••••') { + return null; // User didn't change the masked value, that's OK + } + if (!value.startsWith('sk-ant-')) { + return 'Anthropic API keys should start with "sk-ant-"'; + } + return null; + } + }); + + if (input !== undefined) { + // Only update if user didn't just keep the masked value + if (input !== '••••••••••••••••') { + try { + await vscode.workspace.getConfiguration('superdesign').update( + 'anthropicApiKey', + input.trim(), + vscode.ConfigurationTarget.Global + ); + vscode.window.showInformationMessage('✅ Anthropic API key configured successfully!'); + } catch (error) { + vscode.window.showErrorMessage(`Failed to save API key: ${error}`); + } + } else if (currentKey) { + vscode.window.showInformationMessage('API key unchanged (already configured)'); + } else { + vscode.window.showWarningMessage('No API key was set'); + } + } +} + +// Function to configure OpenAI API key +async function configureOpenAIApiKey() { + const currentKey = vscode.workspace.getConfiguration('superdesign').get<string>('openaiApiKey'); + + const input = await vscode.window.showInputBox({ + title: 'Configure OpenAI API Key', + prompt: 'Enter your OpenAI API key (get one from https://platform.openai.com/api-keys)', + value: currentKey ? '••••••••••••••••' : '', + password: true, + placeHolder: 'sk-...', + validateInput: (value) => { + if (!value || value.trim().length === 0) { + return 'API key cannot be empty'; + } + if (value === '••••••••••••••••') { + return null; // User didn't change the masked value, that's OK + } + if (!value.startsWith('sk-')) { + return 'OpenAI API keys should start with "sk-"'; + } + return null; + } + }); + + if (input !== undefined) { + // Only update if user didn't just keep the masked value + if (input !== '••••••••••••••••') { + try { + await vscode.workspace.getConfiguration('superdesign').update( + 'openaiApiKey', + input.trim(), + vscode.ConfigurationTarget.Global + ); + vscode.window.showInformationMessage('✅ OpenAI API key configured successfully!'); + } catch (error) { + vscode.window.showErrorMessage(`Failed to save API key: ${error}`); + } + } else if (currentKey) { + vscode.window.showInformationMessage('API key unchanged (already configured)'); + } else { + vscode.window.showWarningMessage('No API key was set'); + } + } +} + +// Function to configure OpenRouter API key +async function configureOpenRouterApiKey() { + const currentKey = vscode.workspace.getConfiguration('superdesign').get<string>('openrouterApiKey'); + + const input = await vscode.window.showInputBox({ + title: 'Configure OpenRouter API Key', + prompt: 'Enter your OpenRouter API key (get one from https://openrouter.ai/)', + value: currentKey ? '••••••••••••••••' : '', + password: true, + placeHolder: 'sk-...', + validateInput: (value) => { + if (!value || value.trim().length === 0) { + return 'API key cannot be empty'; + } + if (value === '••••••••••••••••') { + return null; // User didn't change the masked value, that's OK + } + if (!value.startsWith('sk-')) { + return 'OpenRouter API keys should start with "sk-"'; + } + return null; + } + }); + + if (input !== undefined) { + // Only update if user didn't just keep the masked value + if (input !== '••••••••••••••••') { + try { + await vscode.workspace.getConfiguration('superdesign').update( + 'openrouterApiKey', + input.trim(), + vscode.ConfigurationTarget.Global + ); + vscode.window.showInformationMessage('✅ OpenRouter API key configured successfully!'); + } catch (error) { + vscode.window.showErrorMessage(`Failed to save API key: ${error}`); + } + } else if (currentKey) { + vscode.window.showInformationMessage('API key unchanged (already configured)'); + } else { + vscode.window.showWarningMessage('No API key was set'); + } + } +} + +class SuperdesignCanvasPanel { + public static currentPanel: SuperdesignCanvasPanel | undefined; + public static readonly viewType = 'superdesignCanvasPanel'; + + private readonly _panel: vscode.WebviewPanel; + private readonly _extensionUri: vscode.Uri; + private readonly _sidebarProvider: ChatSidebarProvider; + private _disposables: vscode.Disposable[] = []; + private _fileWatcher: vscode.FileSystemWatcher | undefined; + + public static createOrShow(extensionUri: vscode.Uri, sidebarProvider: ChatSidebarProvider) { + const column = vscode.window.activeTextEditor?.viewColumn; + + if (SuperdesignCanvasPanel.currentPanel) { + SuperdesignCanvasPanel.currentPanel._panel.reveal(column); + return; + } + + const panel = vscode.window.createWebviewPanel( + SuperdesignCanvasPanel.viewType, + 'Superdesign Canvas', + column || vscode.ViewColumn.One, + { + enableScripts: true, + localResourceRoots: [ + vscode.Uri.joinPath(extensionUri, 'dist'), + vscode.Uri.joinPath(extensionUri, 'src', 'assets') + ] + } + ); + + SuperdesignCanvasPanel.currentPanel = new SuperdesignCanvasPanel(panel, extensionUri, sidebarProvider); + } + + private constructor(panel: vscode.WebviewPanel, extensionUri: vscode.Uri, sidebarProvider: ChatSidebarProvider) { + this._panel = panel; + this._extensionUri = extensionUri; + this._sidebarProvider = sidebarProvider; + + this._update(); + this._panel.onDidDispose(() => this.dispose(), null, this._disposables); + this._setupFileWatcher(); + + // Handle messages from the webview + this._panel.webview.onDidReceiveMessage( + message => { + switch (message.command) { + case 'loadDesignFiles': + this._loadDesignFiles(); + break; + case 'selectFrame': + Logger.debug(`Frame selected: ${message.data?.fileName}`); + break; + case 'setContextFromCanvas': + // Forward context to chat sidebar + this._sidebarProvider.sendMessage({ + command: 'contextFromCanvas', + data: message.data + }); + break; + case 'setChatPrompt': + // Forward prompt to chat sidebar + this._sidebarProvider.sendMessage({ + command: 'setChatPrompt', + data: message.data + }); + break; + } + }, + null, + this._disposables + ); + } + + public dispose() { + SuperdesignCanvasPanel.currentPanel = undefined; + + // Dispose of file watcher + if (this._fileWatcher) { + this._fileWatcher.dispose(); + this._fileWatcher = undefined; + } + + this._panel.dispose(); + while (this._disposables.length) { + const x = this._disposables.pop(); + if (x) { + x.dispose(); + } + } + } + + private _setupFileWatcher() { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + return; + } + + // Watch for changes in .superdesign/design_iterations/*.html, *.svg, and *.css + const pattern = new vscode.RelativePattern( + workspaceFolder, + '.superdesign/design_iterations/**/*.{html,svg,css}' + ); + + this._fileWatcher = vscode.workspace.createFileSystemWatcher( + pattern, + false, // Don't ignore create events + false, // Don't ignore change events + false // Don't ignore delete events + ); + + // Handle file creation + this._fileWatcher.onDidCreate((uri) => { + Logger.debug(`Design file created: ${uri.fsPath}`); + this._panel.webview.postMessage({ + command: 'fileChanged', + data: { + fileName: uri.fsPath.split('/').pop() || '', + changeType: 'created' + } + }); + }); + + // Handle file modification + this._fileWatcher.onDidChange((uri) => { + Logger.debug(`Design file modified: ${uri.fsPath}`); + this._panel.webview.postMessage({ + command: 'fileChanged', + data: { + fileName: uri.fsPath.split('/').pop() || '', + changeType: 'modified' + } + }); + }); + + // Handle file deletion + this._fileWatcher.onDidDelete((uri) => { + Logger.debug(`Design file deleted: ${uri.fsPath}`); + this._panel.webview.postMessage({ + command: 'fileChanged', + data: { + fileName: uri.fsPath.split('/').pop() || '', + changeType: 'deleted' + } + }); + }); + } + + private _update() { + const webview = this._panel.webview; + this._panel.webview.html = this._getHtmlForWebview(webview); + } + + private _getHtmlForWebview(webview: vscode.Webview) { + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(this._extensionUri, 'dist', 'webview.js') + ); + + // Generate webview URIs for logo images + const logoUris = { + cursor: webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'src', 'assets', 'cursor_logo.png')).toString(), + windsurf: webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'src', 'assets', 'windsurf_logo.png')).toString(), + claudeCode: webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'src', 'assets', 'claude_code_logo.png')).toString(), + lovable: webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'src', 'assets', 'lovable_logo.png')).toString(), + bolt: webview.asWebviewUri(vscode.Uri.joinPath(this._extensionUri, 'src', 'assets', 'bolt_logo.jpg')).toString(), + }; + + // Debug logging + Logger.debug(`Canvas Panel - Extension URI: ${this._extensionUri.toString()}`); + Logger.debug(`Canvas Panel - Generated logo URIs: ${JSON.stringify(logoUris)}`); + + const nonce = getNonce(); + + return `<!DOCTYPE html> + <html lang="en"> + <head> + <meta charset="UTF-8"> + <meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src ${webview.cspSource} 'unsafe-inline'; img-src ${webview.cspSource} data: https: vscode-webview:; script-src 'nonce-${nonce}'; frame-src ${webview.cspSource};"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>Superdesign Canvas + + +
+ + + + `; + } + + private async _loadDesignFiles() { + const workspaceFolder = vscode.workspace.workspaceFolders?.[0]; + if (!workspaceFolder) { + this._panel.webview.postMessage({ + command: 'error', + data: { error: 'No workspace folder found. Please open a workspace first.' } + }); + return; + } + + try { + const designFolder = vscode.Uri.joinPath(workspaceFolder.uri, '.superdesign', 'design_iterations'); + + // Check if the design_files folder exists + try { + await vscode.workspace.fs.stat(designFolder); + } catch (error) { + // Folder doesn't exist, create it + try { + await vscode.workspace.fs.createDirectory(designFolder); + Logger.info('Created .superdesign/design_iterations directory'); + } catch (createError) { + this._panel.webview.postMessage({ + command: 'error', + data: { error: `Failed to create design_files directory: ${createError}` } + }); + return; + } + } + + // Read all files in the directory + const files = await vscode.workspace.fs.readDirectory(designFolder); + const designFiles = files.filter(([name, type]) => + type === vscode.FileType.File && ( + name.toLowerCase().endsWith('.html') || + name.toLowerCase().endsWith('.svg') + ) + ); + + const loadedFiles = await Promise.all( + designFiles.map(async ([fileName, _]) => { + const filePath = vscode.Uri.joinPath(designFolder, fileName); + + try { + // Read file stats and content + const [stat, content] = await Promise.all([ + vscode.workspace.fs.stat(filePath), + vscode.workspace.fs.readFile(filePath) + ]); + + const fileType = fileName.toLowerCase().endsWith('.svg') ? 'svg' : 'html'; + let htmlContent = Buffer.from(content).toString('utf8'); + + // For HTML files, inline any external CSS files + if (fileType === 'html') { + htmlContent = await this._inlineExternalCSS(htmlContent, designFolder); + } + + return { + name: fileName, + path: filePath.fsPath, + content: htmlContent, + size: stat.size, + modified: new Date(stat.mtime), + fileType + }; + } catch (fileError) { + Logger.error(`Failed to read file ${fileName}: ${fileError}`); + return null; + } + }) + ); + + // Filter out any failed file reads + const validFiles = loadedFiles.filter(file => file !== null); + + Logger.info(`Loaded ${validFiles.length} design files (HTML & SVG)`); + + this._panel.webview.postMessage({ + command: 'designFilesLoaded', + data: { files: validFiles } + }); + + } catch (error) { + Logger.error(`Error loading design files: ${error}`); + this._panel.webview.postMessage({ + command: 'error', + data: { error: `Failed to load design files: ${error}` } + }); + } + } + + private async _inlineExternalCSS(htmlContent: string, designFolder: vscode.Uri): Promise { + // Match link tags that reference CSS files + const linkRegex = /]*rel=["']stylesheet["'][^>]*href=["']([^"']+)["'][^>]*>/gi; + let modifiedContent = htmlContent; + const matches = Array.from(htmlContent.matchAll(linkRegex)); + + for (const match of matches) { + const fullLinkTag = match[0]; + const cssFileName = match[1]; + + try { + // Only process relative paths (not absolute URLs) + if (!cssFileName.startsWith('http') && !cssFileName.startsWith('//')) { + const cssFilePath = vscode.Uri.joinPath(designFolder, cssFileName); + + // Check if CSS file exists + try { + const cssContent = await vscode.workspace.fs.readFile(cssFilePath); + const cssText = Buffer.from(cssContent).toString('utf8'); + + // Replace the link tag with a style tag containing the CSS content + const styleTag = ``; + modifiedContent = modifiedContent.replace(fullLinkTag, styleTag); + + Logger.debug(`Inlined CSS file: ${cssFileName}`); + } catch (cssError) { + Logger.warn(`Could not read CSS file ${cssFileName}: ${cssError}`); + // Leave the original link tag in place if CSS file can't be read + } + } + } catch (error) { + Logger.warn(`Error processing CSS link ${cssFileName}: ${error}`); + } + } + + return modifiedContent; + } +} + +function getNonce() { + let text = ''; + const possible = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'; + for (let i = 0; i < 32; i++) { + text += possible.charAt(Math.floor(Math.random() * possible.length)); + } + return text; +} + +// This method is called when your extension is deactivated +export function deactivate() { + Logger.dispose(); +} + diff --git a/src/providers/chatSidebarProvider.ts b/src/providers/chatSidebarProvider.ts new file mode 100644 index 0000000..6614939 --- /dev/null +++ b/src/providers/chatSidebarProvider.ts @@ -0,0 +1,345 @@ +import * as vscode from 'vscode'; +import { ClaudeCodeService } from '../services/claudeCodeService'; +import { ChatMessageService } from '../services/chatMessageService'; +import { generateWebviewHtml } from '../templates/webviewTemplate'; +import { WebviewContext } from '../types/context'; +import { AgentService } from '../types/agent'; + +export class ChatSidebarProvider implements vscode.WebviewViewProvider { + public static readonly VIEW_TYPE = 'superdesign.chatView'; + private _view?: vscode.WebviewView; + private messageHandler: ChatMessageService; + private customMessageHandler?: (message: any) => void; + + constructor( + private readonly _extensionUri: vscode.Uri, + private readonly agentService: AgentService, + private readonly outputChannel: vscode.OutputChannel + ) { + this.messageHandler = new ChatMessageService(agentService, outputChannel); + } + + public setMessageHandler(handler: (message: any) => void) { + this.customMessageHandler = handler; + } + + public sendMessage(message: any) { + if (this._view) { + this._view.webview.postMessage(message); + } + } + + public resolveWebviewView( + webviewView: vscode.WebviewView, + _context: vscode.WebviewViewResolveContext, + _token: vscode.CancellationToken, + ) { + this._view = webviewView; + + webviewView.webview.options = { + enableScripts: true, + localResourceRoots: [ + vscode.Uri.joinPath(this._extensionUri, 'dist'), + vscode.Uri.joinPath(this._extensionUri, 'src', 'assets') + ] + }; + + const webviewContext: WebviewContext = { + layout: 'sidebar', + extensionUri: this._extensionUri.toString() + }; + + webviewView.webview.html = generateWebviewHtml( + webviewView.webview, + this._extensionUri, + webviewContext + ); + + // Handle messages from the webview + webviewView.webview.onDidReceiveMessage( + async (message) => { + // First try custom message handler for auto-canvas functionality + if (this.customMessageHandler) { + this.customMessageHandler(message); + } + + // Then handle regular chat messages + switch (message.command) { + case 'chatMessage': + await this.messageHandler.handleChatMessage(message, webviewView.webview); + break; + case 'stopChat': + await this.messageHandler.stopCurrentChat(webviewView.webview); + break; + case 'executeAction': + // Execute command from error action buttons + console.log('Executing action:', message.actionCommand, message.actionArgs); + if (message.actionArgs) { + await vscode.commands.executeCommand(message.actionCommand, message.actionArgs); + } else { + await vscode.commands.executeCommand(message.actionCommand); + } + break; + case 'getBase64Image': + // Forward to extension for image conversion + // This will be handled by extension.ts + break; + case 'getCurrentProvider': + await this.handleGetCurrentProvider(webviewView.webview); + break; + case 'changeProvider': + await this.handleChangeProvider(message.model, webviewView.webview); + break; + } + } + ); + } + + private async handleGetCurrentProvider(webview: vscode.Webview) { + const config = vscode.workspace.getConfiguration('superdesign'); + const currentProvider = config.get('aiModelProvider', 'anthropic'); + const currentModel = config.get('aiModel'); + + // If no specific model is set, use defaults + let defaultModel: string; + switch (currentProvider) { + case 'openai': + defaultModel = 'gpt-4o'; + break; + case 'openrouter': + defaultModel = 'anthropic/claude-3-7-sonnet-20250219'; + break; + case 'anthropic': + default: + defaultModel = 'claude-3-5-sonnet-20241022'; + break; + } + + webview.postMessage({ + command: 'currentProviderResponse', + provider: currentProvider, + model: currentModel || defaultModel + }); + } + + private async handleChangeProvider(model: string, webview: vscode.Webview) { + try { + const config = vscode.workspace.getConfiguration('superdesign'); + + // Determine provider and API key based on model + let provider: string; + let apiKeyKey: string; + let configureCommand: string; + let displayName: string; + + if (model === 'openai-auto') { + provider = 'openai'; + apiKeyKey = 'openaiApiKey'; + configureCommand = 'superdesign.configureOpenAIApiKey'; + displayName = 'OpenAI (AUTO)'; + } else if (model.includes('/')) { + // OpenRouter model (contains slash like "openai/gpt-4o") + provider = 'openrouter'; + apiKeyKey = 'openrouterApiKey'; + configureCommand = 'superdesign.configureOpenRouterApiKey'; + displayName = `OpenRouter (${this.getModelDisplayName(model)})`; + } else if (model.startsWith('claude-')) { + provider = 'anthropic'; + apiKeyKey = 'anthropicApiKey'; + configureCommand = 'superdesign.configureApiKey'; + displayName = `Anthropic (${this.getModelDisplayName(model)})`; + } else { + provider = 'openai'; + apiKeyKey = 'openaiApiKey'; + configureCommand = 'superdesign.configureOpenAIApiKey'; + displayName = `OpenAI (${this.getModelDisplayName(model)})`; + } + + // Update both provider and specific model + await config.update('aiModelProvider', provider, vscode.ConfigurationTarget.Global); + await config.update('aiModel', model, vscode.ConfigurationTarget.Global); + + // Check if the API key is configured for the selected provider + const apiKey = config.get(apiKeyKey); + + if (!apiKey) { + const result = await vscode.window.showWarningMessage( + `${displayName} selected, but API key is not configured. Would you like to configure it now?`, + 'Configure API Key', + 'Later' + ); + + if (result === 'Configure API Key') { + await vscode.commands.executeCommand(configureCommand); + } + } + + // Notify webview of successful change + webview.postMessage({ + command: 'providerChanged', + provider: provider, + model: model + }); + + } catch (error) { + vscode.window.showErrorMessage(`Failed to update AI model: ${error}`); + } + } + + private getModelDisplayName(model: string): string { + const modelNames: { [key: string]: string } = { + // OpenAI models + 'gpt-4.1': 'GPT-4.1', + 'gpt-4.1-mini': 'GPT-4.1 Mini', + 'gpt-4.1-nano': 'GPT-4.1 Nano', + 'gpt-4o': 'GPT-4o', + 'gpt-4o-mini': 'GPT-4o Mini', + // Anthropic models + 'claude-4-opus-20250514': 'Claude 4 Opus', + 'claude-4-sonnet-20250514': 'Claude 4 Sonnet', + 'claude-3-7-sonnet-20250219': 'Claude 3.7 Sonnet', + 'claude-3-5-sonnet-20241022': 'Claude 3.5 Sonnet', + 'claude-3-opus-20240229': 'Claude 3 Opus', + 'claude-3-sonnet-20240229': 'Claude 3 Sonnet', + 'claude-3-haiku-20240307': 'Claude 3 Haiku', + // OpenRouter - Google models + 'google/gemini-2.5-pro': 'Gemini 2.5 Pro', + 'google/gemini-2.5-flash': 'Gemini 2.5 Flash', + 'google/gemini-2.5-pro-preview-06-05': 'Gemini 2.5 Pro Preview', + 'google/gemini-2.5-flash-preview-05-20': 'Gemini 2.5 Flash Preview', + 'google/gemini-2.5-pro-preview-03-25': 'Gemini 2.5 Pro Preview (Mar)', + 'google/gemini-2.0-flash-001': 'Gemini 2.0 Flash', + 'google/gemini-2.0-flash-exp': 'Gemini 2.0 Flash Exp', + 'google/gemini-2.0-flash-lite-001': 'Gemini 2.0 Flash Lite', + 'google/gemma-3-27b-it': 'Gemma 3 27B', + 'google/gemma-3-12b-it': 'Gemma 3 12B', + 'google/gemma-3-4b-it': 'Gemma 3 4B', + 'google/gemma-2-27b-it': 'Gemma 2 27B', + 'google/gemma-2-9b-it': 'Gemma 2 9B', + 'google/gemini-flash-1.5': 'Gemini Flash 1.5', + 'google/gemini-flash-1.5-8b': 'Gemini Flash 1.5 8B', + 'google/gemini-pro-1.5': 'Gemini Pro 1.5', + // OpenRouter - Meta models + 'meta-llama/llama-4-maverick-17b-128e-instruct': 'Llama 4 Maverick 17B', + 'meta-llama/llama-4-scout-17b-16e-instruct': 'Llama 4 Scout 17B', + 'meta-llama/llama-3.3-70b-instruct': 'Llama 3.3 70B', + 'meta-llama/llama-3.2-90b-vision-instruct': 'Llama 3.2 90B Vision', + 'meta-llama/llama-3.2-11b-vision-instruct': 'Llama 3.2 11B Vision', + 'meta-llama/llama-3.2-3b-instruct': 'Llama 3.2 3B', + 'meta-llama/llama-3.2-1b-instruct': 'Llama 3.2 1B', + 'meta-llama/llama-3.1-405b-instruct': 'Llama 3.1 405B', + 'meta-llama/llama-3.1-70b-instruct': 'Llama 3.1 70B', + 'meta-llama/llama-3.1-8b-instruct': 'Llama 3.1 8B', + 'meta-llama/llama-3-70b-instruct': 'Llama 3 70B', + 'meta-llama/llama-3-8b-instruct': 'Llama 3 8B', + 'meta-llama/llama-guard-4-12b': 'Llama Guard 4 12B', + 'meta-llama/llama-guard-3-8b': 'Llama Guard 3 8B', + 'meta-llama/llama-guard-2-8b': 'Llama Guard 2 8B', + // OpenRouter - DeepSeek models + 'deepseek/deepseek-r1': 'DeepSeek R1', + 'deepseek/deepseek-r1-0528': 'DeepSeek R1 0528', + 'deepseek/deepseek-r1-distill-llama-70b': 'DeepSeek R1 Distill Llama 70B', + 'deepseek/deepseek-r1-distill-llama-8b': 'DeepSeek R1 Distill Llama 8B', + 'deepseek/deepseek-r1-distill-qwen-32b': 'DeepSeek R1 Distill Qwen 32B', + 'deepseek/deepseek-r1-distill-qwen-14b': 'DeepSeek R1 Distill Qwen 14B', + 'deepseek/deepseek-r1-distill-qwen-7b': 'DeepSeek R1 Distill Qwen 7B', + 'deepseek/deepseek-r1-distill-qwen-1.5b': 'DeepSeek R1 Distill Qwen 1.5B', + 'deepseek/deepseek-chat-v3': 'DeepSeek Chat V3', + 'deepseek/deepseek-v3-base': 'DeepSeek V3 Base', + 'deepseek/deepseek-prover-v2': 'DeepSeek Prover V2', + // OpenRouter - Mistral models + 'mistralai/mistral-small-3.2-24b-instruct-2506': 'Mistral Small 3.2 24B', + 'mistralai/magistral-small-2506': 'Magistral Small', + 'mistralai/magistral-medium-2506': 'Magistral Medium', + 'mistralai/devstral-small-2505': 'Devstral Small', + 'mistralai/mistral-medium-3': 'Mistral Medium 3', + 'mistralai/mistral-small-3.1-24b-instruct-2503': 'Mistral Small 3.1 24B', + 'mistralai/mistral-saba-2502': 'Mistral Saba', + 'mistralai/mistral-small-24b-instruct-2501': 'Mistral Small 24B', + 'mistralai/codestral-2501': 'Codestral', + 'mistralai/mistral-large-2411': 'Mistral Large 2411', + 'mistralai/mistral-large-2407': 'Mistral Large 2407', + 'mistralai/pixtral-large-2411': 'Pixtral Large', + 'mistralai/pixtral-12b': 'Pixtral 12B', + 'mistralai/ministral-8b': 'Ministral 8B', + 'mistralai/ministral-3b': 'Ministral 3B', + 'mistralai/mistral-nemo': 'Mistral Nemo', + 'mistralai/mistral-large': 'Mistral Large', + 'mistralai/mixtral-8x22b-instruct': 'Mixtral 8x22B', + 'mistralai/mixtral-8x7b-instruct': 'Mixtral 8x7B', + 'mistralai/mistral-7b-instruct': 'Mistral 7B', + // OpenRouter - xAI models + 'x-ai/grok-3': 'Grok 3', + 'x-ai/grok-3-mini': 'Grok 3 Mini', + 'x-ai/grok-3-beta': 'Grok 3 Beta', + 'x-ai/grok-3-mini-beta': 'Grok 3 Mini Beta', + 'x-ai/grok-2-vision-1212': 'Grok 2 Vision', + 'x-ai/grok-2-1212': 'Grok 2', + 'x-ai/grok-vision-beta': 'Grok Vision Beta', + // OpenRouter - Qwen models + 'qwen/qwen3-235b-a22b-04-28': 'Qwen3 235B', + 'qwen/qwen3-32b-04-28': 'Qwen3 32B', + 'qwen/qwen3-30b-a3b-04-28': 'Qwen3 30B', + 'qwen/qwen3-14b-04-28': 'Qwen3 14B', + 'qwen/qwen3-8b-04-28': 'Qwen3 8B', + 'qwen/qwen2.5-vl-72b-instruct': 'Qwen2.5 VL 72B', + 'qwen/qwen2.5-vl-32b-instruct': 'Qwen2.5 VL 32B', + 'qwen/qwen-2.5-coder-32b-instruct': 'Qwen 2.5 Coder 32B', + 'qwen/qwen-2.5-72b-instruct': 'Qwen 2.5 72B', + 'qwen/qwen-2.5-7b-instruct': 'Qwen 2.5 7B', + 'qwen/qwen-2-72b-instruct': 'Qwen 2 72B', + 'qwen/qwen-2-vl-7b-instruct': 'Qwen 2 VL 7B', + 'qwen/qwq-32b': 'QwQ 32B', + 'qwen/qwq-32b-preview': 'QwQ 32B Preview', + 'qwen/qwen-vl-max-2025-01-25': 'Qwen VL Max', + 'qwen/qwen-vl-plus': 'Qwen VL Plus', + 'qwen/qwen-max-2025-01-25': 'Qwen Max', + 'qwen/qwen-plus-2025-01-25': 'Qwen Plus', + 'qwen/qwen-turbo-2024-11-01': 'Qwen Turbo', + // OpenRouter - Perplexity models + 'perplexity/sonar-reasoning-pro': 'Sonar Reasoning Pro', + 'perplexity/sonar-pro': 'Sonar Pro', + 'perplexity/sonar-deep-research': 'Sonar Deep Research', + 'perplexity/sonar-reasoning': 'Sonar Reasoning', + 'perplexity/sonar': 'Sonar', + 'perplexity/r1-1776': 'R1-1776', + 'perplexity/llama-3.1-sonar-large-128k-online': 'Llama 3.1 Sonar Large Online', + 'perplexity/llama-3.1-sonar-small-128k-online': 'Llama 3.1 Sonar Small Online', + // OpenRouter - Microsoft models + 'microsoft/phi-4-reasoning-plus-04-30': 'Phi-4 Reasoning Plus', + 'microsoft/mai-ds-r1': 'MAI-DS-R1', + 'microsoft/phi-4-multimodal-instruct': 'Phi-4 Multimodal', + 'microsoft/phi-4': 'Phi-4', + 'microsoft/phi-3.5-mini-128k-instruct': 'Phi-3.5 Mini', + 'microsoft/phi-3-medium-128k-instruct': 'Phi-3 Medium', + 'microsoft/phi-3-mini-128k-instruct': 'Phi-3 Mini', + 'microsoft/wizardlm-2-8x22b': 'WizardLM-2 8x22B', + // OpenRouter - NVIDIA models + 'nvidia/llama-3.3-nemotron-super-49b-v1': 'Llama 3.3 Nemotron Super 49B', + 'nvidia/llama-3.1-nemotron-ultra-253b-v1': 'Llama 3.1 Nemotron Ultra 253B', + 'nvidia/llama-3.1-nemotron-70b-instruct': 'Llama 3.1 Nemotron 70B', + // OpenRouter - Other models + 'minimax/minimax-01': 'MiniMax-01', + 'minimax/minimax-m1': 'MiniMax-M1', + 'liquid/lfm-40b': 'LFM 40B', + 'liquid/lfm-7b': 'LFM 7B', + 'liquid/lfm-3b': 'LFM 3B', + 'cohere/command-a-03-2025': 'Command A', + 'cohere/command-r7b-12-2024': 'Command R7B', + 'cohere/command-r-plus': 'Command R Plus', + 'cohere/command-r': 'Command R', + 'amazon/nova-pro-v1': 'Nova Pro', + 'amazon/nova-lite-v1': 'Nova Lite', + 'amazon/nova-micro-v1': 'Nova Micro', + 'ai21/jamba-1.6-large': 'Jamba 1.6 Large', + 'ai21/jamba-1.6-mini': 'Jamba 1.6 Mini', + '01-ai/yi-large': 'Yi Large', + 'inflection/inflection-3-productivity': 'Inflection 3 Productivity', + 'inflection/inflection-3-pi': 'Inflection 3 Pi', + 'rekaai/reka-flash-3': 'Reka Flash 3', + 'openrouter/auto': 'Auto (Best Available)' + }; + + return modelNames[model] || model; + } +} \ No newline at end of file diff --git a/src/services/chatMessageService.ts b/src/services/chatMessageService.ts new file mode 100644 index 0000000..8f9f605 --- /dev/null +++ b/src/services/chatMessageService.ts @@ -0,0 +1,457 @@ +import * as vscode from 'vscode'; +import { ClaudeCodeService } from './claudeCodeService'; +import { AgentService } from '../types/agent'; +import { CoreMessage } from 'ai'; +import { Logger } from './logger'; + +export class ChatMessageService { + private currentRequestController?: AbortController; + + constructor( + private agentService: AgentService, + private outputChannel: vscode.OutputChannel + ) {} + + async handleChatMessage(message: any, webview: vscode.Webview): Promise { + try { + const chatHistory: CoreMessage[] = message.chatHistory || []; + const latestMessage = message.message || ''; + const messageContent = message.messageContent || latestMessage; + + console.log('========chatHistory', chatHistory); + + Logger.info(`Chat message received with ${chatHistory.length} history messages`); + Logger.info(`Latest message: ${latestMessage}`); + + // Debug structured content + if (typeof messageContent !== 'string' && Array.isArray(messageContent)) { + Logger.info(`Structured content: ${messageContent.length} parts`); + messageContent.forEach((part, index) => { + if (part.type === 'text') { + Logger.info(` [${index}] text: "${part.text?.substring(0, 100)}..."`); + } else if (part.type === 'image') { + Logger.info(` [${index}] image: ${part.mimeType || 'unknown type'} (${part.image?.length || 0} chars)`); + } + }); + } else { + Logger.info(`Simple text content: ${String(messageContent).substring(0, 100)}...`); + } + + // Create new AbortController for this request + this.currentRequestController = new AbortController(); + + // Send initial streaming start message + webview.postMessage({ + command: 'chatStreamStart' + }); + + // Debug log chat history with VS Code output channel + this.outputChannel.appendLine('=== CHAT HISTORY DEBUG ==='); + this.outputChannel.appendLine(`📥 Input: ${chatHistory.length} CoreMessage messages`); + + // Log each message + this.outputChannel.appendLine('📋 Chat history:'); + chatHistory.forEach((msg, index) => { + const content = typeof msg.content === 'string' ? msg.content : + Array.isArray(msg.content) ? + msg.content.map(part => + part.type === 'text' ? part.text?.substring(0, 50) + '...' : + part.type === 'tool-call' ? `[tool-call: ${part.toolName}]` : + part.type === 'tool-result' ? `[tool-result: ${part.toolName}]` : + `[${part.type}]` + ).join(', ') : + '[complex content]'; + + this.outputChannel.appendLine(` [${index}] ${msg.role}: "${content.substring(0, 100)}..."`); + }); + + this.outputChannel.appendLine('=== END CHAT HISTORY DEBUG ==='); + + // Use conversation history or single prompt + let response: any[]; + if (chatHistory.length > 0) { + // Use conversation history - CoreMessage format is already compatible + this.outputChannel.appendLine(`Using conversation history with ${chatHistory.length} messages`); + response = await this.agentService.query( + undefined, // no prompt + chatHistory, // use CoreMessage array directly + undefined, + this.currentRequestController, + (streamMessage: any) => { + // Process and send each message as it arrives + this.handleStreamMessage(streamMessage, webview); + } + ); + } else { + // Fallback to single prompt for first message + this.outputChannel.appendLine('No conversation history, using single prompt'); + response = await this.agentService.query( + latestMessage, // use latest message as prompt + undefined, // no messages array + undefined, + this.currentRequestController, + (streamMessage: any) => { + // Process and send each message as it arrives + this.handleStreamMessage(streamMessage, webview); + } + ); + } + + // Check if request was aborted + if (this.currentRequestController.signal.aborted) { + Logger.warn('Request was aborted'); + return; + } + + Logger.info(`Agent response completed with ${response.length} total messages`); + + // Send stream end message + webview.postMessage({ + command: 'chatStreamEnd' + }); + + } catch (error) { + // Check if the error is due to abort + if (this.currentRequestController?.signal.aborted) { + Logger.info('Request was stopped by user'); + webview.postMessage({ + command: 'chatStopped' + }); + return; + } + + Logger.error(`Chat message failed: ${error}`); + Logger.error(`Error type: ${typeof error}, constructor: ${error?.constructor?.name}`); + + // Check if this is an API key authentication error or process failure + const errorMessage = error instanceof Error ? error.message : String(error); + Logger.error(`Processing error message: "${errorMessage}"`); + if (this.agentService.isApiKeyAuthError(errorMessage) || !this.agentService.hasApiKey()) { + // Determine which provider is currently selected to show specific error + const config = vscode.workspace.getConfiguration('superdesign'); + const specificModel = config.get('aiModel'); + const provider = config.get('aiModelProvider', 'anthropic'); + + // Determine provider from model name if specific model is set + let effectiveProvider = provider; + let providerName = 'AI'; + let configureCommand = 'superdesign.configureApiKey'; + + if (specificModel) { + if (specificModel.includes('/')) { + effectiveProvider = 'openrouter'; + } else if (specificModel.startsWith('claude-')) { + effectiveProvider = 'anthropic'; + } else { + effectiveProvider = 'openai'; + } + } + + switch (effectiveProvider) { + case 'openrouter': + providerName = 'OpenRouter'; + configureCommand = 'superdesign.configureOpenRouterApiKey'; + break; + case 'anthropic': + providerName = 'Anthropic'; + configureCommand = 'superdesign.configureApiKey'; + break; + case 'openai': + providerName = 'OpenAI'; + configureCommand = 'superdesign.configureOpenAIApiKey'; + break; + } + + const hasApiKey = this.agentService.hasApiKey(); + const displayMessage = hasApiKey ? + `Invalid ${providerName} API key. Please check your configuration.` : + `${providerName} API key not configured. Please set up your API key to use this AI model.`; + + webview.postMessage({ + command: 'chatErrorWithActions', + error: displayMessage, + actions: [ + { text: `Configure ${providerName} API Key`, command: configureCommand }, + { text: 'Open Settings', command: 'workbench.action.openSettings', args: '@ext:iganbold.superdesign' } + ] + }); + } else { + // Regular error - show standard error message + vscode.window.showErrorMessage(`Chat failed: ${error}`); + webview.postMessage({ + command: 'chatError', + error: errorMessage + }); + } + } finally { + // Clear the controller when done + this.currentRequestController = undefined; + } + } + + private handleStreamMessage(message: CoreMessage, webview: vscode.Webview): void { + Logger.debug(`Handling CoreMessage: ${JSON.stringify(message, null, 2)}`); + + // Check if this is an update to existing message + const isUpdate = (message as any)._isUpdate; + const updateToolId = (message as any)._updateToolId; + + // Handle assistant messages + if (message.role === 'assistant') { + if (typeof message.content === 'string') { + // Simple text content + if (message.content.trim()) { + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'assistant', + content: message.content, + metadata: {} + }); + } + } else if (Array.isArray(message.content)) { + // Handle assistant content array (text parts, tool calls, etc.) + for (const part of message.content) { + if (part.type === 'text' && (part as any).text) { + // Send text content + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'assistant', + content: (part as any).text, + metadata: {} + }); + } else if (part.type === 'tool-call') { + // Send tool call or update + const toolPart = part as any; + const command = isUpdate ? 'chatToolUpdate' : 'chatResponseChunk'; + const messageType = isUpdate ? undefined : 'tool-call'; + + if (isUpdate) { + // Send tool parameter update + webview.postMessage({ + command: 'chatToolUpdate', + tool_use_id: toolPart.toolCallId, + tool_input: toolPart.args + }); + } else { + // Send new tool call message + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'tool-call', + content: '', + metadata: { + tool_name: toolPart.toolName, + tool_id: toolPart.toolCallId, + tool_input: toolPart.args + } + }); + } + } + } + } + } + + // Handle tool messages (CoreToolMessage) + if (message.role === 'tool' && Array.isArray(message.content)) { + for (const toolResultPart of message.content) { + if (toolResultPart.type === 'tool-result') { + const part = toolResultPart as any; + const content = typeof part.result === 'string' ? + part.result : + JSON.stringify(part.result, null, 2); + + Logger.debug(`Tool result for ${part.toolCallId}: "${content.substring(0, 200)}..."`); + + // Send tool result to frontend + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'tool-result', + content: content, + metadata: { + tool_id: part.toolCallId, + tool_name: part.toolName, + is_error: part.isError || false + } + }); + + // Also send completion signal + webview.postMessage({ + command: 'chatToolResult', + tool_use_id: part.toolCallId, + content: content, + is_error: part.isError || false + }); + } + } + } + + // Handle user messages + if (message.role === 'user') { + if (typeof message.content === 'string' && message.content.trim()) { + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'user', + content: message.content, + metadata: {} + }); + } + } + + // Skip other message types (system, etc.) + } + + // Legacy handler for backward compatibility + private handleLegacyResultMessage(message: any, webview: vscode.Webview): void { + if (message.type === 'result') { + Logger.debug(`Result message structure: ${JSON.stringify(message, null, 2)}`); + + // Skip error result messages that contain raw API key errors - these are handled by our custom error handler + if (message.is_error) { + // Check if this is an API key related error in any field + const messageStr = JSON.stringify(message).toLowerCase(); + if (messageStr.includes('api key') || messageStr.includes('authentication') || + messageStr.includes('unauthorized') || messageStr.includes('anthropic') || + messageStr.includes('process exited') || messageStr.includes('exit code')) { + Logger.debug('Skipping raw API key error result message - handled by custom error handler'); + return; + } + } + + // Skip final success result messages that are just summaries + if (message.subtype === 'success' && message.result && typeof message.result === 'string') { + const resultText = message.result.toLowerCase(); + // Skip if it looks like a final summary (contains phrases like "successfully created", "perfect", etc.) + if (resultText.includes('successfully') || resultText.includes('perfect') || + resultText.includes('created') || resultText.includes('variations')) { + Logger.debug('Skipping final summary result message'); + return; + } + } + + let content = ''; + let resultType = 'result'; + let isError = false; + + if (typeof message.message === 'string') { + content = message.message; + } else if (message.content) { + content = typeof message.content === 'string' ? message.content : JSON.stringify(message.content); + } else if (message.text) { + content = message.text; + } else if (message.result && typeof message.result === 'string') { + content = message.result; + } else { + // Skip messages that would result in raw JSON dump + Logger.debug('Skipping result message with no readable content'); + return; + } + + // Determine result type and error status + if (message.subtype) { + if (message.subtype.includes('error')) { + isError = true; + resultType = 'error'; + } else if (message.subtype === 'success') { + resultType = 'success'; + } + } + + Logger.debug(`Extracted result content: "${content.substring(0, 200)}..."`); + + if (content.trim()) { + webview.postMessage({ + command: 'chatResponseChunk', + messageType: 'tool-result', + content: content, + metadata: { + session_id: message.session_id, + parent_tool_use_id: message.parent_tool_use_id, + result_type: resultType, + is_error: isError, + duration_ms: message.duration_ms, + total_cost_usd: message.total_cost_usd + } + }); + } + } + + // Log tool activity + if ((message.type === 'assistant' || message.type === 'user') && ('subtype' in message) && (message.subtype === 'tool_use' || message.subtype === 'tool_result')) { + Logger.debug(`Tool activity detected: ${message.subtype}`); + } + } + + async stopCurrentChat(webview: vscode.Webview): Promise { + if (this.currentRequestController) { + Logger.info('Stopping current chat request'); + this.currentRequestController.abort(); + + // Send stopped message back to webview + webview.postMessage({ + command: 'chatStopped' + }); + } else { + Logger.info('No active chat request to stop'); + } + } + + private processClaudeResponse(response: any[]): string { + let fullResponse = ''; + let assistantMessages: string[] = []; + let toolResults: string[] = []; + + for (const msg of response) { + const subtype = 'subtype' in msg ? msg.subtype : undefined; + Logger.debug(`Processing message type: ${msg.type}${subtype ? `, subtype: ${subtype}` : ''}`); + + // Collect assistant messages + if (msg.type === 'assistant' && msg.message) { + let content = ''; + + if (typeof msg.message === 'string') { + content = msg.message; + } else if (msg.message.content && Array.isArray(msg.message.content)) { + content = msg.message.content + .filter((item: any) => item.type === 'text') + .map((item: any) => item.text) + .join('\n'); + } else if (msg.message.content && typeof msg.message.content === 'string') { + content = msg.message.content; + } + + if (content.trim()) { + assistantMessages.push(content); + } + } + + // Collect tool results + if (msg.type === 'result' && msg.subtype === 'success' && msg.result) { + const result = typeof msg.result === 'string' ? msg.result : JSON.stringify(msg.result, null, 2); + toolResults.push(result); + } + + // Handle tool usage messages + if ((msg.type === 'assistant' || msg.type === 'user') && ('subtype' in msg) && (msg.subtype === 'tool_use' || msg.subtype === 'tool_result')) { + Logger.debug(`Tool activity detected: ${msg.subtype}`); + } + } + + // Combine all responses + if (assistantMessages.length > 0) { + fullResponse = assistantMessages.join('\n\n'); + } + + if (toolResults.length > 0 && !fullResponse.includes(toolResults[0])) { + if (fullResponse) { + fullResponse += '\n\n--- Tool Results ---\n' + toolResults.join('\n\n'); + } else { + fullResponse = toolResults.join('\n\n'); + } + } + + if (!fullResponse) { + fullResponse = 'I processed your request but didn\'t generate a visible response. Check the console for details.'; + } + + return fullResponse; + } +} \ No newline at end of file diff --git a/src/services/claudeCodeService.ts b/src/services/claudeCodeService.ts new file mode 100644 index 0000000..d3ae33c --- /dev/null +++ b/src/services/claudeCodeService.ts @@ -0,0 +1,458 @@ +// This is deprecated, use customAgentService instead + +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; +import { Logger } from './logger'; + +// Dynamic import types for Claude Code +type SDKMessage = any; // Will be properly typed when imported +type ClaudeCodeOptions = any; // Will be properly typed when imported +type QueryFunction = (params: { + prompt: string; + abortController?: AbortController; + options?: any; +}) => AsyncGenerator; + +export class ClaudeCodeService { + private isInitialized = false; + private initializationPromise: Promise | null = null; + private workingDirectory: string = ''; + private outputChannel: vscode.OutputChannel; + private currentSessionId: string | null = null; + private claudeCodeQuery: QueryFunction | null = null; + + constructor(outputChannel: vscode.OutputChannel) { + this.outputChannel = outputChannel; + // Initialize on construction + this.initializationPromise = this.initialize(); + } + + private async initialize(): Promise { + if (this.isInitialized) { + return; + } + + try { + Logger.info('Starting Claude Code initialization...'); + + if (os.platform() === 'win32') { + throw new Error('Claude Code is not supported on Windows.'); + } + + // Setup working directory first + await this.setupWorkingDirectory(); + + // Check if API key is configured + const config = vscode.workspace.getConfiguration('superdesign'); + const apiKey = config.get('anthropicApiKey'); + + if (!apiKey) { + Logger.warn('No API key found'); + throw new Error('Missing API key'); + } + + // Set the environment variable for Claude Code SDK + process.env.ANTHROPIC_API_KEY = apiKey; + + // Dynamically import Claude Code SDK + Logger.info('Importing Claude Code SDK...'); + // try { + // // Try importing from the copied module location first + // let claudeCodeModule; + // try { + // // Try multiple possible paths for the extension location + // const possiblePaths = [ + // path.resolve(__dirname, '..', 'node_modules', '@anthropic-ai', 'claude-code', 'sdk.mjs'), + // path.resolve(__dirname, 'node_modules', '@anthropic-ai', 'claude-code', 'sdk.mjs'), + // path.join(__dirname, '..', 'node_modules', '@anthropic-ai', 'claude-code', 'sdk.mjs') + // ]; + + // let importSucceeded = false; + // for (const modulePath of possiblePaths) { + // try { + // if (fs.existsSync(modulePath)) { + // claudeCodeModule = await import(`file://${modulePath}`); + // importSucceeded = true; + // break; + // } + // } catch (pathError) { + // continue; + // } + // } + + // if (!importSucceeded) { + // throw new Error('All local import paths failed'); + // } + // } catch (localImportError) { + // // Fallback to standard import + // try { + // // claudeCodeModule = await import('@anthropic-ai/claude-code'); + // } catch (standardImportError) { + // Logger.error(`Claude Code SDK import failed: ${standardImportError}`); + // throw standardImportError; + // } + // } + + // this.claudeCodeQuery = claudeCodeModule.query; + + // if (!this.claudeCodeQuery) { + // throw new Error('Query function not found in Claude Code module'); + // } + + // Logger.info('Claude Code SDK imported successfully'); + // } catch (importError) { + // Logger.error(`Failed to import Claude Code SDK: ${importError}`); + // throw new Error(`Claude Code SDK import failed: ${importError}`); + // } + + this.isInitialized = true; + Logger.info('Claude Code SDK initialized successfully'); + } catch (error) { + Logger.error(`Failed to initialize Claude Code SDK: ${error}`); + + // Check if this is an API key related error (no UI popup needed here as error will be handled in chat) + const errorMessage = error instanceof Error ? error.message : String(error); + if (!this.isApiKeyAuthError(errorMessage)) { + vscode.window.showErrorMessage(`Failed to initialize Claude Code: ${error}`); + } + + // Reset initialization promise so it can be retried + this.initializationPromise = null; + this.isInitialized = false; + throw error; + } + } + + private async setupWorkingDirectory(): Promise { + try { + // Try to get workspace root first + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + + if (workspaceRoot) { + // Create .superdesign folder in workspace root + const superdesignDir = path.join(workspaceRoot, '.superdesign'); + + // Create directory if it doesn't exist + if (!fs.existsSync(superdesignDir)) { + fs.mkdirSync(superdesignDir, { recursive: true }); + Logger.info(`Created .superdesign directory: ${superdesignDir}`); + } + + this.workingDirectory = superdesignDir; + } else { + Logger.warn('No workspace root found, using temporary directory'); + // Fallback to OS temp directory if no workspace + const tempDir = path.join(os.tmpdir(), 'superdesign-claude'); + + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + Logger.info(`Created temporary directory: ${tempDir}`); + } + + this.workingDirectory = tempDir; + + vscode.window.showWarningMessage( + 'No workspace folder found. Using temporary directory for Claude Code operations.' + ); + } + } catch (error) { + Logger.error(`Failed to setup working directory: ${error}`); + // Final fallback to current working directory + this.workingDirectory = process.cwd(); + Logger.warn(`Using current working directory as fallback: ${this.workingDirectory}`); + } + } + + private async ensureInitialized(): Promise { + if (this.initializationPromise) { + await this.initializationPromise; + } + if (!this.isInitialized || !this.claudeCodeQuery) { + // Try to initialize if not already done + if (!this.initializationPromise) { + this.initializationPromise = this.initialize(); + await this.initializationPromise; + } else { + throw new Error('Claude Code SDK not initialized'); + } + } + } + + async query(prompt?: string, conversationMessages?: any, options?: Partial, abortController?: AbortController, onMessage?: (message: SDKMessage) => void): Promise { + // ClaudeCodeService handles conversation via internal session management + // so we ignore conversationMessages and just use the prompt + + if (!prompt) { + throw new Error('ClaudeCodeService requires a prompt parameter'); + } + + Logger.info('=== QUERY FUNCTION CALLED ==='); + Logger.info(`Query prompt: ${prompt.substring(0, 200)}...`); + Logger.info(`Query options: ${JSON.stringify(options, null, 2)}`); + Logger.info(`Streaming enabled: ${!!onMessage}`); + + if (conversationMessages) { + Logger.info('Note: ClaudeCodeService ignores conversationMessages (uses internal session management)'); + } + + await this.ensureInitialized(); + + const messages: SDKMessage[] = []; + const systemPrompt = `# Role +You are a **senior front-end designer**. +You pay close attention to every pixel, spacing, font, color; +Whenever there are UI implementation task, think deeply of the design style first, and then implement UI bit by bit + +# When asked to create design: +1. You ALWAYS spin up 3 parallel sub agents concurrently to implemeht one design with variations, so it's faster for user to iterate (Unless specifically asked to create only one version) + + +1. Build one single html page of just one screen to build a design based on users' feedback/task +2. You ALWAYS output design files in '.superdesign/design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file +3. If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. + + +## When asked to design UI: +1. Similar process as normal design task, but refer to 'UI design & implementation guidelines' for guidelines + +## When asked to update or iterate design: +1. Don't edit the existing design, just create a new html file with the same name but with _n.html appended to the end, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +2. At default you should spin up 3 parallel sub agents concurrently to try implement the design, so it's faster for user to iterate + +## When asked to design logo or icon: +1. Copy/duplicate existing svg file but name it based on our naming convention in design_ierations folder, and then make edits to the copied svg file (So we can avoid lots of mistakes), like 'original_filename.svg .superdesign/design-iterations/new_filename.svg' +2. Very important sub agent copy first, and Each agent just copy & edit a single svg file with svg code +3. you should focus on the the correctness of the svg code + +## When asked to design a component: +1. Similar process as normal design task, and each agent just create a single html page with component inside; +2. Focus just on just one component itself, and don't add any other elements or text +3. Each HTML just have one component with mock data inside + +## When asked to design wireframes: +1. Focus on minimal line style black and white wireframes, no colors, and never include any images, just try to use css to make some placeholder images. (Don't use service like placehold.co too, we can't render it) +2. Don't add any annotation of styles, just basic wireframes like Balsamiq style +3. Focus on building out the flow of the wireframes + +# When asked to extract design system from images: +Your goal is to extract a generalized and reusable design system from the screenshots provided, **without including specific image content**, so that frontend developers or AI agents can reference the JSON as a style foundation for building consistent UIs. + +1. Analyze the screenshots provided: + * Color palette + * Typography rules + * Spacing guidelines + * Layout structure (grids, cards, containers, etc.) + * UI components (buttons, inputs, tables, etc.) + * Border radius, shadows, and other visual styling patterns +2. Create a design-system.json file in 'design_system' folder that clearly defines these rules and can be used to replicate the visual language in a consistent way. +3. if design-system.json already exist, then create a new file with the name design-system_{n}.json (Where n needs to be unique like design-system_1.json, design-system_2.json, etc.) + +**Constraints** + +* Do **not** extract specific content from the screenshots (no text, logos, icons). +* Focus purely on *design principles*, *structure*, and *styles*. + +-------- + +# UI design & implementation guidelines: + +## Design Style +- A **perfect balance** between **elegant minimalism** and **functional design**. +- **Soft, refreshing gradient colors** that seamlessly integrate with the brand palette. +- **Well-proportioned white space** for a clean layout. +- **Light and immersive** user experience. +- **Clear information hierarchy** using **subtle shadows and modular card layouts**. +- **Natural focus on core functionalities**. +- **Refined rounded corners**. +- **Delicate micro-interactions**. +- **Comfortable visual proportions**. +- **Responsive design** You only output responsive design, it needs to look perfect on both mobile, tablet and desktop. + - If its a mobile app, also make sure you have responsive design OR make the center the mobile UI + +## Technical Specifications +1. **Images**: do NEVER include any images, we can't render images in webview,just try to use css to make some placeholder images. (Don't use service like placehold.co too, we can't render it) +2. **Styles**: Use **Tailwind CSS** via **CDN** for styling. (Use !important declarations for critical design tokens that must not be overridden, Load order management - ensure custom styles load after framework CSS, CSS-in-JS or scoped styles to avoid global conflicts, Use utility-first approach - define styles using Tailwind classes instead of custom CSS when possible) +3. **Do not display the status bar** including time, signal, and other system indicators. +4. **All text should be only black or white**. +5. Choose a **4 pt or 8 pt spacing system**—all margins, padding, line-heights, and element sizes must be exact multiples. +6. Use **consistent spacing tokens** (e.g., 4, 8, 16, 24, 32px) — never arbitrary values like 5 px or 13 px. +7. Apply **visual grouping** ("spacing friendship"): tighter gaps (4–8px) for related items, larger gaps (16–24px) for distinct groups. +8. Ensure **typographic rhythm**: font‑sizes, line‑heights, and spacing aligned to the grid (e.g., 16 px text with 24 px line-height). +9. Maintain **touch-area accessibility**: buttons and controls should meet or exceed 48×48 px, padded using grid units. + +## 🎨 Color Style +* Use a **minimal palette**: default to **black, white, and neutrals**—no flashy gradients or mismatched hues . +* Follow a **60‑30‑10 ratio**: \~60% background (white/light gray), \~30% surface (white/medium gray), \~10% accents (charcoal/black) . +* Accent colors limited to **one subtle tint** (e.g., charcoal black or very soft beige). Interactive elements like links or buttons use this tone sparingly. +* Always check **contrast** for text vs background via WCAG (≥4.5:1) + +## ✍️ Typography & Hierarchy + +### 1. 🎯 Hierarchy Levels & Structure +* Always define at least **three typographic levels**: **Heading (H1)**, **Subheading (H2)**, and **Body**. +* Use **size, weight, color**, and **spacing** to create clear differences between them ([toptal.com][1], [skyryedesign.com][2]). +* H1 should stand out clearly (largest & boldest), H2 should be distinctly smaller/medium-weight, and body remains readable and lighter. + +### 2. 📏 Size & Scale +* Follow a modular scale: e.g., **H1: 36px**, **H2: 28px**, **Body: 16px** (min). Adjust for mobile if needed . +* Maintain strong contrast—don't use size differences of only 2px; aim for at least **6–8px difference** between levels . + +### 3. 🧠 Weight, Style & Color +* Use **bold or medium weight** for headings, **regular** for body. +* Utilize **color contrast** (e.g., darker headings, neutral body) to support hierarchy ([mews.design][3], [toptal.com][1]). +* Avoid excessive styles like italics or uppercase—unless used sparingly for emphasis or subheadings. + +### 4. ✂️ Spacing & Rhythm +* Add **0.8×–1.5× line-height** for body and headings to improve legibility ([skyryedesign.com][2]). +* Use consistent **margin spacing above/below headings** (e.g., margin-top: 1.2× line-height) . + +`; + + try { + const finalOptions: Partial = { + maxTurns: 10, + allowedTools: [ + 'Read', 'Write', 'Edit', 'MultiEdit', 'Bash', 'LS', 'Grep', 'Glob' + ], + permissionMode: 'acceptEdits' as const, + cwd: this.workingDirectory, + customSystemPrompt: systemPrompt, + ...options + }; + + if (this.currentSessionId) { + finalOptions.resume = this.currentSessionId; + } + + const queryParams = { + prompt: prompt!, // Non-null assertion since we checked above + abortController: abortController || new AbortController(), + options: finalOptions + }; + + if (!this.claudeCodeQuery) { + throw new Error('Claude Code SDK not properly initialized - query function not available'); + } + + for await (const message of this.claudeCodeQuery(queryParams)) { + messages.push(message as SDKMessage); + + // Call the streaming callback if provided + if (onMessage) { + try { + onMessage(message as SDKMessage); + } catch (callbackError) { + Logger.error(`Streaming callback error: ${callbackError}`); + // Don't break the loop if callback fails + } + } + } + + const lastMessageWithSessionId = [...messages].reverse().find(m => 'session_id' in m && m.session_id); + if (lastMessageWithSessionId && 'session_id' in lastMessageWithSessionId && lastMessageWithSessionId.session_id) { + this.currentSessionId = lastMessageWithSessionId.session_id; + } + + Logger.info(`Query completed successfully. Received ${messages.length} messages`); + return messages; + } catch (error) { + Logger.error(`Claude Code query failed: ${error}`); + + // Check if this is an API key authentication error (handled in chat interface) + const errorMessage = error instanceof Error ? error.message : String(error); + if (!this.isApiKeyAuthError(errorMessage)) { + vscode.window.showErrorMessage(`Claude Code query failed: ${error}`); + } + throw error; + } + } + + get isReady(): boolean { + return this.isInitialized; + } + + async waitForInitialization(): Promise { + try { + await this.ensureInitialized(); + return true; + } catch (error) { + Logger.error(`Initialization failed: ${error}`); + return false; + } + } + + getWorkingDirectory(): string { + return this.workingDirectory; + } + + // Method to refresh API key from settings and reinitialize if needed + async refreshApiKey(): Promise { + try { + const config = vscode.workspace.getConfiguration('superdesign'); + const apiKey = config.get('anthropicApiKey'); + + if (!apiKey) { + Logger.warn('No API key found during refresh'); + return false; + } + + // Update environment variable + process.env.ANTHROPIC_API_KEY = apiKey; + Logger.info('API key refreshed from settings'); + + // If not initialized yet, try to initialize + if (!this.isInitialized) { + try { + await this.initialize(); + return true; + } catch (error) { + Logger.error(`Failed to initialize after API key refresh: ${error}`); + return false; + } + } + + return true; + } catch (error) { + Logger.error(`Failed to refresh API key: ${error}`); + return false; + } + } + + // Method to check if API key is configured + hasApiKey(): boolean { + const config = vscode.workspace.getConfiguration('superdesign'); + const apiKey = config.get('anthropicApiKey'); + return !!apiKey && apiKey.trim().length > 0; + } + + // Method to detect if an error is related to API key authentication + public isApiKeyAuthError(errorMessage: string): boolean { + const authErrorPatterns = [ + 'authentication failed', + 'invalid api key', + 'unauthorized', + 'api key', + 'authentication error', + 'invalid token', + 'access denied', + '401', + 'ANTHROPIC_API_KEY', + 'process exited with code 1', + 'claude code process exited', + 'exit code 1' + ]; + + const lowercaseMessage = errorMessage.toLowerCase(); + const isAuthError = authErrorPatterns.some(pattern => lowercaseMessage.includes(pattern)); + + Logger.info(`Checking if error is auth-related: "${errorMessage}" -> ${isAuthError}`); + if (isAuthError) { + const matchedPattern = authErrorPatterns.find(pattern => lowercaseMessage.includes(pattern)); + Logger.info(`Matched pattern: "${matchedPattern}"`); + } + + return isAuthError; + } +} \ No newline at end of file diff --git a/src/services/customAgentService.ts b/src/services/customAgentService.ts new file mode 100644 index 0000000..80309c1 --- /dev/null +++ b/src/services/customAgentService.ts @@ -0,0 +1,931 @@ +import { streamText, CoreMessage } from 'ai'; +import { createOpenAI } from '@ai-sdk/openai'; +import { createAnthropic } from '@ai-sdk/anthropic'; +import { createOpenRouter } from '@openrouter/ai-sdk-provider'; +import * as vscode from 'vscode'; +import * as path from 'path'; +import * as fs from 'fs'; +import * as os from 'os'; +import { AgentService, ExecutionContext } from '../types/agent'; +import { createReadTool } from '../tools/read-tool'; +import { createWriteTool } from '../tools/write-tool'; +import { createBashTool } from '../tools/bash-tool'; +import { createEditTool } from '../tools/edit-tool'; +import { createGlobTool } from '../tools/glob-tool'; +import { createGrepTool } from '../tools/grep-tool'; +import { createThemeTool } from '../tools/theme-tool'; +import { createLsTool } from '../tools/ls-tool'; +import { createMultieditTool } from '../tools/multiedit-tool'; + +export class CustomAgentService implements AgentService { + private workingDirectory: string = ''; + private outputChannel: vscode.OutputChannel; + private isInitialized = false; + + constructor(outputChannel: vscode.OutputChannel) { + this.outputChannel = outputChannel; + this.outputChannel.appendLine('CustomAgentService constructor called'); + this.setupWorkingDirectory(); + } + + private async setupWorkingDirectory(): Promise { + try { + // Try to get workspace root first + const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath; + this.outputChannel.appendLine(`Workspace root detected: ${workspaceRoot}`); + + if (workspaceRoot) { + // Create .superdesign folder in workspace root + const superdesignDir = path.join(workspaceRoot, '.superdesign'); + this.outputChannel.appendLine(`Setting up .superdesign directory at: ${superdesignDir}`); + + // Create directory if it doesn't exist + if (!fs.existsSync(superdesignDir)) { + fs.mkdirSync(superdesignDir, { recursive: true }); + this.outputChannel.appendLine(`Created .superdesign directory: ${superdesignDir}`); + } else { + this.outputChannel.appendLine(`.superdesign directory already exists: ${superdesignDir}`); + } + + this.workingDirectory = superdesignDir; + this.outputChannel.appendLine(`Working directory set to: ${this.workingDirectory}`); + } else { + this.outputChannel.appendLine('No workspace root found, using fallback'); + // Fallback to OS temp directory if no workspace + const tempDir = path.join(os.tmpdir(), 'superdesign-custom'); + + if (!fs.existsSync(tempDir)) { + fs.mkdirSync(tempDir, { recursive: true }); + this.outputChannel.appendLine(`Created temporary superdesign directory: ${tempDir}`); + } + + this.workingDirectory = tempDir; + this.outputChannel.appendLine(`Working directory set to (fallback): ${this.workingDirectory}`); + + vscode.window.showWarningMessage( + 'No workspace folder found. Using temporary directory for Custom Agent operations.' + ); + } + + this.isInitialized = true; + } catch (error) { + this.outputChannel.appendLine(`Failed to setup working directory: ${error}`); + // Final fallback to current working directory + this.workingDirectory = process.cwd(); + this.outputChannel.appendLine(`Working directory set to (final fallback): ${this.workingDirectory}`); + this.isInitialized = true; + } + } + + private getModel() { + const config = vscode.workspace.getConfiguration('superdesign'); + const specificModel = config.get('aiModel'); + const provider = config.get('aiModelProvider', 'anthropic'); + + this.outputChannel.appendLine(`Using AI provider: ${provider}`); + if (specificModel) { + this.outputChannel.appendLine(`Using specific AI model: ${specificModel}`); + } + + // Determine provider from model name if specific model is set + let effectiveProvider = provider; + if (specificModel) { + if (specificModel.includes('/')) { + effectiveProvider = 'openrouter'; + } else if (specificModel.startsWith('claude-')) { + effectiveProvider = 'anthropic'; + } else { + effectiveProvider = 'openai'; + } + } + + switch (effectiveProvider) { + case 'openrouter': + const openrouterKey = config.get('openrouterApiKey'); + if (!openrouterKey) { + throw new Error('OpenRouter API key not configured. Please run "Configure OpenRouter API Key" command.'); + } + + this.outputChannel.appendLine(`OpenRouter API key found: ${openrouterKey.substring(0, 12)}...`); + + const openrouterBaseURL = config.get('openrouterBaseURL'); + const openrouter = createOpenRouter({ + apiKey: openrouterKey, + baseURL: openrouterBaseURL + }); + + // Use specific model if available, otherwise default to Claude 3.7 Sonnet via OpenRouter + const openrouterModel = specificModel || 'anthropic/claude-3-7-sonnet-20250219'; + this.outputChannel.appendLine(`Using OpenRouter model: ${openrouterModel}`); + return openrouter.chat(openrouterModel); + + case 'anthropic': + const anthropicKey = config.get('anthropicApiKey'); + if (!anthropicKey) { + throw new Error('Anthropic API key not configured. Please run "Configure Anthropic API Key" command.'); + } + + this.outputChannel.appendLine(`Anthropic API key found: ${anthropicKey.substring(0, 12)}...`); + + const anthropicBaseURL = config.get('anthropicBaseURL'); + const anthropic = createAnthropic({ + apiKey: anthropicKey, + baseURL: anthropicBaseURL, + }); + + // Use specific model if available, otherwise default to claude-3-5-sonnet + const anthropicModel = specificModel || 'claude-3-5-sonnet-20241022'; + this.outputChannel.appendLine(`Using Anthropic model: ${anthropicModel}`); + return anthropic(anthropicModel); + + case 'openai': + default: + const openaiKey = config.get('openaiApiKey'); + if (!openaiKey) { + throw new Error('OpenAI API key not configured. Please run "Configure OpenAI API Key" command.'); + } + + this.outputChannel.appendLine(`OpenAI API key found: ${openaiKey.substring(0, 7)}...`); + + const openaiBaseURL = config.get('openaiBaseURL'); + + const openai = createOpenAI({ + apiKey: openaiKey, + baseURL: openaiBaseURL, + }); + + // Use specific model if available, otherwise default to gpt-4o + let openaiModel = specificModel || 'gpt-4o'; + if (openaiModel === 'openai-auto') { + openaiModel = config.get('aiModelAuto') || 'gpt-4o'; + this.outputChannel.appendLine(`AUTO model detected, using configured model: ${openaiModel}`); + } + this.outputChannel.appendLine(`Using OpenAI model: ${openaiModel}`); + return openai(openaiModel); + } + } + + private getSystemPrompt(): string { + const config = vscode.workspace.getConfiguration('superdesign'); + const specificModel = config.get('aiModel'); + const provider = config.get('aiModelProvider', 'anthropic'); + + // Determine the actual model name being used + let modelName: string; + if (specificModel) { + modelName = specificModel; + } else { + // Use defaults based on provider + switch (provider) { + case 'openai': + modelName = 'gpt-4o'; + break; + case 'openrouter': + modelName = 'anthropic/claude-3-7-sonnet-20250219'; + break; + case 'anthropic': + default: + modelName = 'claude-3-5-sonnet-20241022'; + break; + } + } + + return `# Role +You are superdesign, a senior frontend designer integrated into VS Code as part of the Super Design extension. +Your goal is to help user generate amazing design using code + +# Current Context +- Extension: Super Design (Design Agent for VS Code) +- AI Model: ${modelName} +- Working directory: ${this.workingDirectory} + +# Instructions +- Use the available tools when needed to help with file operations and code analysis +- When creating design file: + - Build one single html page of just one screen to build a design based on users' feedback/task + - You ALWAYS output design files in 'design_iterations' folder as {design_name}_{n}.html (Where n needs to be unique like table_1.html, table_2.html, etc.) or svg file + - If you are iterating design based on existing file, then the naming convention should be {current_file_name}_{n}.html, e.g. if we are iterating ui_1.html, then each version should be ui_1_1.html, ui_1_2.html, etc. +- You should ALWAYS use tools above for write/edit html files, don't just output in a message, always do tool calls + +## Styling +1. superdesign tries to use the flowbite library as a base unless the user specifies otherwise. +2. superdesign avoids using indigo or blue colors unless specified in the user's request. +3. superdesign MUST generate responsive designs. +4. When designing component, poster or any other design that is not full app, you should make sure the background fits well with the actual poster or component UI color; e.g. if component is light then background should be dark, vice versa. +5. Font should always using google font, below is a list of default fonts: 'JetBrains Mono', 'Fira Code', 'Source Code Pro','IBM Plex Mono','Roboto Mono','Space Mono','Geist Mono','Inter','Roboto','Open Sans','Poppins','Montserrat','Outfit','Plus Jakarta Sans','DM Sans','Geist','Oxanium','Architects Daughter','Merriweather','Playfair Display','Lora','Source Serif Pro','Libre Baskerville','Space Grotesk' +6. When creating CSS, make sure you include !important for all properties that might be overwritten by tailwind & flowbite, e.g. h1, body, etc. +7. Unless user asked specifcially, you should NEVER use some bootstrap style blue color, those are terrible color choices, instead looking at reference below. +8. Example theme patterns: +Ney-brutalism style that feels like 90s web design + +:root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0 0 0); + --primary: oklch(0.6489 0.2370 26.9728); + --primary-foreground: oklch(1.0000 0 0); + --secondary: oklch(0.9680 0.2110 109.7692); + --secondary-foreground: oklch(0 0 0); + --muted: oklch(0.9551 0 0); + --muted-foreground: oklch(0.3211 0 0); + --accent: oklch(0.5635 0.2408 260.8178); + --accent-foreground: oklch(1.0000 0 0); + --destructive: oklch(0 0 0); + --destructive-foreground: oklch(1.0000 0 0); + --border: oklch(0 0 0); + --input: oklch(0 0 0); + --ring: oklch(0.6489 0.2370 26.9728); + --chart-1: oklch(0.6489 0.2370 26.9728); + --chart-2: oklch(0.9680 0.2110 109.7692); + --chart-3: oklch(0.5635 0.2408 260.8178); + --chart-4: oklch(0.7323 0.2492 142.4953); + --chart-5: oklch(0.5931 0.2726 328.3634); + --sidebar: oklch(0.9551 0 0); + --sidebar-foreground: oklch(0 0 0); + --sidebar-primary: oklch(0.6489 0.2370 26.9728); + --sidebar-primary-foreground: oklch(1.0000 0 0); + --sidebar-accent: oklch(0.5635 0.2408 260.8178); + --sidebar-accent-foreground: oklch(1.0000 0 0); + --sidebar-border: oklch(0 0 0); + --sidebar-ring: oklch(0.6489 0.2370 26.9728); + --font-sans: DM Sans, sans-serif; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: Space Mono, monospace; + --radius: 0px; + --shadow-2xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-xs: 4px 4px 0px 0px hsl(0 0% 0% / 0.50); + --shadow-sm: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 1px 2px -1px hsl(0 0% 0% / 1.00); + --shadow-md: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 2px 4px -1px hsl(0 0% 0% / 1.00); + --shadow-lg: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 4px 6px -1px hsl(0 0% 0% / 1.00); + --shadow-xl: 4px 4px 0px 0px hsl(0 0% 0% / 1.00), 4px 8px 10px -1px hsl(0 0% 0% / 1.00); + --shadow-2xl: 4px 4px 0px 0px hsl(0 0% 0% / 2.50); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +Modern dark mode style like vercel, linear + +:root { + --background: oklch(1 0 0); + --foreground: oklch(0.1450 0 0); + --card: oklch(1 0 0); + --card-foreground: oklch(0.1450 0 0); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.1450 0 0); + --primary: oklch(0.2050 0 0); + --primary-foreground: oklch(0.9850 0 0); + --secondary: oklch(0.9700 0 0); + --secondary-foreground: oklch(0.2050 0 0); + --muted: oklch(0.9700 0 0); + --muted-foreground: oklch(0.5560 0 0); + --accent: oklch(0.9700 0 0); + --accent-foreground: oklch(0.2050 0 0); + --destructive: oklch(0.5770 0.2450 27.3250); + --destructive-foreground: oklch(1 0 0); + --border: oklch(0.9220 0 0); + --input: oklch(0.9220 0 0); + --ring: oklch(0.7080 0 0); + --chart-1: oklch(0.8100 0.1000 252); + --chart-2: oklch(0.6200 0.1900 260); + --chart-3: oklch(0.5500 0.2200 263); + --chart-4: oklch(0.4900 0.2200 264); + --chart-5: oklch(0.4200 0.1800 266); + --sidebar: oklch(0.9850 0 0); + --sidebar-foreground: oklch(0.1450 0 0); + --sidebar-primary: oklch(0.2050 0 0); + --sidebar-primary-foreground: oklch(0.9850 0 0); + --sidebar-accent: oklch(0.9700 0 0); + --sidebar-accent-foreground: oklch(0.2050 0 0); + --sidebar-border: oklch(0.9220 0 0); + --sidebar-ring: oklch(0.7080 0 0); + --font-sans: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji'; + --font-serif: ui-serif, Georgia, Cambria, "Times New Roman", Times, serif; + --font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; + --radius: 0.625rem; + --shadow-2xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-xs: 0 1px 3px 0px hsl(0 0% 0% / 0.05); + --shadow-sm: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 1px 2px -1px hsl(0 0% 0% / 0.10); + --shadow-md: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 2px 4px -1px hsl(0 0% 0% / 0.10); + --shadow-lg: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 4px 6px -1px hsl(0 0% 0% / 0.10); + --shadow-xl: 0 1px 3px 0px hsl(0 0% 0% / 0.10), 0 8px 10px -1px hsl(0 0% 0% / 0.10); + --shadow-2xl: 0 1px 3px 0px hsl(0 0% 0% / 0.25); + --tracking-normal: 0em; + --spacing: 0.25rem; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); +} + + +## Images & icons +1. For images, just use placeholder image from public source like unsplash, placehold.co or others that you already know exact image url; Don't make up urls +2. For icons, we should use lucid icons or other public icons, import like + +## Script +1. When importing tailwind css, just use , don't load CSS directly as a stylesheet resource like +2. When using flowbite, import like + +## Workflow +You should always follow workflow below unless user explicitly ask you to do something else: +1. Layout design +2. Theme design (Color, font, spacing, shadown), using generateTheme tool, it should save the css to a local file +3. Core Animation design +4. Generate a singlehtml file for the UI +5. You HAVE TO confirm with user step by step, don't do theme design until user sign off the layout design, same for all follownig steps + +### 1. Layout design +Think through how should the layout of interface look like, what are different UI components +And present the layout in ASCII wireframe format, here are the guidelines of good ASCII wireframe, you can do ASCII art too for more custom layout or graphic design + +### 2. Theme design +Think through what are the colors, fonts, spacing, etc. + +### 3. Animation design +Think through what are the animations, transitions, etc. + +### 4. Generate html file for each UI component and then combine them together to form a single html file +Generate html file for each UI component and then combine them together to form a single html file +Make sure to reference the theme css file you created in step 2, and add custom ones that doesn't exist yet in html file + + +design an AI chat UI + + +Let's think through the layout design for an AI chat UI. Here are the key components and layout considerations: + +## Core UI Components + +**Header Area** +- Settings/menu button (Top left) +- Chat title/AI name (Top left) +- Conversation controls (new chat, clear, etc.) (Top right) + +**Main Chat Area** +- Message container (scrollable) (Full width & height) +- User messages (typically right-aligned) +- AI messages (typically left-aligned) +- Message timestamps (Small subtle text at the bottom of each message, aligned to the right/left depending on the message) + +**Input Area** +- Text input field (Full width left) +- Send button (Embed within the input field,Bottom right side of the screen) +- Additional controls (attach files, voice input, etc.) (Embed within the input field, Bottom left) +- Character/token counter (optional) (Embed within the input field, top right corner, small text) + + +## Layout Structure Options + +┌─────────────────────────────────────┐ +│ ☰ HEADER BAR + │ +├─────────────────────────────────────┤ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ User Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ ┌─────────────────────────────┐ │ +│ │ AI Message Bubble │ │ +│ └─────────────────────────────┘ │ +│ │ +│ [CHAT AREA] │ +│ │ +├─────────────────────────────────────┤ +│ [Text Input Field] [Send] │ +└─────────────────────────────────────┘ + +When hamburger (☰) is clicked, sidebar slides out: +┌──────────────┬─────────────────────────────────────┐ +│ SIDEBAR │ ☰ HEADER BAR + │ +│ ┌──────────┐ ├─────────────────────────────────────┤ +│ │ Chat 1 │ │ │ +│ │ Chat 2 │ │ ┌─────────────────────────────┐ │ +│ │ Chat 3 │ │ │ AI Message Bubble │ │ +│ │ + New │ │ └─────────────────────────────┘ │ +│ └──────────┘ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ User Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ ┌─────────────────────────────┐ │ +│ │ │ AI Message Bubble │ │ +│ │ └─────────────────────────────┘ │ +│ │ │ +│ │ [CHAT AREA] │ +│ │ │ +│ ├─────────────────────────────────────┤ +│ │ [Text Input Field] [Send] │ +└──────────────┘─────────────────────────────────────┘ + +Would you like to go ahead with this layout & UI interaction or needs any modification? + + + +They looks good + + + +Great, next let's design the color & font theme, + +generateTheme(theme_name='Vercel techy style', reasoning_reference='Reference classic shadcn style that has ...', cssFilePath='design_iterations/theme_1.css', cssSheet=':root { + --background: oklch(1.0000 0 0); + --foreground: oklch(0.1448 0 0); + --card: oklch(1.0000 0 0); + --card-foreground: oklch(0.1448 0 0); + --popover: oklch(1.0000 0 0); + --popover-foreground: oklch(0.1448 0 0); + --primary: oklch(0.5555 0 0); + --primary-foreground: oklch(0.9851 0 0); + --secondary: oklch(0.9702 0 0); + --secondary-foreground: oklch(0.2046 0 0); + --muted: oklch(0.9702 0 0); + --muted-foreground: oklch(0.5486 0 0); + --accent: oklch(0.9702 0 0); + --accent-foreground: oklch(0.2046 0 0); + --destructive: oklch(0.5830 0.2387 28.4765); + --destructive-foreground: oklch(0.9702 0 0); + --border: oklch(0.9219 0 0); + --input: oklch(0.9219 0 0); + --ring: oklch(0.7090 0 0); + --chart-1: oklch(0.5555 0 0); + --chart-2: oklch(0.5555 0 0); + --chart-3: oklch(0.5555 0 0); + --chart-4: oklch(0.5555 0 0); + --chart-5: oklch(0.5555 0 0); + --sidebar: oklch(0.9851 0 0); + --sidebar-foreground: oklch(0.1448 0 0); + --sidebar-primary: oklch(0.2046 0 0); + --sidebar-primary-foreground: oklch(0.9851 0 0); + --sidebar-accent: oklch(0.9702 0 0); + --sidebar-accent-foreground: oklch(0.2046 0 0); + --sidebar-border: oklch(0.9219 0 0); + --sidebar-ring: oklch(0.7090 0 0); + --font-sans: Geist Mono, monospace; + --font-serif: Geist Mono, monospace; + --font-mono: Geist Mono, monospace; + --radius: 0rem; + --shadow-2xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-xs: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --shadow-sm: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 1px 2px -1px hsl(0 0% 0% / 0.00); + --shadow-md: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 2px 4px -1px hsl(0 0% 0% / 0.00); + --shadow-lg: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 4px 6px -1px hsl(0 0% 0% / 0.00); + --shadow-xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00), 0px 8px 10px -1px hsl(0 0% 0% / 0.00); + --shadow-2xl: 0px 1px 0px 0px hsl(0 0% 0% / 0.00); + --tracking-normal: 0em; + --spacing: 0.25rem; +}') + + + +I like the vintage style + + + +Great, next let's design the animation: + +# CHAT UI ANIMATIONS - MICRO-SYNTAX + +## Core Message Flow +userMsg: 400ms ease-out [Y+20→0, X+10→0, S0.9→1] +aiMsg: 600ms bounce [Y+15→0, S0.95→1] +200ms +typing: 1400ms ∞ [Y±8, α0.4→1] stagger+200ms +status: 300ms ease-out [α0.6→1, S1→1.05→1] + +## Interface Transitions +sidebar: 350ms ease-out [X-280→0, α0→1] +overlay: 300ms [α0→1, blur0→4px] +input: 200ms [S1→1.01, shadow+ring] focus +input: 150ms [S1.01→1, shadow-ring] blur + +## Button Interactions +sendBtn: 150ms [S1→0.95→1, R±2°] press +sendBtn: 200ms [S1→1.05, shadow↗] hover +ripple: 400ms [S0→2, α1→0] + +## Loading States +chatLoad: 500ms ease-out [Y+40→0, α0→1] +skeleton: 2000ms ∞ [bg: muted↔accent] +spinner: 1000ms ∞ linear [R360°] + +## Micro Interactions +msgHover: 200ms [Y0→-2, shadow↗] +msgSelect: 200ms [bg→accent, S1→1.02] +error: 400ms [X±5] shake +success: 600ms bounce [S0→1.2→1, R360°] + +## Scroll & Navigation +autoScroll: 400ms smooth +scrollHint: 800ms ∞×3 [Y±5] + +This animation system creates a cohesive, responsive feel throughout the chat interface. Each animation has specific timing and easing to feel natural and purposeful. Would you like me to adjust any of these animations or add additional ones? + + + + +This looks great, lets do it + + + +Great, I will generate the html file for each UI component and then combine them together to form a single html file + +write(file_path='design_iterations/chat_ui.css', content='...') +write(file_path='design_iterations/chat_ui.html', content='...') + +I've created the html design, please reveiw and let me know if you need any changes + + + + +# Available Tools +- **read**: Read file contents within the workspace (supports text files, images, with line range options) +- **write**: Write content to files in the workspace (creates parent directories automatically) +- **edit**: Replace text within files using exact string matching (requires precise text matching including whitespace and indentation) +- **multiedit**: Perform multiple find-and-replace operations on a single file in sequence (each edit applied to result of previous edit) +- **glob**: Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts") - efficient for locating files by name or path structure +- **grep**: Search for text patterns within file contents using regular expressions (can filter by file types and paths) +- **ls**: List directory contents with optional filtering, sorting, and detailed information (shows files and subdirectories) +- **bash**: Execute shell/bash commands within the workspace (secure execution with timeouts and output capture) +- **generateTheme**: Generate a theme for the design +`;} + + async query( + prompt?: string, + conversationHistory?: CoreMessage[], + options?: any, + abortController?: AbortController, + onMessage?: (message: any) => void + ): Promise { + this.outputChannel.appendLine('=== CUSTOM AGENT QUERY CALLED ==='); + + // Determine which input format we're using + const usingConversationHistory = !!conversationHistory && conversationHistory.length > 0; + + if (usingConversationHistory) { + this.outputChannel.appendLine(`Query using conversation history: ${conversationHistory!.length} messages`); + } else if (prompt) { + this.outputChannel.appendLine(`Query prompt: ${prompt.substring(0, 200)}...`); + } else { + throw new Error('Either prompt or conversationHistory must be provided'); + } + + this.outputChannel.appendLine(`Query options: ${JSON.stringify(options, null, 2)}`); + this.outputChannel.appendLine(`Streaming enabled: ${!!onMessage}`); + + if (!this.isInitialized) { + await this.setupWorkingDirectory(); + } + + const responseMessages: any[] = []; + const sessionId = `session_${Date.now()}`; + let messageBuffer = ''; + + // Tool call streaming state + let currentToolCall: any = null; + let toolCallBuffer = ''; + + try { + this.outputChannel.appendLine('Starting AI SDK streamText...'); + + // Create execution context for tools + const executionContext: ExecutionContext = { + workingDirectory: this.workingDirectory, + sessionId: sessionId, + outputChannel: this.outputChannel, + abortController: abortController, + }; + + // Create tools with context + const tools = { + read: createReadTool(executionContext), + write: createWriteTool(executionContext), + edit: createEditTool(executionContext), + multiedit: createMultieditTool(executionContext), + glob: createGlobTool(executionContext), + grep: createGrepTool(executionContext), + ls: createLsTool(executionContext), + bash: createBashTool(executionContext), + generateTheme: createThemeTool(executionContext) + }; + + // Prepare AI SDK input based on available data + const streamTextConfig: any = { + model: this.getModel(), + system: this.getSystemPrompt(), + tools: tools, + toolCallStreaming: true, + maxSteps: 10, // Enable multi-step reasoning with tools + maxTokens: 8192 // Increase token limit to prevent truncation + }; + + if (usingConversationHistory) { + // Use conversation messages + streamTextConfig.messages = conversationHistory; + this.outputChannel.appendLine(`Using conversation history with ${conversationHistory!.length} messages`); + + // Debug: Log the actual messages being sent to AI SDK + this.outputChannel.appendLine('=== AI SDK MESSAGES DEBUG ==='); + conversationHistory!.forEach((msg, index) => { + const content = typeof msg.content === 'string' ? msg.content : JSON.stringify(msg.content); + this.outputChannel.appendLine(` [${index}] ${msg.role}: "${content.substring(0, 150)}..."`); + }); + this.outputChannel.appendLine('=== END AI SDK MESSAGES DEBUG ==='); + } else { + // Use single prompt + streamTextConfig.prompt = prompt; + this.outputChannel.appendLine(`Using single prompt: ${prompt!.substring(0, 100)}...`); + } + + console.log('========streamTextConfig', streamTextConfig); + + const result = streamText(streamTextConfig); + + this.outputChannel.appendLine('AI SDK streamText created, starting to process chunks...'); + + + + for await (const chunk of result.fullStream) { + // Check for abort signal + if (abortController?.signal.aborted) { + this.outputChannel.appendLine('Operation aborted by user'); + throw new Error('Operation cancelled'); + } + + this.outputChannel.appendLine(`Received chunk type: ${chunk.type}`); + + switch (chunk.type) { + case 'text-delta': + // Handle streaming text (assistant message chunks) - CoreMessage format + messageBuffer += chunk.textDelta; + + const textMessage: CoreMessage = { + role: 'assistant', + content: chunk.textDelta + }; + + onMessage?.(textMessage); + responseMessages.push(textMessage); + break; + + case 'finish': + // Final result message - CoreMessage format + this.outputChannel.appendLine(`===Stream finished with reason: ${chunk.finishReason}`); + this.outputChannel.appendLine(`${JSON.stringify(chunk)}`); + this.outputChannel.appendLine(`========================================`); + + const resultMessage: CoreMessage = { + role: 'assistant', + content: chunk.finishReason === 'stop' ? 'Response completed successfully' : 'Response completed' + }; + + onMessage?.(resultMessage); + responseMessages.push(resultMessage); + break; + + case 'error': + // Error handling - CoreMessage format + const errorMsg = (chunk as any).error?.message || 'Unknown error occurred'; + this.outputChannel.appendLine(`Stream error: ${errorMsg}`); + + const errorMessage: CoreMessage = { + role: 'assistant', + content: `Error: ${errorMsg}` + }; + + onMessage?.(errorMessage); + responseMessages.push(errorMessage); + break; + + case 'tool-call-streaming-start': + // Tool call streaming started - CoreAssistantMessage format + const streamStart = chunk as any; + currentToolCall = { + toolCallId: streamStart.toolCallId, + toolName: streamStart.toolName, + args: {} + }; + toolCallBuffer = ''; + + this.outputChannel.appendLine(`Tool call streaming started: ${streamStart.toolName} (ID: ${streamStart.toolCallId})`); + + // Send initial tool call message in CoreAssistantMessage format + const toolCallStartMessage: CoreMessage = { + role: 'assistant', + content: [{ + type: 'tool-call', + toolCallId: streamStart.toolCallId, + toolName: streamStart.toolName, + args: {} // Empty initially, will be updated with deltas + }] + }; + + onMessage?.(toolCallStartMessage); + responseMessages.push(toolCallStartMessage); + break; + + case 'tool-call-delta': + // Streaming tool call parameters - update existing message + const delta = chunk as any; + if (currentToolCall && delta.argsTextDelta) { + toolCallBuffer += delta.argsTextDelta; + + // Try to parse current buffer as JSON and send update + try { + const parsedArgs = JSON.parse(toolCallBuffer); + + // Send UPDATE signal (not new message) with special marker + const updateMessage: CoreMessage & { _isUpdate?: boolean, _updateToolId?: string } = { + role: 'assistant', + content: [{ + type: 'tool-call', + toolCallId: currentToolCall.toolCallId, + toolName: currentToolCall.toolName, + args: parsedArgs + }], + _isUpdate: true, + _updateToolId: currentToolCall.toolCallId + }; + + onMessage?.(updateMessage); + + } catch (parseError) { + // JSON not complete yet, continue buffering + if (toolCallBuffer.length % 100 === 0) { + this.outputChannel.appendLine(`Tool call progress: ${toolCallBuffer.length} characters received (parsing...)`); + } + } + } + break; + + case 'tool-call': + // Handle final complete tool call - CoreAssistantMessage format + const toolCall = chunk as any; + this.outputChannel.appendLine(`=====Tool call complete: ${JSON.stringify(toolCall)}`); + this.outputChannel.appendLine(`========================================`); + + // Skip sending duplicate tool call message if we already sent streaming start + if (!currentToolCall) { + // Only send if we didn't already send a streaming start message + const toolCallMessage: CoreMessage = { + role: 'assistant', + content: [{ + type: 'tool-call', + toolCallId: toolCall.toolCallId, + toolName: toolCall.toolName, + args: toolCall.args + }] + }; + + onMessage?.(toolCallMessage); + responseMessages.push(toolCallMessage); + } else { + this.outputChannel.appendLine(`Skipping duplicate tool call message - already sent streaming start for ID: ${toolCall.toolCallId}`); + } + + // Reset tool call streaming state + currentToolCall = null; + toolCallBuffer = ''; + break; + + case 'step-start': + // Log step start with details + const stepStart = chunk as any; + this.outputChannel.appendLine(`====Step ${stepStart.step || 'unknown'} started: ${stepStart.stepType || 'reasoning'}`); + this.outputChannel.appendLine(`${JSON.stringify(chunk)}`); + this.outputChannel.appendLine(`========================================`); + break; + + case 'step-finish': + // Log step completion with details + const stepFinish = chunk as any; + this.outputChannel.appendLine(`====Step ${stepFinish.step || 'unknown'} finished: ${stepFinish.stepType || 'reasoning'} (${stepFinish.finishReason || 'completed'})`); + this.outputChannel.appendLine(`${JSON.stringify(chunk)}`); + this.outputChannel.appendLine(`========================================`); + break; + + default: + // Handle tool results and other unknown chunk types + if ((chunk as any).type === 'tool-result') { + const toolResult = chunk as any; + this.outputChannel.appendLine(`Tool result received for ID: ${toolResult.toolCallId}: ${JSON.stringify(toolResult.result).substring(0, 200)}...`); + + // Send tool result in CoreToolMessage format + const toolResultMessage: CoreMessage = { + role: 'tool', + content: [{ + type: 'tool-result', + toolCallId: toolResult.toolCallId, + toolName: toolResult.toolName, + result: toolResult.result, + isError: toolResult.isError || false + }] + }; + + onMessage?.(toolResultMessage); + responseMessages.push(toolResultMessage); + } else { + this.outputChannel.appendLine(`Unknown chunk type: ${chunk.type}`); + } + break; + } + } + + this.outputChannel.appendLine(`Query completed successfully. Total messages: ${responseMessages.length}`); + this.outputChannel.appendLine(`Complete response: "${messageBuffer}"`); + + return responseMessages; + + } catch (error) { + this.outputChannel.appendLine(`Custom Agent query failed: ${error}`); + this.outputChannel.appendLine(`Error stack: ${error instanceof Error ? error.stack : 'No stack trace'}`); + + // Send error message if streaming callback is available + if (onMessage) { + const errorMessage = { + type: 'result', + subtype: 'error', + result: error instanceof Error ? error.message : String(error), + session_id: sessionId, + is_error: true + }; + onMessage(errorMessage); + } + + throw error; + } + } + + get isReady(): boolean { + return this.isInitialized; + } + + async waitForInitialization(): Promise { + if (!this.isInitialized) { + await this.setupWorkingDirectory(); + } + return this.isInitialized; + } + + getWorkingDirectory(): string { + return this.workingDirectory; + } + + hasApiKey(): boolean { + const config = vscode.workspace.getConfiguration('superdesign'); + const specificModel = config.get('aiModel'); + const provider = config.get('aiModelProvider', 'anthropic'); + + // Determine provider from model name if specific model is set + let effectiveProvider = provider; + if (specificModel) { + if (specificModel.includes('/')) { + effectiveProvider = 'openrouter'; + } else if (specificModel.startsWith('claude-')) { + effectiveProvider = 'anthropic'; + } else { + effectiveProvider = 'openai'; + } + } + + switch (effectiveProvider) { + case 'openrouter': + return !!config.get('openrouterApiKey'); + case 'anthropic': + return !!config.get('anthropicApiKey'); + case 'openai': + default: + return !!config.get('openaiApiKey'); + } + } + + isApiKeyAuthError(errorMessage: string): boolean { + if (!errorMessage) { + return false; + } + + const lowerError = errorMessage.toLowerCase(); + return lowerError.includes('api key') || + lowerError.includes('authentication') || + lowerError.includes('unauthorized') || + lowerError.includes('invalid_api_key') || + lowerError.includes('permission_denied') || + lowerError.includes('api_key_invalid') || + lowerError.includes('unauthenticated'); + } +} \ No newline at end of file diff --git a/src/services/logger.ts b/src/services/logger.ts new file mode 100644 index 0000000..fc0a9df --- /dev/null +++ b/src/services/logger.ts @@ -0,0 +1,80 @@ +import * as vscode from 'vscode'; + +export enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR, +} + +export class Logger { + private static outputChannel: vscode.OutputChannel; + private static currentLevel: LogLevel = LogLevel.INFO; + + public static initialize() { + if (!this.outputChannel) { + this.outputChannel = vscode.window.createOutputChannel('Superdesign'); + } + } + + public static setLevel(level: LogLevel) { + this.currentLevel = level; + } + + private static log(level: LogLevel, label: string, message: string, showNotification: boolean = false) { + if (level < this.currentLevel) { + return; + } + + this.initialize(); + const timestamp = new Date().toISOString(); + this.outputChannel.appendLine(`[${timestamp}] [${label}] ${message}`); + + if (showNotification) { + switch (level) { + case LogLevel.ERROR: + vscode.window.showErrorMessage(message); + break; + case LogLevel.WARN: + vscode.window.showWarningMessage(message); + break; + case LogLevel.INFO: + vscode.window.showInformationMessage(message); + break; + default: + // No notification for debug + break; + } + } + } + + public static debug(message: string, showNotification: boolean = false) { + this.log(LogLevel.DEBUG, 'DEBUG', message, showNotification); + } + + public static info(message: string, showNotification: boolean = false) { + this.log(LogLevel.INFO, 'INFO', message, showNotification); + } + + public static warn(message: string, showNotification: boolean = false) { + this.log(LogLevel.WARN, 'WARN', message, showNotification); + } + + public static error(message: string, showNotification: boolean = false) { + this.log(LogLevel.ERROR, 'ERROR', message, showNotification); + } + + public static dispose() { + if (this.outputChannel) { + this.outputChannel.dispose(); + } + } + + /** + * Get the output channel for direct access if needed + */ + public static getOutputChannel(): vscode.OutputChannel { + this.initialize(); + return this.outputChannel; + } +} \ No newline at end of file diff --git a/src/templates/webviewTemplate.ts b/src/templates/webviewTemplate.ts new file mode 100644 index 0000000..dde591d --- /dev/null +++ b/src/templates/webviewTemplate.ts @@ -0,0 +1,81 @@ +import * as vscode from 'vscode'; +import { WebviewContext } from '../types/context'; + +export function generateWebviewHtml( + webview: vscode.Webview, + extensionUri: vscode.Uri, + context: WebviewContext +): string { + const scriptUri = webview.asWebviewUri( + vscode.Uri.joinPath(extensionUri, 'dist', 'webview.js') + ); + + // Generate webview URIs for logo images + const logoUris = { + cursor: webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'src', 'assets', 'cursor_logo.png')).toString(), + windsurf: webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'src', 'assets', 'windsurf_logo.png')).toString(), + claudeCode: webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'src', 'assets', 'claude_code_logo.png')).toString(), + lovable: webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'src', 'assets', 'lovable_logo.png')).toString(), + bolt: webview.asWebviewUri(vscode.Uri.joinPath(extensionUri, 'src', 'assets', 'bolt_logo.jpg')).toString(), + }; + + // Debug logging + console.log('Extension URI:', extensionUri.toString()); + console.log('Generated logo URIs:', logoUris); + + // Check if files exist + const fs = require('fs'); + const path = require('path'); + Object.entries(logoUris).forEach(([name, uri]) => { + const filePath = path.join(extensionUri.fsPath, 'src', 'assets', name === 'bolt' ? 'bolt_logo.jpg' : `${name === 'claudeCode' ? 'claude_code' : name}_logo.png`); + const exists = fs.existsSync(filePath); + console.log(`${name} logo exists at ${filePath}:`, exists); + }); + + return ` + + + + + + Superdesign Chat + + + +
+ + + + `; +} + + \ No newline at end of file diff --git a/src/tools/bash-tool.ts b/src/tools/bash-tool.ts new file mode 100644 index 0000000..c25fd1c --- /dev/null +++ b/src/tools/bash-tool.ts @@ -0,0 +1,263 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import { spawn, ChildProcess } from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import * as os from 'os'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateDirectoryExists, + ToolResponse +} from './tool-utils'; + +const bashParametersSchema = z.object({ + command: z.string().describe('Shell command to execute (e.g., "npm install", "ls -la", "git status")'), + description: z.string().optional().describe('Brief description of what the command does for logging purposes'), + directory: z.string().optional().describe('Directory to run command in (relative to workspace root). Defaults to workspace root.'), + timeout: z.number().optional().describe('Timeout in milliseconds (default: 30000ms = 30 seconds)'), + capture_output: z.boolean().optional().describe('Whether to capture and return command output (default: true)'), + env: z.record(z.string()).optional().describe('Environment variables to set for the command execution') +}); + +interface CommandResult { + command: string; + directory: string; + stdout: string; + stderr: string; + exitCode: number | null; + signal: string | null; + duration: number; + timedOut: boolean; + processId?: number; +} + +/** + * Check for potentially unsafe commands + */ +function hasUnsafeCommand(command: string): boolean { + const unsafePatterns = [ + // System modification + /\brm\s+(-[rf]*\s+)?\/\s*$/i, + /\brm\s+-[rf]*\s+\/$/i, + /\b(format|fdisk|mkfs)\b/i, + // Network operations that could be dangerous + /\b(curl|wget)\s+.*\|\s*(bash|sh|python|ruby|perl)/i, + // Process manipulation + /\b(kill|killall|pkill)\s+(-9\s+)?1\b/i, + // System shutdown/reboot + /\b(shutdown|reboot|halt|init\s+0)\b/i, + // Privilege escalation + /\b(sudo\s+su|sudo.*passwd|chmod\s+777)/i, + // Directory traversal attempts + /\.\.(\/|\\)/, + // Dangerous redirections + />\s*(\/dev\/|\/proc\/|\/sys\/)/i, + ]; + + return unsafePatterns.some(pattern => pattern.test(command)); +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Execute command with proper process management + */ +async function executeCommand( + command: string, + options: { + cwd: string; + env: NodeJS.ProcessEnv; + timeout: number; + captureOutput: boolean; + } +): Promise { + const startTime = Date.now(); + const isWindows = os.platform() === 'win32'; + + // Choose shell based on platform + const shell = isWindows ? 'cmd.exe' : 'bash'; + const shellArgs = isWindows ? ['/c', command] : ['-c', command]; + + const child: ChildProcess = spawn(shell, shellArgs, { + cwd: options.cwd, + env: options.env, + stdio: options.captureOutput ? ['ignore', 'pipe', 'pipe'] : 'inherit', + detached: !isWindows, // Create process group on Unix systems + }); + + let stdout = ''; + let stderr = ''; + let timedOut = false; + + // Capture output if requested + if (options.captureOutput && child.stdout && child.stderr) { + child.stdout.on('data', (data: Buffer) => { + stdout += data.toString(); + }); + + child.stderr.on('data', (data: Buffer) => { + stderr += data.toString(); + }); + } + + // Set up timeout + const timeoutHandle = setTimeout(() => { + timedOut = true; + if (child.pid) { + try { + if (isWindows) { + // On Windows, use taskkill to terminate process tree + spawn('taskkill', ['/pid', child.pid.toString(), '/f', '/t']); + } else { + // On Unix, kill the process group + process.kill(-child.pid, 'SIGTERM'); + // Force kill after 1 second if still running + setTimeout(() => { + if (child.pid && !child.killed) { + try { + process.kill(-child.pid, 'SIGKILL'); + } catch (e) { + // Process might already be dead + } + } + }, 1000); + } + } catch (error) { + // Process might already be dead + } + } + }, options.timeout); + + // Wait for process to complete + const exitPromise = new Promise<{ code: number | null; signal: string | null }>((resolve) => { + child.on('exit', (code, signal) => { + clearTimeout(timeoutHandle); + resolve({ code, signal }); + }); + }); + + const { code, signal } = await exitPromise; + const duration = Date.now() - startTime; + + return { + command, + directory: path.relative(options.cwd, options.cwd) || '.', + stdout: stdout.trim(), + stderr: stderr.trim(), + exitCode: code, + signal: signal, + duration, + timedOut, + processId: child.pid + }; +} + +export function createBashTool(context: ExecutionContext) { + return tool({ + description: 'Execute shell/bash commands within the SuperDesign workspace. Supports timeouts, output capture, and secure execution.', + parameters: bashParametersSchema, + execute: async (params): Promise => { + try { + const { command, description, directory, timeout = 30000, capture_output = true, env } = params; + + // Security checks + if (hasUnsafeCommand(command)) { + return handleToolError('Command contains potentially unsafe operations', 'Security check', 'security'); + } + + // Resolve execution directory + const workingDir = directory || '.'; + + // Security check for workspace boundary + const pathError = validateWorkspacePath(workingDir, context); + if (pathError) { + return pathError; + } + + const absolutePath = resolveWorkspacePath(workingDir, context); + + // Check if directory exists + const dirError = validateDirectoryExists(absolutePath, workingDir); + if (dirError) { + return dirError; + } + + console.log(`Executing command: ${command}${description ? ` (${description})` : ''}`); + console.log(`Working directory: ${workingDir}`); + + // Prepare environment + const processEnv = { + ...process.env, + ...env + }; + + // Execute the command + const result = await executeCommand(command, { + cwd: absolutePath, + env: processEnv, + timeout, + captureOutput: capture_output + }); + + // Log results + if (result.timedOut) { + console.log(`Command timed out after ${timeout}ms`); + } else if (result.exitCode === 0) { + console.log(`Command completed successfully in ${result.duration}ms`); + } else { + console.log(`Command failed with exit code ${result.exitCode} in ${result.duration}ms`); + } + + // Create summary for display + let summary = `Command: ${command}\n`; + summary += `Directory: ${result.directory}\n`; + summary += `Exit Code: ${result.exitCode}\n`; + summary += `Duration: ${result.duration}ms\n`; + + if (result.timedOut) { + summary += `Status: TIMED OUT (${timeout}ms)\n`; + } else if (result.signal) { + summary += `Signal: ${result.signal}\n`; + } + + if (capture_output) { + if (result.stdout) { + summary += `\nStdout:\n${result.stdout}\n`; + } + if (result.stderr) { + summary += `\nStderr:\n${result.stderr}\n`; + } + } + + if (result.timedOut) { + return handleToolError(`Command timed out after ${timeout}ms`, 'Command execution', 'execution'); + } + + if (result.exitCode !== 0) { + return handleToolError( + `Command failed with exit code ${result.exitCode}${result.stderr ? `\nStderr: ${result.stderr}` : ''}`, + 'Command execution', + 'execution' + ); + } + + return createSuccessResponse({ + command, + directory: workingDir, + exitCode: result.exitCode, + duration: result.duration, + stdout: result.stdout, + stderr: result.stderr, + summary + }); + + } catch (error) { + return handleToolError(error, 'Bash tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/edit-tool.ts b/src/tools/edit-tool.ts new file mode 100644 index 0000000..8f3bb53 --- /dev/null +++ b/src/tools/edit-tool.ts @@ -0,0 +1,197 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateFileExists, + ToolResponse +} from './tool-utils'; + +const editParametersSchema = z.object({ + file_path: z.string().describe('Path to the file to edit (relative to workspace root, or absolute path within workspace)'), + old_string: z.string().describe('The exact text to find and replace. Must match exactly including whitespace, indentation, and context. For single replacements, include 3+ lines of context before and after the target text.'), + new_string: z.string().describe('The text to replace old_string with. Should maintain proper indentation and formatting.'), + expected_replacements: z.number().min(1).optional().describe('Number of replacements expected (default: 1). Use when replacing multiple occurrences.') +}); + +interface CalculatedEdit { + currentContent: string; + newContent: string; + occurrences: number; + isNewFile: boolean; + error?: string; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Escape special regex characters + */ +function escapeRegExp(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Calculate the edit operation without executing it + */ +function calculateEdit( + file_path: string, + old_string: string, + new_string: string, + expected_replacements: number, + context: ExecutionContext +): CalculatedEdit { + // Use the utility function to resolve paths + const absolutePath = resolveWorkspacePath(file_path, context); + + // Check if file exists + if (!fs.existsSync(absolutePath)) { + if (old_string === '') { + // Creating a new file + return { + currentContent: '', + newContent: new_string, + occurrences: 1, + isNewFile: true + }; + } else { + return { + currentContent: '', + newContent: '', + occurrences: 0, + isNewFile: false, + error: `File not found: ${file_path}. Cannot apply edit. Use empty old_string to create a new file.` + }; + } + } + + // Read current content + let currentContent: string; + try { + currentContent = fs.readFileSync(absolutePath, 'utf8'); + // Normalize line endings to LF + currentContent = currentContent.replace(/\r\n/g, '\n'); + } catch (error) { + return { + currentContent: '', + newContent: '', + occurrences: 0, + isNewFile: false, + error: `Failed to read file: ${error instanceof Error ? error.message : String(error)}` + }; + } + + // Handle creating file that already exists + if (old_string === '') { + return { + currentContent, + newContent: '', + occurrences: 0, + isNewFile: false, + error: `File already exists, cannot create: ${file_path}` + }; + } + + // Count occurrences + const occurrences = (currentContent.match(new RegExp(escapeRegExp(old_string), 'g')) || []).length; + + // Validate occurrence count + if (occurrences === 0) { + return { + currentContent, + newContent: currentContent, + occurrences: 0, + isNewFile: false, + error: `Text not found in file. 0 occurrences of old_string found. Ensure exact text match including whitespace and indentation.` + }; + } + + if (occurrences !== expected_replacements) { + return { + currentContent, + newContent: currentContent, + occurrences, + isNewFile: false, + error: `Expected ${expected_replacements} replacement(s) but found ${occurrences} occurrence(s).` + }; + } + + // Apply replacement + const newContent = currentContent.split(old_string).join(new_string); + + return { + currentContent, + newContent, + occurrences, + isNewFile: false + }; +} + +export function createEditTool(context: ExecutionContext) { + return tool({ + description: 'Replace text within a file using exact string matching. Accepts both relative and absolute file paths within the workspace.', + parameters: editParametersSchema, + execute: async (params): Promise => { + try { + const { file_path, old_string, new_string, expected_replacements = 1 } = params; + + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(file_path, context); + if (pathError) { + return pathError; + } + + console.log(`Editing file: ${file_path}`); + + // Calculate the edit + const editResult = calculateEdit(file_path, old_string, new_string, expected_replacements, context); + + if (editResult.error) { + return handleToolError(editResult.error, 'Edit operation', 'execution'); + } + + const absolutePath = resolveWorkspacePath(file_path, context); + + // Create parent directories if needed (for new files) + if (editResult.isNewFile) { + const dirName = path.dirname(absolutePath); + if (!fs.existsSync(dirName)) { + fs.mkdirSync(dirName, { recursive: true }); + console.log(`Created parent directories for: ${file_path}`); + } + } + + // Write the updated content + fs.writeFileSync(absolutePath, editResult.newContent, 'utf8'); + + const newLines = editResult.newContent.split('\n').length; + const newSize = Buffer.byteLength(editResult.newContent, 'utf8'); + + if (editResult.isNewFile) { + console.log(`Created new file: ${file_path} (${newLines} lines)`); + } else { + console.log(`Applied ${editResult.occurrences} replacement(s) to: ${file_path} (${newLines} lines)`); + } + + return createSuccessResponse({ + file_path, + absolute_path: absolutePath, + is_new_file: editResult.isNewFile, + replacements_made: editResult.occurrences, + lines_total: newLines, + bytes_total: newSize, + old_string_length: old_string.length, + new_string_length: new_string.length + }); + + } catch (error) { + return handleToolError(error, 'Edit tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/glob-tool.ts b/src/tools/glob-tool.ts new file mode 100644 index 0000000..e1ec401 --- /dev/null +++ b/src/tools/glob-tool.ts @@ -0,0 +1,294 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateDirectoryExists, + ToolResponse +} from './tool-utils'; + +const globParametersSchema = z.object({ + pattern: z.string().describe('Glob pattern to match (e.g., "*.js", "src/**/*.ts", "**/*.{js,ts}")'), + path: z.string().optional().describe('Directory to search in (relative to workspace root, or absolute path within workspace). Defaults to workspace root.'), + case_sensitive: z.boolean().optional().describe('Whether the search should be case-sensitive (default: false)'), + include_dirs: z.boolean().optional().describe('Whether to include directories in results (default: false)'), + show_hidden: z.boolean().optional().describe('Whether to include hidden files/directories (starting with .)'), + max_results: z.number().min(1).optional().describe('Maximum number of results to return (default: 500)'), + sort_by_time: z.boolean().optional().describe('Whether to sort results by modification time, newest first (default: false)') +}); + +interface GlobFileEntry { + path: string; + absolutePath: string; + isDirectory: boolean; + size: number; + modifiedTime: Date; + extension?: string; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Convert glob pattern to regex pattern + */ +function globToRegex(pattern: string, caseSensitive: boolean = false): RegExp { + // Handle special cases for braces {js,ts} + let regexPattern = pattern; + + // Handle brace expansion like {js,ts,jsx} + const braceRegex = /\{([^}]+)\}/g; + regexPattern = regexPattern.replace(braceRegex, (match, content) => { + const options = content.split(',').map((s: string) => s.trim()); + return `(${options.join('|')})`; + }); + + // Escape regex special characters except glob chars + regexPattern = regexPattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars except *, ?, and already handled {} + .replace(/\\\{/g, '{') // Restore { that we want to keep + .replace(/\\\}/g, '}') // Restore } that we want to keep + .replace(/\\\|/g, '|') // Restore | that we want to keep + .replace(/\\\(/g, '(') // Restore ( that we want to keep + .replace(/\\\)/g, ')'); // Restore ) that we want to keep + + // Handle glob patterns + regexPattern = regexPattern + .replace(/\*\*/g, '###DOUBLESTAR###') // Temporarily replace ** + .replace(/\*/g, '[^/]*') // * becomes [^/]* (match any chars except path separator) + .replace(/###DOUBLESTAR###/g, '.*') // ** becomes .* (match any chars including path separator) + .replace(/\?/g, '[^/]'); // ? becomes [^/] (match single char except path separator) + + const flags = caseSensitive ? '' : 'i'; + return new RegExp(`^${regexPattern}$`, flags); +} + +/** + * Check if a file should be skipped based on common patterns + */ +function shouldSkipPath(relativePath: string, showHidden: boolean): boolean { + // Skip hidden files unless requested + if (!showHidden && relativePath.split('/').some(part => part.startsWith('.'))) { + return true; + } + + // Skip common directories that should never be searched + const skipPatterns = [ + /node_modules/, + /\.git$/, + /\.svn$/, + /\.hg$/, + /\.vscode$/, + /dist$/, + /build$/, + /coverage$/, + /\.nyc_output$/, + /\.next$/, + /\.cache$/ + ]; + + return skipPatterns.some(pattern => pattern.test(relativePath)); +} + +/** + * Recursively find files matching the pattern + */ +async function findMatches( + searchDir: string, + pattern: RegExp, + options: { + includeDirs: boolean; + showHidden: boolean; + maxResults: number; + } +): Promise { + const results: GlobFileEntry[] = []; + + const scanDirectory = async (currentDir: string): Promise => { + if (results.length >= options.maxResults) { + return; + } + + try { + const entries = await fs.promises.readdir(currentDir, { withFileTypes: true }); + + for (const entry of entries) { + if (results.length >= options.maxResults) { + break; + } + + const fullPath = path.join(currentDir, entry.name); + const relativePath = path.relative(searchDir, fullPath); + + // Skip paths that should be ignored + if (shouldSkipPath(relativePath, options.showHidden)) { + continue; + } + + const isDirectory = entry.isDirectory(); + + // Check if this path matches the pattern + const matches = pattern.test(relativePath); + + if (matches && (options.includeDirs || !isDirectory)) { + try { + const stats = await fs.promises.stat(fullPath); + + results.push({ + path: relativePath, + absolutePath: fullPath, + isDirectory, + size: isDirectory ? 0 : stats.size, + modifiedTime: stats.mtime, + extension: isDirectory ? undefined : path.extname(entry.name).slice(1) + }); + } catch (error) { + // Ignore stat errors and continue + } + } + + // Recursively scan subdirectories + if (isDirectory) { + await scanDirectory(fullPath); + } + } + } catch (error) { + // Ignore permission errors and continue + } + }; + + await scanDirectory(searchDir); + return results; +} + +/** + * Sort results by modification time (newest first) or alphabetically + */ +function sortResults(results: GlobFileEntry[], sortByTime: boolean): GlobFileEntry[] { + if (!sortByTime) { + // Sort alphabetically with directories first + return results.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) { + return -1; + } + if (!a.isDirectory && b.isDirectory) { + return 1; + } + return a.path.localeCompare(b.path); + }); + } + + // Sort by modification time (newest first) with recent files prioritized + const oneDayAgo = new Date().getTime() - (24 * 60 * 60 * 1000); + + return results.sort((a, b) => { + const aTime = a.modifiedTime.getTime(); + const bTime = b.modifiedTime.getTime(); + const aIsRecent = aTime > oneDayAgo; + const bIsRecent = bTime > oneDayAgo; + + // Both recent: newest first + if (aIsRecent && bIsRecent) { + return bTime - aTime; + } + + // One recent: recent first + if (aIsRecent) { + return -1; + } + if (bIsRecent) { + return 1; + } + + // Both old: alphabetical + return a.path.localeCompare(b.path); + }); +} + +export function createGlobTool(context: ExecutionContext) { + return tool({ + description: 'Find files and directories matching glob patterns (e.g., "*.js", "src/**/*.ts"). Efficient for locating files by name or path structure.', + parameters: globParametersSchema, + execute: async (params): Promise => { + try { + const { + pattern, + path: searchPath = '.', + case_sensitive = false, + include_dirs = false, + show_hidden = false, + max_results = 500, + sort_by_time = false + } = params; + + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(searchPath, context); + if (pathError) { + return pathError; + } + + // Resolve search directory + const absolutePath = resolveWorkspacePath(searchPath, context); + + // Check if path exists and is a directory + const dirError = validateDirectoryExists(absolutePath, searchPath); + if (dirError) { + return dirError; + } + + console.log(`Finding files matching pattern "${pattern}" in ${searchPath}`); + + // Convert glob pattern to regex + const regex = globToRegex(pattern, case_sensitive); + + // Find matching files + const matches = await findMatches(absolutePath, regex, { + includeDirs: include_dirs, + showHidden: show_hidden, + maxResults: max_results + }); + + // Sort results + const sortedMatches = sortResults(matches, sort_by_time); + + // Create summary + const fileCount = sortedMatches.filter(m => !m.isDirectory).length; + const dirCount = sortedMatches.filter(m => m.isDirectory).length; + + let summary = `Found ${sortedMatches.length} match(es) for pattern "${pattern}"`; + if (fileCount > 0 && dirCount > 0) { + summary += ` (${fileCount} files, ${dirCount} directories)`; + } else if (fileCount > 0) { + summary += ` (${fileCount} files)`; + } else if (dirCount > 0) { + summary += ` (${dirCount} directories)`; + } + + if (sortedMatches.length >= max_results) { + summary += ` - results truncated at ${max_results}`; + } + + console.log(summary); + + return createSuccessResponse({ + pattern, + search_path: searchPath, + matches: sortedMatches, + total_matches: sortedMatches.length, + file_count: fileCount, + directory_count: dirCount, + summary, + truncated: sortedMatches.length >= max_results, + sorted_by_time: sort_by_time + }); + + } catch (error) { + return handleToolError(error, 'Glob tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/grep-tool.ts b/src/tools/grep-tool.ts new file mode 100644 index 0000000..2af5b08 --- /dev/null +++ b/src/tools/grep-tool.ts @@ -0,0 +1,315 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateDirectoryExists, + ToolResponse +} from './tool-utils'; + +const grepParametersSchema = z.object({ + pattern: z.string().describe('Regular expression pattern to search for (e.g., "function\\s+\\w+", "import.*from")'), + path: z.string().optional().describe('Directory to search in (relative to workspace root, or absolute path within workspace). Defaults to workspace root.'), + include: z.string().optional().describe('File pattern to include (e.g., "*.js", "*.{ts,tsx}", "src/**/*.ts")'), + case_sensitive: z.boolean().optional().describe('Whether the search should be case-sensitive (default: false)'), + max_files: z.number().min(1).optional().describe('Maximum number of files to search (default: 1000)'), + max_matches: z.number().min(1).optional().describe('Maximum number of matches to return (default: 100)') +}); + +interface GrepMatch { + filePath: string; + lineNumber: number; + line: string; + matchStart: number; + matchEnd: number; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Check if a file path matches the include pattern + */ +function matchesIncludePattern(filePath: string, includePattern?: string): boolean { + if (!includePattern) { + return true; + } + + // Convert glob pattern to regex (simplified) + const regexPattern = includePattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars + .replace(/\*\*/g, '###DOUBLESTAR###') // Temporarily replace ** + .replace(/\*/g, '[^/]*') // * becomes [^/]* (no directory separators) + .replace(/###DOUBLESTAR###/g, '.*') // ** becomes .* (any characters) + .replace(/\?/g, '[^/]'); // ? becomes [^/] (single char, no dir sep) + + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(filePath); +} + +/** + * Check if a file should be skipped based on common patterns + */ +function shouldSkipFile(filePath: string): boolean { + const skipPatterns = [ + /node_modules/, + /\.git/, + /\.vscode/, + /dist/, + /build/, + /coverage/, + /\.nyc_output/, + /\.next/, + /\.cache/, + /\.DS_Store/, + /Thumbs\.db/, + /\.log$/, + /\.tmp$/, + /\.temp$/ + ]; + + return skipPatterns.some(pattern => pattern.test(filePath)); +} + +/** + * Simple check if file is likely a text file + */ +function isTextFile(filePath: string): boolean { + const textExtensions = [ + '.js', '.ts', '.jsx', '.tsx', '.json', '.html', '.htm', '.css', '.scss', '.sass', + '.py', '.java', '.cpp', '.c', '.h', '.hpp', '.cs', '.php', '.rb', '.go', + '.rs', '.swift', '.kt', '.scala', '.clj', '.hs', '.elm', '.ml', '.f', + '.txt', '.md', '.rst', '.asciidoc', '.xml', '.yaml', '.yml', '.toml', + '.ini', '.cfg', '.conf', '.properties', '.env', '.gitignore', '.gitattributes', + '.dockerfile', '.makefile', '.sh', '.bat', '.ps1', '.sql', '.graphql', + '.vue', '.svelte', '.astro', '.prisma', '.proto' + ]; + + const ext = path.extname(filePath).toLowerCase(); + return textExtensions.includes(ext) || !ext; // Include extensionless files +} + +/** + * Recursively find files to search + */ +async function findFilesToSearch( + dirPath: string, + includePattern?: string, + maxFiles: number = 1000 +): Promise { + const files: string[] = []; + + const scanDirectory = async (currentPath: string): Promise => { + if (files.length >= maxFiles) { + return; + } + + try { + const entries = await fs.promises.readdir(currentPath, { withFileTypes: true }); + + for (const entry of entries) { + if (files.length >= maxFiles) { + break; + } + + const fullPath = path.join(currentPath, entry.name); + const relativePath = path.relative(dirPath, fullPath); + + // Skip common directories and files + if (shouldSkipFile(relativePath)) { + continue; + } + + if (entry.isDirectory()) { + await scanDirectory(fullPath); + } else if (entry.isFile()) { + // Check if file matches include pattern + if (matchesIncludePattern(relativePath, includePattern)) { + // Only include text files (basic check) + if (isTextFile(fullPath)) { + files.push(fullPath); + } + } + } + } + } catch (error) { + // Ignore permission errors and continue + } + }; + + await scanDirectory(dirPath); + return files; +} + +/** + * Search for pattern in a single file + */ +async function searchInFile(filePath: string, regex: RegExp, maxMatches: number): Promise { + const matches: GrepMatch[] = []; + + try { + const content = await fs.promises.readFile(filePath, 'utf8'); + const lines = content.split(/\r?\n/); + + for (let lineIndex = 0; lineIndex < lines.length; lineIndex++) { + if (matches.length >= maxMatches) { + break; + } + + const line = lines[lineIndex]; + let match; + regex.lastIndex = 0; // Reset regex state + + while ((match = regex.exec(line)) !== null) { + matches.push({ + filePath, + lineNumber: lineIndex + 1, + line: line, + matchStart: match.index, + matchEnd: match.index + match[0].length + }); + + if (matches.length >= maxMatches) { + break; + } + + // Prevent infinite loop on zero-length matches + if (match.index === regex.lastIndex) { + regex.lastIndex++; + } + } + } + } catch (error) { + // Ignore files that can't be read (binary files, permission issues, etc.) + } + + return matches; +} + +export function createGrepTool(context: ExecutionContext) { + return tool({ + description: 'Search for text patterns within file contents using regular expressions. Can filter by file types and paths.', + parameters: grepParametersSchema, + execute: async (params): Promise => { + try { + const { + pattern, + path: searchPath = '.', + include, + case_sensitive = false, + max_files = 1000, + max_matches = 100 + } = params; + + // Pattern validation (test if it's a valid regex) + try { + new RegExp(pattern); + } catch (error) { + return handleToolError( + `Invalid regular expression pattern: ${error instanceof Error ? error.message : String(error)}`, + 'Pattern validation', + 'validation' + ); + } + + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(searchPath, context); + if (pathError) { + return pathError; + } + + // Resolve search directory + const absolutePath = resolveWorkspacePath(searchPath, context); + + // Check if path exists and is a directory + const dirError = validateDirectoryExists(absolutePath, searchPath); + if (dirError) { + return dirError; + } + + console.log(`Searching for pattern "${pattern}" in ${searchPath}`); + + // Create regex pattern + const regexFlags = case_sensitive ? 'g' : 'gi'; + const regex = new RegExp(pattern, regexFlags); + + // Find files to search + const filesToSearch = await findFilesToSearch(absolutePath, include, max_files); + + if (filesToSearch.length === 0) { + const message = `No files found to search in ${searchPath}${include ? ` matching ${include}` : ''}`; + return createSuccessResponse({ + pattern, + search_path: searchPath, + include_pattern: include, + files_searched: 0, + matches: [], + total_matches: 0, + message + }); + } + + // Search in files + const allMatches: GrepMatch[] = []; + let filesSearched = 0; + let filesWithMatches = 0; + + for (const file of filesToSearch) { + if (allMatches.length >= max_matches) { + break; + } + + const fileMatches = await searchInFile(file, regex, max_matches - allMatches.length); + if (fileMatches.length > 0) { + // Convert absolute paths to relative paths for output + const relativePath = path.relative(absolutePath, file); + fileMatches.forEach(match => { + match.filePath = relativePath; + }); + + allMatches.push(...fileMatches); + filesWithMatches++; + } + filesSearched++; + } + + // Format results + let summary = `Found ${allMatches.length} match(es) for "${pattern}" in ${filesWithMatches} file(s)`; + if (filesSearched < filesToSearch.length) { + summary += ` (searched ${filesSearched}/${filesToSearch.length} files)`; + } + + // Group matches by file for better readability + const matchesByFile: Record = {}; + allMatches.forEach(match => { + if (!matchesByFile[match.filePath]) { + matchesByFile[match.filePath] = []; + } + matchesByFile[match.filePath].push(match); + }); + + console.log(summary); + + return createSuccessResponse({ + pattern, + search_path: searchPath, + include_pattern: include, + files_searched: filesSearched, + files_with_matches: filesWithMatches, + matches: allMatches, + matches_by_file: matchesByFile, + total_matches: allMatches.length, + summary, + truncated: allMatches.length >= max_matches + }); + + } catch (error) { + return handleToolError(error, 'Grep tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/ls-tool.ts b/src/tools/ls-tool.ts new file mode 100644 index 0000000..4a3e477 --- /dev/null +++ b/src/tools/ls-tool.ts @@ -0,0 +1,227 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateDirectoryExists, + ToolResponse +} from './tool-utils'; + +const lsParametersSchema = z.object({ + path: z.string().optional().describe('Path to the directory to list (relative to workspace root, or absolute path within workspace). Defaults to workspace root.'), + show_hidden: z.boolean().optional().describe('Whether to show hidden files and directories (starting with .)'), + ignore: z.array(z.string()).optional().describe('Array of glob patterns to ignore (e.g., ["*.log", "temp*"])'), + detailed: z.boolean().optional().describe('Whether to show detailed file information (size, modified time)') +}); + +interface FileEntry { + name: string; + isDirectory: boolean; + size: number; + modifiedTime: Date; + extension?: string; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Check if a filename should be ignored based on patterns + */ +function shouldIgnore(filename: string, patterns?: string[]): boolean { + if (!patterns || patterns.length === 0) { + return false; + } + + for (const pattern of patterns) { + // Convert glob pattern to RegExp (simplified version) + const regexPattern = pattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape special regex chars + .replace(/\*/g, '.*') // * becomes .* + .replace(/\?/g, '.'); // ? becomes . + + const regex = new RegExp(`^${regexPattern}$`); + if (regex.test(filename)) { + return true; + } + } + + return false; +} + +/** + * Format file size in human-readable format + */ +function formatFileSize(bytes: number): string { + if (bytes === 0) { + return '0 B'; + } + + const units = ['B', 'KB', 'MB', 'GB', 'TB']; + const k = 1024; + const i = Math.floor(Math.log(bytes) / Math.log(k)); + + return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${units[i]}`; +} + +/** + * Format modified time in relative format + */ +function formatModifiedTime(date: Date): string { + const now = new Date(); + const diffMs = now.getTime() - date.getTime(); + const diffMinutes = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + const diffDays = Math.floor(diffMs / 86400000); + + if (diffMinutes < 1) { + return 'just now'; + } + if (diffMinutes < 60) { + return `${diffMinutes}m ago`; + } + if (diffHours < 24) { + return `${diffHours}h ago`; + } + if (diffDays < 7) { + return `${diffDays}d ago`; + } + + return date.toLocaleDateString(); +} + +export function createLsTool(context: ExecutionContext) { + return tool({ + description: 'List the contents of a directory in the SuperDesign workspace. Shows files and subdirectories with optional filtering.', + parameters: lsParametersSchema, + execute: async (params): Promise => { + try { + const { path: targetPath = '.', show_hidden = false, ignore, detailed = false } = params; + + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(targetPath, context); + if (pathError) { + return pathError; + } + + // Resolve target directory + const absolutePath = resolveWorkspacePath(targetPath, context); + + console.log(`Listing directory: ${targetPath}`); + + // Check if path exists and is a directory + const dirError = validateDirectoryExists(absolutePath, targetPath); + if (dirError) { + return dirError; + } + + // Read directory contents + const files = fs.readdirSync(absolutePath); + + if (files.length === 0) { + console.log(`Directory is empty: ${targetPath}`); + return createSuccessResponse({ + path: targetPath, + absolute_path: absolutePath, + entries: [], + total_count: 0 + }); + } + + const entries: FileEntry[] = []; + let hiddenCount = 0; + let ignoredCount = 0; + + // Process each file/directory + for (const file of files) { + // Skip hidden files unless requested + if (!show_hidden && file.startsWith('.')) { + hiddenCount++; + continue; + } + + // Check ignore patterns + if (shouldIgnore(file, ignore)) { + ignoredCount++; + continue; + } + + const fullPath = path.join(absolutePath, file); + + try { + const fileStats = fs.statSync(fullPath); + const isDir = fileStats.isDirectory(); + + const entry: FileEntry = { + name: file, + isDirectory: isDir, + size: isDir ? 0 : fileStats.size, + modifiedTime: fileStats.mtime, + extension: isDir ? undefined : path.extname(file).slice(1) + }; + + entries.push(entry); + } catch (error) { + // Log error but continue with other files + console.log(`Error accessing ${file}: ${error instanceof Error ? error.message : String(error)}`); + } + } + + // Sort entries (directories first, then alphabetically) + entries.sort((a, b) => { + if (a.isDirectory && !b.isDirectory) { + return -1; + } + if (!a.isDirectory && b.isDirectory) { + return 1; + } + return a.name.localeCompare(b.name); + }); + + // Create formatted output + let summary = `Listed ${entries.length} item(s) in ${targetPath}`; + if (hiddenCount > 0) { + summary += ` (${hiddenCount} hidden)`; + } + if (ignoredCount > 0) { + summary += ` (${ignoredCount} ignored)`; + } + + // Create detailed listing if requested + let detailedListing = ''; + if (detailed && entries.length > 0) { + detailedListing = '\n\nDetailed listing:\n'; + detailedListing += entries.map(entry => { + const type = entry.isDirectory ? '[DIR]' : '[FILE]'; + const size = entry.isDirectory ? '' : ` ${formatFileSize(entry.size)}`; + const modified = ` ${formatModifiedTime(entry.modifiedTime)}`; + const ext = entry.extension ? ` .${entry.extension}` : ''; + return `${type} ${entry.name}${size}${modified}${ext}`; + }).join('\n'); + } + + console.log(`${summary}${detailedListing}`); + + return createSuccessResponse({ + path: targetPath, + absolute_path: absolutePath, + entries, + total_count: entries.length, + hidden_count: hiddenCount, + ignored_count: ignoredCount, + directories: entries.filter(e => e.isDirectory).length, + files: entries.filter(e => !e.isDirectory).length, + summary, + detailed_listing: detailed ? detailedListing : undefined + }); + + } catch (error) { + return handleToolError(error, 'Ls tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/multiedit-tool.ts b/src/tools/multiedit-tool.ts new file mode 100644 index 0000000..ede24a6 --- /dev/null +++ b/src/tools/multiedit-tool.ts @@ -0,0 +1,183 @@ +import { z } from 'zod'; +import { tool } from 'ai'; +import * as fs from 'fs'; +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateFileExists, + ToolResponse +} from './tool-utils'; + +const singleEditSchema = z.object({ + old_string: z.string().describe('The exact text to find and replace. Must match exactly including whitespace.'), + new_string: z.string().describe('The text to replace old_string with.'), + expected_replacements: z.number().min(1).optional().describe('Number of replacements expected (default: 1)') +}); + +const multieditParametersSchema = z.object({ + file_path: z.string().describe('Path to the file to edit (relative to workspace root, or absolute path within workspace)'), + edits: z.array(singleEditSchema).min(1).describe('Array of edit operations to perform in sequence'), + fail_fast: z.boolean().optional().describe('Whether to stop on first error (true) or continue with remaining edits (false, default)') +}); + +interface SingleEdit { + old_string: string; + new_string: string; + expected_replacements?: number; +} + +interface EditResult { + edit: SingleEdit; + success: boolean; + occurrences: number; + error?: string; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Escape special regex characters + */ +function escapeRegExp(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); +} + +/** + * Apply a single edit to content + */ +function applySingleEdit(content: string, edit: SingleEdit): EditResult { + const expectedReplacements = edit.expected_replacements ?? 1; + + // Count occurrences + const regex = new RegExp(escapeRegExp(edit.old_string), 'g'); + const matches = content.match(regex) || []; + const occurrences = matches.length; + + // Validate occurrence count + if (occurrences === 0) { + return { + edit, + success: false, + occurrences: 0, + error: `Text not found: "${edit.old_string.substring(0, 50)}${edit.old_string.length > 50 ? '...' : ''}"` + }; + } + + if (occurrences !== expectedReplacements) { + return { + edit, + success: false, + occurrences, + error: `Expected ${expectedReplacements} replacement(s) but found ${occurrences} occurrence(s)` + }; + } + + return { + edit, + success: true, + occurrences + }; +} + +export function createMultieditTool(context: ExecutionContext) { + return tool({ + description: 'Perform multiple find-and-replace operations on a single file in sequence. Each edit is applied to the result of the previous edit. Accepts both relative and absolute file paths within the workspace.', + parameters: multieditParametersSchema, + execute: async (params): Promise => { + try { + const { file_path, edits, fail_fast = true } = params; + + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(file_path, context); + if (pathError) { + return pathError; + } + + // Resolve path + const absolutePath = resolveWorkspacePath(file_path, context); + + // Check if file exists + const fileError = validateFileExists(absolutePath, file_path); + if (fileError) { + return fileError; + } + + console.log(`Performing ${edits.length} edit(s) on: ${file_path}`); + + // Read current content + let currentContent: string; + try { + currentContent = fs.readFileSync(absolutePath, 'utf8'); + // Normalize line endings to LF + currentContent = currentContent.replace(/\r\n/g, '\n'); + } catch (error) { + return handleToolError(error, 'Failed to read file', 'permission'); + } + + const originalContent = currentContent; + const editResults: EditResult[] = []; + let successCount = 0; + let totalReplacements = 0; + + // Apply edits sequentially + for (let i = 0; i < edits.length; i++) { + const edit = edits[i]; + + console.log(`Applying edit ${i + 1}/${edits.length}: "${edit.old_string.substring(0, 30)}..." => "${edit.new_string.substring(0, 30)}..."`); + + const editResult = applySingleEdit(currentContent, edit); + editResults.push(editResult); + + if (editResult.success) { + // Apply the edit + currentContent = currentContent.split(edit.old_string).join(edit.new_string); + successCount++; + totalReplacements += editResult.occurrences; + console.log(`✓ Edit ${i + 1} successful: ${editResult.occurrences} replacement(s)`); + } else { + console.log(`✗ Edit ${i + 1} failed: ${editResult.error}`); + + if (fail_fast) { + return handleToolError( + `Edit operation failed at step ${i + 1}: ${editResult.error}`, + 'Edit sequence', + 'execution' + ); + } + } + } + + // Write the updated content if any edits were successful + if (successCount > 0) { + fs.writeFileSync(absolutePath, currentContent, 'utf8'); + } + + const newLines = currentContent.split('\n').length; + const newSize = Buffer.byteLength(currentContent, 'utf8'); + const hasErrors = editResults.some(r => !r.success); + + console.log(`Multi-edit completed: ${successCount}/${edits.length} edits successful, ${totalReplacements} total replacements`); + + return createSuccessResponse({ + file_path, + absolute_path: absolutePath, + edits_total: edits.length, + edits_successful: successCount, + edits_failed: edits.length - successCount, + total_replacements: totalReplacements, + lines_total: newLines, + bytes_total: newSize, + content_changed: currentContent !== originalContent, + edit_results: editResults + }); + + } catch (error) { + return handleToolError(error, 'Multiedit tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/read-tool.ts b/src/tools/read-tool.ts new file mode 100644 index 0000000..8dd9eb3 --- /dev/null +++ b/src/tools/read-tool.ts @@ -0,0 +1,301 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import * as mime from 'mime-types'; +import { tool } from 'ai'; +import { z } from 'zod'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateFileExists, + ToolResponse +} from './tool-utils'; + +/** + * File read result with metadata + */ +export interface FileReadResult { + content: string; + filePath: string; + fileType: 'text' | 'image' | 'pdf' | 'binary'; + mimeType?: string; + lineCount?: number; + isTruncated?: boolean; + linesShown?: [number, number]; // [startLine, endLine] + size: number; +} + +// Constants for file processing +const DEFAULT_MAX_LINES = 1000; +const MAX_LINE_LENGTH = 2000; +const MAX_FILE_SIZE_BYTES = 10 * 1024 * 1024; // 10MB + +// Path validation is now handled by validateWorkspacePath in tool-utils + + /** + * Check if a file is likely binary by sampling content + */ +function isBinaryFile(filePath: string): boolean { + try { + const fd = fs.openSync(filePath, 'r'); + const fileSize = fs.fstatSync(fd).size; + + if (fileSize === 0) { + fs.closeSync(fd); + return false; + } + + const bufferSize = Math.min(4096, fileSize); + const buffer = Buffer.alloc(bufferSize); + const bytesRead = fs.readSync(fd, buffer, 0, buffer.length, 0); + fs.closeSync(fd); + + if (bytesRead === 0) {return false;} + + // Check for null bytes (strong binary indicator) + for (let i = 0; i < bytesRead; i++) { + if (buffer[i] === 0) { + return true; + } + } + + // Count non-printable characters + let nonPrintableCount = 0; + for (let i = 0; i < bytesRead; i++) { + if (buffer[i] < 9 || (buffer[i] > 13 && buffer[i] < 32)) { + nonPrintableCount++; + } + } + + // If >30% non-printable characters, consider binary + return nonPrintableCount / bytesRead > 0.3; + } catch { + return false; + } + } + + /** + * Detect file type based on extension and content + */ +function detectFileType(filePath: string): 'text' | 'image' | 'pdf' | 'binary' { + const ext = path.extname(filePath).toLowerCase(); + const mimeType = mime.lookup(filePath); + + // Check for images + if (mimeType && mimeType.startsWith('image/')) { + return 'image'; + } + + // Check for PDF + if (mimeType === 'application/pdf') { + return 'pdf'; + } + + // Known binary extensions + const binaryExtensions = [ + '.exe', '.dll', '.so', '.dylib', '.zip', '.tar', '.gz', '.7z', + '.bin', '.dat', '.class', '.jar', '.war', '.pyc', '.pyo', + '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', + '.odt', '.ods', '.odp', '.wasm', '.obj', '.o', '.a', '.lib' + ]; + + if (binaryExtensions.includes(ext)) { + return 'binary'; + } + + // Content-based binary detection + if (isBinaryFile(filePath)) { + return 'binary'; + } + + return 'text'; + } + + /** + * Process text file content with line range support + */ +async function processTextFile( + filePath: string, + startLine?: number, + lineCount?: number, + encoding: string = 'utf-8' + ): Promise<{ content: string; metadata: Partial }> { + const content = await fs.promises.readFile(filePath, encoding as BufferEncoding); + const lines = content.split('\n'); + const originalLineCount = lines.length; + + // Handle line range + const actualStartLine = Math.max((startLine || 1) - 1, 0); // Convert to 0-based + const actualLineCount = lineCount || Math.min(DEFAULT_MAX_LINES, originalLineCount); + const endLine = Math.min(actualStartLine + actualLineCount, originalLineCount); + + const selectedLines = lines.slice(actualStartLine, endLine); + + // Truncate long lines + let linesWereTruncated = false; + const processedLines = selectedLines.map(line => { + if (line.length > MAX_LINE_LENGTH) { + linesWereTruncated = true; + return line.substring(0, MAX_LINE_LENGTH) + '... [line truncated]'; + } + return line; + }); + + const contentWasTruncated = endLine < originalLineCount; + const isTruncated = contentWasTruncated || linesWereTruncated; + + let processedContent = processedLines.join('\n'); + + // Add truncation notice + if (contentWasTruncated) { + processedContent = `[Content truncated: showing lines ${actualStartLine + 1}-${endLine} of ${originalLineCount} total lines]\n\n` + processedContent; + } else if (linesWereTruncated) { + processedContent = `[Some lines truncated due to length (max ${MAX_LINE_LENGTH} chars)]\n\n` + processedContent; + } + + return { + content: processedContent, + metadata: { + lineCount: originalLineCount, + isTruncated, + linesShown: [actualStartLine + 1, endLine] + } + }; + } + + /** + * Process image or PDF file + */ +async function processMediaFile( + filePath: string, + fileType: 'image' | 'pdf' + ): Promise<{ content: string; metadata: Partial }> { + const buffer = await fs.promises.readFile(filePath); + const base64Data = buffer.toString('base64'); + const mimeType = mime.lookup(filePath) || 'application/octet-stream'; + + // For SuperDesign, we'll return a descriptive message rather than raw base64 + // The actual file handling would be done by the VS Code webview + const fileName = path.basename(filePath); + const fileSize = (buffer.length / 1024).toFixed(1); + + return { + content: `[${fileType.toUpperCase()} FILE: ${fileName}]\nFile size: ${fileSize} KB\nMIME type: ${mimeType}\nBase64 data available for webview display.`, + metadata: { + mimeType + } + }; + } + + /** + * Create SuperDesign read tool with execution context + */ +export function createReadTool(context: ExecutionContext) { + return tool({ + description: 'Read the contents of a file within the SuperDesign workspace. Supports text files, images (PNG, JPG, SVG, etc.), and handles large files with line-range reading.', + parameters: z.object({ + filePath: z.string().describe('Path to the file to read, relative to the workspace root or absolute path within workspace'), + startLine: z.number().optional().describe('Optional: Starting line number to read from (1-based). Use with lineCount for large files.'), + lineCount: z.number().optional().describe('Optional: Number of lines to read. Use with startLine to read specific sections.'), + encoding: z.string().optional().describe('Optional: File encoding (utf-8, ascii, etc.). Defaults to utf-8.') + }), + execute: async ({ filePath, startLine, lineCount, encoding }): Promise => { + const startTime = Date.now(); + + try { + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(filePath, context); + if (pathError) { + return pathError; + } + + // Resolve file path + const absolutePath = resolveWorkspacePath(filePath, context); + + // Check file existence + const fileError = validateFileExists(absolutePath, filePath); + if (fileError) { + return fileError; + } + + // Check if it's a directory + const stats = fs.statSync(absolutePath); + if (stats.isDirectory()) { + return handleToolError(`Path is a directory, not a file: ${filePath}`, 'Path validation', 'validation'); + } + + // Check file size + if (stats.size > MAX_FILE_SIZE_BYTES) { + const sizeMB = (stats.size / 1024 / 1024).toFixed(1); + return handleToolError( + `File too large (${sizeMB}MB). Maximum size: ${MAX_FILE_SIZE_BYTES / 1024 / 1024}MB`, + 'File size check', + 'validation' + ); + } + + // Detect file type + const fileType = detectFileType(absolutePath); + context.outputChannel.appendLine(`[read] Reading ${fileType} file: ${filePath} (${(stats.size / 1024).toFixed(1)} KB)`); + + let content: string; + let metadata: Partial = {}; + + // Process based on file type + switch (fileType) { + case 'text': { + const result = await processTextFile( + absolutePath, + startLine, + lineCount, + encoding + ); + content = result.content; + metadata = result.metadata; + break; + } + + case 'image': + case 'pdf': { + const result = await processMediaFile(absolutePath, fileType); + content = result.content; + metadata = result.metadata; + break; + } + + case 'binary': { + const fileName = path.basename(absolutePath); + const fileSize = (stats.size / 1024).toFixed(1); + content = `[BINARY FILE: ${fileName}]\nFile size: ${fileSize} KB\nCannot display binary content as text.`; + break; + } + + default: + return handleToolError(`Unsupported file type: ${fileType}`, 'File type detection', 'validation'); + } + + // Create result + const fileReadResult: FileReadResult = { + content, + filePath, + fileType, + mimeType: mime.lookup(absolutePath) || undefined, + size: stats.size, + ...metadata + }; + + const duration = Date.now() - startTime; + context.outputChannel.appendLine(`[read] File read completed in ${duration}ms`); + + return createSuccessResponse(fileReadResult); + + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + context.outputChannel.appendLine(`[read] Read failed: ${errorMessage}`); + return handleToolError(error, 'Read tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/theme-tool.ts b/src/tools/theme-tool.ts new file mode 100644 index 0000000..265b1ee --- /dev/null +++ b/src/tools/theme-tool.ts @@ -0,0 +1,90 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { tool } from 'ai'; +import { z } from 'zod'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + validateFileExists, + ToolResponse +} from './tool-utils'; + + + +const themePrompt = `Design a perfect theme that including color, font, spacing, shadown, etc. +`; + +const cssSheetDescription = `The full css sheet content, has to include below classes: +:root selector - Must contain CSS custom properties +CSS custom properties format - --variable-name: value; +Semicolon-terminated - Each property must end with ; +--background, --foreground (basic colors) +--primary, --primary-foreground (brand colors) +--secondary, --muted, --accent (semantic colors) +--destructive, --border, --input, --ring (UI elements) +--card, --popover + their foreground variants +--chart-1 through --chart-5 (data visualization) +--sidebar-* variables for navigation +--font-sans, --font-serif, --font-mono +--radius, --spacing +--shadow-* variables (xs, sm, md, lg, xl, etc.) + +You can add more relevant ones based on use cases, but make sure to include all the above classes.`; + + /** + * Create SuperDesign read tool with execution context + */ +export function createThemeTool(context: ExecutionContext) { + return tool({ + description: themePrompt, + parameters: z.object({ + theme_name: z.string().describe('The name of the theme'), + reasoning_reference: z.string().describe('Think through the theme design to make it coherent and what reference you used'), + cssSheet: z.string().describe(cssSheetDescription), + cssFilePath: z.string().describe('Path to the css file to write to (relative to workspace root, or absolute path within workspace)'), + create_dirs: z.boolean().optional().default(true).describe('Whether to create parent directories if they don\'t exist (default: true)') + }), + execute: async ({ cssSheet, theme_name, reasoning_reference, cssFilePath, create_dirs = true}): Promise => { + + try { + // Validate and resolve the CSS file path + const validationError = validateWorkspacePath(cssFilePath, context); + if (validationError) { + return validationError; + } + + const resolvedPath = resolveWorkspacePath(cssFilePath, context); + + // Create parent directories if needed + if (create_dirs) { + const dirPath = path.dirname(resolvedPath); + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + context.outputChannel.appendLine(`[theme] Created directory: ${dirPath}`); + } + } + + // Write the CSS content to file + fs.writeFileSync(resolvedPath, cssSheet, 'utf8'); + + context.outputChannel.appendLine(`[theme] Successfully created theme "${theme_name}" at: ${resolvedPath}`); + + return createSuccessResponse({ + success: true, + message: `Theme "${theme_name}" saved successfully`, + filePath: resolvedPath, + theme_name, + reasoning_reference, + cssSheet + }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + context.outputChannel.appendLine(`[theme] Theme creation failed: ${errorMessage}`); + return handleToolError(error, 'Theme tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/tools/tool-utils.ts b/src/tools/tool-utils.ts new file mode 100644 index 0000000..d2bb317 --- /dev/null +++ b/src/tools/tool-utils.ts @@ -0,0 +1,181 @@ +import * as path from 'path'; +import { ExecutionContext } from '../types/agent'; + +/** + * Standard error response structure for all tools + */ +export interface ToolErrorResponse { + success: false; + error: string; + error_type?: 'validation' | 'security' | 'file_not_found' | 'permission' | 'execution' | 'unknown'; + details?: any; +} + +/** + * Standard success response structure for all tools + */ +export interface ToolSuccessResponse { + success: true; + [key: string]: any; +} + +export type ToolResponse = ToolSuccessResponse | ToolErrorResponse; + +/** + * Generic error handler that converts exceptions/errors to standardized error responses + */ +export function handleToolError( + error: unknown, + context?: string, + errorType: ToolErrorResponse['error_type'] = 'unknown' +): ToolErrorResponse { + let errorMessage: string; + let details: any; + + if (error instanceof Error) { + errorMessage = error.message; + details = { + name: error.name, + stack: error.stack?.split('\n').slice(0, 3) // Truncated stack trace + }; + } else if (typeof error === 'string') { + errorMessage = error; + } else { + errorMessage = 'An unknown error occurred'; + details = { originalError: error }; + } + + // Add context if provided + if (context) { + errorMessage = `${context}: ${errorMessage}`; + } + + console.error(`Tool error (${errorType}): ${errorMessage}`); + + return { + success: false, + error: errorMessage, + error_type: errorType, + details + }; +} + +/** + * Validate if a path is within the workspace directory (supports both absolute and relative paths) + */ +export function validateWorkspacePath(filePath: string, context: ExecutionContext): ToolErrorResponse | null { + try { + // Prevent directory traversal attacks + if (filePath.includes('..')) { + return handleToolError('Path cannot contain ".." for security reasons', 'Path validation', 'security'); + } + + const normalizedWorkspace = path.normalize(context.workingDirectory); + + // Handle both absolute and relative paths + let resolvedPath: string; + if (path.isAbsolute(filePath)) { + resolvedPath = path.normalize(filePath); + } else { + resolvedPath = path.resolve(context.workingDirectory, filePath); + } + + // Check if path is within workspace boundary + if (!resolvedPath.startsWith(normalizedWorkspace)) { + return handleToolError( + `Path must be within workspace directory: ${filePath}`, + 'Security check', + 'security' + ); + } + + return null; // No error + } catch (error) { + return handleToolError(error, 'Path validation', 'validation'); + } +} + +/** + * Safely resolve a file path (supports both absolute and relative paths) + */ +export function resolveWorkspacePath(filePath: string, context: ExecutionContext): string { + if (path.isAbsolute(filePath)) { + return path.normalize(filePath); + } else { + return path.resolve(context.workingDirectory, filePath); + } +} + +/** + * Create a success response + */ +export function createSuccessResponse(data: Record): ToolSuccessResponse { + return { + success: true, + ...data + }; +} + +/** + * Validation helper for required string parameters + */ +export function validateRequiredString(value: any, paramName: string): ToolErrorResponse | null { + if (!value || typeof value !== 'string' || value.trim() === '') { + return handleToolError( + `${paramName} is required and must be a non-empty string`, + 'Parameter validation', + 'validation' + ); + } + return null; +} + +/** + * Validation helper for file existence + */ +export function validateFileExists(absolutePath: string, filePath: string): ToolErrorResponse | null { + const fs = require('fs'); + + try { + if (!fs.existsSync(absolutePath)) { + return handleToolError( + `File not found: ${filePath}`, + 'File existence check', + 'file_not_found' + ); + } + return null; + } catch (error) { + return handleToolError(error, 'File existence check', 'permission'); + } +} + +/** + * Validation helper for directory existence + */ +export function validateDirectoryExists(absolutePath: string, dirPath: string): ToolErrorResponse | null { + const fs = require('fs'); + + try { + if (!fs.existsSync(absolutePath)) { + return handleToolError( + `Directory not found: ${dirPath}`, + 'Directory existence check', + 'file_not_found' + ); + } + + const stats = fs.statSync(absolutePath); + if (!stats.isDirectory()) { + return handleToolError( + `Path is not a directory: ${dirPath}`, + 'Directory validation', + 'validation' + ); + } + + return null; + } catch (error) { + return handleToolError(error, 'Directory validation', 'permission'); + } +} \ No newline at end of file diff --git a/src/tools/write-tool.ts b/src/tools/write-tool.ts new file mode 100644 index 0000000..dd6fbac --- /dev/null +++ b/src/tools/write-tool.ts @@ -0,0 +1,101 @@ +import * as fs from 'fs'; +import * as path from 'path'; +import { tool } from 'ai'; +import { z } from 'zod'; +import { ExecutionContext } from '../types/agent'; +import { + handleToolError, + validateWorkspacePath, + resolveWorkspacePath, + createSuccessResponse, + ToolResponse +} from './tool-utils'; + +/** + * Write tool result with metadata + */ +export interface WriteToolResult { + file_path: string; + absolute_path: string; + is_new_file: boolean; + lines_written: number; + bytes_written: number; +} + +// Path validation is now handled by validateWorkspacePath in tool-utils + +/** + * Create SuperDesign write tool with execution context + */ +export function createWriteTool(context: ExecutionContext) { + return tool({ + description: 'Write content to a file in the SuperDesign workspace. Creates parent directories if needed.', + parameters: z.object({ + file_path: z.string().describe('Path to the file to write to (relative to workspace root, or absolute path within workspace)'), + content: z.string().describe('Content to write to the file'), + create_dirs: z.boolean().optional().default(true).describe('Whether to create parent directories if they don\'t exist (default: true)') + }), + execute: async ({ file_path, content, create_dirs = true }): Promise => { + const startTime = Date.now(); + + try { + // Validate workspace path (handles both absolute and relative paths) + const pathError = validateWorkspacePath(file_path, context); + if (pathError) { + return pathError; + } + + // Resolve absolute path within workspace + const absolutePath = resolveWorkspacePath(file_path, context); + + context.outputChannel.appendLine(`[write] Writing to file: ${file_path}`); + + // Check if target is a directory + if (fs.existsSync(absolutePath)) { + const stats = fs.lstatSync(absolutePath); + if (stats.isDirectory()) { + return handleToolError(`Target path is a directory, not a file: ${file_path}`, 'Path validation', 'validation'); + } + } + + // Create parent directories if needed and requested + if (create_dirs) { + const dirName = path.dirname(absolutePath); + if (!fs.existsSync(dirName)) { + fs.mkdirSync(dirName, { recursive: true }); + context.outputChannel.appendLine(`[write] Created parent directories for: ${file_path}`); + } + } + + // Determine if this is a new file or overwrite + const isNewFile = !fs.existsSync(absolutePath); + + // Write the file + fs.writeFileSync(absolutePath, content, 'utf8'); + + const duration = Date.now() - startTime; + const lines = content.split('\n').length; + const size = Buffer.byteLength(content, 'utf8'); + + context.outputChannel.appendLine(`[write] ${isNewFile ? 'Created' : 'Updated'} file: ${file_path} (${lines} lines, ${size} bytes) in ${duration}ms`); + + const result: WriteToolResult = { + file_path, + absolute_path: absolutePath, + is_new_file: isNewFile, + lines_written: lines, + bytes_written: size + }; + + return createSuccessResponse(result); + + } catch (error) { + const duration = Date.now() - startTime; + const errorMessage = error instanceof Error ? error.message : String(error); + + context.outputChannel.appendLine(`[write] Error writing file: ${errorMessage} (${duration}ms)`); + return handleToolError(error, 'Write tool execution', 'execution'); + } + } + }); +} \ No newline at end of file diff --git a/src/types/agent.ts b/src/types/agent.ts new file mode 100644 index 0000000..4b6b509 --- /dev/null +++ b/src/types/agent.ts @@ -0,0 +1,22 @@ +import * as vscode from 'vscode'; +import { CoreMessage } from 'ai'; + +export interface AgentService { + query( + prompt?: string, + messages?: CoreMessage[], + options?: any, + abortController?: AbortController, + onMessage?: (message: any) => void + ): Promise; + + hasApiKey(): boolean; + isApiKeyAuthError(errorMessage: string): boolean; +} + +export interface ExecutionContext { + workingDirectory: string; + sessionId: string; + outputChannel: vscode.OutputChannel; + abortController?: AbortController; +} \ No newline at end of file diff --git a/src/types/context.ts b/src/types/context.ts new file mode 100644 index 0000000..dacf037 --- /dev/null +++ b/src/types/context.ts @@ -0,0 +1,46 @@ +export type WebviewLayout = 'sidebar' | 'panel'; + +export interface WebviewContext { + layout: WebviewLayout; + extensionUri: string; + logoUris?: { + cursor: string; + windsurf: string; + claudeCode: string; + lovable: string; + bolt: string; + }; +} + +export interface WebviewMessage { + command: string; + [key: string]: any; +} + +export interface ChatCommand extends WebviewMessage { + command: 'chatMessage'; + message: string; +} + +export interface ChatResponse extends WebviewMessage { + command: 'chatResponse'; + response: string; +} + +export interface ChatError extends WebviewMessage { + command: 'chatError'; + error: string; +} + +export interface StopChatCommand extends WebviewMessage { + command: 'stopChat'; +} + +export interface ChatStopped extends WebviewMessage { + command: 'chatStopped'; +} + +export interface InitContext extends WebviewMessage { + command: 'initContext'; + context: WebviewContext; +} \ No newline at end of file diff --git a/src/webview/App.css b/src/webview/App.css new file mode 100644 index 0000000..a814f74 --- /dev/null +++ b/src/webview/App.css @@ -0,0 +1,1586 @@ +/* Main App Container */ +.superdesign-app { + width: 100%; + height: 100vh; + display: flex; + flex-direction: column; + background: var(--vscode-sideBar-background); + color: var(--vscode-sideBar-foreground); + border-right: 1px solid var(--vscode-sideBar-border); + font-family: var(--vscode-font-family); + overflow: hidden; + padding: 0; + margin: 0; +} + +/* App container variants for different contexts */ +.superdesign-app.chat-view { + background: var(--vscode-sideBar-background); + color: var(--vscode-sideBar-foreground); + border-right: 1px solid var(--vscode-sideBar-border); +} + +.superdesign-app.chat-view.panel-layout { + background: var(--vscode-panel-background); + color: var(--vscode-panel-foreground); + border-right: 1px solid var(--vscode-panel-border); +} + +.superdesign-app.canvas-view { + background: var(--vscode-panel-background); + color: var(--vscode-panel-foreground); + border-right: 1px solid var(--vscode-panel-border); +} + +/* App Header */ +.app-header { + padding: 20px 24px 16px 24px; + border-bottom: 1px solid var(--vscode-panel-border); + background: var(--vscode-sideBar-background); +} + +.app-header h1 { + margin: 0 0 8px 0; + font-size: 1.5rem; + font-weight: 600; + color: var(--vscode-foreground); +} + +.app-header p { + margin: 0; + color: var(--vscode-descriptionForeground); + font-size: 0.9rem; +} + +/* Chat Panel */ +.chat-panel { + flex: 1; + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +.chat-header { + padding: 24px 24px 16px 24px; + border-bottom: 1px solid var(--vscode-panel-border); +} + +.chat-header h2 { + margin: 0 0 8px 0; + font-size: 1.2rem; + font-weight: 500; + color: var(--vscode-foreground); +} + +.chat-header p { + margin: 0; + color: var(--vscode-descriptionForeground); + font-size: 0.9rem; +} + +/* Chat Container */ +.chat-container { + flex: 1; + display: flex; + flex-direction: column; + height: 100%; + overflow: hidden; +} + +/* Chat History */ +.chat-history { + flex: 1; + overflow-y: auto; + padding: 20px 24px; + display: flex; + flex-direction: column; + gap: 16px; + scroll-behavior: smooth; +} + +.chat-placeholder { + text-align: center; + padding: 40px 20px; + color: var(--vscode-descriptionForeground); +} + +.chat-placeholder p { + margin: 0 0 16px 0; + font-size: 1rem; +} + +.chat-placeholder p:first-child { + font-size: 1.1rem; + font-weight: 500; + color: var(--vscode-foreground); +} + +.chat-placeholder ul { + text-align: left; + max-width: 300px; + margin: 0 auto; + padding-left: 20px; +} + +.chat-placeholder li { + margin: 8px 0; + line-height: 1.4; +} + +/* Chat Messages */ +.chat-message { + max-width: 85%; + margin-bottom: 4px; +} + +.chat-message.user { + align-self: flex-end; +} + +.chat-message.assistant { + align-self: flex-start; +} + +.message-header { + margin-bottom: 6px; +} + +.message-header strong { + font-size: 0.85rem; + color: var(--vscode-descriptionForeground); + font-weight: 500; +} + +.message-content { + padding: 12px 16px; + border-radius: 12px; + line-height: 1.5; + font-size: 0.95rem; + word-wrap: break-word; + white-space: pre-wrap; +} + +.chat-message.user .message-content { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border-bottom-right-radius: 4px; +} + +.chat-message.assistant .message-content { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + color: var(--vscode-foreground); + border-bottom-left-radius: 4px; +} + +.message-content.typing { + font-style: italic; + opacity: 0.8; +} + +.typing-indicator { + animation: pulse 1.5s ease-in-out infinite; +} + +@keyframes pulse { + 0%, 100% { opacity: 0.4; } + 50% { opacity: 1; } +} + +/* Chat Input Container */ +.chat-input-container { + border-top: 1px solid var(--vscode-panel-border); + background: var(--vscode-sideBar-background); + padding: 16px 24px 24px 24px; +} + +/* Quick Suggestions */ +.quick-suggestions { + display: flex; + gap: 8px; + margin-bottom: 16px; + flex-wrap: wrap; +} + +.suggestion-btn { + background: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + border: 1px solid var(--vscode-input-border); + padding: 8px 12px; + border-radius: 20px; + cursor: pointer; + font-size: 0.8rem; + font-weight: 500; + transition: all 0.2s ease; + white-space: nowrap; +} + +.suggestion-btn:hover:not(:disabled) { + background: var(--vscode-button-secondaryHoverBackground); + border-color: var(--vscode-focusBorder); + transform: translateY(-1px); +} + +.suggestion-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +/* Chat Input */ +.chat-input { + display: flex; + gap: 12px; + align-items: flex-end; +} + +.message-input { + flex: 1; + padding: 12px 16px; + background: var(--vscode-input-background); + border: 2px solid var(--vscode-input-border); + border-radius: 24px; + color: var(--vscode-input-foreground); + font-family: var(--vscode-font-family); + font-size: 0.95rem; + resize: none; + outline: none; + transition: border-color 0.2s ease; +} + +.message-input:focus { + border-color: var(--vscode-focusBorder); +} + +.message-input::placeholder { + color: var(--vscode-input-placeholderForeground); +} + +.send-btn { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + padding: 12px 16px; + border-radius: 50%; + cursor: pointer; + font-size: 1rem; + transition: all 0.2s ease; + min-width: 48px; + height: 48px; + display: flex; + align-items: center; + justify-content: center; +} + +.send-btn:hover:not(:disabled) { + background: var(--vscode-button-hoverBackground); + transform: scale(1.05); +} + +.send-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} + +/* Scrollbar Styling */ +.chat-history::-webkit-scrollbar { + width: 8px; +} + +.chat-history::-webkit-scrollbar-track { + background: var(--vscode-scrollbarSlider-background); + border-radius: 4px; +} + +.chat-history::-webkit-scrollbar-thumb { + background: var(--vscode-scrollbarSlider-hoverBackground); + border-radius: 4px; +} + +.chat-history::-webkit-scrollbar-thumb:hover { + background: var(--vscode-scrollbarSlider-activeBackground); +} + +/* Responsive Design */ +@media (max-width: 768px) { + .chat-history { + padding: 16px; + } + + .chat-input-container { + padding: 12px 16px; + } + + .chat-message { + max-width: 95%; + } +} + +/* Colors Panel */ +.colors-panel h3 { + margin: 0 0 20px 0; + color: var(--vscode-foreground); +} + +.chat-header p { + margin: 0; + color: var(--vscode-descriptionForeground); + font-size: 0.9rem; +} + +.control-btn, .viewport-btn { + background-color: transparent; + border: none; + color: #ffffff; + padding: 0; + border-radius: 5px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + transition: background-color 0.15s ease; +} + +.control-btn:hover, .viewport-btn:hover { + background-color: #3f3f3f; +} + +.control-btn:disabled, .viewport-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + background-color: transparent; +} + +.control-btn.active, +.viewport-btn.active, +.viewport-toggle.active { + background-color: #4f4f4f; +} + +.viewport-divider { + width: 1px; + height: 24px; + background-color: #4a4a4a; + margin: 0 4px; +} + +.zoom-indicator { + padding: 0 10px; + background-color: transparent; + border-radius: 4px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + min-width: 45px; + font-weight: 500; +} + +.viewport-controls { + display: flex; + align-items: center; + background-color: #1e1e1e; + border-radius: 5px; + padding: 2px; +} + +.viewport-controls .control-btn { + width: 30px; + height: 30px; +} + +.canvas-info { + position: absolute; + top: 12px; + right: 12px; + background-color: #2c2c2c; + color: #ffffff; + padding: 8px 14px; + border-radius: 8px; + font-size: 12px; + z-index: 100; + white-space: nowrap; + box-shadow: 0 4px 12px rgba(0,0,0,0.25); +} + +/* Transform Wrapper */ +.canvas-transform-wrapper { + flex: 1; + overflow: hidden; + cursor: grab; +} + +.canvas-transform-wrapper:active { + cursor: grabbing; +} + +.canvas-transform-content { + position: relative; + /* Grid background is handled by canvas-grid */ +} + +/* Canvas Grid */ +.canvas-grid { + position: relative; + min-width: 10000px; + min-height: 10000px; + user-select: none; + /* Always show subtle grid background */ + background-image: + linear-gradient(90deg, rgba(var(--vscode-panel-border-rgb, 128, 128, 128), 0.3) 1px, transparent 1px), + linear-gradient(rgba(var(--vscode-panel-border-rgb, 128, 128, 128), 0.3) 1px, transparent 1px); + background-size: 50px 50px; + background-position: 0 0, 0 0; + /* Fallback for browsers that don't support CSS custom property RGB values */ + background: + linear-gradient(90deg, var(--vscode-panel-border) 1px, transparent 1px), + linear-gradient(var(--vscode-panel-border) 1px, transparent 1px); + background-size: 50px 50px; +} + +.canvas-grid.dragging { + /* Enhance grid visibility during dragging with smaller, more visible grid */ + background-image: + linear-gradient(90deg, var(--vscode-focusBorder) 1px, transparent 1px), + linear-gradient(var(--vscode-focusBorder) 1px, transparent 1px); + background-size: 25px 25px; + background-position: 0 0, 0 0; +} + +/* Design Frames */ +.design-frame { + background: var(--vscode-editor-background); + border: 2px solid var(--vscode-input-border); + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.design-frame:hover { + border-color: var(--vscode-focusBorder); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15); +} + +.design-frame.selected { + border-color: var(--vscode-button-background); + box-shadow: 0 0 0 2px var(--vscode-button-background); +} + +.design-frame.dragging { + border-color: var(--vscode-focusBorder); + box-shadow: 0 8px 25px rgba(0, 0, 0, 0.3); + transform: scale(1.02); + transition: none; +} + +.frame-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px; + background: var(--vscode-sideBar-background); + border-bottom: 1px solid var(--vscode-panel-border); + border-radius: 6px 6px 0 0; + height: 50px; + box-sizing: border-box; + gap: 8px; + user-select: none; +} + +.frame-title { + font-size: 12px; + font-weight: 500; + color: var(--vscode-foreground); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.frame-meta { + display: flex; + align-items: center; + gap: 8px; + flex-shrink: 0; +} + +.frame-size { + font-size: 10px; + color: var(--vscode-descriptionForeground); + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + padding: 2px 6px; + border-radius: 10px; + font-weight: 500; +} + +.frame-dimensions { + font-size: 9px; + color: var(--vscode-descriptionForeground); + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + padding: 1px 4px; + border-radius: 3px; + font-family: var(--vscode-editor-font-family); +} + +/* Frame Viewport Controls */ +.frame-viewport-controls { + display: flex; + align-items: center; + gap: 2px; + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 4px; + padding: 1px; +} + +.frame-viewport-btn { + background: transparent; + border: none; + padding: 2px 4px; + border-radius: 3px; + cursor: pointer; + font-size: 10px; + transition: background-color 0.2s ease; + color: var(--vscode-foreground); + line-height: 1; +} + +.frame-viewport-btn:hover { + background: var(--vscode-list-hoverBackground); +} + +.frame-viewport-btn.active { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); +} + +/* Frame Viewport Indicator */ +.frame-viewport-indicator { + display: flex; + align-items: center; + gap: 4px; + font-size: 10px; + color: var(--vscode-descriptionForeground); +} + +.global-indicator { + font-size: 8px; + opacity: 0.7; +} + +.viewport-icon { + font-size: 10px; +} + +.frame-status { + font-size: 8px; + line-height: 1; +} + +.frame-status.loading { + color: var(--vscode-progressBar-background); + animation: pulse 1.5s ease-in-out infinite; +} + +.frame-status.error { + color: var(--vscode-errorForeground); +} + +.frame-status.loaded { + color: var(--vscode-charts-green); +} + +@keyframes pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.5; } +} + +/* Viewport Controls */ +.viewport-toggle.active { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); +} + +.frame-content { + position: relative; + height: calc(100% - 50px); + padding: 0; + overflow: hidden; + background: white; +} + +.frame-placeholder { + width: 100%; + height: 100%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + background: #ffffff; + color: #333; + padding: 20px; + text-align: center; + border-radius: 0 0 6px 6px; +} + +.placeholder-icon { + font-size: 32px; + margin-bottom: 12px; + opacity: 0.6; +} + +.placeholder-name { + margin: 0 0 8px 0; + font-size: 13px; + font-weight: 500; + color: var(--vscode-foreground); +} + +.placeholder-meta { + display: flex; + flex-direction: column; + gap: 2px; + margin-bottom: 8px; +} + +.placeholder-meta span { + font-size: 11px; + color: var(--vscode-descriptionForeground); +} + +.placeholder-meta .file-type { + font-size: 9px; + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + padding: 2px 6px; + border-radius: 10px; + font-weight: 500; + text-transform: uppercase; +} + +.placeholder-hint { + font-size: 10px; + color: var(--vscode-descriptionForeground); + font-style: italic; + opacity: 0.8; +} + +/* Frame Loading & Error Overlays */ +.frame-loading-overlay, +.frame-error-overlay, +.frame-drag-overlay { + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10; +} + +.frame-loading-overlay, +.frame-error-overlay { + display: flex; + justify-content: center; + align-items: center; + backdrop-filter: blur(2px); +} + +.frame-loading-overlay, +.frame-error-overlay { + background: rgba(255, 255, 255, 0.9); +} + +.frame-drag-overlay { + position: absolute; + top: 0; + left: 0; + background: transparent; + cursor: grab; + z-index: 5; /* Lower than loading/error overlays but higher than iframe */ +} + +.design-frame.dragging .frame-drag-overlay { + cursor: grabbing; +} + +.drag-ready-hint { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--vscode-editor-background); + border: 2px solid var(--vscode-focusBorder); + border-radius: 8px; + padding: 8px 12px; + text-align: center; + color: var(--vscode-foreground); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + opacity: 0; + animation: dragHint 0.3s ease-out forwards; + pointer-events: none; +} + +.drag-ready-hint span { + font-size: 16px; + display: block; + margin-bottom: 4px; +} + +.drag-ready-hint p { + margin: 0; + font-size: 10px; + font-weight: 500; +} + +@keyframes dragHint { + 0% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8); + } + 100% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +.design-frame:hover .frame-drag-overlay { + cursor: grabbing; +} + +.frame-loading-spinner { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + color: var(--vscode-foreground); +} + +.font-sample { + padding: 12px; + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); +} + +.frame-error-content span { + font-size: 24px; + display: block; + margin-bottom: 8px; +} + +.frame-error-content p { + margin: 0 0 4px 0; + font-size: 12px; + font-weight: 500; +} + +.font-sample.body { + font-size: 1rem; +} + +.suggestion-btn { + background: var(--vscode-button-secondaryBackground); + color: var(--vscode-button-secondaryForeground); + border: 1px solid var(--vscode-input-border); + padding: 8px 12px; + border-radius: 20px; + cursor: pointer; + font-size: 0.8rem; + font-weight: 500; + transition: all 0.2s ease; + white-space: nowrap; +} + +.suggestion-btn:hover:not(:disabled) { + background: var(--vscode-button-secondaryHoverBackground); + border-color: var(--vscode-focusBorder); + transform: translateY(-1px); +} + +/* Loading States */ +.canvas-loading { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background: var(--vscode-editor-background); +} + +.loading-spinner { + text-align: center; + color: var(--vscode-foreground); +} + +.spinner { + width: 32px; + height: 32px; + border: 3px solid var(--vscode-input-border); + border-top: 3px solid var(--vscode-button-background); + border-radius: 50%; + animation: spin 1s linear infinite; + margin: 0 auto 16px; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Error States */ +.canvas-error { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background: var(--vscode-editor-background); +} + +.error-message { + text-align: center; + padding: 32px; + background: var(--vscode-sideBar-background); + border-radius: 8px; + border: 1px solid var(--vscode-panel-border); + max-width: 400px; +} + +.error-message h3 { + margin: 0 0 12px 0; + color: var(--vscode-errorForeground); +} + +.error-message p { + margin: 0 0 20px 0; + color: var(--vscode-foreground); +} + +.error-message button { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; +} + +/* Empty States */ +.canvas-empty { + display: flex; + justify-content: center; + align-items: center; + height: 100vh; + background: var(--vscode-editor-background); +} + +.empty-state { + text-align: center; + color: var(--vscode-descriptionForeground); +} + +.empty-state h3 { + margin: 0 0 12px 0; + color: var(--vscode-foreground); +} + +.empty-state code { + background: var(--vscode-textCodeBlock-background); + padding: 2px 6px; + border-radius: 3px; + font-family: var(--vscode-editor-font-family); +} + +/* Design Panel Styles */ +.design-panel { + flex: 1; + display: flex; + flex-direction: column; + height: 100%; +} + +.tab-nav { + display: flex; + background: var(--vscode-sideBar-background); + border-bottom: 1px solid var(--vscode-panel-border); +} + +.tab { + flex: 1; + padding: 12px 16px; + background: transparent; + border: none; + color: var(--vscode-foreground); + cursor: pointer; + font-size: 0.9rem; + transition: background-color 0.2s ease; +} + +.tab:hover { + background: var(--vscode-list-hoverBackground); +} + +.tab.active { + background: var(--vscode-tab-activeBackground); + border-bottom: 2px solid var(--vscode-button-background); +} + +.tab-content { + flex: 1; + padding: 24px; + overflow-y: auto; +} + +.colors-panel h3, +.typography-panel h3, +.components-panel h3 { + margin: 0 0 20px 0; + color: var(--vscode-foreground); +} + +.color-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 16px; +} + +.color-item { + display: flex; + align-items: center; + gap: 12px; + padding: 12px; + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 6px; +} + +.color-swatch { + width: 40px; + height: 40px; + border-radius: 4px; + border: 1px solid var(--vscode-input-border); +} + +.color-info { + display: flex; + flex-direction: column; + gap: 4px; +} + +.color-name { + font-weight: 500; + color: var(--vscode-foreground); +} + +.color-value { + font-size: 0.85rem; + color: var(--vscode-descriptionForeground); + font-family: var(--vscode-editor-font-family); +} + +.font-samples { + display: flex; + flex-direction: column; + gap: 16px; +} + +.font-sample { + padding: 12px; + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 6px; +} + +.font-sample.h1 { + font-size: 2rem; + font-weight: 700; +} + +.font-sample.h2 { + font-size: 1.5rem; + font-weight: 600; +} + +.font-sample.body { + font-size: 1rem; +} + +.font-sample.caption { + font-size: 0.85rem; + opacity: 0.8; +} + +.actions { + padding: 16px 24px; + border-top: 1px solid var(--vscode-panel-border); + background: var(--vscode-sideBar-background); +} + +.export-btn { + width: 100%; + padding: 12px 16px; + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-radius: 6px; + cursor: pointer; + font-size: 0.95rem; + font-weight: 500; + transition: background-color 0.2s ease; +} + +.export-btn:hover { + background: var(--vscode-button-hoverBackground); +} + +.export-btn:active { + background: var(--vscode-button-activeBackground); +} + +/* Canvas View Styles */ +.canvas-container { + display: flex; + flex-direction: column; + height: 100vh; + background: var(--vscode-editor-background); + position: relative; +} + +.canvas-toolbar { + position: absolute; + top: 16px; + left: 16px; + z-index: 1000; + + display: flex; + align-items: stretch; + background: var(--vscode-titleBar-activeBackground); + border: 1px solid var(--vscode-titleBar-border, var(--vscode-contrastBorder)); + border-radius: 6px; + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.16); + backdrop-filter: blur(8px); + font-size: 12px; + min-height: 32px; + max-width: calc(100vw - 32px); + overflow: hidden; + transition: all 0.2s ease; +} + +.canvas-toolbar:hover { + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2); + border-color: var(--vscode-focusBorder, var(--vscode-titleBar-border)); +} + +.toolbar-section { + display: flex; + align-items: center; + padding: 0 12px; + border-right: 1px solid var(--vscode-titleBar-border, var(--vscode-contrastBorder)); + position: relative; + min-height: 32px; +} + +.toolbar-section:last-child { + border-right: none; +} + +.toolbar-section:hover { + background: var(--vscode-titleBar-hoverBackground, rgba(255, 255, 255, 0.05)); +} + +.control-group { + display: flex; + align-items: center; + gap: 2px; +} + +/* Toolbar Buttons */ +.toolbar-btn { + background: transparent; + border: none; + color: var(--vscode-titleBar-activeForeground); + padding: 4px 6px; + border-radius: 3px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + min-width: 24px; + height: 24px; + transition: all 0.15s ease; + font-size: 12px; +} + +.toolbar-btn:hover { + background: var(--vscode-toolbar-hoverBackground, rgba(255, 255, 255, 0.1)); + color: var(--vscode-titleBar-activeForeground); +} + +.toolbar-btn:active { + background: var(--vscode-toolbar-activeBackground, rgba(255, 255, 255, 0.15)); +} + +.toolbar-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + background: transparent; +} + +.toolbar-btn.active { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); +} + +.toolbar-btn.active:hover { + background: var(--vscode-button-hoverBackground); +} + +/* Specific Button Types */ +.zoom-btn { + width: 28px; +} + +.layout-toggle { + display: flex; + background: var(--vscode-dropdown-background); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 4px; + overflow: hidden; + margin-left: 6px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +/* Ensure no individual button radius */ +.layout-toggle .toggle-btn { + border-radius: 0 !important; +} + +.layout-toggle .toggle-btn:first-child { + border-radius: 0 !important; +} + +.layout-toggle .toggle-btn:last-child { + border-radius: 0 !important; +} + +.toggle-btn { + background: transparent; + border: none; + border-radius: 0 !important; + color: var(--vscode-dropdown-foreground); + padding: 5px 8px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + min-width: 32px; + height: 26px; + transition: all 0.2s ease; + border-right: 1px solid var(--vscode-dropdown-border); + position: relative; +} + +.toggle-btn:last-child { + border-right: none; +} + +.toggle-btn:hover { + background: var(--vscode-list-hoverBackground); + color: var(--vscode-list-hoverForeground); + transform: translateY(-1px); +} + +.toggle-btn.active { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.toggle-btn.active:hover { + background: var(--vscode-button-hoverBackground); + transform: none; +} + +/* Enhanced icon styling for layout buttons */ +.toggle-btn svg { + transition: transform 0.2s ease; +} + +.toggle-btn:hover svg { + transform: scale(1.1); +} + +.toggle-btn.active svg { + transform: scale(1.05); +} + +/* Toolbar Dividers and Displays */ +.toolbar-divider { + width: 1px; + height: 16px; + background: var(--vscode-titleBar-border, var(--vscode-contrastBorder)); + margin: 0 4px; +} + +.zoom-display { + display: flex; + align-items: center; + padding: 0 6px; + margin: 0 2px; +} + +.zoom-value { + font-size: 11px; + font-weight: 500; + color: var(--vscode-titleBar-activeForeground); + min-width: 32px; + text-align: center; +} + +/* Viewport Controls - Segmented Control Style */ +.viewport-selector { + display: flex; + background: var(--vscode-dropdown-background); + border: 1px solid var(--vscode-dropdown-border); + border-radius: 4px; + overflow: hidden; + margin-left: 6px; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +/* Ensure no individual button radius */ +.viewport-selector .viewport-btn { + border-radius: 0 !important; +} + +.viewport-selector .viewport-btn:first-child { + border-radius: 0 !important; +} + +.viewport-selector .viewport-btn:last-child { + border-radius: 0 !important; +} + +.viewport-btn { + background: transparent; + border: none; + border-radius: 0 !important; + color: var(--vscode-dropdown-foreground); + padding: 5px 8px; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + font-size: 12px; + min-width: 32px; + height: 26px; + transition: all 0.2s ease; + border-right: 1px solid var(--vscode-dropdown-border); + position: relative; +} + +.viewport-btn:last-child { + border-right: none; +} + +.viewport-btn:hover:not(:disabled) { + background: var(--vscode-list-hoverBackground); + color: var(--vscode-list-hoverForeground); +} + +.viewport-btn:disabled { + opacity: 0.3; + cursor: not-allowed; + background: var(--vscode-input-background); + color: var(--vscode-disabledForeground); +} + +.viewport-btn.active { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.viewport-btn.active:hover { + background: var(--vscode-button-hoverBackground); +} + +/* Enhanced icon styling for viewport buttons */ +.viewport-btn svg { + transition: transform 0.2s ease; +} + +.viewport-btn:hover:not(:disabled) svg { + transform: scale(1.1); +} + +.viewport-btn.active svg { + transform: scale(1.05); +} + +/* Additional Button Styles */ +.connection-btn { + margin-left: 6px; +} + +.viewport-mode-btn { + margin-right: 6px; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .canvas-toolbar { + left: 8px; + top: 8px; + flex-wrap: wrap; + max-width: calc(100vw - 16px); + } + + .toolbar-section { + padding: 0 8px; + } +} + +/* Floating Action Buttons */ +.floating-action-buttons { + position: absolute; + top: 8px; + right: -190px; + display: flex; + flex-direction: column; + gap: 6px; + z-index: 200; + animation: fadeInFromRight 0.3s ease forwards; +} + +.floating-action-btn { + background: white; + color: #333; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 8px; + padding: 10px 14px; + font-size: 0.8rem; + font-weight: 500; + cursor: pointer; + display: flex; + align-items: center; + gap: 8px; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15); + transition: all 0.2s ease; + white-space: nowrap; + min-width: 150px; + justify-content: flex-start; +} + +.floating-action-btn:hover { + background: #f8f9fa; + transform: translateY(-1px); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + border-color: rgba(0, 0, 0, 0.2); +} + +.floating-action-btn:active { + transform: translateY(0); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.floating-action-btn .btn-icon { + width: 16px; + height: 16px; + flex-shrink: 0; + color: #666; +} + +.floating-action-btn:hover .btn-icon { + color: #333; +} + +.floating-action-btn .btn-text { + font-size: 0.8rem; + font-weight: 500; + color: #333; +} + +@keyframes fadeInFromRight { + from { + opacity: 0; + transform: translateX(20px); + } + to { + opacity: 1; + transform: translateX(0); + } +} + +/* Copied Notification */ +.copied-notification { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: var(--vscode-notifications-background); + color: var(--vscode-notifications-foreground); + border: 1px solid var(--vscode-notifications-border); + border-radius: 6px; + padding: 12px 16px; + display: flex; + align-items: center; + gap: 8px; + font-size: 0.85rem; + font-weight: 500; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + z-index: 300; + animation: fadeInOut 2s ease forwards; + pointer-events: none; +} + +.copied-notification .copied-icon { + font-size: 1rem; +} + +.copied-notification .copied-text { + white-space: nowrap; +} + +@keyframes fadeInOut { + 0% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8); + } + 15% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + 85% { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } + 100% { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8); + } +} + +/* Copy Prompt Dropdown */ +.copy-prompt-dropdown { + position: relative; + display: inline-block; +} + +.copy-prompt-main-btn { + position: relative; + padding-right: 40px !important; /* Make room for dropdown arrow */ +} + +.copy-prompt-main-btn.success { + background: #1a1a1a !important; + color: white !important; + border-color: #1a1a1a !important; +} + +.copy-prompt-main-btn.success .btn-icon, +.copy-prompt-main-btn.success .dropdown-arrow { + color: white !important; +} + +.copy-prompt-main-btn.success .btn-text { + color: white !important; +} + +.copy-prompt-main-btn.success .btn-icon { + display: none; +} + +.copy-prompt-main-btn.success::before { + content: "✓"; + font-size: 16px; + margin-right: 8px; + color: white !important; +} + +.dropdown-arrow { + position: absolute; + right: 12px; + top: 50%; + transform: translateY(-50%); + width: 12px; + height: 12px; + color: #666; + transition: transform 0.2s ease; +} + +.copy-prompt-main-btn:hover .dropdown-arrow { + color: #333; +} + +.copy-dropdown-menu { + position: absolute; + top: 100%; + left: 0; + right: 0; + background: white; + border: 1px solid rgba(0, 0, 0, 0.1); + border-radius: 8px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + z-index: 300; + margin-top: 4px; + overflow: hidden; + animation: dropdownFadeIn 0.2s ease forwards; +} + +@keyframes dropdownFadeIn { + from { + opacity: 0; + transform: translateY(-8px) scale(0.95); + } + to { + opacity: 1; + transform: translateY(0) scale(1); + } +} + +.copy-dropdown-item { + width: 100%; + background: none; + border: none; + padding: 10px 14px; + display: flex; + align-items: center; + gap: 10px; + cursor: pointer; + font-size: 0.8rem; + font-weight: 500; + color: #333; + text-align: left; + transition: background-color 0.2s ease; + border-bottom: 1px solid rgba(0, 0, 0, 0.05); +} + +.copy-dropdown-item:last-child { + border-bottom: none; +} + +.copy-dropdown-item:hover { + background: #f8f9fa; +} + +.copy-dropdown-item:active { + background: #e9ecef; +} + +.platform-logo { + width: 20px; + height: 20px; + object-fit: contain; + flex-shrink: 0; +} + +.copy-dropdown-item span { + flex: 1; + white-space: nowrap; +} \ No newline at end of file diff --git a/src/webview/App.tsx b/src/webview/App.tsx new file mode 100644 index 0000000..805286a --- /dev/null +++ b/src/webview/App.tsx @@ -0,0 +1,113 @@ +import React, { useState, useEffect } from 'react'; +import ChatInterface from './components/Chat/ChatInterface'; +import CanvasView from './components/CanvasView'; +import { WebviewContext } from '../types/context'; + +// Import CSS as string for esbuild +import styles from './App.css'; + +const App: React.FC = () => { + console.log('🚀 App component starting...'); + + const [vscode] = useState(() => { + console.log('📞 Acquiring vscode API...'); + return acquireVsCodeApi(); + }); + + const [context, setContext] = useState(null); + const [currentView, setCurrentView] = useState<'chat' | 'canvas'>('chat'); + const [nonce, setNonce] = useState(null); + + useEffect(() => { + console.log('🔄 App useEffect running...'); + + // Detect which view to render based on data-view attribute + const rootElement = document.getElementById('root'); + console.log('📍 Root element:', rootElement); + + const viewType = rootElement?.getAttribute('data-view'); + const nonceValue = rootElement?.getAttribute('data-nonce'); + + console.log('🎯 View type detected:', viewType); + console.log('🔐 Nonce value:', nonceValue); + + if (nonceValue) { + setNonce(nonceValue); + console.log('✅ Nonce set:', nonceValue); + } + + if (viewType === 'canvas') { + setCurrentView('canvas'); + console.log('🎨 Switching to canvas view'); + } else { + setCurrentView('chat'); + console.log('💬 Switching to chat view'); + } + + // Inject CSS styles + const styleElement = document.createElement('style'); + styleElement.textContent = styles; + document.head.appendChild(styleElement); + console.log('🎨 CSS styles injected'); + + // Get context from window (only needed for chat interface) + const webviewContext = (window as any).__WEBVIEW_CONTEXT__; + console.log('🌐 Webview context from window:', webviewContext); + + if (webviewContext) { + setContext(webviewContext); + console.log('✅ Context set:', webviewContext); + } else { + console.log('⚠️ No webview context found in window'); + } + + return () => { + document.head.removeChild(styleElement); + }; + }, []); + + const renderView = () => { + console.log('🖼️ Rendering view, currentView:', currentView); + + switch (currentView) { + case 'canvas': + console.log('🎨 Rendering CanvasView with vscode:', !!vscode, 'nonce:', nonce); + try { + // Canvas view doesn't need context - it gets data from extension directly + return ; + } catch (error) { + console.error('❌ Error rendering CanvasView:', error); + return
Error rendering canvas: {String(error)}
; + } + case 'chat': + default: + console.log('💬 Rendering ChatInterface, context:', !!context); + // Chat interface needs context + if (!context) { + console.log('⏳ Context not ready, showing loading...'); + return
Loading...
; + } + try { + return ( + + ); + } catch (error) { + console.error('❌ Error rendering ChatInterface:', error); + return
Error rendering chat: {String(error)}
; + } + } + }; + + console.log('🔄 App rendering, currentView:', currentView); + + return ( +
+ {renderView()} +
+ ); +}; + +export default App; \ No newline at end of file diff --git a/src/webview/components/CanvasView.tsx b/src/webview/components/CanvasView.tsx new file mode 100644 index 0000000..20607ff --- /dev/null +++ b/src/webview/components/CanvasView.tsx @@ -0,0 +1,860 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { TransformWrapper, TransformComponent, ReactZoomPanPinchRef } from 'react-zoom-pan-pinch'; +import DesignFrame from './DesignFrame'; +import { calculateGridPosition, calculateFitToView, getGridMetrics, generateResponsiveConfig, buildHierarchyTree, calculateHierarchyPositions, getHierarchicalPosition, detectDesignRelationships } from '../utils/gridLayout'; +import { + DesignFile, + CanvasState, + WebviewMessage, + ExtensionToWebviewMessage, + CanvasConfig, + ViewportMode, + FrameViewportState, + FramePositionState, + DragState, + GridPosition, + LayoutMode, + HierarchyTree, + ConnectionLine +} from '../types/canvas.types'; +import ConnectionLines from './ConnectionLines'; +import { + ZoomInIcon, + ZoomOutIcon, + HomeIcon, + ScaleIcon, + RefreshIcon, + GlobeIcon, + MobileIcon, + TabletIcon, + DesktopIcon, + TreeIcon, + LinkIcon +} from './Icons'; + +interface CanvasViewProps { + vscode: any; + nonce: string | null; +} + +const CANVAS_CONFIG: CanvasConfig = { + frameSize: { width: 320, height: 400 }, // Smaller default frame size for better density + gridSpacing: 50, // Much tighter spacing between frames + framesPerRow: 4, // Fit 4 frames per row by default + minZoom: 0.1, + maxZoom: 5, + responsive: { + enableScaling: true, + minFrameSize: { width: 160, height: 200 }, // Reduced minimum size + maxFrameSize: { width: 400, height: 500 }, // Reduced maximum size + scaleWithZoom: false + }, + viewports: { + desktop: { width: 1000, height: 600 }, // More compact desktop view + tablet: { width: 640, height: 800 }, // Smaller tablet view + mobile: { width: 320, height: 550 } // More compact mobile view + }, + hierarchy: { + horizontalSpacing: 180, // Reduced horizontal spacing for hierarchy + verticalSpacing: 120, // Reduced vertical spacing for hierarchy + connectionLineWidth: 2, + connectionLineColor: 'var(--vscode-textLink-foreground)', + showConnections: true + } +}; + +const CanvasView: React.FC = ({ vscode, nonce }) => { + console.log('🎨 CanvasView component starting...'); + console.log('📞 CanvasView props - vscode:', !!vscode, 'nonce:', nonce); + + const [designFiles, setDesignFiles] = useState([]); + const [selectedFrames, setSelectedFrames] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [error, setError] = useState(null); + const [currentZoom, setCurrentZoom] = useState(1); + const [currentConfig, setCurrentConfig] = useState(CANVAS_CONFIG); + const [globalViewportMode, setGlobalViewportMode] = useState('tablet'); + const [frameViewports, setFrameViewports] = useState({}); + const [useGlobalViewport, setUseGlobalViewport] = useState(true); + const [customPositions, setCustomPositions] = useState({}); + const [dragState, setDragState] = useState({ + isDragging: false, + draggedFrame: null, + startPosition: { x: 0, y: 0 }, + currentPosition: { x: 0, y: 0 }, + offset: { x: 0, y: 0 } + }); + const [layoutMode, setLayoutMode] = useState('grid'); + const [hierarchyTree, setHierarchyTree] = useState(null); + const [showConnections, setShowConnections] = useState(true); + const transformRef = useRef(null); + + console.log('✅ CanvasView state initialized successfully'); + + // Performance optimization: Switch render modes based on zoom level + const getOptimalRenderMode = (_zoom: number): 'placeholder' | 'iframe' => { + // Always render iframe as requested by the user + return 'iframe'; + }; + + // Helper function to transform mouse coordinates to canvas space + const transformMouseToCanvasSpace = (clientX: number, clientY: number, canvasRect: DOMRect): GridPosition => { + // Get current transform state from the TransformWrapper + const transformState = transformRef.current?.instance?.transformState; + const currentScale = transformState?.scale || 1; + const currentTranslateX = transformState?.positionX || 0; + const currentTranslateY = transformState?.positionY || 0; + + // Calculate mouse position relative to canvas, then adjust for zoom and pan + const rawMouseX = clientX - canvasRect.left; + const rawMouseY = clientY - canvasRect.top; + + // Transform mouse coordinates to canvas space (inverse of current transform) + return { + x: (rawMouseX - currentTranslateX) / currentScale, + y: (rawMouseY - currentTranslateY) / currentScale + }; + }; + + // Viewport management functions + const getFrameViewport = (fileName: string): ViewportMode => { + if (useGlobalViewport) { + return globalViewportMode; + } + return frameViewports[fileName] || 'desktop'; + }; + + const handleFrameViewportChange = (fileName: string, viewport: ViewportMode) => { + setFrameViewports(prev => ({ + ...prev, + [fileName]: viewport + })); + }; + + const handleGlobalViewportChange = (viewport: ViewportMode) => { + setGlobalViewportMode(viewport); + if (useGlobalViewport) { + // Update all frames to the new global viewport + const newFrameViewports: FrameViewportState = {}; + designFiles.forEach(file => { + newFrameViewports[file.name] = viewport; + }); + setFrameViewports(newFrameViewports); + + // Update hierarchy positioning when viewport changes to adjust connection spacing + if (hierarchyTree && designFiles.length > 0) { + // Recalculate frame dimensions for new viewport + let totalWidth = 0; + let totalHeight = 0; + let frameCount = 0; + + designFiles.forEach(file => { + const viewportDimensions = currentConfig.viewports[viewport]; + totalWidth += viewportDimensions.width; + totalHeight += viewportDimensions.height + 50; // Add header space + frameCount++; + }); + + const avgFrameDimensions = frameCount > 0 ? { + width: Math.round(totalWidth / frameCount), + height: Math.round(totalHeight / frameCount) + } : { width: 400, height: 550 }; + + const updatedTree = calculateHierarchyPositions(hierarchyTree, currentConfig, avgFrameDimensions); + setHierarchyTree(updatedTree); + } + } + }; + + const toggleGlobalViewport = () => { + const newUseGlobal = !useGlobalViewport; + setUseGlobalViewport(newUseGlobal); + + if (newUseGlobal) { + // Set all frames to current global viewport + const newFrameViewports: FrameViewportState = {}; + designFiles.forEach(file => { + newFrameViewports[file.name] = globalViewportMode; + }); + setFrameViewports(newFrameViewports); + } + }; + + // Responsive config update + useEffect(() => { + const updateConfig = () => { + const responsive = generateResponsiveConfig(CANVAS_CONFIG, window.innerWidth); + setCurrentConfig(responsive); + }; + + updateConfig(); + window.addEventListener('resize', updateConfig); + return () => window.removeEventListener('resize', updateConfig); + }, []); + + useEffect(() => { + // Request design files from extension + const loadMessage: WebviewMessage = { + command: 'loadDesignFiles' + }; + vscode.postMessage(loadMessage); + + // Listen for messages from extension + const messageHandler = (event: MessageEvent) => { + const message: ExtensionToWebviewMessage = event.data; + + switch (message.command) { + case 'designFilesLoaded': + // Convert date strings back to Date objects + const filesWithDates = message.data.files.map(file => ({ + ...file, + modified: new Date(file.modified) + })); + + // Detect design relationships and build hierarchy + const filesWithRelationships = detectDesignRelationships(filesWithDates); + setDesignFiles(filesWithRelationships); + + // Build hierarchy tree + const tree = buildHierarchyTree(filesWithRelationships); + + // Calculate average frame dimensions based on viewport usage + let totalWidth = 0; + let totalHeight = 0; + let frameCount = 0; + + filesWithRelationships.forEach(file => { + const frameViewport = getFrameViewport(file.name); + const viewportDimensions = currentConfig.viewports[frameViewport]; + totalWidth += viewportDimensions.width; + totalHeight += viewportDimensions.height + 50; // Add header space + frameCount++; + }); + + const avgFrameDimensions = frameCount > 0 ? { + width: Math.round(totalWidth / frameCount), + height: Math.round(totalHeight / frameCount) + } : { width: 400, height: 550 }; + + const positionedTree = calculateHierarchyPositions(tree, currentConfig, avgFrameDimensions); + setHierarchyTree(positionedTree); + + setIsLoading(false); + + // Auto-center view after files are loaded + setTimeout(() => { + if (transformRef.current) { + transformRef.current.resetTransform(); + } + }, 100); + break; + + case 'error': + setError(message.data.error); + setIsLoading(false); + break; + + case 'fileChanged': + // Handle file system changes (will implement in Task 2.3) + console.log('File changed:', message.data); + // Re-request files when changes occur + vscode.postMessage({ command: 'loadDesignFiles' }); + break; + } + }; + + window.addEventListener('message', messageHandler); + return () => window.removeEventListener('message', messageHandler); + }, [vscode]); // Removed currentConfig dependency to prevent constant re-renders + + const handleFrameSelect = (fileName: string) => { + setSelectedFrames([fileName]); // Single selection for now + + // Find the selected file to get its full path + const selectedFile = designFiles.find(file => file.name === fileName); + const filePath = selectedFile ? selectedFile.path : fileName; + + const selectMessage: WebviewMessage = { + command: 'selectFrame', + data: { fileName } + }; + vscode.postMessage(selectMessage); + + // Also send context to chat interface with full path + const contextMessage: WebviewMessage = { + command: 'setContextFromCanvas', + data: { fileName: filePath, type: 'frame' } + }; + vscode.postMessage(contextMessage); + }; + + const handleSendToChat = (fileName: string, prompt: string) => { + // Find the selected file to get its full path + const selectedFile = designFiles.find(file => file.name === fileName); + const filePath = selectedFile ? selectedFile.path : fileName; + + // Set context first + const contextMessage: WebviewMessage = { + command: 'setContextFromCanvas', + data: { fileName: filePath, type: 'frame' } + }; + vscode.postMessage(contextMessage); + + // Then send the prompt to the chat input + const promptMessage: WebviewMessage = { + command: 'setChatPrompt', + data: { prompt } + }; + vscode.postMessage(promptMessage); + }; + + // Canvas control functions + const handleZoomIn = () => { + if (transformRef.current) { + const currentState = transformRef.current.instance?.transformState; + console.log('🔍 ZOOM IN - Before:', { + scale: currentState?.scale, + positionX: currentState?.positionX, + positionY: currentState?.positionY, + step: 0.05, + minScale: 0.1, + maxScale: 3, + smooth: false + }); + + transformRef.current.zoomIn(0.05); + + // Log after zoom (with small delay to capture the change) + setTimeout(() => { + const newState = transformRef.current?.instance?.transformState; + console.log('🔍 ZOOM IN - After:', { + scale: newState?.scale, + positionX: newState?.positionX, + positionY: newState?.positionY, + scaleDiff: newState?.scale ? (newState.scale - (currentState?.scale || 1)) : 0, + positionXDiff: newState?.positionX ? (newState.positionX - (currentState?.positionX || 0)) : 0, + positionYDiff: newState?.positionY ? (newState.positionY - (currentState?.positionY || 0)) : 0 + }); + }, 50); + } + }; + + const handleZoomOut = () => { + if (transformRef.current) { + const currentState = transformRef.current.instance?.transformState; + console.log('🔍 ZOOM OUT - Before:', { + scale: currentState?.scale, + positionX: currentState?.positionX, + positionY: currentState?.positionY, + step: 0.05 + }); + + transformRef.current.zoomOut(0.05); + + // Log after zoom (with small delay to capture the change) + setTimeout(() => { + const newState = transformRef.current?.instance?.transformState; + console.log('🔍 ZOOM OUT - After:', { + scale: newState?.scale, + positionX: newState?.positionX, + positionY: newState?.positionY, + scaleDiff: newState?.scale ? (newState.scale - (currentState?.scale || 1)) : 0, + positionXDiff: newState?.positionX ? (newState.positionX - (currentState?.positionX || 0)) : 0, + positionYDiff: newState?.positionY ? (newState.positionY - (currentState?.positionY || 0)) : 0 + }); + }, 50); + } + }; + + const handleResetZoom = () => { + if (transformRef.current) { + const currentState = transformRef.current.instance?.transformState; + console.log('🔍 RESET ZOOM - Before:', { + scale: currentState?.scale, + positionX: currentState?.positionX, + positionY: currentState?.positionY + }); + + transformRef.current.resetTransform(); + + setTimeout(() => { + const newState = transformRef.current?.instance?.transformState; + console.log('🔍 RESET ZOOM - After:', { + scale: newState?.scale, + positionX: newState?.positionX, + positionY: newState?.positionY + }); + }, 50); + } + }; + + const handleTransformChange = (ref: ReactZoomPanPinchRef) => { + const state = ref.state; + + // Prevent negative or zero scales + if (state.scale <= 0) { + console.error('🚨 INVALID SCALE DETECTED:', state.scale, '- Resetting to minimum'); + ref.setTransform(state.positionX, state.positionY, 0.1); + return; + } + + console.log('🔄 TRANSFORM CHANGE:', { + scale: state.scale, + positionX: state.positionX, + positionY: state.positionY, + previousScale: currentZoom + }); + setCurrentZoom(state.scale); + }; + + // Get frame position (custom, hierarchy, or default grid position) + const getFramePosition = (fileName: string, index: number): GridPosition => { + if (customPositions[fileName]) { + return customPositions[fileName]; + } + + // Use hierarchy layout if in hierarchy mode and tree is available + if (layoutMode === 'hierarchy' && hierarchyTree) { + return getHierarchicalPosition(fileName, hierarchyTree); + } + + // Default grid position calculation + const viewportMode = getFrameViewport(fileName); + const viewportDimensions = currentConfig.viewports[viewportMode]; + const actualWidth = viewportDimensions.width; + const actualHeight = viewportDimensions.height + 50; + + const col = index % currentConfig.framesPerRow; + const row = Math.floor(index / currentConfig.framesPerRow); + + const x = col * (Math.max(actualWidth, currentConfig.frameSize.width) + currentConfig.gridSpacing); + const y = row * (Math.max(actualHeight, currentConfig.frameSize.height) + currentConfig.gridSpacing); + + return { x, y }; + }; + + // Drag handlers + const handleDragStart = (fileName: string, startPos: GridPosition, mouseEvent: React.MouseEvent) => { + // Get canvas grid element for proper coordinate calculation + const canvasGrid = document.querySelector('.canvas-grid') as HTMLElement; + if (!canvasGrid) return; + + const canvasRect = canvasGrid.getBoundingClientRect(); + const canvasMousePos = transformMouseToCanvasSpace(mouseEvent.clientX, mouseEvent.clientY, canvasRect); + + // Also ensure this frame is selected + if (!selectedFrames.includes(fileName)) { + setSelectedFrames([fileName]); + } + + setDragState({ + isDragging: true, + draggedFrame: fileName, + startPosition: startPos, + currentPosition: startPos, + offset: { + x: canvasMousePos.x - startPos.x, + y: canvasMousePos.y - startPos.y + } + }); + }; + + const handleDragMove = (mousePos: GridPosition) => { + if (!dragState.isDragging || !dragState.draggedFrame) return; + + const newPosition = { + x: mousePos.x - dragState.offset.x, + y: mousePos.y - dragState.offset.y + }; + + setDragState(prev => ({ + ...prev, + currentPosition: newPosition + })); + }; + + const handleDragEnd = () => { + if (!dragState.isDragging || !dragState.draggedFrame) return; + + // Snap to grid (optional - makes positioning cleaner) + const gridSize = 25; + const snappedPosition = { + x: Math.round(dragState.currentPosition.x / gridSize) * gridSize, + y: Math.round(dragState.currentPosition.y / gridSize) * gridSize + }; + + // Save the new position + setCustomPositions(prev => ({ + ...prev, + [dragState.draggedFrame!]: snappedPosition + })); + + // Reset drag state + setDragState({ + isDragging: false, + draggedFrame: null, + startPosition: { x: 0, y: 0 }, + currentPosition: { x: 0, y: 0 }, + offset: { x: 0, y: 0 } + }); + }; + + // Reset positions to grid + const handleResetPositions = () => { + setCustomPositions({}); + }; + + // Update connection positions based on current frame positions + const updateConnectionPositions = (connections: ConnectionLine[], files: DesignFile[]): ConnectionLine[] => { + return connections.map(connection => { + const fromIndex = files.findIndex(f => f.name === connection.fromFrame); + const toIndex = files.findIndex(f => f.name === connection.toFrame); + + if (fromIndex === -1 || toIndex === -1) { + return connection; // Keep original if frame not found + } + + // Get current positions (custom or calculated) + const fromPosition = getFramePosition(connection.fromFrame, fromIndex); + const toPosition = getFramePosition(connection.toFrame, toIndex); + + // Get frame dimensions for connection point calculation + const fromViewport = getFrameViewport(connection.fromFrame); + const toViewport = getFrameViewport(connection.toFrame); + const fromDimensions = currentConfig.viewports[fromViewport]; + const toDimensions = currentConfig.viewports[toViewport]; + + // Calculate connection points (center-right of from frame to center-left of to frame) + const fromConnectionPoint = { + x: fromPosition.x + fromDimensions.width, + y: fromPosition.y + (fromDimensions.height + 50) / 2 // +50 for header + }; + + const toConnectionPoint = { + x: toPosition.x, + y: toPosition.y + (toDimensions.height + 50) / 2 // +50 for header + }; + + return { + ...connection, + fromPosition: fromConnectionPoint, + toPosition: toConnectionPoint + }; + }); + }; + + // Keyboard shortcuts for zoom + useEffect(() => { + const handleKeyDown = (e: KeyboardEvent) => { + if ((e.metaKey || e.ctrlKey) && !e.shiftKey) { + switch (e.key) { + case '=': + case '+': + e.preventDefault(); + handleZoomIn(); + break; + case '-': + e.preventDefault(); + handleZoomOut(); + break; + case '0': + e.preventDefault(); + handleResetZoom(); + break; + } + } + }; + + window.addEventListener('keydown', handleKeyDown); + return () => window.removeEventListener('keydown', handleKeyDown); + }, []); + + if (isLoading) { + return ( +
+
+
+

Loading design files...

+
+
+ ); + } + + if (error) { + return ( +
+
+

Error loading canvas

+

{error}

+ +
+
+ ); + } + + if (designFiles.length === 0) { + return ( +
+
+

No design files found in .superdesign/design_iterations/

+

Prompt Superdesign OR Cursor/Windsurf/Claude Code to design UI like Help me design a calculator UI and preview the UI here

+
+
+ ); + } + + return ( +
+ {/* Canvas Controls - Clean Minimal Design */} +
+ {/* Navigation Section */} +
+
+ +
+ {Math.round(currentZoom * 100)}% +
+ +
+ + +
+
+ + {/* Layout Section */} +
+
+
+ + +
+ {layoutMode === 'hierarchy' && ( + + )} +
+
+ + {/* Viewport Section */} +
+
+ +
+ + + +
+
+
+
+ + {/* Infinite Canvas */} + handleTransformChange(ref)} + onZoom={(ref) => { + const state = ref.state; + + // Check for invalid scale and fix it + if (state.scale <= 0) { + console.error('🚨 ZOOM EVENT - Invalid scale:', state.scale, '- Fixing...'); + ref.setTransform(state.positionX, state.positionY, 0.1); + return; + } + + console.log('📏 ZOOM EVENT:', { + scale: state.scale, + positionX: state.positionX, + positionY: state.positionY, + event: 'onZoom' + }); + }} + onPanning={(ref) => { + console.log('👆 PAN EVENT:', { + scale: ref.state.scale, + positionX: ref.state.positionX, + positionY: ref.state.positionY, + event: 'onPanning' + }); + }} + onZoomStart={(ref) => { + console.log('🔍 ZOOM START:', { + scale: ref.state.scale, + positionX: ref.state.positionX, + positionY: ref.state.positionY, + event: 'onZoomStart' + }); + }} + onZoomStop={(ref) => { + console.log('🔍 ZOOM STOP:', { + scale: ref.state.scale, + positionX: ref.state.positionX, + positionY: ref.state.positionY, + event: 'onZoomStop' + }); + }} + > + +
{ + if (dragState.isDragging) { + const rect = e.currentTarget.getBoundingClientRect(); + const mousePos = transformMouseToCanvasSpace(e.clientX, e.clientY, rect); + handleDragMove(mousePos); + } + }} + onMouseUp={handleDragEnd} + onMouseLeave={handleDragEnd} + onClick={(e) => { + // Clear selection when clicking on empty space + if (e.target === e.currentTarget) { + setSelectedFrames([]); + + // Also clear context in chat + const clearContextMessage: WebviewMessage = { + command: 'setContextFromCanvas', + data: { fileName: '', type: 'clear' } + }; + vscode.postMessage(clearContextMessage); + } + }} + > + {/* Connection Lines (render behind frames) */} + {layoutMode === 'hierarchy' && hierarchyTree && showConnections && ( + + )} + {designFiles.map((file, index) => { + const frameViewport = getFrameViewport(file.name); + const viewportDimensions = currentConfig.viewports[frameViewport]; + + // Use actual viewport dimensions (add frame border/header space) + const actualWidth = viewportDimensions.width; + const actualHeight = viewportDimensions.height + 50; // Add space for header + + // Get position (custom or default grid) + const position = getFramePosition(file.name, index); + + // If this frame is being dragged, use current drag position + const finalPosition = dragState.isDragging && dragState.draggedFrame === file.name + ? dragState.currentPosition + : position; + + return ( + + ); + })} +
+
+
+
+ ); +}; + +export default CanvasView; \ No newline at end of file diff --git a/src/webview/components/Chat/ChatInterface.css b/src/webview/components/Chat/ChatInterface.css new file mode 100644 index 0000000..47c27d9 --- /dev/null +++ b/src/webview/components/Chat/ChatInterface.css @@ -0,0 +1,2285 @@ +/* Base chat interface styles */ +.chat-interface { + display: flex; + flex-direction: column; + height: 100vh; + width: 100%; + background: var(--vscode-sideBar-background); + color: var(--vscode-sideBar-foreground); + position: relative; + overflow: hidden; +} + +.chat-interface--panel { + max-width: 100%; + background: var(--vscode-panel-background); + color: var(--vscode-panel-foreground); +} + +.chat-interface--sidebar { + font-size: 12px; + background: var(--vscode-sideBar-background); + color: var(--vscode-sideBar-foreground); +} + +/* Header styles (panel only) */ +.chat-header { + padding: 0; + border-bottom: 1px solid var(--vscode-panel-border); + margin-bottom: 0; + position: relative; +} + +.chat-header h2 { + margin: 0 0 8px 0; + font-size: 18px; + font-weight: 600; +} + +.chat-header p { + margin: 0; + color: var(--vscode-descriptionForeground); + font-size: 14px; +} + +/* New Conversation Button */ +.new-conversation-btn { + position: absolute; + top: 0; + right: 0; + background: transparent; + border: none; + color: var(--vscode-descriptionForeground); + cursor: pointer; + padding: 8px; + border-radius: 4px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + opacity: 0.7; +} + +.new-conversation-btn:hover { + background: var(--vscode-list-hoverBackground); + color: var(--vscode-foreground); + opacity: 1; +} + +.new-conversation-btn:active { + background: var(--vscode-list-activeSelectionBackground); + transform: scale(0.95); +} + +.new-conversation-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + background: transparent; + transform: none; +} + +/* Chat container */ +.chat-container { + flex: 1; + display: flex; + flex-direction: column; + overflow: hidden; + position: relative; +} + +.chat-history { + flex: 1; + overflow-y: auto; + padding: 0 12px 8px 12px; + margin-bottom: 0; + scroll-behavior: smooth; +} + +.chat-history::-webkit-scrollbar { + width: 6px; +} + +.chat-history::-webkit-scrollbar-track { + background: transparent; +} + +.chat-history::-webkit-scrollbar-thumb { + background: var(--vscode-scrollbarSlider-background); + border-radius: 3px; +} + +.chat-history::-webkit-scrollbar-thumb:hover { + background: var(--vscode-scrollbarSlider-hoverBackground); +} + +/* Chat messages */ +.chat-message { + margin-bottom: 16px; + padding: 14px 16px; + border-radius: 12px; + line-height: 1.5; + border: 1px solid transparent; + transition: all 0.2s ease; + position: relative; +} + +.chat-message--panel { + padding: 18px 20px; + margin-bottom: 20px; + font-size: 14px; + border-radius: 16px; +} + +.chat-message--sidebar { + padding: 10px 12px; + margin-bottom: 12px; + font-size: 12px; + border-radius: 10px; +} + +/* User message specific overrides for compact styling */ +.chat-message--user.chat-message--panel { + padding: 12px 14px; + margin-bottom: 16px; + border-radius: 8px; +} + +.chat-message--user.chat-message--sidebar { + padding: 8px 10px; + margin-bottom: 10px; + border-radius: 6px; +} + +.chat-message--user { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + margin-left: 0; + margin-right: 0; + max-width: 100%; + padding: 10px 12px; + border-radius: 8px; + margin-bottom: 12px; +} + +/* SDK User messages (from Claude Code SDK) */ +.chat-message--user-sdk { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-textLink-foreground); + border-left: 3px solid var(--vscode-textLink-foreground); + margin-left: 0; + margin-right: 0; + max-width: 100%; + padding: 10px 12px; + border-radius: 8px; + margin-bottom: 12px; + opacity: 0.9; +} + +.chat-message--assistant { + background: transparent; + border: none; + border-left: none; + margin-left: 0; + margin-right: 0; + max-width: 100%; + padding-left: 0; + border-radius: 0; +} + +/* Result messages */ +.chat-message--result { + background: var(--vscode-editorWidget-background); + border: 1px solid var(--vscode-editorWidget-border); + border-left: 3px solid var(--vscode-charts-green); + margin-left: 0; + margin-right: 0; + max-width: 100%; + padding: 10px 12px; + border-radius: 8px; + margin-bottom: 12px; + font-family: var(--vscode-editor-font-family); + font-size: 13px; +} + +/* Error result messages */ +.chat-message--result-error { + background: var(--vscode-inputValidation-errorBackground); + border: 1px solid var(--vscode-inputValidation-errorBorder); + border-left: 3px solid var(--vscode-errorForeground); + margin-left: 0; + margin-right: 0; + max-width: 100%; + padding: 10px 12px; + border-radius: 8px; + margin-bottom: 12px; + font-family: var(--vscode-editor-font-family); + font-size: 13px; +} + +.chat-message__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 8px; + font-weight: 600; + opacity: 0.8; + font-size: 11px; +} + +.chat-message--sidebar .chat-message__header { + margin-bottom: 6px; + font-size: 10px; +} + +.chat-message__label { + font-size: 11px; + color: var(--vscode-descriptionForeground); + font-weight: 500; +} + +/* Metadata display */ +.chat-message__metadata { + display: flex; + align-items: center; + gap: 8px; + font-size: 10px; + color: var(--vscode-descriptionForeground); + opacity: 0.7; +} + +.metadata-item { + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + padding: 2px 4px; + border-radius: 3px; + font-size: 9px; + font-weight: 500; +} + +.chat-message__content { + white-space: pre-line; + word-wrap: break-word; + line-height: 1.3; + color: var(--vscode-editor-foreground); +} + +/* Override white-space for markdown content to prevent excessive spacing */ +.chat-message__content .markdown-content { + white-space: normal; +} + +.chat-message--user .chat-message__content { + line-height: 1.3; + font-size: 13px; +} + +.chat-message--user:hover { + border-color: var(--vscode-input-border); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +/* Typing indicator */ +.typing .typing-indicator { + animation: typing 1.4s infinite ease-in-out; + color: var(--vscode-descriptionForeground); + font-style: italic; +} + +.typing .typing-indicator::before { + content: ''; + display: inline-block; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--vscode-textLink-foreground); + margin-right: 8px; + animation: pulse 1.5s infinite ease-in-out; +} + +@keyframes typing { + 0%, 80%, 100% { opacity: 0.3; } + 40% { opacity: 1; } +} + +@keyframes pulse { + 0%, 100% { opacity: 0.4; transform: scale(0.8); } + 50% { opacity: 1; transform: scale(1.1); } +} + +/* Generating content layout */ +.generating-content { + display: flex; + align-items: center; + justify-content: space-between; + gap: 6px; + line-height: 1.3; + margin-top: 4px; + padding: 4px 0; + margin-left: 0; + margin-right: 0; + padding-left: 0; + padding-right: 0; +} + +.generating-text { + color: var(--vscode-descriptionForeground); + font-size: 11px; + flex: 1; + animation: generating-pulse 1.8s ease-in-out infinite; + opacity: 0.8; +} + +.generating-text::after { + content: ''; + animation: generating-dots 1.5s ease-in-out infinite; + margin-left: 2px; +} + +@keyframes generating-pulse { + 0%, 100% { opacity: 0.6; } + 50% { opacity: 1; } +} + +@keyframes generating-dots { + 0%, 20% { content: ''; } + 25%, 45% { content: '.'; } + 50%, 70% { content: '..'; } + 75%, 100% { content: '...'; } +} + +.generating-stop-btn { + background: transparent; + border: 1px solid var(--vscode-input-border); + color: var(--vscode-descriptionForeground); + font-size: 10px; + cursor: pointer; + padding: 2px 6px; + font-family: inherit; + transition: all 0.2s ease; + border-radius: 4px; + font-weight: normal; +} + +.generating-stop-btn:hover { + background: var(--vscode-list-hoverBackground); + color: var(--vscode-foreground); + border-color: var(--vscode-focusBorder); +} + +/* Responsive generating content for different layouts - simplified */ +.chat-message--panel .generating-content, +.chat-message--sidebar .generating-content { + margin-left: 0; + margin-right: 0; + padding-left: 0; + padding-right: 0; +} + + + +/* Chat placeholder */ +.chat-placeholder { + text-align: center; + color: var(--vscode-descriptionForeground); + padding: 0 20px 20px 20px; +} + +.chat-placeholder--panel { + padding: 0 20px 32px 20px; + font-size: 14px; +} + +.chat-placeholder--sidebar { + padding: 0 8px 16px 8px; + font-size: 11px; +} + +.chat-placeholder__features { + margin-top: 16px; +} + +.chat-placeholder__features ul { + text-align: left; + max-width: 300px; + margin: 12px auto; +} + +.chat-placeholder__features li { + margin: 8px 0; +} + +/* Empty state styles */ +.chat-placeholder__content { + margin-top: 20px; + max-width: 500px; + margin-left: auto; + margin-right: auto; +} + +.empty-state-message { + display: flex; + flex-direction: column; + gap: 16px; + align-items: center; + text-align: center; +} + +.empty-state-message p { + margin: 0; + line-height: 1.5; + color: var(--vscode-foreground); +} + +.empty-state-message strong { + color: var(--vscode-textLink-foreground); + font-weight: 600; +} + +.empty-state-message kbd { + background: var(--vscode-keybindingLabel-background); + border: 1px solid var(--vscode-keybindingLabel-border); + border-bottom: 2px solid var(--vscode-keybindingLabel-bottomBorder); + color: var(--vscode-keybindingLabel-foreground); + padding: 2px 6px; + border-radius: 3px; + font-size: 11px; + font-family: var(--vscode-editor-font-family); + white-space: nowrap; +} + +.empty-state-message code { + background: var(--vscode-textCodeBlock-background); + color: var(--vscode-textPreformat-foreground); + padding: 2px 4px; + border-radius: 3px; + font-size: 12px; + font-family: var(--vscode-editor-font-family); +} + +.empty-state-divider { + color: var(--vscode-descriptionForeground); + font-weight: 500; + font-size: 13px; + margin: 8px 0; + position: relative; +} + +.empty-state-divider::before, +.empty-state-divider::after { + content: ''; + position: absolute; + top: 50%; + width: 40px; + height: 1px; + background: var(--vscode-separator-foreground); + opacity: 0.3; +} + +.empty-state-divider::before { + right: calc(100% + 10px); +} + +.empty-state-divider::after { + left: calc(100% + 10px); +} + +.empty-state-message em { + color: var(--vscode-descriptionForeground); + font-style: italic; +} + +/* Sidebar layout adjustments */ +.chat-placeholder--sidebar .chat-placeholder__content { + margin-top: 16px; + max-width: 100%; + padding: 0 8px; +} + +.chat-placeholder--sidebar .empty-state-message { + gap: 12px; +} + +.chat-placeholder--sidebar .empty-state-message p { + font-size: 12px; + line-height: 1.4; +} + +.chat-placeholder--sidebar .empty-state-message kbd { + font-size: 10px; + padding: 1px 4px; +} + +.chat-placeholder--sidebar .empty-state-message code { + font-size: 10px; + padding: 1px 3px; +} + +.chat-placeholder--sidebar .empty-state-divider { + font-size: 11px; +} + +.chat-placeholder--sidebar .empty-state-divider::before, +.chat-placeholder--sidebar .empty-state-divider::after { + width: 20px; +} + +/* Responsive adjustments for empty state */ +@media (max-width: 600px) { + .chat-placeholder__content { + max-width: 90%; + margin-top: 16px; + } + + .empty-state-message { + gap: 12px; + } + + .empty-state-message p { + font-size: 13px; + } + + .empty-state-divider::before, + .empty-state-divider::after { + width: 20px; + } +} + +/* Input container - Cursor style - REMOVED */ + +/* Add Context Button */ +.add-context-btn { + display: flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + background: transparent; + color: var(--vscode-foreground); + border: 1px solid var(--vscode-input-border); + border-radius: 6px; + padding: 4px 4px; + margin: 6px 6px 6px 6px; + cursor: pointer; + font-size: 11px; + font-family: inherit; + transition: all 0.2s ease; + width: fit-content; + line-height: 1; +} + +.add-context-btn:hover:not(:disabled) { + background: var(--vscode-button-hoverBackground); + border-color: var(--vscode-focusBorder); +} + +.add-context-btn:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.add-context-icon { + font-weight: normal; + font-size: 11px; + line-height: 1; + display: inline-block; +} + +/* Message context display in user bubbles */ +.message-context-display { + display: inline-flex; + align-items: center; + justify-content: flex-start; + gap: 4px; + background: transparent; + color: var(--vscode-foreground); + border: 1px solid var(--vscode-input-border); + border-radius: 4px; + padding: 4px 8px; + margin-bottom: 8px; + font-size: 11px; + font-family: inherit; + width: fit-content; + line-height: 1; +} + +.message-context-display .context-icon { + font-weight: normal; + font-size: 11px; + line-height: 1; + display: inline-block; +} + +.message-context-display .context-text { + font-size: 11px; + line-height: 1; + color: var(--vscode-foreground); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; +} + +.message-text { + margin: 0; +} + +/* Main Input Wrapper */ +.chat-input-wrapper { + background: var(--vscode-input-background); + border: 1px solid var(--vscode-input-border); + border-radius: 12px; + overflow: hidden; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08); + transition: all 0.2s ease; + margin: 6px 6px 12px 6px; + margin-top: auto; + flex-shrink: 0; +} + +/* Remove focus highlight from chat input wrapper */ + +/* Input Controls */ +.input-controls { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 8px; + gap: 6px; +} + +.selectors-group { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 1; + min-width: 0; +} + +.selector-wrapper { + position: relative; + display: inline-block; +} + +/* Selector icon styles removed - handled by ModelSelector component */ + +.agent-selector { + background: transparent; + color: var(--vscode-foreground); + border: none; + outline: none; + font-size: 11px; + font-family: inherit; + cursor: pointer; + padding: 2px 8px 2px 18px; + border-radius: 4px; + transition: background-color 0.2s ease; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 12 4-4 4 4'/%3E%3C/svg%3E"); + background-position: right 6px center; + background-repeat: no-repeat; + background-size: 12px; + min-width: 120px; + white-space: nowrap; + font-weight: 500; +} + +.agent-selector:hover { + background: var(--vscode-list-hoverBackground); +} + +/* Chat input */ +.chat-input { + padding: 6px 8px 4px 8px; + position: relative; +} + +.message-input { + width: 100%; + padding: 0; + background: transparent; + color: var(--vscode-input-foreground); + border: none; + outline: none; + font-size: 14px; + font-family: inherit; + resize: none; + min-height: 24px; + max-height: 120px; + line-height: 1.5; + overflow-y: auto; + box-shadow: none; + transition: all 0.2s ease; +} + +.message-input:focus { + outline: none; + box-shadow: none; + border: none; +} + +.message-input::selection { + background: var(--vscode-editor-selectionBackground); +} + +.message-input::placeholder { + color: var(--vscode-input-placeholderForeground); + font-size: 14px; +} + +.chat-interface--sidebar .message-input { + font-size: 12px; +} + +.chat-interface--sidebar .message-input::placeholder { + font-size: 12px; +} + +/* Input Actions */ +.input-actions { + display: flex; + align-items: center; + gap: 4px; + flex-shrink: 0; +} + +.attach-btn, +.send-btn { + width: 24px; + height: 24px; + border: none; + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + flex-shrink: 0; + position: relative; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); +} + +.attach-btn { + background: transparent; + color: var(--vscode-descriptionForeground); + box-shadow: none; + opacity: 0.6; + border-radius: 0; +} + +.attach-btn:hover:not(:disabled) { + background: var(--vscode-list-hoverBackground); + color: var(--vscode-foreground); + opacity: 1; + border-radius: 50%; +} + +.send-btn { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); +} + +.send-btn:hover:not(:disabled) { + background: var(--vscode-button-hoverBackground); + transform: scale(1.05); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.send-btn:disabled, +.attach-btn:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; + box-shadow: none; +} + +.stop-btn { + background: var(--vscode-input-background) !important; + color: var(--vscode-foreground) !important; + border: 1px solid var(--vscode-input-border) !important; +} + +.stop-btn:hover:not(:disabled) { + background: var(--vscode-list-hoverBackground) !important; + border-color: var(--vscode-focusBorder) !important; + transform: scale(1.05); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15); +} + +.attach-btn svg { + width: 12px; + height: 12px; +} + +.send-btn svg { + width: 14px; + height: 14px; +} + +.chat-interface--sidebar .attach-btn, +.chat-interface--sidebar .send-btn { + width: 28px; + height: 28px; + font-size: 12px; +} + +/* Responsive adjustments */ +@media (max-width: 480px) { + .input-controls { + padding: 4px 6px; + gap: 4px; + } + + .selectors-group { + gap: 2px; + flex-shrink: 1; + min-width: 0; + } + + .agent-selector { + min-width: 80px; + font-size: 10px; + padding: 2px 6px 2px 16px; + background-size: 10px; + background-position: right 4px center; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3E%3Cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 12 4-4 4 4'/%3E%3C/svg%3E"); + } + + /* Selector icon responsive styles removed - handled by ModelSelector component */ + + .attach-btn, + .send-btn { + width: 20px; + height: 20px; + flex-shrink: 0; + } + + .attach-btn svg { + width: 10px; + height: 10px; + } + + .send-btn svg { + width: 12px; + height: 12px; + } + + .chat-input { + padding: 6px 8px 4px 8px; + } + + .message-input { + font-size: 13px; + } +} + +/* Streaming cursor animation - simplified */ +.streaming-cursor { + color: var(--vscode-descriptionForeground); + font-weight: normal; + animation: blink 1.2s ease-in-out infinite; + margin-left: 1px; +} + +@keyframes blink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.3; } +} + +/* Tool message styles - compact */ +.tool-message { + margin-bottom: 4px; + padding: 6px 8px; + border: 1px solid var(--vscode-input-border); + border-radius: 6px; + background: var(--vscode-input-background); + line-height: 1.2; + transition: all 0.2s ease; + position: relative; +} + +.tool-message:hover { + border-color: var(--vscode-focusBorder); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.tool-message--panel { + margin-bottom: 6px; + padding: 8px 10px; + border-radius: 8px; + font-size: 12px; +} + +.tool-message--sidebar { + margin-bottom: 3px; + padding: 6px 8px; + border-radius: 6px; + font-size: 11px; +} + +.tool-message--success { + border-left: 3px solid var(--vscode-charts-green); +} + +.tool-message--error { + background: var(--vscode-inputValidation-errorBackground); + border: 1px solid var(--vscode-inputValidation-errorBorder); + border-left: 3px solid var(--vscode-errorForeground); +} + +.tool-message--loading { + border-left: 3px solid var(--vscode-progressBar-background); +} + +.tool-message--complete { + border-left: 3px solid var(--vscode-charts-green); +} + +.tool-message__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 0; + font-weight: 600; + opacity: 0.8; + font-size: 11px; +} + +.chat-interface--sidebar .tool-message__header { + margin-bottom: 0; + font-size: 10px; +} + +/* Only add bottom margin when details are showing */ +.tool-message__header:has(+ .tool-message__details) { + margin-bottom: 8px; +} + +.chat-interface--sidebar .tool-message__header:has(+ .tool-message__details) { + margin-bottom: 6px; +} + +.tool-message__main { + display: flex; + align-items: center; + gap: 6px; + flex: 1; + min-width: 0; +} + +.tool-icon { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 14px; + height: 14px; + color: var(--vscode-descriptionForeground); +} + +/* Tool icon state-specific colors */ +.tool-message--success .tool-icon { + color: var(--vscode-charts-green); +} + +.tool-message--complete .tool-icon { + color: var(--vscode-charts-green); +} + +.tool-message--error .tool-icon { + color: var(--vscode-errorForeground); +} + +.tool-info { + display: flex; + flex-direction: column; + gap: 0; + flex: 1; + min-width: 0; +} + +.tool-name { + font-weight: 500; + color: var(--vscode-foreground); + flex-shrink: 0; + font-size: 11px; + line-height: 1.2; +} + +.tool-description { + color: var(--vscode-descriptionForeground); + font-size: 10px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + line-height: 1.2; +} + +.tool-actions { + display: flex; + align-items: center; + gap: 2px; + flex-shrink: 0; +} + +.tool-status { + display: flex; + align-items: center; + justify-content: center; + width: 12px; + height: 12px; + flex-shrink: 0; + color: var(--vscode-charts-green); +} + +.tool-status--success { + color: var(--vscode-charts-green); +} + +.tool-status--complete { + color: var(--vscode-charts-green); +} + +.tool-status--error { + color: var(--vscode-errorForeground); +} + +.tool-expand-btn { + background: transparent; + border: none; + color: var(--vscode-descriptionForeground); + cursor: pointer; + padding: 1px; + border-radius: 2px; + transition: all 0.2s ease; + flex-shrink: 0; + width: 14px; + height: 14px; + display: flex; + align-items: center; + justify-content: center; +} + +.tool-expand-btn:hover { + background: var(--vscode-toolbar-hoverBackground); + color: var(--vscode-foreground); +} + +.tool-expand-btn.expanded { + transform: rotate(45deg); + color: var(--vscode-foreground); +} + +.tool-message__details { + margin-top: 6px; + padding-top: 6px; + border-top: 1px solid var(--vscode-input-border); +} + +.tool-detail { + margin-bottom: 8px; +} + +.tool-detail:last-child { + margin-bottom: 0; +} + +.tool-detail__label { + display: block; + font-weight: 600; + color: var(--vscode-descriptionForeground); + font-size: 9px; + margin-bottom: 2px; + text-transform: uppercase; + letter-spacing: 0.3px; + line-height: 1.2; +} + +.tool-detail__value { + display: block; + color: var(--vscode-editor-foreground); + font-size: 10px; + line-height: 1.3; +} + +.tool-detail__value--text { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-widget-border); + border-radius: 3px; + padding: 4px 6px; + white-space: pre-wrap; + word-wrap: break-word; +} + +.tool-detail__value--json { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-widget-border); + border-radius: 3px; + padding: 4px 6px; + font-family: var(--vscode-editor-font-family); + font-size: 9px; + overflow-x: auto; + max-height: 150px; + overflow-y: auto; +} + +/* Details element styling */ +.tool-detail details { + margin: 0; +} + +.tool-detail details summary { + cursor: pointer; + padding: 2px 0; + font-weight: 600; + color: var(--vscode-descriptionForeground); + font-size: 9px; + text-transform: uppercase; + letter-spacing: 0.3px; + transition: color 0.2s ease; +} + +.tool-detail details summary:hover { + color: var(--vscode-foreground); +} + +.tool-detail details[open] summary { + margin-bottom: 4px; +} + +.tool-detail__value--result { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-widget-border); + border-radius: 3px; + padding: 0; + overflow: hidden; +} + +.tool-detail__value--error { + border-color: var(--vscode-errorForeground); + background: var(--vscode-inputValidation-errorBackground); +} + +.tool-result-content { + padding: 6px 8px; + margin: 0; + font-family: var(--vscode-editor-font-family); + font-size: 9px; + line-height: 1.3; + white-space: pre-wrap; + word-wrap: break-word; + max-height: 200px; + overflow-y: auto; + background: transparent; + border: none; +} + +.tool-result__show-more { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-top: 1px solid var(--vscode-widget-border); + width: 100%; + padding: 4px 8px; + font-size: 9px; + cursor: pointer; + transition: background-color 0.15s ease; +} + +.tool-result__show-more:hover { + background: var(--vscode-button-hoverBackground); +} + +.tool-result__show-more:active { + background: var(--vscode-button-background); +} + +/* Tool result styles */ +.tool-result { + margin-bottom: 6px; + border: 1px solid var(--vscode-widget-border); + border-radius: 4px; + background: var(--vscode-editor-background); + overflow: hidden; +} + +.tool-result--panel { + margin-bottom: 8px; +} + +.tool-result--sidebar { + margin-bottom: 4px; +} + +.tool-result--success { + border-left: 2px solid var(--vscode-charts-green); +} + +.tool-result--error { + border-left: 2px solid var(--vscode-errorForeground); +} + +.tool-result__header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 4px 6px; + cursor: pointer; + background: transparent; + border-bottom: 1px solid transparent; + transition: background-color 0.15s ease; +} + +.tool-result__header:hover { + background: var(--vscode-list-hoverBackground); +} + +.tool-result__main { + display: flex; + align-items: center; + gap: 4px; + flex: 1; + min-width: 0; +} + +.tool-result-icon { + font-size: 10px; + flex-shrink: 0; +} + +.tool-result-label { + font-weight: 600; + color: var(--vscode-foreground); + flex-shrink: 0; + font-size: 9px; +} + +.tool-result-meta { + color: var(--vscode-descriptionForeground); + font-size: 8px; + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + padding: 1px 3px; + border-radius: 6px; + flex-shrink: 0; +} + +.tool-result__content { + padding: 6px 8px; + border-top: 1px solid var(--vscode-widget-border); + background: var(--vscode-editor-background); +} + +.tool-result__text { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-widget-border); + border-radius: 3px; + padding: 6px 8px; + font-family: var(--vscode-editor-font-family); + font-size: 9px; + line-height: 1.3; + white-space: pre-wrap; + word-wrap: break-word; + margin: 0; + max-height: 250px; + overflow-y: auto; +} + +.tool-result__show-more { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: none; + border-radius: 3px; + padding: 3px 6px; + font-size: 9px; + cursor: pointer; + margin-top: 4px; + transition: background-color 0.15s ease; +} + +.tool-result__show-more:hover { + background: var(--vscode-button-hoverBackground); +} + +.tool-result__show-more:active { + background: var(--vscode-button-background); +} + +.loading-spinner { + animation: spin 1s linear infinite; +} + +@keyframes spin { + from { transform: rotate(0deg); } + to { transform: rotate(360deg); } +} + +/* Tool group styles - compact */ +.tool-group { + margin-bottom: 8px; + padding: 6px 8px; + border: 1px solid var(--vscode-input-border); + border-radius: 6px; + background: var(--vscode-input-background); + line-height: 1.2; + transition: all 0.2s ease; + position: relative; +} + +.tool-group:hover { + border-color: var(--vscode-focusBorder); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); +} + +.tool-group--panel { + margin-bottom: 10px; + padding: 8px 10px; + border-radius: 8px; + font-size: 12px; +} + +.tool-group--sidebar { + margin-bottom: 6px; + padding: 6px 8px; + border-radius: 6px; + font-size: 11px; +} + +.tool-group--success { + border-left: 3px solid var(--vscode-charts-green); +} + +.tool-group--error { + background: var(--vscode-inputValidation-errorBackground); + border: 1px solid var(--vscode-inputValidation-errorBorder); + border-left: 3px solid var(--vscode-errorForeground); +} + +.tool-group--loading { + border-left: 3px solid var(--vscode-progressBar-background); +} + +.tool-group--complete { + border-left: 3px solid var(--vscode-charts-green); +} + +.tool-group__header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 8px; + margin-bottom: 0; + font-weight: 600; + opacity: 0.8; + font-size: 11px; +} + +.chat-interface--sidebar .tool-group__header { + margin-bottom: 0; + font-size: 10px; +} + +/* Only add bottom margin when children are showing */ +.tool-group__header:has(+ .tool-group__children) { + margin-bottom: 8px; +} + +.chat-interface--sidebar .tool-group__header:has(+ .tool-group__children) { + margin-bottom: 6px; +} + +.tool-group__main { + display: flex; + align-items: center; + gap: 6px; + flex: 1; + min-width: 0; +} + +.tool-group-icon { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + width: 14px; + height: 14px; + color: var(--vscode-descriptionForeground); +} + +/* Tool group icon state-specific colors */ +.tool-group--success .tool-group-icon { + color: var(--vscode-charts-green); +} + +.tool-group--complete .tool-group-icon { + color: var(--vscode-charts-green); +} + +.tool-group--error .tool-group-icon { + color: var(--vscode-errorForeground); +} + +.tool-group-info { + display: flex; + flex-direction: column; + gap: 0; + flex: 1; + min-width: 0; +} + +.tool-group-name { + font-weight: 500; + color: var(--vscode-foreground); + flex-shrink: 0; + font-size: 11px; + line-height: 1.2; +} + +.tool-time-remaining { + display: flex; + align-items: center; + gap: 2px; + font-size: 10px; + color: var(--vscode-descriptionForeground); + font-weight: normal; + line-height: 1.2; +} + +.tool-group-actions { + display: flex; + align-items: center; + gap: 2px; + flex-shrink: 0; +} + +.tool-group-count { + background: var(--vscode-badge-background); + color: var(--vscode-badge-foreground); + font-size: 9px; + padding: 1px 3px; + border-radius: 2px; + font-weight: 500; + flex-shrink: 0; +} + +/* Simple loading icon */ +.loading-icon-simple { + position: relative; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; +} + +.loading-ring { + width: 12px; + height: 12px; + border: 1px solid var(--vscode-list-inactiveSelectionBackground); + border-top: 1px solid var(--vscode-progressBar-background); + border-radius: 50%; + animation: spin-simple 1s linear infinite; +} + +@keyframes spin-simple { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +.tool-group__children { + margin-top: 6px; + padding-top: 6px; + border-top: 1px solid var(--vscode-input-border); +} + +.tool-group__children .tool-message { + margin-bottom: 6px; + margin-left: 8px; + border-left: 2px solid var(--vscode-input-border); + padding-left: 6px; + background: transparent; + border-radius: 4px; + padding: 4px 6px; +} + +.tool-group__children .tool-message:last-child { + margin-bottom: 0; +} + +/* Context Display */ +.context-display { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background: transparent; + border: 1px solid var(--vscode-input-border); + border-radius: 4px; + font-size: 11px; + margin-left: 6px; + margin-top: 6px; + width: fit-content; +} + +.context-icon { + font-size: 12px; + opacity: 0.8; +} + +.context-text { + color: var(--vscode-descriptionForeground); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 200px; +} + +.context-clear-btn { + background: none; + border: none; + color: var(--vscode-descriptionForeground); + cursor: pointer; + font-size: 14px; + padding: 0; + margin-left: 4px; + opacity: 0.6; + transition: opacity 0.2s; +} + +.context-clear-btn:hover { + opacity: 1; +} + +/* Upload Progress */ +.upload-progress { + margin-left: 6px; + margin-top: 6px; + display: flex; + flex-direction: column; + gap: 4px; +} + +.upload-summary { + font-size: 11px; + color: var(--vscode-button-background); + font-weight: 600; + margin-bottom: 2px; + padding-left: 2px; +} + +.uploading-item { + display: flex; + align-items: center; + gap: 6px; + padding: 4px 8px; + background: var(--vscode-editor-hoverHighlightBackground); + border: 1px solid var(--vscode-input-border); + border-radius: 4px; + font-size: 11px; + width: fit-content; + max-width: 300px; +} + +.upload-icon { + font-size: 12px; + opacity: 0.8; +} + +.upload-text { + color: var(--vscode-descriptionForeground); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.upload-spinner { + width: 12px; + height: 12px; + border: 1px solid var(--vscode-input-border); + border-top: 1px solid var(--vscode-button-background); + border-radius: 50%; + animation: spin 1s linear infinite; +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +/* Loading Tips */ +.tool-loading-tips { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-input-border); + border-radius: 6px; + padding: 12px; + margin-bottom: 12px; +} + +.loading-tip { + display: flex; + align-items: flex-start; + gap: 8px; +} + +.tip-icon { + display: flex; + align-items: center; + justify-content: center; + flex-shrink: 0; + color: var(--vscode-descriptionForeground); + opacity: 0.8; +} + +.tip-text { + font-size: 12px; + line-height: 1.4; + color: var(--vscode-foreground); + font-style: italic; + animation: tipFadeIn 0.5s ease-in; +} + +@keyframes tipFadeIn { + 0% { opacity: 0; transform: translateY(5px); } + 100% { opacity: 1; transform: translateY(0); } +} + +/* Enhanced tool message states */ +.tool-message--loading .tool-icon { + color: var(--vscode-progressBar-background); + animation: pulse-loading 2s ease-in-out infinite; +} + +/* Enhanced tool group states */ +.tool-group--loading .tool-group-icon { + color: var(--vscode-progressBar-background); + animation: pulse-loading 2s ease-in-out infinite; +} + +/* Tool status enhancements - simplified */ +.tool-status--loading { + color: var(--vscode-descriptionForeground); + animation: rotate 2s linear infinite; +} + +@keyframes rotate { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} + +@keyframes pulse-loading { + 0%, 100% { opacity: 0.5; } + 50% { opacity: 1; } +} + +/* Responsive adjustments for mobile */ +@media (max-width: 768px) { + .tip-text { + font-size: 11px; + } + + .tool-group-info { + gap: 1px; + } + + .tool-time-remaining { + font-size: 10px; + } +} + +/* Sidebar Action Bar - VS Code style action buttons */ +.sidebar-action-bar { + position: absolute; + top: 8px; + right: 12px; + z-index: 100; + display: flex; + gap: 4px; + align-items: center; +} + +.sidebar-action-btn { + background: transparent; + border: none; + color: var(--vscode-icon-foreground); + cursor: pointer; + padding: 2px; + border-radius: 3px; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + opacity: 0.7; + width: 22px; + height: 22px; +} + +.sidebar-action-btn:hover:not(:disabled) { + background: var(--vscode-toolbar-hoverBackground); + opacity: 1; +} + +.sidebar-action-btn:active { + background: var(--vscode-toolbar-activeBackground); +} + +.sidebar-action-btn:disabled { + opacity: 0.4; + cursor: not-allowed; + background: transparent; +} + +/* Panel Action Bar */ +.chat-action-bar { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 12px; + background: var(--vscode-titleBar-activeBackground); + border-bottom: 1px solid var(--vscode-panel-border); + min-height: 32px; + flex-shrink: 0; +} + +.open-canvas-btn { + display: flex; + align-items: center; + gap: 6px; + background: transparent; + border: none; + color: var(--vscode-titleBar-activeForeground); + cursor: pointer; + padding: 4px 8px; + border-radius: 4px; + font-size: 12px; + font-weight: 500; + transition: all 0.2s ease; +} + +.open-canvas-btn:hover { + background: var(--vscode-titleBar-hoverBackground); + color: var(--vscode-titleBar-hoverForeground); +} + +.open-canvas-btn svg { + flex-shrink: 0; +} + +.clear-chat-icon-btn { + background: transparent; + border: none; + color: var(--vscode-titleBar-activeForeground); + cursor: pointer; + padding: 4px; + border-radius: 3px; + display: flex; + align-items: center; + justify-content: center; + width: 24px; + height: 24px; + transition: all 0.2s ease; + opacity: 0.8; +} + +.clear-chat-icon-btn:hover:not(:disabled) { + background: var(--vscode-titleBar-hoverBackground); + color: var(--vscode-titleBar-hoverForeground); + opacity: 1; +} + +.clear-chat-icon-btn:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +.chat-interface { +} + +/* Markdown content styles */ +.markdown-content { + line-height: 1.3; + word-wrap: break-word; + overflow-wrap: break-word; +} + +.markdown-content h1, +.markdown-content h2, +.markdown-content h3, +.markdown-content h4, +.markdown-content h5, +.markdown-content h6 { + margin: 16px 0 8px 0; + font-weight: 600; + line-height: 1.2; + color: var(--vscode-editor-foreground); +} + +.markdown-content h1:first-child, +.markdown-content h2:first-child, +.markdown-content h3:first-child, +.markdown-content h4:first-child, +.markdown-content h5:first-child, +.markdown-content h6:first-child { + margin-top: 0; +} + +.markdown-content h1 { + font-size: 1.8em; +} + +.markdown-content h2 { + font-size: 1.5em; +} + +.markdown-content h3 { + font-size: 1.25em; +} + +.markdown-content h4 { + font-size: 1.1em; +} + +.markdown-content p { + margin: 8px 0; + line-height: 1.5; +} + +.markdown-content p:first-child { + margin-top: 0; +} + +.markdown-content p:last-child { + margin-bottom: 0; +} + +.markdown-content ul, +.markdown-content ol { + margin: 8px 0; + padding-left: 24px; +} + +.markdown-content ul:first-child, +.markdown-content ol:first-child { + margin-top: 0; +} + +.markdown-content ul:last-child, +.markdown-content ol:last-child { + margin-bottom: 0; +} + +.markdown-content li { + margin: 2px 0; + line-height: 1.4; +} + +.markdown-content li p { + margin: 2px 0; +} + +/* Nested lists */ +.markdown-content li ul, +.markdown-content li ol { + margin: 4px 0; + padding-left: 20px; +} + +.markdown-content pre { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-panel-border); + border-radius: 6px; + padding: 8px; + margin: 4px 0; + overflow-x: auto; + font-family: var(--vscode-editor-font-family); + font-size: 13px; + line-height: 1.3; +} + +.markdown-content pre code { + background: transparent; + padding: 0; + border: none; + border-radius: 0; + font-size: inherit; +} + +.markdown-content .inline-code { + background: var(--vscode-textCodeBlock-background); + border: 1px solid var(--vscode-panel-border); + border-radius: 3px; + padding: 2px 4px; + font-family: var(--vscode-editor-font-family); + font-size: 0.9em; + color: var(--vscode-textPreformat-foreground); +} + +.markdown-content a { + color: var(--vscode-textLink-foreground); + text-decoration: none; +} + +.markdown-content a:hover { + color: var(--vscode-textLink-activeForeground); + text-decoration: underline; +} + +.markdown-content .table-wrapper { + overflow-x: auto; + margin: 8px 0; +} + +.markdown-content table { + border-collapse: collapse; + width: 100%; + border: 1px solid var(--vscode-panel-border); +} + +.markdown-content th, +.markdown-content td { + border: 1px solid var(--vscode-panel-border); + padding: 6px 10px; + text-align: left; +} + +.markdown-content th { + background: var(--vscode-editorWidget-background); + font-weight: 600; +} + +.markdown-content .markdown-blockquote { + border-left: 4px solid var(--vscode-textLink-foreground); + margin: 8px 0; + padding: 6px 12px; + background: var(--vscode-textBlockQuote-background); + font-style: italic; + color: var(--vscode-textBlockQuote-foreground); +} + +.markdown-content hr { + border: none; + border-top: 1px solid var(--vscode-panel-border); + margin: 12px 0; +} + +.markdown-content strong { + font-weight: 600; + color: var(--vscode-editor-foreground); +} + +.markdown-content em { + font-style: italic; +} + +/* Syntax highlighting styles for VS Code theme compatibility */ +.markdown-content .hljs { + background: transparent; + color: var(--vscode-editor-foreground); +} + +.markdown-content .hljs-keyword, +.markdown-content .hljs-built_in { + color: var(--vscode-symbolIcon-keywordForeground, #569cd6); +} + +.markdown-content .hljs-string, +.markdown-content .hljs-title { + color: var(--vscode-symbolIcon-stringForeground, #ce9178); +} + +.markdown-content .hljs-comment { + color: var(--vscode-symbolIcon-colorForeground, #6a9955); + font-style: italic; +} + +.markdown-content .hljs-number { + color: var(--vscode-symbolIcon-numberForeground, #b5cea8); +} + +.markdown-content .hljs-function { + color: var(--vscode-symbolIcon-functionForeground, #dcdcaa); +} + +.markdown-content .hljs-variable { + color: var(--vscode-symbolIcon-variableForeground, #9cdcfe); +} + +/* Responsive adjustments for sidebar */ +.chat-interface--sidebar .markdown-content h1 { + font-size: 1.4em; + margin: 12px 0 6px 0; +} + +.chat-interface--sidebar .markdown-content h2 { + font-size: 1.2em; + margin: 12px 0 6px 0; +} + +.chat-interface--sidebar .markdown-content h3 { + font-size: 1.1em; + margin: 10px 0 4px 0; +} + +.chat-interface--sidebar .markdown-content h4 { + margin: 8px 0 4px 0; +} + +.chat-interface--sidebar .markdown-content p { + margin: 6px 0; + line-height: 1.4; +} + +.chat-interface--sidebar .markdown-content ul, +.chat-interface--sidebar .markdown-content ol { + margin: 6px 0; + padding-left: 20px; +} + +.chat-interface--sidebar .markdown-content li { + margin: 1px 0; + line-height: 1.3; +} + +.chat-interface--sidebar .markdown-content pre { + padding: 6px; + font-size: 11px; + margin: 6px 0; +} + +.chat-interface--sidebar .markdown-content .inline-code { + font-size: 0.85em; +} + +.chat-interface--sidebar .markdown-content table { + font-size: 11px; +} + +.chat-interface--sidebar .markdown-content th, +.chat-interface--sidebar .markdown-content td { + padding: 3px 5px; +} + +/* Drag & Drop Overlay */ + +/* Error message with action buttons */ +.error-message-content { + margin-bottom: 8px; + line-height: 1.4; + position: relative; + padding-right: 24px; /* Add space for close button in sidebar mode */ + color: var(--vscode-errorForeground); +} + +.error-actions { + display: flex; + gap: 8px; + margin-top: 8px; +} + +.error-action-btn { + background: var(--vscode-button-background); + color: var(--vscode-button-foreground); + border: 1px solid var(--vscode-button-border); + border-radius: 4px; + padding: 6px 12px; + font-size: 12px; + cursor: pointer; + transition: all 0.2s ease; + font-family: inherit; +} + +.error-action-btn:hover { + background: var(--vscode-button-hoverBackground); + border-color: var(--vscode-button-hoverBackground); +} + +.error-action-btn:active { + background: var(--vscode-button-activeBackground); + transform: scale(0.98); +} + +.error-action-btn:focus { + outline: 2px solid var(--vscode-focusBorder); + outline-offset: 2px; +} + +.error-close-btn { + background: none; + border: none; + color: var(--vscode-descriptionForeground); + cursor: pointer; + font-size: 18px; + font-weight: bold; + padding: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 3px; + transition: all 0.15s ease; + opacity: 0.6; + flex-shrink: 0; +} + +.error-close-btn:hover { + background: var(--vscode-toolbar-hoverBackground); + opacity: 1; + color: var(--vscode-foreground); +} + +.error-close-btn:active { + background: var(--vscode-toolbar-activeBackground); +} + +.error-close-btn--sidebar { + position: absolute; + top: 8px; + right: 8px; + font-size: 16px; + width: 18px; + height: 18px; +} + +/* Panel specific styling */ +.chat-interface--panel .error-actions { + gap: 10px; +} + +.chat-interface--panel .error-action-btn { + padding: 8px 16px; + font-size: 13px; +} + +/* Theme Tool Message Styles - Added for generateTheme tool calls */ +.theme-tool-message { + margin-bottom: 4px; + padding: 0; + border: none; + border-radius: 0; + background: transparent; + transition: all 0.2s ease; + position: relative; +} + +.theme-tool-message--panel { + margin-bottom: 4px; + padding: 0; +} + +.theme-tool-message--sidebar { + margin-bottom: 4px; + padding: 0; +} + +.theme-error-notice { + margin: 0.5rem 0; + padding: 0.75rem; + background-color: var(--vscode-inputValidation-errorBackground); + color: var(--vscode-inputValidation-errorForeground); + border: 1px solid var(--vscode-inputValidation-errorBorder); + border-left: 3px solid var(--vscode-errorForeground); + border-radius: 0.375rem; + font-size: 0.875rem; + font-family: var(--vscode-editor-font-family); +} + +/* Ensure theme preview cards use proper VSCode CSS variables */ +.theme-tool-message .theme-preview-card { + /* Override any hardcoded colors with VSCode theme variables */ + --background: var(--vscode-editor-background); + --foreground: var(--vscode-editor-foreground); + --muted: var(--vscode-input-background); + --muted-foreground: var(--vscode-descriptionForeground); + --border: var(--vscode-input-border); + --card: var(--vscode-editorWidget-background); + --card-foreground: var(--vscode-editorWidget-foreground); + --primary: var(--vscode-textLink-foreground); + --primary-foreground: var(--vscode-button-foreground); + --secondary: var(--vscode-button-secondaryBackground); + --secondary-foreground: var(--vscode-button-secondaryForeground); + --accent: var(--vscode-list-hoverBackground); + --accent-foreground: var(--vscode-list-hoverForeground); + --destructive: var(--vscode-errorForeground); + --destructive-foreground: var(--vscode-button-foreground); + --ring: var(--vscode-focusBorder); + --input: var(--vscode-input-background); + --font-mono: var(--vscode-editor-font-family); +} + +/* Responsive theme tool message styling */ +@media (max-width: 768px) { + .theme-tool-message--panel { + margin-bottom: 16px; + } + + .theme-tool-message--sidebar { + margin-bottom: 10px; + } +} + +/* Tool result content */ + +/* Remove double margins when chat-message contains tool messages */ +.chat-message--tool-container { + margin-bottom: 0 !important; + padding: 0 !important; + background: transparent !important; + border: none !important; +} + +.chat-message--tool-container .chat-message__header { + display: none !important; +} + +.chat-message--tool-container .chat-message__content { + padding: 0 !important; +} + +/* Mixed content messages (text + tool calls) */ +.chat-message--mixed-content { + background: transparent; + border: none; + margin-bottom: 12px; + padding: 0; +} + +.chat-message--mixed-content .chat-message__content { + margin-bottom: 6px; +} + +.chat-message--mixed-content .chat-message__tools { + margin-top: 4px; +} + +.chat-message--mixed-content .chat-message__tools .tool-message { + margin-bottom: 4px; +} + +.chat-message--mixed-content .chat-message__tools .theme-tool-message { + margin-bottom: 4px; +} + +/* Multiple tool calls container */ +.tool-calls-container { + display: flex; + flex-direction: column; + gap: 4px; +} + +.tool-calls-container .tool-message { + margin-bottom: 0; +} + +.tool-calls-container .theme-tool-message { + margin-bottom: 0; +} + +/* Theme copy button styling */ +.theme-copy-btn:hover { + background: var(--vscode-list-hoverBackground) !important; + color: var(--vscode-foreground) !important; +} + + + \ No newline at end of file diff --git a/src/webview/components/Chat/ChatInterface.tsx b/src/webview/components/Chat/ChatInterface.tsx new file mode 100644 index 0000000..1ede6e6 --- /dev/null +++ b/src/webview/components/Chat/ChatInterface.tsx @@ -0,0 +1,1517 @@ +import React, { useState, useEffect, useRef } from 'react'; +import { useChat, ChatMessage } from '../../hooks/useChat'; +import { useFirstTimeUser } from '../../hooks/useFirstTimeUser'; +import { WebviewLayout } from '../../../types/context'; +import MarkdownRenderer from '../MarkdownRenderer'; +import { TaskIcon, ClockIcon, CheckIcon, LightBulbIcon, GroupIcon, BrainIcon } from '../Icons'; +import Welcome from '../Welcome'; +import ThemePreviewCard from './ThemePreviewCard'; +import ModelSelector from './ModelSelector'; +import chatStyles from './ChatInterface.css'; +import welcomeStyles from '../Welcome/Welcome.css'; + +interface ChatInterfaceProps { + layout: WebviewLayout; + vscode: any; +} + +const ChatInterface: React.FC = ({ layout, vscode }) => { + const { chatHistory, isLoading, sendMessage, clearHistory, setChatHistory } = useChat(vscode); + const { isFirstTime, isLoading: isCheckingFirstTime, markAsReturningUser, resetFirstTimeUser } = useFirstTimeUser(); + const [inputMessage, setInputMessage] = useState(''); + const [selectedModel, setSelectedModel] = useState('claude-3-5-sonnet-20241022'); + const [expandedTools, setExpandedTools] = useState>({}); + const [showFullContent, setShowFullContent] = useState<{[key: string]: boolean}>({}); + const [currentContext, setCurrentContext] = useState<{fileName: string; type: string} | null>(null); + const [showWelcome, setShowWelcome] = useState(false); + + // Drag and drop state + const [uploadingImages, setUploadingImages] = useState([]); + const [pendingImages, setPendingImages] = useState<{fileName: string; originalName: string; fullPath: string}[]>([]); + const [toolTimers, setToolTimers] = useState>({}); + const timerIntervals = useRef>({}); + + // Helper function to check if we have meaningful conversation messages + const hasConversationMessages = () => { + return chatHistory.some(msg => + msg.role === 'user' || + (msg.role === 'assistant' && typeof msg.content === 'string' && msg.content.trim().length > 0) || + (msg.role === 'assistant' && Array.isArray(msg.content) && msg.content.some(part => part.type === 'text' && (part as any).text?.trim().length > 0)) + ); + }; + + // Request current provider on mount + useEffect(() => { + vscode.postMessage({ + command: 'getCurrentProvider' + }); + + const handleMessage = (event: MessageEvent) => { + const message = event.data; + if (message.command === 'currentProviderResponse') { + let fallbackModel: string; + switch (message.provider) { + case 'openai': + fallbackModel = 'gpt-4o'; + break; + case 'openrouter': + fallbackModel = 'anthropic/claude-3-7-sonnet-20250219'; + break; + case 'anthropic': + default: + fallbackModel = 'claude-3-5-sonnet-20241022'; + break; + } + setSelectedModel(message.model || fallbackModel); + } else if (message.command === 'providerChanged') { + setSelectedModel(message.model); + } + }; + + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); + }, []); + + const handleModelChange = (model: string) => { + // Send model change request to extension + vscode.postMessage({ + command: 'changeProvider', + model: model + }); + }; + + useEffect(() => { + // Inject ChatInterface CSS styles + const styleId = 'chat-interface-styles'; + let styleElement = document.getElementById(styleId) as HTMLStyleElement; + + if (!styleElement) { + styleElement = document.createElement('style'); + styleElement.id = styleId; + styleElement.textContent = chatStyles; + document.head.appendChild(styleElement); + } + + // Inject Welcome CSS styles + const welcomeStyleId = 'welcome-styles'; + let welcomeStyleElement = document.getElementById(welcomeStyleId) as HTMLStyleElement; + + if (!welcomeStyleElement) { + welcomeStyleElement = document.createElement('style'); + welcomeStyleElement.id = welcomeStyleId; + welcomeStyleElement.textContent = welcomeStyles; + document.head.appendChild(welcomeStyleElement); + } + + // Auto-open canvas if not already open + const autoOpenCanvas = () => { + // Check if canvas panel is already open by looking for the canvas webview + vscode.postMessage({ + command: 'checkCanvasStatus' + }); + + // Listen for canvas status response and context messages + const handleMessage = (event: MessageEvent) => { + const message = event.data; + if (message.command === 'canvasStatusResponse') { + if (!message.isOpen) { + // Canvas is not open, auto-open it + console.log('🎨 Auto-opening canvas view...'); + vscode.postMessage({ + command: 'autoOpenCanvas' + }); + } + } else if (message.command === 'contextFromCanvas') { + // Handle context from canvas + console.log('📄 Received context from canvas:', message.data); + console.log('📄 Current context before setting:', currentContext); + if (message.data.type === 'clear' || !message.data.fileName) { + setCurrentContext(null); + console.log('📄 Context cleared'); + } else { + setCurrentContext(message.data); + console.log('📄 Context set to:', message.data); + } + } else if (message.command === 'imageSavedToMoodboard') { + // Handle successful image save with full path + console.log('📎 Image saved with full path:', message.data); + setPendingImages(prev => [...prev, { + fileName: message.data.fileName, + originalName: message.data.originalName, + fullPath: message.data.fullPath + }]); + // Remove from uploading state + setUploadingImages(prev => prev.filter(name => name !== message.data.originalName)); + } else if (message.command === 'imageSaveError') { + // Handle image save error + console.error('📎 Image save error:', message.data); + setUploadingImages(prev => prev.filter(name => name !== message.data.originalName)); + } else if (message.command === 'clearChat') { + // Handle clear chat command from toolbar + handleNewConversation(); + } else if (message.command === 'resetWelcome') { + // Handle reset welcome command from command palette + resetFirstTimeUser(); + setShowWelcome(true); + console.log('👋 Welcome screen reset and shown'); + } else if (message.command === 'setChatPrompt') { + // Handle prompt from canvas floating buttons + console.log('📝 Received prompt from canvas:', message.data.prompt); + setInputMessage(message.data.prompt); + } + }; + + window.addEventListener('message', handleMessage); + + // Cleanup listener + return () => { + window.removeEventListener('message', handleMessage); + }; + }; + + // Delay the check slightly to ensure chat is fully loaded + const timeoutId = setTimeout(autoOpenCanvas, 500); + + return () => { + clearTimeout(timeoutId); + // Clean up on unmount + const existingStyle = document.getElementById(styleId); + if (existingStyle) { + document.head.removeChild(existingStyle); + } + const existingWelcomeStyle = document.getElementById(welcomeStyleId); + if (existingWelcomeStyle) { + document.head.removeChild(existingWelcomeStyle); + } + }; + }, [vscode]); + + // Handle first-time user welcome display + useEffect(() => { + if (!isCheckingFirstTime && isFirstTime && !hasConversationMessages()) { + setShowWelcome(true); + console.log('👋 Showing welcome for first-time user'); + } + }, [isCheckingFirstTime, isFirstTime, chatHistory]); + + // Auto-collapse tools when new messages arrive + useEffect(() => { + const handleAutoCollapse = () => { + setExpandedTools(prev => { + const newState = { ...prev }; + const toolIndices = chatHistory + .map((msg, index) => ({ msg, index })) + .filter(({ msg }) => msg.role === 'tool') + .map(({ index }) => index); + + // Keep only the last tool/tool-result expanded + if (toolIndices.length > 1) { + const lastToolIndex = toolIndices[toolIndices.length - 1]; + toolIndices.forEach(index => { + if (index !== lastToolIndex) { + newState[index] = false; + } + }); + } + + return newState; + }); + }; + + window.addEventListener('autoCollapseTools', handleAutoCollapse); + return () => window.removeEventListener('autoCollapseTools', handleAutoCollapse); + }, [chatHistory]); + + const handleSendMessage = async () => { + if (inputMessage.trim()) { + let messageContent: any; + + console.log('📤 Sending message with context:', currentContext); + console.log('📤 Input message:', inputMessage); + + // Check if we have image context to include + if (currentContext && (currentContext.type === 'image' || currentContext.type === 'images')) { + try { + // Create structured content with text and images + const contentParts: any[] = [ + { + type: 'text', + text: inputMessage + } + ]; + + // Process image context + const imagePaths = currentContext.type === 'images' + ? currentContext.fileName.split(', ') + : [currentContext.fileName]; + + // Convert each image to base64 + for (const imagePath of imagePaths) { + try { + // Request base64 data from the extension + const base64Data = await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error('Timeout waiting for base64 data')); + }, 10000); + + const handler = (event: MessageEvent) => { + const message = event.data; + if (message.command === 'base64ImageResponse' && message.filePath === imagePath) { + clearTimeout(timeoutId); + window.removeEventListener('message', handler); + if (message.error) { + reject(new Error(message.error)); + } else { + resolve(message.base64Data); + } + } + }; + + window.addEventListener('message', handler); + + // Request base64 data from extension + vscode.postMessage({ + command: 'getBase64Image', + filePath: imagePath + }); + }); + + // Extract MIME type from base64 data URL + const mimeMatch = base64Data.match(/^data:([^;]+);base64,/); + const mimeType = mimeMatch ? mimeMatch[1] : 'image/png'; + const base64Content = base64Data.replace(/^data:[^;]+;base64,/, ''); + + contentParts.push({ + type: 'image', + image: base64Content, + mimeType: mimeType + }); + + console.log('📎 Added image to message:', imagePath, 'MIME:', mimeType); + } catch (error) { + console.error('Failed to load image:', imagePath, error); + // Add error note to text content instead + contentParts[0].text += `\n\n[Note: Could not load image ${imagePath}: ${error}]`; + } + } + + messageContent = contentParts; + console.log('📤 Final structured message content:', contentParts.length, 'parts'); + } catch (error) { + console.error('Error processing images:', error); + // Fallback to text-only message with context info + messageContent = currentContext.type === 'images' + ? `Context: Multiple images in moodboard\n\nMessage: ${inputMessage}` + : `Context: ${currentContext.fileName}\n\nMessage: ${inputMessage}`; + } + } else if (currentContext) { + // Non-image context - use simple text format + messageContent = `Context: ${currentContext.fileName}\n\nMessage: ${inputMessage}`; + console.log('📤 Final message with non-image context:', messageContent); + } else { + // No context - just the message text + messageContent = inputMessage; + console.log('📤 No context available, sending message as-is'); + } + + sendMessage(messageContent); + setInputMessage(''); + } + }; + + const handleKeyPress = (e: React.KeyboardEvent) => { + if (e.key === 'Enter' && !e.shiftKey) { + e.preventDefault(); + handleSendMessage(); + } + }; + + const handleInputChange = (e: React.ChangeEvent) => { + setInputMessage(e.target.value); + resizeTextarea(e.target); + }; + + const resizeTextarea = (textarea: HTMLTextAreaElement) => { + // Auto-resize textarea + textarea.style.height = 'auto'; // Reset height to calculate new height + + // Set height based on scroll height, with max height of 120px (about 6 lines) + const maxHeight = 120; + const newHeight = Math.min(textarea.scrollHeight, maxHeight); + textarea.style.height = `${newHeight}px`; + }; + + // Reset textarea height when input is cleared (e.g., after sending message) + useEffect(() => { + if (!inputMessage.trim()) { + const textarea = document.querySelector('.message-input') as HTMLTextAreaElement; + if (textarea) { + textarea.style.height = 'auto'; + } + } + }, [inputMessage]); + + const handleAddContext = () => { + // TODO: Implement context addition functionality + console.log('Add Context clicked'); + }; + + const handleNewConversation = () => { + clearHistory(); + setInputMessage(''); + setCurrentContext(null); + setUploadingImages([]); + setPendingImages([]); + setToolTimers({}); // Clear all tool timers + + // Clear all timer intervals + Object.values(timerIntervals.current).forEach(timer => clearInterval(timer)); + timerIntervals.current = {}; + + markAsReturningUser(); + }; + + const handleWelcomeGetStarted = () => { + setShowWelcome(false); + markAsReturningUser(); + console.log('👋 User clicked Get Started, welcome dismissed'); + + // Auto-trigger initialize Superdesign command + vscode.postMessage({ + command: 'initializeSuperdesign' + }); + console.log('🚀 Auto-triggering Initialize Superdesign command'); + }; + + // Drag and drop handlers + const handleDragEnter = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + // Check if dragged items contain files + if (e.dataTransfer.types.includes('Files')) { + e.dataTransfer.effectAllowed = 'copy'; + e.dataTransfer.dropEffect = 'copy'; + } + }; + + const handleDragLeave = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + }; + + const handleDragOver = (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + // Essential: Must prevent default and set dropEffect for drop to work + if (e.dataTransfer.types.includes('Files')) { + e.dataTransfer.dropEffect = 'copy'; + } + }; + + const handleDrop = async (e: React.DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (isLoading) { + return; + } + + const files = Array.from(e.dataTransfer.files); + const imageFiles = files.filter(file => file.type.startsWith('image/')); + + if (imageFiles.length === 0) { + return; + } + + // Process each image file + for (const file of imageFiles) { + try { + await handleImageUpload(file); + } catch (error) { + console.error('Error processing dropped image:', error); + } + } + }; + + const handleImageUpload = async (file: File): Promise => { + const maxSize = 10 * 1024 * 1024; // 10MB limit + if (file.size > maxSize) { + const displayName = file.name || 'clipboard image'; + console.error('Image too large:', displayName); + vscode.postMessage({ + command: 'showError', + data: `Image "${displayName}" is too large. Maximum size is 10MB.` + }); + return; + } + + // Create a unique filename - handle clipboard images without names + const timestamp = Date.now(); + const originalName = file.name || `clipboard-image-${timestamp}`; + const extension = file.type.split('/')[1] || 'png'; + const safeName = originalName.replace(/[^a-zA-Z0-9.-]/g, '_'); + const fileName = safeName.includes('.') ? `${timestamp}_${safeName}` : `${timestamp}_${safeName}.${extension}`; + + // Add to uploading state + setUploadingImages(prev => [...prev, originalName]); + + // Convert to base64 for sending to extension + const reader = new FileReader(); + reader.onload = () => { + const base64Data = reader.result as string; + + // Send to extension to save in moodboard + vscode.postMessage({ + command: 'saveImageToMoodboard', + data: { + fileName, + originalName, + base64Data, + mimeType: file.type, + size: file.size + } + }); + + console.log('📎 Image sent to extension for saving:', fileName); + }; + + reader.onerror = () => { + console.error('Error reading file:', file.name); + setUploadingImages(prev => prev.filter(name => name !== file.name)); + vscode.postMessage({ + command: 'showError', + data: `Failed to read image "${file.name}"` + }); + }; + + reader.readAsDataURL(file); + }; + + // Auto-set context when images finish uploading + useEffect(() => { + if (uploadingImages.length === 0 && pendingImages.length > 0) { + if (pendingImages.length === 1) { + // Single image - set as context with full path + setCurrentContext({ + fileName: pendingImages[0].fullPath, + type: 'image' + }); + } else { + // Multiple images - create a combined context with all full paths + const fullPaths = pendingImages.map(img => img.fullPath).join(', '); + setCurrentContext({ + fileName: fullPaths, + type: 'images' + }); + } + // Clear pending images after setting context + setPendingImages([]); + } + }, [uploadingImages.length, pendingImages.length]); + + // Helper function to check if tool is loading + const isToolLoading = (toolCallPart: any, msgIndex: number) => { + const toolCallId = toolCallPart.toolCallId; + const hasResult = chatHistory.slice(msgIndex + 1).some(laterMsg => + laterMsg.role === 'tool' && + Array.isArray(laterMsg.content) && + laterMsg.content.some(resultPart => + resultPart.type === 'tool-result' && + (resultPart as any).toolCallId === toolCallId + ) + ); + return !hasResult || toolCallPart.metadata?.is_loading || false; + }; + + // Manage countdown timers for tool calls + useEffect(() => { + const activeTimers = new Set(); + + // Process each message to find tool calls + chatHistory.forEach((msg, msgIndex) => { + if (msg.role === 'assistant' && Array.isArray(msg.content)) { + // Find tool call parts and use same indexing as UI + const toolCallParts = msg.content.filter(part => part.type === 'tool-call') as any[]; + + toolCallParts.forEach((toolCallPart, toolCallIndex) => { + const uniqueKey = `${msgIndex}_${toolCallIndex}`; // Use tool call index, not content index + const isLoading = isToolLoading(toolCallPart, msgIndex); + + activeTimers.add(uniqueKey); + + if (isLoading) { + // Initialize timer if doesn't exist + setToolTimers(prev => { + if (!(uniqueKey in prev)) { + const estimatedDuration = toolCallPart.metadata?.estimated_duration || 90; + const elapsedTime = toolCallPart.metadata?.elapsed_time || 0; + const initialRemaining = Math.max(0, estimatedDuration - elapsedTime); + + return { + ...prev, + [uniqueKey]: initialRemaining + }; + } + return prev; + }); + + // Start interval if not already running + if (!timerIntervals.current[uniqueKey]) { + timerIntervals.current[uniqueKey] = setInterval(() => { + setToolTimers(current => { + const newTime = Math.max(0, (current[uniqueKey] || 0) - 1); + return { + ...current, + [uniqueKey]: newTime + }; + }); + }, 1000); + } + } else { + // Tool completed, clean up + if (timerIntervals.current[uniqueKey]) { + clearInterval(timerIntervals.current[uniqueKey]); + delete timerIntervals.current[uniqueKey]; + } + setToolTimers(prev => { + const { [uniqueKey]: removed, ...rest } = prev; + return rest; + }); + } + }); + } + }); + + // Clean up orphaned timers + Object.keys(timerIntervals.current).forEach(key => { + if (!activeTimers.has(key)) { + clearInterval(timerIntervals.current[key]); + delete timerIntervals.current[key]; + } + }); + + // Cleanup on unmount + return () => { + Object.values(timerIntervals.current).forEach(timer => clearInterval(timer)); + }; + }, [chatHistory]); + + // Global drag & drop fallback for VS Code webview + useEffect(() => { + const handleGlobalDragOver = (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + if (e.dataTransfer && e.dataTransfer.types.includes('Files')) { + e.dataTransfer.dropEffect = 'copy'; + } + }; + + const handleGlobalDrop = async (e: DragEvent) => { + e.preventDefault(); + e.stopPropagation(); + console.log('🎯 Global drop detected!', e.dataTransfer?.files.length, 'files'); + + if (!e.dataTransfer?.files) return; + + const files = Array.from(e.dataTransfer.files); + console.log('🎯 Global files from drop:', files.map(f => `${f.name} (${f.type})`)); + + const imageFiles = files.filter(file => file.type.startsWith('image/')); + console.log('🎯 Global image files:', imageFiles.map(f => f.name)); + + if (imageFiles.length > 0 && !isLoading) { + console.log('📎 Processing images from global drop:', imageFiles.map(f => f.name)); + + for (const file of imageFiles) { + try { + await handleImageUpload(file); + } catch (error) { + console.error('Error processing dropped image:', error); + } + } + } + }; + + const handleGlobalPaste = async (e: ClipboardEvent) => { + // Only handle paste if we're focused on the chat and not loading + if (isLoading || showWelcome) return; + + const clipboardItems = e.clipboardData?.items; + if (!clipboardItems) return; + + console.log('📋 Paste detected, checking for images...'); + + // Look for image items in clipboard + const imageItems = Array.from(clipboardItems).filter(item => + item.type.startsWith('image/') + ); + + if (imageItems.length > 0) { + e.preventDefault(); + console.log('📋 Found', imageItems.length, 'image(s) in clipboard'); + + for (const item of imageItems) { + const file = item.getAsFile(); + if (file) { + try { + console.log('📋 Processing pasted image:', file.name || 'clipboard-image', file.type); + await handleImageUpload(file); + } catch (error) { + console.error('Error processing pasted image:', error); + vscode.postMessage({ + command: 'showError', + data: `Failed to process pasted image: ${error instanceof Error ? error.message : String(error)}` + }); + } + } + } + } + }; + + // Add global listeners + document.addEventListener('dragover', handleGlobalDragOver); + document.addEventListener('drop', handleGlobalDrop); + document.addEventListener('paste', handleGlobalPaste); + + return () => { + document.removeEventListener('dragover', handleGlobalDragOver); + document.removeEventListener('drop', handleGlobalDrop); + document.removeEventListener('paste', handleGlobalPaste); + }; + }, [isLoading, handleImageUpload, showWelcome]); + + const renderChatMessage = (msg: ChatMessage, index: number) => { + // Helper function to extract text content from CoreMessage + const getMessageText = (msg: ChatMessage): string => { + if (typeof msg.content === 'string') { + return msg.content; + } else if (Array.isArray(msg.content)) { + // Find text parts and concatenate them + return msg.content + .filter(part => part.type === 'text') + .map(part => (part as any).text) + .join('\n'); + } + return ''; + }; + + // Check if message has tool calls + const hasToolCalls = Array.isArray(msg.content) && + msg.content.some(part => part.type === 'tool-call'); + + // Helper function to find tool result for a tool call + const findToolResult = (toolCallId: string) => { + // Look for a tool message with matching toolCallId + for (let i = index + 1; i < chatHistory.length; i++) { + const laterMsg = chatHistory[i]; + if (laterMsg.role === 'tool' && Array.isArray(laterMsg.content)) { + const toolResultPart = laterMsg.content.find( + part => part.type === 'tool-result' && (part as any).toolCallId === toolCallId + ); + if (toolResultPart) { + return toolResultPart as any; + } + } + } + return null; + }; + + // Check if message has tool results + const hasToolResults = Array.isArray(msg.content) && + msg.content.some(part => part.type === 'tool-result'); + + const isLastUserMessage = msg.role === 'user' && index === chatHistory.length - 1 && isLoading; + const isLastStreamingMessage = (msg.role === 'assistant' || hasToolResults) && index === chatHistory.length - 1; + const isStreaming = isLastStreamingMessage && isLoading; + const messageText = getMessageText(msg); + + // Handle tool call messages specially - but for mixed content, we need to show both text AND tools + if (msg.role === 'assistant' && hasToolCalls) { + // Check if there's also text content + const hasTextContent = messageText.trim().length > 0; + + if (hasTextContent) { + // Mixed content: show both text and tool calls + return ( +
+ {layout === 'panel' && ( +
+ Claude + {msg.metadata && ( + + {msg.metadata.duration_ms && ( + {msg.metadata.duration_ms}ms + )} + {msg.metadata.total_cost_usd && ( + ${msg.metadata.total_cost_usd.toFixed(4)} + )} + + )} +
+ )} +
+ + {isStreaming && } +
+
+ {renderToolCalls(msg, index, findToolResult)} +
+
+ ); + } else { + // Only tool calls, no text content - use original tool-only rendering + return renderToolCalls(msg, index, findToolResult); + } + } + + // Handle error messages with actions specially + if (msg.role === 'assistant' && msg.metadata?.is_error && msg.metadata?.actions) { + return renderErrorMessage(msg, index, setChatHistory); + } + + // Determine message label and styling + let messageLabel = ''; + let messageClass = ''; + + switch (msg.role) { + case 'user': + messageLabel = 'You'; + messageClass = 'user'; + break; + case 'assistant': + messageLabel = 'Claude'; + messageClass = 'assistant'; + break; + case 'system': + messageLabel = 'System'; + messageClass = 'system'; + break; + case 'tool': + messageLabel = 'Tool Result'; + messageClass = 'tool-result'; + break; + } + + const hasToolCall = hasToolCalls || hasToolResults; + + return ( +
+ {layout === 'panel' && ( +
+ + {messageLabel} + + {msg.metadata && ( + + {msg.metadata.duration_ms && ( + {msg.metadata.duration_ms}ms + )} + {msg.metadata.total_cost_usd && ( + ${msg.metadata.total_cost_usd.toFixed(4)} + )} + + )} +
+ )} +
+ {(msg.role === 'assistant') ? ( + + ) : ( + (() => { + // Check if this is a user message with context + if (messageText.startsWith('Context: ') && messageText.includes('\n\nMessage: ')) { + const contextMatch = messageText.match(/^Context: (.+)\n\nMessage: (.+)$/s); + if (contextMatch) { + const contextFile = contextMatch[1]; + const actualMessage = contextMatch[2]; + + // Handle display for multiple images or single image + let displayFileName; + if (contextFile.includes(', ')) { + // Multiple images - show count + const paths = contextFile.split(', '); + displayFileName = `${paths.length} images in moodboard`; + } else { + // Single image - show just filename + displayFileName = contextFile.includes('.superdesign') + ? contextFile.split('.superdesign/')[1] || contextFile.split('/').pop() || contextFile + : contextFile.split('/').pop() || contextFile; + } + + return ( + <> +
+ @ + {displayFileName} +
+
{actualMessage}
+ + ); + } + } + return messageText; + })() + )} + {isStreaming && } +
+ {isLastUserMessage && ( +
+ Generating +
+ )} +
+ ); + }; + + // New function to handle multiple tool calls in a single message + const renderToolCalls = (msg: ChatMessage, index: number, findToolResult: (toolCallId: string) => any) => { + if (!Array.isArray(msg.content)) { + return
Invalid tool message content
; + } + + // Find ALL tool call parts + const toolCallParts = msg.content.filter(part => part.type === 'tool-call') as any[]; + + if (toolCallParts.length === 0) { + return
No tool calls found
; + } + + // Render each tool call separately + return ( +
+ {toolCallParts.map((toolCallPart, subIndex) => + renderSingleToolCall(toolCallPart, index, subIndex, findToolResult) + )} +
+ ); + }; + + // Updated function to render a single tool call with unique subIndex for state management + const renderSingleToolCall = (toolCallPart: any, messageIndex: number, subIndex: number, findToolResult: (toolCallId: string) => any) => { + try { + const toolName = toolCallPart.toolName || 'Unknown Tool'; + const toolInput = toolCallPart.args || {}; + const uniqueKey = `${messageIndex}_${subIndex}`; + + // Special handling for generateTheme tool calls + if (toolName === 'generateTheme') { + // For generateTheme, check if we have a tool result to determine completion + const toolCallId = toolCallPart.toolCallId; + const toolResultPart = findToolResult(toolCallId); + const hasResult = !!toolResultPart; + const resultIsError = toolResultPart?.isError || false; + + // Tool is loading if we don't have a result yet, or if metadata indicates loading + const isLoading = !hasResult || toolCallPart.metadata?.is_loading || false; + + // Extract theme data from tool input + const themeName = toolInput.theme_name || 'Untitled Theme'; + const reasoning = toolInput.reasoning_reference || ''; + const cssSheet = toolInput.cssSheet || ''; + + // Try to get CSS file path from metadata or result + let cssFilePath = null; + if (hasResult && !resultIsError) { + // Check both input and result for cssFilePath + cssFilePath = toolInput.cssFilePath || toolResultPart?.result?.cssFilePath; + } + + return ( +
+ + {resultIsError && ( +
+ ⚠️ Theme generation encountered an error. The preview above shows the input data. +
+ )} +
+ ); + } + + // Continue with existing generic tool rendering for other tools + const isExpanded = expandedTools[uniqueKey] || false; + const showFullResult = showFullContent[uniqueKey] || false; + const showFullInput = showFullContent[`${uniqueKey}_input`] || false; + const showFullPrompt = showFullContent[`${uniqueKey}_prompt`] || false; + + const description = toolInput.description || ''; + const command = toolInput.command || ''; + const prompt = toolInput.prompt || ''; + + // Tool result data - find from separate tool message + const toolCallId = toolCallPart.toolCallId; + const toolResultPart = findToolResult(toolCallId); + const hasResult = !!toolResultPart; + const resultIsError = toolResultPart?.isError || false; + + // Tool is loading if we don't have a result yet, or if metadata indicates loading + const isLoading = !hasResult || toolCallPart.metadata?.is_loading || false; + + const toolResult = toolResultPart ? + (typeof toolResultPart.result === 'string' ? toolResultPart.result : JSON.stringify(toolResultPart.result, null, 2)) : + ''; + + // Tool is complete when it has finished (regardless of errors) + const toolComplete = hasResult && !isLoading; + + // Get the countdown timer for this specific tool + const timerRemaining = toolTimers[uniqueKey] || 0; + + // Enhanced loading data + const estimatedDuration = toolCallPart.metadata?.estimated_duration || 90; + const elapsedTime = toolCallPart.metadata?.elapsed_time || 0; + const progressPercentage = toolCallPart.metadata?.progress_percentage || 0; + // Use timer state for remaining time, fallback to calculated if timer not started yet + const remainingTime = isLoading ? (timerRemaining > 0 ? timerRemaining : Math.max(0, estimatedDuration - elapsedTime)) : 0; + + // Format time display + const formatTime = (seconds: number): string => { + const mins = Math.floor(seconds / 60); + const secs = Math.floor(seconds % 60); + return `${mins}:${secs.toString().padStart(2, '0')}`; + }; + + // Get friendly tool name for display + const getFriendlyToolName = (name: string): string => { + const friendlyNames: { [key: string]: string } = { + 'mcp_taskmaster-ai_parse_prd': 'Parsing Requirements Document', + 'mcp_taskmaster-ai_analyze_project_complexity': 'Analyzing Project Complexity', + 'mcp_taskmaster-ai_expand_task': 'Expanding Task', + 'mcp_taskmaster-ai_expand_all': 'Expanding All Tasks', + 'mcp_taskmaster-ai_research': 'Researching Information', + 'codebase_search': 'Searching Codebase', + 'read_file': 'Reading File', + 'edit_file': 'Editing File', + 'run_terminal_cmd': 'Running Command' + }; + return friendlyNames[name] || name.replace(/mcp_|_/g, ' ').replace(/\b\w/g, l => l.toUpperCase()); + }; + + // Get helpful loading tips based on tool and progress + const getLoadingTip = (toolName: string, progress: number): string => { + const progressStage = progress < 25 ? 'early' : progress < 50 ? 'mid' : progress < 75 ? 'late' : 'final'; + + const tipsByTool: { [key: string]: { [stage: string]: string[] } } = { + 'mcp_taskmaster-ai_parse_prd': { + early: ['Analyzing requirements and identifying key features...', 'Breaking down complex requirements into manageable tasks...'], + mid: ['Structuring tasks based on dependencies and priorities...', 'Defining implementation details for each component...'], + late: ['Finalizing task relationships and estimates...', 'Optimizing task breakdown for efficient development...'], + final: ['Completing task generation and validation...', 'Almost ready with your project roadmap!'] + }, + 'mcp_taskmaster-ai_research': { + early: ['Gathering the latest information from multiple sources...', 'Searching for best practices and recent developments...'], + mid: ['Analyzing findings and filtering relevant information...', 'Cross-referencing multiple sources for accuracy...'], + late: ['Synthesizing research into actionable insights...', 'Preparing comprehensive research summary...'], + final: ['Finalizing research report with recommendations...', 'Almost done with your research!'] + }, + 'mcp_taskmaster-ai_expand_task': { + early: ['Breaking down the task into detailed subtasks...', 'Analyzing task complexity and dependencies...'], + mid: ['Defining implementation steps and requirements...', 'Creating detailed subtask specifications...'], + late: ['Optimizing subtask flow and dependencies...', 'Adding implementation details and strategies...'], + final: ['Finalizing subtask breakdown...', 'Your detailed implementation plan is almost ready!'] + } + }; + + const generalTips = { + early: ['AI is working hard to process your request...', 'Analyzing your requirements in detail...', 'Loading the best approach for your needs...'], + mid: ['Making good progress on your request...', 'Processing complex logic and relationships...', 'Halfway there! Building your solution...'], + late: ['Finalizing details and optimizations...', 'Almost finished with the heavy lifting...', 'Putting the finishing touches on your request...'], + final: ['Just a few more seconds...', 'Completing final validations...', 'Almost ready with your results!'] + }; + + const toolTips = tipsByTool[toolName] || generalTips; + const stageTips = toolTips[progressStage] || generalTips[progressStage]; + const randomIndex = Math.floor((progress / 10)) % stageTips.length; + + return stageTips[randomIndex]; + }; + + const toggleExpanded = () => { + setExpandedTools(prev => ({ + ...prev, + [uniqueKey]: !prev[uniqueKey] + })); + }; + + const toggleShowFullResult = () => { + setShowFullContent(prev => ({ + ...prev, + [uniqueKey]: !prev[uniqueKey] + })); + }; + + const toggleShowFullInput = () => { + setShowFullContent(prev => ({ + ...prev, + [`${uniqueKey}_input`]: !prev[`${uniqueKey}_input`] + })); + }; + + const toggleShowFullPrompt = () => { + setShowFullContent(prev => ({ + ...prev, + [`${uniqueKey}_prompt`]: !prev[`${uniqueKey}_prompt`] + })); + }; + + // Determine if content needs truncation + const MAX_PREVIEW = 300; + + // Result truncation + const resultNeedsTruncation = toolResult.length > MAX_PREVIEW; + const displayResult = resultNeedsTruncation && !showFullResult + ? toolResult.substring(0, MAX_PREVIEW) + '...' + : toolResult; + + // Input truncation + const inputString = JSON.stringify(toolInput, null, 2); + const inputNeedsTruncation = inputString.length > MAX_PREVIEW; + const displayInput = inputNeedsTruncation && !showFullInput + ? inputString.substring(0, MAX_PREVIEW) + '...' + : inputString; + + // Prompt truncation + const promptNeedsTruncation = prompt.length > MAX_PREVIEW; + const displayPrompt = promptNeedsTruncation && !showFullPrompt + ? prompt.substring(0, MAX_PREVIEW) + '...' + : prompt; + + return ( +
+
+
+ + {isLoading ? ( +
+
+
+ ) : ( + + )} +
+
+ {getFriendlyToolName(toolName)} + {description && ( + {description} + )} + {isLoading && ( + + {formatTime(remainingTime)} remaining + + )} +
+
+
+ {toolComplete && ( + + + + )} + +
+
+ {isExpanded && ( +
+ {isLoading && ( +
+
+ + + {getLoadingTip(toolName, Math.floor((estimatedDuration - remainingTime) / estimatedDuration * 100))} + +
+
+ )} + {command && ( +
+ Command: + {command} +
+ )} + {Object.keys(toolInput).length > 0 && ( +
+ Input: +
+
+                                            {displayInput}
+                                        
+ {inputNeedsTruncation && ( + + )} +
+
+ )} + {prompt && ( +
+ Prompt: +
+
+                                            {displayPrompt}
+                                        
+ {promptNeedsTruncation && ( + + )} +
+
+ )} + {hasResult && ( +
+ + {resultIsError ? 'Error Result:' : 'Result:'} + +
+
+                                            {displayResult}
+                                        
+ {resultNeedsTruncation && ( + + )} +
+
+ )} +
+ )} +
+ ); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : 'Unknown error'; + return ( +
+
+
+ ⚠️ +
+ Error rendering tool: {toolCallPart.toolName || 'Unknown'} + {errorMessage} +
+
+
+
+ ); + } + }; + + const renderErrorMessage = (msg: ChatMessage, index: number, setChatHistory: React.Dispatch>) => { + const handleActionClick = (action: { text: string; command: string; args?: string }) => { + console.log('Action clicked:', action); + vscode.postMessage({ + command: 'executeAction', + actionCommand: action.command, + actionArgs: action.args + }); + }; + + const handleCloseError = () => { + // Remove this error message from chat history + setChatHistory((prev: ChatMessage[]) => prev.filter((_, i: number) => i !== index)); + }; + + return ( +
+ {layout === 'panel' && ( +
+ Error + +
+ )} +
+
+ {typeof msg.content === 'string' ? msg.content : 'Error occurred'} +
+ {msg.metadata?.actions && msg.metadata.actions.length > 0 && ( +
+ {msg.metadata.actions.map((action, actionIndex) => ( + + ))} +
+ )} + {layout === 'sidebar' && ( + + )} +
+
+ ); + }; + + const renderPlaceholder = () => ( +
+
+
+

+ Cursor/Windsurf/Claude Code rules already added, prompt Cursor/Windsurf/Claude Code to design UI like Help me design a calculator UI and preview the UI in Superdesign canvas by Cmd+Shift+P 'Superdesign: Open canvas view' +

+
OR
+

+ You can start with native superdesign agent chat below (We have better UX) +

+
+
+
+ ); + + return ( +
+ + {layout === 'panel' && ( +
+

💬 Chat with Claude

+

Ask Claude anything about code, design, or development!

+ +
+ )} + +
+
+ {showWelcome ? ( + + ) : hasConversationMessages() ? ( + <> + {chatHistory + .filter(msg => { + // All messages are now displayed since we use CoreMessage format + return true; + }) + .map(renderChatMessage) + } + + ) : renderPlaceholder()} +
+ + {!showWelcome && ( +
+ {/* Context Display */} + {currentContext ? ( +
+ + {currentContext.type === 'image' ? '🖼️' : currentContext.type === 'images' ? '🖼️' : '📄'} + + + {currentContext.type === 'image' ? 'Image: ' : currentContext.type === 'images' ? 'Images: ' : 'Context: '} + {currentContext.type === 'images' ? + `${currentContext.fileName.split(', ').length} images in moodboard` : + (currentContext.fileName.includes('.superdesign') + ? currentContext.fileName.split('.superdesign/')[1] || currentContext.fileName.split('/').pop() || currentContext.fileName + : currentContext.fileName.split('/').pop() || currentContext.fileName + ) + } + + +
+ ) : null} + + {/* Upload Progress */} + {uploadingImages.length > 0 && ( +
+ {uploadingImages.length > 1 && ( +
+ Uploading {uploadingImages.length} images... +
+ )} + {uploadingImages.map((fileName, index) => ( +
+ 📎 + Uploading {fileName}... +
+
+ ))} +
+ )} + + {/* Add Context Button */} + {!currentContext && uploadingImages.length === 0 && ( + + )} + + {/* Input Area */} +
+ +
+ + +
+
+ `; + + return ( +
+
+
+
+
+ ); +}; + +export default ThemePreview; \ No newline at end of file diff --git a/src/webview/components/Chat/ThemePreviewCard.tsx b/src/webview/components/Chat/ThemePreviewCard.tsx new file mode 100644 index 0000000..5c21ad5 --- /dev/null +++ b/src/webview/components/Chat/ThemePreviewCard.tsx @@ -0,0 +1,419 @@ +import React, { useState, useEffect } from 'react'; +import ThemePreviewHeader from './ThemePreviewHeader'; +import ColorPalette from './ColorPalette'; +import ThemePreview from './ThemePreview'; +import ModeToggle from './ModeToggle'; +import { parseThemeCSS, extractColorPalette, type ParsedTheme } from '../../utils/themeParser'; + +interface ThemePreviewCardProps { + themeName: string; + reasoning?: string; + cssSheet?: string | null; + cssFilePath?: string | null; + isLoading?: boolean; + vscode?: any; +} + +const ThemePreviewCard: React.FC = ({ + themeName, + reasoning, + cssSheet, + cssFilePath, + isLoading = false, + vscode +}) => { + const [isExpanded, setIsExpanded] = useState(false); + const [parsedTheme, setParsedTheme] = useState(null); + const [activeTab, setActiveTab] = useState<'theme' | 'components'>('theme'); + const [isDarkMode, setIsDarkMode] = useState(false); + const [currentCssContent, setCurrentCssContent] = useState(''); + const [isLoadingCss, setIsLoadingCss] = useState(false); + const [cssLoadError, setCssLoadError] = useState(null); + + // Pre-inject minimal CSS to avoid FOUC (Flash of Unstyled Content) + useEffect(() => { + const minimalCssId = 'theme-preview-minimal-css'; + let existingStyle = document.getElementById(minimalCssId); + + if (!existingStyle) { + const minimalStyle = document.createElement('style'); + minimalStyle.id = minimalCssId; + minimalStyle.textContent = ` + .theme-preview-live { + background: var(--vscode-editor-background); + color: var(--vscode-editor-foreground); + font-family: var(--vscode-font-family); + border-radius: 4px; + overflow: hidden; + min-height: 400px; + border: 1px solid var(--vscode-panel-border); + } + `; + document.head.appendChild(minimalStyle); + } + + // Cleanup on unmount + return () => { + const styleToRemove = document.getElementById(minimalCssId); + if (styleToRemove) { + document.head.removeChild(styleToRemove); + } + }; + }, []); + + // Set initial loading state immediately when cssFilePath is provided + useEffect(() => { + if (cssFilePath && vscode) { + setIsLoadingCss(true); + setIsExpanded(true); // Auto-expand to show loading state + } + }, [cssFilePath, vscode]); + + // Load CSS from file if cssFilePath is provided + useEffect(() => { + const loadCssFromFile = async () => { + // Reset states + setCssLoadError(null); + + if (cssFilePath && vscode) { + try { + // Request CSS file content from extension + const response = await new Promise((resolve, reject) => { + const timeoutId = setTimeout(() => { + reject(new Error('Timeout loading CSS file')); + }, 3000); // Reduced timeout + + const handler = (event: MessageEvent) => { + const message = event.data; + if (message.command === 'cssFileContentResponse' && message.filePath === cssFilePath) { + clearTimeout(timeoutId); + window.removeEventListener('message', handler); + if (message.error) { + reject(new Error(message.error)); + } else { + resolve(message.content); + } + } + }; + + window.addEventListener('message', handler); + + // Request CSS file content + vscode.postMessage({ + command: 'getCssFileContent', + filePath: cssFilePath + }); + }); + + setCurrentCssContent(response); + setIsExpanded(true); // Auto-expand when CSS loads successfully + } catch (error) { + console.warn('Failed to load CSS from file, falling back to cssSheet:', error); + setCssLoadError(error instanceof Error ? error.message : 'Failed to load CSS'); + setCurrentCssContent(cssSheet || ''); + } finally { + setIsLoadingCss(false); + } + } else if (cssSheet) { + setCurrentCssContent(cssSheet); + setIsExpanded(true); // Auto-expand when CSS is available + } + }; + + loadCssFromFile(); + }, [cssFilePath, cssSheet, vscode]); + + // Parse CSS when content is available + useEffect(() => { + if (currentCssContent && !isLoadingCss) { + try { + const theme = parseThemeCSS(currentCssContent); + setParsedTheme(theme); + } catch (error) { + console.error('Failed to parse theme:', error); + setCssLoadError('Failed to parse theme CSS'); + } + } + }, [currentCssContent, isLoadingCss]); + + const handleCopyCSS = () => { + if (currentCssContent) { + navigator.clipboard.writeText(currentCssContent); + } + }; + + const handleToggleExpanded = () => { + setIsExpanded(!isExpanded); + }; + + // Convert parsed theme to grouped colors format + const getGroupedColors = (theme: ParsedTheme) => { + const palette = extractColorPalette(theme); + return palette.reduce((acc, color) => { + if (!acc[color.category]) { + acc[color.category] = {}; + } + acc[color.category][color.name] = color.value; + return acc; + }, {} as Record>); + }; + + // Show component if we're loading, have a theme, or have an error to display + if (!parsedTheme && !isLoading && !isLoadingCss && !cssLoadError) { + return null; + } + + return ( + <> + +
+ + + {isExpanded && ( + <> + {/* Loading State */} + {isLoadingCss && ( +
+
+
+
+
+ Loading theme CSS... +
+
+ )} + + {/* Error State */} + {cssLoadError && !isLoadingCss && ( +
+
+ ⚠️ {cssLoadError} +
+
+ )} + + {/* Normal Content */} + {!isLoadingCss && !cssLoadError && parsedTheme && ( + <> +
+ + +
+ +
+ {activeTab === 'theme' && ( + <> + {/* CSS File Name - Subtle Display */} + {cssFilePath && ( +
+ {cssFilePath.split('/').pop()} +
+ )} + + {/* Typography Preview */} +
+
+
+
+ Sans +
+
+ {parsedTheme.fonts?.sans?.split(',')[0]?.trim() || 'Default'} +
+
+ +
+
+ Serif +
+
+ {parsedTheme.fonts?.serif?.split(',')[0]?.trim() || 'Default'} +
+
+ +
+
+ Mono +
+
+ {parsedTheme.fonts?.mono?.split(',')[0]?.trim() || 'Default'} +
+
+
+
+ + {/* Color Palette */} + + + )} + + {activeTab === 'components' && ( +
+
+

Component Preview

+ +
+ +
+ )} +
+ + )} + + )} +
+ + ); +}; + +export default ThemePreviewCard; \ No newline at end of file diff --git a/src/webview/components/Chat/ThemePreviewHeader.tsx b/src/webview/components/Chat/ThemePreviewHeader.tsx new file mode 100644 index 0000000..5625697 --- /dev/null +++ b/src/webview/components/Chat/ThemePreviewHeader.tsx @@ -0,0 +1,82 @@ +import React from 'react'; + +interface ThemePreviewHeaderProps { + themeName: string; + isExpanded: boolean; + onToggleExpanded: () => void; + isLoading: boolean; + onCopyCSS: () => void; +} + +const ThemePreviewHeader: React.FC = ({ + themeName, + isExpanded, + onToggleExpanded, + isLoading, + onCopyCSS +}) => { + const handleActionClick = (e: React.MouseEvent, action: () => void) => { + e.stopPropagation(); + action(); + }; + + return ( +
+
+ + {isLoading ? ( +
+
+
+ ) : ( + '⚙️' + )} +
+
+ + Generate Theme - {themeName} + + {isLoading && ( + + Generating theme... + + )} +
+
+
+ {!isLoading && ( + + )} + +
+
+ ); +}; + +export default ThemePreviewHeader; \ No newline at end of file diff --git a/src/webview/components/ConnectionLines.tsx b/src/webview/components/ConnectionLines.tsx new file mode 100644 index 0000000..302a96a --- /dev/null +++ b/src/webview/components/ConnectionLines.tsx @@ -0,0 +1,101 @@ +import React from 'react'; +import { ConnectionLine } from '../types/canvas.types'; + +interface ConnectionLinesProps { + connections: ConnectionLine[]; + containerBounds: { width: number; height: number }; + isVisible: boolean; + zoomLevel: number; +} + +const ConnectionLines: React.FC = ({ + connections, + containerBounds, + isVisible, + zoomLevel +}) => { + if (!isVisible || connections.length === 0) { + return null; + } + + // Adjust line styling based on zoom level + const getLineStyle = (connection: ConnectionLine) => ({ + stroke: connection.color || 'var(--vscode-textLink-foreground)', + strokeWidth: (connection.width || 2) / zoomLevel, // Thinner lines when zoomed out + strokeDasharray: zoomLevel < 0.5 ? '5,5' : 'none', // Dashed when very zoomed out + opacity: Math.max(0.3, Math.min(1, zoomLevel)), // More transparent when zoomed out + markerEnd: 'url(#arrowhead)' + }); + + // Calculate curve path for organic looking connections + const createCurvePath = (from: { x: number; y: number }, to: { x: number; y: number }) => { + const dx = to.x - from.x; + const dy = to.y - from.y; + + // Create a smooth curve with control points + const cp1x = from.x + dx * 0.6; + const cp1y = from.y; + const cp2x = to.x - dx * 0.6; + const cp2y = to.y; + + return `M ${from.x} ${from.y} C ${cp1x} ${cp1y}, ${cp2x} ${cp2y}, ${to.x} ${to.y}`; + }; + + return ( + + {/* Arrow marker definition */} + + + + + + + {/* Render all connection lines */} + {connections.map((connection) => ( + + {/* Main connection line */} + + + {/* Optional: Add a thicker invisible line for easier hover detection */} + + {`${connection.fromFrame} → ${connection.toFrame}`} + + + ))} + + ); +}; + +export default ConnectionLines; \ No newline at end of file diff --git a/src/webview/components/DesignFrame.tsx b/src/webview/components/DesignFrame.tsx new file mode 100644 index 0000000..d64a607 --- /dev/null +++ b/src/webview/components/DesignFrame.tsx @@ -0,0 +1,838 @@ +import React from 'react'; +import { DesignFile, GridPosition, FrameDimensions, ViewportMode, WebviewMessage } from '../types/canvas.types'; +import { MobileIcon, TabletIcon, DesktopIcon, GlobeIcon } from './Icons'; + +// Import logo images +import cursorLogo from '../../assets/cursor_logo.png'; +import windsurfLogo from '../../assets/windsurf_logo.png'; +import claudeCodeLogo from '../../assets/claude_code_logo.png'; +import lovableLogo from '../../assets/lovable_logo.png'; +import boltLogo from '../../assets/bolt_logo.jpg'; + +interface DesignFrameProps { + file: DesignFile; + position: GridPosition; + dimensions: FrameDimensions; + isSelected: boolean; + onSelect: (fileName: string) => void; + renderMode?: 'placeholder' | 'iframe' | 'html'; + showMetadata?: boolean; + viewport?: ViewportMode; + viewportDimensions?: FrameDimensions; + onViewportChange?: (fileName: string, viewport: ViewportMode) => void; + useGlobalViewport?: boolean; + onDragStart?: (fileName: string, startPos: GridPosition, mouseEvent: React.MouseEvent) => void; + isDragging?: boolean; + nonce?: string | null; + onSendToChat?: (fileName: string, prompt: string) => void; +} + +const DesignFrame: React.FC = ({ + file, + position, + dimensions, + isSelected, + onSelect, + renderMode = 'placeholder', + showMetadata = true, + viewport = 'desktop', + viewportDimensions, + onViewportChange, + useGlobalViewport = false, + onDragStart, + isDragging = false, + nonce = null, + onSendToChat +}) => { + const [isLoading, setIsLoading] = React.useState(renderMode === 'iframe'); + const [hasError, setHasError] = React.useState(false); + const [dragPreventOverlay, setDragPreventOverlay] = React.useState(false); + const [showCopyDropdown, setShowCopyDropdown] = React.useState(false); + const [copyButtonState, setCopyButtonState] = React.useState<{ text: string; isSuccess: boolean }>({ text: 'Copy prompt', isSuccess: false }); + const [copyPathButtonState, setCopyPathButtonState] = React.useState<{ text: string; isSuccess: boolean }>({ text: 'Copy design path', isSuccess: false }); + + const handleClick = () => { + onSelect(file.name); + }; + + const handleMouseDown = (e: React.MouseEvent) => { + if (onDragStart && e.button === 0) { // Left mouse button only + e.preventDefault(); + e.stopPropagation(); + + // Show overlay to prevent iframe interaction during potential drag + setDragPreventOverlay(true); + + onDragStart(file.name, position, e); + } + }; + + // Clear drag prevention overlay when dragging ends + React.useEffect(() => { + if (!isDragging) { + setDragPreventOverlay(false); + } + }, [isDragging]); + + // Close dropdown when clicking outside + React.useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (showCopyDropdown) { + const target = event.target as Element; + const dropdownElement = target.closest('.copy-prompt-dropdown'); + if (!dropdownElement) { + setShowCopyDropdown(false); + } + } + }; + + if (showCopyDropdown) { + document.addEventListener('mousedown', handleClickOutside); + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + } + }, [showCopyDropdown]); + + const handleViewportToggle = (newViewport: ViewportMode) => { + if (onViewportChange && !useGlobalViewport) { + onViewportChange(file.name, newViewport); + } + }; + + const handleCopyPrompt = async (e: React.MouseEvent, platform?: string) => { + e.preventDefault(); + e.stopPropagation(); + + let promptText = ''; + let platformName = ''; + + switch (platform) { + case 'cursor': + promptText = `${file.content}\n\nAbove is the design implementation, please use that as a reference to build a similar UI component. Make sure to follow modern React and TypeScript best practices.`; + platformName = 'Cursor'; + break; + case 'windsurf': + promptText = `${file.content}\n\nAbove is the design implementation. Please analyze this design and create a similar UI component using modern web technologies and best practices.`; + platformName = 'Windsurf'; + break; + case 'claude-code': + promptText = `${file.content}\n\nAbove is the design implementation. Please use this as a reference to create a similar component. Focus on clean, maintainable code structure.`; + platformName = 'Claude Code'; + break; + case 'lovable': + promptText = `${file.content}\n\nAbove is the design implementation. Please recreate this design as a responsive React component with modern styling.`; + platformName = 'Lovable'; + break; + case 'bolt': + promptText = `${file.content}\n\nAbove is the design implementation. Please create a similar UI using this as reference. Make it production-ready with proper styling.`; + platformName = 'Bolt'; + break; + default: + promptText = `${file.content}\n\nAbove is the design implementation, please use that as a reference`; + platformName = ''; + } + + try { + await navigator.clipboard.writeText(promptText); + console.log(`✅ Copied ${platformName} prompt to clipboard for:`, file.name); + + // Show success state on button + setCopyButtonState({ text: `Copied for ${platformName}!`, isSuccess: true }); + setTimeout(() => { + setCopyButtonState({ text: 'Copy prompt', isSuccess: false }); + }, 2000); + + // Hide dropdown + setShowCopyDropdown(false); + } catch (err) { + console.error('❌ Failed to copy to clipboard:', err); + + // Fallback: create a temporary textarea and copy + const textarea = document.createElement('textarea'); + textarea.value = promptText; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + + console.log(`✅ Copied ${platformName} prompt using fallback method for:`, file.name); + + // Show success state on button + setCopyButtonState({ text: `Copied for ${platformName}!`, isSuccess: true }); + setTimeout(() => { + setCopyButtonState({ text: 'Copy prompt', isSuccess: false }); + }, 2000); + + // Hide dropdown + setShowCopyDropdown(false); + } + }; + + const handleCopyDropdownToggle = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + console.log('Dropdown toggle clicked. Current context:', (window as any).__WEBVIEW_CONTEXT__); + console.log('Logo URIs available:', (window as any).__WEBVIEW_CONTEXT__?.logoUris); + setShowCopyDropdown(!showCopyDropdown); + }; + + const handleCopyDesignPath = async (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + const designPath = `Design file: ${file.path}`; + + try { + await navigator.clipboard.writeText(designPath); + console.log(`✅ Copied design path to clipboard:`, designPath); + + // Show success state on button + setCopyPathButtonState({ text: 'Copied!', isSuccess: true }); + setTimeout(() => { + setCopyPathButtonState({ text: 'Copy design path', isSuccess: false }); + }, 2000); + + } catch (err) { + console.error('❌ Failed to copy design path to clipboard:', err); + + // Fallback: create a temporary textarea and copy + const textarea = document.createElement('textarea'); + textarea.value = designPath; + document.body.appendChild(textarea); + textarea.select(); + document.execCommand('copy'); + document.body.removeChild(textarea); + + console.log(`✅ Copied design path using fallback method:`, designPath); + + // Show success state on button + setCopyPathButtonState({ text: 'Copied!', isSuccess: true }); + setTimeout(() => { + setCopyPathButtonState({ text: 'Copy design path', isSuccess: false }); + }, 2000); + } + }; + + const handleCreateVariations = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (onSendToChat) { + onSendToChat(file.name, 'Create more variations based on this style'); + } + }; + + const handleIterateWithFeedback = (e: React.MouseEvent) => { + e.preventDefault(); + e.stopPropagation(); + + if (onSendToChat) { + onSendToChat(file.name, 'Please create a few variations with this feedback: '); + } + }; + + const getViewportIcon = (mode: ViewportMode): React.ReactElement => { + switch (mode) { + case 'mobile': return ; + case 'tablet': return ; + case 'desktop': return ; + default: return ; + } + }; + + const getViewportLabel = (mode: ViewportMode): string => { + switch (mode) { + case 'mobile': return 'Mobile'; + case 'tablet': return 'Tablet'; + case 'desktop': return 'Desktop'; + default: return 'Desktop'; + } + }; + + const renderContent = () => { + switch (renderMode) { + case 'iframe': + // Handle SVG files differently than HTML files + if (file.fileType === 'svg') { + // For SVG files, wrap in HTML with proper viewport + const svgHtml = ` + + + + + + ${viewportDimensions ? `` : ''} + + + + ${file.content} + + + + `; + + return ( +