From df7302b8b06cfacd9c97b47b9556b1982964bc53 Mon Sep 17 00:00:00 2001 From: Ian Date: Sat, 25 Apr 2026 12:37:38 +0300 Subject: [PATCH] Initial commit --- .copilotignore | 1 + .devcontainer/devcontainer.json | 27 + .github/agents/pixel-jam.agent.md | 31 + .github/agents/quiz-master.agent.md | 19 + .github/agents/tdd-green.agent.md | 16 + .github/agents/tdd-red.agent.md | 13 + .github/agents/tdd-refactor.agent.md | 9 + .github/agents/tdd.agent.md | 16 + .github/agents/ui-review.agent.md | 20 + .../frontend-design.instructions.md | 27 + .../instructions/tailwind-4.instructions.md | 83 + .github/prompts/cloud-explore.prompt.md | 13 + .github/prompts/setup.prompt.md | 14 + .github/workflows/deploy.yml | 59 + .gitignore | 24 + .solutions/.hidden-from-ai-agents | 1 + .vscode/extensions.json | 7 + .vscode/launch.json | 13 + .vscode/mcp.json | 4 + .vscode/settings.json | 26 + .vscode/tasks.json | 28 + CODE_OF_CONDUCT.md | 10 + CONTRIBUTING.md | 15 + LICENSE | 21 + README.es.md | 50 + README.md | 51 + README.pt_BR.md | 50 + SECURITY.md | 14 + SUPPORT.md | 13 + docs/es/index.html | 200 + docs/es/step.html | 373 ++ docs/index.html | 200 + docs/light-theme.css | 65 + docs/pt_BR/index.html | 200 + docs/pt_BR/step.html | 373 ++ docs/step.css | 249 + docs/step.html | 380 ++ docs/styles.css | 151 + docs/theme-toggle.js | 24 + eslint.config.js | 23 + index.html | 15 + package-lock.json | 4403 +++++++++++++++++ package.json | 38 + public/vite.svg | 1 + src/App.tsx | 38 + src/assets/react.svg | 1 + src/components/BingoBoard.tsx | 23 + src/components/BingoModal.tsx | 22 + src/components/BingoSquare.tsx | 35 + src/components/GameScreen.tsx | 55 + src/components/StartScreen.tsx | 30 + src/data/questions.ts | 28 + src/hooks/useBingoGame.ts | 211 + src/index.css | 21 + src/main.tsx | 10 + src/test/setup.ts | 9 + src/types/index.ts | 16 + src/utils/bingoLogic.test.ts | 401 ++ src/utils/bingoLogic.ts | 126 + tsconfig.app.json | 28 + tsconfig.json | 7 + tsconfig.node.json | 26 + vite.config.ts | 16 + workshop/.ignored-by-agents | 1 + workshop/00-overview.md | 59 + workshop/01-setup.md | 120 + workshop/02-design.md | 90 + workshop/03-quiz-master.md | 49 + workshop/04-multi-agent.md | 116 + workshop/05-complete.md | 56 + workshop/GUIDE.md | 82 + workshop/es/00-overview.md | 60 + workshop/es/01-setup.md | 121 + workshop/es/02-design.md | 91 + workshop/es/03-quiz-master.md | 50 + workshop/es/04-multi-agent.md | 117 + workshop/es/05-complete.md | 57 + workshop/es/GUIDE.md | 83 + workshop/pt_BR/00-overview.md | 60 + workshop/pt_BR/01-setup.md | 121 + workshop/pt_BR/02-design.md | 91 + workshop/pt_BR/03-quiz-master.md | 50 + workshop/pt_BR/04-multi-agent.md | 117 + workshop/pt_BR/05-complete.md | 57 + workshop/pt_BR/GUIDE.md | 83 + 85 files changed, 10204 insertions(+) create mode 100644 .copilotignore create mode 100644 .devcontainer/devcontainer.json create mode 100644 .github/agents/pixel-jam.agent.md create mode 100644 .github/agents/quiz-master.agent.md create mode 100644 .github/agents/tdd-green.agent.md create mode 100644 .github/agents/tdd-red.agent.md create mode 100644 .github/agents/tdd-refactor.agent.md create mode 100644 .github/agents/tdd.agent.md create mode 100644 .github/agents/ui-review.agent.md create mode 100644 .github/instructions/frontend-design.instructions.md create mode 100644 .github/instructions/tailwind-4.instructions.md create mode 100644 .github/prompts/cloud-explore.prompt.md create mode 100644 .github/prompts/setup.prompt.md create mode 100644 .github/workflows/deploy.yml create mode 100644 .gitignore create mode 100644 .solutions/.hidden-from-ai-agents create mode 100644 .vscode/extensions.json create mode 100644 .vscode/launch.json create mode 100644 .vscode/mcp.json create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 CODE_OF_CONDUCT.md create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE create mode 100644 README.es.md create mode 100644 README.md create mode 100644 README.pt_BR.md create mode 100644 SECURITY.md create mode 100644 SUPPORT.md create mode 100644 docs/es/index.html create mode 100644 docs/es/step.html create mode 100644 docs/index.html create mode 100644 docs/light-theme.css create mode 100644 docs/pt_BR/index.html create mode 100644 docs/pt_BR/step.html create mode 100644 docs/step.css create mode 100644 docs/step.html create mode 100644 docs/styles.css create mode 100644 docs/theme-toggle.js create mode 100644 eslint.config.js create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 public/vite.svg create mode 100644 src/App.tsx create mode 100644 src/assets/react.svg create mode 100644 src/components/BingoBoard.tsx create mode 100644 src/components/BingoModal.tsx create mode 100644 src/components/BingoSquare.tsx create mode 100644 src/components/GameScreen.tsx create mode 100644 src/components/StartScreen.tsx create mode 100644 src/data/questions.ts create mode 100644 src/hooks/useBingoGame.ts create mode 100644 src/index.css create mode 100644 src/main.tsx create mode 100644 src/test/setup.ts create mode 100644 src/types/index.ts create mode 100644 src/utils/bingoLogic.test.ts create mode 100644 src/utils/bingoLogic.ts create mode 100644 tsconfig.app.json create mode 100644 tsconfig.json create mode 100644 tsconfig.node.json create mode 100644 vite.config.ts create mode 100644 workshop/.ignored-by-agents create mode 100644 workshop/00-overview.md create mode 100644 workshop/01-setup.md create mode 100644 workshop/02-design.md create mode 100644 workshop/03-quiz-master.md create mode 100644 workshop/04-multi-agent.md create mode 100644 workshop/05-complete.md create mode 100644 workshop/GUIDE.md create mode 100644 workshop/es/00-overview.md create mode 100644 workshop/es/01-setup.md create mode 100644 workshop/es/02-design.md create mode 100644 workshop/es/03-quiz-master.md create mode 100644 workshop/es/04-multi-agent.md create mode 100644 workshop/es/05-complete.md create mode 100644 workshop/es/GUIDE.md create mode 100644 workshop/pt_BR/00-overview.md create mode 100644 workshop/pt_BR/01-setup.md create mode 100644 workshop/pt_BR/02-design.md create mode 100644 workshop/pt_BR/03-quiz-master.md create mode 100644 workshop/pt_BR/04-multi-agent.md create mode 100644 workshop/pt_BR/05-complete.md create mode 100644 workshop/pt_BR/GUIDE.md diff --git a/.copilotignore b/.copilotignore new file mode 100644 index 0000000..3879701 --- /dev/null +++ b/.copilotignore @@ -0,0 +1 @@ +workshop \ No newline at end of file diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..13459d0 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,27 @@ +{ + "name": "Soc-Ops React TypeScript", + "image": "mcr.microsoft.com/devcontainers/typescript-node:22", + "customizations": { + "vscode": { + "extensions": [ + "vitest.explorer", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss" + ] + }, + "codespaces": { + "openFiles": [ + "src/App.tsx", + "README.md" + ] + } + }, + "postCreateCommand": "npm install", + "forwardPorts": [5173], + "portsAttributes": { + "5173": { + "label": "Vite Dev Server", + "onAutoForward": "openPreview" + } + } +} diff --git a/.github/agents/pixel-jam.agent.md b/.github/agents/pixel-jam.agent.md new file mode 100644 index 0000000..10fcd04 --- /dev/null +++ b/.github/agents/pixel-jam.agent.md @@ -0,0 +1,31 @@ +--- +name: Pixel Jam +argument-hint: What should we design today? +description: Design user interfaces quickly and iteratively in code. +target: vscode +--- + +Implement UI elements from the provided plan through small, focused iterations with the user in the loop. + +**Artifact:** Design Spec file `docs/design-spec.md` + +## Goal +- Translate planned UX into concrete screens and interactions. +- Iterate in atomic visual or interaction changes for rapid feedback. +- Start with entry points first, so the user can start using the screens, and then add more details. +- Document design rationale and decisions in the design spec file. + +## Scope +- Work only on UI-facing layers (layout, styling, components, states). + +## Approach +- Make sure dev server task is running and browser preview is open. +- Prioritize clarity, responsiveness, and visual alignment with intent. +- Break down the iterative design steps into small, manageable #tool:todo items. +- After each step, make sure the build task is OK, then use #tool:playwright/* to visually review components and interactions. +- Keep tracking decisions and findings in the design spec file. +- PAUSE for user feedback after each completed iteration. + +## Constraints +- Keep iterations minimal and reversible. +- Avoid premature optimization with components or abstractions. diff --git a/.github/agents/quiz-master.agent.md b/.github/agents/quiz-master.agent.md new file mode 100644 index 0000000..591e783 --- /dev/null +++ b/.github/agents/quiz-master.agent.md @@ -0,0 +1,19 @@ +--- +name: Quiz Master +description: Creates fun and engaging icebreaker questions and bingo prompts +argument-hint: Describe the theme of questions you want +tools: ['search', 'edit'] +--- + +Your goal is to curate engaging icebreaker bingo questions tailored to a given theme. + +If the user didn't provide a theme, PAUSE and suggest a few themes to apply. + +## Question Design Guidelines + +- **Difficulty Mix:** Include a balanced set of easy, medium, and bold prompts so all comfort levels can participate. +- **Category Variety:** Blend personal, work-related, and fun/random prompts. +- **Inclusive & Safe:** Keep all questions low-stakes, respectful, and free from sensitive topics (e.g., health, finances, politics, relationships). +- **Conversation Starters:** Favor prompts that spark follow-up stories or small interactions (e.g., "Show something you built last year," "Share a surprising skill"). +- **Wildcards:** Include a few playful action-based squares (e.g., rock–paper–scissors, teach a 5-second trick). +- **Easy Wins:** Ensure 40–60% of squares/questions are simple "gimmes" to maintain flow and avoid frustration. diff --git a/.github/agents/tdd-green.agent.md b/.github/agents/tdd-green.agent.md new file mode 100644 index 0000000..391e0e3 --- /dev/null +++ b/.github/agents/tdd-green.agent.md @@ -0,0 +1,16 @@ +--- +name: TDD Green +description: TDD phase for writing MINIMAL implementation to pass tests +infer: true +tools: ['search', 'edit', 'execute/runTests'] +handoffs: + - label: TDD Refactor + agent: TDD Refactor + prompt: Refactor the implementation +--- + +You are TDD Green, the code-implementer. Given a failing test case and context (existing codebase or module), write the minimal code change needed so that the test passes — no extra features. + +ONLY update implementation, do not touch tests. + +After implementing changes, invoke "TDD Test Runner" agent using runSubagent to verify the tests pass. diff --git a/.github/agents/tdd-red.agent.md b/.github/agents/tdd-red.agent.md new file mode 100644 index 0000000..1d7bf31 --- /dev/null +++ b/.github/agents/tdd-red.agent.md @@ -0,0 +1,13 @@ +--- +name: TDD Red +description: TDD phase for writing FAILING tests +infer: true +tools: ['read', 'edit', 'search'] +handoffs: + - label: TDD Green + agent: TDD Green + prompt: Implement minimal implementation +--- +You are TDD Red, the test-writer: for a given task, generate complete tests that asserts the expected behavior, which must fail when run against the current codebase. Use the project’s style/conventions. + +ONLY write tests, no implementation. \ No newline at end of file diff --git a/.github/agents/tdd-refactor.agent.md b/.github/agents/tdd-refactor.agent.md new file mode 100644 index 0000000..fc6739e --- /dev/null +++ b/.github/agents/tdd-refactor.agent.md @@ -0,0 +1,9 @@ +--- +name: TDD Refactor +description: Refactor code while maintaining passing tests +tools: ['search', 'edit', 'execute/runTests'] +infer: true +--- +You are TDD Refactor, the refactor-assistant. Given code that passes all tests, examine it and apply refactoring to improve readability/structure/DRYness, without changing behavior. + +Only suggest refactorings, no new functionality, no breaking changes. \ No newline at end of file diff --git a/.github/agents/tdd.agent.md b/.github/agents/tdd.agent.md new file mode 100644 index 0000000..91fa3ca --- /dev/null +++ b/.github/agents/tdd.agent.md @@ -0,0 +1,16 @@ +--- +name: TDD Supervisor +description: Orchestrate full TDD cycle from request to implementation +tools: ['agent'] +--- + +Your goal is take high-level user instructions (feature, spec, bug fix) to orchestrate the TDD cycle: + +1. Invoke "TDD Red" agent to write failing tests +2. Invoke "TDD Green" agent to write minimal implementation +3. Invoke "TDD Test Runner" agent to verify tests pass +4. If tests fail, ask user to decide whether to revise or abort +5. If tests pass, optionally invoke "TDD Refactor" agent to improve code quality +6. Output a summary of changes ready for review/commit + +Use the #tool:agent/runSubagent tool with the exact agent names above. diff --git a/.github/agents/ui-review.agent.md b/.github/agents/ui-review.agent.md new file mode 100644 index 0000000..8b7f107 --- /dev/null +++ b/.github/agents/ui-review.agent.md @@ -0,0 +1,20 @@ +--- +name: UI Review +argument-hint: Optional review area to focus on (or just "start") +model: Claude Haiku 4.5 (copilot) +tools: ['search', 'execute/getTaskOutput', 'github/*', 'playwright/*', 'search/usages', 'read/problems', 'web/fetch', 'web/githubRepo', 'todo', 'agent'] +infer: true +--- + +Your goal is to do an in-depth UI review of a website using Playwright and scope potential fixes. + +Assume the dev server is running as task and check those first. + +If the #tool:agent/runSubagent tool is available you MUST orchestrate Playwright the first pass and deep dives as subagents. + + +1. Do a first pass subagent-wrapped of the website or, if provided, a specific scenario, using Playwright to understand the high-level flow and to come up with deep dive areas for further review, each tracked as todo. +2. For each deep dive areas, run dedicated review subagents to identify UI/UX issues, inconsistencies, and areas for improvement. +3. Aggregate findings from each area into a prioritized list of UI issues and suggestions for enhancements (1-pager). +4. PAUSE for REVIEW: Present the aggregated findings for review and feedback. + \ No newline at end of file diff --git a/.github/instructions/frontend-design.instructions.md b/.github/instructions/frontend-design.instructions.md new file mode 100644 index 0000000..0ba7045 --- /dev/null +++ b/.github/instructions/frontend-design.instructions.md @@ -0,0 +1,27 @@ +--- +name: frontend-design +description: Use this skill when the user asks to design/build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics. +--- + +You tend to converge toward generic, "on distribution" outputs. In frontend design,this creates what users call the "AI slop" aesthetic. Avoid this: make creative,distinctive frontends that surprise and delight. + +Focus on: +- Typography: Choose fonts that are beautiful, unique, and interesting. Avoid generic fonts like Arial and Inter; opt instead for distinctive choices that elevate the frontend's aesthetics. +- Color & Theme: Commit to a cohesive aesthetic. Use CSS variables for consistency. Dominant colors with sharp accents outperform timid, evenly-distributed palettes. Draw from IDE themes and cultural aesthetics for inspiration. +- Motion: Use animations for effects and micro-interactions. Prioritize CSS-only solutions for HTML. Use Motion library for React when available. Focus on high-impact moments: one well-orchestrated page load with staggered reveals (animation-delay) creates more delight than scattered micro-interactions. +- Backgrounds: Create atmosphere and depth rather than defaulting to solid colors. Layer CSS gradients, use geometric patterns, or add contextual effects that match the overall aesthetic. + +Avoid generic AI-generated aesthetics: +- Overused font families (Inter, Roboto, Arial, system fonts) +- Clichéd color schemes (particularly purple gradients on white backgrounds) +- Predictable layouts and component patterns +- Cookie-cutter design that lacks context-specific character + +Interpret creatively and make unexpected choices that feel genuinely designed for the context. Vary between light and dark themes, different fonts, different aesthetics. You still tend to converge on common choices (Space Grotesk, for example) across generations. Avoid this: it is critical that you think outside the box! + + +**IMPORTANT**: Match implementation complexity to the aesthetic vision. Maximalist designs need elaborate code with extensive animations and effects. Minimalist or refined designs need restraint, precision, and careful attention to spacing, typography, and subtle details. Elegance comes from executing the vision well. + +**Remember:** The agent is capable of extraordinary creative work. Don't hold back, show what can truly be created when thinking outside the box and committing fully to a distinctive vision. + +// Source: [Improving frontend design through Skills](https://www.claude.com/blog/improving-frontend-design-through-skills). \ No newline at end of file diff --git a/.github/instructions/tailwind-4.instructions.md b/.github/instructions/tailwind-4.instructions.md new file mode 100644 index 0000000..532ac69 --- /dev/null +++ b/.github/instructions/tailwind-4.instructions.md @@ -0,0 +1,83 @@ +--- +description: Read before writing any Tailwind CSS to understand the latest v4 features. +--- + +# Tailwind CSS v4 Development Practices + +## Core Philosophy +- CSS-first configuration via `@theme` directive (no `tailwind.config.js`) +- Native CSS features: cascade layers, `@property`, `color-mix()`, logical properties +- Automatic content detection + +## Setup +```css +@import "tailwindcss"; +``` + +## @theme Configuration + +### Define Tokens +```css +@theme { + --color-brand: oklch(0.72 0.11 178); + --font-display: "Inter", sans-serif; + --breakpoint-tablet: 640px; +} +``` +→ Use as: `bg-brand`, `font-display` + +### Multi-theme Pattern +```css +@theme inline { + --color-primary: var(--primary); +} + +:root { --primary: #3b82f6; } +.dark { --primary: #60a5fa; } +``` +Use `@theme inline` for runtime variable resolution. + +### Rules +- Keep @theme variables flat (top-level only) +- No nesting in @media or selectors +- Use :root for non-Tailwind CSS variables + +## v4 Features + +### Native Opacity +```html +
+``` + +### Container Queries +```html +
+
+
+``` + +### Arbitrary CSS Variables +```html +
+``` + +### New Utilities +- 3D: `rotate-x-45`, `rotate-y-90` +- Gradients: `bg-gradient-radial`, `bg-gradient-conic` +- Variants: `not-*`, `color-scheme:*`, `@starting-style` + +## Migration + +### Renamed Classes +- `bg-opacity-50` → `bg-black/50` +- `overflow-ellipsis` → `text-ellipsis` +- `shadow-sm` → `shadow-xs` + +## Best Practices +1. Put design tokens in `@theme` +2. Use `:root` for regular CSS variables +3. Trust auto-detection +4. Prefer `w-[--custom]` over complex config + +// Seed prompt +// > Fill in a 1-pager copilot instructions (compact, minimal prose), targeted for frontend devs and focused on tailwind v4-specific development essentials; #web_search \ No newline at end of file diff --git a/.github/prompts/cloud-explore.prompt.md b/.github/prompts/cloud-explore.prompt.md new file mode 100644 index 0000000..e0ace18 --- /dev/null +++ b/.github/prompts/cloud-explore.prompt.md @@ -0,0 +1,13 @@ +--- +agent: agent +argument-hint: Optionally specify variations +tools: ['github/create_pull_request_with_copilot', 'todo'] +--- + +Your goal is to explore potential solutions with a remote coding agent for a given plan. + +1. Look at the plan to identify 3 variations to explore (trade-offs, interfaces, approaches, technologies, etc). +2. Create #tool:todo for each variation +3. Call #tool:github/create_pull_request_with_copilot to hand off the specifics for each variation to implement. Coding agent will handle the implementation details, so your focus should be on the overall plan and constraints for each variation. + +End with summarizing each variation and compare & contrast. \ No newline at end of file diff --git a/.github/prompts/setup.prompt.md b/.github/prompts/setup.prompt.md new file mode 100644 index 0000000..e58fc17 --- /dev/null +++ b/.github/prompts/setup.prompt.md @@ -0,0 +1,14 @@ +--- +agent: agent +description: Get my development workspace ready +tools: ['vscode/openSimpleBrowser', 'execute/runTask', 'read/getTaskOutput', 'execute/runInTerminal', 'read', 'search', 'todo'] +--- + +Your goal is to successfully build and run the workspace as local development environment. + +## Checklist +- [ ] Required dependencies from package.json/README.md installed +- [ ] Lint, build and test tasks functional +- [ ] Dev server running +- [ ] Site open in browser preview +- [ ] Short engaging welcome tour for the workspace \ No newline at end of file diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml new file mode 100644 index 0000000..30120f8 --- /dev/null +++ b/.github/workflows/deploy.yml @@ -0,0 +1,59 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: [main] + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: 'pages' + cancel-in-progress: true + +jobs: + build-and-deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: 'npm' + + - run: npm ci + + - name: Build app + run: npm run build + env: + VITE_REPO_NAME: ${{ github.event.repository.name }} + + - name: Prepare deployment + run: | + mkdir -p ./_site + + # Copy workshop docs to deployment root + cp -r docs/* ./_site/ + + # Copy built app to /game/ subfolder + cp -r dist ./_site/game + + # Prevent Jekyll processing + touch ./_site/.nojekyll + + - uses: actions/configure-pages@v4 + + - uses: actions/upload-pages-artifact@v3 + with: + path: _site + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..71bef6f --- /dev/null +++ b/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? +.playwright-mcp diff --git a/.solutions/.hidden-from-ai-agents b/.solutions/.hidden-from-ai-agents new file mode 100644 index 0000000..f7c077b --- /dev/null +++ b/.solutions/.hidden-from-ai-agents @@ -0,0 +1 @@ +Don't use this folder for code suggestions. \ No newline at end of file diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 0000000..2db72c3 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,7 @@ +{ + "recommendations": [ + "vitest.explorer", + "dbaeumer.vscode-eslint", + "bradlc.vscode-tailwindcss" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..59e3f12 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Launch in integrated browser", + "request": "launch", + "type": "editor-browser", + "webRoot": "${workspaceFolder}", + "url": "http://localhost:5173/", + "preLaunchTask": "npm: dev" + } + ] +} diff --git a/.vscode/mcp.json b/.vscode/mcp.json new file mode 100644 index 0000000..127af59 --- /dev/null +++ b/.vscode/mcp.json @@ -0,0 +1,4 @@ +{ + "servers": { + } +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..7807ce8 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,26 @@ +{ + "chat.promptFilesRecommendations": { + "setup": "resourceFilename == 'README.md'" + }, + "chat.tools.terminal.enableAutoApprove": true, + "chat.tools.terminal.autoApprove": { + "npm run lint": true, + "npm run dev": true, + "npm run build": true, + "npm test": true, + "npm install": true, + "npx eslint": true + }, + "github.copilot.chat.githubMcpServer.enabled": true, + "github.copilot.chat.githubMcpServer.toolsets": [ + "repos,issues,pull_requests,code_search,copilot,web_search" + ], + "workbench.browser.enableChatTools": true, + "github.copilot.chat.agentDebugLog.enabled": true, + "github.copilot.chat.agentDebugLog.fileLogging.enabled": true, + "imageCarousel.explorerContextMenu.enabled": true, + "imageCarousel.chat.enabled": true, + "chat.customizationsMenu.enabled": true, + "chat.useCustomizationsInParentRepositories": true, + "chat.useCustomAgentHooks": true +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..75c70dd --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,28 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "npm: dev", + "type": "shell", + "command": "npm run dev", + "isBackground": true, + "problemMatcher": { + "pattern": { + "regexp": "." + }, + "background": { + "activeOnStart": true, + "beginsPattern": "^.*VITE v\\d+\\.\\d+\\.\\d+.*", + "endsPattern": "^.*ready in.*" + } + }, + "presentation": { + "reveal": "never" + }, + "group": { + "kind": "build", + "isDefault": true + } + } + ] +} \ No newline at end of file diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..686e5e7 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,10 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..3b4db64 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,15 @@ +# Contributing + +This project welcomes contributions and suggestions. Most contributions require you to agree to a +Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us +the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. + +When you submit a pull request, a CLA bot will automatically determine whether you need to provide +a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions +provided by the bot. You will only need to do this once across all repos using our CLA. + +## Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). +For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or +contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..9e841e7 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE 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 diff --git a/README.es.md b/README.es.md new file mode 100644 index 0000000..f766376 --- /dev/null +++ b/README.es.md @@ -0,0 +1,50 @@ + +# Bingo Mixer + +Bingo Mixer es un juego de bingo social para encuentros presenciales. ¡Encuentra personas que coincidan con las preguntas y consigue 5 en línea! + +📚 **[Ver Guía del Lab](workshop/es/GUIDE.md)** +🚀 **[Comienza por la configuración](workshop/es/01-setup.md)** + +--- + +## 📚 Guía del Lab + +| Parte | Título | +|-------|--------| +| [**00**](workshop/es/00-overview.md) | Descripción General & Lista Rápida | +| [**01**](workshop/es/01-setup.md) | Configuración & Ingeniería de Contexto | +| [**02**](workshop/es/02-design.md) | Frontend Design-First | +| [**03**](workshop/es/03-quiz-master.md) | Quiz Master Personalizado | +| [**04**](workshop/es/04-multi-agent.md) | Desarrollo Multi-Agente | + +> 📝 Las guías del lab también están disponibles en la carpeta [`workshop/es/`](workshop/es/) para lectura offline. + +--- + +## Requisitos Previos + +- [Node.js 22](https://nodejs.org/) o superior + +## Dev Container / Codespaces + +Este repo ya incluye un `.devcontainer/devcontainer.json`. + +- **GitHub Codespaces:** crea un Codespace desde **tu propio repositorio** y usará el devcontainer automáticamente. +- **VS Code Dev Containers:** clona localmente, luego ejecuta `Dev Containers: Reopen in Container`. +- **Publicación en GitHub Pages:** el deploy está configurado para tu propio repositorio. + +## Ejecutar + +```bash +npm install +npm run dev +``` + +## Build + +```bash +npm run build +``` + +El deploy se realiza automáticamente en GitHub Pages al hacer push a la rama `main`. diff --git a/README.md b/README.md new file mode 100644 index 0000000..7ee6b92 --- /dev/null +++ b/README.md @@ -0,0 +1,51 @@ +🌐 [Português (BR)](README.pt_BR.md) | [Español](README.es.md) + +# Bingo Mixer + +Bingo Mixer is a social bingo game for in-person mixers. Find people who match the questions and get 5 in a row! + +📚 **[View Lab Guide](workshop/GUIDE.md)** +🚀 **[Start with setup flow](workshop/01-setup.md)** + +--- + +## 📚 Lab Guide + +| Part | Title | +|------|-------| +| [**00**](workshop/00-overview.md) | Overview & Checklist | +| [**01**](workshop/01-setup.md) | Setup & Context Engineering | +| [**02**](workshop/02-design.md) | Design-First Frontend | +| [**03**](workshop/03-quiz-master.md) | Custom Quiz Master | +| [**04**](workshop/04-multi-agent.md) | Multi-Agent Development | + +> 📝 Lab guides are also available in the [`workshop/`](workshop/) folder for offline reading. + +--- + +## Prerequisites + +- [Node.js 22](https://nodejs.org/) or higher + +## Dev Container / Codespaces + +This repo already includes a `.devcontainer/devcontainer.json`. + +- **GitHub Codespaces:** create a Codespace from **your own repository** and it will use the devcontainer automatically. +- **VS Code Dev Containers:** clone locally, then run `Dev Containers: Reopen in Container`. +- **GitHub Pages publishing:** deployment is configured for your own repository. + +## Run + +```bash +npm install +npm run dev +``` + +## Build + +```bash +npm run build +``` + +Deploys automatically to GitHub Pages on push to `main`. diff --git a/README.pt_BR.md b/README.pt_BR.md new file mode 100644 index 0000000..8054d3d --- /dev/null +++ b/README.pt_BR.md @@ -0,0 +1,50 @@ + +# Bingo Mixer + +Bingo Mixer é um jogo de bingo social para encontros presenciais. Encontre pessoas que correspondam às perguntas e consiga 5 em linha! + +📚 **[Ver Guia do Lab](workshop/pt_BR/GUIDE.md)** +🚀 **[Comece pela configuração](workshop/pt_BR/01-setup.md)** + +--- + +## 📚 Guia do Lab + +| Parte | Título | +|-------|--------| +| [**00**](workshop/pt_BR/00-overview.md) | Visão Geral & Lista Rápida | +| [**01**](workshop/pt_BR/01-setup.md) | Configuração & Engenharia de Contexto | +| [**02**](workshop/pt_BR/02-design.md) | Frontend Design-First | +| [**03**](workshop/pt_BR/03-quiz-master.md) | Quiz Master Personalizado | +| [**04**](workshop/pt_BR/04-multi-agent.md) | Desenvolvimento Multi-Agente | + +> 📝 Os guias do lab também estão disponíveis na pasta [`workshop/pt_BR/`](workshop/pt_BR/) para leitura offline. + +--- + +## Pré-requisitos + +- [Node.js 22](https://nodejs.org/) ou superior + +## Dev Container / Codespaces + +Este repo já inclui um `.devcontainer/devcontainer.json`. + +- **GitHub Codespaces:** crie um Codespace a partir do **seu próprio repositório** e ele usará o devcontainer automaticamente. +- **VS Code Dev Containers:** clone localmente, depois execute `Dev Containers: Reopen in Container`. +- **Publicação no GitHub Pages:** o deploy está configurado para o seu próprio repositório. + +## Executar + +```bash +npm install +npm run dev +``` + +## Build + +```bash +npm run build +``` + +O deploy é feito automaticamente no GitHub Pages ao fazer push na branch `main`. diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..656f791 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,14 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which +includes all source code repositories in our GitHub organizations. + +**Please do not report security vulnerabilities through public GitHub issues.** + +For security reporting information, locations, contact information, and policies, +please review the latest guidance for Microsoft repositories at +[https://aka.ms/SECURITY.md](https://aka.ms/SECURITY.md). + + diff --git a/SUPPORT.md b/SUPPORT.md new file mode 100644 index 0000000..1601f9e --- /dev/null +++ b/SUPPORT.md @@ -0,0 +1,13 @@ +# Support + +## How to file issues and get help + +This project uses GitHub Issues to track bugs and feature requests. Please search the existing +issues before filing new issues to avoid duplicates. For new issues, file your bug or +feature request as a new Issue. + +For help and questions about using this project, please file an issue on GitHub. + +## Microsoft Support Policy + +Support for this project is limited to the resources listed above. diff --git a/docs/es/index.html b/docs/es/index.html new file mode 100644 index 0000000..862b136 --- /dev/null +++ b/docs/es/index.html @@ -0,0 +1,200 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab | Edición TypeScript + + + + + + + + + + + + +
+
+ +
    +
  • 🇬🇧 English
  • +
  • 🇪🇸 Español
  • +
  • 🇧🇷 Português (Brasil)
  • +
+
+ +
+
+
+ 🚀 TypeScript + Vite + React + Tailwind +

VS Code GitHub Copilot Agent Lab

+

+ Construye un juego de Bingo Social mientras dominas el Agent Mode de VS Code con GitHub Copilot. + Aprende ingeniería de contexto, agentes personalizados y flujos de desarrollo con IA. +

+ +

También disponible en .NET, Java y Python

+
+
+
~1hr
+
Duración
+
+
+
4
+
Partes
+
+
+
9
+
Etapas
+
+
+
+ +
+

Lo que Aprenderás

+
+
+
🧠
+

Ingeniería de Contexto

+

Enseña a la IA sobre tu código con instrucciones, haciendo las sugerencias de Copilot más precisas y relevantes.

+
+
+
🤖
+

Primitivas Agénticas

+

Usa agentes en segundo plano, agentes en la nube y flujos personalizados para escalar tu desarrollo.

+
+
+
🎨
+

Desarrollo Design-First

+

Deja que la IA itere en la UI mientras tú guías la visión creativa con agentes de diseño especializados.

+
+
+
🧪
+

Desarrollo Guiado por Pruebas

+

Construye funcionalidades confiables usando agentes TDD con el flujo Red-Green-Refactor.

+
+
+
+ +
+

Partes del Taller

+ +
+
+ +
+
+

Requisitos Previos

+
+
+ ✓ + VS Code v1.107+ +
+
+ ✓ + GitHub Copilot +
+
+ ✓ + Node.js 22+ +
+
+ ✓ + Git instalado +
+
+
+
+ +
+ +
+ + + diff --git a/docs/es/step.html b/docs/es/step.html new file mode 100644 index 0000000..421d113 --- /dev/null +++ b/docs/es/step.html @@ -0,0 +1,373 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab + + + + + + + + + + + + + +
+
+ + 🎮 + VS Code Copilot Agent Lab + + +
+
+ +
+ + +
+ +
+
+ + + + + + diff --git a/docs/index.html b/docs/index.html new file mode 100644 index 0000000..1b132a7 --- /dev/null +++ b/docs/index.html @@ -0,0 +1,200 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab | TypeScript Edition + + + + + + + + + + + + +
+
+ +
    +
  • 🇬🇧 English
  • +
  • 🇪🇸 Español
  • +
  • 🇧🇷 Português (Brasil)
  • +
+
+ +
+
+
+ 🚀 TypeScript + Vite + React + Tailwind +

VS Code GitHub Copilot Agent Lab

+

+ Build a Social Bingo game while mastering VS Code's Agent Mode with GitHub Copilot. + Learn context engineering, custom agents, and AI-first development workflows. +

+ +

Also available in .NET, Java, and Python

+
+
+
~1hr
+
Duration
+
+
+
4
+
Parts
+
+
+
9
+
Checkpoints
+
+
+
+ +
+

What You'll Learn

+
+
+
🧠
+

Context Engineering

+

Teach AI about your codebase with instructions, making Copilot suggestions more accurate and relevant.

+
+
+
🤖
+

Agentic Primitives

+

Use background agents, cloud agents, and custom workflows to scale your development.

+
+
+
🎨
+

Design-First Development

+

Let AI iterate on UI while you guide the creative vision with specialized design agents.

+
+
+
🧪
+

Test-Driven Development

+

Build reliable features using TDD agents with Red-Green-Refactor workflow.

+
+
+
+ +
+

Workshop Parts

+ +
+
+ +
+
+

Prerequisites

+
+
+ ✓ + VS Code v1.107+ +
+
+ ✓ + GitHub Copilot +
+
+ ✓ + Node.js 22+ +
+
+ ✓ + Git installed +
+
+
+
+ +
+ +
+ + + diff --git a/docs/light-theme.css b/docs/light-theme.css new file mode 100644 index 0000000..d44ecad --- /dev/null +++ b/docs/light-theme.css @@ -0,0 +1,65 @@ +html[data-theme="light"] { + --bg-dark: #f5f5f7; + --bg-card: #ffffff; + --bg-code: #f0f0f5; + --neon-cyan: #0077b6; + --neon-magenta: #c2185b; + --neon-purple: #7b1fa2; + --text-primary: #1a1a2e; + --text-secondary: #555570; + --border-color: #d0d0dd; + --success-green: #2e7d32; + --warning-yellow: #f9a825; +} + +html[data-theme="light"] body::before { + background: linear-gradient(90deg, rgba(0,119,182,0.05) 1px, transparent 1px), + linear-gradient(rgba(0,119,182,0.05) 1px, transparent 1px); +} + +html[data-theme="light"] .hero::before { + background: radial-gradient(ellipse at 50% 0%, rgba(0,119,182,0.15), transparent 70%); +} + +html[data-theme="light"] .header { + background: rgba(245,245,247,0.95); +} + +html[data-theme="light"] code, +html[data-theme="light"] pre { + background: var(--bg-code); + color: #1a1a2e; +} + +html[data-theme="light"] .btn-secondary { + border-color: var(--border-color); + color: var(--text-primary); +} + +html[data-theme="light"] .btn-secondary:hover { + background: rgba(0,119,182,0.1); + border-color: var(--neon-cyan); +} + +html[data-theme="light"] .card { + box-shadow: 0 2px 8px rgba(0,0,0,0.08); +} + +.theme-toggle { + background: none; + border: 1px solid var(--border-color); + color: var(--text-primary); + cursor: pointer; + padding: 0.4rem 0.8rem; + border-radius: 6px; + font-size: 1rem; + transition: all 0.3s ease; + display: inline-flex; + align-items: center; + gap: 0.4rem; +} + +.theme-toggle:hover { + border-color: var(--neon-cyan); + color: var(--neon-cyan); +} diff --git a/docs/pt_BR/index.html b/docs/pt_BR/index.html new file mode 100644 index 0000000..df609b5 --- /dev/null +++ b/docs/pt_BR/index.html @@ -0,0 +1,200 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab | Edição TypeScript + + + + + + + + + + + + +
+
+ +
    +
  • 🇬🇧 English
  • +
  • 🇪🇸 Español
  • +
  • 🇧🇷 Português (Brasil)
  • +
+
+ +
+
+
+ 🚀 TypeScript + Vite + React + Tailwind +

VS Code GitHub Copilot Agent Lab

+

+ Construa um jogo de Bingo Social enquanto domina o Agent Mode do VS Code com GitHub Copilot. + Aprenda engenharia de contexto, agentes personalizados e fluxos de desenvolvimento com IA. +

+ +

Também disponível em .NET, Java e Python

+
+
+
~1hr
+
Duração
+
+
+
4
+
Partes
+
+
+
9
+
Etapas
+
+
+
+ +
+

O que Você Vai Aprender

+
+
+
🧠
+

Engenharia de Contexto

+

Ensine a IA sobre seu código com instruções, tornando as sugestões do Copilot mais precisas e relevantes.

+
+
+
🤖
+

Primitivas Agênticas

+

Use agentes em segundo plano, agentes na nuvem e fluxos personalizados para escalar seu desenvolvimento.

+
+
+
🎨
+

Desenvolvimento Design-First

+

Deixe a IA iterar na UI enquanto você guia a visão criativa com agentes de design especializados.

+
+
+
🧪
+

Desenvolvimento Orientado a Testes

+

Construa funcionalidades confiáveis usando agentes TDD com o fluxo Red-Green-Refactor.

+
+
+
+ +
+

Partes do Workshop

+ +
+
+ +
+
+

Pré-requisitos

+
+
+ ✓ + VS Code v1.107+ +
+
+ ✓ + GitHub Copilot +
+
+ ✓ + Node.js 22+ +
+
+ ✓ + Git instalado +
+
+
+
+ +
+ +
+ + + diff --git a/docs/pt_BR/step.html b/docs/pt_BR/step.html new file mode 100644 index 0000000..f9c2491 --- /dev/null +++ b/docs/pt_BR/step.html @@ -0,0 +1,373 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab + + + + + + + + + + + + + +
+
+ + 🎮 + VS Code Copilot Agent Lab + + +
+
+ +
+ + +
+ +
+
+ + + + + + diff --git a/docs/step.css b/docs/step.css new file mode 100644 index 0000000..30faf96 --- /dev/null +++ b/docs/step.css @@ -0,0 +1,249 @@ + :root { + --bg-dark: #0a0a0f; + --bg-card: #12121a; + --bg-code: #1a1a2e; + --bg-overlay: rgba(18, 18, 26, 0.92); + --neon-cyan: #00f5ff; + --neon-magenta: #ff00ff; + --neon-purple: #b366ff; + --text-primary: #ffffff; + --text-secondary: #a0a0b0; + --border-color: #2a2a3a; + --success-green: #4ec9b0; + --warning-yellow: #dcdcaa; + --code-block-top-padding: 3.5rem; + --code-block-horizontal-padding: 1.25rem; + } + + * { margin: 0; padding: 0; box-sizing: border-box; } + + body { + font-family: 'Space Grotesk', sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + line-height: 1.7; + min-height: 100vh; + } + + body::before { + content: ''; + position: fixed; + top: 0; left: 0; width: 100%; height: 100%; + background: + linear-gradient(90deg, rgba(0,245,255,0.03) 1px, transparent 1px), + linear-gradient(rgba(0,245,255,0.03) 1px, transparent 1px); + background-size: 50px 50px; + pointer-events: none; + z-index: -1; + } + + .header { + background: var(--bg-card); + border-bottom: 1px solid var(--border-color); + padding: 1rem 2rem; + position: sticky; + top: 0; + z-index: 100; + } + .header-content { + max-width: 1200px; margin: 0 auto; + display: flex; align-items: center; justify-content: space-between; gap: 1rem; + } + .header-brand { + display: flex; align-items: center; gap: 1rem; + text-decoration: none; color: var(--text-primary); + } + .header-brand:hover { color: var(--neon-cyan); } + .header-title { + font-size: 1.25rem; font-weight: 600; + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-magenta)); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; + } + .header-nav { display: flex; gap: 0.5rem; } + .nav-btn { + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 0.5rem 1rem; background: transparent; + border: 1px solid var(--border-color); border-radius: 6px; + color: var(--text-secondary); text-decoration: none; + font-size: 0.875rem; transition: all 0.2s ease; + cursor: pointer; font-family: inherit; + } + .nav-btn:hover:not(:disabled) { border-color: var(--neon-cyan); color: var(--neon-cyan); } + .nav-btn:disabled { opacity: 0.4; cursor: not-allowed; } + + .layout { display: flex; max-width: 1400px; margin: 0 auto; } + + .sidebar { + width: 280px; background: var(--bg-card); + border-right: 1px solid var(--border-color); + height: calc(100vh - 65px); position: sticky; top: 65px; + overflow-y: auto; flex-shrink: 0; + } + .sidebar-section { padding: 1rem; } + .sidebar-title { + font-size: 0.75rem; text-transform: uppercase; + letter-spacing: 0.1em; color: var(--text-secondary); + margin-bottom: 0.75rem; padding: 0 0.5rem; + } + .step-link { + display: flex; align-items: center; gap: 0.75rem; + padding: 0.625rem 0.75rem; border-radius: 6px; + text-decoration: none; color: var(--text-secondary); + font-size: 0.875rem; transition: all 0.2s ease; + margin-bottom: 0.25rem; + } + .step-link:hover { background: rgba(0,245,255,0.1); color: var(--text-primary); } + .step-link.active { + background: linear-gradient(135deg, rgba(0,245,255,0.2), rgba(255,0,255,0.2)); + color: var(--text-primary); border-left: 3px solid var(--neon-cyan); + } + .step-number { + width: 24px; height: 24px; display: flex; align-items: center; justify-content: center; + background: var(--border-color); border-radius: 6px; + font-size: 0.75rem; font-weight: 600; + font-family: 'JetBrains Mono', monospace; flex-shrink: 0; + } + .step-link.active .step-number { + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-magenta)); + color: var(--bg-dark); + } + + .main { flex: 1; min-width: 0; padding: 2rem 3rem; } + .content { max-width: 800px; margin: 0 auto; } + + .loading { + display: flex; align-items: center; justify-content: center; + height: 300px; color: var(--text-secondary); + } + .loading::after { + content: ''; width: 32px; height: 32px; + border: 3px solid var(--border-color); + border-top-color: var(--neon-cyan); border-radius: 50%; + animation: spin 0.8s linear infinite; margin-left: 1rem; + } + @keyframes spin { to { transform: rotate(360deg); } } + + .markdown h1 { + font-size: 2.25rem; font-weight: 700; margin-bottom: 1.5rem; + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-magenta)); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; + background-clip: text; line-height: 1.3; + } + .markdown h2 { + font-size: 1.5rem; font-weight: 600; margin: 2.5rem 0 1rem; + color: var(--neon-cyan); border-bottom: 1px solid var(--border-color); + padding-bottom: 0.5rem; + } + .markdown h3 { font-size: 1.25rem; font-weight: 600; margin: 2rem 0 0.75rem; color: var(--neon-magenta); } + .markdown h4 { font-size: 1.1rem; font-weight: 600; margin: 1.5rem 0 0.5rem; color: var(--text-primary); } + .markdown p { margin-bottom: 1rem; color: var(--text-secondary); } + .markdown a { color: var(--neon-cyan); text-decoration: none; } + .markdown a:hover { text-decoration: underline; } + .markdown strong { color: var(--text-primary); font-weight: 600; } + .markdown ul, .markdown ol { margin: 1rem 0 1.5rem 1.5rem; color: var(--text-secondary); } + .markdown li { margin-bottom: 0.5rem; } + .markdown li::marker { color: var(--neon-cyan); } + + .markdown input[type="checkbox"] { + appearance: none; width: 18px; height: 18px; + border: 2px solid var(--border-color); border-radius: 4px; + margin-right: 0.5rem; vertical-align: middle; + position: relative; cursor: pointer; + } + .markdown input[type="checkbox"]:checked { background: var(--neon-cyan); border-color: var(--neon-cyan); } + .markdown input[type="checkbox"]:checked::after { + content: '✓'; position: absolute; color: var(--bg-dark); + font-size: 12px; top: 50%; left: 50%; transform: translate(-50%, -50%); + } + + .markdown code { font-family: 'JetBrains Mono', monospace; font-size: 0.875em; } + .markdown :not(pre) > code { + background: var(--bg-code); padding: 0.2em 0.4em; + border-radius: 4px; color: var(--warning-yellow); + } + .markdown pre { + position: relative; + background: var(--bg-code); border: 1px solid var(--border-color); + border-radius: 8px; + padding: var(--code-block-top-padding) var(--code-block-horizontal-padding) var(--code-block-horizontal-padding) var(--code-block-horizontal-padding); + overflow-x: auto; margin: 1.5rem 0; + } + .markdown pre code { color: var(--text-primary); background: none; padding: 0; } + .markdown .code-copy-btn { + position: absolute; top: 0.75rem; right: 0.75rem; + display: inline-flex; align-items: center; justify-content: center; + min-width: max-content; padding: 0.4rem 0.75rem; + border: 1px solid var(--border-color); border-radius: 999px; + background: var(--bg-overlay); color: var(--text-secondary); + font: inherit; font-size: 0.8rem; cursor: pointer; transition: all 0.2s ease; + } + .markdown .code-copy-btn:hover, + .markdown .code-copy-btn:focus-visible { + border-color: var(--neon-cyan); color: var(--neon-cyan); + outline: none; + } + .markdown .code-copy-btn[data-state="copied"] { + border-color: var(--success-green); color: var(--success-green); + } + .markdown .code-copy-btn[data-state="error"] { + border-color: var(--neon-magenta); color: var(--neon-magenta); + } + .sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; + } + + .markdown blockquote { + background: var(--bg-card); border-left: 4px solid var(--neon-cyan); + border-radius: 0 8px 8px 0; padding: 1rem 1.25rem; margin: 1.5rem 0; + } + .markdown blockquote p { margin: 0; color: var(--text-secondary); } + .markdown blockquote p:first-child { font-weight: 600; color: var(--neon-cyan); } + + .markdown img { max-width: 100%; border-radius: 8px; border: 1px solid var(--border-color); margin: 1.5rem 0; } + + .markdown table { width: 100%; border-collapse: collapse; margin: 1.5rem 0; } + .markdown th, .markdown td { padding: 0.75rem 1rem; border: 1px solid var(--border-color); text-align: left; } + .markdown th { background: var(--bg-card); color: var(--text-primary); font-weight: 600; } + .markdown td { color: var(--text-secondary); } + .markdown tr:hover td { background: rgba(0,245,255,0.05); } + + .markdown hr { border: none; border-top: 1px solid var(--border-color); margin: 2.5rem 0; } + + .nav-footer { + display: flex; justify-content: space-between; + margin-top: 3rem; padding-top: 2rem; + border-top: 1px solid var(--border-color); + } + .nav-footer-btn { + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 0.75rem 1.5rem; background: var(--bg-card); + border: 1px solid var(--border-color); border-radius: 8px; + color: var(--text-primary); text-decoration: none; + font-weight: 500; transition: all 0.2s ease; + } + .nav-footer-btn:hover { border-color: var(--neon-cyan); transform: translateY(-2px); } + .nav-footer-btn.disabled { opacity: 0.4; pointer-events: none; } + + @media (max-width: 900px) { + .sidebar { display: none; } + .main { padding: 1.5rem; } + .header-title { display: none; } + } + + /* --- Locale Picker --- */ + .locale-picker { position: relative; display: inline-flex; } + .locale-toggle { display: flex; align-items: center; justify-content: center; padding: 8px; border-radius: 10px; border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-secondary); font-size: 1rem; line-height: 1; transition: all 0.2s; cursor: pointer; } + .locale-toggle:hover { background: var(--neon-cyan); color: #fff; border-color: var(--neon-cyan); } + .locale-picker ul { display: none; position: absolute; top: 100%; right: 0; margin: 4px 0 0; padding: 4px 0; list-style: none; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 100; min-width: 180px; } + .locale-picker li { padding: 8px 16px; cursor: pointer; font-size: 0.9rem; color: var(--text-primary); white-space: nowrap; transition: background 0.15s; } + .locale-picker li:hover { background: var(--neon-cyan); color: #fff; } + .locale-picker li.active { font-weight: 600; color: var(--neon-cyan); } + .locale-picker li.active:hover { color: #fff; } diff --git a/docs/step.html b/docs/step.html new file mode 100644 index 0000000..0a2b17f --- /dev/null +++ b/docs/step.html @@ -0,0 +1,380 @@ + + + + + + + + + VS Code GitHub Copilot Agent Lab + + + + + + + + + + + + + +
+
+ + 🎮 + VS Code Copilot Agent Lab + + +
+
+ +
+ + +
+ +
+
+ + + + + + diff --git a/docs/styles.css b/docs/styles.css new file mode 100644 index 0000000..3303e2e --- /dev/null +++ b/docs/styles.css @@ -0,0 +1,151 @@ +:root { + --bg-dark: #0a0a0f; + --bg-card: #12121a; + --neon-cyan: #00f5ff; + --neon-magenta: #ff00ff; + --neon-purple: #b366ff; + --text-primary: #ffffff; + --text-secondary: #a0a0b0; + --border-color: #2a2a3a; +} + +* { margin: 0; padding: 0; box-sizing: border-box; } + +body { + font-family: 'Space Grotesk', sans-serif; + background: var(--bg-dark); + color: var(--text-primary); + line-height: 1.6; + min-height: 100vh; +} + +body::before { + content: ''; + position: fixed; + top: 0; left: 0; width: 100%; height: 100%; + background: + linear-gradient(90deg, rgba(0,245,255,0.03) 1px, transparent 1px), + linear-gradient(rgba(0,245,255,0.03) 1px, transparent 1px); + background-size: 50px 50px; + pointer-events: none; + z-index: -1; +} + +.container { max-width: 1200px; margin: 0 auto; padding: 0 2rem; } + +.hero { padding: 6rem 0 4rem; text-align: center; } +.hero-badge { + display: inline-block; padding: 0.5rem 1rem; + background: rgba(0,245,255,0.1); border: 1px solid var(--neon-cyan); + border-radius: 50px; font-size: 0.875rem; color: var(--neon-cyan); + margin-bottom: 2rem; font-family: 'JetBrains Mono', monospace; +} +.hero h1 { + font-size: clamp(2.5rem, 6vw, 4rem); font-weight: 700; margin-bottom: 1rem; + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-magenta)); + -webkit-background-clip: text; -webkit-text-fill-color: transparent; background-clip: text; +} +.hero-subtitle { font-size: 1.25rem; color: var(--text-secondary); max-width: 600px; margin: 0 auto 2rem; } +.hero-cta { display: flex; gap: 1rem; justify-content: center; flex-wrap: wrap; } + +.btn { + display: inline-flex; align-items: center; gap: 0.5rem; + padding: 1rem 2rem; border-radius: 8px; font-weight: 600; + text-decoration: none; transition: all 0.3s ease; font-size: 1rem; +} +.btn-primary { + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-purple)); + color: var(--bg-dark); +} +.btn-primary:hover { transform: translateY(-2px); box-shadow: 0 10px 40px rgba(0,245,255,0.3); } +.btn-secondary { background: transparent; border: 2px solid var(--border-color); color: var(--text-primary); } +.btn-secondary:hover { border-color: var(--neon-cyan); color: var(--neon-cyan); } + +.stats { display: flex; justify-content: center; gap: 3rem; margin-top: 4rem; flex-wrap: wrap; } +.stat { text-align: center; } +.stat-value { font-size: 2rem; font-weight: 700; color: var(--neon-cyan); font-family: 'JetBrains Mono', monospace; } +.stat-label { font-size: 0.875rem; color: var(--text-secondary); margin-top: 0.25rem; } + +.features { padding: 4rem 0; } +.section-title { font-size: 2rem; text-align: center; margin-bottom: 3rem; } +.features-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 1.5rem; } +.feature-card { + background: var(--bg-card); border: 1px solid var(--border-color); + border-radius: 12px; padding: 2rem; transition: all 0.3s ease; +} +.feature-card:hover { border-color: var(--neon-cyan); transform: translateY(-4px); } +.feature-icon { font-size: 2.5rem; margin-bottom: 1rem; } +.feature-card h3 { font-size: 1.25rem; margin-bottom: 0.5rem; } +.feature-card p { color: var(--text-secondary); font-size: 0.95rem; } + +.workshop { padding: 4rem 0; } +.parts-list { display: flex; flex-direction: column; gap: 1rem; max-width: 800px; margin: 0 auto; } +.part-card { + display: flex; align-items: center; gap: 1.5rem; + background: var(--bg-card); border: 1px solid var(--border-color); + border-radius: 12px; padding: 1.5rem; text-decoration: none; + color: inherit; transition: all 0.3s ease; +} +.part-card:hover { border-color: var(--neon-magenta); background: rgba(255,0,255,0.05); } +.part-number { + width: 50px; height: 50px; display: flex; align-items: center; justify-content: center; + background: linear-gradient(135deg, var(--neon-cyan), var(--neon-magenta)); + border-radius: 12px; font-weight: 700; font-size: 1.25rem; + color: var(--bg-dark); flex-shrink: 0; +} +.part-content { flex: 1; } +.part-content h3 { font-size: 1.1rem; margin-bottom: 0.25rem; } +.part-content p { color: var(--text-secondary); font-size: 0.9rem; } +.part-time { + font-family: 'JetBrains Mono', monospace; font-size: 0.875rem; + color: var(--neon-cyan); padding: 0.25rem 0.75rem; + background: rgba(0,245,255,0.1); border-radius: 50px; +} + +.prereqs { padding: 4rem 0; background: var(--bg-card); } +.prereqs-grid { + display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 1rem; max-width: 800px; margin: 0 auto; +} +.prereq-item { display: flex; align-items: center; gap: 0.75rem; padding: 1rem; background: var(--bg-dark); border-radius: 8px; } +.prereq-check { color: var(--neon-cyan); font-size: 1.25rem; } + +footer { padding: 3rem 0; text-align: center; border-top: 1px solid var(--border-color); } +.footer-links { display: flex; justify-content: center; gap: 2rem; margin-bottom: 1rem; flex-wrap: wrap; } +.footer-links a { color: var(--text-secondary); text-decoration: none; transition: color 0.3s ease; } +.footer-links a:hover { color: var(--neon-cyan); } +.footer-credit { color: var(--text-secondary); font-size: 0.875rem; } + +@media (max-width: 768px) { + .hero { padding: 4rem 0 3rem; } + .stats { gap: 2rem; } + .part-card { flex-direction: column; text-align: center; } + .part-time { margin-top: 0.5rem; } +} + +.hero-also { + margin-top: 1rem; + font-size: 0.95rem; + color: var(--text-secondary); +} + +.hero-also a { + color: var(--neon-cyan); + text-decoration: none; + transition: color 0.3s ease; +} + +.hero-also a:hover { + color: var(--neon-magenta); + text-decoration: underline; +} + +/* --- Locale Picker --- */ +.locale-picker { position: relative; display: inline-flex; } +.locale-toggle { display: flex; align-items: center; justify-content: center; padding: 8px; border-radius: 10px; border: 1px solid var(--border-color); background: var(--bg-card); color: var(--text-secondary); font-size: 1rem; line-height: 1; transition: all 0.2s; cursor: pointer; } +.locale-toggle:hover { background: var(--neon-cyan); color: #fff; border-color: var(--neon-cyan); } +.locale-picker ul { display: none; position: absolute; top: 100%; right: 0; margin: 4px 0 0; padding: 4px 0; list-style: none; background: var(--bg-card); border: 1px solid var(--border-color); border-radius: 10px; box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 100; min-width: 180px; } +.locale-picker li { padding: 8px 16px; cursor: pointer; font-size: 0.9rem; color: var(--text-primary); white-space: nowrap; transition: background 0.15s; } +.locale-picker li:hover { background: var(--neon-cyan); color: #fff; } +.locale-picker li.active { font-weight: 600; color: var(--neon-cyan); } +.locale-picker li.active:hover { color: #fff; } diff --git a/docs/theme-toggle.js b/docs/theme-toggle.js new file mode 100644 index 0000000..418d19b --- /dev/null +++ b/docs/theme-toggle.js @@ -0,0 +1,24 @@ +(function() { + const saved = localStorage.getItem('theme'); + if (saved) { + document.documentElement.setAttribute('data-theme', saved); + } +})(); + +function toggleTheme() { + const html = document.documentElement; + const current = html.getAttribute('data-theme'); + const next = current === 'light' ? 'dark' : 'light'; + html.setAttribute('data-theme', next); + localStorage.setItem('theme', next); + updateToggleIcon(); +} + +function updateToggleIcon() { + const btn = document.querySelector('.theme-toggle'); + if (!btn) return; + const isLight = document.documentElement.getAttribute('data-theme') === 'light'; + btn.innerHTML = isLight ? '🌙 Dark' : '☀️ Light'; +} + +document.addEventListener('DOMContentLoaded', updateToggleIcon); diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..5e6b472 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,23 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + ecmaVersion: 2020, + globals: globals.browser, + }, + }, +]) diff --git a/index.html b/index.html new file mode 100644 index 0000000..ba418f7 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + + + + Bingo Mixer + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..9e067fc --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4403 @@ +{ + "name": "bingo-mixer", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "bingo-mixer", + "version": "0.0.0", + "license": "MIT", + "dependencies": { + "@tailwindcss/vite": "^4.1.17", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwindcss": "^4.1.17" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^25.5.0", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "jsdom": "^29.0.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.1", + "vite": "^8.0.1", + "vitest": "^4.0.15" + } + }, + "node_modules/@adobe/css-tools": { + "version": "4.4.4", + "resolved": "https://registry.npmjs.org/@adobe/css-tools/-/css-tools-4.4.4.tgz", + "integrity": "sha512-Elp+iwUx5rN5+Y8xLt5/GRoG20WGoDCQ/1Fb+1LiGtvwbDavuSk0jhD/eZdckHAuzcDzccnkv+rEjyWfRx18gg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.1.tgz", + "integrity": "sha512-iGWN8E45Ws0XWx3D44Q1t6vX2LqhCKcwfmwBYCDsFrYFS6m4q/Ks61L2veETaLv+ckDC6+dTETJoaAAb7VjLiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^3.1.1", + "@csstools/css-color-parser": "^4.0.2", + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0", + "lru-cache": "^11.2.7" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.0.4.tgz", + "integrity": "sha512-jXR6x4AcT3eIrS2fSNAwJpwirOkGcd+E7F7CP3zjdTqz9B/2huHOL8YJZBgekKwLML+u7qB/6P1LXQuMScsx0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.2.1", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.7" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.2.tgz", + "integrity": "sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@bramus/specificity": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", + "integrity": "sha512-ctxtJ/eA+t+6q2++vj5j7FYX3nRu311q1wfYH3xjlLOsczhlhxAg2FWNUXhpGvAw3BWo1xBcvOV6/YLc2r5FJw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-tree": "^3.0.0" + }, + "bin": { + "specificity": "bin/cli.js" + } + }, + "node_modules/@csstools/color-helpers": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz", + "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@csstools/css-calc": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.1.1.tgz", + "integrity": "sha512-HJ26Z/vmsZQqs/o3a6bgKslXGFAungXGbinULZO3eMsOyNJHeBBZfup5FiZInOghgoM4Hwnmw+OgbJCNg1wwUQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.0.2.tgz", + "integrity": "sha512-0GEfbBLmTFf0dJlpsNU7zwxRIH0/BGEMuXLTCvFYxuL1tNhqzTbtnFICyJLTNK4a+RechKP75e7w42ClXSnJQw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^6.0.2", + "@csstools/css-calc": "^3.1.1" + }, + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^4.0.0", + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-4.0.0.tgz", + "integrity": "sha512-+B87qS7fIG3L5h3qwJ/IFbjoVoOe/bpOdh9hAjXbvx0o8ImEmUsGXN0inFOnk2ChCFgqkkGFQ+TpM5rbhkKe4w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^4.0.0" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.2.tgz", + "integrity": "sha512-5GkLzz4prTIpoyeUiIu3iV6CSG3Plo7xRVOFPKI7FVEJ3mZ0A8SwK0XU3Gl7xAkiQ+mDyam+NNp875/C5y+jSA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "peerDependencies": { + "css-tree": "^3.2.1" + }, + "peerDependenciesMeta": { + "css-tree": { + "optional": true + } + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-4.0.0.tgz", + "integrity": "sha512-QxULHAm7cNu72w97JUNCBFODFaXpbDg+dP8b/oWFAZ2MTRppA3U00Y2L1HqaS4J6yBqxwa/Y3nMBaxVKbB/NsA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=20.19.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.2.tgz", + "integrity": "sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz", + "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "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/eslint-utils/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-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.7", + "debug": "^4.3.1", + "minimatch": "^3.1.5" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "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.5", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", + "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.14.0", + "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.1", + "minimatch": "^3.1.5", + "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/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/@eslint/js": { + "version": "9.39.4", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", + "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", + "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.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.0.tgz", + "integrity": "sha512-UY0nlA+feH81UGSHv92sLEPLCeZFjXOuHhrIo0HQydScuQc8s0A7kL/UdgwgDq8g8ilksmuoF35YVTNphV2aBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "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.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "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/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.2.tgz", + "integrity": "sha512-sNXv5oLJ7ob93xkZ1XnxisYhGYXfaG9f65/ZgYuAu3qt7b3NadcOEhLvx28hv31PgX8SZJRYrAIPQilQmFpLVw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "node_modules/@oxc-project/types": { + "version": "0.122.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.122.0.tgz", + "integrity": "sha512-oLAl5kBpV4w69UtFZ9xqcmTi+GENWOcPF7FCrczTiBbmC0ibXxCwyvZGbO39rCVEuLGAZM84DH0pUIyyv/YJzA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-pv1y2Fv0JybcykuiiD3qBOBdz6RteYojRFY1d+b95WVuzx211CRh+ytI/+9iVyWQ6koTh5dawe4S/yRfOFjgaA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-cFYr6zTG/3PXXF3pUO+umXxt1wkRK/0AYT8lDwuqvRC+LuKYWSAQAQZjCWDQpAH172ZV6ieYrNnFzVVcnSflAg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-ZCsYknnHzeXYps0lGBz8JrF37GpE9bFVefrlmDrAQhOEi4IOIlcoU1+FwHEtyXGx2VkYAvhu7dyBf75EJQffBw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.12.tgz", + "integrity": "sha512-dMLeprcVsyJsKolRXyoTH3NL6qtsT0Y2xeuEA8WQJquWFXkEC4bcu1rLZZSnZRMtAqwtrF/Ib9Ddtpa/Gkge9Q==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.12.tgz", + "integrity": "sha512-YqWjAgGC/9M1lz3GR1r1rP79nMgo3mQiiA+Hfo+pvKFK1fAJ1bCi0ZQVh8noOqNacuY1qIcfyVfP6HoyBRZ85Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-/I5AS4cIroLpslsmzXfwbe5OmWvSsrFuEw3mwvbQ1kDxJ822hFHIx+vsN/TAzNVyepI/j/GSzrtCIwQPeKCLIg==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-V6/wZztnBqlx5hJQqNWwFdxIKN0m38p8Jas+VoSfgH54HSj9tKTt1dZvG6JRHcjh6D7TvrJPWFGaY9UBVOaWPw==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-AP3E9BpcUYliZCxa3w5Kwj9OtEVDYK6sVoUzy4vTOJsjPOgdaJZKFmN4oOlX0Wp0RPV2ETfmIra9x1xuayFB7g==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-nWwpvUSPkoFmZo0kQazZYOrT7J5DGOJ/+QHHzjvNlooDZED8oH82Yg67HvehPPLAg5fUff7TfWFHQS8IV1n3og==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.12.tgz", + "integrity": "sha512-RNrafz5bcwRy+O9e6P8Z/OCAJW/A+qtBczIqVYwTs14pf4iV1/+eKEjdOUta93q2TsT/FI0XYDP3TCky38LMAg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.12.tgz", + "integrity": "sha512-Jpw/0iwoKWx3LJ2rc1yjFrj+T7iHZn2JDg1Yny1ma0luviFS4mhAIcd1LFNxK3EYu3DHWCps0ydXQ5i/rrJ2ig==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.12.tgz", + "integrity": "sha512-vRugONE4yMfVn0+7lUKdKvN4D5YusEiPilaoO2sgUWpCvrncvWgPMzK00ZFFJuiPgLwgFNP5eSiUlv2tfc+lpA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.12.tgz", + "integrity": "sha512-ykGiLr/6kkiHc0XnBfmFJuCjr5ZYKKofkx+chJWDjitX+KsJuAmrzWhwyOMSHzPhzOHOy7u9HlFoa5MoAOJ/Zg==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.1.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-5eOND4duWkwx1AzCxadcOrNeighiLwMInEADT0YM7xeEOOFcovWZCq8dadXgcRHSf3Ulh1kFo/qvzoFiCLOL1Q==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.12.tgz", + "integrity": "sha512-PyqoipaswDLAZtot351MLhrlrh6lcZPo2LSYE+VDxbVk24LVKAGOuE4hb8xZQmrPAuEtTZW8E6D2zc5EUZX4Lw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.7", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.7.tgz", + "integrity": "sha512-qujRfC8sFVInYSPPMLQByRh7zhwkGFS4+tyMQ83srV1qrxL4g8E2tyxVVyxd0+8QeBM1mIk9KbWxkegRr76XzA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@standard-schema/spec": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", + "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.2.tgz", + "integrity": "sha512-pXS+wJ2gZpVXqFaUEjojq7jzMpTGf8rU6ipJz5ovJV6PUGmlJ+jvIwGrzdHdQ80Sg+wmQxUFuoW1UAAwHNEdFA==", + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.2.tgz", + "integrity": "sha512-qEUA07+E5kehxYp9BVMpq9E8vnJuBHfJEC0vPC5e7iL/hw7HR61aDKoVoKzrG+QKp56vhNZe4qwkRmMC0zDLvg==", + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-arm64": "4.2.2", + "@tailwindcss/oxide-darwin-x64": "4.2.2", + "@tailwindcss/oxide-freebsd-x64": "4.2.2", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.2", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.2", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.2", + "@tailwindcss/oxide-linux-x64-musl": "4.2.2", + "@tailwindcss/oxide-wasm32-wasi": "4.2.2", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.2", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.2" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.2.tgz", + "integrity": "sha512-dXGR1n+P3B6748jZO/SvHZq7qBOqqzQ+yFrXpoOWWALWndF9MoSKAT3Q0fYgAzYzGhxNYOoysRvYlpixRBBoDg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.2.tgz", + "integrity": "sha512-iq9Qjr6knfMpZHj55/37ouZeykwbDqF21gPFtfnhCCKGDcPI/21FKC9XdMO/XyBM7qKORx6UIhGgg6jLl7BZlg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.2.tgz", + "integrity": "sha512-BlR+2c3nzc8f2G639LpL89YY4bdcIdUmiOOkv2GQv4/4M0vJlpXEa0JXNHhCHU7VWOKWT/CjqHdTP8aUuDJkuw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.2.tgz", + "integrity": "sha512-YUqUgrGMSu2CDO82hzlQ5qSb5xmx3RUrke/QgnoEx7KvmRJHQuZHZmZTLSuuHwFf0DJPybFMXMYf+WJdxHy/nQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.2.tgz", + "integrity": "sha512-FPdhvsW6g06T9BWT0qTwiVZYE2WIFo2dY5aCSpjG/S/u1tby+wXoslXS0kl3/KXnULlLr1E3NPRRw0g7t2kgaQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.2.tgz", + "integrity": "sha512-4og1V+ftEPXGttOO7eCmW7VICmzzJWgMx+QXAJRAhjrSjumCwWqMfkDrNu1LXEQzNAwz28NCUpucgQPrR4S2yw==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.2.tgz", + "integrity": "sha512-oCfG/mS+/+XRlwNjnsNLVwnMWYH7tn/kYPsNPh+JSOMlnt93mYNCKHYzylRhI51X+TbR+ufNhhKKzm6QkqX8ag==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.2.tgz", + "integrity": "sha512-rTAGAkDgqbXHNp/xW0iugLVmX62wOp2PoE39BTCGKjv3Iocf6AFbRP/wZT/kuCxC9QBh9Pu8XPkv/zCZB2mcMg==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.2.tgz", + "integrity": "sha512-XW3t3qwbIwiSyRCggeO2zxe3KWaEbM0/kW9e8+0XpBgyKU4ATYzcVSMKteZJ1iukJ3HgHBjbg9P5YPRCVUxlnQ==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.2.tgz", + "integrity": "sha512-eKSztKsmEsn1O5lJ4ZAfyn41NfG7vzCg496YiGtMDV86jz1q/irhms5O0VrY6ZwTUkFy/EKG3RfWgxSI3VbZ8Q==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.2.tgz", + "integrity": "sha512-qPmaQM4iKu5mxpsrWZMOZRgZv1tOZpUm+zdhhQP0VhJfyGGO3aUKdbh3gDZc/dPLQwW4eSqWGrrcWNBZWUWaXQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.2.tgz", + "integrity": "sha512-1T/37VvI7WyH66b+vqHj/cLwnCxt7Qt3WFu5Q8hk65aOvlwAhs7rAp1VkulBJw/N4tMirXjVnylTR72uI0HGcA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.2.tgz", + "integrity": "sha512-mEiF5HO1QqCLXoNEfXVA1Tzo+cYsrqV7w9Juj2wdUFyW07JRenqMG225MvPwr3ZD9N1bFQj46X7r33iHxLUW0w==", + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.2", + "@tailwindcss/oxide": "4.2.2", + "tailwindcss": "4.2.2" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@testing-library/jest-dom": { + "version": "6.9.1", + "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-6.9.1.tgz", + "integrity": "sha512-zIcONa+hVtVSSep9UT3jZ5rizo2BsxgyDYU7WFD5eICBE7no3881HGeb/QkGfsJs6JTkY1aQhT7rIPC7e+0nnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@adobe/css-tools": "^4.4.0", + "aria-query": "^5.0.0", + "css.escape": "^1.5.1", + "dom-accessibility-api": "^0.6.3", + "picocolors": "^1.1.1", + "redent": "^3.0.0" + }, + "engines": { + "node": ">=14", + "npm": ">=6", + "yarn": ">=1" + } + }, + "node_modules/@testing-library/jest-dom/node_modules/dom-accessibility-api": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.6.3.tgz", + "integrity": "sha512-7ZgogeTnjuHbo+ct10G9Ffp0mif17idi0IyWNVA/wcwcm7NPOD/WEHVP3n7n3MhXqxoIYm8d6MuZohYWIZ4T3w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@testing-library/react": { + "version": "16.3.2", + "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-16.3.2.tgz", + "integrity": "sha512-XU5/SytQM+ykqMnAnvB2umaJNIOsLF3PVv//1Ew4CTcpz0/BRyy/af40qqrt7SjKpDdT1saBMc42CUok5gaw+g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@testing-library/dom": "^10.0.0", + "@types/react": "^18.0.0 || ^19.0.0", + "@types/react-dom": "^18.0.0 || ^19.0.0", + "react": "^18.0.0 || ^19.0.0", + "react-dom": "^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/aria-query": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", + "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/@types/chai": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", + "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/deep-eql": "*", + "assertion-error": "^2.0.1" + } + }, + "node_modules/@types/deep-eql": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", + "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", + "dev": true, + "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==", + "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/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.58.0.tgz", + "integrity": "sha512-RLkVSiNuUP1C2ROIWfqX+YcUfLaSnxGE/8M+Y57lopVwg9VTYYfhuz15Yf1IzCKgZj6/rIbYTmJCUSqr76r0Wg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/type-utils": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.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.58.0", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/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/@typescript-eslint/parser": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.58.0.tgz", + "integrity": "sha512-rLoGZIf9afaRBYsPUMtvkDWykwXwUPL60HebR4JgTI8mxfFe2cQTu3AGitANp4b9B2QlVru6WzjgB2IzJKiCSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3" + }, + "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 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.58.0.tgz", + "integrity": "sha512-8Q/wBPWLQP1j16NxoPNIKpDZFMaxl7yWIoqXWYeWO+Bbd2mjgvoF0dxP2jKZg5+x49rgKdf7Ck473M8PC3V9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.58.0", + "@typescript-eslint/types": "^8.58.0", + "debug": "^4.4.3" + }, + "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 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.58.0.tgz", + "integrity": "sha512-W1Lur1oF50FxSnNdGp3Vs6P+yBRSmZiw4IIjEeYxd8UQJwhUF0gDgDD/W/Tgmh73mxgEU3qX0Bzdl/NGuSPEpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0" + }, + "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.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.58.0.tgz", + "integrity": "sha512-doNSZEVJsWEu4htiVC+PR6NpM+pa+a4ClH9INRWOWCUzMst/VA9c4gXq92F8GUD1rwhNvRLkgjfYtFXegXQF7A==", + "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 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.58.0.tgz", + "integrity": "sha512-aGsCQImkDIqMyx1u4PrVlbi/krmDsQUs4zAcCV6M7yPcPev+RqVlndsJy9kJ8TLihW9TZ0kbDAzctpLn5o+lOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.0", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.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 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.58.0.tgz", + "integrity": "sha512-O9CjxypDT89fbHxRfETNoAnHj/i6IpRK0CvbVN3qibxlLdo5p5hcLmUuCCrHMpxiWSwKyI8mCP7qRNYuOJ0Uww==", + "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.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.58.0.tgz", + "integrity": "sha512-7vv5UWbHqew/dvs+D3e1RvLv1v2eeZ9txRHPnEEBUgSNLx5ghdzjHa0sgLWYVKssH+lYmV0JaWdoubo0ncGYLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.58.0", + "@typescript-eslint/tsconfig-utils": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/visitor-keys": "8.58.0", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.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 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.58.0.tgz", + "integrity": "sha512-RfeSqcFeHMHlAWzt4TBjWOAtoW9lnsAGiP3GbaX9uVgTYYrMbVnGONEfUCiSss+xMHFl+eHZiipmA8WkQ7FuNA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.58.0", + "@typescript-eslint/types": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.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 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.58.0.tgz", + "integrity": "sha512-XJ9UD9+bbDo4a4epraTwG3TsNPeiB9aShrUneAVXy8q4LuwowN+qu89/6ByLMINqvIMeI9H9hOHQtg/ijrYXzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.58.0", + "eslint-visitor-keys": "^5.0.0" + }, + "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": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.1.tgz", + "integrity": "sha512-l9X/E3cDb+xY3SWzlG1MOGt2usfEHGMNIaegaUGFsLkb3RCn/k8/TOXBcab+OndDI4TBtktT8/9BwwW8Vi9KUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "1.0.0-rc.7" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/@vitest/expect": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.2.tgz", + "integrity": "sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.1.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "chai": "^6.2.2", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.2.tgz", + "integrity": "sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.1.2", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.21" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.2.tgz", + "integrity": "sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.2.tgz", + "integrity": "sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.1.2", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.2.tgz", + "integrity": "sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.2", + "@vitest/utils": "4.1.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.2.tgz", + "integrity": "sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.2.tgz", + "integrity": "sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.1.2", + "convert-source-map": "^2.0.0", + "tinyrainbow": "^3.1.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "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/ajv": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", + "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", + "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-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", + "peer": true, + "engines": { + "node": ">=8" + } + }, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/aria-query": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "dequal": "^2.0.3" + } + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "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/baseline-browser-mapping": { + "version": "2.10.13", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.13.tgz", + "integrity": "sha512-BL2sTuHOdy0YT1lYieUxTw/QMtPBC3pmlJC6xk8BBYVv6vcw3SGdKemQ+Xsx9ik2F/lYDO9tqsFQH1r9PFuHKw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "require-from-string": "^2.0.2" + } + }, + "node_modules/brace-expansion": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.13.tgz", + "integrity": "sha512-9ZLprWS6EENmhEOpjCYW2c8VkmOvckIJZfkr7rBW6dObmfgJ/L1GpSYW5Hpo9lDz4D1+n0Ckz8rU7FwHDQiG/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "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/caniuse-lite": { + "version": "1.0.30001784", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001784.tgz", + "integrity": "sha512-WU346nBTklUV9YfUl60fqRbU5ZqyXlqvo1SgigE1OAXK5bFL8LL9q1K7aap3N739l4BvNqnkm3YrGHiY9sfUQw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chai": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "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/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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT" + }, + "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/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/css-tree": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.2.1.tgz", + "integrity": "sha512-X7sjQzceUhu1u7Y/ylrRZFU2FS6LRiFVp6rKLPg23y3x3c3DOKAwuXGDp+PAGjh6CSnCjYeAul8pcT8bAl+lSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "mdn-data": "2.27.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" + } + }, + "node_modules/css.escape": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", + "integrity": "sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/data-urls": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-7.0.0.tgz", + "integrity": "sha512-23XHcCF+coGYevirZceTVD7NdJOqVn+49IHyxgszm+JIiHLoB2TkmPtsYkNWT1pvRSGkc35L6NHs0yHkN2SumA==", + "dev": true, + "license": "MIT", + "dependencies": { + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decimal.js": { + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", + "dev": true, + "license": "MIT" + }, + "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/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-accessibility-api": { + "version": "0.5.16", + "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", + "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/electron-to-chromium": { + "version": "1.5.331", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.331.tgz", + "integrity": "sha512-IbxXrsTlD3hRodkLnbxAPP4OuJYdWCeM3IOdT+CpcMoIwIoDfCmRpEtSPfwBXxVkg9xmBeY7Lz2Eo2TDn/HC3Q==", + "dev": true, + "license": "ISC" + }, + "node_modules/enhanced-resolve": { + "version": "5.20.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.20.1.tgz", + "integrity": "sha512-Qohcme7V1inbAfvjItgw0EaxVX5q2rdVEZHRBrEQdRZTssLDGsL8Lwrznl8oQ/6kuTJONLaDcGjkNP247XEhcA==", + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/es-module-lexer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", + "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", + "dev": true, + "license": "MIT" + }, + "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.39.4", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", + "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.5", + "@eslint/js": "9.39.4", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "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.5", + "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-plugin-react-hooks": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.0.1.tgz", + "integrity": "sha512-O0d0m04evaNzEPoSW+59Mezf8Qt0InfgGIBJnpC0h3NH/WjUAR7BIKUfysC6todmtiZ/A0oUVS8Gce0WhBrHsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "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": "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/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/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "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-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "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/expect-type": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", + "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "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-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/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "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/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-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.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "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==", + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "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/globals": { + "version": "17.4.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.4.0.tgz", + "integrity": "sha512-hjrNztw/VajQwOLsMNT1cbJiH2muO3OROCHnbehc8eY5JyD2gqz4AcMHPqgaOR59DjgUjYAYLeH699g/eWi2jw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "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==", + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/html-encoding-sniffer": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.6.0" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "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/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/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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-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-potential-custom-element-name": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.1.tgz", + "integrity": "sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==", + "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==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", + "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsdom": { + "version": "29.0.1", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.0.1.tgz", + "integrity": "sha512-z6JOK5gRO7aMybVq/y/MlIpKh8JIi68FBKMUtKkK2KH/wMSRlCxQ682d08LB9fYXplyY/UXG8P4XXTScmdjApg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/css-color": "^5.0.1", + "@asamuzakjp/dom-selector": "^7.0.3", + "@bramus/specificity": "^2.4.2", + "@csstools/css-syntax-patches-for-csstree": "^1.1.1", + "@exodus/bytes": "^1.15.0", + "css-tree": "^3.2.1", + "data-urls": "^7.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.7", + "parse5": "^8.0.0", + "saxes": "^6.0.0", + "symbol-tree": "^3.2.4", + "tough-cookie": "^6.0.1", + "undici": "^7.24.5", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.1", + "whatwg-mimetype": "^5.0.0", + "whatwg-url": "^16.0.1", + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24.0.0" + }, + "peerDependencies": { + "canvas": "^3.0.0" + }, + "peerDependenciesMeta": { + "canvas": { + "optional": true + } + } + }, + "node_modules/jsdom/node_modules/lru-cache": { + "version": "11.2.7", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.7.tgz", + "integrity": "sha512-aY/R+aEsRelme17KGQa/1ZSIpLpNYYrhcrepKTZgE+W3WM16YMCaPwOHLHsmopZHELU0Ojin1lPVxKR0MihncA==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "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-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/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "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/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "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/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lz-string": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz", + "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", + "dev": true, + "license": "MIT", + "peer": true, + "bin": { + "lz-string": "bin/bin.js" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/mdn-data": { + "version": "2.27.1", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.27.1.tgz", + "integrity": "sha512-9Yubnt3e8A0OKwxYSXyhLymGW4sCufcLG6VdiDdUGVkPhpqLxlvP5vl1983gQjJl3tqbrM731mjaZaP68AgosQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "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==", + "dev": true, + "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/node-releases": { + "version": "2.0.37", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.37.tgz", + "integrity": "sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==", + "dev": true, + "license": "MIT" + }, + "node_modules/obug": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", + "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/sxzz", + "https://opencollective.com/debug" + ], + "license": "MIT" + }, + "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/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/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/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "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-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/pathe": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", + "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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-format": { + "version": "27.5.1", + "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz", + "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "ansi-regex": "^5.0.1", + "ansi-styles": "^5.0.0", + "react-is": "^17.0.1" + }, + "engines": { + "node": "^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0" + } + }, + "node_modules/pretty-format/node_modules/ansi-styles": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", + "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "dev": true, + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "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/react": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.4.tgz", + "integrity": "sha512-9nfp2hYpCwOjAN+8TZFGhtWEwgvWHXqESH8qT89AT/lWklpLON22Lc8pEtnpsZz7VmawabSU0gCjnj8aC0euHQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.4.tgz", + "integrity": "sha512-AXJdLo8kgMbimY95O2aKQqsz2iWi9jMgKJhRBAxECE4IFxfcazB2LmzloIoibJI3C12IlY20+KFaLv+71bUJeQ==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.4" + } + }, + "node_modules/react-is": { + "version": "17.0.2", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", + "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", + "dev": true, + "license": "MIT", + "peer": true + }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "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/rolldown": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.12.tgz", + "integrity": "sha512-yP4USLIMYrwpPHEFB5JGH1uxhcslv6/hL0OyvTuY+3qlOSJvZ7ntYnoWpehBxufkgN0cvXxppuTu5hHa/zPh+A==", + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.122.0", + "@rolldown/pluginutils": "1.0.0-rc.12" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-arm64": "1.0.0-rc.12", + "@rolldown/binding-darwin-x64": "1.0.0-rc.12", + "@rolldown/binding-freebsd-x64": "1.0.0-rc.12", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.12", + "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.12", + "@rolldown/binding-linux-x64-musl": "1.0.0-rc.12", + "@rolldown/binding-openharmony-arm64": "1.0.0-rc.12", + "@rolldown/binding-wasm32-wasi": "1.0.0-rc.12", + "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.12", + "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.12" + } + }, + "node_modules/rolldown/node_modules/@rolldown/pluginutils": { + "version": "1.0.0-rc.12", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.12.tgz", + "integrity": "sha512-HHMwmarRKvoFsJorqYlFeFRzXZqCt2ETQlEDOb9aqssrnVBB1/+xgTGtuTrIk5vzLNX1MjMtTf7W9z3tsSbrxw==", + "license": "MIT" + }, + "node_modules/saxes": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", + "integrity": "sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==", + "dev": true, + "license": "ISC", + "dependencies": { + "xmlchars": "^2.2.0" + }, + "engines": { + "node": ">=v12.22.7" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", + "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "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/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/symbol-tree": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", + "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", + "dev": true, + "license": "MIT" + }, + "node_modules/tailwindcss": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.2.tgz", + "integrity": "sha512-KWBIxs1Xb6NoLdMVqhbhgwZf2PGBpPEiwOqgI4pFIYbNTfBXiKYyWoTsXgBQ9WFg/OlhnvHaY+AEpW7wSmFo2Q==", + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.2.tgz", + "integrity": "sha512-1MOpMXuhGzGL5TTCZFItxCc0AARf1EZFQkGqMm7ERKj8+Hgr5oLvJOVFcC+lRmR8hCe2S3jC4T5D7Vg/d7/fhA==", + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.4.tgz", + "integrity": "sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/tinyrainbow": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", + "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tldts": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.27.tgz", + "integrity": "sha512-I4FZcVFcqCRuT0ph6dCDpPuO4Xgzvh+spkcTr1gK7peIvxWauoloVO0vuy1FQnijT63ss6AsHB6+OIM4aXHbPg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tldts-core": "^7.0.27" + }, + "bin": { + "tldts": "bin/cli.js" + } + }, + "node_modules/tldts-core": { + "version": "7.0.27", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.27.tgz", + "integrity": "sha512-YQ7uPjgWUibIK6DW5lrKujGwUKhLevU4hcGbP5O6TcIUb+oTjJYJVWPS4nZsIHrEEEG6myk/oqAJUEQmpZrHsg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tough-cookie": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz", + "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tldts": "^7.0.5" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, + "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/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.58.0", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.58.0.tgz", + "integrity": "sha512-e2TQzKfaI85fO+F3QywtX+tCTsu/D3WW5LVU6nz8hTFKFZ8yBJ6mSYRpXqdR3mFjPWmO0eWsTa5f+UpAOe/FMA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.58.0", + "@typescript-eslint/parser": "8.58.0", + "@typescript-eslint/typescript-estree": "8.58.0", + "@typescript-eslint/utils": "8.58.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 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici": { + "version": "7.24.7", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.7.tgz", + "integrity": "sha512-H/nlJ/h0ggGC+uRL3ovD+G0i4bqhvsDOpbDv7At5eFLlj2b41L8QliGbnl2H7SnDiYhENphh1tQFJZf+MyfLsQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "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/vite": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.3.tgz", + "integrity": "sha512-B9ifbFudT1TFhfltfaIPgjo9Z3mDynBTJSUYxTjOQruf/zHH+ezCQKcoqO+h7a9Pw9Nm/OtlXAiGT1axBgwqrQ==", + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.8", + "rolldown": "1.0.0-rc.12", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.0", + "esbuild": "^0.27.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/vitest": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.2.tgz", + "integrity": "sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.1.2", + "@vitest/mocker": "4.1.2", + "@vitest/pretty-format": "4.1.2", + "@vitest/runner": "4.1.2", + "@vitest/snapshot": "4.1.2", + "@vitest/spy": "4.1.2", + "@vitest/utils": "4.1.2", + "es-module-lexer": "^2.0.0", + "expect-type": "^1.3.0", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^4.0.0-rc.1", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.1.0", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@opentelemetry/api": "^1.9.0", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.1.2", + "@vitest/browser-preview": "4.1.2", + "@vitest/browser-webdriverio": "4.1.2", + "@vitest/ui": "4.1.2", + "happy-dom": "*", + "jsdom": "*", + "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + }, + "vite": { + "optional": false + } + } + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.1.tgz", + "integrity": "sha512-BMhLD/Sw+GbJC21C/UgyaZX41nPt8bUTg+jWyDeg7e7YN4xOM05YPSIXceACnXVtqyEw/LMClUQMtMZ+PGGpqQ==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-mimetype": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-5.0.0.tgz", + "integrity": "sha512-sXcNcHOC51uPGF0P/D4NVtrkjSU2fNsm9iog4ZvZJsL3rjoDAzXZhkm2MWt1y+PUdggKAYVoMAIYcs78wJ51Cw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/whatwg-url": { + "version": "16.0.1", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-16.0.1.tgz", + "integrity": "sha512-1to4zXBxmXHV3IiSSEInrreIlu02vUOvrhxJJH5vcxYTBDAx51cqZiKdyTxlecdKNSjj8EcxGBxNf6Vg+945gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@exodus/bytes": "^1.11.0", + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "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/xml-name-validator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18" + } + }, + "node_modules/xmlchars": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-2.2.0.tgz", + "integrity": "sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==", + "dev": true, + "license": "MIT" + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "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/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2f5b82c --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "bingo-mixer", + "private": true, + "version": "0.0.0", + "license": "MIT", + "author": "Harald Kirschner", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "test": "vitest run" + }, + "dependencies": { + "@tailwindcss/vite": "^4.1.17", + "react": "^19.2.0", + "react-dom": "^19.2.0", + "tailwindcss": "^4.1.17" + }, + "devDependencies": { + "@eslint/js": "^9.39.1", + "@testing-library/jest-dom": "^6.9.1", + "@testing-library/react": "^16.3.0", + "@types/node": "^25.5.0", + "@types/react": "^19.2.5", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "eslint": "^9.39.1", + "eslint-plugin-react-hooks": "^7.0.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.4.0", + "jsdom": "^29.0.1", + "typescript": "~5.9.3", + "typescript-eslint": "^8.57.1", + "vite": "^8.0.1", + "vitest": "^4.0.15" + } +} diff --git a/public/vite.svg b/public/vite.svg new file mode 100644 index 0000000..e7b8dfb --- /dev/null +++ b/public/vite.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx new file mode 100644 index 0000000..36c4815 --- /dev/null +++ b/src/App.tsx @@ -0,0 +1,38 @@ +import { useBingoGame } from './hooks/useBingoGame'; +import { StartScreen } from './components/StartScreen'; +import { GameScreen } from './components/GameScreen'; +import { BingoModal } from './components/BingoModal'; + +function App() { + const { + gameState, + board, + winningSquareIds, + showBingoModal, + startGame, + handleSquareClick, + resetGame, + dismissModal, + } = useBingoGame(); + + if (gameState === 'start') { + return ; + } + + return ( + <> + + {showBingoModal && ( + + )} + + ); +} + +export default App; diff --git a/src/assets/react.svg b/src/assets/react.svg new file mode 100644 index 0000000..6c87de9 --- /dev/null +++ b/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/components/BingoBoard.tsx b/src/components/BingoBoard.tsx new file mode 100644 index 0000000..739de7a --- /dev/null +++ b/src/components/BingoBoard.tsx @@ -0,0 +1,23 @@ +import type { BingoSquareData } from '../types'; +import { BingoSquare } from './BingoSquare'; + +interface BingoBoardProps { + board: BingoSquareData[]; + winningSquareIds: Set; + onSquareClick: (squareId: number) => void; +} + +export function BingoBoard({ board, winningSquareIds, onSquareClick }: BingoBoardProps) { + return ( +
+ {board.map((square) => ( + onSquareClick(square.id)} + /> + ))} +
+ ); +} diff --git a/src/components/BingoModal.tsx b/src/components/BingoModal.tsx new file mode 100644 index 0000000..c6dd9a8 --- /dev/null +++ b/src/components/BingoModal.tsx @@ -0,0 +1,22 @@ +interface BingoModalProps { + onDismiss: () => void; +} + +export function BingoModal({ onDismiss }: BingoModalProps) { + return ( +
+
+
🎉
+

BINGO!

+

You completed a line!

+ + +
+
+ ); +} diff --git a/src/components/BingoSquare.tsx b/src/components/BingoSquare.tsx new file mode 100644 index 0000000..1102071 --- /dev/null +++ b/src/components/BingoSquare.tsx @@ -0,0 +1,35 @@ +import type { BingoSquareData } from '../types'; + +interface BingoSquareProps { + square: BingoSquareData; + isWinning: boolean; + onClick: () => void; +} + +export function BingoSquare({ square, isWinning, onClick }: BingoSquareProps) { + const baseClasses = + 'relative flex items-center justify-center p-1 text-center border border-gray-300 rounded transition-all duration-150 select-none min-h-[60px] text-xs leading-tight'; + + const stateClasses = square.isMarked + ? isWinning + ? 'bg-amber-200 border-amber-400 text-amber-900' + : 'bg-marked border-marked-border text-green-800' + : 'bg-white text-gray-700 active:bg-gray-100'; + + const freeSpaceClasses = square.isFreeSpace ? 'font-bold text-sm' : ''; + + return ( + + ); +} diff --git a/src/components/GameScreen.tsx b/src/components/GameScreen.tsx new file mode 100644 index 0000000..7e39d4c --- /dev/null +++ b/src/components/GameScreen.tsx @@ -0,0 +1,55 @@ +import type { BingoSquareData } from '../types'; +import { BingoBoard } from './BingoBoard'; + +interface GameScreenProps { + board: BingoSquareData[]; + winningSquareIds: Set; + hasBingo: boolean; + onSquareClick: (squareId: number) => void; + onReset: () => void; +} + +export function GameScreen({ + board, + winningSquareIds, + hasBingo, + onSquareClick, + onReset, +}: GameScreenProps) { + return ( +
+ {/* Header */} +
+ +

Bingo Mixer

+
+
+ + {/* Instructions */} +

+ Tap a square when you find someone who matches it. +

+ + {/* Bingo indicator */} + {hasBingo && ( +
+ 🎉 BINGO! You got a line! +
+ )} + + {/* Board */} +
+ +
+
+ ); +} diff --git a/src/components/StartScreen.tsx b/src/components/StartScreen.tsx new file mode 100644 index 0000000..ef45ff6 --- /dev/null +++ b/src/components/StartScreen.tsx @@ -0,0 +1,30 @@ +interface StartScreenProps { + onStart: () => void; +} + +export function StartScreen({ onStart }: StartScreenProps) { + return ( +
+
+

Bingo Mixer

+

Find your people!

+ +
+

How to play

+
    +
  • • Find people who match the questions
  • +
  • • Tap a square when you find a match
  • +
  • • Get 5 in a row to win!
  • +
+
+ + +
+
+ ); +} diff --git a/src/data/questions.ts b/src/data/questions.ts new file mode 100644 index 0000000..9380d6c --- /dev/null +++ b/src/data/questions.ts @@ -0,0 +1,28 @@ +export const questions: string[] = [ + "bikes to work", + "has lived in another country", + "has a pet", + "prefers tea over coffee", + "plays an instrument", + "speaks more than 2 languages", + "has run a marathon", + "was born in a different state", + "has met a celebrity", + "can juggle", + "has been skydiving", + "loves cooking", + "has a garden", + "has traveled to Asia", + "is left-handed", + "has a twin", + "plays video games", + "does yoga", + "has a hidden talent", + "loves spicy food", + "has been on TV", + "collects something unique", + "has read a book this month", + "knows sign language", +]; + +export const FREE_SPACE = "FREE SPACE"; diff --git a/src/hooks/useBingoGame.ts b/src/hooks/useBingoGame.ts new file mode 100644 index 0000000..9fa0652 --- /dev/null +++ b/src/hooks/useBingoGame.ts @@ -0,0 +1,211 @@ +import { useState, useCallback, useMemo, useEffect } from 'react'; +import type { BingoSquareData, BingoLine, GameState } from '../types'; +import { + generateBoard, + toggleSquare, + checkBingo, + getWinningSquareIds, +} from '../utils/bingoLogic'; + +export interface BingoGameState { + gameState: GameState; + board: BingoSquareData[]; + winningLine: BingoLine | null; + winningSquareIds: Set; + showBingoModal: boolean; +} + +export interface BingoGameActions { + startGame: () => void; + handleSquareClick: (squareId: number) => void; + resetGame: () => void; + dismissModal: () => void; +} + +const STORAGE_KEY = 'bingo-game-state'; +const STORAGE_VERSION = 1; + +interface StoredGameData { + version: number; + gameState: GameState; + board: BingoSquareData[]; + winningLine: BingoLine | null; +} + +function validateStoredData(data: unknown): data is StoredGameData { + if (!data || typeof data !== 'object') { + return false; + } + + const obj = data as Record; + + if (obj.version !== STORAGE_VERSION) { + return false; + } + + if (typeof obj.gameState !== 'string' || !['start', 'playing', 'bingo'].includes(obj.gameState)) { + return false; + } + + if (!Array.isArray(obj.board) || (obj.board.length !== 0 && obj.board.length !== 25)) { + return false; + } + + const validSquares = obj.board.every((sq: unknown) => { + if (!sq || typeof sq !== 'object') return false; + const square = sq as Record; + return ( + typeof square.id === 'number' && + typeof square.text === 'string' && + typeof square.isMarked === 'boolean' && + typeof square.isFreeSpace === 'boolean' + ); + }); + + if (!validSquares) { + return false; + } + + if (obj.winningLine !== null) { + if (typeof obj.winningLine !== 'object') { + return false; + } + const line = obj.winningLine as Record; + if ( + typeof line.type !== 'string' || + !['row', 'column', 'diagonal'].includes(line.type) || + typeof line.index !== 'number' || + !Array.isArray(line.squares) + ) { + return false; + } + } + + return true; +} + +function loadGameState(): Pick | null { + // SSR guard + if (typeof window === 'undefined') { + return null; + } + + try { + const saved = localStorage.getItem(STORAGE_KEY); + if (!saved) { + return null; + } + + const parsed = JSON.parse(saved); + + if (validateStoredData(parsed)) { + return { + gameState: parsed.gameState, + board: parsed.board, + winningLine: parsed.winningLine, + }; + } else { + console.warn('Invalid game state data in localStorage, clearing...'); + localStorage.removeItem(STORAGE_KEY); + } + } catch (error) { + console.warn('Failed to load game state:', error); + if (typeof window !== 'undefined') { + localStorage.removeItem(STORAGE_KEY); + } + } + + return null; +} + +function saveGameState(gameState: GameState, board: BingoSquareData[], winningLine: BingoLine | null): void { + // SSR guard + if (typeof window === 'undefined') { + return; + } + + try { + const data: StoredGameData = { + version: STORAGE_VERSION, + gameState, + board, + winningLine, + }; + localStorage.setItem(STORAGE_KEY, JSON.stringify(data)); + } catch (error) { + console.warn('Failed to save game state:', error); + } +} + +export function useBingoGame(): BingoGameState & BingoGameActions { + const loadedState = useMemo(() => loadGameState(), []); + + const [gameState, setGameState] = useState( + () => loadedState?.gameState || 'start' + ); + const [board, setBoard] = useState( + () => loadedState?.board || [] + ); + const [winningLine, setWinningLine] = useState( + () => loadedState?.winningLine || null + ); + const [showBingoModal, setShowBingoModal] = useState(false); + + const winningSquareIds = useMemo( + () => getWinningSquareIds(winningLine), + [winningLine] + ); + + // Save game state to localStorage whenever it changes + useEffect(() => { + saveGameState(gameState, board, winningLine); + }, [gameState, board, winningLine]); + + const startGame = useCallback(() => { + setBoard(generateBoard()); + setWinningLine(null); + setGameState('playing'); + }, []); + + const handleSquareClick = useCallback((squareId: number) => { + setBoard((currentBoard) => { + const newBoard = toggleSquare(currentBoard, squareId); + + // Check for bingo after toggling + const bingo = checkBingo(newBoard); + if (bingo && !winningLine) { + // Schedule state updates to avoid synchronous setState in effect + queueMicrotask(() => { + setWinningLine(bingo); + setGameState('bingo'); + setShowBingoModal(true); + }); + } + + return newBoard; + }); + }, [winningLine]); + + const resetGame = useCallback(() => { + setGameState('start'); + setBoard([]); + setWinningLine(null); + setShowBingoModal(false); + }, []); + + const dismissModal = useCallback(() => { + setShowBingoModal(false); + }, []); + + return { + gameState, + board, + winningLine, + winningSquareIds, + showBingoModal, + startGame, + handleSquareClick, + resetGame, + dismissModal, + }; +} diff --git a/src/index.css b/src/index.css new file mode 100644 index 0000000..d110eaa --- /dev/null +++ b/src/index.css @@ -0,0 +1,21 @@ +@import 'tailwindcss'; + +@theme { + --color-accent: #2563eb; + --color-accent-light: #3b82f6; + --color-marked: #dcfce7; + --color-marked-border: #22c55e; + --color-bingo: #fbbf24; +} + +html, +body, +#root { + height: 100%; +} + +body { + margin: 0; + font-family: system-ui, -apple-system, sans-serif; + -webkit-tap-highlight-color: transparent; +} diff --git a/src/main.tsx b/src/main.tsx new file mode 100644 index 0000000..bef5202 --- /dev/null +++ b/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/src/test/setup.ts b/src/test/setup.ts new file mode 100644 index 0000000..d6bcad1 --- /dev/null +++ b/src/test/setup.ts @@ -0,0 +1,9 @@ +import * as matchers from '@testing-library/jest-dom/matchers'; +import { expect, afterEach } from 'vitest'; +import { cleanup } from '@testing-library/react'; + +expect.extend(matchers); + +afterEach(() => { + cleanup(); +}); diff --git a/src/types/index.ts b/src/types/index.ts new file mode 100644 index 0000000..3f8bfb8 --- /dev/null +++ b/src/types/index.ts @@ -0,0 +1,16 @@ +/** Domain types for the Bingo game */ + +export interface BingoSquareData { + id: number; + text: string; + isMarked: boolean; + isFreeSpace: boolean; +} + +export interface BingoLine { + type: 'row' | 'column' | 'diagonal' | 'corners'; + index: number; + squares: number[]; +} + +export type GameState = 'start' | 'playing' | 'bingo'; diff --git a/src/utils/bingoLogic.test.ts b/src/utils/bingoLogic.test.ts new file mode 100644 index 0000000..8b2b8fc --- /dev/null +++ b/src/utils/bingoLogic.test.ts @@ -0,0 +1,401 @@ +import { describe, it, expect, beforeEach, vi } from 'vitest'; +import { + generateBoard, + toggleSquare, + checkBingo, + getWinningSquareIds, + type BingoSquareData, +} from './bingoLogic'; +import { questions, FREE_SPACE } from '../data/questions'; + +describe('bingoLogic', () => { + describe('generateBoard', () => { + it('should generate a board with 25 squares', () => { + const board = generateBoard(); + expect(board).toHaveLength(25); + }); + + it('should have a free space in the center (index 12)', () => { + const board = generateBoard(); + expect(board[12].isFreeSpace).toBe(true); + expect(board[12].isMarked).toBe(true); + }); + + it('should have unique IDs from 0 to 24', () => { + const board = generateBoard(); + const ids = board.map((square) => square.id); + expect(ids).toEqual(Array.from({ length: 25 }, (_, i) => i)); + }); + + it('should have 24 non-free spaces', () => { + const board = generateBoard(); + const nonFreeSpaces = board.filter((square) => !square.isFreeSpace); + expect(nonFreeSpaces).toHaveLength(24); + }); + + it('should have all non-free spaces unmarked initially', () => { + const board = generateBoard(); + const nonFreeSpaces = board.filter((square) => !square.isFreeSpace); + nonFreeSpaces.forEach((square) => { + expect(square.isMarked).toBe(false); + }); + }); + + it('should have the FREE_SPACE text in the center', () => { + const board = generateBoard(); + expect(board[12].text).toBe(FREE_SPACE); + }); + + it('should have unique question texts across non-free squares', () => { + const board = generateBoard(); + const texts = board.filter((s) => !s.isFreeSpace).map((s) => s.text); + expect(new Set(texts).size).toBe(texts.length); + }); + + it('should only have text from the questions pool', () => { + const board = generateBoard(); + const texts = board.filter((s) => !s.isFreeSpace).map((s) => s.text); + texts.forEach((text) => { + expect(questions).toContain(text); + }); + }); + + it('should randomize question order between boards', () => { + // Mock Math.random to make it deterministic for first call + const originalRandom = Math.random; + let callCount = 0; + vi.spyOn(Math, 'random').mockImplementation(() => { + callCount++; + return callCount / 100; + }); + + const board1 = generateBoard(); + + // Reset counter for second board + callCount = 0; + const board2 = generateBoard(); + + Math.random = originalRandom; + + // Boards should have different order (very unlikely to be the same with randomization) + const texts1 = board1.filter((s) => !s.isFreeSpace).map((s) => s.text); + const texts2 = board2.filter((s) => !s.isFreeSpace).map((s) => s.text); + + // At least verify structure is correct + expect(texts1).toHaveLength(24); + expect(texts2).toHaveLength(24); + }); + }); + + describe('toggleSquare', () => { + let mockBoard: BingoSquareData[]; + + beforeEach(() => { + mockBoard = [ + { id: 0, text: 'Q1', isMarked: false, isFreeSpace: false }, + { id: 1, text: 'Q2', isMarked: true, isFreeSpace: false }, + { id: 2, text: 'Free', isMarked: true, isFreeSpace: true }, + ]; + }); + + it('should toggle unmarked square to marked', () => { + const newBoard = toggleSquare(mockBoard, 0); + expect(newBoard[0].isMarked).toBe(true); + }); + + it('should toggle marked square to unmarked', () => { + const newBoard = toggleSquare(mockBoard, 1); + expect(newBoard[1].isMarked).toBe(false); + }); + + it('should not modify free space', () => { + const newBoard = toggleSquare(mockBoard, 2); + expect(newBoard[2].isMarked).toBe(true); + }); + + it('should return a new array', () => { + const newBoard = toggleSquare(mockBoard, 0); + expect(newBoard).not.toBe(mockBoard); + }); + + it('should not modify other squares', () => { + const newBoard = toggleSquare(mockBoard, 0); + expect(newBoard[1].isMarked).toBe(mockBoard[1].isMarked); + expect(newBoard[2].isMarked).toBe(mockBoard[2].isMarked); + }); + + it('should create a new object for the toggled square (immutability)', () => { + const newBoard = toggleSquare(mockBoard, 0); + expect(newBoard[0]).not.toBe(mockBoard[0]); + }); + + it('should not create new objects for untouched squares', () => { + const newBoard = toggleSquare(mockBoard, 0); + expect(newBoard[1]).toBe(mockBoard[1]); + expect(newBoard[2]).toBe(mockBoard[2]); + }); + + it('should not change anything for a non-existent square ID', () => { + const newBoard = toggleSquare(mockBoard, 999); + expect(newBoard.map((s) => s.isMarked)).toEqual(mockBoard.map((s) => s.isMarked)); + }); + + it('should support toggling the same square back and forth', () => { + const toggled = toggleSquare(mockBoard, 0); + expect(toggled[0].isMarked).toBe(true); + const toggledBack = toggleSquare(toggled, 0); + expect(toggledBack[0].isMarked).toBe(false); + }); + }); + + describe('checkBingo', () => { + it('should return null for board without enough squares', () => { + const board = generateBoard(); + // Clear some squares to test edge case + expect(checkBingo(board)).toBeNull(); + }); + + it('should return null when no lines are complete', () => { + const board = generateBoard(); + expect(checkBingo(board)).toBeNull(); + }); + + it('should detect a complete row', () => { + const board = generateBoard(); + // Mark first row (indices 0-4) + for (let i = 0; i < 5; i++) { + board[i].isMarked = true; + } + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('row'); + expect(result?.index).toBe(0); + }); + + it('should detect a complete column', () => { + const board = generateBoard(); + // Mark first column (indices 0, 5, 10, 15, 20) + for (let i = 0; i < 5; i++) { + board[i * 5].isMarked = true; + } + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('column'); + expect(result?.index).toBe(0); + }); + + it('should detect a complete diagonal (top-left to bottom-right)', () => { + const board = generateBoard(); + // Mark diagonal (indices 0, 6, 12, 18, 24) + // Note: 12 is already marked as free space + [0, 6, 12, 18, 24].forEach((i) => { + board[i].isMarked = true; + }); + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('diagonal'); + }); + + it('should detect a complete diagonal (top-right to bottom-left)', () => { + const board = generateBoard(); + // Mark diagonal (indices 4, 8, 12, 16, 20) + [4, 8, 12, 16, 20].forEach((i) => { + board[i].isMarked = true; + }); + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('diagonal'); + }); + + it('should work with free space in center', () => { + const board = generateBoard(); + [10, 11, 12, 13, 14].forEach((i) => { + board[i].isMarked = true; + }); + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('row'); + expect(result?.index).toBe(2); + }); + + it('should detect all 5 rows', () => { + for (let row = 0; row < 5; row++) { + const board = generateBoard(); + for (let col = 0; col < 5; col++) { + board[row * 5 + col].isMarked = true; + } + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('row'); + expect(result?.index).toBe(row); + } + }); + + it('should detect all 5 columns', () => { + for (let col = 0; col < 5; col++) { + const board = generateBoard(); + for (let row = 0; row < 5; row++) { + board[row * 5 + col].isMarked = true; + } + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('column'); + expect(result?.index).toBe(col); + } + }); + + it('should return null when only 4 of 5 in a row are marked', () => { + const board = generateBoard(); + [0, 1, 2, 3].forEach((i) => { + board[i].isMarked = true; + }); + expect(checkBingo(board)).toBeNull(); + }); + + it('should return null when only 4 of 5 in a column are marked', () => { + const board = generateBoard(); + [0, 5, 10, 15].forEach((i) => { + board[i].isMarked = true; + }); + expect(checkBingo(board)).toBeNull(); + }); + + it('should return null when only 4 of 5 in a diagonal are marked', () => { + const board = generateBoard(); + [0, 6, 12, 18].forEach((i) => { + board[i].isMarked = true; + }); + expect(checkBingo(board)).toBeNull(); + }); + + it('should return the first winning line when multiple exist', () => { + const board = generateBoard(); + [0, 1, 2, 3, 4, 5, 10, 15, 20].forEach((i) => { + board[i].isMarked = true; + }); + const result = checkBingo(board); + expect(result).not.toBeNull(); + expect(result?.type).toBe('row'); + expect(result?.index).toBe(0); + }); + + it('should detect bingo on a fully marked board', () => { + const board = generateBoard(); + board.forEach((square) => { + square.isMarked = true; + }); + const result = checkBingo(board); + expect(result).not.toBeNull(); + }); + + it('should return the correct squares array for a winning line', () => { + const board = generateBoard(); + [0, 1, 2, 3, 4].forEach((i) => { + board[i].isMarked = true; + }); + const result = checkBingo(board); + expect(result?.squares).toEqual([0, 1, 2, 3, 4]); + }); + + it('should not count scattered marks as a bingo', () => { + const board = generateBoard(); + [0, 6, 2, 8, 20].forEach((i) => { + board[i].isMarked = true; + }); + expect(checkBingo(board)).toBeNull(); + }); + }); + + describe('getWinningSquareIds', () => { + it('should return empty set when no winning line', () => { + const result = getWinningSquareIds(null); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(0); + }); + + it('should return square IDs from winning line', () => { + const winningLine = { + type: 'row' as const, + index: 0, + squares: [0, 1, 2, 3, 4], + }; + const result = getWinningSquareIds(winningLine); + expect(result).toBeInstanceOf(Set); + expect(result.size).toBe(5); + expect(result.has(0)).toBe(true); + expect(result.has(1)).toBe(true); + expect(result.has(2)).toBe(true); + expect(result.has(3)).toBe(true); + expect(result.has(4)).toBe(true); + }); + + it('should handle diagonal winning line', () => { + const winningLine = { + type: 'diagonal' as const, + index: 0, + squares: [0, 6, 12, 18, 24], + }; + const result = getWinningSquareIds(winningLine); + expect(result.size).toBe(5); + expect(result.has(0)).toBe(true); + expect(result.has(6)).toBe(true); + expect(result.has(12)).toBe(true); + expect(result.has(18)).toBe(true); + expect(result.has(24)).toBe(true); + }); + + it('should handle column winning line', () => { + const winningLine = { + type: 'column' as const, + index: 2, + squares: [2, 7, 12, 17, 22], + }; + const result = getWinningSquareIds(winningLine); + expect(result.size).toBe(5); + [2, 7, 12, 17, 22].forEach((id) => { + expect(result.has(id)).toBe(true); + }); + }); + }); + + describe('integration: generateBoard + toggleSquare + checkBingo', () => { + it('should detect bingo after toggling an entire row', () => { + const board = generateBoard(); + let current = board; + [0, 1, 2, 3, 4].forEach((i) => { + if (!current[i].isMarked) { + current = toggleSquare(current, i); + } + }); + expect(checkBingo(current)).not.toBeNull(); + }); + + it('should lose bingo after untoggling a square from a winning line', () => { + const board = generateBoard(); + let current = board; + [0, 1, 2, 3, 4].forEach((i) => { + if (!current[i].isMarked) { + current = toggleSquare(current, i); + } + }); + expect(checkBingo(current)).not.toBeNull(); + current = toggleSquare(current, 0); + expect(checkBingo(current)).toBeNull(); + }); + + it('should report correct winning square IDs from a detected bingo', () => { + const board = generateBoard(); + let current = board; + const lastRowIds = [20, 21, 22, 23, 24]; + lastRowIds.forEach((i) => { + if (!current[i].isMarked) { + current = toggleSquare(current, i); + } + }); + const line = checkBingo(current); + expect(line).not.toBeNull(); + const ids = getWinningSquareIds(line); + expect(ids).toEqual(new Set(lastRowIds)); + }); + }); +}); diff --git a/src/utils/bingoLogic.ts b/src/utils/bingoLogic.ts new file mode 100644 index 0000000..a5e80c2 --- /dev/null +++ b/src/utils/bingoLogic.ts @@ -0,0 +1,126 @@ +import { questions, FREE_SPACE } from '../data/questions'; +import type { BingoSquareData, BingoLine } from '../types'; + +// Re-export types for convenience +export type { BingoSquareData, BingoLine } from '../types'; + +const BOARD_SIZE = 5; +const CENTER_INDEX = 12; // 5x5 grid, center is index 12 (row 2, col 2) + +/** + * Shuffle an array using Fisher-Yates algorithm + */ +function shuffleArray(array: T[]): T[] { + const shuffled = [...array]; + for (let i = shuffled.length - 1; i > 0; i--) { + const j = Math.floor(Math.random() * (i + 1)); + [shuffled[i], shuffled[j]] = [shuffled[j], shuffled[i]]; + } + return shuffled; +} + +/** + * Generate a new 5x5 bingo board + */ +export function generateBoard(): BingoSquareData[] { + const shuffledQuestions = shuffleArray(questions).slice(0, 24); + const board: BingoSquareData[] = []; + + let questionIndex = 0; + for (let i = 0; i < 25; i++) { + if (i === CENTER_INDEX) { + board.push({ + id: i, + text: FREE_SPACE, + isMarked: true, + isFreeSpace: true, + }); + } else { + board.push({ + id: i, + text: shuffledQuestions[questionIndex], + isMarked: false, + isFreeSpace: false, + }); + questionIndex++; + } + } + + return board; +} + +/** + * Toggle a square's marked state + */ +export function toggleSquare(board: BingoSquareData[], squareId: number): BingoSquareData[] { + return board.map((square) => + square.id === squareId && !square.isFreeSpace + ? { ...square, isMarked: !square.isMarked } + : square + ); +} + +/** + * Get all possible winning lines + */ +function getWinningLines(): BingoLine[] { + const lines: BingoLine[] = []; + + // Rows + for (let row = 0; row < BOARD_SIZE; row++) { + const squares = []; + for (let col = 0; col < BOARD_SIZE; col++) { + squares.push(row * BOARD_SIZE + col); + } + lines.push({ type: 'row', index: row, squares }); + } + + // Columns + for (let col = 0; col < BOARD_SIZE; col++) { + const squares = []; + for (let row = 0; row < BOARD_SIZE; row++) { + squares.push(row * BOARD_SIZE + col); + } + lines.push({ type: 'column', index: col, squares }); + } + + // Diagonal (top-left to bottom-right) + lines.push({ + type: 'diagonal', + index: 0, + squares: [0, 6, 12, 18, 24], + }); + + // Diagonal (top-right to bottom-left) + lines.push({ + type: 'diagonal', + index: 1, + squares: [4, 8, 12, 16, 20], + }); + + return lines; +} + +/** + * Check if there's a bingo and return the winning line(s) + */ +export function checkBingo(board: BingoSquareData[]): BingoLine | null { + const lines = getWinningLines(); + + for (const line of lines) { + const isComplete = line.squares.every((idx) => board[idx].isMarked); + if (isComplete) { + return line; + } + } + + return null; +} + +/** + * Get the square IDs that are part of a winning line + */ +export function getWinningSquareIds(line: BingoLine | null): Set { + if (!line) return new Set(); + return new Set(line.squares); +} diff --git a/tsconfig.app.json b/tsconfig.app.json new file mode 100644 index 0000000..491255e --- /dev/null +++ b/tsconfig.app.json @@ -0,0 +1,28 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "ES2022", + "useDefineForClassFields": true, + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "module": "ESNext", + "types": ["vite/client", "vitest/globals", "@testing-library/jest-dom"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["src"] +} diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..1ffef60 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/tsconfig.node.json b/tsconfig.node.json new file mode 100644 index 0000000..8a67f62 --- /dev/null +++ b/tsconfig.node.json @@ -0,0 +1,26 @@ +{ + "compilerOptions": { + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "ES2023", + "lib": ["ES2023"], + "module": "ESNext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "strict": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "erasableSyntaxOnly": true, + "noFallthroughCasesInSwitch": true, + "noUncheckedSideEffectImports": true + }, + "include": ["vite.config.ts"] +} diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..c21de28 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,16 @@ +import { defineConfig } from 'vitest/config' +import react from '@vitejs/plugin-react' +import tailwindcss from '@tailwindcss/vite' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react(), tailwindcss()], + // Auto-detect base path from environment (GitHub Actions sets VITE_REPO_NAME) + base: process.env.VITE_REPO_NAME ? `/${process.env.VITE_REPO_NAME}/game/` : '/', + test: { + globals: true, + environment: 'jsdom', + setupFiles: ['./src/test/setup.ts'], + watch: false, + }, +}) diff --git a/workshop/.ignored-by-agents b/workshop/.ignored-by-agents new file mode 100644 index 0000000..d7d5fc3 --- /dev/null +++ b/workshop/.ignored-by-agents @@ -0,0 +1 @@ +Agents MUST ignore use this folder for code suggestions. \ No newline at end of file diff --git a/workshop/00-overview.md b/workshop/00-overview.md new file mode 100644 index 0000000..dd34be6 --- /dev/null +++ b/workshop/00-overview.md @@ -0,0 +1,59 @@ +# 🎮 VS Code GitHub Copilot Agent Lab +[← README](../README.md) + +- [ ] Latest VS Code **v1.113** (no update pending) + +> **Level:** Intermediate +> **Stack:** TypeScript / Vite / Tailwind CSS + +Welcome to **Bingo Mixer** — a hands-on workshop where you'll transform a social bingo app using VS Code's Agent Mode with GitHub Copilot. + +--- + +## 📋 Quick Checklist + +- [ ] Latest VS Code **v1.113** (no update pending) + - [ ] Make sure updates are not disabled +- [ ] Signed in with GitHub (Copilot Free can not run cloud agent!) +- [ ] Git & Node installed +- [ ] Chat open and Agent ready for action + +*Optional*: Use DevContainer or WSL. + +--- + +## 🎯 What You'll Learn + +| # | Skill | Description | +|---|-------|-------------| +| 1 | **Context Engineering** | Onboard AI to your codebase and workflows | +| 2 | **Agentic Primitives** | Build an AI-first engineering flow | +| 3 | **Agentic Abundance** | Scale exploration and speed up learning | +| 4 | **Design-First Development** | Let AI iterate on UI while you guide the vision | + +--- + +## 📚 Lab Parts + +| Part | Title | Description | +|------|-------|-------------| +| [**01**](01-setup.md) | Setup & Context Engineering | Clone, configure, and teach the AI about your project | +| [**02**](02-design.md) | Design-First Frontend | Redesign the UI with creative themes | +| [**03**](03-quiz-master.md) | Custom Quiz Master | Create your own quiz themes with custom agents | +| [**04**](04-multi-agent.md) | Multi-Agent Development | Build new features with TDD and design agents | + +--- + +## 💡 Tips + +1. Keep the live-updated browser open +2. Commit working code often +3. Revert unexpected changes using chat *Checkpoints* and *Undo*, then try again + +--- + +## 🔗 Resources + +- [VS Code on YouTube](https://www.youtube.com/code) +- [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot) for more customizations diff --git a/workshop/01-setup.md b/workshop/01-setup.md new file mode 100644 index 0000000..74b16dc --- /dev/null +++ b/workshop/01-setup.md @@ -0,0 +1,120 @@ +# Part 1: Setup & Context Engineering + +[← Overview](00-overview.md) + +--- + +In this section, you'll set up your development environment and teach GitHub Copilot about your codebase. + +> 🎮 **[Play the Bingo Mixer game](../game/)** — See what you'll be building! + +--- + +## 🔧 Initial Setup + +### Step 1: Create Your Repository (Required) + +1. Open [github.com/copilot-dev-days/agent-lab-typescript](https://github.com/copilot-dev-days/agent-lab-typescript) +2. Click **Use this template** → **Create a new repository** + - Name: `my-bingo-mixer` + - Visibility: **Public** +3. ✅ Your own Bingo Mixer repo is ready! + +### Step 2: Enable GitHub Pages + +1. Go to your repo's **Settings** → **Pages** +2. Under "Build and deployment", change *Deploy from a branch* to **GitHub Actions** +3. ✅ Any commit will now publish to: `https://{username}.github.io/{repo-name}` + +### Step 3: Choose How You'll Develop + +#### Option A: Clone locally in VS Code + +1. Open VS Code +2. Run command: `Git: Clone` → `Clone from GitHub` +3. Select your new repository +4. Install recommended extensions (notification or `Extensions: Show Recommended Extensions`) + +#### Option B: Create a Codespace for your repo + +1. Open your repository on GitHub +2. Click **Code** → **Codespaces** → **Create codespace on main** +3. ✅ Codespace starts with your repo and devcontainer. + +### Step 4: Run the Setup Agent + +In the Chat panel: + +``` +/setup +``` + +The agent will figure out any required installation steps. + +On the first terminal tool, enable *Auto*-*Approve*. + +Let the agent figure out any required installation steps. + +✅ **Success:** App is running and open in browser! + +Keep this `workshop/` guide open (📌 Pin). + +--- + +## 📚 Understanding Context Engineering + +Context engineering is how you teach AI about your specific codebase. This makes Copilot's suggestions more accurate and relevant. + +### Task 1: Auto-generated Instructions + +Instructions guide all agentic codebase interactions, making them more efficient and reliable. Add them early, but make sure to keep them maintained and succinct. + +**Steps:** + +1. Run prompt: `/init` with Autopilot enabled + - While agent analyzes the codebase, optionally start next task +2. Open a new chat for the next task, as init can take a while and we can review later. +3. Review results — is it not too long and overly detailed + 1. Optional follow-up: *"Compress down and add a mandatory development [ ] checklist (lint, build, test) to the top"* +4. Apply and commit + +✅ **Result:** All future requests will have a basic map of the workspace. + +--- + +### Task 2: Dogfooding & Custom Skills + +**Steps:** + +1. Prompt: *"Test the app like a critical dogfooder, giving critical feedback on how fun it is"* +2. Watch how the agent launches the built-in browser, interacts with the app, and writes a report +3. `/create-skill` *for dogfooding* — or: *"Save this as a dogfooding skill"* + +✅ **Result:** The agent used the browser to actually play the app and produced a critical report. You then saved that testing approach as a reusable dogfooding skill. + +> 💡 **Context engineering insight:** The agent needs hands-on context — running the app, seeing the UI, clicking through flows — to truly understand how it works and validate functionality. Code alone isn't enough; interacting with the running product closes the gap between reading code and understanding the user experience. + +--- + +### Task 3: Customization Modal + +Tour the repo customizations to understand how context engineering works in practice. + +**Steps:** + +1. Open the Customization panel, Command: `Chat: Open Customizations (Preview)` +2. Browse the repo customizations — instructions, skills, and agents +3. Look at the **frontend design skill** and **Tailwind 4 instructions** + +> 💡 **Think about:** What conventions or patterns from your own projects could become instructions or skills? + +--- + +## ✅ Part 1 Complete! + +You've learned how to: +- Set up your development environment +- Generate and refine workspace instructions +- Dogfood your app with the agent's built-in browser +- Create and save custom skills +- Tour repo customizations (instructions, skills, agents) diff --git a/workshop/02-design.md b/workshop/02-design.md new file mode 100644 index 0000000..74e3a8a --- /dev/null +++ b/workshop/02-design.md @@ -0,0 +1,90 @@ +# Part 2: Design-First Frontend + +[← Part 1](01-setup.md) + +--- + +Now that we've engineered the repo context, let's get creative. + +--- + +### Task 1: Make It Yours + +Plan mode to start off any bigger work items — iterate on the plan (2+ times!) with tweaks and clarifications. + +**Steps:** + +1. Switch to Plan mode +2. *Lets do a full redesign. Make it …* +3. Review plan and start Implement + +**Theme Ideas:** + +- Minimalist Mono +- Grotesque Type Grid +- Retro Terminal Green +- Vaporwave Sunset +- Cyberpunk Neon +- Brutalist Blocks +- Soft Pastel Clouds +- Skeuomorphic Stickers +- Dark Mode Noir +- Playful Candy Pop +- Pixel Arcade Style +- Scandinavian Calm +- Corporate Clean Blue +- Gradient Glass UI +- Notebook Doodle Sketch +- Space Galaxy Glow +- Paper Card Cutouts +- Geometric Memphis +- Cozy Coffee Shop +- Metallic Chrome UI +- Bold Constructivist +- Eco Leafy Green +- Anime Bubble Aesthetic +- Monochrome Newspaper +- Chalkboard Classroom +- Yacht Club Nautical +- Desert Sand Minimal +- Bold Serif Vintage +- Toybox Primary Colors + +✅ **Result:** Frontend and Tailwind instructions are used to build a beautiful design. + +--- + +### Task 2: Keep Instructions Updated + +Keep instructions updated with major architecture/design/dependency changes. + +**Steps:** + +1. Follow-up: `/create-instructions design guide` +2. Confirm, commit and push + +Bonus: Check that GitHub Pages is updating. + +--- + +### Task 3: Dogfood the Redesign + +Re-run the dogfooding skill from Part 1 to validate your new design. + +**Steps:** + +1. Prompt: *"Dogfood the app — focus on the new design"* +2. Compare feedback to the earlier dogfooding report +3. Optional: Address any issues the agent flags + +✅ **Result:** The agent plays through the redesigned app and gives fresh critical feedback. + +--- + +## ✅ Part 2 Complete! + +You've learned how to: +- Use Plan Mode for complex design tasks +- Iterate on plans before implementing +- Keep instructions updated with changes +- Re-run dogfooding to validate design changes diff --git a/workshop/03-quiz-master.md b/workshop/03-quiz-master.md new file mode 100644 index 0000000..ddcbbd6 --- /dev/null +++ b/workshop/03-quiz-master.md @@ -0,0 +1,49 @@ +# Part 3: Custom Quiz Master + +[← Part 2](02-design.md) + +--- + +Define your own specialized workflows with custom agents, beyond generic coding prompts. + +--- + +### Task: Your Own Quiz Master + +**Steps:** + +1. New Chat, pick *Quiz Master* as custom agent +2. `Update questions to …` or just `Update quiz` +3. Check out the prompt in `.github/agents/quiz-master.agent.md` +4. Optional: Follow-up to nudge for more creativity, chaos, engagement + +**Theme Ideas:** + +- Skill Bingo: Workplace or technical skills +- Personality Bingo: Preferences, quirks, fun traits +- Secret Challenge Bingo: Quick micro-tasks with people you meet +- Team Bingo: Department or team categories +- Classic Facts Bingo: Birthday month, pets, etc. +- Work Culture Bingo: Productivity habits, meeting styles, tools +- Tech Life Bingo: Coding languages, shortcuts, frameworks, dev memes +- Travel Bingo: Cities lived in, countries visited, cultural traditions +- Creative Bingo: Music, art, design tastes, creative skills +- Micro-Challenge Bingo: Quick actions (teach a word, show a meme) +- Deep Chat Bingo: Reflective or meaningful conversation starters +- Office Humor Bingo: Desk quirks, caffeine habits, meeting hot takes +- Opposites Bingo: Find someone who is your opposite on specific axes +- Fandom Bingo: Sci-fi, gaming, books, shows, niche interests +- Lifestyle Bingo: Sleep, fitness, food, routines +- Mystery Bingo: Guess who matches a trait and verify +- Chaos Bingo: Surprising, absurd, unpredictable prompts + +✅ **Result:** Custom agent runs through the updated quiz, generating new creative and engaging questions. + +--- + +## ✅ Part 3 Complete! + +You've learned how to: +- Use custom agents for specialized workflows +- Generate themed quiz questions +- Iterate on agent output for better results diff --git a/workshop/04-multi-agent.md b/workshop/04-multi-agent.md new file mode 100644 index 0000000..9634569 --- /dev/null +++ b/workshop/04-multi-agent.md @@ -0,0 +1,116 @@ +# Part 4: Multi-Agent Development + +[← Part 3](03-quiz-master.md) + +--- + +## Task 1: Agent Hooks — Test Gate + +[Agent hooks](https://code.visualstudio.com/docs/copilot/customization/hooks) execute shell commands at key lifecycle points during agent sessions. We'll add a workspace-scoped **Stop hook** that gates *every* agent — no agent can finish until all tests pass. + +**Steps:** + +1. Open the `.github/hooks/` folder +2. Prompt: *Add an agent hook that runs the tests and blocks the agent from finishing if any test fails* +3. The hook should be a JSON file in `.github/hooks/` (e.g. `stop-test-gate.json`), not inside any single agent file. + +✅ **Result:** Every agent now has a safety net — it will keep working until all tests pass before handing back control. + +--- + +## Task 2: New Bingo Pattern (TDD-Driven) + +Use the TDD agent to add a "Four Corners" bingo pattern. The workspace stop hook you set up will enforce test discipline — every agent must leave tests passing before it hands back control. + +**Steps:** + +1. New chat with agent: `TDD` +2. *Add a "Four Corners" bingo win pattern — all four corner squares (top-left, top-right, bottom-left, bottom-right) must be marked* +3. Watch TDD orchestrate: + - **TDD Red** writes failing tests for Four Corners detection + - Review the new tests in VS Code's test runner + - **TDD Green** implements the minimal code to pass — stop hook fires, keeps it going if tests fail + - **TDD Refactor** cleans up the implementation + - Click on any sub-agent while it runs to see its context and instructions +4. Review the summary of changes + +✅ **Result:** Orchestrated TDD cycle with automatic test gating — no manual handoffs between agents. + +--- + +## Task 3: Verify with Agent Debug Logs + +Inspect what happened under the hood — did the hook fire? How did agents communicate? + +**Steps:** + +1. Verify the hook loads: open the **GitHub Copilot Chat Hooks** output channel (Output panel → channel dropdown) +2. Open Agent Debug Logs: gear icon (⚙️) in Chat view → **Show Agent Debug Logs** +3. **Logs view:** filter for hook execution events during the TDD cycle +4. **Agent Flow Chart:** visualize the TDD → Red → Green → Refactor orchestration +5. **Summary view:** review total tool calls and token usage + +**Bonus:** Click the ✨ sparkle icon to attach debug events to a new chat, then ask: `/troubleshoot did the Stop hook fire during the TDD cycle?` + +✅ **Result:** Full observability into multi-agent orchestration and hook execution. + +--- + +## Task 4: Card Deck Shuffle (Design-Driven) + +Break down agent workflows into specific focus areas, like design-first. + +**Steps:** + +1. New chat with agent: `Pixel Jam` +2. *New mode: Card Deck Shuffle. Every player opens the game → taps → gets a random card with a question.* +3. Agent iterates on the UI +4. Follow up to make it work like you want: + - *Add left/right (fail, success)* + - *Draw a card right when I open it* +5. Commit + +--- + +## Task 5: UX Review Agent + +Combine MCP, custom workflows, and subagent isolation in an agent for powerful workflows. Focus on different aspects, like usability, a11y, compliance. + +**Steps:** + +1. New chat with agent: `Pixel Jam`: *Run review* +2. Keep the app open in VS Code browser preview while the review runs +3. Follow along as it reviews + - Aside: Open `.github/agents/pixel-jam.agent.md` to review the prompt +4. Behold a mighty in-depth review + +**Bonus:** +- File findings as issues on GitHub for later +- Assign critical issues to coding agent to fix + +--- + +## Bonus: Keep Going + +- Fix UX review problems, delegated to background or cloud agent +- Add ability to have multiple question themes to pick from +- Add social sharing to win state +- Make a real iOS or full-stack app? + +--- + +## ✅ Part 4 Complete! + +You've learned how to: +- Add workspace-scoped agent hooks to enforce quality gates (Stop hook on all agents) +- Use TDD to orchestrate Red → Green → Refactor automatically +- Inspect agent behavior with Agent Debug Logs and Flow Charts +- Use design-first agents for UI-driven development +- Run UX review agents for comprehensive testing +- Combine multiple agent types for complex workflows + +### Keep Going + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) diff --git a/workshop/05-complete.md b/workshop/05-complete.md new file mode 100644 index 0000000..6216361 --- /dev/null +++ b/workshop/05-complete.md @@ -0,0 +1,56 @@ +# 🎉 Workshop Complete! + +[← Part 4](04-multi-agent.md) + +--- + +## Congratulations! 🎊 + +You've completed the **VS Code GitHub Copilot Agent Lab**! + +--- + +## 🏆 What You Built + +- ✅ A fully redesigned Social Bingo app +- ✅ Custom quiz themes with Quiz Master agent +- ✅ Agent hooks for automated test gating +- ✅ Four Corners bingo pattern (TDD-built) +- ✅ Card Deck Shuffle mode (design-driven) +- ✅ Comprehensive UX review + +--- + +## 🧠 What You Learned + +| Skill | Description | +|-------|-------------| +| **Context Engineering** | Onboarding AI to your codebase with instructions | +| **Agentic Primitives** | Background agents, cloud agents, and custom workflows | +| **Design-First Development** | UI iteration with AI assistance | +| **Test-Driven Development** | Reliable code with TDD agents | + +--- + +## 🚀 Keep Going! + +### Resources + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) + +### Next Steps + +1. **Share your creation** — Show off your themed bingo app! +2. **Try more themes** — Use Pixel Jam for more design variations +3. **Add features** — Leaderboards, sound effects, multiplayer? +4. **Teach others** — Run this workshop for your team + +--- + +## 💜 Thank You! + +Thanks for joining us on this journey into AI-assisted development. + +We can't wait to see what you build next! diff --git a/workshop/GUIDE.md b/workshop/GUIDE.md new file mode 100644 index 0000000..11b8ad8 --- /dev/null +++ b/workshop/GUIDE.md @@ -0,0 +1,82 @@ +# Bingo Mixer — VS Code GitHub Copilot Agent Lab +[← README](../README.md) + +--- +- [ ] VS Code **v1.113+** +> **Quick Reference Guide** — For detailed instructions, see the individual parts below. + +--- + +## 📚 Lab Parts + +| Part | Title | +|------|-------| +| [**00**](00-overview.md) | Overview & Checklist | +| [**01**](01-setup.md) | Setup & Context Engineering | +| [**02**](02-design.md) | Design-First Frontend | +| [**03**](03-quiz-master.md) | Custom Quiz Master | +| [**04**](04-multi-agent.md) | Multi-Agent Development | + +--- + +## ⚡ Quick Checklist + +- [ ] VS Code **v1.113+** +- [ ] GitHub Copilot (Pro/Business/Enterprise) +- [ ] Git & Node 22+ +- [ ] Chat panel open + +--- + +## 🚀 Quick Setup + +``` +# 1. Create repo from template: github.com/copilot-dev-days/agent-lab-typescript +# 2. Enable Pages: Settings > Pages > GitHub Actions +# 3. Choose one: clone repo in VS Code OR create a Codespace from your repo +# Then run: +# /setup +``` + +--- + +## 📋 Task Summary + +### Part 1: Context Engineering +- [ ] Run /setup agent +- [ ] Generate workspace instructions +- [ ] Dogfood the app with built-in browser +- [ ] Create a custom dogfooding skill +- [ ] Tour repo customizations + +### Part 2: Design-First Frontend +- [ ] Full UI redesign (Plan Mode) +- [ ] Update copilot-instructions.md +- [ ] Dogfood the redesign + +### Part 3: Quiz Master +- [ ] Generate custom quiz theme + +### Part 4: Multi-Agent Development +- [ ] Agent hooks — test gate +- [ ] Four Corners bingo pattern (TDD) +- [ ] Agent Debug Logs +- [ ] Card Deck Shuffle mode (Pixel Jam) +- [ ] UX Review + +--- + +## 💡 Tips + +1. Keep browser open for live updates +2. Commit working code often +3. Use checkpoints to revert mistakes +4. Iterate on plans 2+ times before implementing + +--- + +## 🔗 Resources + +- [VS Code YouTube](https://www.youtube.com/code) +- [Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot) diff --git a/workshop/es/00-overview.md b/workshop/es/00-overview.md new file mode 100644 index 0000000..8be83fc --- /dev/null +++ b/workshop/es/00-overview.md @@ -0,0 +1,60 @@ + +# 🎮 VS Code GitHub Copilot Agent Lab +[← README](../../README.es.md) + +- [ ] VS Code más reciente **v1.113** (sin actualización pendiente) + +> **Nivel:** Intermedio +> **Stack:** TypeScript / Vite / Tailwind CSS + +Bienvenido a **Bingo Mixer** — un workshop práctico donde transformarás una app de bingo social usando el Agent Mode de VS Code con GitHub Copilot. + +--- + +## 📋 Lista Rápida + +- [ ] VS Code más reciente **v1.113** (sin actualización pendiente) + - [ ] Asegúrate de que las actualizaciones no estén deshabilitadas +- [ ] Sesión iniciada con GitHub (¡Copilot Free no puede ejecutar cloud agent!) +- [ ] Git & Node instalados +- [ ] Chat abierto y Agent listo para la acción + +*Opcional*: Usa DevContainer o WSL. + +--- + +## 🎯 Lo que Aprenderás + +| # | Habilidad | Descripción | +|---|-----------|-------------| +| 1 | **Ingeniería de Contexto** | Integra la IA a tu codebase y flujos de trabajo | +| 2 | **Primitivas Agénticas** | Construye un flujo de ingeniería AI-first | +| 3 | **Abundancia Agéntica** | Escala la exploración y acelera el aprendizaje | +| 4 | **Desarrollo Design-First** | Deja que la IA itere en la UI mientras tú guías la visión | + +--- + +## 📚 Partes del Lab + +| Parte | Título | Descripción | +|-------|--------|-------------| +| [**01**](01-setup.md) | Configuración & Ingeniería de Contexto | Clona, configura y enséñale a la IA sobre tu proyecto | +| [**02**](02-design.md) | Frontend Design-First | Rediseña la UI con temas creativos | +| [**03**](03-quiz-master.md) | Quiz Master Personalizado | Crea tus propios temas de quiz con agentes personalizados | +| [**04**](04-multi-agent.md) | Desarrollo Multi-Agente | Construye nuevas funcionalidades con TDD y agentes de diseño | + +--- + +## 💡 Consejos + +1. Mantén el navegador con actualización en tiempo real abierto +2. Haz commit del código funcional con frecuencia +3. Revierte cambios inesperados usando *Checkpoints* y *Undo* del chat, luego intenta de nuevo + +--- + +## 🔗 Recursos + +- [VS Code on YouTube](https://www.youtube.com/code) +- [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot) para más personalizaciones diff --git a/workshop/es/01-setup.md b/workshop/es/01-setup.md new file mode 100644 index 0000000..db4afdc --- /dev/null +++ b/workshop/es/01-setup.md @@ -0,0 +1,121 @@ + +# Parte 1: Configuración & Ingeniería de Contexto + +[← Descripción General](00-overview.md) + +--- + +En esta sección, configurarás tu entorno de desarrollo y le enseñarás a GitHub Copilot sobre tu codebase. + +> 🎮 **[Juega Bingo Mixer](../game/)** — ¡Mira lo que vas a construir! + +--- + +## 🔧 Configuración Inicial + +### Paso 1: Crea Tu Repositorio (Obligatorio) + +1. Abre [github.com/copilot-dev-days/agent-lab-typescript](https://github.com/copilot-dev-days/agent-lab-typescript) +2. Haz clic en **Use this template** → **Create a new repository** + - Nombre: `my-bingo-mixer` + - Visibilidad: **Public** +3. ✅ ¡Tu propio repo de Bingo Mixer está listo! + +### Paso 2: Habilita GitHub Pages + +1. Ve a **Settings** → **Pages** de tu repo +2. En "Build and deployment", cambia *Deploy from a branch* a **GitHub Actions** +3. ✅ Cualquier commit ahora publicará en: `https://{username}.github.io/{repo-name}` + +### Paso 3: Elige Cómo Vas a Desarrollar + +#### Opción A: Clona localmente en VS Code + +1. Abre VS Code +2. Ejecuta el comando: `Git: Clone` → `Clone from GitHub` +3. Selecciona tu nuevo repositorio +4. Instala las extensiones recomendadas (notificación o `Extensions: Show Recommended Extensions`) + +#### Opción B: Crea un Codespace para tu repo + +1. Abre tu repositorio en GitHub +2. Haz clic en **Code** → **Codespaces** → **Create codespace on main** +3. ✅ El Codespace inicia con tu repo y devcontainer. + +### Paso 4: Ejecuta el Agente de Setup + +En el panel de Chat: + +``` +/setup +``` + +El agente identificará los pasos de instalación necesarios. + +En la primera herramienta de terminal, habilita *Auto*-*Approve*. + +Deja que el agente identifique los pasos de instalación necesarios. + +✅ **Éxito:** ¡La app está corriendo y abierta en el navegador! + +Mantén esta guía del `workshop/` abierta (📌 Fijar). + +--- + +## 📚 Entendiendo la Ingeniería de Contexto + +La ingeniería de contexto es cómo le enseñas a la IA sobre tu codebase específico. Esto hace que las sugerencias de Copilot sean más precisas y relevantes. + +### Tarea 1: Instrucciones Auto-generadas + +Las instrucciones guían todas las interacciones agénticas con el codebase, haciéndolas más eficientes y confiables. Agrégalas temprano, pero asegúrate de mantenerlas actualizadas y concisas. + +**Pasos:** + +1. Ejecuta el prompt: `/init` con Autopilot habilitado + - Mientras el agente analiza el codebase, opcionalmente comienza la siguiente tarea +2. Abre un nuevo chat para la siguiente tarea, ya que init puede tardar y podemos revisar después. +3. Revisa los resultados — ¿no es demasiado largo y detallado? + 1. Follow-up opcional: *"Comprime y agrega una checklist de desarrollo obligatoria [ ] (lint, build, test) en la parte superior"* +4. Aplica y haz commit + +✅ **Resultado:** Todas las solicitudes futuras tendrán un mapa básico del workspace. + +--- + +### Tarea 2: Dogfooding & Skills Personalizadas + +**Pasos:** + +1. Prompt: *"Prueba la app como un dogfooder crítico, dando feedback crítico sobre qué tan divertida es"* +2. Observa cómo el agente abre el navegador integrado, interactúa con la app y escribe un reporte +3. `/create-skill` *para dogfooding* — o: *"Guarda esto como una skill de dogfooding"* + +✅ **Resultado:** El agente usó el navegador para realmente jugar la app y produjo un reporte crítico. Luego guardaste ese enfoque de prueba como una skill reutilizable de dogfooding. + +> 💡 **Insight de ingeniería de contexto:** El agente necesita contexto práctico — ejecutar la app, ver la UI, hacer clic en los flujos — para realmente entender cómo funciona y validar la funcionalidad. Solo el código no es suficiente; interactuar con el producto en ejecución cierra la brecha entre leer código y entender la experiencia del usuario. + +--- + +### Tarea 3: Modal de Personalización + +Explora las personalizaciones del repo para entender cómo funciona la ingeniería de contexto en la práctica. + +**Pasos:** + +1. Abre el panel de Personalización, Comando: `Chat: Open Customizations (Preview)` +2. Navega por las personalizaciones del repo — instrucciones, skills y agentes +3. Mira la **skill de diseño frontend** y las **instrucciones de Tailwind 4** + +> 💡 **Piensa en:** ¿Qué convenciones o patrones de tus propios proyectos podrían convertirse en instrucciones o skills? + +--- + +## ✅ ¡Parte 1 Completa! + +Aprendiste cómo: +- Configurar tu entorno de desarrollo +- Generar y refinar instrucciones del workspace +- Probar tu app con el navegador integrado del agente +- Crear y guardar skills personalizadas +- Explorar personalizaciones del repo (instrucciones, skills, agentes) diff --git a/workshop/es/02-design.md b/workshop/es/02-design.md new file mode 100644 index 0000000..399aa46 --- /dev/null +++ b/workshop/es/02-design.md @@ -0,0 +1,91 @@ + +# Parte 2: Frontend Design-First + +[← Parte 1](01-setup.md) + +--- + +Ahora que hemos trabajado la ingeniería de contexto del repo, vamos a ponernos creativos. + +--- + +### Tarea 1: Hazlo Tuyo + +Plan mode para comenzar cualquier trabajo grande — itera en el plan (¡2+ veces!) con ajustes y aclaraciones. + +**Pasos:** + +1. Cambia a Plan mode +2. *Hagamos un rediseño completo. Hazlo…* +3. Revisa el plan y comienza a Implementar + +**Ideas de Temas:** + +- Minimalist Mono +- Grotesque Type Grid +- Retro Terminal Green +- Vaporwave Sunset +- Cyberpunk Neon +- Brutalist Blocks +- Soft Pastel Clouds +- Skeuomorphic Stickers +- Dark Mode Noir +- Playful Candy Pop +- Pixel Arcade Style +- Scandinavian Calm +- Corporate Clean Blue +- Gradient Glass UI +- Notebook Doodle Sketch +- Space Galaxy Glow +- Paper Card Cutouts +- Geometric Memphis +- Cozy Coffee Shop +- Metallic Chrome UI +- Bold Constructivist +- Eco Leafy Green +- Anime Bubble Aesthetic +- Monochrome Newspaper +- Chalkboard Classroom +- Yacht Club Nautical +- Desert Sand Minimal +- Bold Serif Vintage +- Toybox Primary Colors + +✅ **Resultado:** Las instrucciones de frontend y Tailwind se usan para crear un diseño hermoso. + +--- + +### Tarea 2: Mantén las Instrucciones Actualizadas + +Mantén las instrucciones actualizadas con cambios importantes de arquitectura/diseño/dependencias. + +**Pasos:** + +1. Follow-up: `/create-instructions design guide` +2. Confirma, haz commit y push + +Bonus: Verifica que GitHub Pages se esté actualizando. + +--- + +### Tarea 3: Prueba el Rediseño + +Re-ejecuta la skill de dogfooding de la Parte 1 para validar tu nuevo diseño. + +**Pasos:** + +1. Prompt: *"Haz dogfooding de la app — enfócate en el nuevo diseño"* +2. Compara el feedback con el reporte de dogfooding anterior +3. Opcional: Resuelve cualquier problema que el agente señale + +✅ **Resultado:** El agente juega la app rediseñada y da feedback crítico actualizado. + +--- + +## ✅ ¡Parte 2 Completa! + +Aprendiste cómo: +- Usar Plan Mode para tareas complejas de diseño +- Iterar en los planes antes de implementar +- Mantener instrucciones actualizadas con cambios +- Re-ejecutar dogfooding para validar cambios de diseño diff --git a/workshop/es/03-quiz-master.md b/workshop/es/03-quiz-master.md new file mode 100644 index 0000000..d7466d6 --- /dev/null +++ b/workshop/es/03-quiz-master.md @@ -0,0 +1,50 @@ + +# Parte 3: Quiz Master Personalizado + +[← Parte 2](02-design.md) + +--- + +Define tus propios flujos de trabajo especializados con agentes personalizados, yendo más allá de prompts genéricos de código. + +--- + +### Tarea: Tu Propio Quiz Master + +**Pasos:** + +1. Nuevo Chat, selecciona *Quiz Master* como agente personalizado +2. `Actualiza las preguntas para…` o simplemente `Update quiz` +3. Revisa el prompt en `.github/agents/quiz-master.agent.md` +4. Opcional: Follow-up para pedir más creatividad, caos, engagement + +**Ideas de Temas:** + +- Skill Bingo: Habilidades profesionales o técnicas +- Personality Bingo: Preferencias, manías, rasgos divertidos +- Secret Challenge Bingo: Micro-desafíos rápidos con personas que conozcas +- Team Bingo: Categorías por departamento o equipo +- Classic Facts Bingo: Mes de cumpleaños, mascotas, etc. +- Work Culture Bingo: Hábitos de productividad, estilos de reunión, herramientas +- Tech Life Bingo: Lenguajes de programación, atajos, frameworks, memes de dev +- Travel Bingo: Ciudades donde viviste, países visitados, tradiciones culturales +- Creative Bingo: Gustos musicales, arte, diseño, habilidades creativas +- Micro-Challenge Bingo: Acciones rápidas (enseñar una palabra, mostrar un meme) +- Deep Chat Bingo: Conversaciones reflexivas o significativas para iniciar +- Office Humor Bingo: Manías de escritorio, hábitos de cafeína, opiniones picantes sobre reuniones +- Opposites Bingo: Encuentra a alguien que sea tu opuesto en ejes específicos +- Fandom Bingo: Sci-fi, gaming, libros, series, intereses de nicho +- Lifestyle Bingo: Sueño, ejercicio, comida, rutinas +- Mystery Bingo: Adivina quién tiene un rasgo y verifica +- Chaos Bingo: Prompts sorprendentes, absurdos e impredecibles + +✅ **Resultado:** El agente personalizado recorre el quiz actualizado, generando nuevas preguntas creativas y atractivas. + +--- + +## ✅ ¡Parte 3 Completa! + +Aprendiste cómo: +- Usar agentes personalizados para flujos de trabajo especializados +- Generar preguntas de quiz temáticas +- Iterar en la salida del agente para mejores resultados diff --git a/workshop/es/04-multi-agent.md b/workshop/es/04-multi-agent.md new file mode 100644 index 0000000..b5d569c --- /dev/null +++ b/workshop/es/04-multi-agent.md @@ -0,0 +1,117 @@ + +# Parte 4: Desarrollo Multi-Agente + +[← Parte 3](03-quiz-master.md) + +--- + +## Tarea 1: Agent Hooks — Puerta de Pruebas + +[Agent hooks](https://code.visualstudio.com/docs/copilot/customization/hooks) ejecutan comandos shell en puntos clave del ciclo de vida durante sesiones de agente. Vamos a agregar un **Stop hook** con alcance de workspace que bloquea *todos* los agentes — ningún agente puede finalizar hasta que todas las pruebas pasen. + +**Pasos:** + +1. Abre la carpeta `.github/hooks/` +2. Prompt: *Agrega un agent hook que ejecute las pruebas y bloquee al agente de finalizar si alguna prueba falla* +3. El hook debe ser un archivo JSON en `.github/hooks/` (ej: `stop-test-gate.json`), no dentro de ningún archivo de agente individual. + +✅ **Resultado:** Cada agente ahora tiene una red de seguridad — seguirá trabajando hasta que todas las pruebas pasen antes de devolver el control. + +--- + +## Tarea 2: Nuevo Patrón de Bingo (Orientado por TDD) + +Usa el agente TDD para agregar un patrón de bingo "Four Corners". El stop hook del workspace que configuraste garantizará la disciplina de pruebas — cada agente debe dejar las pruebas pasando antes de devolver el control. + +**Pasos:** + +1. Nuevo chat con agente: `TDD` +2. *Agrega un patrón de victoria "Four Corners" en bingo — los cuatro cuadrados de las esquinas (superior-izquierda, superior-derecha, inferior-izquierda, inferior-derecha) deben estar marcados* +3. Observa cómo TDD orquesta: + - **TDD Red** escribe pruebas que fallan para la detección de Four Corners + - Revisa las nuevas pruebas en el test runner de VS Code + - **TDD Green** implementa el código mínimo para pasar — el stop hook se activa, continúa si las pruebas fallan + - **TDD Refactor** limpia la implementación + - Haz clic en cualquier sub-agente mientras se ejecuta para ver su contexto e instrucciones +4. Revisa el resumen de cambios + +✅ **Resultado:** Ciclo TDD orquestado con puerta de pruebas automática — sin handoffs manuales entre agentes. + +--- + +## Tarea 3: Verifica con Agent Debug Logs + +Inspecciona lo que pasó bajo el capó — ¿se activó el hook? ¿Cómo se comunicaron los agentes? + +**Pasos:** + +1. Verifica que el hook cargue: abre el canal de salida **GitHub Copilot Chat Hooks** (panel Output → dropdown de canal) +2. Abre Agent Debug Logs: ícono de engranaje (⚙️) en la vista de Chat → **Show Agent Debug Logs** +3. **Pestaña Logs:** filtra por eventos de ejecución del hook durante el ciclo TDD +4. **Agent Flow Chart:** visualiza la orquestación TDD → Red → Green → Refactor +5. **Pestaña Summary:** revisa el total de llamadas de herramienta y uso de tokens + +**Bonus:** Haz clic en el ícono ✨ sparkle para adjuntar eventos de debug a un nuevo chat, luego pregunta: `/troubleshoot ¿se activó el Stop hook durante el ciclo TDD?` + +✅ **Resultado:** Observabilidad completa de la orquestación multi-agente y ejecución de hooks. + +--- + +## Tarea 4: Card Deck Shuffle (Orientado por Diseño) + +Divide los flujos de trabajo de agentes en áreas de enfoque específicas, como design-first. + +**Pasos:** + +1. Nuevo chat con agente: `Pixel Jam` +2. *Nuevo modo: Card Deck Shuffle. Cada jugador abre el juego → toca → recibe una carta aleatoria con una pregunta.* +3. El agente itera en la UI +4. Da follow-up para que funcione como quieres: + - *Agrega izquierda/derecha (fallo, éxito)* + - *Saca una carta justo cuando lo abra* +5. Commit + +--- + +## Tarea 5: Agente de Revisión de UX + +Combina MCP, flujos de trabajo personalizados y aislamiento de sub-agentes en un agente para flujos de trabajo poderosos. Enfócate en diferentes aspectos, como usabilidad, a11y, cumplimiento. + +**Pasos:** + +1. Nuevo chat con agente: `Pixel Jam`: *Ejecuta la revisión* +2. Mantén la app abierta en la vista previa del navegador de VS Code mientras la revisión se ejecuta +3. Sigue el proceso mientras revisa + - Aparte: Abre `.github/agents/pixel-jam.agent.md` para revisar el prompt +4. Contempla una revisión profunda y detallada + +**Bonus:** +- Registra hallazgos como issues en GitHub para después +- Asigna issues críticas al agente de código para corregir + +--- + +## Bonus: Sigue Explorando + +- Corrige problemas de la revisión de UX, delegados a agente en background o cloud +- Agrega la posibilidad de tener múltiples temas de preguntas para elegir +- Agrega compartir en redes sociales en el estado de victoria +- ¿Haz una app real para iOS o full-stack? + +--- + +## ✅ ¡Parte 4 Completa! + +Aprendiste cómo: +- Agregar agent hooks con alcance de workspace para imponer puertas de calidad (Stop hook en todos los agentes) +- Usar TDD para orquestar Red → Green → Refactor automáticamente +- Inspeccionar comportamiento del agente con Agent Debug Logs y Flow Charts +- Usar agentes design-first para desarrollo orientado por UI +- Ejecutar agentes de revisión de UX para pruebas integrales +- Combinar múltiples tipos de agentes para flujos de trabajo complejos + +### Sigue Explorando + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) diff --git a/workshop/es/05-complete.md b/workshop/es/05-complete.md new file mode 100644 index 0000000..446ac5e --- /dev/null +++ b/workshop/es/05-complete.md @@ -0,0 +1,57 @@ + +# 🎉 ¡Workshop Completo! + +[← Parte 4](04-multi-agent.md) + +--- + +## ¡Felicidades! 🎊 + +¡Completaste el **VS Code GitHub Copilot Agent Lab**! + +--- + +## 🏆 Lo que Construiste + +- ✅ Una app de Social Bingo completamente rediseñada +- ✅ Temas de quiz personalizados con el agente Quiz Master +- ✅ Agent hooks para puerta de pruebas automatizada +- ✅ Patrón de bingo Four Corners (construido con TDD) +- ✅ Modo Card Deck Shuffle (orientado por diseño) +- ✅ Revisión integral de UX + +--- + +## 🧠 Lo que Aprendiste + +| Habilidad | Descripción | +|-----------|-------------| +| **Ingeniería de Contexto** | Integración de la IA a tu codebase con instrucciones | +| **Primitivas Agénticas** | Background agents, cloud agents y flujos de trabajo personalizados | +| **Desarrollo Design-First** | Iteración de UI con asistencia de IA | +| **Desarrollo Orientado por Pruebas** | Código confiable con agentes TDD | + +--- + +## 🚀 ¡Sigue Explorando! + +### Recursos + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) + +### Próximos Pasos + +1. **Comparte tu creación** — ¡Muestra tu app de bingo temática! +2. **Prueba más temas** — Usa Pixel Jam para más variaciones de diseño +3. **Agrega funcionalidades** — ¿Tabla de posiciones, efectos de sonido, multiplayer? +4. **Enséñale a otros** — Ejecuta este workshop para tu equipo + +--- + +## 💜 ¡Gracias! + +Gracias por acompañarnos en este viaje por el desarrollo asistido por IA. + +¡No podemos esperar a ver lo que construirás! diff --git a/workshop/es/GUIDE.md b/workshop/es/GUIDE.md new file mode 100644 index 0000000..a42264e --- /dev/null +++ b/workshop/es/GUIDE.md @@ -0,0 +1,83 @@ + +# Bingo Mixer — VS Code GitHub Copilot Agent Lab +[← README](../../README.es.md) + +--- +- [ ] VS Code **v1.113+** +> **Guía de Referencia Rápida** — Para instrucciones detalladas, consulta las partes individuales a continuación. + +--- + +## 📚 Partes del Lab + +| Parte | Título | +|-------|--------| +| [**00**](00-overview.md) | Descripción General & Lista Rápida | +| [**01**](01-setup.md) | Configuración & Ingeniería de Contexto | +| [**02**](02-design.md) | Frontend Design-First | +| [**03**](03-quiz-master.md) | Quiz Master Personalizado | +| [**04**](04-multi-agent.md) | Desarrollo Multi-Agente | + +--- + +## ⚡ Lista Rápida + +- [ ] VS Code **v1.113+** +- [ ] GitHub Copilot (Pro/Business/Enterprise) +- [ ] Git & Node 22+ +- [ ] Panel de Chat abierto + +--- + +## 🚀 Configuración Rápida + +``` +# 1. Crea un repo desde la plantilla: github.com/copilot-dev-days/agent-lab-typescript +# 2. Habilita Pages: Settings > Pages > GitHub Actions +# 3. Elige una opción: clona el repo en VS Code O crea un Codespace desde tu repo +# Luego ejecuta: +# /setup +``` + +--- + +## 📋 Resumen de Tareas + +### Parte 1: Ingeniería de Contexto +- [ ] Ejecutar el agente /setup +- [ ] Generar instrucciones del workspace +- [ ] Probar la app con el navegador integrado +- [ ] Crear una skill personalizada de dogfooding +- [ ] Explorar las personalizaciones del repo + +### Parte 2: Frontend Design-First +- [ ] Rediseño completo de la UI (Plan Mode) +- [ ] Actualizar copilot-instructions.md +- [ ] Probar el rediseño + +### Parte 3: Quiz Master +- [ ] Generar tema personalizado de quiz + +### Parte 4: Desarrollo Multi-Agente +- [ ] Agent hooks — puerta de pruebas +- [ ] Patrón de bingo Four Corners (TDD) +- [ ] Agent Debug Logs +- [ ] Modo Card Deck Shuffle (Pixel Jam) +- [ ] Revisión de UX + +--- + +## 💡 Consejos + +1. Mantén el navegador abierto para actualizaciones en tiempo real +2. Haz commit del código funcional con frecuencia +3. Usa checkpoints para revertir errores +4. Itera en los planes 2+ veces antes de implementar + +--- + +## 🔗 Recursos + +- [VS Code YouTube](https://www.youtube.com/code) +- [Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot) diff --git a/workshop/pt_BR/00-overview.md b/workshop/pt_BR/00-overview.md new file mode 100644 index 0000000..01cecc5 --- /dev/null +++ b/workshop/pt_BR/00-overview.md @@ -0,0 +1,60 @@ + +# 🎮 VS Code GitHub Copilot Agent Lab +[← README](../../README.pt_BR.md) + +- [ ] VS Code mais recente **v1.113** (sem atualização pendente) + +> **Nível:** Intermediário +> **Stack:** TypeScript / Vite / Tailwind CSS + +Bem-vindo ao **Bingo Mixer** — um workshop prático onde você vai transformar um app de bingo social usando o Agent Mode do VS Code com GitHub Copilot. + +--- + +## 📋 Lista Rápida + +- [ ] VS Code mais recente **v1.113** (sem atualização pendente) + - [ ] Verifique se as atualizações não estão desabilitadas +- [ ] Logado com GitHub (Copilot Free não pode executar cloud agent!) +- [ ] Git & Node instalados +- [ ] Chat aberto e Agent pronto para ação + +*Opcional*: Use DevContainer ou WSL. + +--- + +## 🎯 O que Você Vai Aprender + +| # | Habilidade | Descrição | +|---|------------|-----------| +| 1 | **Engenharia de Contexto** | Integre a IA ao seu codebase e fluxos de trabalho | +| 2 | **Primitivas Agênticas** | Construa um fluxo de engenharia AI-first | +| 3 | **Abundância Agêntica** | Escale exploração e acelere o aprendizado | +| 4 | **Desenvolvimento Design-First** | Deixe a IA iterar na UI enquanto você guia a visão | + +--- + +## 📚 Partes do Lab + +| Parte | Título | Descrição | +|-------|--------|-----------| +| [**01**](01-setup.md) | Configuração & Engenharia de Contexto | Clone, configure e ensine a IA sobre seu projeto | +| [**02**](02-design.md) | Frontend Design-First | Redesenhe a UI com temas criativos | +| [**03**](03-quiz-master.md) | Quiz Master Personalizado | Crie seus próprios temas de quiz com agentes personalizados | +| [**04**](04-multi-agent.md) | Desenvolvimento Multi-Agente | Construa novas funcionalidades com TDD e agentes de design | + +--- + +## 💡 Dicas + +1. Mantenha o navegador com atualização em tempo real aberto +2. Faça commit do código funcional com frequência +3. Reverta mudanças inesperadas usando *Checkpoints* e *Undo* do chat, depois tente novamente + +--- + +## 🔗 Recursos + +- [VS Code on YouTube](https://www.youtube.com/code) +- [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot) para mais customizações diff --git a/workshop/pt_BR/01-setup.md b/workshop/pt_BR/01-setup.md new file mode 100644 index 0000000..49ebba7 --- /dev/null +++ b/workshop/pt_BR/01-setup.md @@ -0,0 +1,121 @@ + +# Parte 1: Configuração & Engenharia de Contexto + +[← Visão Geral](00-overview.md) + +--- + +Nesta seção, você vai configurar seu ambiente de desenvolvimento e ensinar o GitHub Copilot sobre seu codebase. + +> 🎮 **[Jogue o Bingo Mixer](../game/)** — Veja o que você vai construir! + +--- + +## 🔧 Configuração Inicial + +### Passo 1: Crie Seu Repositório (Obrigatório) + +1. Abra [github.com/copilot-dev-days/agent-lab-typescript](https://github.com/copilot-dev-days/agent-lab-typescript) +2. Clique em **Use this template** → **Create a new repository** + - Nome: `my-bingo-mixer` + - Visibilidade: **Public** +3. ✅ Seu próprio repo do Bingo Mixer está pronto! + +### Passo 2: Habilite o GitHub Pages + +1. Vá em **Settings** → **Pages** do seu repo +2. Em "Build and deployment", mude *Deploy from a branch* para **GitHub Actions** +3. ✅ Qualquer commit agora vai publicar em: `https://{username}.github.io/{repo-name}` + +### Passo 3: Escolha Como Vai Desenvolver + +#### Opção A: Clone localmente no VS Code + +1. Abra o VS Code +2. Execute o comando: `Git: Clone` → `Clone from GitHub` +3. Selecione seu novo repositório +4. Instale as extensões recomendadas (notificação ou `Extensions: Show Recommended Extensions`) + +#### Opção B: Crie um Codespace para seu repo + +1. Abra seu repositório no GitHub +2. Clique em **Code** → **Codespaces** → **Create codespace on main** +3. ✅ O Codespace inicia com seu repo e devcontainer. + +### Passo 4: Execute o Agente de Setup + +No painel de Chat: + +``` +/setup +``` + +O agente vai identificar quaisquer passos de instalação necessários. + +Na primeira ferramenta de terminal, habilite *Auto*-*Approve*. + +Deixe o agente identificar quaisquer passos de instalação necessários. + +✅ **Sucesso:** O app está rodando e aberto no navegador! + +Mantenha este guia do `workshop/` aberto (📌 Fixar). + +--- + +## 📚 Entendendo Engenharia de Contexto + +Engenharia de contexto é como você ensina a IA sobre seu codebase específico. Isso torna as sugestões do Copilot mais precisas e relevantes. + +### Tarefa 1: Instruções Auto-geradas + +Instruções orientam todas as interações agênticas com o codebase, tornando-as mais eficientes e confiáveis. Adicione-as cedo, mas mantenha-as atualizadas e concisas. + +**Passos:** + +1. Execute o prompt: `/init` com Autopilot habilitado + - Enquanto o agente analisa o codebase, opcionalmente comece a próxima tarefa +2. Abra um novo chat para a próxima tarefa, pois o init pode demorar e podemos revisar depois. +3. Revise os resultados — não está longo e detalhado demais? + 1. Follow-up opcional: *"Comprima e adicione uma checklist de desenvolvimento obrigatória [ ] (lint, build, test) no topo"* +4. Aplique e faça commit + +✅ **Resultado:** Todas as futuras solicitações terão um mapa básico do workspace. + +--- + +### Tarefa 2: Dogfooding & Skills Personalizadas + +**Passos:** + +1. Prompt: *"Teste o app como um dogfooder crítico, dando feedback crítico sobre o quão divertido ele é"* +2. Observe como o agente abre o navegador integrado, interage com o app e escreve um relatório +3. `/create-skill` *para dogfooding* — ou: *"Salve isso como uma skill de dogfooding"* + +✅ **Resultado:** O agente usou o navegador para realmente jogar o app e produziu um relatório crítico. Você então salvou essa abordagem de teste como uma skill reutilizável de dogfooding. + +> 💡 **Insight de engenharia de contexto:** O agente precisa de contexto prático — executar o app, ver a UI, clicar pelos fluxos — para realmente entender como funciona e validar a funcionalidade. Só o código não basta; interagir com o produto em execução fecha a lacuna entre ler código e entender a experiência do usuário. + +--- + +### Tarefa 3: Modal de Customização + +Explore as customizações do repo para entender como a engenharia de contexto funciona na prática. + +**Passos:** + +1. Abra o painel de Customização, Comando: `Chat: Open Customizations (Preview)` +2. Navegue pelas customizações do repo — instruções, skills e agentes +3. Veja a **skill de design frontend** e as **instruções de Tailwind 4** + +> 💡 **Pense sobre:** Quais convenções ou padrões dos seus próprios projetos poderiam se tornar instruções ou skills? + +--- + +## ✅ Parte 1 Completa! + +Você aprendeu como: +- Configurar seu ambiente de desenvolvimento +- Gerar e refinar instruções do workspace +- Testar seu app com o navegador integrado do agente +- Criar e salvar skills personalizadas +- Explorar customizações do repo (instruções, skills, agentes) diff --git a/workshop/pt_BR/02-design.md b/workshop/pt_BR/02-design.md new file mode 100644 index 0000000..3fcde04 --- /dev/null +++ b/workshop/pt_BR/02-design.md @@ -0,0 +1,91 @@ + +# Parte 2: Frontend Design-First + +[← Parte 1](01-setup.md) + +--- + +Agora que engenheiramos o contexto do repo, vamos ser criativos. + +--- + +### Tarefa 1: Deixe a Sua Cara + +Plan mode para começar qualquer trabalho maior — itere no plano (2+ vezes!) com ajustes e esclarecimentos. + +**Passos:** + +1. Mude para Plan mode +2. *Vamos fazer um redesign completo. Faça algo…* +3. Revise o plano e comece a Implementação + +**Ideias de Temas:** + +- Minimalist Mono +- Grotesque Type Grid +- Retro Terminal Green +- Vaporwave Sunset +- Cyberpunk Neon +- Brutalist Blocks +- Soft Pastel Clouds +- Skeuomorphic Stickers +- Dark Mode Noir +- Playful Candy Pop +- Pixel Arcade Style +- Scandinavian Calm +- Corporate Clean Blue +- Gradient Glass UI +- Notebook Doodle Sketch +- Space Galaxy Glow +- Paper Card Cutouts +- Geometric Memphis +- Cozy Coffee Shop +- Metallic Chrome UI +- Bold Constructivist +- Eco Leafy Green +- Anime Bubble Aesthetic +- Monochrome Newspaper +- Chalkboard Classroom +- Yacht Club Nautical +- Desert Sand Minimal +- Bold Serif Vintage +- Toybox Primary Colors + +✅ **Resultado:** As instruções de frontend e Tailwind são usadas para criar um design bonito. + +--- + +### Tarefa 2: Mantenha as Instruções Atualizadas + +Mantenha as instruções atualizadas com mudanças importantes de arquitetura/design/dependências. + +**Passos:** + +1. Follow-up: `/create-instructions design guide` +2. Confirme, faça commit e push + +Bônus: Verifique se o GitHub Pages está atualizando. + +--- + +### Tarefa 3: Teste o Redesign + +Re-execute a skill de dogfooding da Parte 1 para validar seu novo design. + +**Passos:** + +1. Prompt: *"Faça dogfooding do app — foque no novo design"* +2. Compare o feedback com o relatório de dogfooding anterior +3. Opcional: Resolva quaisquer problemas que o agente apontar + +✅ **Resultado:** O agente joga o app redesenhado e dá feedback crítico atualizado. + +--- + +## ✅ Parte 2 Completa! + +Você aprendeu como: +- Usar Plan Mode para tarefas complexas de design +- Iterar nos planos antes de implementar +- Manter instruções atualizadas com mudanças +- Re-executar dogfooding para validar mudanças de design diff --git a/workshop/pt_BR/03-quiz-master.md b/workshop/pt_BR/03-quiz-master.md new file mode 100644 index 0000000..5d64ea6 --- /dev/null +++ b/workshop/pt_BR/03-quiz-master.md @@ -0,0 +1,50 @@ + +# Parte 3: Quiz Master Personalizado + +[← Parte 2](02-design.md) + +--- + +Defina seus próprios fluxos de trabalho especializados com agentes personalizados, indo além de prompts genéricos de código. + +--- + +### Tarefa: Seu Próprio Quiz Master + +**Passos:** + +1. Novo Chat, selecione *Quiz Master* como agente personalizado +2. `Atualize as perguntas para…` ou apenas `Update quiz` +3. Confira o prompt em `.github/agents/quiz-master.agent.md` +4. Opcional: Follow-up para pedir mais criatividade, caos, engajamento + +**Ideias de Temas:** + +- Skill Bingo: Habilidades profissionais ou técnicas +- Personality Bingo: Preferências, manias, traços divertidos +- Secret Challenge Bingo: Micro-desafios rápidos com pessoas que você conhecer +- Team Bingo: Categorias por departamento ou equipe +- Classic Facts Bingo: Mês de aniversário, pets, etc. +- Work Culture Bingo: Hábitos de produtividade, estilos de reunião, ferramentas +- Tech Life Bingo: Linguagens de programação, atalhos, frameworks, memes de dev +- Travel Bingo: Cidades onde morou, países visitados, tradições culturais +- Creative Bingo: Gostos musicais, arte, design, habilidades criativas +- Micro-Challenge Bingo: Ações rápidas (ensinar uma palavra, mostrar um meme) +- Deep Chat Bingo: Conversas reflexivas ou significativas para começar +- Office Humor Bingo: Manias de mesa, hábitos de cafeína, opiniões quentes sobre reuniões +- Opposites Bingo: Encontre alguém que seja seu oposto em eixos específicos +- Fandom Bingo: Sci-fi, games, livros, séries, interesses de nicho +- Lifestyle Bingo: Sono, exercício, comida, rotinas +- Mystery Bingo: Adivinhe quem tem um traço e verifique +- Chaos Bingo: Prompts surpreendentes, absurdos e imprevisíveis + +✅ **Resultado:** O agente personalizado percorre o quiz atualizado, gerando novas perguntas criativas e envolventes. + +--- + +## ✅ Parte 3 Completa! + +Você aprendeu como: +- Usar agentes personalizados para fluxos de trabalho especializados +- Gerar perguntas de quiz temáticas +- Iterar na saída do agente para melhores resultados diff --git a/workshop/pt_BR/04-multi-agent.md b/workshop/pt_BR/04-multi-agent.md new file mode 100644 index 0000000..b8cb5b5 --- /dev/null +++ b/workshop/pt_BR/04-multi-agent.md @@ -0,0 +1,117 @@ + +# Parte 4: Desenvolvimento Multi-Agente + +[← Parte 3](03-quiz-master.md) + +--- + +## Tarefa 1: Agent Hooks — Portão de Testes + +[Agent hooks](https://code.visualstudio.com/docs/copilot/customization/hooks) executam comandos shell em pontos-chave do ciclo de vida durante sessões de agente. Vamos adicionar um **Stop hook** com escopo de workspace que bloqueia *todos* os agentes — nenhum agente pode finalizar até que todos os testes passem. + +**Passos:** + +1. Abra a pasta `.github/hooks/` +2. Prompt: *Adicione um agent hook que execute os testes e bloqueie o agente de finalizar se algum teste falhar* +3. O hook deve ser um arquivo JSON em `.github/hooks/` (ex: `stop-test-gate.json`), não dentro de nenhum arquivo de agente individual. + +✅ **Resultado:** Todo agente agora tem uma rede de segurança — ele vai continuar trabalhando até que todos os testes passem antes de devolver o controle. + +--- + +## Tarefa 2: Novo Padrão de Bingo (Orientado por TDD) + +Use o agente TDD para adicionar um padrão de bingo "Four Corners". O stop hook do workspace que você configurou vai garantir a disciplina de testes — todo agente deve deixar os testes passando antes de devolver o controle. + +**Passos:** + +1. Novo chat com agente: `TDD` +2. *Adicione um padrão de vitória "Four Corners" no bingo — todos os quatro quadrados dos cantos (superior-esquerdo, superior-direito, inferior-esquerdo, inferior-direito) devem estar marcados* +3. Observe o TDD orquestrar: + - **TDD Red** escreve testes que falham para a detecção de Four Corners + - Revise os novos testes no test runner do VS Code + - **TDD Green** implementa o código mínimo para passar — o stop hook dispara, continua se os testes falharem + - **TDD Refactor** limpa a implementação + - Clique em qualquer sub-agente enquanto ele executa para ver seu contexto e instruções +4. Revise o resumo das mudanças + +✅ **Resultado:** Ciclo TDD orquestrado com portão de testes automático — sem handoffs manuais entre agentes. + +--- + +## Tarefa 3: Verifique com Agent Debug Logs + +Inspecione o que aconteceu por baixo dos panos — o hook disparou? Como os agentes se comunicaram? + +**Passos:** + +1. Verifique se o hook carrega: abra o canal de saída **GitHub Copilot Chat Hooks** (painel Output → dropdown de canal) +2. Abra Agent Debug Logs: ícone de engrenagem (⚙️) na view do Chat → **Show Agent Debug Logs** +3. **Aba Logs:** filtre por eventos de execução do hook durante o ciclo TDD +4. **Agent Flow Chart:** visualize a orquestração TDD → Red → Green → Refactor +5. **Aba Summary:** revise o total de chamadas de ferramenta e uso de tokens + +**Bônus:** Clique no ícone ✨ sparkle para anexar eventos de debug a um novo chat, depois pergunte: `/troubleshoot o Stop hook disparou durante o ciclo TDD?` + +✅ **Resultado:** Observabilidade completa da orquestração multi-agente e execução de hooks. + +--- + +## Tarefa 4: Card Deck Shuffle (Orientado por Design) + +Divida fluxos de trabalho de agentes em áreas de foco específicas, como design-first. + +**Passos:** + +1. Novo chat com agente: `Pixel Jam` +2. *Novo modo: Card Deck Shuffle. Todo jogador abre o jogo → toca → recebe uma carta aleatória com uma pergunta.* +3. O agente itera na UI +4. Dê follow-up para funcionar como você quer: + - *Adicione esquerda/direita (falha, sucesso)* + - *Puxe uma carta logo quando eu abrir* +5. Commit + +--- + +## Tarefa 5: Agente de Revisão de UX + +Combine MCP, fluxos de trabalho personalizados e isolamento de sub-agentes em um agente para fluxos de trabalho poderosos. Foque em diferentes aspectos, como usabilidade, a11y, conformidade. + +**Passos:** + +1. Novo chat com agente: `Pixel Jam`: *Execute a revisão* +2. Mantenha o app aberto na pré-visualização do navegador do VS Code enquanto a revisão executa +3. Acompanhe conforme ele revisa + - À parte: Abra `.github/agents/pixel-jam.agent.md` para revisar o prompt +4. Contemple uma revisão profunda e detalhada + +**Bônus:** +- Registre achados como issues no GitHub para depois +- Atribua issues críticas ao agente de código para corrigir + +--- + +## Bônus: Continue Explorando + +- Corrija problemas da revisão de UX, delegados para agente em background ou cloud +- Adicione a possibilidade de ter múltiplos temas de perguntas para escolher +- Adicione compartilhamento social no estado de vitória +- Faça um app real para iOS ou full-stack? + +--- + +## ✅ Parte 4 Completa! + +Você aprendeu como: +- Adicionar agent hooks com escopo de workspace para impor portões de qualidade (Stop hook em todos os agentes) +- Usar TDD para orquestrar Red → Green → Refactor automaticamente +- Inspecionar comportamento do agente com Agent Debug Logs e Flow Charts +- Usar agentes design-first para desenvolvimento orientado por UI +- Executar agentes de revisão de UX para testes abrangentes +- Combinar múltiplos tipos de agentes para fluxos de trabalho complexos + +### Continue Explorando + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) diff --git a/workshop/pt_BR/05-complete.md b/workshop/pt_BR/05-complete.md new file mode 100644 index 0000000..78e7acd --- /dev/null +++ b/workshop/pt_BR/05-complete.md @@ -0,0 +1,57 @@ + +# 🎉 Workshop Completo! + +[← Parte 4](04-multi-agent.md) + +--- + +## Parabéns! 🎊 + +Você completou o **VS Code GitHub Copilot Agent Lab**! + +--- + +## 🏆 O que Você Construiu + +- ✅ Um app de Social Bingo totalmente redesenhado +- ✅ Temas de quiz personalizados com o agente Quiz Master +- ✅ Agent hooks para portão de testes automatizado +- ✅ Padrão de bingo Four Corners (construído com TDD) +- ✅ Modo Card Deck Shuffle (orientado por design) +- ✅ Revisão abrangente de UX + +--- + +## 🧠 O que Você Aprendeu + +| Habilidade | Descrição | +|------------|-----------| +| **Engenharia de Contexto** | Integração da IA ao seu codebase com instruções | +| **Primitivas Agênticas** | Background agents, cloud agents e fluxos de trabalho personalizados | +| **Desenvolvimento Design-First** | Iteração de UI com assistência de IA | +| **Desenvolvimento Orientado por Testes** | Código confiável com agentes TDD | + +--- + +## 🚀 Continue Explorando! + +### Recursos + +- 📺 [VS Code on YouTube](https://www.youtube.com/code) +- 📖 [VS Code Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- 🌟 [Awesome Copilot](https://github.com/github/awesome-copilot) + +### Próximos Passos + +1. **Compartilhe sua criação** — Mostre seu app de bingo temático! +2. **Experimente mais temas** — Use Pixel Jam para mais variações de design +3. **Adicione funcionalidades** — Placar, efeitos sonoros, multiplayer? +4. **Ensine outros** — Execute este workshop para sua equipe + +--- + +## 💜 Obrigado! + +Obrigado por nos acompanhar nesta jornada pelo desenvolvimento assistido por IA. + +Mal podemos esperar para ver o que você vai construir! diff --git a/workshop/pt_BR/GUIDE.md b/workshop/pt_BR/GUIDE.md new file mode 100644 index 0000000..8b30a6b --- /dev/null +++ b/workshop/pt_BR/GUIDE.md @@ -0,0 +1,83 @@ + +# Bingo Mixer — VS Code GitHub Copilot Agent Lab +[← README](../../README.pt_BR.md) + +--- +- [ ] VS Code **v1.113+** +> **Guia de Referência Rápida** — Para instruções detalhadas, veja as partes individuais abaixo. + +--- + +## 📚 Partes do Lab + +| Parte | Título | +|-------|--------| +| [**00**](00-overview.md) | Visão Geral & Lista Rápida | +| [**01**](01-setup.md) | Configuração & Engenharia de Contexto | +| [**02**](02-design.md) | Frontend Design-First | +| [**03**](03-quiz-master.md) | Quiz Master Personalizado | +| [**04**](04-multi-agent.md) | Desenvolvimento Multi-Agente | + +--- + +## ⚡ Lista Rápida + +- [ ] VS Code **v1.113+** +- [ ] GitHub Copilot (Pro/Business/Enterprise) +- [ ] Git & Node 22+ +- [ ] Painel de Chat aberto + +--- + +## 🚀 Configuração Rápida + +``` +# 1. Crie um repo a partir do template: github.com/copilot-dev-days/agent-lab-typescript +# 2. Habilite Pages: Settings > Pages > GitHub Actions +# 3. Escolha uma opção: clone o repo no VS Code OU crie um Codespace a partir do seu repo +# Depois execute: +# /setup +``` + +--- + +## 📋 Resumo das Tarefas + +### Parte 1: Engenharia de Contexto +- [ ] Executar o agente /setup +- [ ] Gerar instruções do workspace +- [ ] Testar o app com o navegador integrado +- [ ] Criar uma skill personalizada de dogfooding +- [ ] Explorar as customizações do repo + +### Parte 2: Frontend Design-First +- [ ] Redesign completo da UI (Plan Mode) +- [ ] Atualizar copilot-instructions.md +- [ ] Testar o redesign + +### Parte 3: Quiz Master +- [ ] Gerar tema personalizado de quiz + +### Parte 4: Desenvolvimento Multi-Agente +- [ ] Agent hooks — portão de testes +- [ ] Padrão de bingo Four Corners (TDD) +- [ ] Agent Debug Logs +- [ ] Modo Card Deck Shuffle (Pixel Jam) +- [ ] Revisão de UX + +--- + +## 💡 Dicas + +1. Mantenha o navegador aberto para atualizações em tempo real +2. Faça commit do código funcional com frequência +3. Use checkpoints para reverter erros +4. Itere nos planos 2+ vezes antes de implementar + +--- + +## 🔗 Recursos + +- [VS Code YouTube](https://www.youtube.com/code) +- [Copilot Docs](https://code.visualstudio.com/docs/copilot/overview) +- [Awesome Copilot](https://github.com/github/awesome-copilot)