diff --git a/.babelrc b/.babelrc index 93a29a8..464ac5b 100644 --- a/.babelrc +++ b/.babelrc @@ -1,7 +1,4 @@ { "presets": ["next/babel"], - "plugins": [ - ["@babel/plugin-proposal-decorators", { "legacy": true }], - ["@babel/plugin-proposal-class-properties", { "loose": true }] - ] + "plugins": ["@babel/plugin-transform-class-properties", "@babel/plugin-proposal-class-properties"] } diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..464aea2 --- /dev/null +++ b/.env.example @@ -0,0 +1,189 @@ +# Environment Variables Template +# Copy this file to .env.local and fill in your values +# DO NOT commit .env.local to version control! + +# ============================================================================= +# DATABASE +# ============================================================================= +# PostgreSQL connection string +# Format: postgresql://username:password@host:port/database?schema=public +# +# Local development: +# DATABASE_URL="postgresql://postgres:password@localhost:5432/salsabeatmachine?schema=public" +# +# Production (Supabase example): +# DATABASE_URL="postgresql://postgres:[YOUR-PASSWORD]@db.xxxxx.supabase.co:5432/postgres" +# +# Production (PlanetScale example): +# DATABASE_URL="mysql://xxxxx:pscale_pw_xxxxx@xxxxx.us-east-3.psdb.cloud/salsabeatmachine?sslaccept=strict" + +DATABASE_URL="postgresql://postgres:password@localhost:5432/salsabeatmachine?schema=public" + +# ============================================================================= +# AUTHENTICATION +# ============================================================================= +# JWT Secret Key - used to sign authentication tokens +# Generate a secure random string: openssl rand -base64 32 +# IMPORTANT: Keep this secret and use a different value in production! + +JWT_SECRET="your-super-secret-jwt-key-change-this-in-production-use-openssl-rand-base64-32" + +# JWT Token Expiration (in seconds or zeit/ms format) +# Examples: "30d", "7d", "24h", "3600" (seconds) +JWT_EXPIRES_IN="30d" + +# Password hashing bcrypt rounds (10-12 recommended for production) +BCRYPT_ROUNDS="10" + +# ============================================================================= +# OAUTH PROVIDERS (Optional) +# ============================================================================= +# Google OAuth +# Get credentials from: https://console.cloud.google.com/apis/credentials +GOOGLE_CLIENT_ID="" +GOOGLE_CLIENT_SECRET="" + +# Facebook OAuth +# Get credentials from: https://developers.facebook.com/apps/ +FACEBOOK_APP_ID="" +FACEBOOK_APP_SECRET="" + +# Apple OAuth +# Get credentials from: https://developer.apple.com/ +APPLE_CLIENT_ID="" +APPLE_TEAM_ID="" +APPLE_KEY_ID="" +APPLE_PRIVATE_KEY="" + +# ============================================================================= +# EMAIL SERVICE (Optional - for verification and password reset) +# ============================================================================= +# SendGrid +SENDGRID_API_KEY="" +SENDGRID_FROM_EMAIL="noreply@salsabeatmachine.org" + +# Alternative: AWS SES +# AWS_SES_ACCESS_KEY="" +# AWS_SES_SECRET_KEY="" +# AWS_SES_REGION="us-east-1" + +# Alternative: Mailgun +# MAILGUN_API_KEY="" +# MAILGUN_DOMAIN="" + +# ============================================================================= +# FILE STORAGE +# ============================================================================= +# AWS S3 (for audio files, avatars, thumbnails) +AWS_ACCESS_KEY_ID="" +AWS_SECRET_ACCESS_KEY="" +AWS_S3_BUCKET="salsabeatmachine-assets" +AWS_S3_REGION="us-east-1" + +# Alternative: Vercel Blob Storage +# BLOB_READ_WRITE_TOKEN="" + +# Alternative: Firebase Storage +# FIREBASE_STORAGE_BUCKET="" + +# ============================================================================= +# REDIS (Optional - for caching and session management) +# ============================================================================= +# Local Redis +# REDIS_URL="redis://localhost:6379" + +# Upstash Redis (recommended for serverless) +# Get from: https://console.upstash.com/ +# REDIS_URL="rediss://default:xxxxx@xxxxx.upstash.io:6379" + +REDIS_URL="" + +# ============================================================================= +# APPLICATION SETTINGS +# ============================================================================= +# Node environment +NODE_ENV="development" + +# Application URL (used for email links, OAuth callbacks) +NEXT_PUBLIC_APP_URL="http://localhost:3009" + +# API Base URL (for frontend API calls) +NEXT_PUBLIC_API_URL="http://localhost:3009/api" + +# ============================================================================= +# RATE LIMITING +# ============================================================================= +# Requests per window for anonymous users +RATE_LIMIT_ANONYMOUS="100" + +# Requests per window for authenticated users +RATE_LIMIT_AUTHENTICATED="1000" + +# Rate limit window in seconds +RATE_LIMIT_WINDOW="900" + +# ============================================================================= +# MONITORING & ANALYTICS (Optional) +# ============================================================================= +# Sentry (error tracking) +# Get from: https://sentry.io/ +SENTRY_DSN="" + +# Google Analytics +NEXT_PUBLIC_GA_MEASUREMENT_ID="" + +# PostHog (product analytics) +NEXT_PUBLIC_POSTHOG_KEY="" +NEXT_PUBLIC_POSTHOG_HOST="" + +# ============================================================================= +# CDN (Optional) +# ============================================================================= +# CloudFront Distribution ID (AWS CDN) +CDN_DISTRIBUTION_ID="" + +# CloudFlare CDN +CLOUDFLARE_ZONE_ID="" +CLOUDFLARE_API_TOKEN="" + +# ============================================================================= +# FEATURE FLAGS (Optional) +# ============================================================================= +# Enable/disable features +ENABLE_OAUTH="false" +ENABLE_EMAIL_VERIFICATION="false" +ENABLE_SOCIAL_FEATURES="true" +ENABLE_ANALYTICS="true" +ENABLE_COMMENTS="true" + +# ============================================================================= +# ADMIN SETTINGS +# ============================================================================= +# Admin email addresses (comma-separated) +ADMIN_EMAILS="admin@salsabeatmachine.org" + +# ============================================================================= +# WEBHOOKS (Optional) +# ============================================================================= +# Webhook secret for verifying webhook requests +WEBHOOK_SECRET="" + +# ============================================================================= +# PRODUCTION ONLY +# ============================================================================= +# Database connection pooling (for production) +# DATABASE_CONNECTION_LIMIT="10" + +# Session secret (for production) +# SESSION_SECRET="" + +# ============================================================================= +# NOTES +# ============================================================================= +# 1. Never commit this file with real values to version control +# 2. Use different values for development, staging, and production +# 3. Store production secrets in your hosting platform's environment variables +# (Vercel, Railway, AWS Secrets Manager, etc.) +# 4. Rotate secrets regularly, especially after team member departures +# 5. Use strong, randomly generated values for all secrets +# 6. Keep a secure backup of production environment variables diff --git a/.eslintrc.json b/.eslintrc.json new file mode 100644 index 0000000..9282f9f --- /dev/null +++ b/.eslintrc.json @@ -0,0 +1,49 @@ +{ + "parser": "@typescript-eslint/parser", + "parserOptions": { + "ecmaVersion": 2022, + "sourceType": "module", + "ecmaFeatures": { + "jsx": true + } + }, + "plugins": ["react", "jsx-a11y", "import", "@typescript-eslint"], + "extends": [ + "eslint:recommended", + "plugin:react/recommended", + "plugin:jsx-a11y/recommended", + "plugin:import/errors", + "plugin:import/warnings", + "plugin:import/typescript", + "plugin:@typescript-eslint/recommended", + "prettier" + ], + "env": { + "browser": true, + "es2021": true, + "node": true + }, + "rules": { + "react/react-in-jsx-scope": "off", + "react/prop-types": "off", + "import/no-unresolved": "error", + "import/named": "error", + "import/default": "error", + "import/no-named-as-default": "warn", + "import/no-named-as-default-member": "warn", + "no-unused-vars": "warn", + "@typescript-eslint/no-unused-vars": ["warn"], + "@typescript-eslint/explicit-function-return-type": "off", + "@typescript-eslint/no-explicit-any": "off" + }, + "settings": { + "react": { + "version": "detect" + }, + "import/resolver": { + "node": { + "extensions": [".js", ".jsx", ".ts", ".tsx"] + } + } + } +} diff --git a/.gitignore b/.gitignore index c51128a..dae9a36 100644 --- a/.gitignore +++ b/.gitignore @@ -36,3 +36,5 @@ yarn-error.log* # firebase .firebase firebase-debug.log +package.json +next.config.js diff --git a/.vscode/extensions.json b/.vscode/extensions.json index 498e8f7..4915776 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -6,6 +6,14 @@ "esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "clinyong.vscode-css-modules" + "dbaeumer.vscode-eslint", // ESLint linting + "ms-vscode.vscode-typescript-next", // rask TS server + "formulahendry.auto-close-tag", + "formulahendry.auto-rename-tag", + "streetsidesoftware.code-spell-checker", + "eamodio.gitlens", // Git superpowers + "orta.vscode-jest", // hvis du kjører Jest-tester + "msjsdiag.debugger-for-chrome" // Chrome-debug ], // List of extensions recommended by VS Code that should not be recommended for users of this workspace. diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..8d0f09b --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,14 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Next.js Debug (Chrome)", + "type": "pwa-chrome", + "request": "launch", + "url": "http://localhost:3009", + "webRoot": "${workspaceFolder}", + "breakOnLoad": true + } + ] + } + \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..dd1ee9e --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,20 @@ +{ + // Bruk Volta-installert Node 14 til scripts i terminalen + "terminal.integrated.env.osx": { + "PATH": "${env:HOME}/.volta/bin:${env:PATH}" + }, + + // Prettier som format-motor + "editor.defaultFormatter": "esbenp.prettier-vscode", + "editor.formatOnSave": true, + + // ESLint fixer på lagring + "editor.codeActionsOnSave": { + "source.fixAll.eslint": "explicit" + }, + + // Snu på disable-varsling for useLayoutEffect (valgfritt) + "javascript.inlayHints.parameterNames.enabled": "all", + "typescript.tsdk": "node_modules/typescript/lib" + } + \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..2f6ca5c --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,26 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "dev", + "type": "npm", + "script": "dev", + "group": "build", + "problemMatcher": [] + }, + { + "label": "build", + "type": "npm", + "script": "build", + "group": "build", + "problemMatcher": [] + }, + { + "label": "vite-bundle", + "type": "npm", + "script": "bundle", + "detail": "Bygg standalone-widgeten til WordPress" + } + ] + } + \ No newline at end of file diff --git a/README.md b/README.md index b77c29b..68fad08 100644 --- a/README.md +++ b/README.md @@ -24,3 +24,40 @@ npm run dev ``` Then go to http://localhost:3009/ and start hacking! + +## Backend Features + +This repository includes comprehensive documentation for adding backend functionality to the SalsaNor Beat Machine. The backend would enable user accounts, pattern saving, social features, and analytics. + +### 📚 Backend Documentation + +- **[Quick Start Guide](./docs/QUICK_START.md)** - ⚡ Get started in 5 minutes +- **[Backend Summary](./docs/BACKEND_SUMMARY.md)** - High-level overview of proposed backend features +- **[Backend Architecture](./docs/BACKEND_ARCHITECTURE.md)** - Technical architecture and technology stack +- **[API Specification](./docs/API_SPECIFICATION.md)** - Complete API endpoint documentation +- **[Database Schema](./docs/DATABASE_SCHEMA.md)** - Database design with Prisma schema +- **[Implementation Guide](./docs/IMPLEMENTATION_GUIDE.md)** - Step-by-step implementation instructions +- **[Environment Variables](.env.example)** - Environment configuration template + +### 🎯 Key Backend Features + +- **User Authentication** - Register, login, OAuth integration +- **Pattern Management** - Save, load, and share custom beat patterns +- **Social Features** - Follow users, like patterns, comment, activity feed +- **User Profiles** - Customizable profiles with preferences and statistics +- **Analytics** - Track popular instruments, tempos, and patterns +- **Dynamic Configurations** - Serve machine configurations via API + +### 🚀 Getting Started with Backend + +**Quick Setup (5 minutes):** Follow the [Quick Start Guide](./docs/QUICK_START.md) to add authentication and pattern saving. + +**Full Implementation:** To add complete backend functionality to this project: + +1. Review the [Backend Summary](./docs/BACKEND_SUMMARY.md) for an overview +2. Follow the [Quick Start Guide](./docs/QUICK_START.md) for initial setup +3. Use the [Implementation Guide](./docs/IMPLEMENTATION_GUIDE.md) for detailed step-by-step instructions +4. Refer to the [API Specification](./docs/API_SPECIFICATION.md) as a reference for endpoints +5. Use the [Database Schema](./docs/DATABASE_SCHEMA.md) for data modeling + +The documentation provides everything needed to implement a production-ready backend, from authentication to social features to analytics. diff --git a/components/beat-indicator.module.scss b/components/beat-indicator.module.scss new file mode 100644 index 0000000..f95d2ee --- /dev/null +++ b/components/beat-indicator.module.scss @@ -0,0 +1,15 @@ +.beat { + width: 12px; + height: 12px; + border-radius: 9999px; // $radius-full + background: rgba(255, 255, 255, 0.2); + transition: all 0.15s ease; + display: inline-block; + margin-right: 0.5rem; // $spacing-2 +} + +.active { + background: #FFC947; + box-shadow: 0 0 15px rgba(0, 245, 255, 0.6); + transform: scale(1.3); +} diff --git a/components/beat-indicator.tsx b/components/beat-indicator.tsx index a414d50..d67058c 100644 --- a/components/beat-indicator.tsx +++ b/components/beat-indicator.tsx @@ -1,4 +1,4 @@ -import styles from './beat-indicator.module.css'; +import styles from './beat-indicator.module.scss'; import classnames from 'classnames'; export interface IBeatIndicatorProps { diff --git a/components/beat-machine-ui-glass.module.scss b/components/beat-machine-ui-glass.module.scss new file mode 100644 index 0000000..9797da4 --- /dev/null +++ b/components/beat-machine-ui-glass.module.scss @@ -0,0 +1,87 @@ +@use '../styles/abstracts/mixins-modules' as *; + +.container { + max-width: 1400px; + margin: 0 auto; + padding: 1rem; // $spacing-4 + + @include respond-to('desktop') { + padding: 1.5rem; // $spacing-6 + } +} + +.controlBar { + margin-bottom: 1.5rem; // $spacing-6 +} + +.controls { + display: flex; + gap: 1rem; // $spacing-4 + align-items: center; + flex-wrap: wrap; +} + +.bpmControl { + display: flex; + align-items: center; + gap: 0.75rem; // $spacing-3 + flex: 1; + min-width: 280px; + + @include respond-to('mobile') { + width: 100%; + flex-basis: 100%; + } +} + +.bpmLabel { + font-size: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); + color: var(--text-secondary); + font-weight: 500; +} + +.bpmValue { + font-weight: 600; + color: var(--color-golden); + min-width: 3ch; + text-align: right; +} + +.bpmSlider { + flex: 1; +} + +.flavorSelect { + display: flex; + gap: 0.5rem; // $spacing-2 + + @include respond-to('mobile') { + width: 100%; + + button { + flex: 1; + } + } +} + +.beatIndicator { + display: flex; + justify-content: center; + padding: 1.25rem; // $spacing-5 + margin-bottom: 1.5rem; // $spacing-6 +} + +.instrumentGrid { + display: grid; + gap: 1.25rem; // $spacing-5 + + @include responsive-grid(5, 3, 2); +} + +.instrumentCard { + display: flex; + justify-content: center; + align-items: center; + min-height: 150px; + padding: 0.5rem !important; // Minimal padding så tools kan være i hjørnet +} diff --git a/components/beat-machine-ui-glass.tsx b/components/beat-machine-ui-glass.tsx new file mode 100644 index 0000000..73b32fe --- /dev/null +++ b/components/beat-machine-ui-glass.tsx @@ -0,0 +1,142 @@ +import { observer } from 'mobx-react-lite'; +import { useEffect, useState } from 'react'; +import { observable } from 'mobx'; +import PlayArrowIcon from '@mui/icons-material/PlayArrow'; +import PauseIcon from '@mui/icons-material/Pause'; +import StopIcon from '@mui/icons-material/Stop'; + +import { IMachine } from '../engine/machine-interfaces'; +import { useBeatEngine } from '../hooks/use-beat-engine'; +import { useWindowListener } from '../hooks/use-window-listener'; +import { GlassContainer, GlassButton, GlassSlider } from './ui'; +import { BeatIndicator } from './beat-indicator'; +import { InstrumentTile } from './instrument-tile'; +import styles from './beat-machine-ui-glass.module.scss'; +import { IDefaultMachines } from './beat-machine-ui'; + +export interface IBeatMachineUIGlassProps { + machines: IDefaultMachines; +} + +export const BeatMachineUIGlass = observer(({ machines }: IBeatMachineUIGlassProps) => { + const { salsa, merengue } = machines; + const engine = useBeatEngine(); + const [machine, setMachine] = useState(observable(salsa)); + + useEffect(() => { + if (engine && machine) { + engine.machine = machine; + } + }, [engine, machine]); + + const beatCount = machine.flavor === 'Merengue' ? 4 : 8; + const beatDivider = machine.flavor === 'Merengue' ? 2 : 1; + const beatIndex = engine?.playing ? Math.round(0.5 + ((engine.beat / beatDivider) % beatCount)) : 0; + + useWindowListener( + 'keydown', + (event: KeyboardEvent) => { + switch (event.key) { + case '+': + case '=': + machine.bpm = Math.min(250, machine.bpm + 5); + break; + case '-': + machine.bpm = Math.max(80, machine.bpm - 5); + break; + case 'k': + machine.keyNote = (machine.keyNote + 7) % 12; + break; + case 'K': + machine.keyNote = (machine.keyNote + 5) % 12; + break; + } + if (event.key >= '0' && event.key <= '9') { + const index = (parseInt(event.key, 10) + 10 - 1) % 10; + const instrument = machine.instruments[index]; + if (instrument) { + if (event.altKey) { + instrument.activeProgram = (instrument.activeProgram + 1) % instrument.programs.length; + } else { + instrument.enabled = !instrument.enabled; + } + } + } + }, + [machine], + ); + + const handlePlayPause = () => { + if (engine?.playing) { + engine?.stop(); + } else { + engine?.play(); + } + }; + + const handleStop = () => { + engine?.stop(); + }; + + return ( +
+ {/* Control Bar */} + +
+ : } + onClick={handlePlayPause} + > + {engine?.playing ? 'Pause' : 'Play'} + + } onClick={handleStop}> + Stop + + +
+ BPM: + {machine.bpm} + (machine.bpm = value)} + className={styles.bpmSlider} + /> +
+ +
+ setMachine(observable(salsa))} + > + Salsa + + setMachine(observable(merengue))} + > + Merengue + +
+
+
+ + {/* Beat Indicator */} + + + + + {/* Instrument Grid */} +
+ {machine.instruments.map((instrument) => ( + + + + ))} +
+
+ ); +}); diff --git a/components/beat-machine-ui.module.css b/components/beat-machine-ui.module.css index 409e674..a63694e 100644 --- a/components/beat-machine-ui.module.css +++ b/components/beat-machine-ui.module.css @@ -5,6 +5,11 @@ padding: 16px; border-radius: 4px; } +.card { + display: grid; + grid-template-columns: 1fr 3fr 1fr; + gap: 16px; +} .instrumentList { display: flex; @@ -20,3 +25,18 @@ .instrumentTile { margin-bottom: 12px; } +.MuiSlider-root { + width: 100% !important; + height: 8px !important; + background-color: #ccc !important; + margin-top: 10px; +} + +.MuiSlider-track { + background-color: #3f51b5 !important; +} + +.MuiSlider-thumb { + background-color: #3f51b5 !important; + border: 2px solid white !important; +} diff --git a/components/beat-machine-ui.tsx b/components/beat-machine-ui.tsx index 66d9dba..8bd52b1 100644 --- a/components/beat-machine-ui.tsx +++ b/components/beat-machine-ui.tsx @@ -1,16 +1,21 @@ import { Button, ButtonGroup, + Container, FormControl, Grid, IconButton, InputLabel, Select, Slider, + Stack, Typography, -} from '@material-ui/core'; -import PauseIcon from '@material-ui/icons/Pause'; -import PlayIcon from '@material-ui/icons/PlayArrow'; +} from '@mui/material'; + +import PauseIcon from '@mui/icons-material/Pause'; +import PlayIcon from '@mui/icons-material/PlayArrow'; +import PauseCircleOutlineIcon from '@mui/icons-material/PauseCircleOutline'; +import PlayCircleOutlineIcon from '@mui/icons-material/PlayCircleOutline'; import classnames from 'classnames'; import { observable } from 'mobx'; import { observer } from 'mobx-react-lite'; @@ -19,7 +24,7 @@ import { IMachine } from '../engine/machine-interfaces'; import { useBeatEngine } from '../hooks/use-beat-engine'; import { useWindowListener } from '../hooks/use-window-listener'; import { BeatIndicator } from './beat-indicator'; -import styles from './beat-machine-ui.module.css'; +import styles from './css/beat-machine-ui.module.css'; import { InstrumentTile } from './instrument-tile'; export interface IDefaultMachines { @@ -90,80 +95,105 @@ export const BeatMachineUI = observer(({ machines }: IBeatMachineUIProps) => { }; return ( -
+
- - + + - {engine?.playing ? : } + {engine?.playing ? ( + + ) : ( + + )} - - (machine.bpm = newValue as number)} - /> - - - - {machine.bpm} BPM - - - - - {engine && salsa && merengue && ( - - - - - )} - - - - - Key - (machine.keyNote = parseInt(String(e.target.value), 10))} + inputProps={{ + id: 'machine-key-note', + }} + > + + + + + + + + + + + + + + + + + Tempo: + + + + {machine.bpm} BPM + + + + + + + (machine.bpm = newValue as number)} + /> + + + + + - - - - - - - - - - - - - - +
+ +
+
+
- -
- -
@@ -173,6 +203,6 @@ export const BeatMachineUI = observer(({ machines }: IBeatMachineUIProps) => {
))}
- + ); }); diff --git a/components/css/beat-indicator.module copy.css b/components/css/beat-indicator.module copy.css new file mode 100644 index 0000000..fc56bf3 --- /dev/null +++ b/components/css/beat-indicator.module copy.css @@ -0,0 +1,14 @@ +.beat { + box-sizing: content-box; + display: inline-block; + height: 18px; + width: 18px; + border-radius: 11px; + border: #333 groove 2px; + background-color: #400; + margin-right: 4px; +} + +.active { + background-color: red; +} diff --git a/components/css/beat-indicator.module.css b/components/css/beat-indicator.module.css new file mode 100644 index 0000000..19aab7c --- /dev/null +++ b/components/css/beat-indicator.module.css @@ -0,0 +1,14 @@ +.beat { + box-sizing: content-box; + display: inline-block; + height: 18px; + width: 18px; + border-radius: 11px; + border: #333 groove 2px; + background-color: #400; + margin-right: 4px; +} + +.active { + background-color: yellow; +} diff --git a/components/css/beat-machine-ui.module copy 2.css b/components/css/beat-machine-ui.module copy 2.css new file mode 100644 index 0000000..44a5c0c --- /dev/null +++ b/components/css/beat-machine-ui.module copy 2.css @@ -0,0 +1,99 @@ +/* ───────── GENERELT ───────── */ +.wrapper { + font-family: system-ui, sans-serif; +} + +/* Kort/stil */ +.card { + display: flex; + align-items: center; + gap: 1rem; + padding: 0.8rem 1.1rem; + background: #ffffff; + border-radius: 12px; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08); + max-width: 480px; + margin-bottom: 1rem; +} + +/* Ikonkolonne */ +.icon { + flex: 0 0 56px; + height: 56px; + border-radius: 12px; + background: #f3f3f3; + display: flex; + align-items: center; + justify-content: center; + font-size: 2rem; + transition: background 0.1s ease; +} +.icon.active { + background: var(--accent); + color: #fff; +} + +/* Høyre panel */ +.panel { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; /* nødv. for overflow riktig */ +} +.row { + display: flex; + align-items: center; + gap: 0.75rem; + flex-wrap: wrap; +} + +/* Instrumentnavn */ +.label { + font-weight: 600; + margin-right: auto; /* skyv resten mot høyre */ +} + +/* Play-knapp */ +.play { + width: 46px !important; + height: 46px !important; + border-radius: 50% !important; + background: #111 !important; + color: #fff !important; +} +.play:hover { + background: #222 !important; +} + +/* Tempo */ +.tempo { + flex: 1; + display: flex; + align-items: center; + gap: 0.5rem; + min-width: 180px; +} +.slider { + flex: 1; +} +.bpm { + width: 70px; + text-align: right; + font-variant-numeric: tabular-nums; +} + +/* Beat-indikator */ +.indicator { + margin-left: 0.5rem; +} + +/* ───────── INSTRUMENTLISTE (som før, kun paddings) ───────── */ +.instrumentList { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); + gap: 0.75rem; + padding: 0.5rem; +} +.instrumentTile { + min-width: 0; +} diff --git a/components/css/beat-machine-ui.module copy.css b/components/css/beat-machine-ui.module copy.css new file mode 100644 index 0000000..409e674 --- /dev/null +++ b/components/css/beat-machine-ui.module copy.css @@ -0,0 +1,22 @@ +.card { + background: white; + margin-bottom: 10px; + box-shadow: 0 2px 1px -1px rgba(0, 0, 0, 0.2), 0 1px 1px 0 rgba(0, 0, 0, 0.14), 0 1px 3px 0 rgba(0, 0, 0, 0.12); + padding: 16px; + border-radius: 4px; +} + +.instrumentList { + display: flex; + flex-wrap: wrap; + justify-content: space-between; +} + +.controlsIndicator { + margin-top: 16px; + text-align: center; +} + +.instrumentTile { + margin-bottom: 12px; +} diff --git a/components/css/beat-machine-ui.module.css b/components/css/beat-machine-ui.module.css new file mode 100644 index 0000000..9f22fb6 --- /dev/null +++ b/components/css/beat-machine-ui.module.css @@ -0,0 +1,70 @@ +/* ============================== + Kort Styling +============================== */ +.card { + background-color: #ffffff; + margin-bottom: 16px; + box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); + padding: 20px; + border-radius: 8px; + transition: all 0.3s ease; +} + +.card:hover { + box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.15); +} + +/* ============================== + Instrument Liste +============================== */ +.instrumentList { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); + gap: 12px; + margin-top: 12px; +} + +/* ============================== + Instrument Tile +============================== */ +.instrumentTile { + background-color: #f5f5f5; + border-radius: 8px; + padding: 12px; + box-shadow: 0px 2px 4px rgba(0, 0, 0, 0.1); + transition: all 0.3s ease; +} + +.instrumentTile:hover { + box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.15); +} + +/* ============================== + Kontroll Indikator +============================== */ +.controlsIndicator { + margin-top: 24px; + text-align: center; + display: flex; + justify-content: center; + gap: 8px; +} + +/* ============================== + Responsiv justering +============================== */ +@media (max-width: 768px) { + .card { + padding: 16px; + } + + .instrumentList { + grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); + } +} + +@media (max-width: 480px) { + .instrumentList { + grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); + } +} diff --git a/components/css/instrument-tile.module.css b/components/css/instrument-tile.module.css new file mode 100644 index 0000000..81f7703 --- /dev/null +++ b/components/css/instrument-tile.module.css @@ -0,0 +1,132 @@ +.container { + display: flex; + flex-direction: column; + font-size: 12px; + width: 100%; + height: 100%; + position: relative; + align-items: center; + justify-content: center; +} + +.thumbnail { + cursor: pointer; + background: no-repeat 50% 50%; + background-size: contain; + width: 96px; + height: 88px; +} + +.thumbnail.disabled { + filter: url(#gray-overlay); +} + +.main { + position: relative; + width: 100%; + display: flex; + justify-content: center; + align-items: center; + min-height: 88px; +} + +.bottom { + font-size: 18px; + height: 40px; + width: 100%; +} + +.tools { + position: absolute; + bottom: 0; + right: 0; + display: flex; + gap: 4px; + opacity: 0; + transition: opacity 0.3s ease, transform 0.3s ease; + transform: translateY(8px); + z-index: 10; +} + +.tools .iconButton { + color: rgba(255, 255, 255, 0.95); + padding: 6px; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + border-radius: 6px; + transition: all 0.2s ease; + min-width: auto; + width: 32px; + height: 32px; +} + +.tools .iconButton:hover { + background: rgba(0, 0, 0, 0.65); + transform: scale(1.1); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.tools .iconButton svg { + font-size: 18px; +} + +.tools .active { + color: #FFC947; + background: rgba(255, 201, 71, 0.2); + box-shadow: 0 0 12px rgba(255, 201, 71, 0.4); +} + +.instrumentLabel { + opacity: 0; + transition: opacity ease 0.3s; + text-align: center; +} + +.container:hover .instrumentLabel, +.container:hover .tools { + opacity: 1; + transform: translateY(0); +} + +.settingsPanel { + position: absolute; + bottom: 45px; + left: 50%; + transform: translateX(-50%); + padding: 12px; + background: rgba(0, 0, 0, 0.85); + backdrop-filter: blur(16px); + -webkit-backdrop-filter: blur(16px); + border: 1px solid rgba(255, 255, 255, 0.15); + border-radius: 8px; + animation: slideUp 0.3s cubic-bezier(0.4, 0, 0.2, 1); + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); + width: 90%; + max-width: 200px; + z-index: 100; +} + +.settingsPanel :global(.MuiSlider-root) { + color: #FFC947; +} + +.settingsPanel :global(.MuiSelect-root) { + color: rgba(255, 255, 255, 0.95); +} + +.settingsPanel :global(.MuiOutlinedInput-notchedOutline) { + border-color: rgba(255, 255, 255, 0.2); +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateX(-50%) translateY(12px); + } + to { + opacity: 1; + transform: translateX(-50%) translateY(0); + } +} + diff --git a/components/css/mobile-app-links.module.css b/components/css/mobile-app-links.module.css new file mode 100644 index 0000000..ead400f --- /dev/null +++ b/components/css/mobile-app-links.module.css @@ -0,0 +1,8 @@ +.mobileLinks { + text-align: center; + margin: 16px 0 24px; +} + +.mobileLinks a:not(:first-child) { + margin-left: 16px; +} diff --git a/components/instrument-tile.module.css b/components/instrument-tile.module.css index 2afc150..b93c80f 100644 --- a/components/instrument-tile.module.css +++ b/components/instrument-tile.module.css @@ -21,6 +21,7 @@ .main { display: flex; position: relative; + flex-direction: column; } .bottom { @@ -30,21 +31,40 @@ } .tools { - background: rgba(255, 255, 255, 0.5); - bottom: 0; + position: absolute; + bottom: 8px; + right: 8px; + display: flex; + gap: 4px; + opacity: 0; + transition: opacity 0.3s ease, transform 0.3s ease; + transform: translateY(4px); } .tools .iconButton { - color: black; - padding: 8px; + color: rgba(255, 255, 255, 0.9); + padding: 6px; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(8px); + border-radius: 6px; + transition: all 0.2s ease; +} + +.tools .iconButton:hover { + background: rgba(0, 0, 0, 0.6); + transform: scale(1.1); +} + +.tools .iconButton svg { + font-size: 18px; } .tools .active { - color: blue; + color: #FFC947; + background: rgba(255, 201, 71, 0.2); } -.instrumentLabel, -.tools { +.instrumentLabel { opacity: 0; transition: opacity ease 0.3s; text-align: center; @@ -53,5 +73,26 @@ .container:hover .instrumentLabel, .container:hover .tools { opacity: 1; + transform: translateY(0); +} + +.settingsPanel { + margin-top: 8px; + padding: 12px; + background: rgba(0, 0, 0, 0.3); + backdrop-filter: blur(10px); + border-radius: 8px; + animation: slideUp 0.2s ease; +} + +@keyframes slideUp { + from { + opacity: 0; + transform: translateY(8px); + } + to { + opacity: 1; + transform: translateY(0); + } } diff --git a/components/instrument-tile.tsx b/components/instrument-tile.tsx index 6f7276e..947d519 100644 --- a/components/instrument-tile.tsx +++ b/components/instrument-tile.tsx @@ -1,11 +1,11 @@ -import { FormControl, IconButton, Select, Slider } from '@material-ui/core'; +import { FormControl, IconButton, Select, Slider } from '@mui/material'; import classnames from 'classnames'; import { observer } from 'mobx-react-lite'; -import { useState } from 'react'; +import { useState, useEffect, useRef } from 'react'; import { IInstrument } from '../engine/machine-interfaces'; -import styles from './instrument-tile.module.css'; -import SettingsIcon from '@material-ui/icons/Settings'; -import VolumeIcon from '@material-ui/icons/VolumeUp'; +import styles from './css/instrument-tile.module.css'; +import SettingsIcon from '@mui/icons-material/Settings'; +import VolumeIcon from '@mui/icons-material/VolumeUp'; interface IInstrumentTileProps { instrument: IInstrument; @@ -15,7 +15,24 @@ export const InstrumentTile = observer(({ instrument }: IInstrumentTileProps) => const [showSettings, setShowSettings] = useState(false); const [showVolume, setShowVolume] = useState(false); const [previousVolume, setPreviousVolume] = useState(instrument.volume); - const showTitle = !showSettings && !showVolume; + const containerRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (containerRef.current && !containerRef.current.contains(event.target as Node)) { + setShowSettings(false); + setShowVolume(false); + } + }; + + if (showSettings || showVolume) { + document.addEventListener('mousedown', handleClickOutside); + } + + return () => { + document.removeEventListener('mousedown', handleClickOutside); + }; + }, [showSettings, showVolume]); const toggle = () => { if (instrument.enabled) { @@ -28,7 +45,7 @@ export const InstrumentTile = observer(({ instrument }: IInstrumentTileProps) => }; return ( -
+
{ setShowSettings(!showSettings); setShowVolume(false); @@ -47,6 +65,7 @@ export const InstrumentTile = observer(({ instrument }: IInstrumentTileProps) => { setShowVolume(!showVolume); setShowSettings(false); @@ -56,33 +75,38 @@ export const InstrumentTile = observer(({ instrument }: IInstrumentTileProps) =>
- {showTitle &&
{instrument.title}
} +
{instrument.title}
{showVolume && ( - { - instrument.volume = newValue as number; - }} - /> +
+ { + instrument.volume = newValue as number; + }} + /> +
)} {showSettings && ( - - - +
+ + + +
)} {/* filter is used by CSS to draw disabled instruments */} diff --git a/components/mobile-app-links.tsx b/components/mobile-app-links.tsx index de51a08..b4607e8 100644 --- a/components/mobile-app-links.tsx +++ b/components/mobile-app-links.tsx @@ -1,5 +1,5 @@ import { AppStoreIcon } from './app-store-icon'; -import styles from './mobile-app-links.module.css'; +import styles from './css/mobile-app-links.module.css'; import { PlayStoreIcon } from './plasy-store-icon'; export function MobileAppLinks() { diff --git a/components/ui/GlassButton.module.scss b/components/ui/GlassButton.module.scss new file mode 100644 index 0000000..1bae8b9 --- /dev/null +++ b/components/ui/GlassButton.module.scss @@ -0,0 +1,77 @@ +@use '../../styles/abstracts/mixins-modules' as *; + +.btn { + padding: 0.75rem 1.25rem; // $spacing-3 $spacing-5 + border-radius: 0.5rem; // $radius-md + font-weight: 500; // $font-weight-medium + font-size: 1rem; // $font-size-base + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + cursor: pointer; + border: none; + display: inline-flex; + align-items: center; + gap: 0.5rem; // $spacing-2 + @include smooth-transition(all, 0.2s, ease); + @include focus-visible; + + &--primary { + background: linear-gradient(135deg, #FFC947, #FF9933); + color: rgba(255, 255, 255, 0.95); + box-shadow: 0 4px 15px rgba(255, 153, 51, 0.4); + + &:hover:not(:disabled) { + transform: translateY(-2px) scale(1.02); + box-shadow: 0 0 30px rgba(255, 201, 71, 0.7); + } + + &:active { + transform: translateY(0) scale(0.98); + } + } + + &--ghost { + background: transparent; + border: 1px solid rgba(255, 153, 51, 0.3); + color: rgba(255, 255, 255, 0.95); + + &:hover:not(:disabled) { + background: rgba(255, 153, 51, 0.15); + border-color: rgba(255, 201, 71, 0.5); + } + } + + &--icon { + width: 36px; + height: 36px; + padding: 0; + border-radius: 0.375rem; // $radius-sm + background: rgba(255, 255, 255, 0.1); + color: rgba(255, 255, 255, 0.95); + justify-content: center; + + &:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.2); + transform: scale(1.1); + } + + &.active { + background: #FFC947; + color: #0f0c29; + } + } + + &:disabled { + opacity: 0.5; + cursor: not-allowed; + } +} + +.icon { + display: flex; + align-items: center; + + svg { + width: 20px; + height: 20px; + } +} diff --git a/components/ui/GlassButton.tsx b/components/ui/GlassButton.tsx new file mode 100644 index 0000000..0fc3f07 --- /dev/null +++ b/components/ui/GlassButton.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './GlassButton.module.scss'; + +interface GlassButtonProps extends React.ButtonHTMLAttributes { + variant?: 'primary' | 'ghost' | 'icon'; + children: React.ReactNode; + leftIcon?: React.ReactNode; + rightIcon?: React.ReactNode; +} + +export const GlassButton: React.FC = ({ + variant = 'primary', + children, + leftIcon, + rightIcon, + className, + ...props +}) => { + return ( + {leftIcon}} + {children} + {rightIcon && {rightIcon}} + + ); +}; diff --git a/components/ui/GlassContainer.module.scss b/components/ui/GlassContainer.module.scss new file mode 100644 index 0000000..6c2909c --- /dev/null +++ b/components/ui/GlassContainer.module.scss @@ -0,0 +1,28 @@ +@use '../../styles/abstracts/mixins-modules' as *; + +.glass { + @include glass-light; + border-radius: 0.75rem; // $radius-lg + padding: 1.5rem; // $spacing-6 + + &--light { + @include glass-light; + } + + &--medium { + @include glass-medium; + } + + &--dark { + @include glass-dark; + } + + &--hover { + @include hover-lift; + + &:hover { + background: rgba(255, 153, 51, 0.15); + border-color: rgba(255, 201, 71, 0.3); + } + } +} diff --git a/components/ui/GlassContainer.tsx b/components/ui/GlassContainer.tsx new file mode 100644 index 0000000..642d151 --- /dev/null +++ b/components/ui/GlassContainer.tsx @@ -0,0 +1,28 @@ +import React from 'react'; +import classNames from 'classnames'; +import styles from './GlassContainer.module.scss'; + +interface GlassContainerProps { + children: React.ReactNode; + variant?: 'light' | 'medium' | 'dark'; + className?: string; + hover?: boolean; +} + +export const GlassContainer: React.FC = ({ + children, + variant = 'light', + className, + hover = true +}) => { + return ( +
+ {children} +
+ ); +}; diff --git a/components/ui/GlassSlider.module.scss b/components/ui/GlassSlider.module.scss new file mode 100644 index 0000000..ed35534 --- /dev/null +++ b/components/ui/GlassSlider.module.scss @@ -0,0 +1,61 @@ +.sliderControl { + display: flex; + flex-direction: column; + gap: 0.5rem; // $spacing-2 + width: 100%; +} + +.labelRow { + display: flex; + justify-content: space-between; + align-items: center; +} + +.label { + font-size: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); + color: rgba(255, 255, 255, 0.75); + font-weight: 500; +} + +.value { + font-size: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); + color: #FFC947; + font-weight: 600; +} + +.slider { + -webkit-appearance: none; + appearance: none; + width: 100%; + height: 4px; + background: rgba(255, 255, 255, 0.2); + border-radius: 9999px; // $radius-full + outline: none; + + &::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 9999px; + background: linear-gradient(135deg, #FFC947, #FF9933); + cursor: pointer; + box-shadow: 0 0 10px rgba(255, 201, 71, 0.5); + transition: all 0.2s ease; + + &:hover { + transform: scale(1.2); + box-shadow: 0 0 20px rgba(255, 201, 71, 0.8); + } + } + + &::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 9999px; + background: linear-gradient(135deg, #FFC947, #FF9933); + cursor: pointer; + box-shadow: 0 0 10px rgba(255, 201, 71, 0.5); + border: none; + } +} diff --git a/components/ui/GlassSlider.tsx b/components/ui/GlassSlider.tsx new file mode 100644 index 0000000..da12dd5 --- /dev/null +++ b/components/ui/GlassSlider.tsx @@ -0,0 +1,44 @@ +import React from 'react'; +import styles from './GlassSlider.module.scss'; + +interface GlassSliderProps { + label?: string; + value: number; + min: number; + max: number; + step?: number; + onChange: (value: number) => void; + className?: string; + showValue?: boolean; +} + +export const GlassSlider: React.FC = ({ + label, + value, + min, + max, + step = 1, + onChange, + className, + showValue = false +}) => { + return ( +
+ {label && ( +
+ + {showValue && {value}} +
+ )} + onChange(parseFloat(e.target.value))} + /> +
+ ); +}; diff --git a/components/ui/index.ts b/components/ui/index.ts new file mode 100644 index 0000000..7093468 --- /dev/null +++ b/components/ui/index.ts @@ -0,0 +1,3 @@ +export { GlassContainer } from './GlassContainer'; +export { GlassButton } from './GlassButton'; +export { GlassSlider } from './GlassSlider'; diff --git a/demo/ui-mockup.html b/demo/ui-mockup.html new file mode 100644 index 0000000..ae57053 --- /dev/null +++ b/demo/ui-mockup.html @@ -0,0 +1,945 @@ + + + + + + SalsaNor Beat Machine - UI Design System Demo + + + + + + + +
+ +
+

SalsaNor Beat Machine

+

Glassmorphic UI Design System Demo

+
+ + +
+
+ + + + +
+ +
+ + +
+
+ + +
+
+
+
+
+
+
+
+
+
+ + +
+ +
+
+

🥁 Drums

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+

🎸 Bass

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+

🎹 Piano

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+

🎛 Synth

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+

🪘 Percussion

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+ + +
+
+

🎺 Trumpet

+
+ + + +
+
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+

+ SalsaNor Beat Machine - Glassmorphic UI Design System Demo
+ Built with ❤️ for the SalsaNor Project +

+
+
+ + + + diff --git a/dependencies.json b/dependencies.json new file mode 100644 index 0000000..a0b78aa --- /dev/null +++ b/dependencies.json @@ -0,0 +1,148 @@ +{ + "version": "2.0.0", + "name": "beat-machine", + "problems": [ + "extraneous: @emnapi/runtime@1.4.3 /Users/bjorn-torealmas/Documents/GIT/BeatMachine/The-SalsaNor-beat-machine/node_modules/@emnapi/runtime" + ], + "dependencies": { + "@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "overridden": false + }, + "@babel/plugin-proposal-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.27.1.tgz", + "overridden": false + }, + "@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "overridden": false + }, + "@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "overridden": false, + "extraneous": true, + "problems": [ + "extraneous: @emnapi/runtime@1.4.3 /Users/bjorn-torealmas/Documents/GIT/BeatMachine/The-SalsaNor-beat-machine/node_modules/@emnapi/runtime" + ] + }, + "@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "overridden": false + }, + "@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "overridden": false + }, + "@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "overridden": false + }, + "@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "overridden": false + }, + "@mui/icons-material": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.17.1.tgz", + "overridden": false + }, + "@mui/material": { + "version": "5.17.1", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.17.1.tgz", + "overridden": false + }, + "@mui/styles": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-6.4.11.tgz", + "overridden": false + }, + "@types/node": { + "version": "18.19.100", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.100.tgz", + "overridden": false + }, + "@types/react-dom": { + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "overridden": false + }, + "@types/react": { + "version": "18.3.21", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.21.tgz", + "overridden": false + }, + "@types/xmldom": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/@types/xmldom/-/xmldom-0.1.34.tgz", + "overridden": false + }, + "@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "overridden": false + }, + "classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "overridden": false + }, + "eslint-config-next": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/eslint-config-next/-/eslint-config-next-15.3.2.tgz", + "overridden": false + }, + "eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "overridden": false + }, + "latest": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/latest/-/latest-0.2.0.tgz", + "overridden": false + }, + "mobx-react-lite": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-3.4.3.tgz", + "overridden": false + }, + "mobx": { + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", + "overridden": false + }, + "next": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.2.tgz", + "overridden": false + }, + "node": { + "version": "20.19.2", + "resolved": "https://registry.npmjs.org/node/-/node-20.19.2.tgz", + "overridden": false + }, + "react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "overridden": false + }, + "react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "overridden": false + }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "overridden": false + } + } +} diff --git a/docs/API_SPECIFICATION.md b/docs/API_SPECIFICATION.md new file mode 100644 index 0000000..1dcde0a --- /dev/null +++ b/docs/API_SPECIFICATION.md @@ -0,0 +1,942 @@ +# API Specification for SalsaNor Beat Machine Backend + +## Base URL +``` +Production: https://api.salsabeatmachine.org +Development: http://localhost:3009/api +``` + +## Authentication + +All authenticated endpoints require a Bearer token in the Authorization header: +``` +Authorization: Bearer +``` + +## Response Format + +### Success Response +```json +{ + "success": true, + "data": { /* response data */ } +} +``` + +### Error Response +```json +{ + "success": false, + "error": { + "code": "ERROR_CODE", + "message": "Human readable error message", + "details": { /* optional additional info */ } + } +} +``` + +## Endpoints + +--- + +## Authentication Endpoints + +### POST /auth/register +Register a new user account. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "securePassword123", + "displayName": "Salsa Dancer", + "acceptTerms": true +} +``` + +**Response:** `201 Created` +```json +{ + "success": true, + "data": { + "user": { + "id": "usr_123abc", + "email": "user@example.com", + "displayName": "Salsa Dancer", + "createdAt": "2024-01-17T12:00:00Z" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } +} +``` + +### POST /auth/login +Login with email and password. + +**Request Body:** +```json +{ + "email": "user@example.com", + "password": "securePassword123" +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "user": { + "id": "usr_123abc", + "email": "user@example.com", + "displayName": "Salsa Dancer" + }, + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." + } +} +``` + +### POST /auth/logout +Logout current user (invalidate token). + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "message": "Logged out successfully" + } +} +``` + +### POST /auth/forgot-password +Request password reset email. + +**Request Body:** +```json +{ + "email": "user@example.com" +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "message": "Password reset email sent" + } +} +``` + +### POST /auth/reset-password +Reset password with token from email. + +**Request Body:** +```json +{ + "token": "reset_token_from_email", + "newPassword": "newSecurePassword123" +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "message": "Password reset successfully" + } +} +``` + +### POST /auth/oauth/google +Authenticate with Google OAuth. + +**Request Body:** +```json +{ + "idToken": "google_id_token" +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "user": { /* user object */ }, + "token": "jwt_token" + } +} +``` + +--- + +## User Profile Endpoints + +### GET /users/me +Get current user profile. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "usr_123abc", + "email": "user@example.com", + "displayName": "Salsa Dancer", + "avatar": "https://cdn.example.com/avatars/usr_123abc.jpg", + "bio": "Passionate salsa dancer and musician", + "preferences": { + "defaultBpm": 120, + "defaultKey": 0, + "defaultFlavor": "Salsa", + "useGlassUI": true + }, + "stats": { + "patternsCreated": 15, + "patternsShared": 8, + "followers": 23, + "following": 45 + }, + "createdAt": "2024-01-17T12:00:00Z", + "updatedAt": "2024-01-17T15:30:00Z" + } +} +``` + +### PUT /users/me +Update current user profile. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "displayName": "New Name", + "bio": "Updated bio", + "avatar": "base64_encoded_image_or_url" +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "usr_123abc", + "displayName": "New Name", + "bio": "Updated bio", + "avatar": "https://cdn.example.com/avatars/usr_123abc.jpg", + "updatedAt": "2024-01-17T16:00:00Z" + } +} +``` + +### PUT /users/me/preferences +Update user preferences. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "defaultBpm": 130, + "defaultKey": 7, + "defaultFlavor": "Merengue", + "useGlassUI": false +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "preferences": { + "defaultBpm": 130, + "defaultKey": 7, + "defaultFlavor": "Merengue", + "useGlassUI": false + } + } +} +``` + +### GET /users/:userId +Get public profile of another user. + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "usr_456def", + "displayName": "Other User", + "avatar": "https://cdn.example.com/avatars/usr_456def.jpg", + "bio": "Salsa instructor", + "stats": { + "patternsShared": 25, + "followers": 120 + }, + "isFollowing": false + } +} +``` + +--- + +## Beat Pattern Endpoints + +### GET /patterns +List beat patterns with filtering and pagination. + +**Query Parameters:** +- `page` (number, default: 1): Page number +- `limit` (number, default: 20, max: 100): Items per page +- `sort` (string, default: "recent"): Sort order (recent, popular, liked) +- `flavor` (string): Filter by flavor (Salsa, Merengue) +- `userId` (string): Filter by user ID +- `search` (string): Search in title and description + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "patterns": [ + { + "id": "pat_123abc", + "title": "My Salsa Beat", + "description": "A groovy salsa pattern with complex cowbell", + "flavor": "Salsa", + "bpm": 120, + "keyNote": 0, + "isPublic": true, + "thumbnail": "https://cdn.example.com/thumbnails/pat_123abc.png", + "author": { + "id": "usr_123abc", + "displayName": "Salsa Dancer", + "avatar": "https://cdn.example.com/avatars/usr_123abc.jpg" + }, + "stats": { + "plays": 245, + "likes": 38, + "comments": 5 + }, + "createdAt": "2024-01-15T10:00:00Z", + "updatedAt": "2024-01-15T10:00:00Z" + } + ], + "pagination": { + "page": 1, + "limit": 20, + "total": 150, + "pages": 8 + } + } +} +``` + +### GET /patterns/:patternId +Get a specific beat pattern. + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "pat_123abc", + "title": "My Salsa Beat", + "description": "A groovy salsa pattern with complex cowbell", + "flavor": "Salsa", + "bpm": 120, + "keyNote": 0, + "isPublic": true, + "configuration": { + "instruments": [ + { + "id": "clave", + "enabled": true, + "activeProgram": 0, + "volume": 1.0 + } + ] + }, + "author": { + "id": "usr_123abc", + "displayName": "Salsa Dancer", + "avatar": "https://cdn.example.com/avatars/usr_123abc.jpg" + }, + "stats": { + "plays": 245, + "likes": 38, + "comments": 5, + "isLikedByCurrentUser": false + }, + "createdAt": "2024-01-15T10:00:00Z", + "updatedAt": "2024-01-15T10:00:00Z" + } +} +``` + +### POST /patterns +Create a new beat pattern. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "title": "My New Beat", + "description": "Description of the beat", + "flavor": "Salsa", + "bpm": 125, + "keyNote": 0, + "isPublic": true, + "configuration": { + "instruments": [ + { + "id": "clave", + "enabled": true, + "activeProgram": 0, + "volume": 1.0, + "pitchOffset": 0 + } + ] + } +} +``` + +**Response:** `201 Created` +```json +{ + "success": true, + "data": { + "id": "pat_789xyz", + "title": "My New Beat", + "description": "Description of the beat", + "flavor": "Salsa", + "bpm": 125, + "keyNote": 0, + "isPublic": true, + "shareUrl": "https://salsabeatmachine.org/patterns/pat_789xyz", + "createdAt": "2024-01-17T18:00:00Z" + } +} +``` + +### PUT /patterns/:patternId +Update an existing beat pattern. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "title": "Updated Title", + "description": "Updated description", + "bpm": 130, + "configuration": { /* updated configuration */ } +} +``` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "pat_789xyz", + "title": "Updated Title", + "updatedAt": "2024-01-17T19:00:00Z" + } +} +``` + +### DELETE /patterns/:patternId +Delete a beat pattern. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "message": "Pattern deleted successfully" + } +} +``` + +### GET /patterns/me +Get current user's beat patterns. + +**Headers:** `Authorization: Bearer ` + +**Query Parameters:** +- `page` (number, default: 1) +- `limit` (number, default: 20) +- `includePrivate` (boolean, default: true) + +**Response:** `200 OK` (same format as GET /patterns) + +### POST /patterns/:patternId/like +Like a beat pattern. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "liked": true, + "likeCount": 39 + } +} +``` + +### DELETE /patterns/:patternId/like +Unlike a beat pattern. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "liked": false, + "likeCount": 38 + } +} +``` + +### POST /patterns/:patternId/play +Track a play/view of a pattern. + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "playCount": 246 + } +} +``` + +### POST /patterns/:patternId/fork +Create a copy of a pattern. + +**Headers:** `Authorization: Bearer ` + +**Response:** `201 Created` +```json +{ + "success": true, + "data": { + "id": "pat_999zzz", + "title": "My New Beat (Fork)", + "originalId": "pat_123abc", + "createdAt": "2024-01-17T20:00:00Z" + } +} +``` + +--- + +## Comment Endpoints + +### GET /patterns/:patternId/comments +Get comments for a pattern. + +**Query Parameters:** +- `page` (number, default: 1) +- `limit` (number, default: 20) + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "comments": [ + { + "id": "cmt_123abc", + "text": "Great beat! Love the cowbell pattern.", + "author": { + "id": "usr_456def", + "displayName": "Another User", + "avatar": "https://cdn.example.com/avatars/usr_456def.jpg" + }, + "createdAt": "2024-01-16T12:00:00Z", + "updatedAt": "2024-01-16T12:00:00Z" + } + ], + "pagination": { + "page": 1, + "limit": 20, + "total": 5 + } + } +} +``` + +### POST /patterns/:patternId/comments +Add a comment to a pattern. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "text": "This is an amazing beat!" +} +``` + +**Response:** `201 Created` +```json +{ + "success": true, + "data": { + "id": "cmt_789xyz", + "text": "This is an amazing beat!", + "author": { + "id": "usr_123abc", + "displayName": "Salsa Dancer" + }, + "createdAt": "2024-01-17T21:00:00Z" + } +} +``` + +### PUT /comments/:commentId +Update a comment. + +**Headers:** `Authorization: Bearer ` + +**Request Body:** +```json +{ + "text": "Updated comment text" +} +``` + +**Response:** `200 OK` + +### DELETE /comments/:commentId +Delete a comment. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` + +--- + +## Social Endpoints + +### POST /users/:userId/follow +Follow a user. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "following": true, + "followerCount": 121 + } +} +``` + +### DELETE /users/:userId/follow +Unfollow a user. + +**Headers:** `Authorization: Bearer ` + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "following": false, + "followerCount": 120 + } +} +``` + +### GET /users/:userId/followers +Get list of user's followers. + +**Query Parameters:** +- `page` (number, default: 1) +- `limit` (number, default: 20) + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "users": [ + { + "id": "usr_123abc", + "displayName": "User Name", + "avatar": "https://cdn.example.com/avatars/usr_123abc.jpg", + "isFollowedByCurrentUser": false + } + ], + "pagination": { + "page": 1, + "limit": 20, + "total": 120 + } + } +} +``` + +### GET /users/:userId/following +Get list of users that a user follows. + +**Response:** Same format as GET /users/:userId/followers + +### GET /feed +Get activity feed for current user. + +**Headers:** `Authorization: Bearer ` + +**Query Parameters:** +- `page` (number, default: 1) +- `limit` (number, default: 20) + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "activities": [ + { + "id": "act_123abc", + "type": "pattern_created", + "user": { + "id": "usr_456def", + "displayName": "Other User" + }, + "pattern": { + "id": "pat_123abc", + "title": "New Beat" + }, + "createdAt": "2024-01-17T15:00:00Z" + }, + { + "id": "act_456def", + "type": "pattern_liked", + "user": { + "id": "usr_789ghi", + "displayName": "Third User" + }, + "pattern": { + "id": "pat_456def", + "title": "Cool Pattern" + }, + "createdAt": "2024-01-17T14:30:00Z" + } + ], + "pagination": { + "page": 1, + "limit": 20, + "total": 45 + } + } +} +``` + +--- + +## Machine Configuration Endpoints + +### GET /machines +Get available machine configurations (Salsa, Merengue, etc.). + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "machines": [ + { + "id": "salsa", + "title": "Salsa", + "flavor": "Salsa", + "description": "Traditional Salsa rhythm machine" + }, + { + "id": "merengue", + "title": "Merengue", + "flavor": "Merengue", + "description": "Traditional Merengue rhythm machine" + } + ] + } +} +``` + +### GET /machines/:machineId +Get detailed machine configuration. + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "id": "salsa", + "title": "Salsa", + "flavor": "Salsa", + "bpm": 120, + "keyNote": 0, + "instruments": [ + { + "id": "clave", + "title": "Clave", + "enabled": true, + "activeProgram": 0, + "respectsClave": true, + "programs": [ + { + "title": "Son Clave", + "length": 16, + "notes": [ + {"index": 2, "pitch": 0}, + {"index": 4, "pitch": 0} + ] + } + ] + } + ] + } +} +``` + +--- + +## Analytics Endpoints + +### GET /analytics/popular-instruments +Get most popular instruments. + +**Query Parameters:** +- `timeRange` (string): "day", "week", "month", "year", "all" +- `limit` (number, default: 10) + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "instruments": [ + { + "id": "clave", + "title": "Clave", + "usageCount": 1234, + "percentage": 85.5 + }, + { + "id": "cowbell", + "title": "Cowbell", + "usageCount": 1150, + "percentage": 79.8 + } + ] + } +} +``` + +### GET /analytics/popular-patterns +Get trending beat patterns. + +**Query Parameters:** +- `timeRange` (string): "day", "week", "month", "year" +- `limit` (number, default: 10) + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "patterns": [ + { + "id": "pat_123abc", + "title": "Popular Beat", + "plays": 5432, + "likes": 234, + "author": { + "id": "usr_123abc", + "displayName": "User Name" + } + } + ] + } +} +``` + +### GET /analytics/tempo-distribution +Get BPM usage distribution. + +**Response:** `200 OK` +```json +{ + "success": true, + "data": { + "distribution": [ + {"bpm": 120, "count": 450}, + {"bpm": 125, "count": 380}, + {"bpm": 130, "count": 320} + ] + } +} +``` + +--- + +## Error Codes + +- `AUTH_REQUIRED`: Authentication required +- `INVALID_CREDENTIALS`: Invalid email or password +- `USER_EXISTS`: User already exists +- `USER_NOT_FOUND`: User not found +- `PATTERN_NOT_FOUND`: Pattern not found +- `UNAUTHORIZED`: Insufficient permissions +- `VALIDATION_ERROR`: Request validation failed +- `RATE_LIMIT_EXCEEDED`: Too many requests +- `SERVER_ERROR`: Internal server error + +--- + +## Rate Limiting + +- Anonymous users: 100 requests per 15 minutes +- Authenticated users: 1000 requests per 15 minutes +- Pattern creation: 20 per hour +- Comment posting: 60 per hour + +Rate limit headers are included in all responses: +``` +X-RateLimit-Limit: 1000 +X-RateLimit-Remaining: 998 +X-RateLimit-Reset: 1705512000 +``` + +--- + +## Webhooks (Future Feature) + +Allow external services to subscribe to events like: +- `pattern.created` +- `pattern.liked` +- `user.followed` +- `comment.created` diff --git a/docs/BACKEND_ARCHITECTURE.md b/docs/BACKEND_ARCHITECTURE.md new file mode 100644 index 0000000..9d98323 --- /dev/null +++ b/docs/BACKEND_ARCHITECTURE.md @@ -0,0 +1,208 @@ +# Backend Architecture for SalsaNor Beat Machine + +## Overview + +This document outlines the proposed backend architecture for the SalsaNor Beat Machine application. The backend will enhance the current client-side application with user accounts, data persistence, social features, and analytics. + +## Technology Stack + +### Recommended Technologies + +- **Runtime**: Node.js (aligns with existing Next.js frontend) +- **Framework**: Next.js API Routes (serverless functions) or Express.js (dedicated server) +- **Database**: PostgreSQL (relational) or MongoDB (document-based) +- **Authentication**: NextAuth.js or Firebase Authentication +- **ORM**: Prisma (for PostgreSQL) or Mongoose (for MongoDB) +- **File Storage**: AWS S3 or Firebase Storage (for audio files) +- **Caching**: Redis (for session management and frequently accessed data) + +## Core Components + +### 1. Authentication Service +- User registration and login +- OAuth integration (Google, Facebook, Apple) +- JWT-based session management +- Password reset functionality + +### 2. User Profile Service +- User profile CRUD operations +- User preferences management +- User settings (default BPM, favorite key, UI theme) + +### 3. Beat Pattern Service +- Create, read, update, delete custom beat patterns +- Save/load machine configurations +- Version control for beat patterns +- Export/import beat patterns + +### 4. Social Features Service +- Share beat patterns with public links +- Like/favorite beat patterns +- Comment on beat patterns +- Follow other users +- Activity feed + +### 5. Analytics Service +- Track instrument usage statistics +- Popular tempo ranges +- Most used key signatures +- User engagement metrics +- Pattern popularity rankings + +### 6. Machine Configuration Service +- Serve instrument definitions dynamically +- Custom instrument creation (admin only) +- Machine configuration versioning + +### 7. Media Management Service +- Upload custom audio samples +- Manage instrument sound libraries +- Audio file optimization and conversion + +## Architecture Diagram + +``` +┌─────────────────────────────────────────────────────────────┐ +│ Next.js Frontend │ +│ (React Components + UI) │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ API Gateway │ +│ (Next.js API Routes / Express) │ +└─────────────────────────────────────────────────────────────┘ + │ + ┌─────────────────┼─────────────────┐ + ▼ ▼ ▼ + ┌───────────────┐ ┌──────────────┐ ┌──────────────┐ + │ Auth Service │ │ Beat Pattern │ │ Social │ + │ │ │ Service │ │ Service │ + └───────────────┘ └──────────────┘ └──────────────┘ + │ │ │ + └─────────────────┼─────────────────┘ + ▼ + ┌──────────────────┐ + │ Database │ + │ (PostgreSQL/ │ + │ MongoDB) │ + └──────────────────┘ + │ + ┌─────────┴──────────┐ + ▼ ▼ + ┌──────────────┐ ┌──────────────┐ + │ Redis │ │ S3/Storage │ + │ (Cache) │ │ (Audio Files)│ + └──────────────┘ └──────────────┘ +``` + +## Deployment Options + +### Option 1: Serverless (Vercel) +- Deploy Next.js app with API routes to Vercel +- Use Vercel Postgres or external database (Supabase, PlanetScale) +- Redis via Upstash +- S3 or Vercel Blob storage + +**Pros**: Easy deployment, automatic scaling, low maintenance +**Cons**: Cold starts, function timeout limits + +### Option 2: Traditional Server (AWS/GCP/Azure) +- Deploy Next.js SSR + Express API server +- Managed database service (RDS, Cloud SQL) +- Managed Redis (ElastiCache, Memorystore) +- S3 or Cloud Storage + +**Pros**: More control, no timeout limits, consistent performance +**Cons**: Higher complexity, manual scaling, more maintenance + +### Option 3: Hybrid +- Next.js frontend on Vercel +- Separate API server on AWS ECS/Fargate or Railway +- Managed database and Redis +- S3 for storage + +**Pros**: Best of both worlds, separation of concerns +**Cons**: More infrastructure to manage, higher cost + +## Security Considerations + +1. **Authentication**: Use secure password hashing (bcrypt), implement rate limiting +2. **Authorization**: Role-based access control (RBAC) for admin features +3. **Data Validation**: Validate all inputs on server-side +4. **HTTPS**: Enforce SSL/TLS for all connections +5. **CORS**: Configure proper CORS policies +6. **SQL Injection**: Use parameterized queries (ORM handles this) +7. **XSS Protection**: Sanitize user-generated content +8. **Rate Limiting**: Prevent API abuse +9. **Environment Variables**: Secure storage of secrets and API keys + +## Performance Optimization + +1. **Caching**: Cache frequently accessed data (machine configs, popular patterns) +2. **CDN**: Serve audio files via CDN +3. **Database Indexing**: Index frequently queried fields +4. **Pagination**: Implement pagination for lists +5. **Compression**: Enable gzip/brotli compression +6. **Lazy Loading**: Load beat patterns on demand +7. **Connection Pooling**: Efficient database connections + +## Monitoring and Observability + +1. **Logging**: Structured logging with timestamps and context +2. **Error Tracking**: Sentry or similar for error monitoring +3. **Metrics**: Track API response times, error rates, user activity +4. **Uptime Monitoring**: Service health checks +5. **Analytics**: Google Analytics, Mixpanel, or custom analytics + +## Migration Strategy + +### Phase 1: Foundation +1. Set up database and authentication +2. Create basic API structure +3. Implement user registration/login + +### Phase 2: Core Features +1. Beat pattern save/load +2. User profiles and preferences +3. Basic sharing functionality + +### Phase 3: Social Features +1. Pattern sharing and discovery +2. Comments and likes +3. User following + +### Phase 4: Advanced Features +1. Analytics dashboard +2. Custom instrument uploads +3. Collaborative editing + +## Cost Estimation (Monthly) + +### Small Scale (< 1000 users) +- Hosting: $0-20 (Vercel/Railway free tier) +- Database: $5-25 (Supabase/PlanetScale) +- Redis: $0-10 (Upstash free tier) +- Storage: $5-10 (S3/Vercel Blob) +**Total**: $10-65/month + +### Medium Scale (1000-10000 users) +- Hosting: $20-100 +- Database: $25-100 +- Redis: $10-30 +- Storage: $10-50 +- CDN: $10-30 +**Total**: $75-310/month + +### Large Scale (> 10000 users) +- Custom scaling required +- Estimated: $300-1000+/month + +## Next Steps + +1. Review and approve architecture +2. Choose specific technologies (database, hosting, etc.) +3. Set up development environment +4. Create API specification (see API_SPECIFICATION.md) +5. Design database schema (see DATABASE_SCHEMA.md) +6. Begin implementation diff --git a/docs/BACKEND_SUMMARY.md b/docs/BACKEND_SUMMARY.md new file mode 100644 index 0000000..4b1d4cc --- /dev/null +++ b/docs/BACKEND_SUMMARY.md @@ -0,0 +1,341 @@ +# Backend Feature Summary + +## Overview + +This document provides a high-level summary of the proposed backend infrastructure for the SalsaNor Beat Machine. The backend will transform the application from a purely client-side experience into a comprehensive platform with user accounts, social features, and data persistence. + +## Core Features to Add + +### 1. **User Authentication & Accounts** 🔐 +Enable users to create accounts and securely log in to the application. + +**Why it's natural:** +- Users want to save their custom beat patterns +- Personalized experience across devices +- Access to premium features + +**Key Components:** +- Email/password registration and login +- OAuth integration (Google, Facebook, Apple) +- Password reset functionality +- Email verification +- JWT-based session management + +--- + +### 2. **Save & Load Beat Patterns** 💾 +Allow users to save their custom rhythm creations and load them later. + +**Why it's natural:** +- Currently, all patterns are lost on page refresh +- Users want to preserve their creative work +- Share patterns with others + +**Key Components:** +- Create, read, update, delete (CRUD) operations for patterns +- Store instrument configurations (which instruments are enabled, volumes, programs) +- Store tempo (BPM) and key settings +- Public/private pattern visibility options +- Pattern versioning and history + +--- + +### 3. **User Profiles & Preferences** 👤 +Store user-specific settings and display profile information. + +**Why it's natural:** +- Remember preferred tempo, key, and music style +- Customize UI preferences (Glass UI vs Classic) +- Display user statistics and achievements + +**Key Components:** +- User profile pages with bio and avatar +- Default BPM, key signature, and flavor preferences +- UI theme preferences +- Account settings management +- User statistics (patterns created, likes received) + +--- + +### 4. **Social Features** 🌐 +Enable users to share, discover, and interact with others' beat patterns. + +**Why it's natural:** +- Music is inherently social +- Learn from other musicians' patterns +- Build a community of salsa enthusiasts + +**Key Components:** +- **Pattern Sharing:** Public URLs for beat patterns +- **Like/Favorite System:** Appreciate others' work +- **Comments:** Discuss patterns and provide feedback +- **User Following:** Follow favorite creators +- **Activity Feed:** See what people you follow are creating +- **Pattern Discovery:** Browse trending and popular patterns +- **Fork/Remix:** Create your own version of someone's pattern + +--- + +### 5. **Analytics & Insights** 📊 +Track usage patterns and provide insights to users and administrators. + +**Why it's natural:** +- Understand which instruments are most popular +- Identify common tempo ranges +- Help users discover popular patterns +- Guide future feature development + +**Key Components:** +- Popular instruments tracking +- Tempo distribution analysis +- Most-used key signatures +- Pattern popularity rankings +- User engagement metrics +- Geographic distribution (optional) +- Peak usage times + +--- + +### 6. **Dynamic Machine Configurations** ⚙️ +Serve instrument and machine configurations from the backend instead of static files. + +**Why it's natural:** +- Easier updates without redeploying frontend +- Support for custom instruments (future) +- A/B testing of new instruments +- Different configurations for different regions + +**Key Components:** +- API endpoints for machine configurations +- Version control for configurations +- Admin interface for managing instruments +- Custom instrument support (future enhancement) + +--- + +### 7. **Media Management** 🎵 +Handle audio file uploads and management. + +**Why it's natural:** +- Users may want to upload custom sounds +- Optimize audio delivery via CDN +- Support for different audio formats + +**Key Components:** +- Audio file storage (AWS S3, Firebase Storage) +- CDN integration for fast delivery +- Audio format conversion and optimization +- Custom sample uploads (premium feature) + +--- + +## Technical Architecture Summary + +### Backend Stack +- **Framework:** Next.js API Routes (serverless) or Express.js (dedicated server) +- **Database:** PostgreSQL with Prisma ORM +- **Authentication:** JWT tokens with bcrypt password hashing +- **File Storage:** AWS S3 or Vercel Blob +- **Caching:** Redis (optional, for performance) +- **Deployment:** Vercel (serverless) or AWS/Railway (dedicated) + +### Database Tables +1. **users** - User accounts and authentication +2. **user_preferences** - User settings and preferences +3. **patterns** - Saved beat patterns +4. **likes** - Pattern likes/favorites +5. **comments** - Comments on patterns +6. **follows** - User follow relationships +7. **sessions** - Authentication sessions +8. **activities** - Activity feed events +9. **analytics** - Usage statistics + +### API Structure +``` +/api/ +├── auth/ +│ ├── register +│ ├── login +│ ├── logout +│ └── reset-password +├── users/ +│ ├── me +│ ├── me/preferences +│ └── [userId] +├── patterns/ +│ ├── / +│ ├── [patternId] +│ ├── [patternId]/like +│ └── [patternId]/comments +├── social/ +│ ├── [userId]/follow +│ ├── [userId]/followers +│ ├── [userId]/following +│ └── feed +├── analytics/ +│ ├── popular-instruments +│ ├── popular-patterns +│ └── tempo-distribution +└── machines/ + ├── / + └── [machineId] +``` + +--- + +## Benefits of Adding a Backend + +### For Users +✅ **Persistence** - Never lose your creations +✅ **Portability** - Access patterns from any device +✅ **Discovery** - Find inspiration from other users +✅ **Community** - Connect with other salsa enthusiasts +✅ **Personalization** - Customized experience based on preferences + +### For the Platform +✅ **User Engagement** - Increased retention with saved content +✅ **Growth** - Social features drive viral growth +✅ **Insights** - Understand user behavior and preferences +✅ **Monetization** - Potential for premium features +✅ **Quality** - Community moderation and curated content + +--- + +## Implementation Phases + +### Phase 1: Foundation (Weeks 1-2) +- Set up database and authentication system +- Implement user registration and login +- Create basic pattern save/load functionality + +### Phase 2: Core Features (Weeks 3-4) +- User profile pages +- Pattern CRUD operations +- User preferences management +- Basic pattern browsing + +### Phase 3: Social Features (Weeks 5-6) +- Pattern sharing with public URLs +- Like/favorite system +- Comment system +- User following + +### Phase 4: Advanced Features (Weeks 7-8) +- Activity feed +- Analytics dashboard +- Pattern discovery and search +- Performance optimization + +### Phase 5: Polish & Launch (Weeks 9-10) +- Testing and bug fixes +- Documentation +- Deployment and monitoring +- User onboarding flow + +--- + +## Cost Estimation + +### Startup Phase (< 1,000 users) +- **Hosting:** $0-20/month (Vercel free tier) +- **Database:** $5-25/month (Supabase/PlanetScale) +- **Storage:** $5-10/month (S3/Vercel Blob) +- **Redis:** $0-10/month (Upstash free tier) +- **Total:** ~$10-65/month + +### Growth Phase (1,000-10,000 users) +- **Total:** ~$75-310/month + +### Scale Phase (10,000+ users) +- **Total:** $300-1,000+/month +- Custom infrastructure may be needed + +--- + +## Security Considerations + +✅ **Password Security** - bcrypt hashing with salt rounds +✅ **JWT Tokens** - Secure token generation and validation +✅ **Input Validation** - Zod schema validation on all inputs +✅ **SQL Injection** - Prevented by Prisma ORM +✅ **XSS Protection** - Sanitize user-generated content +✅ **Rate Limiting** - Prevent API abuse +✅ **HTTPS** - Enforce SSL/TLS for all connections +✅ **CORS** - Proper CORS configuration + +--- + +## Success Metrics + +### User Engagement +- Number of registered users +- Patterns created per user +- Daily/weekly active users +- Time spent on platform + +### Social Features +- Patterns shared publicly +- Likes and comments per pattern +- User follow relationships +- Activity feed interactions + +### Technical Performance +- API response times +- Database query performance +- Error rates +- Uptime percentage + +--- + +## Documentation Index + +1. **[QUICK_START.md](./QUICK_START.md)** - ⚡ Quick setup guide (start here!) +2. **[BACKEND_ARCHITECTURE.md](./BACKEND_ARCHITECTURE.md)** - Complete architecture overview +3. **[API_SPECIFICATION.md](./API_SPECIFICATION.md)** - Detailed API endpoints and responses +4. **[DATABASE_SCHEMA.md](./DATABASE_SCHEMA.md)** - Complete database schema with Prisma +5. **[IMPLEMENTATION_GUIDE.md](./IMPLEMENTATION_GUIDE.md)** - Step-by-step implementation instructions +6. **[.env.example](../.env.example)** - Environment variable template + +--- + +## Getting Started + +**Quick Start (5 minutes):** Follow the [Quick Start Guide](./QUICK_START.md) to set up a basic backend with authentication. + +**Full Implementation:** To begin implementing the complete backend: + +1. **Review the documentation** in the `/docs` folder +2. **Choose your hosting platform** (Vercel, Railway, AWS) +3. **Set up your database** (PostgreSQL recommended) +4. **Follow the Implementation Guide** for step-by-step instructions +5. **Start with Phase 1** (Authentication and basic pattern storage) + +--- + +## Questions to Consider + +Before starting implementation, discuss with stakeholders: + +1. **Budget:** What's the budget for hosting and infrastructure? +2. **Timeline:** When do you want to launch these features? +3. **Priorities:** Which features are must-haves vs nice-to-haves? +4. **Scale:** How many users do you expect in the first year? +5. **Monetization:** Will this remain free or have premium features? +6. **Mobile Apps:** Will the Android/iOS apps also need backend integration? +7. **Moderation:** Who will moderate user-generated content? +8. **Support:** Who will handle technical support and bug reports? + +--- + +## Conclusion + +Adding a backend to the SalsaNor Beat Machine is a natural evolution that will: +- **Enhance user experience** with saved patterns and personalization +- **Build community** through social features +- **Enable growth** through viral sharing and discovery +- **Provide insights** via analytics +- **Create opportunities** for monetization and premium features + +The proposed architecture is scalable, secure, and follows modern best practices. Implementation can be done in phases to manage risk and validate features with users early. + +Ready to get started? Begin with the **[IMPLEMENTATION_GUIDE.md](./IMPLEMENTATION_GUIDE.md)**! 🚀 diff --git a/docs/CROSS_DOMAIN_SETUP.md b/docs/CROSS_DOMAIN_SETUP.md new file mode 100644 index 0000000..17ced57 --- /dev/null +++ b/docs/CROSS_DOMAIN_SETUP.md @@ -0,0 +1,197 @@ +# Cross-Domain Widget Setup + +Dette dokumentet forklarer hvordan du bruker Beat Machine Widget på tvers av domener. + +## Scenario + +Du vil hoste Beat Machine på `beat.salsanor.no` og bruke widgeten på `salsanor.no` (eller andre domener). + +## Steg 1: Server-konfigurasjon (beat.salsanor.no) + +Serveren som hoster Beat Machine må sende riktige CORS-headere for å tillate cross-origin requests. + +### Next.js (next.config.js) + +```javascript +module.exports = { + async headers() { + return [ + { + source: '/assets/:path*', + headers: [ + { key: 'Access-Control-Allow-Origin', value: '*' }, + { key: 'Access-Control-Allow-Methods', value: 'GET, OPTIONS' }, + ], + }, + { + source: '/widget.js', + headers: [ + { key: 'Access-Control-Allow-Origin', value: '*' }, + ], + }, + ]; + }, +}; +``` + +### Apache (.htaccess) + +```apache + + + Header set Access-Control-Allow-Origin "*" + + +``` + +### Nginx + +```nginx +location ~* \.(js|webm|mp3|json|xml)$ { + add_header Access-Control-Allow-Origin *; + add_header Access-Control-Allow-Methods "GET, OPTIONS"; +} +``` + +## Steg 2: Bygg widget for produksjon + +```bash +npm run bundle +``` + +Dette genererer `dist/widget.js` og `dist/widget.css`. + +## Steg 3: Deploy til beat.salsanor.no + +Last opp følgende filer til `beat.salsanor.no`: + +``` +/widget.js # Fra dist/widget.js (includes CSS) +/assets/ + audio/ + main.webm # Audio samples + main.mp3 # Fallback for eldre nettlesere + main.json # Audio sample descriptor + machines/ + salsa.xml # Salsa rhythm definitions + merengue.xml # Merengue rhythm definitions +``` + +## Steg 4: Bruk på salsanor.no + +### HTML + +```html + + + + My Salsa Article + + +

Min Salsa Artikkel

+ +

Her er en clave-rytme:

+
+ +

Og her er en full salsa-rytme:

+
+ + + + + +``` + +**Note:** CSS is automatically injected by the widget.js file - no separate CSS file needed! + +### WordPress + +I WordPress kan du legge til følgende i `functions.php`: + +```php +function enqueue_beat_machine_widget() { + wp_enqueue_script('beat-machine-widget', 'https://beat.salsanor.no/widget.js', array(), null, true); + wp_add_inline_script('beat-machine-widget', + 'window.BeatMachineWidget.setBaseUrl("https://beat.salsanor.no");' + ); +} +add_action('wp_enqueue_scripts', 'enqueue_beat_machine_widget'); +``` + +**Note:** CSS is automatically injected - no need to enqueue a separate stylesheet! + +Deretter kan du bruke widgeten i innlegg/sider: + +```html +
+``` + +## Widget-attributter + +- `data-beat-widget` - Påkrevd for å initialisere widgeten +- `data-instruments` - Kommaseparert liste over instrumenter (f.eks. "clave,cowbell,bongo") +- `data-programs` - Velg spesifikt pattern for instrumenter (f.eks. "clave:1,cowbell:2" der tallet er program-index) +- `data-bpm` - Tempo (60-200, standard: 120) +- `data-machine` - Velg maskin ("salsa" eller "merengue", standard: "salsa") +- `data-autoplay` - Start automatisk ("true" eller "false", standard: "false") + +### Eksempel med custom patterns: +```html + +
+``` + +## Tilgjengelige instrumenter + +### Salsa +- `clave` - Son clave (3-2 eller 2-3) +- `cowbell` - Campana/Bell +- `bongo` - Bongo +- `bass` - Bass +- `congas` - Congas +- `timbales` - Timbales +- `guiro` - Güiro +- `maracas` - Maracas +- `piano` - Piano montuno + +### Merengue +- `tambora` - Tambora +- `guira` - Güira +- `bass` - Bass + +## Feilsøking + +### Widget laster ikke +- Sjekk at `widget.js` lastes uten feil i nettleserens konsoll +- Verifiser at CORS-headere er konfigurert riktig + +### Ingen lyd +- Sjekk at audio-filer er tilgjengelige på `beat.salsanor.no/assets/audio/` +- Åpne nettleserens konsoll og se etter feilmeldinger +- Verifiser at `setBaseUrl()` er kalt før widgeten initialiseres + +### Console errors om CORS +- Serveren på `beat.salsanor.no` må sende `Access-Control-Allow-Origin` header +- Test at ressursene er tilgjengelige: åpne f.eks. `https://beat.salsanor.no/assets/machines/salsa.xml` direkte i nettleseren + +## Sikkerhet + +Hvis du vil begrense hvem som kan bruke widgeten, kan du endre CORS-konfigurasjonen: + +```javascript +// Kun tillat salsanor.no +{ key: 'Access-Control-Allow-Origin', value: 'https://salsanor.no' } + +// Tillat flere domener +{ key: 'Access-Control-Allow-Origin', value: 'https://salsanor.no, https://www.salsanor.no' } +``` + +## Support + +For spørsmål eller problemer, opprett en issue på GitHub eller kontakt utvikler. diff --git a/docs/DATABASE_SCHEMA.md b/docs/DATABASE_SCHEMA.md new file mode 100644 index 0000000..5d3fd7e --- /dev/null +++ b/docs/DATABASE_SCHEMA.md @@ -0,0 +1,684 @@ +# Database Schema for SalsaNor Beat Machine + +## Overview + +This document defines the database schema for the SalsaNor Beat Machine backend. The schema is designed to be database-agnostic but examples use PostgreSQL syntax with Prisma ORM conventions. + +## Technology Choice + +**Recommended**: PostgreSQL with Prisma ORM +- Strong typing support +- Excellent JSON support for flexible instrument configurations +- ACID compliance for data integrity +- Strong community and tooling +- Easy migration management with Prisma + +**Alternative**: MongoDB with Mongoose +- More flexible schema for rapidly evolving data models +- Native JSON storage +- Good for real-time features + +## Schema Diagram + +``` +┌─────────────┐ ┌──────────────┐ ┌─────────────┐ +│ Users │────────<│ Patterns │>────────│ Comments │ +└─────────────┘ └──────────────┘ └─────────────┘ + │ │ │ + │ │ │ + ▼ ▼ ▼ +┌─────────────┐ ┌──────────────┐ ┌─────────────┐ +│ Sessions │ │ Likes │ │ Analytics │ +└─────────────┘ └──────────────┘ └─────────────┘ + │ + │ ┌──────────────┐ + └────────────────>│ Follows │ + └──────────────┘ +``` + +## Tables + +### Users + +Stores user account information and authentication data. + +```sql +CREATE TABLE users ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + email VARCHAR(255) UNIQUE NOT NULL, + password_hash VARCHAR(255), -- NULL for OAuth-only users + display_name VARCHAR(100) NOT NULL, + avatar_url TEXT, + bio TEXT, + oauth_provider VARCHAR(50), -- 'google', 'facebook', 'apple', NULL + oauth_id VARCHAR(255), + email_verified BOOLEAN DEFAULT FALSE, + is_active BOOLEAN DEFAULT TRUE, + is_admin BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + last_login_at TIMESTAMP WITH TIME ZONE, + + CONSTRAINT unique_oauth UNIQUE (oauth_provider, oauth_id) +); + +CREATE INDEX idx_users_email ON users(email); +CREATE INDEX idx_users_oauth ON users(oauth_provider, oauth_id); +CREATE INDEX idx_users_created_at ON users(created_at); +``` + +**Prisma Schema:** +```prisma +model User { + id String @id @default(uuid()) + email String @unique + passwordHash String? @map("password_hash") + displayName String @map("display_name") + avatarUrl String? @map("avatar_url") + bio String? + oauthProvider String? @map("oauth_provider") + oauthId String? @map("oauth_id") + emailVerified Boolean @default(false) @map("email_verified") + isActive Boolean @default(true) @map("is_active") + isAdmin Boolean @default(false) @map("is_admin") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + lastLoginAt DateTime? @map("last_login_at") + + preferences UserPreference? + sessions Session[] + patterns Pattern[] + likes Like[] + comments Comment[] + followers Follow[] @relation("UserFollowers") + following Follow[] @relation("UserFollowing") + activities Activity[] + + @@unique([oauthProvider, oauthId], name: "unique_oauth") + @@index([email]) + @@index([oauthProvider, oauthId]) + @@index([createdAt]) + @@map("users") +} +``` + +--- + +### UserPreferences + +Stores user-specific application preferences and settings. + +```sql +CREATE TABLE user_preferences ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID UNIQUE NOT NULL REFERENCES users(id) ON DELETE CASCADE, + default_bpm INTEGER DEFAULT 120, + default_key_note INTEGER DEFAULT 0, + default_flavor VARCHAR(20) DEFAULT 'Salsa', + use_glass_ui BOOLEAN DEFAULT TRUE, + auto_play BOOLEAN DEFAULT FALSE, + show_beat_numbers BOOLEAN DEFAULT TRUE, + default_volume DECIMAL(3,2) DEFAULT 1.0, + theme VARCHAR(20) DEFAULT 'light', -- 'light', 'dark', 'auto' + language VARCHAR(10) DEFAULT 'en', + notifications_enabled BOOLEAN DEFAULT TRUE, + email_notifications BOOLEAN DEFAULT TRUE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_user_preferences_user_id ON user_preferences(user_id); +``` + +**Prisma Schema:** +```prisma +model UserPreference { + id String @id @default(uuid()) + userId String @unique @map("user_id") + defaultBpm Int @default(120) @map("default_bpm") + defaultKeyNote Int @default(0) @map("default_key_note") + defaultFlavor String @default("Salsa") @map("default_flavor") + useGlassUI Boolean @default(true) @map("use_glass_ui") + autoPlay Boolean @default(false) @map("auto_play") + showBeatNumbers Boolean @default(true) @map("show_beat_numbers") + defaultVolume Decimal @default(1.0) @map("default_volume") + theme String @default("light") + language String @default("en") + notificationsEnabled Boolean @default(true) @map("notifications_enabled") + emailNotifications Boolean @default(true) @map("email_notifications") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@map("user_preferences") +} +``` + +--- + +### Sessions + +Stores user session tokens for authentication. + +```sql +CREATE TABLE sessions ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + token VARCHAR(500) NOT NULL UNIQUE, + ip_address INET, + user_agent TEXT, + expires_at TIMESTAMP WITH TIME ZONE NOT NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + last_used_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_sessions_user_id ON sessions(user_id); +CREATE INDEX idx_sessions_token ON sessions(token); +CREATE INDEX idx_sessions_expires_at ON sessions(expires_at); +``` + +**Prisma Schema:** +```prisma +model Session { + id String @id @default(uuid()) + userId String @map("user_id") + token String @unique + ipAddress String? @map("ip_address") + userAgent String? @map("user_agent") + expiresAt DateTime @map("expires_at") + createdAt DateTime @default(now()) @map("created_at") + lastUsedAt DateTime @default(now()) @map("last_used_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([token]) + @@index([expiresAt]) + @@map("sessions") +} +``` + +--- + +### Patterns + +Stores user-created beat patterns. + +```sql +CREATE TABLE patterns ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + title VARCHAR(200) NOT NULL, + description TEXT, + flavor VARCHAR(20) NOT NULL, -- 'Salsa', 'Merengue' + bpm INTEGER NOT NULL, + key_note INTEGER NOT NULL, + is_public BOOLEAN DEFAULT TRUE, + configuration JSONB NOT NULL, -- Stores full machine configuration + thumbnail_url TEXT, + original_pattern_id UUID REFERENCES patterns(id) ON DELETE SET NULL, -- For forks + play_count INTEGER DEFAULT 0, + like_count INTEGER DEFAULT 0, + comment_count INTEGER DEFAULT 0, + fork_count INTEGER DEFAULT 0, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + published_at TIMESTAMP WITH TIME ZONE, + + CONSTRAINT valid_bpm CHECK (bpm >= 80 AND bpm <= 250), + CONSTRAINT valid_key_note CHECK (key_note >= 0 AND key_note <= 11) +); + +CREATE INDEX idx_patterns_user_id ON patterns(user_id); +CREATE INDEX idx_patterns_flavor ON patterns(flavor); +CREATE INDEX idx_patterns_is_public ON patterns(is_public); +CREATE INDEX idx_patterns_created_at ON patterns(created_at DESC); +CREATE INDEX idx_patterns_play_count ON patterns(play_count DESC); +CREATE INDEX idx_patterns_like_count ON patterns(like_count DESC); +CREATE INDEX idx_patterns_original_pattern_id ON patterns(original_pattern_id); +``` + +**Configuration JSONB Example:** +```json +{ + "instruments": [ + { + "id": "clave", + "enabled": true, + "activeProgram": 0, + "volume": 1.0, + "pitchOffset": 0 + }, + { + "id": "cowbell", + "enabled": true, + "activeProgram": 1, + "volume": 0.8, + "pitchOffset": 0 + } + ] +} +``` + +**Prisma Schema:** +```prisma +model Pattern { + id String @id @default(uuid()) + userId String @map("user_id") + title String + description String? + flavor String + bpm Int + keyNote Int @map("key_note") + isPublic Boolean @default(true) @map("is_public") + configuration Json + thumbnailUrl String? @map("thumbnail_url") + originalPatternId String? @map("original_pattern_id") + playCount Int @default(0) @map("play_count") + likeCount Int @default(0) @map("like_count") + commentCount Int @default(0) @map("comment_count") + forkCount Int @default(0) @map("fork_count") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + publishedAt DateTime? @map("published_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + originalPattern Pattern? @relation("PatternForks", fields: [originalPatternId], references: [id], onDelete: SetNull) + forks Pattern[] @relation("PatternForks") + likes Like[] + comments Comment[] + activities Activity[] + + @@index([userId]) + @@index([flavor]) + @@index([isPublic]) + @@index([createdAt]) + @@index([playCount]) + @@index([likeCount]) + @@index([originalPatternId]) + @@map("patterns") +} +``` + +--- + +### Likes + +Stores user likes on patterns. + +```sql +CREATE TABLE likes ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + pattern_id UUID NOT NULL REFERENCES patterns(id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT unique_user_pattern_like UNIQUE (user_id, pattern_id) +); + +CREATE INDEX idx_likes_user_id ON likes(user_id); +CREATE INDEX idx_likes_pattern_id ON likes(pattern_id); +CREATE INDEX idx_likes_created_at ON likes(created_at); +``` + +**Prisma Schema:** +```prisma +model Like { + id String @id @default(uuid()) + userId String @map("user_id") + patternId String @map("pattern_id") + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pattern Pattern @relation(fields: [patternId], references: [id], onDelete: Cascade) + + @@unique([userId, patternId], name: "unique_user_pattern_like") + @@index([userId]) + @@index([patternId]) + @@index([createdAt]) + @@map("likes") +} +``` + +--- + +### Comments + +Stores comments on patterns. + +```sql +CREATE TABLE comments ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + pattern_id UUID NOT NULL REFERENCES patterns(id) ON DELETE CASCADE, + text TEXT NOT NULL, + parent_comment_id UUID REFERENCES comments(id) ON DELETE CASCADE, -- For nested comments + is_edited BOOLEAN DEFAULT FALSE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT valid_comment_length CHECK (LENGTH(text) >= 1 AND LENGTH(text) <= 5000) +); + +CREATE INDEX idx_comments_user_id ON comments(user_id); +CREATE INDEX idx_comments_pattern_id ON comments(pattern_id); +CREATE INDEX idx_comments_parent_comment_id ON comments(parent_comment_id); +CREATE INDEX idx_comments_created_at ON comments(created_at); +``` + +**Prisma Schema:** +```prisma +model Comment { + id String @id @default(uuid()) + userId String @map("user_id") + patternId String @map("pattern_id") + text String + parentCommentId String? @map("parent_comment_id") + isEdited Boolean @default(false) @map("is_edited") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pattern Pattern @relation(fields: [patternId], references: [id], onDelete: Cascade) + parentComment Comment? @relation("CommentReplies", fields: [parentCommentId], references: [id], onDelete: Cascade) + replies Comment[] @relation("CommentReplies") + + @@index([userId]) + @@index([patternId]) + @@index([parentCommentId]) + @@index([createdAt]) + @@map("comments") +} +``` + +--- + +### Follows + +Stores user follow relationships. + +```sql +CREATE TABLE follows ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + follower_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + following_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT unique_follower_following UNIQUE (follower_id, following_id), + CONSTRAINT no_self_follow CHECK (follower_id != following_id) +); + +CREATE INDEX idx_follows_follower_id ON follows(follower_id); +CREATE INDEX idx_follows_following_id ON follows(following_id); +CREATE INDEX idx_follows_created_at ON follows(created_at); +``` + +**Prisma Schema:** +```prisma +model Follow { + id String @id @default(uuid()) + followerId String @map("follower_id") + followingId String @map("following_id") + createdAt DateTime @default(now()) @map("created_at") + + follower User @relation("UserFollowers", fields: [followerId], references: [id], onDelete: Cascade) + following User @relation("UserFollowing", fields: [followingId], references: [id], onDelete: Cascade) + + @@unique([followerId, followingId], name: "unique_follower_following") + @@index([followerId]) + @@index([followingId]) + @@index([createdAt]) + @@map("follows") +} +``` + +--- + +### Activities + +Stores activity feed events. + +```sql +CREATE TABLE activities ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + activity_type VARCHAR(50) NOT NULL, -- 'pattern_created', 'pattern_liked', 'user_followed', 'comment_created' + pattern_id UUID REFERENCES patterns(id) ON DELETE CASCADE, + target_user_id UUID REFERENCES users(id) ON DELETE CASCADE, + metadata JSONB, -- Additional context-specific data + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +CREATE INDEX idx_activities_user_id ON activities(user_id); +CREATE INDEX idx_activities_activity_type ON activities(activity_type); +CREATE INDEX idx_activities_pattern_id ON activities(pattern_id); +CREATE INDEX idx_activities_created_at ON activities(created_at DESC); +``` + +**Prisma Schema:** +```prisma +model Activity { + id String @id @default(uuid()) + userId String @map("user_id") + activityType String @map("activity_type") + patternId String? @map("pattern_id") + targetUserId String? @map("target_user_id") + metadata Json? + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pattern Pattern? @relation(fields: [patternId], references: [id], onDelete: Cascade) + + @@index([userId]) + @@index([activityType]) + @@index([patternId]) + @@index([createdAt]) + @@map("activities") +} +``` + +--- + +### Analytics + +Stores aggregated analytics data. + +```sql +CREATE TABLE analytics ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + metric_type VARCHAR(50) NOT NULL, -- 'instrument_usage', 'tempo_distribution', 'key_usage' + metric_key VARCHAR(100) NOT NULL, -- 'clave', '120_bpm', 'key_0' + metric_value INTEGER NOT NULL, + time_period DATE NOT NULL, + aggregation_level VARCHAR(20) NOT NULL, -- 'daily', 'weekly', 'monthly', 'yearly' + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + + CONSTRAINT unique_analytics_entry UNIQUE (metric_type, metric_key, time_period, aggregation_level) +); + +CREATE INDEX idx_analytics_metric_type ON analytics(metric_type); +CREATE INDEX idx_analytics_time_period ON analytics(time_period DESC); +CREATE INDEX idx_analytics_aggregation_level ON analytics(aggregation_level); +``` + +**Prisma Schema:** +```prisma +model Analytics { + id String @id @default(uuid()) + metricType String @map("metric_type") + metricKey String @map("metric_key") + metricValue Int @map("metric_value") + timePeriod DateTime @map("time_period") @db.Date + aggregationLevel String @map("aggregation_level") + createdAt DateTime @default(now()) @map("created_at") + + @@unique([metricType, metricKey, timePeriod, aggregationLevel], name: "unique_analytics_entry") + @@index([metricType]) + @@index([timePeriod]) + @@index([aggregationLevel]) + @@map("analytics") +} +``` + +--- + +## Complete Prisma Schema + +Here's the complete `schema.prisma` file: + +```prisma +// This is your Prisma schema file +// Learn more: https://pris.ly/d/prisma-schema + +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +// Copy all the model definitions from above into this schema.prisma file +// Models: User, UserPreference, Session, Pattern, Like, Comment, Follow, Activity, Analytics +// Refer to the individual model sections above for the complete definitions +``` + +--- + +## Migrations + +### Initial Migration + +```bash +# Create initial migration +npx prisma migrate dev --name init + +# Generate Prisma Client +npx prisma generate + +# Seed database (optional) +npx prisma db seed +``` + +### Sample Seed Data + +```typescript +// prisma/seed.ts +import { PrismaClient } from '@prisma/client'; +import bcrypt from 'bcrypt'; + +const prisma = new PrismaClient(); + +async function main() { + // Create demo user + const hashedPassword = await bcrypt.hash('demo123', 10); + + const demoUser = await prisma.user.create({ + data: { + email: 'demo@salsabeatmachine.org', + passwordHash: hashedPassword, + displayName: 'Demo User', + emailVerified: true, + preferences: { + create: { + defaultBpm: 120, + defaultKeyNote: 0, + defaultFlavor: 'Salsa', + useGlassUI: true, + }, + }, + }, + }); + + // Create demo pattern + await prisma.pattern.create({ + data: { + userId: demoUser.id, + title: 'Classic Salsa Beat', + description: 'A traditional salsa rhythm with clave and cowbell', + flavor: 'Salsa', + bpm: 120, + keyNote: 0, + isPublic: true, + configuration: { + instruments: [ + { + id: 'clave', + enabled: true, + activeProgram: 0, + volume: 1.0, + pitchOffset: 0, + }, + { + id: 'cowbell', + enabled: true, + activeProgram: 0, + volume: 0.8, + pitchOffset: 0, + }, + ], + }, + }, + }); + + console.log('Seed data created successfully'); +} + +main() + .catch((e) => { + console.error(e); + process.exit(1); + }) + .finally(async () => { + await prisma.$disconnect(); + }); +``` + +--- + +## Database Indexes Summary + +Key indexes for optimal query performance: + +1. **Users**: email, oauth credentials, created_at +2. **Patterns**: user_id, flavor, is_public, created_at, play_count, like_count +3. **Likes**: user_id, pattern_id, created_at +4. **Comments**: user_id, pattern_id, created_at +5. **Follows**: follower_id, following_id +6. **Sessions**: token, expires_at +7. **Activities**: user_id, created_at +8. **Analytics**: metric_type, time_period + +--- + +## Data Retention Policy + +- **Sessions**: Expire after 30 days of inactivity, cleaned up daily +- **Activities**: Keep for 90 days, archived after that +- **Analytics**: Keep daily data for 1 year, monthly aggregates indefinitely +- **Deleted Users**: Soft delete with 30-day grace period before hard delete +- **Deleted Patterns**: Keep for 7 days in case of accidental deletion + +--- + +## Backup Strategy + +1. **Daily automated backups** of entire database +2. **Point-in-time recovery** enabled (last 7 days) +3. **Weekly full backups** stored off-site +4. **Monthly backup testing** to ensure recoverability +5. **Critical data replication** to secondary region + +--- + +## Performance Considerations + +1. **Use connection pooling** (e.g., PgBouncer) +2. **Implement query result caching** with Redis +3. **Use read replicas** for analytics queries +4. **Partition large tables** (activities, analytics) by date +5. **Regular vacuum and analyze** for PostgreSQL maintenance +6. **Monitor slow query log** and optimize problematic queries diff --git a/docs/DEPLOYMENT_OPTIONS.md b/docs/DEPLOYMENT_OPTIONS.md new file mode 100644 index 0000000..61271ff --- /dev/null +++ b/docs/DEPLOYMENT_OPTIONS.md @@ -0,0 +1,502 @@ +# Deployment Options for SalsaNor Beat Machine + +## 📋 Project Overview + +**Technology Stack:** +- **Framework:** Next.js 15.3.2 (React 18.3.1) +- **Runtime:** Node.js 20.19.2 +- **Type:** SSR/SSG Hybrid Application +- **Build Output:** Static files + API routes (if used) +- **Assets:** Large audio files (~5.6 MB total: main.webm + main.mp3) + +**Key Requirements:** +- Serve static HTML/CSS/JS +- Host audio files (WebM/MP3) +- Support CORS for cross-domain widget embedding +- Fast content delivery for audio samples + +--- + +## 🎯 Deployment Options + +### Option 1: Static Export to Shared Host (RECOMMENDED) ⭐ + +**Approach:** Build as static HTML and upload via FTP/cPanel File Manager + +#### Steps: +```bash +# 1. Update next.config.js to enable static export +# Add: output: 'export' + +# 2. Build static files +npm run build +npm run export + +# 3. Upload 'out' folder contents to public_html via: +# - cPanel File Manager +# - FTP (FileZilla) +# - SSH/rsync if available +``` + +#### Configuration Required: + +**next.config.js:** +```javascript +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'export', + trailingSlash: true, + images: { + unoptimized: true, // Required for static export + }, + // Base path if not at domain root + basePath: process.env.BASE_PATH || '', +} + +export default nextConfig; +``` + +**.htaccess (for Apache):** +```apache +# Enable CORS for widget embedding + + Header set Access-Control-Allow-Origin "*" + Header set Access-Control-Allow-Methods "GET, OPTIONS" + Header set Access-Control-Allow-Headers "Content-Type" + + +# Serve correct MIME types + + AddType audio/webm .webm + AddType audio/mpeg .mp3 + AddType application/json .json + + +# Enable compression + + AddOutputFilterByType DEFLATE text/html text/css application/javascript application/json + + +# Cache audio files (1 year) + + ExpiresActive On + ExpiresByType audio/webm "access plus 1 year" + ExpiresByType audio/mpeg "access plus 1 year" + ExpiresByType application/json "access plus 1 week" + + +# Redirect to index.html for SPA routing (if needed) + + RewriteEngine On + RewriteBase / + RewriteRule ^index\.html$ - [L] + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + RewriteRule . /index.html [L] + +``` + +#### Pros: +✅ Works on any shared hosting (ProISP, One.com, etc.) +✅ No Node.js runtime required +✅ Fast page loads (pre-rendered HTML) +✅ Easy to deploy (just upload files) +✅ Very affordable hosting +✅ CDN-friendly + +#### Cons: +❌ No server-side API routes +❌ No dynamic rendering +❌ Must rebuild for content changes +❌ Large initial upload (~50 MB with audio files) + +#### Cost: **~50-100 NOK/month** (shared hosting) + +--- + +### Option 2: cPanel Node.js App + +**Approach:** Use cPanel's Node.js manager to run Next.js server + +#### Steps via cPanel: +``` +1. Log into cPanel +2. Go to "Setup Node.js App" +3. Create New Application: + - Node.js version: 20.x + - Application mode: Production + - Application root: /home/username/beat-machine + - Application URL: beat.salsanor.no + - Application startup file: server.js +4. Install dependencies via terminal +5. Start application +``` + +#### Required Files: + +**server.js:** +```javascript +const { createServer } = require('http'); +const { parse } = require('url'); +const next = require('next'); + +const dev = process.env.NODE_ENV !== 'production'; +const hostname = 'localhost'; +const port = process.env.PORT || 3000; + +const app = next({ dev, hostname, port }); +const handle = app.getRequestHandler(); + +app.prepare().then(() => { + createServer(async (req, res) => { + try { + const parsedUrl = parse(req.url, true); + await handle(req, res, parsedUrl); + } catch (err) { + console.error('Error occurred handling', req.url, err); + res.statusCode = 500; + res.end('internal server error'); + } + }).listen(port, (err) => { + if (err) throw err; + console.log(`> Ready on http://${hostname}:${port}`); + }); +}); +``` + +#### Pros: +✅ Full Next.js features (SSR, API routes) +✅ Dynamic content possible +✅ Easy updates via Git +✅ Can use Node.js ecosystem + +#### Cons: +❌ cPanel Node.js support varies by host +❌ Memory limits (often 512 MB - 1 GB) +❌ CPU throttling on shared hosting +❌ More complex setup +❌ Process may restart/crash on shared hosting +❌ ProISP may not support Node.js apps + +#### Cost: **~150-300 NOK/month** (cPanel with Node.js) + +--- + +### Option 3: Git Deploy + Build on Server + +**Approach:** Push to Git, trigger build on server + +#### Setup: +```bash +# 1. Initialize Git repo locally +git init +git add . +git commit -m "Initial commit" + +# 2. Add remote (cPanel Git Version Control) +git remote add production ssh://user@host/~/repositories/beat-machine.git + +# 3. Push to server +git push production master + +# 4. SSH into server and build +ssh user@host +cd ~/beat-machine +npm install +npm run build +npm run start # or export for static +``` + +#### Pros: +✅ Version control integration +✅ Easy rollbacks +✅ Automated deployments possible +✅ Clean workflow + +#### Cons: +❌ Requires SSH access +❌ Server must have Node.js +❌ Build process consumes resources +❌ ProISP may not allow SSH/Git + +#### Cost: **~150-300 NOK/month** (hosting with SSH) + +--- + +### Option 4: Vercel/Netlify (BEST for Next.js) ⭐⭐⭐ + +**Approach:** Deploy to specialized Next.js platform + +#### Vercel (by Next.js creators): +```bash +# 1. Install Vercel CLI +npm i -g vercel + +# 2. Deploy +vercel + +# 3. Connect custom domain +vercel domains add beat.salsanor.no +``` + +#### Pros: +✅ **Zero configuration** for Next.js +✅ Automatic builds on Git push +✅ Global CDN (fast worldwide) +✅ Free SSL/HTTPS +✅ Preview deployments +✅ Built-in analytics +✅ Automatic scaling +✅ Edge functions support +✅ **FREE tier sufficient for this project** + +#### Cons: +❌ Audio files may count toward bandwidth limits +❌ Need separate domain DNS setup +❌ Vendor lock-in (but easy to migrate) + +#### Cost: **FREE** (Hobby tier) or **$20/month** (Pro) +**Bandwidth:** 100 GB/month free (plenty for this app) + +--- + +### Option 5: Static CDN + Cloudflare + +**Approach:** Static export + Cloudflare CDN + +#### Setup: +```bash +# 1. Build static +npm run build && npm run export + +# 2. Upload to: + - Bunny CDN (~1 EUR/month) + - Cloudflare R2 Storage (~5 USD/month) + - AWS S3 + CloudFront + +# 3. Configure Cloudflare: + - Add domain + - Point to storage URL + - Enable caching rules +``` + +#### Pros: +✅ Ultra-fast global delivery +✅ Excellent caching +✅ DDoS protection +✅ Very affordable +✅ Unlimited bandwidth (Cloudflare) + +#### Cons: +❌ More complex setup +❌ Multiple services to configure +❌ Overkill for small project + +#### Cost: **~50-100 NOK/month** + +--- + +## 🎯 Recommendation for ProISP Shared Hosting + +### Primary Recommendation: **Option 1 - Static Export** + +**Why:** +1. ProISP shared hosting typically **does NOT** support Node.js apps +2. Static files work on **any web host** +3. **Simplest deployment** - just upload via FTP +4. **Fast performance** - no server processing +5. **Most affordable** - works with basic shared hosting + +### Implementation Plan: + +#### Phase 1: Enable Static Export +```bash +# 1. Modify next.config.js +cat >> next.config.js << 'EOF' +/** @type {import('next').NextConfig} */ +const nextConfig = { + output: 'export', + trailingSlash: true, + images: { + unoptimized: true, + }, + // Remove if at root domain + // basePath: '/beat', +} + +export default nextConfig; +EOF + +# 2. Update package.json scripts +"export": "next export", +"deploy": "npm run build && npm run export" +``` + +#### Phase 2: Build & Test Locally +```bash +npm run deploy + +# Test the output +cd out +npx serve +# Visit http://localhost:3000 +``` + +#### Phase 3: Upload to ProISP +``` +Option A - cPanel File Manager: +1. Login to cPanel +2. Navigate to public_html (or subdomain folder) +3. Upload entire 'out' folder contents +4. Add .htaccess file (see configuration above) + +Option B - FTP (FileZilla): +1. Connect to FTP server +2. Navigate to web root +3. Upload 'out' folder contents +4. Set permissions: folders 755, files 644 +``` + +#### Phase 4: Configure Domain +``` +DNS Settings (in ProISP or domain registrar): +- A Record: beat.salsanor.no → ProISP server IP +- CNAME: www → beat.salsanor.no + +Wait for DNS propagation (5-60 minutes) +``` + +#### Phase 5: Test CORS for Widget +```bash +# Test from browser console on different domain +fetch('https://beat.salsanor.no/assets/audio/main.json') + .then(r => r.json()) + .then(console.log) + .catch(console.error); + +# Should return JSON, not CORS error +``` + +--- + +## ⚠️ Potential Issues & Solutions + +### Issue 1: Next.js API Routes Don't Work + +**Problem:** Static export doesn't support API routes +**Solution:** Move any API logic to client-side or external service +**Status:** ✅ **Not a problem** - this app has no API routes + +### Issue 2: Dynamic Routing + +**Problem:** Dynamic routes need special handling +**Solution:** Pre-generate all routes or use hash routing +**Status:** ✅ **Not a problem** - all pages are static + +### Issue 3: Large Audio Files + +**Problem:** 5.6 MB audio files slow upload +**Solution:** +- Upload once, only update HTML/JS/CSS on changes +- Use FTP resume if connection drops +- Consider Git LFS for version control + +### Issue 4: Build Size + +**Problem:** `out` folder is ~50 MB +**Solution:** +- Add `.htaccess` gzip compression +- Serve WebM only (modern browsers) +- Lazy-load audio on demand + +### Issue 5: CORS Errors + +**Problem:** Widget doesn't load from other domains +**Solution:** +- Add `.htaccess` CORS headers (see above) +- Test with browser dev tools +- Ensure ProISP doesn't override headers + +--- + +## 📊 Quick Comparison + +| Solution | Cost/Month | Complexity | Node.js | Auto-Deploy | CORS | Speed | +|----------|-----------|------------|---------|-------------|------|-------| +| **Static Export (ProISP)** | **50-100 kr** | ⭐ Low | ❌ No | Manual | ✅ Yes | ⚡ Fast | +| cPanel Node.js | 150-300 kr | ⭐⭐⭐ High | ✅ Yes | Manual | ✅ Yes | 🐢 Slow | +| Git + Build | 150-300 kr | ⭐⭐⭐ High | ✅ Yes | ✅ Auto | ✅ Yes | ⚡ Fast | +| **Vercel** | **FREE** | ⭐ Low | ✅ Yes | ✅ Auto | ✅ Yes | ⚡⚡ Very Fast | +| CDN + Cloudflare | 50-100 kr | ⭐⭐⭐⭐ Very High | ❌ No | Manual | ✅ Yes | ⚡⚡⚡ Ultra Fast | + +--- + +## 🚀 Final Recommendation + +### For ProISP Shared Hosting: +**Use Option 1: Static Export** ⭐ + +**Steps:** +1. Run `npm run build && npm run export` +2. Upload `out/` folder to ProISP via cPanel File Manager +3. Add `.htaccess` with CORS headers +4. Point DNS to ProISP server +5. Test widget embedding from salsanor.no + +**Pros:** Simple, cheap, works everywhere +**Cons:** Manual updates required + +--- + +### Alternative: Vercel (Highly Recommended) ⭐⭐⭐ + +If you want: +- Automatic deployments on Git push +- Zero configuration +- Free hosting +- Global CDN + +**Then use Vercel:** +```bash +npm i -g vercel +vercel login +vercel --prod +vercel domains add beat.salsanor.no +``` + +**Cost:** FREE (sufficient for this project) +**Time to deploy:** 5 minutes +**Updates:** Push to Git, auto-deploys + +--- + +## 📝 Next Steps + +1. **Verify ProISP supports:** + - Custom .htaccess + - CORS headers + - Large file uploads (50 MB) + +2. **Choose deployment method:** + - Static export for simplicity + - Vercel for best developer experience + +3. **Test audio file delivery:** + - Check gzip compression works + - Verify CORS headers + - Test load times from Norway + +4. **Set up continuous deployment:** + - Git repository + - Build script + - Upload automation (optional) + +--- + +**Questions to answer:** +1. Does ProISP hosting support Node.js apps? (probably no) +2. Is FTP/SSH access available? (probably FTP only) +3. Are there bandwidth limits? (check if audio files count) +4. Can .htaccess override server config? (needed for CORS) + +**Recommended:** Start with **Static Export** to ProISP. It's the safest, simplest option that works on any host. If you need automatic deployments later, migrate to Vercel (very easy). diff --git a/docs/DEPLOYMENT_STATIC.md b/docs/DEPLOYMENT_STATIC.md new file mode 100644 index 0000000..9194833 --- /dev/null +++ b/docs/DEPLOYMENT_STATIC.md @@ -0,0 +1,338 @@ +# Deployment Guide - Static Export to Shared Hosting + +This guide explains how to deploy the SalsaNor Beat Machine to shared hosting (ProISP, One.com, etc.) using Next.js static export. + +## 📋 Prerequisites + +- Node.js 20.x installed locally +- FTP/SFTP access to your web host OR cPanel File Manager +- Domain configured: `beat.salsanor.no` + +## 🚀 Quick Start + +### 1. Build the Static Site + +```bash +# Install dependencies (if not already done) +npm install + +# Build static files for production +npm run deploy + +# Optional: Preview locally before uploading +npm run deploy:preview +``` + +This creates an `out/` folder containing all static files ready for deployment. + +## 📁 What Gets Built + +The `out/` folder contains: + +``` +out/ +├── index.html # Main page +├── widget-generator.html # Widget generator page +├── widget-demo.html # Widget demo page +├── 404.html # 404 error page +├── _next/ # Next.js assets (CSS, JS) +│ ├── static/ +│ └── ... +├── assets/ # Audio files and resources +│ ├── audio/ +│ │ ├── main.webm (~2.8 MB) +│ │ ├── main.mp3 (~2.8 MB) +│ │ └── main.json +│ ├── machines/ # XML configurations +│ └── instruments/ # Instrument icons +└── ... +``` + +**Total size:** ~50-60 MB + +## 📤 Upload to ProISP (or any shared host) + +### Option A: cPanel File Manager + +1. **Login to cPanel** + - Navigate to your hosting control panel + - Click "File Manager" + +2. **Navigate to web root** + - Usually `public_html` or `www` + - For subdomain: `public_html/beat` or similar + +3. **Upload files** + - Click "Upload" + - Drag the entire contents of `out/` folder + - OR select files and upload + - Wait for completion (may take 5-10 minutes due to audio files) + +4. **Copy .htaccess** + - Upload `public/.htaccess` to the root of your domain + - This enables CORS and caching + +### Option B: FTP (FileZilla) + +1. **Connect via FTP** + ``` + Host: ftp.yourdomain.com + Username: your_username + Password: your_password + Port: 21 + ``` + +2. **Navigate to web root** + - Remote site: `/public_html` or `/www` + +3. **Upload files** + - Drag contents of `out/` folder to remote site + - Upload `public/.htaccess` to root + - Set file permissions: 644 for files, 755 for folders + +### Option C: SFTP/rsync (if SSH available) + +```bash +# Upload via rsync (fastest) +rsync -avz --delete out/ user@host:/path/to/public_html/ + +# Copy .htaccess +scp public/.htaccess user@host:/path/to/public_html/.htaccess +``` + +## 🔧 Configuration + +### .htaccess (Already included) + +The `.htaccess` file in `public/` folder provides: + +✅ **CORS headers** - Required for widget embedding +✅ **Compression** - Reduces bandwidth by ~60% +✅ **Caching** - Faster load times (1 year for audio, 1 hour for HTML) +✅ **Security headers** - XSS protection, clickjacking prevention +✅ **Correct MIME types** - Ensures browsers handle files correctly + +**Important:** Make sure your host allows `.htaccess` overrides. Most shared hosts do by default. + +### DNS Configuration + +Point your domain to the hosting server: + +``` +Type: A Record +Name: beat (or @) +Value: [Your ProISP server IP] +TTL: 3600 +``` + +Wait 5-60 minutes for DNS propagation. + +### SSL Certificate (Recommended) + +Most hosts offer free SSL via Let's Encrypt in cPanel: + +1. Go to cPanel > SSL/TLS > Manage SSL +2. Click "Issue Certificate" for your domain +3. Wait 5-10 minutes for activation +4. Uncomment HTTPS redirect in `.htaccess` + +## ✅ Verify Deployment + +### 1. Test Main Site + +Visit: `https://beat.salsanor.no` + +You should see the beat machine interface. + +### 2. Test Audio Loading + +Open browser console (F12) and check: +- No 404 errors for audio files +- `main.json` loads correctly +- Audio plays when clicking instruments + +### 3. Test CORS for Widget + +From a different domain (e.g., `salsanor.no`), run in console: + +```javascript +fetch('https://beat.salsanor.no/assets/audio/main.json') + .then(r => r.json()) + .then(data => console.log('CORS working!', Object.keys(data).length + ' audio samples')) + .catch(err => console.error('CORS error:', err)); +``` + +Should print: `CORS working! 227 audio samples` + +### 4. Test Widget Embedding + +On `salsanor.no`, add this HTML: + +```html +
+ + +``` + +Widget should load and play audio. + +## 🔄 Updating the Site + +When you make changes: + +```bash +# 1. Make your code changes +# 2. Build again +npm run deploy + +# 3. Upload only changed files: +# - Upload modified files from out/ folder +# - Usually just _next/static/*.js and HTML files +# - Audio files rarely change, no need to re-upload +``` + +**Tip:** Use FTP client's "sync" feature to only upload changed files. + +## 📊 Performance Tips + +### 1. Enable Compression + +Already configured in `.htaccess`. Verify it's working: + +```bash +curl -H "Accept-Encoding: gzip" -I https://beat.salsanor.no +# Should return: Content-Encoding: gzip +``` + +### 2. Cache Audio Files + +Audio files are cached for 1 year. Clear browser cache to force reload during testing: + +- Chrome: Ctrl+Shift+R (Cmd+Shift+R on Mac) +- Firefox: Ctrl+F5 + +### 3. Use WebM Only (Optional) + +Modern browsers support WebM. To reduce size, consider removing MP3: + +1. Delete `public/assets/audio/main.mp3` +2. Update audio loading logic to use WebM only +3. Saves ~2.8 MB + +## 🐛 Troubleshooting + +### Issue: "404 Not Found" for pages + +**Cause:** HTML files not uploaded or wrong directory +**Solution:** +- Verify `index.html` exists in web root +- Check file permissions (644) +- Ensure trailing slashes in URLs: `/widget-generator/` + +### Issue: Audio doesn't play + +**Cause:** MIME types not set or CORS blocked +**Solution:** +- Verify `.htaccess` is uploaded and active +- Check server error logs in cPanel +- Test audio URL directly: `https://beat.salsanor.no/assets/audio/main.webm` + +### Issue: Widget doesn't load on other domains + +**Cause:** CORS headers missing +**Solution:** +- Verify `.htaccess` CORS headers are active +- Test with `curl -I` command +- Check browser console for CORS errors + +### Issue: Site works locally but not on server + +**Cause:** Base path or asset path mismatch +**Solution:** +- Check `next.config.js` - ensure `basePath` is correct +- Verify asset URLs in HTML start with `/` or full domain +- Check browser console for 404 errors + +### Issue: Large upload takes forever + +**Cause:** 50 MB upload over slow connection +**Solution:** +- Use FTP/SFTP instead of web uploader +- Upload audio files separately (one-time only) +- Consider compressing with zip and extracting on server (if SSH available) + +### Issue: Page looks broken/unstyled + +**Cause:** CSS not loading +**Solution:** +- Check network tab for 404 errors on CSS files +- Verify `_next/static/` folder uploaded correctly +- Clear browser cache and reload + +## 🔐 Security Checklist + +- [ ] `.htaccess` uploaded and active +- [ ] Directory browsing disabled (`Options -Indexes`) +- [ ] SSL certificate installed +- [ ] HTTPS redirect enabled (uncomment in `.htaccess`) +- [ ] Security headers active (X-Content-Type-Options, etc.) +- [ ] No sensitive data in public files + +## 📈 Monitoring + +### Bandwidth Usage + +Audio files are heavy. Monitor bandwidth in cPanel: +- Expected: ~5-10 GB/month for moderate traffic +- If exceeding: Consider CDN (Cloudflare) + +### Error Logs + +Check cPanel error logs for: +- 404 errors (missing files) +- 500 errors (server configuration issues) +- CORS errors (cross-domain problems) + +### Analytics (Optional) + +Add Google Analytics or similar to track: +- Page views +- Widget embeds +- Audio plays +- Geographic distribution + +## 🆘 Support + +If deployment fails: + +1. **Check host compatibility:** + - Does it support `.htaccess`? + - Is `mod_rewrite` enabled? + - Is `mod_headers` enabled? + +2. **Contact ProISP support:** + - Ask about `.htaccess` support + - Request CORS header configuration + - Ask about Node.js (if considering server-side) + +3. **Alternative: Use Vercel (free)** + ```bash + npm i -g vercel + vercel login + vercel --prod + ``` + +## 📚 Additional Resources + +- [Next.js Static Export Documentation](https://nextjs.org/docs/app/building-your-application/deploying/static-exports) +- [Apache .htaccess Guide](https://httpd.apache.org/docs/current/howto/htaccess.html) +- [CORS Configuration](https://enable-cors.org/server_apache.html) +- Main deployment options: See [DEPLOYMENT_OPTIONS.md](./DEPLOYMENT_OPTIONS.md) + +--- + +**Need help?** Check the full deployment analysis in [DEPLOYMENT_OPTIONS.md](./DEPLOYMENT_OPTIONS.md) for alternative hosting solutions including Vercel (free, recommended for automatic deployments). diff --git a/docs/IMPLEMENTATION_GUIDE.md b/docs/IMPLEMENTATION_GUIDE.md new file mode 100644 index 0000000..ac50012 --- /dev/null +++ b/docs/IMPLEMENTATION_GUIDE.md @@ -0,0 +1,826 @@ +# Backend Implementation Guide + +## Overview + +This guide provides practical steps for implementing the backend architecture for the SalsaNor Beat Machine. It includes code examples, configuration files, and deployment instructions. + +## Prerequisites + +- Node.js 18+ and npm +- PostgreSQL 14+ or access to a managed database service +- Redis (optional, for caching) +- Basic understanding of Next.js, TypeScript, and REST APIs + +## Project Structure + +``` +/ +├── pages/ +│ ├── api/ # API routes (Next.js API Routes) +│ │ ├── auth/ +│ │ │ ├── register.ts +│ │ │ ├── login.ts +│ │ │ └── logout.ts +│ │ ├── users/ +│ │ │ ├── me.ts +│ │ │ └── [userId].ts +│ │ ├── patterns/ +│ │ │ ├── index.ts +│ │ │ ├── [patternId].ts +│ │ │ └── [patternId]/ +│ │ │ ├── like.ts +│ │ │ └── comments.ts +│ │ └── analytics/ +│ │ └── popular-instruments.ts +│ └── ...existing frontend pages +├── lib/ +│ ├── prisma.ts # Prisma client instance +│ ├── auth.ts # Authentication utilities +│ ├── redis.ts # Redis client (optional) +│ └── validations.ts # Input validation schemas +├── services/ +│ ├── user.service.ts # User business logic +│ ├── pattern.service.ts # Pattern business logic +│ ├── auth.service.ts # Auth business logic +│ └── analytics.service.ts # Analytics business logic +├── middleware/ +│ ├── auth.middleware.ts # JWT verification +│ └── ratelimit.middleware.ts +├── prisma/ +│ ├── schema.prisma # Database schema +│ ├── migrations/ # Database migrations +│ └── seed.ts # Seed data +├── types/ +│ └── api.ts # API types +└── .env.local # Environment variables +``` + +## Step 1: Install Dependencies + +```bash +# Core dependencies +npm install @prisma/client bcrypt jsonwebtoken +npm install --save-dev prisma @types/bcrypt @types/jsonwebtoken + +# Validation and utilities +npm install zod + +# Optional: Redis for caching +npm install redis +npm install --save-dev @types/redis +``` + +## Step 2: Set Up Prisma + +### Initialize Prisma + +```bash +npx prisma init +``` + +### Configure Database Connection + +Create/update `.env.local`: + +```env +# Database +DATABASE_URL="postgresql://username:password@localhost:5432/salsabeatmachine?schema=public" + +# JWT Secret (generate with: openssl rand -base64 32) +JWT_SECRET="your-super-secret-jwt-key-change-this-in-production" + +# Optional: Redis +REDIS_URL="redis://localhost:6379" + +# App Config +NODE_ENV="development" +NEXT_PUBLIC_API_URL="http://localhost:3009/api" +``` + +### Create Prisma Schema + +Copy the schema from `DATABASE_SCHEMA.md` into `prisma/schema.prisma`, starting with a minimal version: + +```prisma +// prisma/schema.prisma +generator client { + provider = "prisma-client-js" +} + +datasource db { + provider = "postgresql" + url = env("DATABASE_URL") +} + +model User { + id String @id @default(uuid()) + email String @unique + passwordHash String? @map("password_hash") + displayName String @map("display_name") + avatarUrl String? @map("avatar_url") + bio String? + emailVerified Boolean @default(false) @map("email_verified") + isActive Boolean @default(true) @map("is_active") + isAdmin Boolean @default(false) @map("is_admin") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + lastLoginAt DateTime? @map("last_login_at") + + preferences UserPreference? + patterns Pattern[] + likes Like[] + comments Comment[] + + @@index([email]) + @@map("users") +} + +model UserPreference { + id String @id @default(uuid()) + userId String @unique @map("user_id") + defaultBpm Int @default(120) @map("default_bpm") + defaultKeyNote Int @default(0) @map("default_key_note") + defaultFlavor String @default("Salsa") @map("default_flavor") + useGlassUI Boolean @default(true) @map("use_glass_ui") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + + @@map("user_preferences") +} + +model Pattern { + id String @id @default(uuid()) + userId String @map("user_id") + title String + description String? + flavor String + bpm Int + keyNote Int @map("key_note") + isPublic Boolean @default(true) @map("is_public") + configuration Json + playCount Int @default(0) @map("play_count") + likeCount Int @default(0) @map("like_count") + commentCount Int @default(0) @map("comment_count") + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + likes Like[] + comments Comment[] + + @@index([userId]) + @@index([isPublic]) + @@index([createdAt(sort: Desc)]) + @@map("patterns") +} + +model Like { + id String @id @default(uuid()) + userId String @map("user_id") + patternId String @map("pattern_id") + createdAt DateTime @default(now()) @map("created_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pattern Pattern @relation(fields: [patternId], references: [id], onDelete: Cascade) + + @@unique([userId, patternId]) + @@map("likes") +} + +model Comment { + id String @id @default(uuid()) + userId String @map("user_id") + patternId String @map("pattern_id") + text String + createdAt DateTime @default(now()) @map("created_at") + updatedAt DateTime @updatedAt @map("updated_at") + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + pattern Pattern @relation(fields: [patternId], references: [id], onDelete: Cascade) + + @@index([patternId]) + @@map("comments") +} +``` + +### Run Migration + +```bash +npx prisma migrate dev --name init +npx prisma generate +``` + +## Step 3: Create Core Utilities + +### Prisma Client Singleton + +```typescript +// lib/prisma.ts +import { PrismaClient } from '@prisma/client'; + +declare global { + var prisma: PrismaClient | undefined; +} + +export const prisma = global.prisma || new PrismaClient(); + +if (process.env.NODE_ENV !== 'production') { + global.prisma = prisma; +} +``` + +### Authentication Utilities + +```typescript +// lib/auth.ts +import bcrypt from 'bcrypt'; +import jwt from 'jsonwebtoken'; + +const JWT_SECRET = process.env.JWT_SECRET!; +const SALT_ROUNDS = 10; + +export interface JWTPayload { + userId: string; + email: string; +} + +export async function hashPassword(password: string): Promise { + return bcrypt.hash(password, SALT_ROUNDS); +} + +export async function verifyPassword(password: string, hash: string): Promise { + return bcrypt.compare(password, hash); +} + +export function generateToken(payload: JWTPayload): string { + return jwt.sign(payload, JWT_SECRET, { expiresIn: '30d' }); +} + +export function verifyToken(token: string): JWTPayload { + return jwt.verify(token, JWT_SECRET) as JWTPayload; +} + +export function extractTokenFromHeader(authHeader: string | undefined): string | null { + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return null; + } + return authHeader.substring(7); +} +``` + +### Input Validation with Zod + +```typescript +// lib/validations.ts +import { z } from 'zod'; + +export const registerSchema = z.object({ + email: z.string().email('Invalid email address'), + password: z.string().min(8, 'Password must be at least 8 characters'), + displayName: z.string().min(2, 'Display name must be at least 2 characters').max(100), + acceptTerms: z.boolean().refine((val) => val === true, 'You must accept the terms'), +}); + +export const loginSchema = z.object({ + email: z.string().email('Invalid email address'), + password: z.string().min(1, 'Password is required'), +}); + +export const createPatternSchema = z.object({ + title: z.string().min(1).max(200), + description: z.string().max(5000).optional(), + flavor: z.enum(['Salsa', 'Merengue']), + bpm: z.number().int().min(80).max(250), + keyNote: z.number().int().min(0).max(11), + isPublic: z.boolean().default(true), + configuration: z.object({ + instruments: z.array(z.object({ + id: z.string(), + enabled: z.boolean(), + activeProgram: z.number().int().min(0), + volume: z.number().min(0).max(1), + pitchOffset: z.number().int().optional(), + })), + }), +}); + +export const updateUserSchema = z.object({ + displayName: z.string().min(2).max(100).optional(), + bio: z.string().max(500).optional(), + avatar: z.string().url().optional(), +}); + +export const updatePreferencesSchema = z.object({ + defaultBpm: z.number().int().min(80).max(250).optional(), + defaultKeyNote: z.number().int().min(0).max(11).optional(), + defaultFlavor: z.enum(['Salsa', 'Merengue']).optional(), + useGlassUI: z.boolean().optional(), +}); +``` + +## Step 4: Create API Response Utilities + +```typescript +// lib/api-response.ts +import { NextApiResponse } from 'next'; + +export function successResponse(res: NextApiResponse, data: T, statusCode = 200) { + return res.status(statusCode).json({ + success: true, + data, + }); +} + +export function errorResponse( + res: NextApiResponse, + code: string, + message: string, + statusCode = 400, + details?: any +) { + return res.status(statusCode).json({ + success: false, + error: { + code, + message, + ...(details && { details }), + }, + }); +} +``` + +## Step 5: Create Authentication Middleware + +```typescript +// middleware/auth.middleware.ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { extractTokenFromHeader, verifyToken } from '../lib/auth'; +import { errorResponse } from '../lib/api-response'; +import { prisma } from '../lib/prisma'; + +export interface AuthenticatedRequest extends NextApiRequest { + user?: { + id: string; + email: string; + displayName: string; + isAdmin: boolean; + }; +} + +export function withAuth( + handler: (req: AuthenticatedRequest, res: NextApiResponse) => Promise +) { + return async (req: AuthenticatedRequest, res: NextApiResponse) => { + try { + const token = extractTokenFromHeader(req.headers.authorization); + + if (!token) { + return errorResponse(res, 'AUTH_REQUIRED', 'Authentication required', 401); + } + + const payload = verifyToken(token); + + const user = await prisma.user.findUnique({ + where: { id: payload.userId }, + select: { id: true, email: true, displayName: true, isAdmin: true, isActive: true }, + }); + + if (!user || !user.isActive) { + return errorResponse(res, 'USER_NOT_FOUND', 'User not found or inactive', 401); + } + + req.user = user; + return handler(req, res); + } catch (error) { + return errorResponse(res, 'INVALID_TOKEN', 'Invalid or expired token', 401); + } + }; +} +``` + +## Step 6: Create API Endpoints + +### Register Endpoint + +```typescript +// pages/api/auth/register.ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { prisma } from '../../../lib/prisma'; +import { hashPassword, generateToken } from '../../../lib/auth'; +import { successResponse, errorResponse } from '../../../lib/api-response'; +import { registerSchema } from '../../../lib/validations'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') { + return res.status(405).json({ message: 'Method not allowed' }); + } + + try { + const validation = registerSchema.safeParse(req.body); + + if (!validation.success) { + return errorResponse(res, 'VALIDATION_ERROR', 'Invalid input', 400, validation.error.errors); + } + + const { email, password, displayName } = validation.data; + + // Check if user exists + const existingUser = await prisma.user.findUnique({ where: { email } }); + if (existingUser) { + return errorResponse(res, 'USER_EXISTS', 'User with this email already exists', 400); + } + + // Create user + const passwordHash = await hashPassword(password); + const user = await prisma.user.create({ + data: { + email, + passwordHash, + displayName, + preferences: { + create: {}, + }, + }, + select: { + id: true, + email: true, + displayName: true, + createdAt: true, + }, + }); + + // Generate token + const token = generateToken({ userId: user.id, email: user.email }); + + return successResponse(res, { user, token }, 201); + } catch (error) { + console.error('Registration error:', error); + return errorResponse(res, 'SERVER_ERROR', 'An error occurred during registration', 500); + } +} +``` + +### Login Endpoint + +```typescript +// pages/api/auth/login.ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { prisma } from '../../../lib/prisma'; +import { verifyPassword, generateToken } from '../../../lib/auth'; +import { successResponse, errorResponse } from '../../../lib/api-response'; +import { loginSchema } from '../../../lib/validations'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + if (req.method !== 'POST') { + return res.status(405).json({ message: 'Method not allowed' }); + } + + try { + const validation = loginSchema.safeParse(req.body); + + if (!validation.success) { + return errorResponse(res, 'VALIDATION_ERROR', 'Invalid input', 400, validation.error.errors); + } + + const { email, password } = validation.data; + + // Find user + const user = await prisma.user.findUnique({ + where: { email }, + select: { + id: true, + email: true, + displayName: true, + passwordHash: true, + isActive: true, + }, + }); + + if (!user || !user.passwordHash) { + return errorResponse(res, 'INVALID_CREDENTIALS', 'Invalid email or password', 401); + } + + if (!user.isActive) { + return errorResponse(res, 'USER_INACTIVE', 'User account is inactive', 401); + } + + // Verify password + const isValidPassword = await verifyPassword(password, user.passwordHash); + if (!isValidPassword) { + return errorResponse(res, 'INVALID_CREDENTIALS', 'Invalid email or password', 401); + } + + // Update last login + await prisma.user.update({ + where: { id: user.id }, + data: { lastLoginAt: new Date() }, + }); + + // Generate token + const token = generateToken({ userId: user.id, email: user.email }); + + return successResponse(res, { + user: { + id: user.id, + email: user.email, + displayName: user.displayName, + }, + token, + }); + } catch (error) { + console.error('Login error:', error); + return errorResponse(res, 'SERVER_ERROR', 'An error occurred during login', 500); + } +} +``` + +### Get Current User + +```typescript +// pages/api/users/me.ts +import { NextApiResponse } from 'next'; +import { prisma } from '../../../lib/prisma'; +import { successResponse, errorResponse } from '../../../lib/api-response'; +import { withAuth, AuthenticatedRequest } from '../../../middleware/auth.middleware'; + +async function handler(req: AuthenticatedRequest, res: NextApiResponse) { + if (req.method === 'GET') { + try { + const user = await prisma.user.findUnique({ + where: { id: req.user!.id }, + include: { + preferences: true, + _count: { + select: { + patterns: true, + likes: true, + }, + }, + }, + }); + + if (!user) { + return errorResponse(res, 'USER_NOT_FOUND', 'User not found', 404); + } + + return successResponse(res, { + id: user.id, + email: user.email, + displayName: user.displayName, + avatarUrl: user.avatarUrl, + bio: user.bio, + preferences: user.preferences, + stats: { + patternsCreated: user._count.patterns, + patternsLiked: user._count.likes, + }, + createdAt: user.createdAt, + updatedAt: user.updatedAt, + }); + } catch (error) { + console.error('Get user error:', error); + return errorResponse(res, 'SERVER_ERROR', 'An error occurred', 500); + } + } + + return res.status(405).json({ message: 'Method not allowed' }); +} + +export default withAuth(handler); +``` + +### Create Pattern + +```typescript +// pages/api/patterns/index.ts +import { NextApiRequest, NextApiResponse } from 'next'; +import { prisma } from '../../../lib/prisma'; +import { successResponse, errorResponse } from '../../../lib/api-response'; +import { withAuth, AuthenticatedRequest } from '../../../middleware/auth.middleware'; +import { createPatternSchema } from '../../../lib/validations'; + +async function handler(req: AuthenticatedRequest, res: NextApiResponse) { + if (req.method === 'POST') { + try { + const validation = createPatternSchema.safeParse(req.body); + + if (!validation.success) { + return errorResponse(res, 'VALIDATION_ERROR', 'Invalid input', 400, validation.error.errors); + } + + // TODO: Add rate limiting - check pattern creation count in last hour + // Example: Limit to 20 patterns per hour as documented in API_SPECIFICATION.md + // const recentPatterns = await prisma.pattern.count({ + // where: { + // userId: req.user!.id, + // createdAt: { gte: new Date(Date.now() - 3600000) } + // } + // }); + // if (recentPatterns >= 20) { + // return errorResponse(res, 'RATE_LIMIT_EXCEEDED', 'Too many patterns created recently', 429); + // } + + const pattern = await prisma.pattern.create({ + data: { + ...validation.data, + userId: req.user!.id, + }, + }); + + return successResponse(res, pattern, 201); + } catch (error) { + console.error('Create pattern error:', error); + return errorResponse(res, 'SERVER_ERROR', 'An error occurred', 500); + } + } + + if (req.method === 'GET') { + try { + const { page = '1', limit = '20', flavor, sort = 'recent' } = req.query; + const pageNum = parseInt(page as string); + const limitNum = Math.min(parseInt(limit as string), 100); + const skip = (pageNum - 1) * limitNum; + + const where: { isPublic: boolean; flavor?: string } = { isPublic: true }; + if (flavor) where.flavor = flavor as string; + + type OrderBy = { likeCount?: 'desc'; playCount?: 'desc'; createdAt?: 'desc' }; + const orderBy: OrderBy = {}; + if (sort === 'popular') orderBy.likeCount = 'desc'; + else if (sort === 'played') orderBy.playCount = 'desc'; + else orderBy.createdAt = 'desc'; + + const [patterns, total] = await Promise.all([ + prisma.pattern.findMany({ + where, + orderBy, + skip, + take: limitNum, + include: { + user: { + select: { id: true, displayName: true, avatarUrl: true }, + }, + }, + }), + prisma.pattern.count({ where }), + ]); + + return successResponse(res, { + patterns, + pagination: { + page: pageNum, + limit: limitNum, + total, + pages: Math.ceil(total / limitNum), + }, + }); + } catch (error) { + console.error('List patterns error:', error); + return errorResponse(res, 'SERVER_ERROR', 'An error occurred', 500); + } + } + + return res.status(405).json({ message: 'Method not allowed' }); +} + +export default withAuth(handler); +``` + +## Step 7: Frontend Integration + +### Create API Client + +```typescript +// lib/api-client.ts +import axios from 'axios'; + +const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || '/api'; + +export const apiClient = axios.create({ + baseURL: API_BASE_URL, +}); + +// Add auth token to requests +apiClient.interceptors.request.use((config) => { + const token = localStorage.getItem('authToken'); + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } + return config; +}); + +// Auth API +export const authAPI = { + register: (data: { email: string; password: string; displayName: string; acceptTerms: boolean }) => + apiClient.post('/auth/register', data), + login: (email: string, password: string) => + apiClient.post('/auth/login', { email, password }), + logout: () => apiClient.post('/auth/logout'), +}; + +// User API +export const userAPI = { + getMe: () => apiClient.get('/users/me'), + updateMe: (data: { displayName?: string; bio?: string }) => + apiClient.put('/users/me', data), + updatePreferences: (preferences: any) => + apiClient.put('/users/me/preferences', preferences), +}; + +// Pattern API +export const patternAPI = { + list: (params?: { page?: number; limit?: number; flavor?: string; sort?: string }) => + apiClient.get('/patterns', { params }), + get: (patternId: string) => apiClient.get(`/patterns/${patternId}`), + create: (data: any) => apiClient.post('/patterns', data), + update: (patternId: string, data: any) => apiClient.put(`/patterns/${patternId}`, data), + delete: (patternId: string) => apiClient.delete(`/patterns/${patternId}`), + like: (patternId: string) => apiClient.post(`/patterns/${patternId}/like`), + unlike: (patternId: string) => apiClient.delete(`/patterns/${patternId}/like`), +}; +``` + +## Step 8: Testing + +Create tests for your API endpoints: + +```bash +npm install --save-dev jest @types/jest ts-jest supertest @types/supertest +``` + +```typescript +// __tests__/api/auth/register.test.ts +import { createMocks } from 'node-mocks-http'; +import handler from '../../../pages/api/auth/register'; + +describe('/api/auth/register', () => { + it('should register a new user', async () => { + const { req, res } = createMocks({ + method: 'POST', + body: { + email: 'test@example.com', + password: 'password123', + displayName: 'Test User', + acceptTerms: true, + }, + }); + + await handler(req, res); + + expect(res._getStatusCode()).toBe(201); + const data = JSON.parse(res._getData()); + expect(data.success).toBe(true); + expect(data.data.user.email).toBe('test@example.com'); + }); +}); +``` + +## Step 9: Deployment + +### Vercel Deployment + +1. Push code to GitHub +2. Connect repository to Vercel +3. Add environment variables in Vercel dashboard +4. Deploy + +### Database Setup (Production) + +**Option 1: Supabase** +```bash +# Create project at supabase.com +# Copy connection string +# Add to Vercel environment variables +``` + +**Option 2: PlanetScale** +```bash +# Create database at planetscale.com +# Get connection string +# Add to Vercel environment variables +``` + +### Environment Variables (Production) + +```env +DATABASE_URL="postgresql://..." +JWT_SECRET="production-secret-key" +NODE_ENV="production" +``` + +## Next Steps + +1. Implement rate limiting +2. Add email verification +3. Set up monitoring (Sentry) +4. Add API documentation (Swagger) +5. Implement caching with Redis +6. Add comprehensive tests +7. Set up CI/CD pipeline + +## Resources + +- [Next.js API Routes](https://nextjs.org/docs/api-routes/introduction) +- [Prisma Documentation](https://www.prisma.io/docs/) +- [Vercel Deployment](https://vercel.com/docs) +- [PostgreSQL Best Practices](https://wiki.postgresql.org/wiki/Don%27t_Do_This) diff --git a/docs/QUICK_START.md b/docs/QUICK_START.md new file mode 100644 index 0000000..00d3802 --- /dev/null +++ b/docs/QUICK_START.md @@ -0,0 +1,283 @@ +# Backend Quick Start Guide + +This is a streamlined guide to get you started with adding backend functionality to the SalsaNor Beat Machine. For more detailed information, see the complete [Implementation Guide](./IMPLEMENTATION_GUIDE.md). + +## Prerequisites + +✅ Node.js 18 or higher +✅ PostgreSQL 14 or higher (or a managed service like Supabase) +✅ Basic understanding of Next.js and TypeScript + +## Quick Setup (5 Minutes) + +### 1. Install Dependencies + +```bash +npm install @prisma/client bcrypt jsonwebtoken zod +npm install --save-dev prisma @types/bcrypt @types/jsonwebtoken +``` + +### 2. Initialize Database + +```bash +# Initialize Prisma +npx prisma init + +# This creates: +# - prisma/schema.prisma (database schema) +# - .env (environment variables) +``` + +### 3. Configure Environment + +Copy `.env.example` from the project root to `.env.local` and update: + +```env +DATABASE_URL="postgresql://username:password@localhost:5432/salsabeatmachine" +JWT_SECRET="your-super-secret-key-here" +``` + +Generate a secure JWT secret: +```bash +openssl rand -base64 32 +``` + +### 4. Set Up Database Schema + +Copy the minimal schema from `docs/DATABASE_SCHEMA.md` into `prisma/schema.prisma`, then run: + +```bash +# Create database tables +npx prisma migrate dev --name init + +# Generate Prisma client +npx prisma generate +``` + +### 5. Create Your First API Endpoint + +Create `pages/api/health.ts`: + +```typescript +import { NextApiRequest, NextApiResponse } from 'next'; + +export default async function handler(req: NextApiRequest, res: NextApiResponse) { + return res.status(200).json({ + success: true, + message: 'Backend API is running!', + timestamp: new Date().toISOString() + }); +} +``` + +Test it: http://localhost:3009/api/health + +> **Note:** This app uses port 3009 (configured in package.json), not the default Next.js port 3000. + +### 6. Add Authentication + +Create the files from the Implementation Guide: +- `lib/prisma.ts` - Database client +- `lib/auth.ts` - Auth utilities +- `pages/api/auth/register.ts` - Registration endpoint +- `pages/api/auth/login.ts` - Login endpoint + +### 7. Test Authentication + +```bash +# Register a new user +curl -X POST http://localhost:3009/api/auth/register \ + -H "Content-Type: application/json" \ + -d '{ + "email": "test@example.com", + "password": "password123", + "displayName": "Test User", + "acceptTerms": true + }' + +# Login +curl -X POST http://localhost:3009/api/auth/login \ + -H "Content-Type: application/json" \ + -d '{ + "email": "test@example.com", + "password": "password123" + }' +``` + +## What's Next? + +### Week 1-2: Core Backend +- ✅ Authentication (register, login, logout) +- ✅ User profiles +- ✅ Pattern CRUD operations + +### Week 3-4: Features +- 🔲 Pattern sharing (public URLs) +- 🔲 Like/favorite system +- 🔲 User preferences + +### Week 5-6: Social +- 🔲 Comments +- 🔲 User following +- 🔲 Activity feed + +### Week 7-8: Polish +- 🔲 Analytics +- 🔲 Search and discovery +- 🔲 Performance optimization + +## Common Commands + +```bash +# Database +npx prisma migrate dev # Create and apply migration +npx prisma generate # Regenerate Prisma client +npx prisma studio # Open database GUI +npx prisma db push # Sync schema without migration + +# Development +npm run dev # Start dev server +npm run build # Build for production +npm run start # Start production server + +# Testing +npm run test # Run tests +npm run test:watch # Watch mode +``` + +## Project Structure + +``` +/ +├── pages/api/ # API endpoints +│ ├── auth/ # Authentication +│ ├── users/ # User management +│ ├── patterns/ # Pattern CRUD +│ └── analytics/ # Analytics +├── lib/ # Utilities +│ ├── prisma.ts # DB client +│ ├── auth.ts # Auth utils +│ └── validations.ts # Input validation +├── middleware/ # Middleware +│ └── auth.middleware.ts +├── prisma/ # Database +│ ├── schema.prisma # Schema +│ └── migrations/ # Migrations +└── docs/ # Documentation +``` + +## Useful Resources + +### Documentation +- [Backend Summary](./BACKEND_SUMMARY.md) - Feature overview +- [API Specification](./API_SPECIFICATION.md) - All endpoints +- [Database Schema](./DATABASE_SCHEMA.md) - Data model +- [Implementation Guide](./IMPLEMENTATION_GUIDE.md) - Detailed guide +- [Backend Architecture](./BACKEND_ARCHITECTURE.md) - Architecture + +### External Resources +- [Prisma Docs](https://www.prisma.io/docs) - Database ORM +- [Next.js API Routes](https://nextjs.org/docs/api-routes/introduction) +- [JWT.io](https://jwt.io/) - JWT debugger +- [Zod](https://zod.dev/) - Schema validation + +## Deployment Options + +### Option 1: Vercel (Easiest) +```bash +# Deploy to Vercel +vercel + +# Add environment variables in Vercel dashboard +# Use Supabase or PlanetScale for database +``` + +### Option 2: Railway +```bash +# Install Railway CLI +npm install -g railway + +# Deploy +railway login +railway init +railway up +``` + +### Option 3: Docker +```dockerfile +# Dockerfile +FROM node:18-alpine +WORKDIR /app +COPY package*.json ./ +RUN npm ci --only=production +COPY . . +RUN npx prisma generate +RUN npm run build +CMD ["npm", "start"] +``` + +## Troubleshooting + +### "Module not found: @prisma/client" +```bash +npx prisma generate +``` + +### "Connection refused" database errors +Check your DATABASE_URL in .env.local + +### JWT verification fails +Ensure JWT_SECRET is the same everywhere + +### Prisma schema changes not applied +```bash +npx prisma generate +npx prisma migrate dev +``` + +### Port 3009 already in use +```bash +# Kill process on port 3009 +lsof -ti:3009 | xargs kill -9 + +# Or use a different port +npm run dev -- -p 3010 +``` + +## Getting Help + +1. Check the [Implementation Guide](./IMPLEMENTATION_GUIDE.md) for detailed steps +2. Review the [API Specification](./API_SPECIFICATION.md) for endpoint details +3. Look at the [Database Schema](./DATABASE_SCHEMA.md) for data structure +4. Open an issue on GitHub if you're stuck + +## Security Checklist + +Before deploying to production: + +- [ ] Use strong JWT_SECRET (32+ characters, random) +- [ ] Enable HTTPS/SSL +- [ ] Set up CORS properly +- [ ] Add rate limiting +- [ ] Validate all inputs +- [ ] Use parameterized queries (Prisma does this) +- [ ] Don't log sensitive data +- [ ] Set up error monitoring (Sentry) +- [ ] Regular security updates +- [ ] Database backups configured + +## Success! + +If you can: +1. Register a user +2. Login and get a JWT token +3. Save a beat pattern +4. Load it back + +**Congratulations!** 🎉 You have a working backend! + +Now you can add more features from the [Backend Summary](./BACKEND_SUMMARY.md). + +--- + +**Need more details?** Check out the complete [Implementation Guide](./IMPLEMENTATION_GUIDE.md)! diff --git a/docs/UI_GUIDELINES.md b/docs/UI_GUIDELINES.md new file mode 100644 index 0000000..baf5435 --- /dev/null +++ b/docs/UI_GUIDELINES.md @@ -0,0 +1,826 @@ +# SalsaNor Beat Machine - UI Design Guidelines + +## Table of Contents +1. [Design Philosophy](#design-philosophy) +2. [Color System](#color-system) +3. [Typography System](#typography-system) +4. [Spacing & Layout](#spacing--layout) +5. [Component Specifications](#component-specifications) +6. [Responsive Design](#responsive-design) +7. [Animation Guidelines](#animation-guidelines) +8. [Accessibility Standards](#accessibility-standards) +9. [Implementation Guide](#implementation-guide) +10. [Best Practices](#best-practices) + +--- + +## Design Philosophy + +### Frosted Studio Theme + +The SalsaNor Beat Machine embraces a **"Frosted Studio"** aesthetic that combines the elegance of glassmorphism with the energy of music production. Our design language creates an immersive, modern interface that feels both professional and approachable. + +**Core Principles:** + +1. **Transparency & Depth**: Layered glass surfaces create visual hierarchy and spatial relationships +2. **Fluid Motion**: Smooth animations reflect the rhythmic nature of music +3. **Vibrant Accents**: Bold, saturated colors highlight interactive elements against subtle backgrounds +4. **Clarity First**: Despite visual effects, readability and usability remain paramount +5. **Responsive Elegance**: The design adapts gracefully across all device sizes + +**Visual Language:** + +- **Cosmic Background**: Deep purple-to-blue gradient evokes evening studio sessions +- **Frosted Panels**: Semi-transparent surfaces with backdrop blur create floating UI elements +- **Neon Accents**: Cyan, purple, and pink glows add energy and guide user attention +- **Soft Shadows**: Layered shadows provide depth without harsh edges +- **Rounded Corners**: Consistent border radius creates a friendly, modern aesthetic + +--- + +## Color System + +### Background Gradients + +Our background uses a three-stop gradient creating a cosmic, immersive environment: + +```css +background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); +``` + +**Gradient Stops:** +- `#0f0c29` - Deep midnight blue (top-left) +- `#302b63` - Rich cosmic purple (center) +- `#24243e` - Dark twilight (bottom-right) + +### Glass Material Colors + +Glassmorphic surfaces use white with varying opacity levels and backdrop blur: + +**Glass Light** - Subtle surfaces +```scss +background: rgba(255, 255, 255, 0.1); +backdrop-filter: blur(10px); +border: 1px solid rgba(255, 255, 255, 0.15); +``` + +**Glass Medium** - Standard containers +```scss +background: rgba(255, 255, 255, 0.15); +backdrop-filter: blur(20px); +border: 1px solid rgba(255, 255, 255, 0.2); +``` + +**Glass Dark** - Emphasized sections +```scss +background: rgba(255, 255, 255, 0.2); +backdrop-filter: blur(30px); +border: 1px solid rgba(255, 255, 255, 0.25); +``` + +### Accent Colors + +**Primary Purple** +- Base: `#667eea` +- Light: `#8b9fef` +- Dark: `#4d5ed7` +- Usage: Primary actions, main interactive elements + +**Cyan** +- Base: `#00f5ff` +- Light: `#4dfbff` +- Dark: `#00c7d4` +- Usage: Focus states, solo highlighting, active indicators + +**Pink** +- Base: `#f093fb` +- Light: `#f5b5fc` +- Dark: `#e76af0` +- Usage: Accent details, secondary actions + +**Gradient Accent** +```scss +background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +``` + +### Semantic Colors + +**Success** - `#10b981` (Green) +- Usage: Confirmation, successful operations + +**Warning** - `#f59e0b` (Amber) +- Usage: Warnings, important notices + +**Error** - `#ef4444` (Red) +- Usage: Errors, destructive actions + +**Info** - `#3b82f6` (Blue) +- Usage: Informational messages, tips + +### Interactive State Colors + +**Hover** +```scss +background: rgba(255, 255, 255, 0.25); +transform: translateY(-2px); +``` + +**Active** +```scss +background: rgba(102, 126, 234, 0.3); +transform: scale(0.98); +``` + +**Focus** +```scss +outline: 2px solid #00f5ff; +outline-offset: 2px; +box-shadow: 0 0 0 4px rgba(0, 245, 255, 0.2); +``` + +**Disabled** +```scss +opacity: 0.5; +cursor: not-allowed; +``` + +### Text Colors + +**Primary Text** - `rgba(255, 255, 255, 0.95)` +- Usage: Headings, important labels + +**Secondary Text** - `rgba(255, 255, 255, 0.75)` +- Usage: Body text, descriptions + +**Muted Text** - `rgba(255, 255, 255, 0.5)` +- Usage: Helper text, placeholders + +**Disabled Text** - `rgba(255, 255, 255, 0.3)` +- Usage: Disabled states + +### Border Colors + +**Default** - `rgba(255, 255, 255, 0.15)` +**Hover** - `rgba(255, 255, 255, 0.3)` +**Focus** - `#00f5ff` +**Accent** - `rgba(102, 126, 234, 0.5)` + +--- + +## Typography System + +### Font Families + +**UI Font Stack (Inter)** +```css +font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', sans-serif; +``` +- Usage: All UI elements, buttons, labels, body text + +**Display Font (Merriweather)** +```css +font-family: 'Merriweather', Georgia, 'Times New Roman', serif; +``` +- Usage: Large headings, marketing content + +**Monospace Font (Fira Code)** +```css +font-family: 'Fira Code', 'Monaco', 'Courier New', monospace; +``` +- Usage: Code snippets, technical labels, BPM display + +### Fluid Font Sizes + +Using `clamp()` for responsive typography: + +**Heading 1** +```css +font-size: clamp(2rem, 4vw + 1rem, 3.5rem); +line-height: 1.2; +font-weight: 700; +``` + +**Heading 2** +```css +font-size: clamp(1.75rem, 3vw + 0.5rem, 2.5rem); +line-height: 1.3; +font-weight: 600; +``` + +**Heading 3** +```css +font-size: clamp(1.5rem, 2vw + 0.5rem, 2rem); +line-height: 1.4; +font-weight: 600; +``` + +**Body Large** +```css +font-size: clamp(1.125rem, 1vw + 0.5rem, 1.25rem); +line-height: 1.6; +font-weight: 400; +``` + +**Body** +```css +font-size: clamp(0.875rem, 0.5vw + 0.5rem, 1rem); +line-height: 1.6; +font-weight: 400; +``` + +**Small** +```css +font-size: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); +line-height: 1.5; +font-weight: 400; +``` + +### Font Weights + +- **Light**: 300 - Subtle text, large displays +- **Regular**: 400 - Body text, labels +- **Medium**: 500 - Emphasized body text +- **Semibold**: 600 - Subheadings, button text +- **Bold**: 700 - Headings, strong emphasis + +### Line Heights + +- **Tight**: 1.2 - Large headings +- **Normal**: 1.5 - UI elements +- **Relaxed**: 1.6 - Body text, readability +- **Loose**: 1.8 - Documentation, long-form content + +### Letter Spacing + +- **Tight**: -0.025em - Large headings +- **Normal**: 0 - Default +- **Wide**: 0.025em - Uppercase labels +- **Wider**: 0.05em - Buttons, small caps + +--- + +## Spacing & Layout + +### Spacing Scale (8px Grid) + +Our spacing system is based on multiples of 8px for visual consistency: + +```scss +$spacing: ( + 0: 0, + 1: 0.25rem, // 4px + 2: 0.5rem, // 8px + 3: 0.75rem, // 12px + 4: 1rem, // 16px + 5: 1.25rem, // 20px + 6: 1.5rem, // 24px + 8: 2rem, // 32px + 10: 2.5rem, // 40px + 12: 3rem, // 48px + 16: 4rem, // 64px + 20: 5rem, // 80px + 24: 6rem, // 96px +); +``` + +**Usage Guidelines:** +- `spacing-2` (8px) - Minimum padding, tight spacing +- `spacing-4` (16px) - Default padding for buttons, form fields +- `spacing-6` (24px) - Card padding, section spacing +- `spacing-8` (32px) - Large component spacing +- `spacing-12` (48px) - Section breaks + +### Border Radius + +```scss +$radius: ( + sm: 0.375rem, // 6px - Small buttons, badges + md: 0.5rem, // 8px - Default for most components + lg: 0.75rem, // 12px - Cards, panels + xl: 1rem, // 16px - Large containers + 2xl: 1.5rem, // 24px - Hero sections + full: 9999px, // Circular - Pills, avatars +); +``` + +### Container Sizes + +```scss +$containers: ( + xs: 20rem, // 320px + sm: 24rem, // 384px + md: 28rem, // 448px + lg: 32rem, // 512px + xl: 36rem, // 576px + 2xl: 42rem, // 672px + full: 100%, +); +``` + +### Responsive Breakpoints + +```scss +$breakpoints: ( + mobile: 480px, + tablet: 640px, + tablet-lg: 768px, + desktop: 1024px, + desktop-lg: 1280px, + wide: 1536px, +); +``` + +**Usage:** +- **< 480px**: Mobile small (single column, stacked layout) +- **480-640px**: Mobile large (single column with more breathing room) +- **640-1023px**: Tablet (2-column grid for instruments) +- **≥ 1024px**: Desktop (3-column grid, full features) + +### Grid System + +**Default Grid:** +```css +display: grid; +grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); +gap: 1.5rem; +``` + +**Responsive Instrument Grid:** +- Mobile: 1 column +- Tablet: 2 columns +- Desktop: 3 columns + +--- + +## Component Specifications + +### Buttons + +#### Primary Button +```scss +padding: 0.75rem 1.5rem; +background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); +border-radius: 0.5rem; +font-weight: 600; +color: white; +box-shadow: 0 4px 15px rgba(102, 126, 234, 0.4); + +&:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(102, 126, 234, 0.5); +} +``` + +#### Ghost Button +```scss +padding: 0.75rem 1.5rem; +background: rgba(255, 255, 255, 0.1); +backdrop-filter: blur(10px); +border: 1px solid rgba(255, 255, 255, 0.2); +border-radius: 0.5rem; + +&:hover { + background: rgba(255, 255, 255, 0.15); +} +``` + +#### Icon Button +```scss +width: 2.5rem; +height: 2.5rem; +padding: 0.5rem; +border-radius: 0.5rem; +display: flex; +align-items: center; +justify-content: center; +``` + +### Sliders + +```scss +.slider { + width: 100%; + height: 0.375rem; + background: rgba(255, 255, 255, 0.1); + border-radius: 9999px; + + &::-webkit-slider-thumb { + width: 1rem; + height: 1rem; + background: white; + border-radius: 50%; + box-shadow: 0 0 10px rgba(0, 245, 255, 0.6); + } +} +``` + +### Cards (Instrument Tiles) + +```scss +padding: 1.5rem; +background: rgba(255, 255, 255, 0.15); +backdrop-filter: blur(20px); +border: 1px solid rgba(255, 255, 255, 0.2); +border-radius: 0.75rem; +box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3); +``` + +### Toggle Switches + +```scss +width: 3rem; +height: 1.5rem; +background: rgba(255, 255, 255, 0.2); +border-radius: 9999px; +position: relative; + +.toggle-thumb { + width: 1.25rem; + height: 1.25rem; + background: white; + border-radius: 50%; + transition: transform 0.2s; + + &.active { + transform: translateX(1.5rem); + background: #00f5ff; + } +} +``` + +### Step Sequencer Grid + +```scss +display: grid; +grid-template-columns: repeat(16, 1fr); +gap: 0.375rem; + +.step { + aspect-ratio: 1; + background: rgba(255, 255, 255, 0.1); + border-radius: 0.25rem; + + &.active { + background: #667eea; + box-shadow: 0 0 15px rgba(102, 126, 234, 0.8); + } + + &.playing { + animation: pulse 0.3s ease-out; + } +} + +@media (max-width: 640px) { + grid-template-columns: repeat(8, 1fr); +} +``` + +### Beat Indicator + +```scss +display: flex; +gap: 0.5rem; + +.beat-dot { + width: 0.75rem; + height: 0.75rem; + background: rgba(255, 255, 255, 0.2); + border-radius: 50%; + + &.active { + background: #00f5ff; + box-shadow: 0 0 10px #00f5ff; + transform: scale(1.2); + } +} +``` + +--- + +## Responsive Design + +### Mobile-First Approach + +Always start with mobile styles and progressively enhance: + +```scss +// Mobile default +.component { + padding: 1rem; + font-size: 0.875rem; +} + +// Tablet +@media (min-width: 640px) { + .component { + padding: 1.5rem; + font-size: 1rem; + } +} + +// Desktop +@media (min-width: 1024px) { + .component { + padding: 2rem; + font-size: 1.125rem; + } +} +``` + +### Responsive Patterns + +**Stacked to Grid:** +```scss +.instrument-grid { + display: grid; + grid-template-columns: 1fr; + gap: 1rem; + + @media (min-width: 640px) { + grid-template-columns: repeat(2, 1fr); + } + + @media (min-width: 1024px) { + grid-template-columns: repeat(3, 1fr); + gap: 1.5rem; + } +} +``` + +**Fluid Typography:** +```scss +h1 { + font-size: clamp(2rem, 5vw, 3.5rem); +} +``` + +**Adaptive Step Grid:** +```scss +.step-grid { + grid-template-columns: repeat(auto-fit, minmax(30px, 1fr)); + max-width: 100%; +} +``` + +--- + +## Animation Guidelines + +### Timing Functions + +**Standard Easing:** +```scss +transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); +``` + +**Ease Out (Entering):** +```scss +transition-timing-function: cubic-bezier(0, 0, 0.2, 1); +``` + +**Ease In (Exiting):** +```scss +transition-timing-function: cubic-bezier(0.4, 0, 1, 1); +``` + +**Spring (Playful):** +```scss +transition-timing-function: cubic-bezier(0.68, -0.55, 0.265, 1.55); +``` + +### Duration Guidelines + +- **Instant**: 100ms - Hover effects, highlights +- **Quick**: 150ms - Button states, toggles +- **Normal**: 250ms - Panel transitions, fades +- **Slow**: 400ms - Large movements, page transitions +- **Deliberate**: 600ms+ - Emphasis, celebrations + +### Common Animations + +**Hover Lift:** +```scss +transition: transform 0.2s ease; + +&:hover { + transform: translateY(-2px); +} +``` + +**Pulse (Beat Indicator):** +```scss +@keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } +} +``` + +**Glow:** +```scss +@keyframes glow { + 0%, 100% { box-shadow: 0 0 10px currentColor; } + 50% { box-shadow: 0 0 20px currentColor; } +} +``` + +**Reduced Motion:** +```scss +@media (prefers-reduced-motion: reduce) { + * { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} +``` + +--- + +## Accessibility Standards + +### WCAG AA Compliance + +**Contrast Ratios:** +- Normal text (< 18pt): Minimum 4.5:1 +- Large text (≥ 18pt or bold ≥ 14pt): Minimum 3:1 +- UI components: Minimum 3:1 + +**Our Implementation:** +- White text on dark background: ~15:1 ✓ +- Colored text on glass: Tested for minimum 4.5:1 ✓ +- Interactive elements: Minimum 3:1 contrast ✓ + +### Focus Management + +```scss +:focus-visible { + outline: 2px solid #00f5ff; + outline-offset: 2px; + box-shadow: 0 0 0 4px rgba(0, 245, 255, 0.2); +} +``` + +### Screen Reader Support + +```scss +.sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} +``` + +### Keyboard Navigation + +- All interactive elements must be keyboard accessible +- Tab order follows logical visual flow +- Enter/Space activates buttons +- Arrow keys navigate step sequencer +- Escape closes modals/menus + +### ARIA Labels + +```html + + +
+``` + +--- + +## Implementation Guide + +### Setting Up SCSS + +1. **Install dependencies:** +```bash +npm install sass --save-dev +``` + +2. **Import design tokens:** +```scss +@import 'tokens/colors'; +@import 'tokens/typography'; +@import 'tokens/spacing'; +@import 'tokens/shadows'; +``` + +3. **Use mixins:** +```scss +@import 'abstracts/functions'; +@import 'abstracts/mixins'; + +.card { + @include glass-medium; + @include smooth-transition(all, 0.3s, ease); +} +``` + +### Using CSS Custom Properties + +```javascript +// Access in JavaScript +const primaryColor = getComputedStyle(document.documentElement) + .getPropertyValue('--color-primary'); +``` + +### Component Structure + +```scss +.component { + // Layout + display: flex; + padding: spacing(4); + + // Visual + @include glass-medium; + border-radius: var(--radius-md); + + // Typography + font-size: var(--font-size-body); + + // Interaction + @include smooth-transition(all, 0.2s, ease); + + &:hover { + @include hover-lift; + } +} +``` + +--- + +## Best Practices + +### Do's ✓ + +1. **Use design tokens** - Never hardcode values +2. **Mobile-first** - Start small, scale up +3. **Semantic HTML** - Use appropriate elements +4. **Progressive enhancement** - Core functionality without JS +5. **Test accessibility** - Use keyboard, screen readers +6. **Optimize performance** - Minimize repaints, use transforms +7. **Consistent spacing** - Stick to 8px grid +8. **Clear hierarchy** - Visual weight matches importance + +### Don'ts ✗ + +1. **Don't skip focus states** - Always provide visible focus +2. **Don't use color alone** - Add icons, text, patterns +3. **Don't animate everything** - Be purposeful +4. **Don't break keyboard nav** - Test thoroughly +5. **Don't ignore reduced motion** - Respect user preferences +6. **Don't use fixed units** - Prefer rem, em, % +7. **Don't nest too deep** - Keep specificity low +8. **Don't sacrifice performance** - Profile animations + +### Code Quality + +**Write maintainable SCSS:** +```scss +// Good +.button { + @include glass-light; + padding: spacing(4); + + &--primary { + background: var(--color-primary); + } +} + +// Avoid +.button { + background: rgba(255, 255, 255, 0.1); + padding: 16px; +} +.button.primary { + background: #667eea; +} +``` + +### Performance Tips + +1. Use `transform` and `opacity` for animations +2. Minimize backdrop-filter usage (expensive) +3. Use `will-change` sparingly +4. Debounce resize handlers +5. Lazy load off-screen content + +--- + +## Conclusion + +This design system provides a solid foundation for building the SalsaNor Beat Machine UI. By following these guidelines, you'll create a consistent, accessible, and visually stunning experience that delights users while maintaining excellent performance and usability. + +For questions or suggestions, please open an issue in the GitHub repository. + +**Version**: 1.0.0 +**Last Updated**: January 2026 +**Maintained by**: SalsaNor Team diff --git a/docs/issues/instructor-language-selection.md b/docs/issues/instructor-language-selection.md new file mode 100644 index 0000000..0c96489 --- /dev/null +++ b/docs/issues/instructor-language-selection.md @@ -0,0 +1,231 @@ +# Add Language Selection for Instructor Voice + +## 📋 Issue Summary + +The Instructor instrument currently plays voice counting ("1", "2", "3", etc.) in English only, but the audio files include **6 different languages**. These language options are hidden and not accessible through the UI. + +## 🎯 Current State + +**Available Audio Files:** +- 🇬🇧 `instructor-0` to `instructor-7` (English - default) +- 🇮🇹 `italian:instructor-0` to `italian:instructor-7` +- 🇪🇸 `spanish:instructor-0` to `spanish:instructor-7` +- 🇫🇷 `french:instructor-0` to `french:instructor-7` +- 🇷🇺 `russian:instructor-0` to `russian:instructor-7` +- 🇩🇪 `german:instructor-0` to `german:instructor-7` + +**Current Behavior:** +- UI only shows "Instructor" instrument +- Only English (`instructor-X`) samples are played +- No way to select language +- Language-prefixed samples exist but are inaccessible + +## 🎨 Proposed Solution + +### Main App UI + +Add a **language selector toggle** in the footer, next to the Widget Generator button: + +``` +┌─────────────────────────────────────────┐ +│ │ +│ [Beat Machine Content] │ +│ │ +└─────────────────────────────────────────┘ + Footer: + [🌐 EN ▾] [Widget Generator] +``` + +**Language Toggle Options:** +- 🇬🇧 English (EN) +- 🇮🇹 Italian (IT) +- 🇪🇸 Spanish (ES) +- 🇫🇷 French (FR) +- 🇷🇺 Russian (RU) +- 🇩🇪 German (DE) + +### Widget Generator + +Add a **language dropdown** in the Instructor instrument settings section: + +```tsx +
+

Instructor Language

+ +

+ Language for counting beats (1, 2, 3, etc.) +

+
+``` + +### Widget Code Generation + +**Generated code should support:** +```html +
+``` + +**Widget Configuration Interface:** +```typescript +interface WidgetConfig { + instruments?: string[]; + instructorLanguage?: 'italian' | 'spanish' | 'french' | 'russian' | 'german'; + bpm?: number; + autoplay?: boolean; +} +``` + +## 🔧 Technical Implementation + +### 1. Update Machine Interfaces + +```typescript +// engine/machine-interfaces.ts +export interface IInstrument { + id: string; + title: string; + enabled: boolean; + volume: number; + activeProgram: number; + programs: IProgram[]; + + // New property for language-aware instruments + language?: string; // 'italian', 'spanish', 'french', 'russian', 'german' +} +``` + +### 2. Update AudioBackend Sample Loading + +Modify `engine/audio-backend.ts` to check for language prefix: + +```typescript +private getSampleName(instrument: IInstrument, pitch: number): string { + const baseId = `${instrument.id}-${pitch}`; + + // If instrument has language setting and it's instructor + if (instrument.language && instrument.id === 'instructor') { + return `${instrument.language}:${baseId}`; + } + + return baseId; +} +``` + +### 3. Add Language State Management + +```typescript +// Store language preference globally +interface AppState { + instructorLanguage: string; +} + +// Persist to localStorage +localStorage.setItem('beat-machine-instructor-lang', language); +``` + +### 4. Update Widget Entry + +```typescript +// src/widget/widget-entry.tsx +const instructorLanguage = element.dataset.instructorLanguage; + +if (instructorLanguage) { + const instructor = machine.instruments.find(i => i.id === 'instructor'); + if (instructor) { + instructor.language = instructorLanguage; + } +} +``` + +## 📁 Files to Modify + +### Core Engine +- `engine/machine-interfaces.ts` - Add `language?: string` property +- `engine/audio-backend.ts` - Update sample name resolution +- `engine/instrument-player.ts` - Pass language info to audio backend + +### UI Components +- `components/beat-machine-ui-glass.tsx` - Add language toggle in footer +- `components/instrument-tile.tsx` - Show language selector for Instructor +- `pages/widget-generator.tsx` - Add language selection +- `pages/widget-generator.module.css` - Style language selector + +### Widget System +- `src/widget/widget-types.ts` - Add `instructorLanguage?` to config +- `src/widget/widget-entry.tsx` - Parse `data-instructor-language` attribute +- `src/widget/WidgetCompact.tsx` - Support language prop + +### Documentation +- `docs/CROSS_DOMAIN_SETUP.md` - Document language attribute +- `README.md` - Update features list + +## ✅ Acceptance Criteria + +- [ ] Language toggle appears in main app footer +- [ ] Clicking toggle shows dropdown with all 6 language options +- [ ] Selected language persists in localStorage +- [ ] Instructor instrument plays correct language samples +- [ ] Widget generator includes language selector +- [ ] Generated widget code includes `data-instructor-language` attribute +- [ ] Widget respects language setting from data attribute +- [ ] Language setting only affects Instructor instrument (not others) +- [ ] Default behavior (no language set) uses English +- [ ] Invalid language values fallback to English gracefully + +## 🎯 Testing Checklist + +### Main App +- [ ] Toggle appears in footer on all screen sizes +- [ ] Language selection persists after page reload +- [ ] Instructor plays correct language after selection +- [ ] Other instruments unaffected by language setting +- [ ] Mobile responsive (dropdown doesn't break layout) + +### Widget Generator +- [ ] Language dropdown appears in UI +- [ ] Generated code includes correct language attribute +- [ ] Live preview plays selected language +- [ ] Copy button includes language in generated code + +### Widget Embed +- [ ] `data-instructor-language="spanish"` works correctly +- [ ] Missing language attribute uses English (default) +- [ ] Invalid language gracefully falls back to English +- [ ] Multiple widgets on same page can have different languages + +## 🚀 Future Enhancements + +- **Auto-detect language** from browser settings (`navigator.language`) +- **Add more languages** if audio samples become available +- **Language-specific UI text** (not just Instructor voice) +- **Per-pattern language override** (different language for different programs) +- **Voice gender options** (if multiple recordings available) + +## 📝 Notes + +- Audio files already exist for all 6 languages +- No audio recording needed - implementation only +- Consider whether other instruments might need language support +- Performance: all language samples already in single audio file +- File size: no additional downloads needed + +## 🔗 Related Issues + +- Widget Generator feature (#existing-issue-number) +- Cross-domain deployment (#existing-issue-number) + +--- + +**Priority:** Medium +**Estimated Effort:** 4-6 hours +**Impact:** Enhances accessibility for international users diff --git a/docs/issues/standalone-widget.md b/docs/issues/standalone-widget.md new file mode 100644 index 0000000..3d65404 --- /dev/null +++ b/docs/issues/standalone-widget.md @@ -0,0 +1,260 @@ +# Create Standalone JavaScript Widget for Embedding Beat Machine + +**Status:** 📝 Planning +**Priority:** High +**Labels:** enhancement, feature, embed + +--- + +## 🎯 Goal +Create a standalone JavaScript widget that allows the Beat Machine to be embedded on any website with simple HTML and data attributes. + +## 📋 User Story +As a content creator, I want to embed a compact beat machine player in my articles so that I can demonstrate musical concepts (like clave patterns) inline with my content. + +## 🔧 Technical Approach + +### Implementation Steps + +#### 1. Vite Bundle Configuration +- [ ] Create separate entry point for widget (`src/widget.tsx`) +- [ ] Configure Vite to build standalone bundle with minimal dependencies +- [ ] Use existing `vite-bundle` script as starting point +- [ ] Output to `dist/beat-widget.js` and `dist/beat-widget.css` +- [ ] Add source maps for debugging +- [ ] Configure code splitting for optimal loading + +#### 2. Widget API Design +```html + +
+ + + +
+``` + +#### 3. Compact UI Component +- [ ] Create `BeatMachineCompact.tsx` component +- [ ] Minimal controls: play/pause, BPM slider (optional in compact mode) +- [ ] Show only specified instruments +- [ ] Responsive design for various container sizes +- [ ] Maintain glassmorphic design aesthetic +- [ ] Option to hide labels/titles for ultra-compact mode + +#### 4. Configuration via Data Attributes + +**Required:** +- `data-instruments`: Comma-separated instrument IDs (e.g., "clave,cowbell,bongo") + +**Optional:** +- `data-bpm`: Initial tempo (default: 120) +- `data-machine`: Machine type - "salsa" or "merengue" (default: "salsa") +- `data-size`: "compact" or "full" (default: "compact") +- `data-autoplay`: Auto-start playback - "true" or "false" (default: "false") +- `data-pattern`: Pre-configured pattern name (optional) +- `data-theme`: "dark" or "light" (default: "dark") +- `data-show-labels`: Show instrument names - "true" or "false" (default: "true") +- `data-show-bpm`: Show BPM control - "true" or "false" (default: "true") + +#### 5. Widget Initialization System +```typescript +// Auto-initialize all widgets on page load +window.BeatMachineWidget = { + init: (selector?: string) => void, + create: (element: HTMLElement, config: WidgetConfig) => WidgetInstance, + destroy: (element: HTMLElement) => void +}; + +// Auto-init on DOMContentLoaded +document.addEventListener('DOMContentLoaded', () => { + window.BeatMachineWidget.init(); +}); +``` + +## ✅ Acceptance Criteria + +### Functionality +- [ ] Widget can be loaded with single script tag +- [ ] Configurable via HTML data attributes +- [ ] Multiple widgets can coexist on same page +- [ ] Works on any website without CSS/JS conflicts +- [ ] Audio plays correctly in embedded context + +### Performance +- [ ] Bundle size < 200KB (gzipped) +- [ ] Lazy-load audio samples +- [ ] No performance impact on host page + +### UI/UX +- [ ] Compact UI variant exists and looks good +- [ ] Responsive to container width +- [ ] Maintains glassmorphic design +- [ ] Dark/light theme support +- [ ] No global CSS conflicts (scoped styles) + +### Documentation +- [ ] Example HTML page demonstrating usage +- [ ] API documentation in README +- [ ] Integration guide for common platforms +- [ ] WordPress integration example + +## 🎨 UI Considerations + +### Compact Mode Design +- Horizontal layout for space efficiency +- Instrument tiles reduced to icons only (no labels if `data-show-labels="false"`) +- Controls overlay on hover +- Minimal padding (use existing `.instrumentCard` improvements) +- Full glassmorphic aesthetic preserved + +### Sizing Guidelines +- **Ultra-compact:** 300px × 80px (single row, minimal controls) +- **Compact:** 400px × 120px (single row with controls) +- **Medium:** 600px × 150px (instruments + full controls) +- **Full:** Responsive, similar to main app + +## 📦 Bundle Strategy + +### Core Bundle (Required) +- Beat engine +- Audio backend +- Minimal UI components +- Widget initialization + +### Lazy Loaded +- Audio samples (load on demand) +- Full machine configurations +- Additional themes + +### External Dependencies +- Consider inlining critical MUI components +- Remove unused Material-UI modules +- Use tree-shaking aggressively + +## 🧪 Testing Strategy +- [ ] Test on vanilla HTML page +- [ ] Test on WordPress site +- [ ] Test with multiple widgets on same page +- [ ] Test in different browsers +- [ ] Test with different container sizes +- [ ] Test auto-play with browser restrictions + +## 🔗 Future Enhancements +- WordPress plugin wrapper with admin UI +- Web Component version (``) +- React/Vue/Angular component wrappers +- CDN hosting for widget files +- Pattern library with predefined rhythms +- Visual pattern editor in embed mode + +## 💡 Example Use Cases + +### Educational Article +```html +

Understanding the Clave Rhythm

+

The clave is the foundational rhythm in salsa music...

+
+``` + +### Music Blog +```html +

Salsa Rhythm Breakdown

+
+``` + +### Interactive Tutorial +```html +
+``` + +## 📝 Implementation Notes + +### File Structure +``` +src/ + widget/ + widget-entry.tsx # Main entry point + BeatMachineWidget.tsx # Widget component + WidgetCompact.tsx # Compact UI variant + widget-init.ts # Auto-initialization + widget-types.ts # TypeScript definitions + +public/ + widget-demo.html # Demo page + +dist/ + beat-widget.js # Bundled widget + beat-widget.css # Scoped styles + beat-widget.js.map # Source map +``` + +### Vite Configuration +```javascript +// vite.config.widget.js +export default { + build: { + lib: { + entry: 'src/widget/widget-entry.tsx', + name: 'BeatMachineWidget', + fileName: 'beat-widget', + formats: ['iife'] + }, + rollupOptions: { + output: { + assetFileNames: 'beat-widget.[ext]' + } + } + } +} +``` + +## 🚀 Deployment + +### CDN Hosting +```html + + + + + +``` + +### Self-Hosted +- Build widget bundle +- Upload to your server +- Reference from your domain + +--- + +**Related Issues:** +- #2 - Glassmorphic UI design system (provides base styling) + +**References:** +- Current beat-engine implementation +- Existing `vite-bundle` npm script +- Material-UI components in use diff --git a/engine/audio-backend.ts b/engine/audio-backend.ts index a1e2570..30dcf04 100644 --- a/engine/audio-backend.ts +++ b/engine/audio-backend.ts @@ -10,16 +10,41 @@ export class AudioBackend { private bankDescriptor?: BankDescriptor; private zeroTime: number | null = null; private _context?: AudioContext; + private audioFormat: string; - constructor() { + constructor(private baseUrl: string = '') { this.ready = false; const hasWebM = typeof MediaSource !== 'undefined' && MediaSource.isTypeSupported('audio/webm;codecs="vorbis"'); - this.loadBank(hasWebM ? 'assets/audio/main.webm' : 'assets/audio/main.mp3'); - this.loadBankDescriptor('assets/audio/main.json'); + const audioPath = hasWebM ? '/assets/audio/main.webm' : '/assets/audio/main.mp3'; + this.audioFormat = baseUrl ? `${baseUrl}/${audioPath}` : audioPath; + console.log('AudioBackend created - will load audio after init()'); } - init(context = typeof AudioContext !== 'undefined' ? new AudioContext() : undefined) { + /** + * Initialiserer AudioContext og setter zeroTime umiddelbart. + * Starter deretter lasting av audio filer. + */ + async init(context = typeof AudioContext !== 'undefined' ? new AudioContext() : undefined) { this._context = context; + if (this._context) { + this.zeroTime = this._context.currentTime; + console.log('🎵 AudioBackend initialized — zeroTime set to:', this.zeroTime); + + // Load audio files after context is initialized + try { + await Promise.all([ + this.loadBank(this.audioFormat), + this.loadBankDescriptor('/assets/audio/main.json') + ]); + console.log('✅ Audio files loaded successfully'); + } catch (error) { + console.error('❌ Error loading audio files:', error); + console.error('Please ensure the following files exist in public/assets/audio/:'); + console.error(' - main.webm (or main.mp3)'); + console.error(' - main.json'); + console.error('Download them from: https://www.salsabeatmachine.org/assets/audio/'); + } + } } get context() { @@ -27,18 +52,42 @@ export class AudioBackend { } private async loadBank(url: string) { - // on Safari we need to use callbacks using decodeAudioData method. - const req = await fetch(url); - const response = await req.arrayBuffer(); - this.buffer = await new Promise((resolve, reject) => { - this.context?.decodeAudioData(response, resolve, reject); - }); - this.ready = true; + try { + console.log('Loading audio bank from:', url); + const req = await fetch(url); + if (!req.ok) { + throw new Error(`Failed to fetch ${url}: ${req.status} ${req.statusText}`); + } + const response = await req.arrayBuffer(); + if (!this.context) { + throw new Error('AudioContext not initialized - cannot decode audio'); + } + this.buffer = await new Promise((resolve, reject) => { + this.context?.decodeAudioData(response, resolve, reject); + }); + this.ready = true; + console.log('✅ Audio bank loaded successfully'); + } catch (error) { + console.error('❌ Failed to load audio bank:', error); + throw error; + } } private async loadBankDescriptor(url: string) { - const req = await fetch(url); - this.bankDescriptor = await req.json(); + try { + // Prepend base URL if configured + const fullUrl = this.baseUrl && !url.startsWith('http') ? `${this.baseUrl}/${url}` : url; + console.log('Loading bank descriptor from:', fullUrl); + const req = await fetch(fullUrl); + if (!req.ok) { + throw new Error(`Failed to fetch ${fullUrl}: ${req.status} ${req.statusText}`); + } + this.bankDescriptor = await req.json(); + console.log('✅ Bank descriptor loaded successfully'); + } catch (error) { + console.error('❌ Failed to load bank descriptor:', error); + throw error; + } } play(sampleName: string, player: InstrumentPlayer, when: number, velocity?: number) { @@ -46,9 +95,13 @@ export class AudioBackend { bufferSource.connect(player.createNoteDestination(velocity)); bufferSource.buffer = this.buffer!; const sampleInfo = this.bankDescriptor![sampleName]; + + // Setter zeroTime hvis det fortsatt er null (som en fallback) if (this.zeroTime === null) { this.zeroTime = this.context!.currentTime; + console.log('⚠️ Warning: zeroTime was null — setting it now to:', this.zeroTime); } + const startTime = this.zeroTime + when; bufferSource.start(startTime, sampleInfo[1] / 44100.0, sampleInfo[2] / 44100.0); player.registerSample(bufferSource, startTime); @@ -60,6 +113,7 @@ export class AudioBackend { getCurrentTime(): number { if (this.zeroTime == null) { + console.warn('⏱️ Warning: zeroTime is null, returning 0 for current time.'); return 0; } return this.context!.currentTime - this.zeroTime; diff --git a/engine/beat-engine.ts b/engine/beat-engine.ts index ea702b8..7fff930 100644 --- a/engine/beat-engine.ts +++ b/engine/beat-engine.ts @@ -1,4 +1,4 @@ -import { computed, Lambda, observable, observe } from 'mobx'; +import { makeAutoObservable, observe } from 'mobx'; import { AudioBackend } from './audio-backend'; import { InstrumentPlayer } from './instrument-player'; import { createMachine } from './machine'; @@ -13,20 +13,24 @@ export class BeatEngine { private nextSampleIndex = 0; private animationFrameRequest: number | null = null; private audioTimeDelta = 0; - private machineDisposers: Lambda[] = []; + private machineDisposers: (() => void)[] = []; private readonly instrumentPlayers = new Map(); + private mixerNotReadyLogged = false; - @observable - private interval: number | null = null; - - @observable - private _machine: IMachine = createMachine(); - - @observable + interval: number | null = null; + _machine: IMachine = createMachine(); beat = 0; constructor(private mixer: AudioBackend) { - this.mixer.init(); + makeAutoObservable(this); + } + + async init() { + await this.mixer.init(); + console.log('BeatEngine initialized - AudioBackend state:', { + ready: this.mixer.ready, + hasContext: !!this.mixer.context, + }); } get machine() { @@ -38,14 +42,10 @@ export class BeatEngine { this._machine = value; this.machineDisposers.forEach((disposer) => disposer()); this.machineDisposers = []; - for (const player of Array.from(this.instrumentPlayers.values())) { - player.dispose(); - } + this.instrumentPlayers.forEach((player) => player.dispose()); this.instrumentPlayers.clear(); - if (!this.machine) { - return; - } + if (!this.machine) return; if (this.playing) { this.stop(); this.play(); @@ -54,13 +54,26 @@ export class BeatEngine { this.machineDisposers.push( observe(this.machine, 'bpm', ({ oldValue, newValue }) => { if (this.playing) { - if (this.interval) { - clearTimeout(this.interval); - } + if (this.interval) clearTimeout(this.interval); + const currentAudioTime = this.mixer.getCurrentTime(); - if (oldValue != null) { - this.audioTimeDelta = (currentAudioTime + this.audioTimeDelta) * (oldValue / newValue) - currentAudioTime; + + // 🚨 Sjekk for ugyldige verdier: + if (oldValue == null || newValue == null || oldValue === 0 || newValue === 0) { + console.warn('⚠️ Ugyldige verdier for BPM endring: oldValue =', oldValue, ', newValue =', newValue); + return; + } + + // 🚀 Ny kalkulering med sikker fallback: + this.audioTimeDelta = + (currentAudioTime + (this.audioTimeDelta || 0)) * (oldValue / newValue) - currentAudioTime; + + // Hvis audioTimeDelta fortsatt er NaN, setter vi den til 0 + if (isNaN(this.audioTimeDelta)) { + console.warn('⚠️ audioTimeDelta ble NaN — setter den til 0.'); + this.audioTimeDelta = 0; } + this.nextSampleIndex = Math.ceil(this.getBeatIndex() * 2); this.stopAllInstruments(); this.scheduleBuffers(); @@ -84,12 +97,46 @@ export class BeatEngine { } } - public play() { + play() { this.mixer.context?.resume(); this.scheduleBuffers(); this.beatTick(); } + stopAllInstruments = (hard = false) => { + for (const instrument of Array.from(this.instrumentPlayers.values())) { + instrument.reset(hard); + } + }; + + stop() { + if (this.interval) { + clearTimeout(this.interval); + this.interval = null; + } + if (this.animationFrameRequest) { + cancelAnimationFrame(this.animationFrameRequest); + this.animationFrameRequest = null; + } + this.stopAllInstruments(true); + this.mixer.reset(); + this.audioTimeDelta = 0; + this.nextSampleIndex = 0; + } + + get playing() { + return this.interval != null; + } + + get beatTime() { + const result = 60 / this.machine.bpm; + return this.machine.flavor === 'Merengue' ? result / 2 : result; + } + + getBeatIndex() { + return (this.mixer.getCurrentTime() + this.audioTimeDelta) / this.beatTime; + } + private getInstrumentPlayer(context: AudioContext, instrument: IInstrument) { const instrumentPlayer = this.instrumentPlayers.get(instrument); if (instrumentPlayer) { @@ -104,32 +151,6 @@ export class BeatEngine { } } - private scheduleBuffers() { - const context = this.mixer.context; - if (context && this.mixer.ready) { - const sampleTime = this.beatTime / 2; - const currentBeat = this.getBeatIndex(); - while (this.nextSampleIndex - currentBeat * 2 < 64) { - const sampleIndex = this.nextSampleIndex; - this.machine.instruments.forEach((instrument) => { - const instrumentPlayer = this.getInstrumentPlayer(context, instrument); - this.instrumentNotes(instrument, sampleIndex).forEach((note) => { - this.mixer.play( - note.sampleName, - instrumentPlayer, - sampleIndex * sampleTime - this.audioTimeDelta, - note.velocity, - ); - }); - }); - this.nextSampleIndex++; - } - } else { - console.log('Mixer not ready yet'); - } - this.interval = window.setTimeout(() => this.scheduleBuffers(), 1000); - } - rescheduleInstrument(instrument: IInstrument, player: InstrumentPlayer) { player.reset(); const sampleTime = this.beatTime / 2; @@ -175,42 +196,37 @@ export class BeatEngine { return result; } - private stopAllInstruments(hard = false) { - for (const instrument of Array.from(this.instrumentPlayers.values())) { - instrument.reset(hard); - } - } - - public stop() { - if (this.interval) { - clearTimeout(this.interval); - this.interval = null; - } - if (this.animationFrameRequest) { - cancelAnimationFrame(this.animationFrameRequest); - this.animationFrameRequest = null; + scheduleBuffers = () => { + const context = this.mixer.context; + if (context && this.mixer.ready) { + this.mixerNotReadyLogged = false; // Reset the flag when mixer is ready + const sampleTime = this.beatTime / 2; + const currentBeat = this.getBeatIndex(); + while (this.nextSampleIndex - currentBeat * 2 < 64) { + const sampleIndex = this.nextSampleIndex; + this.machine.instruments.forEach((instrument) => { + const instrumentPlayer = this.getInstrumentPlayer(context, instrument); + this.instrumentNotes(instrument, sampleIndex).forEach((note) => { + this.mixer.play( + note.sampleName, + instrumentPlayer, + sampleIndex * sampleTime - this.audioTimeDelta, + note.velocity, + ); + }); + }); + this.nextSampleIndex++; + } + } else { + if (!this.mixerNotReadyLogged) { + console.log('Mixer not ready yet - context:', !!context, 'ready:', this.mixer.ready); + this.mixerNotReadyLogged = true; + } } - this.stopAllInstruments(true); - this.mixer.reset(); - this.audioTimeDelta = 0; - this.nextSampleIndex = 0; - } - - @computed - get playing() { - return this.interval != null; - } - - get beatTime() { - const result = 60 / this.machine.bpm; - return this.machine.flavor === 'Merengue' ? result / 2 : result; - } - - public getBeatIndex() { - return (this.mixer.getCurrentTime() + this.audioTimeDelta) / this.beatTime; - } + this.interval = window.setTimeout(() => this.scheduleBuffers(), 1000); + }; - private beatTick() { + beatTick() { this.beat = this.getBeatIndex(); this.animationFrameRequest = requestAnimationFrame(() => this.beatTick()); } diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..447f9aa --- /dev/null +++ b/eslint.config.js @@ -0,0 +1,66 @@ +import js from '@eslint/js'; +import react from 'eslint-plugin-react'; +import a11y from 'eslint-plugin-jsx-a11y'; +import importPlugin from 'eslint-plugin-import'; +import tsParser from '@typescript-eslint/parser'; +import ts from '@typescript-eslint/eslint-plugin'; +import { defineConfig, globalIgnores } from 'eslint/config'; + +export default defineConfig([ + // Globale ignoreringsmønstre + globalIgnores(['**/node_modules/**', '**/.next/**', '**/dist/**', '**/build/**', '**/*copy*']), + + // ESLint anbefalte konfigurasjoner + js.configs.recommended, + { + plugins: { + react, + 'jsx-a11y': a11y, + import: importPlugin, + '@typescript-eslint': ts, + }, + languageOptions: { + parser: tsParser, + parserOptions: { + ecmaVersion: 'latest', + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + globals: { + module: 'readonly', + require: 'readonly', + process: 'readonly', + self: 'readonly', + __webpack_exports__: 'readonly', + __webpack_require__: 'readonly', + __unused_webpack_module: 'readonly', + }, + }, + settings: { + react: { + version: 'detect', + }, + 'import/resolver': { + node: { + extensions: ['.js', '.jsx', '.ts', '.tsx'], + }, + }, + }, + rules: { + 'react/react-in-jsx-scope': 'off', + 'react/prop-types': 'off', + 'import/no-unresolved': 'error', + 'import/named': 'error', + 'import/default': 'error', + 'import/no-named-as-default': 'warn', + 'import/no-named-as-default-member': 'warn', + 'no-unused-vars': 'warn', + '@typescript-eslint/no-unused-vars': ['warn'], + '@typescript-eslint/explicit-function-return-type': 'off', + '@typescript-eslint/no-explicit-any': 'off', + 'no-undef': 'off', + }, + }, +]); diff --git a/hooks/use-beat-engine.ts b/hooks/use-beat-engine.ts index ad44b74..7105974 100644 --- a/hooks/use-beat-engine.ts +++ b/hooks/use-beat-engine.ts @@ -6,10 +6,20 @@ export function useBeatEngine() { const [engine, setEngine] = useState(null); useEffect(() => { - const newEngine = new BeatEngine(new AudioBackend()); - setEngine(newEngine); + const initEngine = async () => { + const mixer = new AudioBackend(); + const newEngine = new BeatEngine(mixer); + await newEngine.init(); + setEngine(newEngine); + }; - return () => newEngine.stop(); + initEngine(); + + return () => { + if (engine) { + engine.stop(); + } + }; }, []); return engine; diff --git a/next-env.d.ts b/next-env.d.ts index 7b7aa2c..52e831b 100644 --- a/next-env.d.ts +++ b/next-env.d.ts @@ -1,2 +1,5 @@ /// -/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/pages/api-reference/config/typescript for more information. diff --git a/next.config.js b/next.config.js new file mode 100644 index 0000000..bf3eae9 --- /dev/null +++ b/next.config.js @@ -0,0 +1,30 @@ +/** @type {import('next').NextConfig} */ +import path from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const nextConfig = { + // Enable static HTML export for deployment to any web host + output: 'export', + + reactStrictMode: true, + + // Add trailing slashes to URLs for better compatibility + trailingSlash: true, + + // Required for static export: disable image optimization + images: { + unoptimized: true, + }, + + sassOptions: { + includePaths: [path.join(__dirname, 'styles')], + }, + + // Optional: uncomment if deploying to a subdirectory + // basePath: '/beat-machine', +} + +export default nextConfig; diff --git a/package-lock.json b/package-lock.json index bdc7f07..292f2a7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1085 +1,8294 @@ { "name": "beat-machine", - "version": "1.0.0", - "lockfileVersion": 1, + "version": "2.0.0", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/helper-create-class-features-plugin": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.10.5.tgz", - "integrity": "sha512-0nkdeijB7VlZoLT3r/mY3bUkw3T8WG/hNw+FATs/6+pG2039IJWjTYL0VTISqsNHMUTEnwbVnc89WIJX9Qed0A==", - "dev": true, - "requires": { - "@babel/helper-function-name": "^7.10.4", - "@babel/helper-member-expression-to-functions": "^7.10.5", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/helper-replace-supers": "^7.10.4", - "@babel/helper-split-export-declaration": "^7.10.4" - } - }, - "@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true - }, - "@babel/helper-function-name": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz", - "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==", - "dev": true, - "requires": { - "@babel/helper-get-function-arity": "^7.10.4", - "@babel/template": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "packages": { + "": { + "name": "beat-machine", + "version": "2.0.0", + "license": "ISC", + "dependencies": { + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@mui/styles": "^6.4.11", + "@xmldom/xmldom": "^0.9.8", + "axios": "^1.9.0", + "classnames": "^2.5.1", + "minimatch": "^10.0.1", + "mobx": "^6.13.7", + "mobx-react-lite": "^4.1.0", + "next": "^15.3.2", + "node": "^20.19.2", + "qs": "^6.14.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sshpk": "^1.18.0" + }, + "devDependencies": { + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@eslint/config-array": "^0.20.0", + "@eslint/object-schema": "^2.1.6", + "@types/node": "^22.15.18", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.5", + "@types/xmldom": "^0.1.34", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", + "@vitejs/plugin-react": "^5.1.2", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "sass": "^1.69.5", + "typescript": "^5.8.3", + "vite": "^7.3.1" } }, - "@babel/helper-get-function-arity": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz", - "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==", - "dev": true, - "requires": { - "@babel/types": "^7.10.4" - }, + "node_modules/@babel/code-frame": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", + "license": "MIT", "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@babel/compat-data": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, - "requires": { - "@babel/types": "^7.22.5" - }, - "dependencies": { - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" - } - } + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-member-expression-to-functions": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz", - "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==", + "node_modules/@babel/core": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", "dev": true, - "requires": { - "@babel/types": "^7.11.0" + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@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.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", + "license": "MIT", "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-optimise-call-expression": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz", - "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==", + "node_modules/@babel/helper-annotate-as-pure": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.27.1.tgz", + "integrity": "sha512-WnuuDILl9oOBbKnb4L+DyODx7iC47XfzmNCpTttFsSp6hTG7XZxu60+4IO+2/hPfcGOoKbFiwoI/+zwARbNQow==", "dev": true, - "requires": { - "@babel/types": "^7.10.4" - }, + "license": "MIT", "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/helper-plugin-utils": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", - "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==", - "dev": true - }, - "@babel/helper-replace-supers": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz", - "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==", - "dev": true, - "requires": { - "@babel/helper-member-expression-to-functions": "^7.10.4", - "@babel/helper-optimise-call-expression": "^7.10.4", - "@babel/traverse": "^7.10.4", - "@babel/types": "^7.10.4" - }, - "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.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" } }, - "@babel/helper-split-export-declaration": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz", - "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==", + "node_modules/@babel/helper-create-class-features-plugin": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.27.1.tgz", + "integrity": "sha512-QwGAmuvM17btKU5VqXfb+Giw4JcN0hjuufz3DYnpeVDvZLAObloM77bhMXiqry3Iio+Ai4phVRDwl6WU10+r5A==", "dev": true, - "requires": { - "@babel/types": "^7.11.0" - }, + "license": "MIT", "dependencies": { - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/helper-annotate-as-pure": "^7.27.1", + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/helper-replace-supers": "^7.27.1", + "@babel/helper-skip-transparent-expression-wrappers": "^7.27.1", + "@babel/traverse": "^7.27.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", - "dev": true - }, - "@babel/helper-validator-identifier": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", - "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==", - "dev": true + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "@babel/highlight": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz", - "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==", + "node_modules/@babel/helper-member-expression-to-functions": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.27.1.tgz", + "integrity": "sha512-E5chM8eWjTp/aNoVpcbfM7mLxu9XGLWYise2eBKGQomAk/Mb4XoxyqXTZbuTohbsl8EKqdlMhnDI2CCLfcs9wA==", "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/parser": { - "version": "7.11.4", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.4.tgz", - "integrity": "sha512-MggwidiH+E9j5Sh8pbrX5sJvMcsqS5o+7iB42M9/k0CD63MjYbdP4nhSh7uB5wnv2/RVzTZFTxzF/kIa5mrCqA==", - "dev": true + "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==", + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@babel/plugin-proposal-class-properties": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.10.4.tgz", - "integrity": "sha512-vhwkEROxzcHGNu2mzUC0OFFNXdZ4M23ib8aRRcJSsW8BZK9pQMD7QB7csl97NBbgGZO7ZyHUyKDnxzOaP4IrCg==", + "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, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.4", - "@babel/helper-plugin-utils": "^7.10.4" + "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" } }, - "@babel/plugin-proposal-decorators": { - "version": "7.10.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.10.5.tgz", - "integrity": "sha512-Sc5TAQSZuLzgY0664mMDn24Vw2P8g/VhyLyGPaWiHahhgLqeZvcGeyBZOrJW0oSKIK2mvQ22a1ENXBIQLhrEiQ==", + "node_modules/@babel/helper-optimise-call-expression": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.27.1.tgz", + "integrity": "sha512-URMGH08NzYFhubNSGJrpUEphGKQwMQYBySzat5cAByY1/YgIRkULnIy3tAMeszlL/so2HbeilYloUmSpd7GdVw==", "dev": true, - "requires": { - "@babel/helper-create-class-features-plugin": "^7.10.5", - "@babel/helper-plugin-utils": "^7.10.4", - "@babel/plugin-syntax-decorators": "^7.10.4" + "license": "MIT", + "dependencies": { + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/plugin-syntax-decorators": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.10.4.tgz", - "integrity": "sha512-2NaoC6fAk2VMdhY1eerkfHV+lVYC1u8b+jmRJISqANCJlTxYy19HGdIkkQtix2UtkcPuPu+IlDgrVseZnU03bw==", + "node_modules/@babel/helper-plugin-utils": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", + "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, - "requires": { - "@babel/helper-plugin-utils": "^7.10.4" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@babel/runtime": { - "version": "7.9.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.9.6.tgz", - "integrity": "sha512-64AF1xY3OAkFHqOb9s4jpgk1Mm5vDZ4L3acHvAml+53nO1XbXLuDodsVpO4OIUsmemlUHMxNdYMNJmsvOwLrvQ==", - "requires": { - "regenerator-runtime": "^0.13.4" + "node_modules/@babel/helper-replace-supers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.27.1.tgz", + "integrity": "sha512-7EHz6qDZc8RYS5ElPoShMheWvEgERonFCs7IAonWLLUTXW59DP14bCZt89/GKyreYn8g3S83m21FelHKbeDCKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-member-expression-to-functions": "^7.27.1", + "@babel/helper-optimise-call-expression": "^7.27.1", + "@babel/traverse": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" } }, - "@babel/template": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz", - "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==", + "node_modules/@babel/helper-skip-transparent-expression-wrappers": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.27.1.tgz", + "integrity": "sha512-Tub4ZKEXqbPjXgWLl2+3JpQAYBJ8+ikpQ2Ocj/q/r0LwE3UhENh7EUabyHjz2kCEsrRY83ew2DQdHluuiDQFzg==", "dev": true, - "requires": { - "@babel/code-frame": "^7.10.4", - "@babel/parser": "^7.10.4", - "@babel/types": "^7.10.4" - }, + "license": "MIT", "dependencies": { - "@babel/code-frame": { - "version": "7.10.4", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz", - "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, - "@babel/types": { - "version": "7.11.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz", - "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.10.4", - "lodash": "^4.17.19", - "to-fast-properties": "^2.0.0" - } - } + "@babel/traverse": "^7.27.1", + "@babel/types": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" } }, - "@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" - }, - "dependencies": { - "@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", - "dev": true, - "requires": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" - } - }, - "@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", - "dev": true, - "requires": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" - } - }, - "@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", - "dev": true, - "requires": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - } - }, - "@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "requires": { - "@babel/types": "^7.22.5" - } - }, - "@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", - "dev": true - }, - "@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" - } - }, - "@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true - }, - "@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", - "dev": true, - "requires": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - } - }, - "@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, - "requires": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" + "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==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "@jridgewell/gen-mapping": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz", - "integrity": "sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ==", + "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, - "requires": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, - "@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "dev": true - }, - "@jridgewell/set-array": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.1.2.tgz", - "integrity": "sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw==", - "dev": true - }, - "@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", - "dev": true - }, - "@jridgewell/trace-mapping": { - "version": "0.3.19", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz", - "integrity": "sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==", - "dev": true, - "requires": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@babel/helpers": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" } }, - "@material-ui/core": { - "version": "4.11.0", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.11.0.tgz", - "integrity": "sha512-bYo9uIub8wGhZySHqLQ833zi4ZML+XCBE1XwJ8EuUVSpTWWG57Pm+YugQToJNFsEyiKFhPh8DPD0bgupz8n01g==", - "requires": { - "@babel/runtime": "^7.4.4", - "@material-ui/styles": "^4.10.0", - "@material-ui/system": "^4.9.14", - "@material-ui/types": "^5.1.0", - "@material-ui/utils": "^4.10.2", - "@types/react-transition-group": "^4.2.0", - "clsx": "^1.0.4", - "hoist-non-react-statics": "^3.3.2", - "popper.js": "1.16.1-lts", - "prop-types": "^15.7.2", - "react-is": "^16.8.0", - "react-transition-group": "^4.4.0" - } - }, - "@material-ui/icons": { - "version": "4.9.1", - "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.9.1.tgz", - "integrity": "sha512-GBitL3oBWO0hzBhvA9KxqcowRUsA0qzwKkURyC8nppnC3fw54KPKZ+d4V1Eeg/UnDRSzDaI9nGCdel/eh9AQMg==", - "requires": { - "@babel/runtime": "^7.4.4" - } - }, - "@material-ui/styles": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.10.0.tgz", - "integrity": "sha512-XPwiVTpd3rlnbfrgtEJ1eJJdFCXZkHxy8TrdieaTvwxNYj42VnnCyFzxYeNW9Lhj4V1oD8YtQ6S5Gie7bZDf7Q==", - "requires": { - "@babel/runtime": "^7.4.4", - "@emotion/hash": "^0.8.0", - "@material-ui/types": "^5.1.0", - "@material-ui/utils": "^4.9.6", - "clsx": "^1.0.4", - "csstype": "^2.5.2", - "hoist-non-react-statics": "^3.3.2", - "jss": "^10.0.3", - "jss-plugin-camel-case": "^10.0.3", - "jss-plugin-default-unit": "^10.0.3", - "jss-plugin-global": "^10.0.3", - "jss-plugin-nested": "^10.0.3", - "jss-plugin-props-sort": "^10.0.3", - "jss-plugin-rule-value-function": "^10.0.3", - "jss-plugin-vendor-prefixer": "^10.0.3", - "prop-types": "^15.7.2" - }, - "dependencies": { - "csstype": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", - "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" - } + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.6" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" } }, - "@material-ui/system": { - "version": "4.9.14", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.9.14.tgz", - "integrity": "sha512-oQbaqfSnNlEkXEziDcJDDIy8pbvwUmZXWNqlmIwDqr/ZdCK8FuV3f4nxikUh7hvClKV2gnQ9djh5CZFTHkZj3w==", - "requires": { - "@babel/runtime": "^7.4.4", - "@material-ui/utils": "^4.9.6", - "csstype": "^2.5.2", - "prop-types": "^15.7.2" - }, + "node_modules/@babel/plugin-proposal-class-properties": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-class-properties/-/plugin-proposal-class-properties-7.18.6.tgz", + "integrity": "sha512-cumfXOF0+nzZrrN8Rf0t7M+tF6sZc7vhQwYQck9q1/5w2OExlD+b4v4RpMJFaV1Z7WcDRgO6FqvxqxGlwo+RHQ==", + "deprecated": "This proposal has been merged to the ECMAScript standard and thus this plugin is no longer maintained. Please use @babel/plugin-transform-class-properties instead.", + "dev": true, + "license": "MIT", "dependencies": { - "csstype": { - "version": "2.6.13", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.13.tgz", - "integrity": "sha512-ul26pfSQTZW8dcOnD2iiJssfXw0gdNVX9IJDH/X3K5DGPfj+fUYe3kB+swUY6BF3oZDxaID3AJt+9/ojSAE05A==" - } + "@babel/helper-create-class-features-plugin": "^7.18.6", + "@babel/helper-plugin-utils": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@material-ui/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", - "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" + "node_modules/@babel/plugin-proposal-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-proposal-decorators/-/plugin-proposal-decorators-7.27.1.tgz", + "integrity": "sha512-DTxe4LBPrtFdsWzgpmbBKevg3e9PBy+dXRt19kSbucbZvL2uqtdqwwpluL1jfxYE0wIDTFp1nTy/q6gNLsxXrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1", + "@babel/plugin-syntax-decorators": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@material-ui/utils": { - "version": "4.10.2", - "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.10.2.tgz", - "integrity": "sha512-eg29v74P7W5r6a4tWWDAAfZldXIzfyO1am2fIsC39hdUUHm/33k6pGOKPbgDjg/U/4ifmgAePy/1OjkKN6rFRw==", - "requires": { - "@babel/runtime": "^7.4.4", - "prop-types": "^15.7.2", - "react-is": "^16.8.0" + "node_modules/@babel/plugin-syntax-decorators": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-decorators/-/plugin-syntax-decorators-7.27.1.tgz", + "integrity": "sha512-YMq8Z87Lhl8EGkmb0MwYkt36QnxC+fzCgrl66ereamPlYToRpIk5nUjKUY3QKLWq8mwUB1BgbeXcTJhZOCDg5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" } }, - "@next/env": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/env/-/env-13.5.5.tgz", - "integrity": "sha512-agvIhYWp+ilbScg81s/sLueZo8CNEYLjNOqhISxheLmD/AQI4/VxV7bV76i/KzxH4iHy/va0YS9z0AOwGnw4Fg==" + "node_modules/@babel/plugin-transform-class-properties": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-class-properties/-/plugin-transform-class-properties-7.27.1.tgz", + "integrity": "sha512-D0VcalChDMtuRvJIu3U/fwWjf8ZMykz5iZsg77Nuj821vCKI3zCyRLwRdWbsuJ/uRwZhZ002QtCqIkwC/ZkvbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-create-class-features-plugin": "^7.27.1", + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@next/swc-darwin-arm64": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-13.5.5.tgz", - "integrity": "sha512-FvTdcJdTA7H1FGY8dKPPbf/O0oDC041/znHZwXA7liiGUhgw5hOQ+9z8tWvuz0M5a/SDjY/IRPBAb5FIFogYww==", - "optional": true + "node_modules/@babel/plugin-transform-react-jsx-self": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@next/swc-darwin-x64": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.5.5.tgz", - "integrity": "sha512-mTqNIecaojmyia7appVO2QggBe1Z2fdzxgn6jb3x9qlAk8yY2sy4MAcsj71kC9RlenCqDmr9vtC/ESFf110TPA==", - "optional": true + "node_modules/@babel/plugin-transform-react-jsx-source": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.27.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } }, - "@next/swc-linux-arm64-gnu": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.5.5.tgz", - "integrity": "sha512-U9e+kNkfvwh/T8yo+xcslvNXgyMzPPX1IbwCwnHHFmX5ckb1Uc3XZSInNjFQEQR5xhJpB5sFdal+IiBIiLYkZA==", - "optional": true + "node_modules/@babel/runtime": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.1.tgz", + "integrity": "sha512-1x3D2xEk2fRo3PAhwQwu5UubzgiVWSXTBfWpVd2Mx2AzRqJuDJCsgaDVZ7HB5iGzDW1Hl1sWN2mFyKjmR9uAog==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } }, - "@next/swc-linux-arm64-musl": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.5.5.tgz", - "integrity": "sha512-h7b58eIoNCSmKVC5fr167U0HWZ/yGLbkKD9wIller0nGdyl5zfTji0SsPKJvrG8jvKPFt2xOkVBmXlFOtuKynw==", - "optional": true + "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==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@next/swc-linux-x64-gnu": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.5.5.tgz", - "integrity": "sha512-6U4y21T1J6FfcpM9uqzBJicxycpB5gJKLyQ3g6KOfBzT8H1sMwfHTRrvHKB09GIn1BCRy5YJHrA1G26DzqR46w==", - "optional": true + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@next/swc-linux-x64-musl": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.5.5.tgz", - "integrity": "sha512-OuqWSAQHJQM2EsapPFTSU/FLQ0wKm7UeRNatiR/jLeCe1V02aB9xmzuWYo2Neaxxag4rss3S8fj+lvMLzwDaFA==", - "optional": true + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } }, - "@next/swc-win32-arm64-msvc": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.5.5.tgz", - "integrity": "sha512-+yLrOZIIZDY4uGn9bLOc0wTgs+M8RuOUFSUK3BhmcLav9e+tcAj0jyBHD4aXv2qWhppUeuYMsyBo1I58/eE6Dg==", - "optional": true + "node_modules/@emnapi/runtime": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.4.3.tgz", + "integrity": "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } }, - "@next/swc-win32-ia32-msvc": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.5.5.tgz", - "integrity": "sha512-SyMxXyJtf9ScMH0Dh87THJMXNFvfkRAk841xyW9SeOX3KxM1buXX3hN7vof4kMGk0Yg996OGsX+7C9ueS8ugsw==", - "optional": true + "node_modules/@emotion/babel-plugin": { + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.16.7", + "@babel/runtime": "^7.18.3", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", + "babel-plugin-macros": "^3.1.0", + "convert-source-map": "^1.5.0", + "escape-string-regexp": "^4.0.0", + "find-root": "^1.1.0", + "source-map": "^0.5.7", + "stylis": "4.2.0" + } }, - "@next/swc-win32-x64-msvc": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.5.5.tgz", - "integrity": "sha512-n5KVf2Ok0BbLwofAaHiiKf+BQCj1M8WmTujiER4/qzYAVngnsNSjqEWvJ03raeN9eURqxDO+yL5VRoDrR33H9A==", - "optional": true + "node_modules/@emotion/babel-plugin/node_modules/convert-source-map": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" }, - "@swc/helpers": { - "version": "0.5.2", - "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.2.tgz", - "integrity": "sha512-E4KcWTpoLHqwPHLxidpOqQbcrZVgi0rsmmZXUle1jXmJfuIf/UWpczUJ7MZZ5tlxytgJXyp0w4PGkkeLiuIdZw==", - "requires": { - "tslib": "^2.4.0" + "node_modules/@emotion/cache": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "stylis": "4.2.0" } }, - "@types/classnames": { - "version": "2.2.10", - "resolved": "https://registry.npmjs.org/@types/classnames/-/classnames-2.2.10.tgz", - "integrity": "sha512-1UzDldn9GfYYEsWWnn/P4wkTlkZDH7lDb0wBMGbtIQc9zXEQq7FlKBdZUn6OBqD8sKZZ2RQO2mAjGpXiDGoRmQ==", - "dev": true - }, - "@types/node": { - "version": "14.6.2", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.2.tgz", - "integrity": "sha512-onlIwbaeqvZyniGPfdw/TEhKIh79pz66L1q06WUQqJLnAb6wbjvOtepLYTGHTqzdXgBYIE3ZdmqHDGsRsbBz7A==", - "dev": true - }, - "@types/prop-types": { - "version": "15.7.3", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" - }, - "@types/react": { - "version": "16.9.48", - "resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.48.tgz", - "integrity": "sha512-4ykBVswgYitPGMXFRxJCHkxJDU2rjfU3/zw67f8+dB7sNdVJXsrwqoYxz/stkAucymnEEbRPFmX7Ce5Mc/kJCw==", - "requires": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } + "node_modules/@emotion/hash": { + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, - "@types/react-transition-group": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.0.tgz", - "integrity": "sha512-/QfLHGpu+2fQOqQaXh8MG9q03bFENooTb/it4jr5kKaZlDQfWvjqWZg48AwzPVMBHlRuTRAY7hRHCEOXz5kV6w==", - "requires": { - "@types/react": "*" + "node_modules/@emotion/is-prop-valid": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.3.1.tgz", + "integrity": "sha512-/ACwoqx7XQi9knQs/G0qKvv5teDMhD7bXYns9N/wM8ah8iNb8jZ2uNO0YOgiq2o2poIvVtJS2YALasQuMSQ7Kw==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0" } }, - "@types/xmldom": { - "version": "0.1.30", - "resolved": "https://registry.npmjs.org/@types/xmldom/-/xmldom-0.1.30.tgz", - "integrity": "sha512-edqgAFXMEtVvaBZ3YnhamvmrHjoYpuxETmnb0lbTZmf/dXpAsO9ZKotUO4K2rn2SIZBDFCMOuA7fOe0H6dRZcA==", - "dev": true + "node_modules/@emotion/memoize": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "requires": { - "color-convert": "^1.9.0" + "node_modules/@emotion/react": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", + "hoist-non-react-statics": "^3.3.1" + }, + "peerDependencies": { + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, - "busboy": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", - "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", - "requires": { - "streamsearch": "^1.1.0" + "node_modules/@emotion/serialize": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", + "dependencies": { + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", + "csstype": "^3.0.2" } }, - "caniuse-lite": { - "version": "1.0.30001549", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001549.tgz", - "integrity": "sha512-qRp48dPYSCYaP+KurZLhDYdVE+yEyht/3NlmcJgVQ2VMGt6JL36ndQ/7rgspdZsJuxDPFIo/OzBT2+GmIJ53BA==" + "node_modules/@emotion/sheet": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, + "node_modules/@emotion/styled": { + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.0.tgz", + "integrity": "sha512-XxfOnXFffatap2IyCeJyNov3kiDQWoR08gPUQxvbL7fxKryGBKUZUkG6Hz48DZwVrJSVh9sJboyV1Ds4OW6SgA==", + "license": "MIT", "dependencies": { - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } + "@babel/runtime": "^7.18.3", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" + }, + "peerDependencies": { + "@emotion/react": "^11.0.0-rc.0", + "react": ">=16.8.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true } } }, - "classnames": { - "version": "2.2.6", - "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz", - "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q==" + "node_modules/@emotion/unitless": { + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" }, - "client-only": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", - "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==" + "node_modules/@emotion/use-insertion-effect-with-fallbacks": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", + "peerDependencies": { + "react": ">=16.8.0" + } }, - "clsx": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-1.1.1.tgz", - "integrity": "sha512-6/bPho624p3S2pMyvP5kKBPXnI3ufHLObBFCfgx+LkeR5lg2XYy2hqZqUf45ypD8COn2bhgGJSUE+l5dhNBieA==" + "node_modules/@emotion/utils": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" + }, + "node_modules/@emotion/weak-memoize": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], "dev": true, - "requires": { - "color-name": "1.1.3" + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" } }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, - "css-vendor": { - "version": "2.0.8", - "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", - "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", - "requires": { - "@babel/runtime": "^7.8.3", - "is-in-browser": "^1.0.2" + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" } }, - "csstype": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.3.tgz", - "integrity": "sha512-jPl+wbWPOWJ7SXsWyqGRk3lGecbar0Cb0OvZF/r/ZU011R4YqiRehgkQ9p4eQfo9DSDLqLL3wHwfxeJiuIsNag==" + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } }, - "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], "dev": true, - "requires": { - "ms": "^2.1.1" + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "dom-helpers": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.0.tgz", - "integrity": "sha512-Ru5o9+V8CpunKnz5LGgWXkmrH/20cGKwcHwS4m73zIvs54CN9epEmT/HLqFJW3kXpakAFkEdzgy1hzlJe3E4OQ==", - "requires": { - "@babel/runtime": "^7.8.7", - "csstype": "^3.0.2" + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" } }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==" + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } }, - "globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "dev": true + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "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==" + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "hoist-non-react-statics": { - "version": "3.3.2", - "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", - "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", - "requires": { - "react-is": "^16.7.0" + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "hyphenate-style-name": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz", - "integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==" + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "is-in-browser": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", - "integrity": "sha1-Vv9NtoOgeMYILrldrX3GLh0E+DU=" + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" - }, - "jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", - "dev": true - }, - "jss": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss/-/jss-10.4.0.tgz", - "integrity": "sha512-l7EwdwhsDishXzqTc3lbsbyZ83tlUl5L/Hb16pHCvZliA9lRDdNBZmHzeJHP0sxqD0t1mrMmMR8XroR12JBYzw==", - "requires": { - "@babel/runtime": "^7.3.1", - "csstype": "^3.0.2", - "is-in-browser": "^1.1.3", - "tiny-warning": "^1.0.2" + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-camel-case": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.4.0.tgz", - "integrity": "sha512-9oDjsQ/AgdBbMyRjc06Kl3P8lDCSEts2vYZiPZfGAxbGCegqE4RnMob3mDaBby5H9vL9gWmyyImhLRWqIkRUCw==", - "requires": { - "@babel/runtime": "^7.3.1", - "hyphenate-style-name": "^1.0.3", - "jss": "10.4.0" + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-default-unit": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.4.0.tgz", - "integrity": "sha512-BYJ+Y3RUYiMEgmlcYMLqwbA49DcSWsGgHpVmEEllTC8MK5iJ7++pT9TnKkKBnNZZxTV75ycyFCR5xeLSOzVm4A==", - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.4.0" + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-global": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.4.0.tgz", - "integrity": "sha512-b8IHMJUmv29cidt3nI4bUI1+Mo5RZE37kqthaFpmxf5K7r2aAegGliAw4hXvA70ca6ckAoXMUl4SN/zxiRcRag==", - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.4.0" + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-nested": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.4.0.tgz", - "integrity": "sha512-cKgpeHIxAP0ygeWh+drpLbrxFiak6zzJ2toVRi/NmHbpkNaLjTLgePmOz5+67ln3qzJiPdXXJB1tbOyYKAP4Pw==", - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.4.0", - "tiny-warning": "^1.0.2" + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-props-sort": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.4.0.tgz", - "integrity": "sha512-j/t0R40/2fp+Nzt6GgHeUFnHVY2kPGF5drUVlgkcwYoHCgtBDOhTTsOfdaQFW6sHWfoQYgnGV4CXdjlPiRrzwA==", - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.4.0" + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-rule-value-function": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.4.0.tgz", - "integrity": "sha512-w8504Cdfu66+0SJoLkr6GUQlEb8keHg8ymtJXdVHWh0YvFxDG2l/nS93SI5Gfx0fV29dO6yUugXnKzDFJxrdFQ==", - "requires": { - "@babel/runtime": "^7.3.1", - "jss": "10.4.0", - "tiny-warning": "^1.0.2" + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, - "jss-plugin-vendor-prefixer": { - "version": "10.4.0", - "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.4.0.tgz", - "integrity": "sha512-DpF+/a+GU8hMh/948sBGnKSNfKkoHg2p9aRFUmyoyxgKjOeH9n74Ht3Yt8lOgdZsuWNJbPrvaa3U4PXKwxVpTQ==", - "requires": { - "@babel/runtime": "^7.3.1", - "css-vendor": "^2.0.8", - "jss": "10.4.0" + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" } }, - "lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } }, - "loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "requires": { - "js-tokens": "^3.0.0 || ^4.0.0" + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" } }, - "mobx": { - "version": "5.15.7", - "resolved": "https://registry.npmjs.org/mobx/-/mobx-5.15.7.tgz", - "integrity": "sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw==" + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "mobx-react-lite": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-2.2.2.tgz", - "integrity": "sha512-2SlXALHIkyUPDsV4VTKVR9DW7K3Ksh1aaIv3NrNJygTbhXe2A9GrcKHZ2ovIiOp/BXilOcTYemfHHZubP431dg==" + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "nanoid": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.6.tgz", - "integrity": "sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==" - }, - "next": { - "version": "13.5.5", - "resolved": "https://registry.npmjs.org/next/-/next-13.5.5.tgz", - "integrity": "sha512-LddFJjpfrtrMMw8Q9VLhIURuSidiCNcMQjRqcPtrKd+Fx07MsG7hYndJb/f2d3I+mTbTotsTJfCnn0eZ/YPk8w==", - "requires": { - "@next/env": "13.5.5", - "@next/swc-darwin-arm64": "13.5.5", - "@next/swc-darwin-x64": "13.5.5", - "@next/swc-linux-arm64-gnu": "13.5.5", - "@next/swc-linux-arm64-musl": "13.5.5", - "@next/swc-linux-x64-gnu": "13.5.5", - "@next/swc-linux-x64-musl": "13.5.5", - "@next/swc-win32-arm64-msvc": "13.5.5", - "@next/swc-win32-ia32-msvc": "13.5.5", - "@next/swc-win32-x64-msvc": "13.5.5", - "@swc/helpers": "0.5.2", - "busboy": "1.6.0", - "caniuse-lite": "^1.0.30001406", - "postcss": "8.4.31", - "styled-jsx": "5.1.1", - "watchpack": "2.4.0" + "node_modules/@eslint-community/eslint-utils": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", + "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, - "object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + "node_modules/@eslint-community/regexpp": { + "version": "4.12.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", + "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } }, - "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "node_modules/@eslint/config-array": { + "version": "0.20.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", + "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.6", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } }, - "popper.js": { - "version": "1.16.1-lts", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", - "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } }, - "postcss": { - "version": "8.4.31", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", + "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/core": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/eslintrc": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", + "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ajv": "^6.12.4", + "debug": "^4.3.2", + "espree": "^10.0.1", + "globals": "^14.0.0", + "ignore": "^5.2.0", + "import-fresh": "^3.2.1", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", + "strip-json-comments": "^3.1.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "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/eslintrc/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/js": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.27.0.tgz", + "integrity": "sha512-G5JD9Tu5HJEu4z2Uo4aHY2sLV64B7CDMXxFzqzjl3NKd6RVzSXNoE80jk7Y0lJkTTkjiIhBAqmlYwjuBY3tvpA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + } + }, + "node_modules/@eslint/object-schema": { + "version": "2.1.6", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", + "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.1.tgz", + "integrity": "sha512-0J+zgWxHN+xXONWIyPWKFMgVuJoZuGiIFu8yxk7RJjxkzpGmyja5wRFqZIVtjDVOQpV+Rw0iOAjYPE2eQyjr0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.14.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.6", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", + "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.3.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", + "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.1.tgz", + "integrity": "sha512-pn44xgBtgpEbZsu+lWf2KNb6OAf70X68k+yk69Ic2Xz11zHR/w24/U49XT7AeRwJ0Px+mhALhU5LPci1Aymk7A==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.1.tgz", + "integrity": "sha512-VfuYgG2r8BpYiOUN+BfYeFo69nP/MIwAtSJ7/Zpxc5QF3KS22z8Pvg3FkrSFJBPNQ7mmcUcYQFBmEQp7eu1F8Q==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.1.0.tgz", + "integrity": "sha512-HZ/JUmPwrJSoM4DIQPv/BfNh9yrOA8tlBbqbLz4JZ5uew2+o22Ik+tHQJcih7QJuSa0zo5coHTfD5J8inqj9DA==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.1.0.tgz", + "integrity": "sha512-Xzc2ToEmHN+hfvsl9wja0RlnXEgpKNmftriQp6XzY/RaSfwD9th+MSh0WQKzUreLKKINb3afirxW7A0fz2YWuQ==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.1.0.tgz", + "integrity": "sha512-s8BAd0lwUIvYCJyRdFqvsj+BJIpDBSxs6ivrOPm/R7piTs5UIwY5OjXrP2bqXC9/moGsyRa37eYWYCOGVXxVrA==", + "cpu": [ + "arm" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.1.0.tgz", + "integrity": "sha512-IVfGJa7gjChDET1dK9SekxFFdflarnUB8PwW8aGwEoF3oAsSDuNUTYS+SKDOyOJxQyDC1aPFMuRYLoDInyV9Ew==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.1.0.tgz", + "integrity": "sha512-tiXxFZFbhnkWE2LA8oQj7KYR+bWBkiV2nilRldT7bqoEZ4HiDOcePr9wVDAZPi/Id5fT1oY9iGnDq20cwUz8lQ==", + "cpu": [ + "ppc64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.1.0.tgz", + "integrity": "sha512-xukSwvhguw7COyzvmjydRb3x/09+21HykyapcZchiCUkTThEQEOMtBj9UhkaBRLuBrgLFzQ2wbxdeCCJW/jgJA==", + "cpu": [ + "s390x" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.1.0.tgz", + "integrity": "sha512-yRj2+reB8iMg9W5sULM3S74jVS7zqSzHG3Ol/twnAAkAhnGQnpjj6e4ayUz7V+FpKypwgs82xbRdYtchTTUB+Q==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.1.0.tgz", + "integrity": "sha512-jYZdG+whg0MDK+q2COKbYidaqW/WTz0cc1E+tMAusiDygrM4ypmSCjOJPmFTvHHJ8j/6cAGyeDWZOsK06tP33w==", + "cpu": [ + "arm64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.1.0.tgz", + "integrity": "sha512-wK7SBdwrAiycjXdkPnGCPLjYb9lD4l6Ze2gSdAGVZrEL05AOUJESWU2lhlC+Ffn5/G+VKuSm6zzbQSzFX/P65A==", + "cpu": [ + "x64" + ], + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.1.tgz", + "integrity": "sha512-anKiszvACti2sGy9CirTlNyk7BjjZPiML1jt2ZkTdcvpLU1YH6CXwRAZCA2UmRXnhiIftXQ7+Oh62Ji25W72jA==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.1.tgz", + "integrity": "sha512-kX2c+vbvaXC6vly1RDf/IWNXxrlxLNpBVWkdpRq5Ka7OOKj6nr66etKy2IENf6FtOgklkg9ZdGpEu9kwdlcwOQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.1.tgz", + "integrity": "sha512-7s0KX2tI9mZI2buRipKIw2X1ufdTeaRgwmRabt5bi9chYfhur+/C1OXg3TKg/eag1W+6CCWLVmSauV1owmRPxA==", + "cpu": [ + "s390x" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.1.0" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.1.tgz", + "integrity": "sha512-wExv7SH9nmoBW3Wr2gvQopX1k8q2g5V5Iag8Zk6AVENsjwd+3adjwxtp3Dcu2QhOXr8W9NusBU6XcQUohBZ5MA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.1.tgz", + "integrity": "sha512-DfvyxzHxw4WGdPiTF0SOHnm11Xv4aQexvqhRDAoD00MzHekAj9a/jADXeXYCDFH/DzYruwHbXU7uz+H+nWmSOQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.1.tgz", + "integrity": "sha512-pax/kTR407vNb9qaSIiWVnQplPcGU8LRIJpDT5o8PdAx5aAA7AS3X9PS8Isw1/WfqgQorPotjrZL3Pqh6C5EBg==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.1.0" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.1.tgz", + "integrity": "sha512-YDybQnYrLQfEpzGOQe7OKcyLUCML4YOXl428gOOzBgN6Gw0rv8dpsJ7PqTHxBnXnwXr8S1mYFSLSa727tpz0xg==", + "cpu": [ + "wasm32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.4.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.1.tgz", + "integrity": "sha512-WKf/NAZITnonBf3U1LfdjoMgNO5JYRSlhovhRhMxXVdvWYveM4kM3L8m35onYIdh75cOMCo1BexgVQcCDzyoWw==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.1.tgz", + "integrity": "sha512-hw1iIAHpNE8q3uMIRCgGOeDoz9KtFNarFLQclLxr/LK1VBkj8nby18RjFvr6aP7USRYAjTZW6yisnBWMX571Tw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@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==", + "dev": true, + "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/@mui/core-downloads-tracker": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-7.1.0.tgz", + "integrity": "sha512-E0OqhZv548Qdc0PwWhLVA2zmjJZSTvaL4ZhoswmI8NJEC1tpW2js6LLP827jrW9MEiXYdz3QS6+hask83w74yQ==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + } + }, + "node_modules/@mui/icons-material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-7.1.0.tgz", + "integrity": "sha512-1mUPMAZ+Qk3jfgL5ftRR06ATH/Esi0izHl1z56H+df6cwIlCWG66RXciUqeJCttbOXOQ5y2DCjLZI/4t3Yg3LA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@mui/material": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/material": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-7.1.0.tgz", + "integrity": "sha512-ahUJdrhEv+mCp4XHW+tHIEYzZMSRLg8z4AjUOsj44QpD1ZaMxQoVOG2xiHvLFdcsIPbgSRx1bg1eQSheHBgvtg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/core-downloads-tracker": "^7.1.0", + "@mui/system": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.1.0", + "react-transition-group": "^4.4.5" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@mui/material-pigment-css": "^7.1.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@mui/material-pigment-css": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz", + "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.9", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/private-theming/node_modules/@mui/utils": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", + "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "~7.2.24", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styled-engine": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-7.1.0.tgz", + "integrity": "sha512-m0mJ0c6iRC+f9hMeRe0W7zZX1wme3oUX0+XTVHjPG7DJz6OdQ6K/ggEOq7ZdwilcpdsDUwwMfOmvO71qDkYd2w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.4.1", + "@emotion/styled": "^11.3.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + } + } + }, + "node_modules/@mui/styles": { + "version": "6.4.11", + "resolved": "https://registry.npmjs.org/@mui/styles/-/styles-6.4.11.tgz", + "integrity": "sha512-tuF8UT5d6gO4u2pKyYrgVGzbQtIJodILkBwB3iBy7Pg2htvX5ecNyEcKI2d0LQPNHt1ouECaF72GVuQTWLH0dA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@emotion/hash": "^0.9.2", + "@mui/private-theming": "^6.4.9", + "@mui/types": "~7.2.24", + "@mui/utils": "^6.4.9", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "hoist-non-react-statics": "^3.3.2", + "jss": "^10.10.0", + "jss-plugin-camel-case": "^10.10.0", + "jss-plugin-default-unit": "^10.10.0", + "jss-plugin-global": "^10.10.0", + "jss-plugin-nested": "^10.10.0", + "jss-plugin-props-sort": "^10.10.0", + "jss-plugin-rule-value-function": "^10.10.0", + "jss-plugin-vendor-prefixer": "^10.10.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styles/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/styles/node_modules/@mui/utils": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", + "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/types": "~7.2.24", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.0.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-7.1.0.tgz", + "integrity": "sha512-iedAWgRJMCxeMHvkEhsDlbvkK+qKf9me6ofsf7twk/jfT4P1ImVf7Rwb5VubEA0sikrVL+1SkoZM41M4+LNAVA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/private-theming": "^7.1.0", + "@mui/styled-engine": "^7.1.0", + "@mui/types": "^7.4.2", + "@mui/utils": "^7.1.0", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@emotion/react": "^11.5.0", + "@emotion/styled": "^11.3.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@emotion/react": { + "optional": true + }, + "@emotion/styled": { + "optional": true + }, + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/system/node_modules/@mui/private-theming": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-7.1.0.tgz", + "integrity": "sha512-4Kck4jxhqF6YxNwJdSae1WgDfXVg0lIH6JVJ7gtuFfuKcQCgomJxPvUEOySTFRPz1IZzwz5OAcToskRdffElDA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/utils": "^7.1.0", + "prop-types": "^15.8.1" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/types": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.4.2.tgz", + "integrity": "sha512-edRc5JcLPsrlNFYyTPxds+d5oUovuUxnnDtpJUbP6WMeV4+6eaX/mqai1ZIWT62lCOe0nlrON0s9HDiv5en5bA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@mui/utils": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-7.1.0.tgz", + "integrity": "sha512-/OM3S8kSHHmWNOP+NH9xEtpYSG10upXeQ0wLZnfDgmgadTAk5F4MQfFLyZ5FCRJENB3eRzltMmaNl6UtDnPovw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.1", + "@mui/types": "^7.4.2", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", + "prop-types": "^15.8.1", + "react-is": "^19.1.0" + }, + "engines": { + "node": ">=14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mui-org" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/@next/env": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/env/-/env-15.3.2.tgz", + "integrity": "sha512-xURk++7P7qR9JG1jJtLzPzf0qEvqCN0A/T3DXf8IPMKo9/6FfjxtEffRJIIew/bIL4T3C2jLLqBor8B/zVlx6g==", + "license": "MIT" + }, + "node_modules/@next/swc-darwin-arm64": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-15.3.2.tgz", + "integrity": "sha512-2DR6kY/OGcokbnCsjHpNeQblqCZ85/1j6njYSkzRdpLn5At7OkSdmk7WyAmB9G0k25+VgqVZ/u356OSoQZ3z0g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-15.3.2.tgz", + "integrity": "sha512-ro/fdqaZWL6k1S/5CLv1I0DaZfDVJkWNaUU3un8Lg6m0YENWlDulmIWzV96Iou2wEYyEsZq51mwV8+XQXqMp3w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-15.3.2.tgz", + "integrity": "sha512-covwwtZYhlbRWK2HlYX9835qXum4xYZ3E2Mra1mdQ+0ICGoMiw1+nVAn4d9Bo7R3JqSmK1grMq/va+0cdh7bJA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-15.3.2.tgz", + "integrity": "sha512-KQkMEillvlW5Qk5mtGA/3Yz0/tzpNlSw6/3/ttsV1lNtMuOHcGii3zVeXZyi4EJmmLDKYcTcByV2wVsOhDt/zg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-15.3.2.tgz", + "integrity": "sha512-uRBo6THWei0chz+Y5j37qzx+BtoDRFIkDzZjlpCItBRXyMPIg079eIkOCl3aqr2tkxL4HFyJ4GHDes7W8HuAUg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-15.3.2.tgz", + "integrity": "sha512-+uxFlPuCNx/T9PdMClOqeE8USKzj8tVz37KflT3Kdbx/LOlZBRI2yxuIcmx1mPNK8DwSOMNCr4ureSet7eyC0w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-15.3.2.tgz", + "integrity": "sha512-LLTKmaI5cfD8dVzh5Vt7+OMo+AIOClEdIU/TSKbXXT2iScUTSxOGoBhfuv+FU8R9MLmrkIL1e2fBMkEEjYAtPQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-15.3.2.tgz", + "integrity": "sha512-aW5B8wOPioJ4mBdMDXkt5f3j8pUr9W8AnlX0Df35uRWNT1Y6RIybxjnSUe+PhM+M1bwgyY8PHLmXZC6zT1o5tA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@parcel/watcher": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz", + "integrity": "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.5.4", + "@parcel/watcher-darwin-arm64": "2.5.4", + "@parcel/watcher-darwin-x64": "2.5.4", + "@parcel/watcher-freebsd-x64": "2.5.4", + "@parcel/watcher-linux-arm-glibc": "2.5.4", + "@parcel/watcher-linux-arm-musl": "2.5.4", + "@parcel/watcher-linux-arm64-glibc": "2.5.4", + "@parcel/watcher-linux-arm64-musl": "2.5.4", + "@parcel/watcher-linux-x64-glibc": "2.5.4", + "@parcel/watcher-linux-x64-musl": "2.5.4", + "@parcel/watcher-win32-arm64": "2.5.4", + "@parcel/watcher-win32-ia32": "2.5.4", + "@parcel/watcher-win32-x64": "2.5.4" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.4.tgz", + "integrity": "sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.4.tgz", + "integrity": "sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.4.tgz", + "integrity": "sha512-UKaQFhCtNJW1A9YyVz3Ju7ydf6QgrpNQfRZ35wNKUhTQ3dxJ/3MULXN5JN/0Z80V/KUBDGa3RZaKq1EQT2a2gg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.4.tgz", + "integrity": "sha512-Dib0Wv3Ow/m2/ttvLdeI2DBXloO7t3Z0oCp4bAb2aqyqOjKPPGrg10pMJJAQ7tt8P4V2rwYwywkDhUia/FgS+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.4.tgz", + "integrity": "sha512-I5Vb769pdf7Q7Sf4KNy8Pogl/URRCKu9ImMmnVKYayhynuyGYMzuI4UOWnegQNa2sGpsPSbzDsqbHNMyeyPCgw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.4.tgz", + "integrity": "sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.4.tgz", + "integrity": "sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.4.tgz", + "integrity": "sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.4.tgz", + "integrity": "sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.4.tgz", + "integrity": "sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.4.tgz", + "integrity": "sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-ia32": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.4.tgz", + "integrity": "sha512-vQN+KIReG0a2ZDpVv8cgddlf67J8hk1WfZMMP7sMeZmJRSmEax5xNDNWKdgqSe2brOKTQQAs3aCCUal2qBHAyg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.4.tgz", + "integrity": "sha512-3A6efb6BOKwyw7yk9ro2vus2YTt2nvcd56AuzxdMiVOxL9umDyN5PKkKfZ/gZ9row41SjVmTVQNWQhaRRGpOKw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.53", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.53.tgz", + "integrity": "sha512-vENRlFU4YbrwVqNDZ7fLvy+JR1CRkyr01jhSiDpE1u6py3OMzQfztQU2jxykW3ALNxO4kSlqIDeYyD0Y9RcQeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.55.2.tgz", + "integrity": "sha512-21J6xzayjy3O6NdnlO6aXi/urvSRjm6nCI6+nF6ra2YofKruGixN9kfT+dt55HVNwfDmpDHJcaS3JuP/boNnlA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.55.2.tgz", + "integrity": "sha512-eXBg7ibkNUZ+sTwbFiDKou0BAckeV6kIigK7y5Ko4mB/5A1KLhuzEKovsmfvsL8mQorkoincMFGnQuIT92SKqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.55.2.tgz", + "integrity": "sha512-UCbaTklREjrc5U47ypLulAgg4njaqfOVLU18VrCrI+6E5MQjuG0lSWaqLlAJwsD7NpFV249XgB0Bi37Zh5Sz4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.55.2.tgz", + "integrity": "sha512-dP67MA0cCMHFT2g5XyjtpVOtp7y4UyUxN3dhLdt11at5cPKnSm4lY+EhwNvDXIMzAMIo2KU+mc9wxaAQJTn7sQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.55.2.tgz", + "integrity": "sha512-WDUPLUwfYV9G1yxNRJdXcvISW15mpvod1Wv3ok+Ws93w1HjIVmCIFxsG2DquO+3usMNCpJQ0wqO+3GhFdl6Fow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.55.2.tgz", + "integrity": "sha512-Ng95wtHVEulRwn7R0tMrlUuiLVL/HXA8Lt/MYVpy88+s5ikpntzZba1qEulTuPnPIZuOPcW9wNEiqvZxZmgmqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.55.2.tgz", + "integrity": "sha512-AEXMESUDWWGqD6LwO/HkqCZgUE1VCJ1OhbvYGsfqX2Y6w5quSXuyoy/Fg3nRqiwro+cJYFxiw5v4kB2ZDLhxrw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.55.2.tgz", + "integrity": "sha512-ZV7EljjBDwBBBSv570VWj0hiNTdHt9uGznDtznBB4Caj3ch5rgD4I2K1GQrtbvJ/QiB+663lLgOdcADMNVC29Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.55.2.tgz", + "integrity": "sha512-uvjwc8NtQVPAJtq4Tt7Q49FOodjfbf6NpqXyW/rjXoV+iZ3EJAHLNAnKT5UJBc6ffQVgmXTUL2ifYiLABlGFqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.55.2.tgz", + "integrity": "sha512-s3KoWVNnye9mm/2WpOZ3JeUiediUVw6AvY/H7jNA6qgKA2V2aM25lMkVarTDfiicn/DLq3O0a81jncXszoyCFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.55.2.tgz", + "integrity": "sha512-gi21faacK+J8aVSyAUptML9VQN26JRxe484IbF+h3hpG+sNVoMXPduhREz2CcYr5my0NE3MjVvQ5bMKX71pfVA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.55.2.tgz", + "integrity": "sha512-qSlWiXnVaS/ceqXNfnoFZh4IiCA0EwvCivivTGbEu1qv2o+WTHpn1zNmCTAoOG5QaVr2/yhCoLScQtc/7RxshA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.55.2.tgz", + "integrity": "sha512-rPyuLFNoF1B0+wolH277E780NUKf+KoEDb3OyoLbAO18BbeKi++YN6gC/zuJoPPDlQRL3fIxHxCxVEWiem2yXw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.55.2.tgz", + "integrity": "sha512-g+0ZLMook31iWV4PvqKU0i9E78gaZgYpSrYPed/4Bu+nGTgfOPtfs1h11tSSRPXSjC5EzLTjV/1A7L2Vr8pJoQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.55.2.tgz", + "integrity": "sha512-i+sGeRGsjKZcQRh3BRfpLsM3LX3bi4AoEVqmGDyc50L6KfYsN45wVCSz70iQMwPWr3E5opSiLOwsC9WB4/1pqg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.55.2.tgz", + "integrity": "sha512-C1vLcKc4MfFV6I0aWsC7B2Y9QcsiEcvKkfxprwkPfLaN8hQf0/fKHwSF2lcYzA9g4imqnhic729VB9Fo70HO3Q==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.55.2.tgz", + "integrity": "sha512-68gHUK/howpQjh7g7hlD9DvTTt4sNLp1Bb+Yzw2Ki0xvscm2cOdCLZNJNhd2jW8lsTPrHAHuF751BygifW4bkQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.55.2.tgz", + "integrity": "sha512-1e30XAuaBP1MAizaOBApsgeGZge2/Byd6wV4a8oa6jPdHELbRHBiw7wvo4dp7Ie2PE8TZT4pj9RLGZv9N4qwlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.55.2.tgz", + "integrity": "sha512-4BJucJBGbuGnH6q7kpPqGJGzZnYrpAzRd60HQSt3OpX/6/YVgSsJnNzR8Ot74io50SeVT4CtCWe/RYIAymFPwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.55.2.tgz", + "integrity": "sha512-cT2MmXySMo58ENv8p6/O6wI/h/gLnD3D6JoajwXFZH6X9jz4hARqUhWpGuQhOgLNXscfZYRQMJvZDtWNzMAIDw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.55.2.tgz", + "integrity": "sha512-sZnyUgGkuzIXaK3jNMPmUIyJrxu/PjmATQrocpGA1WbCPX8H5tfGgRSuYtqBYAvLuIGp8SPRb1O4d1Fkb5fXaQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.55.2.tgz", + "integrity": "sha512-sDpFbenhmWjNcEbBcoTV0PWvW5rPJFvu+P7XoTY0YLGRupgLbFY0XPfwIbJOObzO7QgkRDANh65RjhPmgSaAjQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.55.2.tgz", + "integrity": "sha512-GvJ03TqqaweWCigtKQVBErw2bEhu1tyfNQbarwr94wCGnczA9HF8wqEe3U/Lfu6EdeNP0p6R+APeHVwEqVxpUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.55.2.tgz", + "integrity": "sha512-KvXsBvp13oZz9JGe5NYS7FNizLe99Ny+W8ETsuCyjXiKdiGrcz2/J/N8qxZ/RSwivqjQguug07NLHqrIHrqfYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.55.2.tgz", + "integrity": "sha512-xNO+fksQhsAckRtDSPWaMeT1uIM+JrDRXlerpnWNXhn1TdB3YZ6uKBMBTKP0eX9XtYEP978hHk1f8332i2AW8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rtsao/scc": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@rtsao/scc/-/scc-1.1.0.tgz", + "integrity": "sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==", + "dev": true, + "license": "MIT" + }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, + "node_modules/@swc/helpers": { + "version": "0.5.15", + "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.15.tgz", + "integrity": "sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==", + "license": "Apache-2.0", + "dependencies": { + "tslib": "^2.8.0" + } + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "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/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "22.15.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.15.18.tgz", + "integrity": "sha512-v1DKRfUdyW+jJhZNEI1PYy29S2YRxMV5AOO/x/SjKmW0acCIOqmbj6Haf9eHAhsPmrhlHSxEhv/1WszcLWV4cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/parse-json": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" + }, + "node_modules/@types/prop-types": { + "version": "15.7.14", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.14.tgz", + "integrity": "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ==", + "license": "MIT" + }, + "node_modules/@types/react": { + "version": "19.1.4", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.4.tgz", + "integrity": "sha512-EB1yiiYdvySuIITtD5lhW4yPyJ31RkJkkDw794LaQYrxCSaQV/47y5o1FMC4zF9ZyjUjzJMZwbovEnT5yHTW6g==", + "license": "MIT", + "dependencies": { + "csstype": "^3.0.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.1.5", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.1.5.tgz", + "integrity": "sha512-CMCjrWucUBZvohgZxkjd6S9h0nZxXjzus6yDfUb+xLxYM7VvjKNH1tQrE9GWLql1XoOP4/Ds3bwFqShHUYraGg==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.0.0" + } + }, + "node_modules/@types/react-transition-group": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { + "@types/react": "*" + } + }, + "node_modules/@types/xmldom": { + "version": "0.1.34", + "resolved": "https://registry.npmjs.org/@types/xmldom/-/xmldom-0.1.34.tgz", + "integrity": "sha512-7eZFfxI9XHYjJJuugddV6N5YNeXgQE1lArWOcd1eCOKWb/FGs5SIjacSYuEJuwhsGS3gy4RuZ5EUIcqYscuPDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.32.1.tgz", + "integrity": "sha512-6u6Plg9nP/J1GRpe/vcjjabo6Uc5YQPAMxsgQyGC/I0RuukiG1wIe3+Vtg3IrSCVJDmqK3j8adrtzXSENRtFgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.10.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/type-utils": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "graphemer": "^1.4.0", + "ignore": "^7.0.0", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.32.1.tgz", + "integrity": "sha512-LKMrmwCPoLhM45Z00O1ulb6jwyVr2kr3XJp+G+tSEZcbauNnScewcQwtJqXDhXeYPDEjZ8C1SjXm015CirEmGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.32.1.tgz", + "integrity": "sha512-7IsIaIDeZn7kffk7qXC3o6Z4UblZJKV3UBpkvRNpr5NSyLji7tvTcvmnMNYuYLyh26mN8W723xpo3i4MlD33vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.32.1.tgz", + "integrity": "sha512-mv9YpQGA8iIsl5KyUPi+FGLm7+bA4fgXaeRcFKRDRwDMu4iwrSHeDPipwueNXhdIIZltwCJv+NkxftECbIZWfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "8.32.1", + "@typescript-eslint/utils": "8.32.1", + "debug": "^4.3.4", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.32.1.tgz", + "integrity": "sha512-YmybwXUJcgGqgAp6bEsgpPXEg6dcCyPyCSr0CAAueacR/CCBi25G3V8gGQ2kRzQRBNol7VQknxMs9HvVa9Rvfg==", + "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.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.32.1.tgz", + "integrity": "sha512-Y3AP9EIfYwBb4kWGb+simvPaqQoT5oJuzzj9m0i6FCY6SPvlomY2Ei4UEMm7+FXtlNJbor80ximyslzaQF6xhg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/visitor-keys": "8.32.1", + "debug": "^4.3.4", + "fast-glob": "^3.3.2", + "is-glob": "^4.0.3", + "minimatch": "^9.0.4", + "semver": "^7.6.0", + "ts-api-utils": "^2.1.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.1.tgz", + "integrity": "sha512-DsSFNIgLSrc89gpq1LJB7Hm1YpuhK086DRDJSNrewcGvYloWW1vZLHBTIvarKZDcAORIy/uWNx8Gad+4oMpkSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.7.0", + "@typescript-eslint/scope-manager": "8.32.1", + "@typescript-eslint/types": "8.32.1", + "@typescript-eslint/typescript-estree": "8.32.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <5.9.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.32.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.32.1.tgz", + "integrity": "sha512-ar0tjQfObzhSaW3C3QNmTc5ofj0hDoNQ5XWrCy6zDyabdr0TWhCkClp+rywGNj/odAFBVzzJrK4tEq5M4Hmu4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.32.1", + "eslint-visitor-keys": "^4.2.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": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "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/@vitejs/plugin-react": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.1.2.tgz", + "integrity": "sha512-EcA07pHJouywpzsoTUqNh5NwGayl2PPVEJKUSinGGSxFGYn+shYbqMGBg6FXDqgXum9Ou/ecb+411ssw8HImJQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.28.5", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.53", + "@types/babel__core": "^7.20.5", + "react-refresh": "^0.18.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" + } + }, + "node_modules/@xmldom/xmldom": { + "version": "0.9.8", + "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.9.8.tgz", + "integrity": "sha512-p96FSY54r+WJ50FIOsCOjyj/wavs8921hG5+kVMmZgKcvIKxMXHTrjNJvRgWa/zuX3B6t2lijLNFaOyuxUH+2A==", + "license": "MIT", + "engines": { + "node": ">=14.6" + } + }, + "node_modules/acorn": { + "version": "8.14.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", + "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "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.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-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.2", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.2.tgz", + "integrity": "sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.2.tgz", + "integrity": "sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "is-array-buffer": "^3.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-includes": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.8.tgz", + "integrity": "sha512-itaWrbYbqpGXkGhZPGUulwnhVf5Hpy1xiCFsGqyIGglbBxmG5vSjxQen3/WGOjPpNEv1RtBLKxbmVXm8HpJStQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.4", + "is-string": "^1.0.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.5.tgz", + "integrity": "sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.findlastindex": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/array.prototype.findlastindex/-/array.prototype.findlastindex-1.2.6.tgz", + "integrity": "sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "es-shim-unscopables": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flat": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.3.3.tgz", + "integrity": "sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.flatmap": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/array.prototype.flatmap/-/array.prototype.flatmap-1.3.3.tgz", + "integrity": "sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array.prototype.tosorted": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/array.prototype.tosorted/-/array.prototype.tosorted-1.1.4.tgz", + "integrity": "sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3", + "es-errors": "^1.3.0", + "es-shim-unscopables": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.4.tgz", + "integrity": "sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.1", + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "is-array-buffer": "^3.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==", + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/ast-types-flow": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz", + "integrity": "sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", + "integrity": "sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "license": "MIT" + }, + "node_modules/available-typed-arrays": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", + "integrity": "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "possible-typed-array-names": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axe-core": { + "version": "4.10.3", + "resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.10.3.tgz", + "integrity": "sha512-Xm7bpRXnDSX2YE2YFfBk2FnF0ep6tmG7xPh8iHee8MIcrgq762Nkce856dYtJYLkuIoYZvGfTs/PbZhideTcEg==", + "dev": true, + "license": "MPL-2.0", + "engines": { + "node": ">=4" + } + }, + "node_modules/axios": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz", + "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.15.6", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/axobject-query": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/axobject-query/-/axobject-query-4.1.0.tgz", + "integrity": "sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/babel-plugin-macros": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", + "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.12.5", + "cosmiconfig": "^7.0.0", + "resolve": "^1.19.0" + }, + "engines": { + "node": ">=10", + "npm": ">=6" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.15", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.15.tgz", + "integrity": "sha512-kX8h7K2srmDyYnXRIppo4AH/wYgzWVCs+eKr3RusRSQ5PvRYoEFmR/I0PbdTjKFAoKqp5+kbxnNTFO9jOfSVJg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", + "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.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/busboy": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz", + "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==", + "dependencies": { + "streamsearch": "^1.1.0" + }, + "engines": { + "node": ">=10.16.0" + } + }, + "node_modules/call-bind": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.8.tgz", + "integrity": "sha512-oKlSFMcMwpUg2ednkhQ454wfWiU/ul3CkJe/PEHcTKuiX6RpbehUiFMXu13HalGZxfUwCQzZG747YXBn1im9ww==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.0", + "es-define-property": "^1.0.0", + "get-intrinsic": "^1.2.4", + "set-function-length": "^1.2.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/callsites": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", + "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001765", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001765.tgz", + "integrity": "sha512-LWcNtSyZrakjECqmpP4qdg0MMGdN368D7X8XvvAqOcqMv0RxnlqVKZl2V6/mBR68oYMxOZPLw/gO7DuisMHUvQ==", + "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/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/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/classnames": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz", + "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", + "license": "MIT" + }, + "node_modules/client-only": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/client-only/-/client-only-0.0.1.tgz", + "integrity": "sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==", + "license": "MIT" + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "devOptional": 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==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "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/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/cosmiconfig/node_modules/yaml": { + "version": "1.10.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz", + "integrity": "sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==", + "license": "ISC", + "engines": { + "node": ">= 6" + } + }, + "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-vendor": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/css-vendor/-/css-vendor-2.0.8.tgz", + "integrity": "sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.3", + "is-in-browser": "^1.0.2" + } + }, + "node_modules/csstype": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", + "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", + "license": "MIT" + }, + "node_modules/damerau-levenshtein": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz", + "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/data-view-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-buffer/-/data-view-buffer-1.0.2.tgz", + "integrity": "sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/data-view-byte-length": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/data-view-byte-length/-/data-view-byte-length-1.0.2.tgz", + "integrity": "sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/inspect-js" + } + }, + "node_modules/data-view-byte-offset": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/data-view-byte-offset/-/data-view-byte-offset-1.0.1.tgz", + "integrity": "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-data-view": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/debug": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", + "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/define-data-property": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", + "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0", + "es-errors": "^1.3.0", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.4.tgz", + "integrity": "sha512-3UDv+G9CsCKO1WKMGw9fwq/SWJYbI0c5Y7LU1AXYoDdbhE2AHQ6N6Nb34sG8Fj7T5APy8qXDCKuuIHd1BR0tVA==", + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/doctrine": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz", + "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "esutils": "^2.0.2" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/dom-helpers": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", + "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.8.7", + "csstype": "^3.0.2" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==", + "license": "MIT", + "dependencies": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.267", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz", + "integrity": "sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==", + "dev": true, + "license": "ISC" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/error-ex": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", + "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "license": "MIT", + "dependencies": { + "is-arrayish": "^0.2.1" + } + }, + "node_modules/es-abstract": { + "version": "1.23.9", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.23.9.tgz", + "integrity": "sha512-py07lI0wjxAC/DcfK1S6G7iANonniZwTISvdPzk9hzeH0IZIshbuuFxLIU96OyF89Yb9hiqWn8M/bY83KY5vzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-buffer-byte-length": "^1.0.2", + "arraybuffer.prototype.slice": "^1.0.4", + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "data-view-buffer": "^1.0.2", + "data-view-byte-length": "^1.0.2", + "data-view-byte-offset": "^1.0.1", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "es-set-tostringtag": "^2.1.0", + "es-to-primitive": "^1.3.0", + "function.prototype.name": "^1.1.8", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.0", + "get-symbol-description": "^1.1.0", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "internal-slot": "^1.1.0", + "is-array-buffer": "^3.0.5", + "is-callable": "^1.2.7", + "is-data-view": "^1.0.2", + "is-regex": "^1.2.1", + "is-shared-array-buffer": "^1.0.4", + "is-string": "^1.1.1", + "is-typed-array": "^1.1.15", + "is-weakref": "^1.1.0", + "math-intrinsics": "^1.1.0", + "object-inspect": "^1.13.3", + "object-keys": "^1.1.1", + "object.assign": "^4.1.7", + "own-keys": "^1.0.1", + "regexp.prototype.flags": "^1.5.3", + "safe-array-concat": "^1.1.3", + "safe-push-apply": "^1.0.0", + "safe-regex-test": "^1.1.0", + "set-proto": "^1.0.0", + "string.prototype.trim": "^1.2.10", + "string.prototype.trimend": "^1.0.9", + "string.prototype.trimstart": "^1.0.8", + "typed-array-buffer": "^1.0.3", + "typed-array-byte-length": "^1.0.3", + "typed-array-byte-offset": "^1.0.4", + "typed-array-length": "^1.0.7", + "unbox-primitive": "^1.1.0", + "which-typed-array": "^1.1.18" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-iterator-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-iterator-helpers/-/es-iterator-helpers-1.2.1.tgz", + "integrity": "sha512-uDn+FE1yrDzyC0pCo961B2IHbdM8y/ACZsKD4dG6WqrjV53BADjwa7D+1aom2rsNVfLyDgU/eigvlJGJ08OQ4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-set-tostringtag": "^2.0.3", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.6", + "globalthis": "^1.0.4", + "gopd": "^1.2.0", + "has-property-descriptors": "^1.0.2", + "has-proto": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "iterator.prototype": "^1.1.4", + "safe-array-concat": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.1.0.tgz", + "integrity": "sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-to-primitive": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.3.0.tgz", + "integrity": "sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7", + "is-date-object": "^1.0.5", + "is-symbol": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "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==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "9.27.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.27.0.tgz", + "integrity": "sha512-ixRawFQuMB9DZ7fjU3iGGganFDp3+45bPOdaRurcFHSXO1e/sYwUX/FtQZpLZJR6SjMoJH8hR2pPEAfDyCoU2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.20.0", + "@eslint/config-helpers": "^0.2.1", + "@eslint/core": "^0.14.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.27.0", + "@eslint/plugin-kit": "^0.3.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "@types/json-schema": "^7.0.15", + "ajv": "^6.12.4", + "chalk": "^4.0.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^8.3.0", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0", + "esquery": "^1.5.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "lodash.merge": "^4.6.2", + "minimatch": "^3.1.2", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-config-prettier": { + "version": "10.1.5", + "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.5.tgz", + "integrity": "sha512-zc1UmCpNltmVY34vuLRV61r1K27sWuX39E+uyUnY8xS2Bex88VV9cugG+UZbRSRGtGyFboj+D8JODyme1plMpw==", + "dev": true, + "license": "MIT", + "bin": { + "eslint-config-prettier": "bin/cli.js" + }, + "funding": { + "url": "https://opencollective.com/eslint-config-prettier" + }, + "peerDependencies": { + "eslint": ">=7.0.0" + } + }, + "node_modules/eslint-import-resolver-node": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz", + "integrity": "sha512-WFj2isz22JahUv+B788TlO3N6zL3nNJGU8CcZbPZvVEkBPaJdCV4vy5wyghty5ROFbCRnm132v8BScu5/1BQ8g==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7", + "is-core-module": "^2.13.0", + "resolve": "^1.22.4" + } + }, + "node_modules/eslint-import-resolver-node/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-module-utils": { + "version": "2.12.0", + "resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.12.0.tgz", + "integrity": "sha512-wALZ0HFoytlyh/1+4wuZ9FJCD/leWHQzzrxJ8+rebyReSLk7LApMyd3WJaLVoN+D5+WIdJyDK1c6JnE65V4Zyg==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^3.2.7" + }, + "engines": { + "node": ">=4" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/eslint-module-utils/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import": { + "version": "2.31.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.31.0.tgz", + "integrity": "sha512-ixmkI62Rbc2/w8Vfxyh1jQRTdRTF52VxwRVHl/ykPAmqG+Nb7/kNn+byLP0LxPgI7zWA16Jt82SybJInmMia3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rtsao/scc": "^1.1.0", + "array-includes": "^3.1.8", + "array.prototype.findlastindex": "^1.2.5", + "array.prototype.flat": "^1.3.2", + "array.prototype.flatmap": "^1.3.2", + "debug": "^3.2.7", + "doctrine": "^2.1.0", + "eslint-import-resolver-node": "^0.3.9", + "eslint-module-utils": "^2.12.0", + "hasown": "^2.0.2", + "is-core-module": "^2.15.1", + "is-glob": "^4.0.3", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "object.groupby": "^1.0.3", + "object.values": "^1.2.0", + "semver": "^6.3.1", + "string.prototype.trimend": "^1.0.8", + "tsconfig-paths": "^3.15.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-import/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.1" + } + }, + "node_modules/eslint-plugin-import/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-jsx-a11y": { + "version": "6.10.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.10.2.tgz", + "integrity": "sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "aria-query": "^5.3.2", + "array-includes": "^3.1.8", + "array.prototype.flatmap": "^1.3.2", + "ast-types-flow": "^0.0.8", + "axe-core": "^4.10.0", + "axobject-query": "^4.1.0", + "damerau-levenshtein": "^1.0.8", + "emoji-regex": "^9.2.2", + "hasown": "^2.0.2", + "jsx-ast-utils": "^3.3.5", + "language-tags": "^1.0.9", + "minimatch": "^3.1.2", + "object.fromentries": "^2.0.8", + "safe-regex-test": "^1.0.3", + "string.prototype.includes": "^2.0.1" + }, + "engines": { + "node": ">=4.0" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-jsx-a11y/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react": { + "version": "7.37.5", + "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.37.5.tgz", + "integrity": "sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.8", + "array.prototype.findlast": "^1.2.5", + "array.prototype.flatmap": "^1.3.3", + "array.prototype.tosorted": "^1.1.4", + "doctrine": "^2.1.0", + "es-iterator-helpers": "^1.2.1", + "estraverse": "^5.3.0", + "hasown": "^2.0.2", + "jsx-ast-utils": "^2.4.1 || ^3.0.0", + "minimatch": "^3.1.2", + "object.entries": "^1.1.9", + "object.fromentries": "^2.0.8", + "object.values": "^1.2.1", + "prop-types": "^15.8.1", + "resolve": "^2.0.0-next.5", + "semver": "^6.3.1", + "string.prototype.matchall": "^4.0.12", + "string.prototype.repeat": "^1.0.0" + }, + "engines": { + "node": ">=4" + }, + "peerDependencies": { + "eslint": "^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7" + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", + "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "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/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint-plugin-react/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/eslint-plugin-react/node_modules/resolve": { + "version": "2.0.0-next.5", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-2.0.0-next.5.tgz", + "integrity": "sha512-U7WjGVG9sH8tvjW5SmGbQuui75FiyjAX72HX15DwBBwF9dNiQZRQAg9nnPhYy+TUnE0+VcrttuvNI8oSxZcocA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-core-module": "^2.13.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/eslint-scope": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", + "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint/node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/espree": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", + "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.14.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^4.2.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", + "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/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/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-root": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" + }, + "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.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "node_modules/follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "license": "MIT", + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.5.tgz", + "integrity": "sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/form-data": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz", + "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==", + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.8", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.8.tgz", + "integrity": "sha512-e5iwyodOHhbMr/yNrc7fDYG4qlbIvI5gajyzPnb5TCwyhjApznQh1BMFou9b30SevY43gCJKXycoCBjMbsuW0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "functions-have-names": "^1.2.3", + "hasown": "^2.0.2", + "is-callable": "^1.2.7" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/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/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.1.0.tgz", + "integrity": "sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==", + "license": "MIT", + "dependencies": { + "assert-plus": "^1.0.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/globalthis": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.4.tgz", + "integrity": "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.2.1", + "gopd": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/has-bigints": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.1.0.tgz", + "integrity": "sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", + "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-define-property": "^1.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.2.0.tgz", + "integrity": "sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hoist-non-react-statics": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", + "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", + "dependencies": { + "react-is": "^16.7.0" + } + }, + "node_modules/hoist-non-react-statics/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/hyphenate-style-name": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.1.0.tgz", + "integrity": "sha512-WDC/ui2VVRrz3jOVi+XtjqkDjiVjTtFaAGiW37k6b+ohyQ5wYDOGkvCZa8+H0nx3gyvv0+BST9xuOgIyGQ00gw==", + "license": "BSD-3-Clause" + }, + "node_modules/ignore": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.4.tgz", + "integrity": "sha512-gJzzk+PQNznz8ysRrC0aOkBNVRBDtE1n53IqyqEf3PXrYwomFs5q4pGMizBMJF+ykh03insJ27hB8gSrD2Hn8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-5.1.4.tgz", + "integrity": "sha512-p6u1bG3YSnINT5RQmx/yRZBpenIl30kVxkTLDyHLIMk0gict704Q9n+thfDI7lTRm9vXdDYutVzXhzcThxTnXA==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/import-fresh": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "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/internal-slot": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.1.0.tgz", + "integrity": "sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "hasown": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.5.tgz", + "integrity": "sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-arrayish": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" + }, + "node_modules/is-async-function": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-async-function/-/is-async-function-2.1.1.tgz", + "integrity": "sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-function": "^1.0.0", + "call-bound": "^1.0.3", + "get-proto": "^1.0.1", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.1.0.tgz", + "integrity": "sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-bigints": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-boolean-object": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.2.2.tgz", + "integrity": "sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-core-module": { + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", + "dependencies": { + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-data-view": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-data-view/-/is-data-view-1.0.2.tgz", + "integrity": "sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "is-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.1.0.tgz", + "integrity": "sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-finalizationregistry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-finalizationregistry/-/is-finalizationregistry-1.1.1.tgz", + "integrity": "sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-generator-function": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", + "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-proto": "^1.0.0", + "has-tostringtag": "^1.0.2", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-browser": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/is-in-browser/-/is-in-browser-1.1.3.tgz", + "integrity": "sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==", + "license": "MIT" + }, + "node_modules/is-map": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz", + "integrity": "sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.1.1.tgz", + "integrity": "sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-regex": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.2.1.tgz", + "integrity": "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-set": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-set/-/is-set-2.0.3.tgz", + "integrity": "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.4.tgz", + "integrity": "sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.1.1.tgz", + "integrity": "sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.1.1.tgz", + "integrity": "sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "has-symbols": "^1.1.0", + "safe-regex-test": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.15.tgz", + "integrity": "sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakmap": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-weakmap/-/is-weakmap-2.0.2.tgz", + "integrity": "sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakref": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.1.1.tgz", + "integrity": "sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-weakset": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-weakset/-/is-weakset-2.0.4.tgz", + "integrity": "sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "get-intrinsic": "^1.2.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/iterator.prototype": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.5.tgz", + "integrity": "sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "get-proto": "^1.0.0", + "has-symbols": "^1.1.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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==", + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==", + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "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-parse-even-better-errors": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "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/jss": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz", + "integrity": "sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "csstype": "^3.0.2", + "is-in-browser": "^1.1.3", + "tiny-warning": "^1.0.2" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/jss" + } + }, + "node_modules/jss-plugin-camel-case": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz", + "integrity": "sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "hyphenate-style-name": "^1.0.3", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-default-unit": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz", + "integrity": "sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-global": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz", + "integrity": "sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-nested": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz", + "integrity": "sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-props-sort": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz", + "integrity": "sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0" + } + }, + "node_modules/jss-plugin-rule-value-function": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz", + "integrity": "sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "jss": "10.10.0", + "tiny-warning": "^1.0.2" + } + }, + "node_modules/jss-plugin-vendor-prefixer": { + "version": "10.10.0", + "resolved": "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz", + "integrity": "sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.3.1", + "css-vendor": "^2.0.8", + "jss": "10.10.0" + } + }, + "node_modules/jsx-ast-utils": { + "version": "3.3.5", + "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz", + "integrity": "sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "array-includes": "^3.1.6", + "array.prototype.flat": "^1.3.1", + "object.assign": "^4.1.4", + "object.values": "^1.1.6" + }, + "engines": { + "node": ">=4.0" + } + }, + "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/language-subtag-registry": { + "version": "0.3.23", + "resolved": "https://registry.npmjs.org/language-subtag-registry/-/language-subtag-registry-0.3.23.tgz", + "integrity": "sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/language-tags": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/language-tags/-/language-tags-1.0.9.tgz", + "integrity": "sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==", + "dev": true, + "license": "MIT", + "dependencies": { + "language-subtag-registry": "^0.3.20" + }, + "engines": { + "node": ">=0.10" + } + }, + "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/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" + }, + "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/loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", + "dependencies": { + "js-tokens": "^3.0.0 || ^4.0.0" + }, + "bin": { + "loose-envify": "cli.js" + } + }, + "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/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mobx": { + "version": "6.13.7", + "resolved": "https://registry.npmjs.org/mobx/-/mobx-6.13.7.tgz", + "integrity": "sha512-aChaVU/DO5aRPmk1GX8L+whocagUUpBQqoPtJk+cm7UOXUk87J4PeWCh6nNmTTIfEhiR9DI/+FnA8dln/hTK7g==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + } + }, + "node_modules/mobx-react-lite": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mobx-react-lite/-/mobx-react-lite-4.1.0.tgz", + "integrity": "sha512-QEP10dpHHBeQNv1pks3WnHRCem2Zp636lq54M2nKO2Sarr13pL4u6diQXf65yzXUn0mkk18SyIDCm9UOJYTi1w==", + "license": "MIT", + "dependencies": { + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mobx" + }, + "peerDependencies": { + "mobx": "^6.9.0", + "react": "^16.8.0 || ^17 || ^18 || ^19" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/next": { + "version": "15.3.2", + "resolved": "https://registry.npmjs.org/next/-/next-15.3.2.tgz", + "integrity": "sha512-CA3BatMyHkxZ48sgOCLdVHjFU36N7TF1HhqAHLFOkV6buwZnvMI84Cug8xD56B9mCuKrqXnLn94417GrZ/jjCQ==", + "license": "MIT", + "dependencies": { + "@next/env": "15.3.2", + "@swc/counter": "0.1.3", + "@swc/helpers": "0.5.15", + "busboy": "1.6.0", + "caniuse-lite": "^1.0.30001579", + "postcss": "8.4.31", + "styled-jsx": "5.1.6" + }, + "bin": { + "next": "dist/bin/next" + }, + "engines": { + "node": "^18.18.0 || ^19.8.0 || >= 20.0.0" + }, + "optionalDependencies": { + "@next/swc-darwin-arm64": "15.3.2", + "@next/swc-darwin-x64": "15.3.2", + "@next/swc-linux-arm64-gnu": "15.3.2", + "@next/swc-linux-arm64-musl": "15.3.2", + "@next/swc-linux-x64-gnu": "15.3.2", + "@next/swc-linux-x64-musl": "15.3.2", + "@next/swc-win32-arm64-msvc": "15.3.2", + "@next/swc-win32-x64-msvc": "15.3.2", + "sharp": "^0.34.1" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.1.0", + "@playwright/test": "^1.41.2", + "babel-plugin-react-compiler": "*", + "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", + "sass": "^1.3.0" + }, + "peerDependenciesMeta": { + "@opentelemetry/api": { + "optional": true + }, + "@playwright/test": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + }, + "sass": { + "optional": true + } + } + }, + "node_modules/node": { + "version": "20.19.2", + "resolved": "https://registry.npmjs.org/node/-/node-20.19.2.tgz", + "integrity": "sha512-3TaQbe1kw5+3VAUjZe18q83DwBK41f3WVw2Mc44gj4VfdqSIS/t5KnfHX9g4Hr9X77uFZb9nRZI23fWZ6bctbQ==", + "hasInstallScript": true, + "license": "ISC", + "dependencies": { + "node-bin-setup": "^1.0.0" + }, + "bin": { + "node": "bin/node" + }, + "engines": { + "npm": ">=5.0.0" + } + }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-bin-setup": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/node-bin-setup/-/node-bin-setup-1.1.4.tgz", + "integrity": "sha512-vWNHOne0ZUavArqPP5LJta50+S8R261Fr5SvGul37HbEDcowvLjwdvd0ZeSr0r2lTSrPxl6okq9QUw8BFGiAxA==", + "license": "ISC" + }, + "node_modules/node-releases": { + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.7", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.7.tgz", + "integrity": "sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0", + "has-symbols": "^1.1.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.entries": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/object.entries/-/object.entries-1.1.9.tgz", + "integrity": "sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.fromentries": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/object.fromentries/-/object.fromentries-2.0.8.tgz", + "integrity": "sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object.groupby": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/object.groupby/-/object.groupby-1.0.3.tgz", + "integrity": "sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.values": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.2.1.tgz", + "integrity": "sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/own-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/own-keys/-/own-keys-1.0.1.tgz", + "integrity": "sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "get-intrinsic": "^1.2.6", + "object-keys": "^1.1.1", + "safe-push-apply": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/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==", + "license": "MIT", + "dependencies": { + "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/parse-json": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", + "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.0.0", + "error-ex": "^1.3.1", + "json-parse-even-better-errors": "^2.3.0", + "lines-and-columns": "^1.1.6" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-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/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "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": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/possible-typed-array-names": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz", + "integrity": "sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/postcss": { + "version": "8.4.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.31.tgz", "integrity": "sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==", - "requires": { + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { "nanoid": "^3.3.6", "picocolors": "^1.0.0", "source-map-js": "^1.0.2" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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/prop-types": { + "version": "15.8.1", + "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", + "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.4.0", + "object-assign": "^4.1.1", + "react-is": "^16.13.1" + } + }, + "node_modules/prop-types/node_modules/react-is": { + "version": "16.13.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "license": "MIT" + }, + "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/qs": { + "version": "6.14.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", + "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0", + "scheduler": "^0.23.2" + }, + "peerDependencies": { + "react": "^18.3.1" + } + }, + "node_modules/react-is": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.1.0.tgz", + "integrity": "sha512-Oe56aUPnkHyyDxxkvqtd7KkdQP5uIUfHxd5XTb3wE9d/kRnZLmKbDB0GWk919tdQ+mxxPtG6EAs6RMT6i1qtHg==", + "license": "MIT" + }, + "node_modules/react-refresh": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.18.0.tgz", + "integrity": "sha512-QgT5//D3jfjJb6Gsjxv0Slpj23ip+HtOpnNgnb2S5zU3CB26G/IDPGoy4RJB42wzFE46DRsstbW6tKHoKbhAxw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-transition-group": { + "version": "4.4.5", + "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", + "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", + "dependencies": { + "@babel/runtime": "^7.5.5", + "dom-helpers": "^5.0.1", + "loose-envify": "^1.4.0", + "prop-types": "^15.6.2" + }, + "peerDependencies": { + "react": ">=16.6.0", + "react-dom": ">=16.6.0" + } + }, + "node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "devOptional": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/reflect.getprototypeof": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/reflect.getprototypeof/-/reflect.getprototypeof-1.0.10.tgz", + "integrity": "sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.9", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.7", + "get-proto": "^1.0.1", + "which-builtin-type": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.4", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.4.tgz", + "integrity": "sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "define-properties": "^1.2.1", + "es-errors": "^1.3.0", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "set-function-name": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve": { + "version": "1.22.10", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", + "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", + "license": "MIT", + "dependencies": { + "is-core-module": "^2.16.0", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rollup": { + "version": "4.55.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.55.2.tgz", + "integrity": "sha512-PggGy4dhwx5qaW+CKBilA/98Ql9keyfnb7lh4SR6shQ91QQQi1ORJ1v4UinkdP2i87OBs9AQFooQylcrrRfIcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.55.2", + "@rollup/rollup-android-arm64": "4.55.2", + "@rollup/rollup-darwin-arm64": "4.55.2", + "@rollup/rollup-darwin-x64": "4.55.2", + "@rollup/rollup-freebsd-arm64": "4.55.2", + "@rollup/rollup-freebsd-x64": "4.55.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.55.2", + "@rollup/rollup-linux-arm-musleabihf": "4.55.2", + "@rollup/rollup-linux-arm64-gnu": "4.55.2", + "@rollup/rollup-linux-arm64-musl": "4.55.2", + "@rollup/rollup-linux-loong64-gnu": "4.55.2", + "@rollup/rollup-linux-loong64-musl": "4.55.2", + "@rollup/rollup-linux-ppc64-gnu": "4.55.2", + "@rollup/rollup-linux-ppc64-musl": "4.55.2", + "@rollup/rollup-linux-riscv64-gnu": "4.55.2", + "@rollup/rollup-linux-riscv64-musl": "4.55.2", + "@rollup/rollup-linux-s390x-gnu": "4.55.2", + "@rollup/rollup-linux-x64-gnu": "4.55.2", + "@rollup/rollup-linux-x64-musl": "4.55.2", + "@rollup/rollup-openbsd-x64": "4.55.2", + "@rollup/rollup-openharmony-arm64": "4.55.2", + "@rollup/rollup-win32-arm64-msvc": "4.55.2", + "@rollup/rollup-win32-ia32-msvc": "4.55.2", + "@rollup/rollup-win32-x64-gnu": "4.55.2", + "@rollup/rollup-win32-x64-msvc": "4.55.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-array-concat": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.1.3.tgz", + "integrity": "sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "get-intrinsic": "^1.2.6", + "has-symbols": "^1.1.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-push-apply": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-push-apply/-/safe-push-apply-1.0.0.tgz", + "integrity": "sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-regex-test": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.1.0.tgz", + "integrity": "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "is-regex": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/sass": { + "version": "1.97.2", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.97.2.tgz", + "integrity": "sha512-y5LWb0IlbO4e97Zr7c3mlpabcbBtS+ieiZ9iwDooShpFKWXf62zz5pEPdwrLYm+Bxn1fnbwFGzHuCLSA9tBmrw==", + "devOptional": true, + "license": "MIT", + "dependencies": { + "chokidar": "^4.0.0", + "immutable": "^5.0.2", + "source-map-js": ">=0.6.2 <2.0.0" + }, + "bin": { + "sass": "sass.js" + }, + "engines": { + "node": ">=14.0.0" + }, + "optionalDependencies": { + "@parcel/watcher": "^2.4.1" + } + }, + "node_modules/scheduler": { + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", + "dependencies": { + "loose-envify": "^1.1.0" + } + }, + "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/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", + "integrity": "sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-proto": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/set-proto/-/set-proto-1.0.0.tgz", + "integrity": "sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/sharp": { + "version": "0.34.1", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.1.tgz", + "integrity": "sha512-1j0w61+eVxu7DawFJtnfYcvSv6qPFvfTaqzTQ2BLknVhHTwGS8sc63ZBF4rzkWMBVKybo4S5OBtDdZahh2A1xg==", + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.7.1" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.1", + "@img/sharp-darwin-x64": "0.34.1", + "@img/sharp-libvips-darwin-arm64": "1.1.0", + "@img/sharp-libvips-darwin-x64": "1.1.0", + "@img/sharp-libvips-linux-arm": "1.1.0", + "@img/sharp-libvips-linux-arm64": "1.1.0", + "@img/sharp-libvips-linux-ppc64": "1.1.0", + "@img/sharp-libvips-linux-s390x": "1.1.0", + "@img/sharp-libvips-linux-x64": "1.1.0", + "@img/sharp-libvips-linuxmusl-arm64": "1.1.0", + "@img/sharp-libvips-linuxmusl-x64": "1.1.0", + "@img/sharp-linux-arm": "0.34.1", + "@img/sharp-linux-arm64": "0.34.1", + "@img/sharp-linux-s390x": "0.34.1", + "@img/sharp-linux-x64": "0.34.1", + "@img/sharp-linuxmusl-arm64": "0.34.1", + "@img/sharp-linuxmusl-x64": "0.34.1", + "@img/sharp-wasm32": "0.34.1", + "@img/sharp-win32-ia32": "0.34.1", + "@img/sharp-win32-x64": "0.34.1" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", + "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-swizzle": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", + "integrity": "sha512-JA//kQgZtbuY83m+xT+tXJkmJncGMTFT+C+g2h2R9uxkYIrE2yy9sgmcLhCnw57/WSD+Eh3J97FPEDFnbXnDUg==", + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, + "node_modules/simple-swizzle/node_modules/is-arrayish": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.2.tgz", + "integrity": "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ==", + "license": "MIT", + "optional": true + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "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/sshpk": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz", + "integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==", + "license": "MIT", + "dependencies": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "bin": { + "sshpk-conv": "bin/sshpk-conv", + "sshpk-sign": "bin/sshpk-sign", + "sshpk-verify": "bin/sshpk-verify" + }, + "engines": { + "node": ">=0.10.0" } }, - "prop-types": { - "version": "15.7.2", - "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz", - "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==", - "requires": { - "loose-envify": "^1.4.0", - "object-assign": "^4.1.1", - "react-is": "^16.8.1" + "node_modules/streamsearch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", + "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==", + "engines": { + "node": ">=10.0.0" } }, - "react": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react/-/react-16.13.1.tgz", - "integrity": "sha512-YMZQQq32xHLX0bz5Mnibv1/LHb3Sqzngu7xstSM+vrkE5Kzr9xE0yMByK5kMoTK30YVJE61WfbxIFFvfeDKT1w==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2" + "node_modules/string.prototype.includes": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.includes/-/string.prototype.includes-2.0.1.tgz", + "integrity": "sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.3" + }, + "engines": { + "node": ">= 0.4" } }, - "react-dom": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.13.1.tgz", - "integrity": "sha512-81PIMmVLnCNLO/fFOQxdQkvEq/+Hfpv24XNJfpyZhTRfO0QcmQIF/PgCa1zCOj2w1hrn12MFLyaJ/G0+Mxtfag==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1", - "prop-types": "^15.6.2", - "scheduler": "^0.19.1" + "node_modules/string.prototype.matchall": { + "version": "4.0.12", + "resolved": "https://registry.npmjs.org/string.prototype.matchall/-/string.prototype.matchall-4.0.12.tgz", + "integrity": "sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.3", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.6", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.0.0", + "get-intrinsic": "^1.2.6", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "internal-slot": "^1.1.0", + "regexp.prototype.flags": "^1.5.3", + "set-function-name": "^2.0.2", + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "react-is": { - "version": "16.13.1", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "node_modules/string.prototype.repeat": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz", + "integrity": "sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } }, - "react-transition-group": { - "version": "4.4.1", - "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.1.tgz", - "integrity": "sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==", - "requires": { - "@babel/runtime": "^7.5.5", - "dom-helpers": "^5.0.1", - "loose-envify": "^1.4.0", - "prop-types": "^15.6.2" + "node_modules/string.prototype.trim": { + "version": "1.2.10", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.10.tgz", + "integrity": "sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-data-property": "^1.1.4", + "define-properties": "^1.2.1", + "es-abstract": "^1.23.5", + "es-object-atoms": "^1.0.0", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "regenerator-runtime": { - "version": "0.13.7", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", - "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + "node_modules/string.prototype.trimend": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.9.tgz", + "integrity": "sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "call-bound": "^1.0.2", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, - "scheduler": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.19.1.tgz", - "integrity": "sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA==", - "requires": { - "loose-envify": "^1.1.0", - "object-assign": "^4.1.1" + "node_modules/string.prototype.trimstart": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz", + "integrity": "sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "define-properties": "^1.2.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "source-map-js": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.0.2.tgz", - "integrity": "sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==" + "node_modules/strip-bom": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", + "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "streamsearch": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz", - "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==" + "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" + } }, - "styled-jsx": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", - "integrity": "sha512-pW7uC1l4mBZ8ugbiZrcIsiIvVx1UmTfw7UkC3Um2tmfUq9Bhk8IiyEIPl6F8agHgjzku6j0xQEZbfA5uSgSaCw==", - "requires": { + "node_modules/styled-jsx": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.6.tgz", + "integrity": "sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==", + "license": "MIT", + "dependencies": { "client-only": "0.0.1" + }, + "engines": { + "node": ">= 12.0.0" + }, + "peerDependencies": { + "react": ">= 16.8.0 || 17.x.x || ^18.0.0-0 || ^19.0.0-0" + }, + "peerDependenciesMeta": { + "@babel/core": { + "optional": true + }, + "babel-plugin-macros": { + "optional": true + } } }, - "tiny-warning": { + "node_modules/stylis": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "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/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tiny-warning": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" + "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==", + "license": "MIT" }, - "to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", - "dev": true + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } }, - "tslib": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", - "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + "node_modules/tinyglobby/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } }, - "typescript": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.0.2.tgz", - "integrity": "sha512-e4ERvRV2wb+rRZ/IQeb3jm2VxBsirQLpQhdxplZ2MEzGvDkkMmPglecnNDfSUBivMjP93vRbngYYDQqQ/78bcQ==", - "dev": true - }, - "watchpack": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", - "integrity": "sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg==", - "requires": { - "glob-to-regexp": "^0.4.1", - "graceful-fs": "^4.1.2" - } - }, - "xmldom": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.5.0.tgz", - "integrity": "sha512-Foaj5FXVzgn7xFzsKeNIde9g6aFBxTPi37iwsno8QvApmtg7KYrr+OPyRHcJF7dud2a5nGRBXK3n0dL62Gf7PA==" + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.1.0.tgz", + "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/tsconfig-paths": { + "version": "3.15.0", + "resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.15.0.tgz", + "integrity": "sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/json5": "^0.0.29", + "json5": "^1.0.2", + "minimist": "^1.2.6", + "strip-bom": "^3.0.0" + } + }, + "node_modules/tsconfig-paths/node_modules/json5": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.2.tgz", + "integrity": "sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==", + "dev": true, + "license": "MIT", + "dependencies": { + "minimist": "^1.2.0" + }, + "bin": { + "json5": "lib/cli.js" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD" + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "license": "Unlicense" + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.3.tgz", + "integrity": "sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "es-errors": "^1.3.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.3.tgz", + "integrity": "sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.14" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.4.tgz", + "integrity": "sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "for-each": "^0.3.3", + "gopd": "^1.2.0", + "has-proto": "^1.2.0", + "is-typed-array": "^1.1.15", + "reflect.getprototypeof": "^1.0.9" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.7.tgz", + "integrity": "sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind": "^1.0.7", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "is-typed-array": "^1.1.13", + "possible-typed-array-names": "^1.0.0", + "reflect.getprototypeof": "^1.0.6" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typescript": { + "version": "5.8.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.3.tgz", + "integrity": "sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/unbox-primitive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.1.0.tgz", + "integrity": "sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.3", + "has-bigints": "^1.0.2", + "has-symbols": "^1.1.0", + "which-boxed-primitive": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/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/use-sync-external-store": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", + "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "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/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vite/node_modules/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/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/which-boxed-primitive": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.1.1.tgz", + "integrity": "sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-bigint": "^1.1.0", + "is-boolean-object": "^1.2.1", + "is-number-object": "^1.1.1", + "is-string": "^1.1.1", + "is-symbol": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-builtin-type": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/which-builtin-type/-/which-builtin-type-1.2.1.tgz", + "integrity": "sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "function.prototype.name": "^1.1.6", + "has-tostringtag": "^1.0.2", + "is-async-function": "^2.0.0", + "is-date-object": "^1.1.0", + "is-finalizationregistry": "^1.1.0", + "is-generator-function": "^1.0.10", + "is-regex": "^1.2.1", + "is-weakref": "^1.0.2", + "isarray": "^2.0.5", + "which-boxed-primitive": "^1.1.0", + "which-collection": "^1.0.2", + "which-typed-array": "^1.1.16" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-collection": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-collection/-/which-collection-1.0.2.tgz", + "integrity": "sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-map": "^2.0.3", + "is-set": "^2.0.3", + "is-weakmap": "^2.0.2", + "is-weakset": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.19", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.19.tgz", + "integrity": "sha512-rEvr90Bck4WZt9HHFC4DJMsjvu7x+r6bImz0/BrbWb7A2djJ8hnZMrWnHo9F8ssv0OMErasDhftrfROTyqSDrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "available-typed-arrays": "^1.0.7", + "call-bind": "^1.0.8", + "call-bound": "^1.0.4", + "for-each": "^0.3.5", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-tostringtag": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/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/yaml": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", + "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", + "dev": true, + "license": "ISC", + "optional": true, + "peer": true, + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "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" + } } } } diff --git a/package.json b/package.json index 95589e8..4dc8565 100644 --- a/package.json +++ b/package.json @@ -1,31 +1,74 @@ { "name": "beat-machine", - "version": "1.0.0", + "version": "2.0.0", "private": true, + "type": "module", "scripts": { "dev": "next dev -p 3009", - "build": "next build", + "build": "npm run build:widget && next build && npm run postbuild", + "build:widget": "vite build", + "postbuild": "cp -r public/assets out/ 2>/dev/null || true", "start": "next start", - "export": "next export" + "deploy": "npm run build", + "deploy:preview": "npm run deploy && npx serve out", + "lint": "next lint" }, "dependencies": { - "@material-ui/core": "^4.11.0", - "@material-ui/icons": "^4.9.1", - "classnames": "^2.2.6", - "mobx": "^5.15.7", - "mobx-react-lite": "^2.2.2", - "next": "^13.5.5", - "react": "16.13.1", - "react-dom": "16.13.1", - "xmldom": "^0.5.0" + "@emotion/react": "^11.14.0", + "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^7.1.0", + "@mui/material": "^7.1.0", + "@mui/styles": "^6.4.11", + "@xmldom/xmldom": "^0.9.8", + "axios": "^1.9.0", + "classnames": "^2.5.1", + "minimatch": "^10.0.1", + "mobx": "^6.13.7", + "mobx-react-lite": "^4.1.0", + "next": "^15.3.2", + "node": "^20.19.2", + "qs": "^6.14.0", + "react": "^18.3.1", + "react-dom": "^18.3.1", + "sshpk": "^1.18.0" }, "devDependencies": { - "@babel/plugin-proposal-class-properties": "^7.10.4", - "@babel/plugin-proposal-decorators": "^7.10.5", - "@types/classnames": "^2.2.10", - "@types/node": "^14.6.2", - "@types/react": "^16.9.48", - "@types/xmldom": "^0.1.30", - "typescript": "^4.0.2" - } + "@babel/plugin-proposal-class-properties": "^7.18.6", + "@babel/plugin-proposal-decorators": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@eslint/config-array": "^0.20.0", + "@eslint/object-schema": "^2.1.6", + "@types/node": "^22.15.18", + "@types/react": "^19.1.4", + "@types/react-dom": "^19.1.5", + "@types/xmldom": "^0.1.34", + "@typescript-eslint/eslint-plugin": "^8.32.1", + "@typescript-eslint/parser": "^8.32.1", + "@vitejs/plugin-react": "^5.1.2", + "eslint": "^9.27.0", + "eslint-config-prettier": "^10.1.5", + "eslint-plugin-import": "^2.31.0", + "eslint-plugin-jsx-a11y": "^6.10.2", + "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^5.2.0", + "sass": "^1.69.5", + "typescript": "^5.8.3", + "vite": "^7.3.1" + }, + "overrides": { + "@rushstack/eslint-patch": "1.10.0" + }, + "description": "Combine and arrange musical instruments to create different Salsa tunes. Great for musicians and dancers who want to practive their Salsa timing and trains their ears.", + "main": "prettier.config.js", + "repository": { + "type": "git", + "url": "git+https://github.com/limekex/The-SalsaNor-beat-machine.git" + }, + "keywords": [], + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/limekex/The-SalsaNor-beat-machine/issues" + }, + "homepage": "https://github.com/limekex/The-SalsaNor-beat-machine#readme" } diff --git a/pages/404.tsx b/pages/404.tsx index ad994d3..b6162e3 100644 --- a/pages/404.tsx +++ b/pages/404.tsx @@ -10,7 +10,6 @@ export default function Error404() { - + + The SalsaNor SalsaBeat Machine 🎼🎹 + + diff --git a/pages/_document.tsx b/pages/_document.tsx index e1b16b3..c0866cf 100644 --- a/pages/_document.tsx +++ b/pages/_document.tsx @@ -1,8 +1,8 @@ -import { ServerStyleSheets } from '@material-ui/core'; -import Document, { DocumentContext } from 'next/document'; +import { ServerStyleSheets } from '@mui/styles'; +import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document'; export default class MyDocument extends Document { - static async getInitialProps(ctx: DocumentContext) { + static async getInitialProps(ctx: DocumentContext) { const sheets = new ServerStyleSheets(); const originalRenderPage = ctx.renderPage; @@ -22,4 +22,19 @@ export default class MyDocument extends Document { ), }; } + + render() { + return ( + + + + + + +
+ + + + ); + } } diff --git a/pages/android-videos.tsx b/pages/android-videos.tsx index 359987c..9443b5f 100644 --- a/pages/android-videos.tsx +++ b/pages/android-videos.tsx @@ -1,6 +1,7 @@ import Head from 'next/head'; import styles from './android-videos.module.css'; -import videos from '../public/assets/timing-videos.json'; + +const videos = { data: [] as Array<{ id: string; youtube_id: string; thumbnail_url: string; title: string }> }; export default function AndroidVideos() { return ( diff --git a/pages/index.module.css b/pages/index.module.css index 3da7505..9cec922 100644 --- a/pages/index.module.css +++ b/pages/index.module.css @@ -1,19 +1,44 @@ +/* ============================== + Hovedheader styling +============================== */ .homepage h1 { text-align: center; font-size: 48px; - color: #aaa; + color: #4a4a4a; font-family: 'Merriweather', serif; - font-weight: normal; - margin-bottom: 12px; + font-weight: 300; + margin-bottom: 16px; + line-height: 1.2; } -@media(max-width: 640px) { +/* Responsiv justering */ +@media (max-width: 640px) { .homepage h1 { - font-size: 6vw; + font-size: 8vw; } } +/* ============================== + Container for BeatMachine UI +============================== */ .appContainer { - max-width: 680px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + max-width: 720px; margin: 0 auto; + padding: 20px; + background-color: #f9f9f9; + border-radius: 8px; + box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1); + gap: 16px; +} + +/* Ekstra responsiv tilpasning for mindre skjermer */ +@media (max-width: 768px) { + .appContainer { + max-width: 100%; + padding: 16px; + } } diff --git a/pages/index.module.scss b/pages/index.module.scss new file mode 100644 index 0000000..011e053 --- /dev/null +++ b/pages/index.module.scss @@ -0,0 +1,96 @@ +@use '../styles/abstracts/mixins-modules' as *; + +.homepage { + min-height: 100vh; + padding: 1rem; // $spacing-4 + + @include respond-to('desktop') { + padding: 1.5rem; // $spacing-6 + } +} + +.title { + @include heading-1; + text-align: center; + margin-bottom: 2rem; // $spacing-8 + background: linear-gradient(135deg, #FFC947, #FF9933, #FF8C69); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; + animation: shimmer 3s ease-in-out infinite; + background-size: 200% 100%; +} + +@keyframes shimmer { + 0%, 100% { + background-position: 0% 50%; + } + 50% { + background-position: 100% 50%; + } +} + +.appContainer { + max-width: 1400px; + margin: 0 auto; +} + +.widgetLink { + position: fixed; + bottom: 1.5rem; + right: 1.5rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.2), rgba(0, 245, 255, 0.2)); + backdrop-filter: blur(10px); + border: 1px solid rgba(0, 245, 255, 0.5); + color: rgba(255, 255, 255, 0.95); + border-radius: 0.5rem; + cursor: pointer; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; + font-size: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); + font-weight: 500; + transition: all 0.3s ease; + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2); + text-decoration: none; + display: inline-flex; + align-items: center; + justify-content: center; + z-index: 1000; + + &:hover { + background: linear-gradient(135deg, rgba(102, 126, 234, 0.3), rgba(0, 245, 255, 0.3)); + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(0, 0, 0, 0.3); + } + + @include respond-to('mobile') { + bottom: 1rem; + right: 1rem; + padding: 0.5rem 1rem; + font-size: clamp(0.6875rem, 0.5vw + 0.125rem, 0.8125rem); + } +} + +.footer { + margin-top: 4rem; + padding: 2rem 1rem; + text-align: center; + color: rgba(255, 255, 255, 0.6); + font-size: 0.875rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + + p { + margin: 0; + } +} + +.footerLink { + color: rgba(0, 245, 255, 0.8); + text-decoration: none; + transition: color 0.2s ease; + + &:hover { + color: rgba(0, 245, 255, 1); + text-decoration: underline; + } +} diff --git a/pages/index.tsx b/pages/index.tsx index 8fefe75..cd96712 100644 --- a/pages/index.tsx +++ b/pages/index.tsx @@ -1,9 +1,9 @@ import { GetStaticProps } from 'next'; import Head from 'next/head'; -import { BeatMachineUI, IDefaultMachines } from '../components/beat-machine-ui'; -import { MobileAppLinks } from '../components/mobile-app-links'; +import { BeatMachineUIGlass } from '../components/beat-machine-ui-glass'; import { loadMachine } from '../services/load-machine'; -import styles from './index.module.css'; +import type { IDefaultMachines } from '../components/beat-machine-ui'; +import styles from './index.module.scss'; interface IHomeProps { machines: IDefaultMachines; @@ -14,47 +14,78 @@ export default function Home({ machines }: IHomeProps) { <> - The Salsa Beat Machine 🎼🎹 - + The SalsaNor SalsaBeat Machine 🎼🎹 - - - - + + + - - - + + + - - - -
-

The Salsa Beat Machine

+
+

🎵 The SalsaNor Beat Machine

-
- +
-
+ + + 🛠️ Widget Generator + + + +
); } export const getStaticProps: GetStaticProps = async () => { - const salsa = await loadMachine('salsa.xml'); - const merengue = await loadMachine('merengue.xml'); - return { - props: { - machines: { salsa, merengue }, - }, - }; + try { + const salsa = await loadMachine('salsa.xml'); + const merengue = await loadMachine('merengue.xml'); + return { + props: { + machines: { salsa, merengue }, + }, + }; + } catch (error) { + console.error('Failed to load machines:', error); + return { + notFound: true, + }; + } }; diff --git a/pages/widget-demo.tsx b/pages/widget-demo.tsx new file mode 100644 index 0000000..b2d3ed1 --- /dev/null +++ b/pages/widget-demo.tsx @@ -0,0 +1,194 @@ +import { useState, useEffect } from 'react'; +import Head from 'next/head'; +import dynamic from 'next/dynamic'; +import { observable } from 'mobx'; +import { useMachine } from '../hooks/use-machine'; +import { IMachine } from '../engine/machine-interfaces'; + +// Import WidgetCompact with SSR disabled +const WidgetCompact = dynamic( + () => import('../src/widget/WidgetCompact').then(mod => mod.WidgetCompact), + { ssr: false } +); + +export default function WidgetDemo() { + // Load the real salsa machine with audio samples + const salsaMachine = useMachine('/assets/machines/salsa.xml'); + + // Create observable copies with filtered instruments and custom BPM + const createFilteredMachine = (instruments: string[], bpm: number): IMachine | null => { + if (!salsaMachine) return null; + + return observable({ + ...salsaMachine, + bpm, + instruments: salsaMachine.instruments.map(inst => + observable({ + ...inst, + enabled: instruments.includes(inst.id) + }) + ) + }); + }; + + // Don't render until machine is loaded + if (!salsaMachine) { + return ( + <> + + Beat Machine Widget Demo + +
+
+

🎵 Loading Beat Machine...

+
+
+ + ); + } + + return ( + <> + + Beat Machine Widget Demo + + + + + +
+
+

🎵 Beat Machine Widget Demo

+ + {/* Example 1: Basic Clave */} +
+

Example 1: Simple Clave Pattern

+

The most basic usage - just a clave rhythm with default settings.

+
<div data-beat-widget data-instruments="clave"></div>
+
+ +
+
+ + {/* Example 2: Cowbell at 140 BPM */} +
+

Example 2: Cowbell with Custom Tempo

+

Cowbell rhythm at a faster tempo of 140 BPM.

+
<div data-beat-widget data-instruments="cowbell" data-bpm="140"></div>
+
+ +
+
+ + {/* Example 3: Multiple Instruments */} +
+

Example 3: Salsa Rhythm Combination

+

Multiple instruments playing together - clave, cowbell, and bongo.

+
<div data-beat-widget data-instruments="clave,cowbell,bongo" data-bpm="120"></div>
+
+ +
+
+ + {/* Example 4: Slow Tempo */} +
+

Example 4: Educational Slow Tempo

+

Perfect for learning - clave at a slow 80 BPM.

+
<div data-beat-widget data-instruments="clave" data-bpm="80"></div>
+
+ +
+
+ + {/* Example 5: Inline */} +
+

Example 5: Inline in Paragraph

+

+ You can even embed the widget inline with text, like this:{' '} + + + + {' '}See how it flows naturally with the content? +

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

📦 How to Use

+

Add this script tag to your page:

+
<script src="/widget.js"></script>
+

Then add the widget anywhere in your HTML:

+
<div data-beat-widget data-instruments="clave,cowbell" data-bpm="120"></div>
+
+
+
+ + ); +} + +// Inline styles +const containerStyle: React.CSSProperties = { + fontFamily: "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif", + background: 'linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%)', + color: 'rgba(255, 255, 255, 0.95)', + padding: '2rem', + minHeight: '100vh', +}; + +const wrapperStyle: React.CSSProperties = { + maxWidth: '1200px', + margin: '0 auto', +}; + +const titleStyle: React.CSSProperties = { + textAlign: 'center', + marginBottom: '3rem', + background: 'linear-gradient(135deg, #FFC947 0%, #FF9933 100%)', + WebkitBackgroundClip: 'text', + WebkitTextFillColor: 'transparent', + backgroundClip: 'text', +}; + +const sectionStyle: React.CSSProperties = { + background: 'rgba(255, 255, 255, 0.1)', + backdropFilter: 'blur(20px)', + border: '1px solid rgba(255, 255, 255, 0.2)', + borderRadius: '1rem', + padding: '2rem', + marginBottom: '2rem', +}; + +const subtitleStyle: React.CSSProperties = { + marginTop: 0, + marginBottom: '1rem', + color: '#FFC947', +}; + +const textStyle: React.CSSProperties = { + marginBottom: '1rem', + lineHeight: 1.6, +}; + +const preStyle: React.CSSProperties = { + background: 'rgba(0, 0, 0, 0.3)', + padding: '1rem', + borderRadius: '0.5rem', + overflow: 'auto', + margin: '1rem 0', +}; + +const codeStyle: React.CSSProperties = { + fontFamily: "'Fira Code', monospace", + fontSize: '0.875rem', + color: '#FFC947', +}; + +const demoBoxStyle: React.CSSProperties = { + padding: '1rem', + background: 'rgba(0, 0, 0, 0.2)', + borderRadius: '0.5rem', + margin: '1rem 0', + display: 'flex', + alignItems: 'center', + justifyContent: 'center', +}; diff --git a/pages/widget-generator.module.css b/pages/widget-generator.module.css new file mode 100644 index 0000000..963736f --- /dev/null +++ b/pages/widget-generator.module.css @@ -0,0 +1,343 @@ +.container { + min-height: 100vh; + background: linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%); + color: rgba(255, 255, 255, 0.95); + padding: 2rem; +} + +.loading { + text-align: center; + font-size: 1.5rem; + padding: 4rem; +} + +.header { + text-align: center; + margin-bottom: 3rem; +} + +.header h1 { + font-size: 2.5rem; + margin-bottom: 0.5rem; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + -webkit-background-clip: text; + -webkit-text-fill-color: transparent; + background-clip: text; +} + +.header p { + font-size: 1.1rem; + opacity: 0.8; +} + +.grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 2rem; + max-width: 1600px; + margin: 0 auto; +} + +@media (max-width: 1200px) { + .grid { + grid-template-columns: 1fr; + } +} + +.panel { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.section { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 1rem; + padding: 1.5rem; +} + +.section h2 { + margin: 0 0 1rem 0; + font-size: 1.3rem; + color: #FFC947; +} + +.sectionHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.sectionHeader h2 { + margin: 0; +} + +.buttonGroup { + display: flex; + gap: 0.5rem; +} + +.smallButton { + padding: 0.3rem 0.8rem; + font-size: 0.85rem; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0.5rem; + color: white; + cursor: pointer; + transition: all 0.2s; +} + +.smallButton:hover { + background: rgba(255, 255, 255, 0.2); + transform: translateY(-1px); +} + +.radioGroup, +.checkboxGroup { + display: flex; + flex-direction: column; + gap: 0.8rem; +} + +.instrumentItem { + display: flex; + flex-direction: column; + gap: 0.5rem; + padding: 0.5rem; + border-radius: 0.5rem; + transition: background 0.2s; +} + +.instrumentItem:hover { + background: rgba(255, 255, 255, 0.05); +} + +.instrumentName { + font-weight: 500; +} + +.programSelect { + margin-left: 2rem; + padding: 0.4rem 0.8rem; + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0.4rem; + color: white; + font-size: 0.9rem; + cursor: pointer; + transition: all 0.2s; +} + +.programSelect:hover { + border-color: #FFC947; + background: rgba(0, 0, 0, 0.5); +} + +.programSelect:focus { + outline: none; + border-color: #FFC947; + box-shadow: 0 0 0 2px rgba(255, 201, 71, 0.2); +} + +.programSelect option { + background: #1a1a2e; + color: white; +} + +.radio, +.checkbox { + display: flex; + align-items: center; + gap: 0.8rem; + cursor: pointer; + padding: 0.5rem; + border-radius: 0.5rem; + transition: background 0.2s; +} + +.radio:hover, +.checkbox:hover { + background: rgba(255, 255, 255, 0.05); +} + +.radio input, +.checkbox input { + width: 1.2rem; + height: 1.2rem; + cursor: pointer; +} + +.radio span, +.checkbox span { + font-size: 1rem; + text-transform: capitalize; +} + +.bpmControl { + display: flex; + align-items: center; + gap: 1rem; +} + +.slider { + flex: 1; + height: 8px; + border-radius: 4px; + background: rgba(255, 255, 255, 0.2); + outline: none; + cursor: pointer; +} + +.slider::-webkit-slider-thumb { + appearance: none; + width: 20px; + height: 20px; + border-radius: 50%; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + cursor: pointer; + box-shadow: 0 2px 8px rgba(255, 201, 71, 0.4); +} + +.slider::-moz-range-thumb { + width: 20px; + height: 20px; + border-radius: 50%; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + cursor: pointer; + border: none; + box-shadow: 0 2px 8px rgba(255, 201, 71, 0.4); +} + +.bpmValue { + font-size: 1.2rem; + font-weight: 600; + min-width: 80px; + text-align: right; + color: #FFC947; +} + +.textInput { + width: 100%; + padding: 0.8rem; + font-size: 1rem; + background: rgba(0, 0, 0, 0.3); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 0.5rem; + color: white; + font-family: 'Monaco', 'Courier New', monospace; +} + +.textInput:focus { + outline: none; + border-color: #FFC947; + box-shadow: 0 0 0 2px rgba(255, 201, 71, 0.2); +} + +.help { + margin-top: 0.5rem; + font-size: 0.9rem; + opacity: 0.7; + line-height: 1.5; +} + +.preview { + display: flex; + justify-content: center; + align-items: center; + min-height: 100px; + background: rgba(0, 0, 0, 0.3); + border-radius: 0.5rem; + padding: 2rem; +} + +.codeHeader { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.codeHeader h2 { + margin: 0; +} + +.copyButton { + padding: 0.5rem 1rem; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + border: none; + border-radius: 0.5rem; + color: white; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; +} + +.copyButton:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(255, 201, 71, 0.4); +} + +.code { + background: rgba(0, 0, 0, 0.5); + border: 1px solid rgba(255, 255, 255, 0.1); + border-radius: 0.5rem; + padding: 1rem; + overflow-x: auto; + overflow-y: auto; + height: 120px; + min-height: 120px; + max-height: 120px; +} + +.code code { + font-family: 'Monaco', 'Courier New', monospace; + font-size: 0.9rem; + line-height: 1.6; + color: #FFC947; + white-space: pre-wrap; + word-break: break-all; + display: block; +} + +.footer { + margin-top: 3rem; + padding-top: 2rem; + border-top: 1px solid rgba(255, 255, 255, 0.1); + text-align: center; + opacity: 0.8; +} + +.footer a { + color: #FFC947; + text-decoration: none; + transition: opacity 0.2s; +} + +.footer a:hover { + opacity: 0.7; + text-decoration: underline; +} + +/* Scrollbar styling */ +.code::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +.code::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.2); + border-radius: 4px; +} + +.code::-webkit-scrollbar-thumb { + background: rgba(0, 245, 255, 0.3); + border-radius: 4px; +} + +.code::-webkit-scrollbar-thumb:hover { + background: rgba(0, 245, 255, 0.5); +} diff --git a/pages/widget-generator.tsx b/pages/widget-generator.tsx new file mode 100644 index 0000000..2542819 --- /dev/null +++ b/pages/widget-generator.tsx @@ -0,0 +1,340 @@ +import { useState, useMemo } from 'react'; +import Head from 'next/head'; +import dynamic from 'next/dynamic'; +import { GetStaticProps } from 'next'; +import { useMachine } from '../hooks/use-machine'; +import { observable } from 'mobx'; +import { IMachine } from '../engine/machine-interfaces'; +import { loadMachine } from '../services/load-machine'; +import styles from './widget-generator.module.css'; + +const WidgetCompact = dynamic( + () => import('../src/widget/WidgetCompact').then(mod => mod.WidgetCompact), + { ssr: false } +); + +interface IWidgetGeneratorProps { + salsaInstruments: string[]; + merengueInstruments: string[]; +} + +export default function WidgetGenerator({ salsaInstruments, merengueInstruments }: IWidgetGeneratorProps) { + const [machineType, setMachineType] = useState<'salsa' | 'merengue'>('salsa'); + const [selectedInstruments, setSelectedInstruments] = useState(['clave']); + const [instrumentPrograms, setInstrumentPrograms] = useState>({}); + const [bpm, setBpm] = useState(120); + const [autoplay, setAutoplay] = useState(false); + const [baseUrl, setBaseUrl] = useState('https://beat.salsanor.no'); + const [copied, setCopied] = useState(false); + + const salsaMachine = useMachine('/assets/machines/salsa.xml'); + const merengueMachine = useMachine('/assets/machines/merengue.xml'); + + const availableInstruments = machineType === 'salsa' ? salsaInstruments : merengueInstruments; + const currentMachine = machineType === 'salsa' ? salsaMachine : merengueMachine; + + const previewMachine = useMemo(() => { + if (!currentMachine) return null; + return observable({ + ...currentMachine, + bpm, + instruments: currentMachine.instruments.map(inst => { + const programIndex = instrumentPrograms[inst.id] ?? inst.activeProgram; + return observable({ + ...inst, + enabled: selectedInstruments.includes(inst.id), + activeProgram: programIndex + }); + }) + }); + }, [currentMachine, selectedInstruments, bpm, instrumentPrograms]); + + const toggleInstrument = (instrumentId: string) => { + setSelectedInstruments(prev => + prev.includes(instrumentId) + ? prev.filter(id => id !== instrumentId) + : [...prev, instrumentId] + ); + }; + + const setInstrumentProgram = (instrumentId: string, programIndex: number) => { + setInstrumentPrograms(prev => ({ + ...prev, + [instrumentId]: programIndex + })); + }; + + const getInstrumentFromMachine = (instrumentId: string) => { + return currentMachine?.instruments.find(i => i.id === instrumentId); + }; + + const selectAllInstruments = () => { + setSelectedInstruments(availableInstruments); + }; + + const clearAllInstruments = () => { + setSelectedInstruments([]); + }; + + const generateCode = () => { + const instruments = selectedInstruments.join(','); + + // Generate programs string: "clave:1,cowbell:2" + const programsArray = Object.entries(instrumentPrograms) + .filter(([instrumentId, _]) => selectedInstruments.includes(instrumentId)) + .map(([instrumentId, programIndex]) => `${instrumentId}:${programIndex}`); + const programsString = programsArray.length > 0 ? programsArray.join(',') : ''; + + const attrs = [ + 'data-beat-widget', + selectedInstruments.length > 0 ? `data-instruments="${instruments}"` : '', + programsString ? `data-programs="${programsString}"` : '', + bpm !== 120 ? `data-bpm="${bpm}"` : '', + machineType !== 'salsa' ? `data-machine="${machineType}"` : '', + autoplay ? `data-autoplay="true"` : '', + ].filter(Boolean).join(' '); + + return `
`; + }; + + const generateFullHTML = () => { + const widgetCode = generateCode(); + + // Generate installation instructions with proper structure + return ` + + + + +${widgetCode}`; + }; + + const copyToClipboard = (text: string) => { + navigator.clipboard.writeText(text); + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }; + + if (!salsaMachine || !merengueMachine) { + return ( +
+
🎵 Loading Beat Machine...
+
+ ); + } + + return ( + <> + + Beat Machine Widget Generator - SalsaNor + + + +
+
+

🎵 Beat Machine Widget Generator

+

Select instruments and settings, see live preview, and copy ready-to-use code.

+
+ +
+ {/* Left Panel - Configuration */} +
+
+

Machine Type

+
+ + +
+
+ +
+
+

Instruments

+
+ + +
+
+
+ {availableInstruments.map(instrumentId => { + const instrument = getInstrumentFromMachine(instrumentId); + const hasMultiplePrograms = instrument && instrument.programs.length > 1; + const selectedProgram = instrumentPrograms[instrumentId] ?? instrument?.activeProgram ?? 0; + + return ( +
+ + + {hasMultiplePrograms && selectedInstruments.includes(instrumentId) && ( + + )} +
+ ); + })} +
+
+ +
+

Tempo (BPM)

+
+ setBpm(parseInt(e.target.value))} + className={styles.slider} + /> + {bpm} BPM +
+
+ +
+

Settings

+ +
+ +
+

Base URL (for cross-domain)

+ setBaseUrl(e.target.value)} + className={styles.textInput} + placeholder="https://beat.salsanor.no" + /> +

+ URL to the server hosting the widget files and audio samples. +

+
+
+ + {/* Right Panel - Preview & Code */} +
+
+

Live Preview

+
+ {previewMachine && ( + + )} +
+
+ +
+
+

Step 1: Add this code just below <body> tag (once per page)

+ +
+
{generateFullHTML()}
+

+ This loads the Beat Machine widget library and sets the base URL for audio files. + Add this only once, even if you have multiple widgets on the same page. +

+
+ +
+
+

Step 2: Add widget(s) anywhere in your content

+ +
+
{generateCode()}
+

+ Place this code wherever you want the instrument player to appear. + You can add multiple widgets with different instruments on the same page. +

+
+
+
+ + +
+ + ); +} + +export const getStaticProps: GetStaticProps = async () => { + try { + const salsa = await loadMachine('salsa.xml'); + const merengue = await loadMachine('merengue.xml'); + + return { + props: { + salsaInstruments: salsa.instruments.map((i: any) => i.id), + merengueInstruments: merengue.instruments.map((i: any) => i.id), + }, + }; + } catch (error) { + console.error('Failed to load machines:', error); + return { + notFound: true, + }; + } +}; diff --git a/public/.htaccess b/public/.htaccess new file mode 100644 index 0000000..dafd870 --- /dev/null +++ b/public/.htaccess @@ -0,0 +1,182 @@ +# ====================================================================== +# SalsaNor Beat Machine - Apache Configuration +# For deployment to shared hosting (ProISP, etc.) +# ====================================================================== + +# ---------------------------------------------------------------------- +# CORS Headers - Required for cross-domain widget embedding +# ---------------------------------------------------------------------- + + # Allow all domains to embed the widget + Header set Access-Control-Allow-Origin "*" + + # Allow specific methods + Header set Access-Control-Allow-Methods "GET, OPTIONS" + + # Allow specific headers + Header set Access-Control-Allow-Headers "Content-Type, Accept" + + # Cache preflight requests for 1 day + Header set Access-Control-Max-Age "86400" + + +# ---------------------------------------------------------------------- +# MIME Types - Ensure correct content types for audio files +# ---------------------------------------------------------------------- + + # Audio formats + AddType audio/webm .webm + AddType audio/mpeg .mp3 + AddType audio/mp4 .m4a + + # Web formats + AddType application/javascript .js + AddType application/json .json + AddType text/css .css + AddType text/html .html + + # Image formats + AddType image/svg+xml .svg + AddType image/webp .webp + + +# ---------------------------------------------------------------------- +# Compression - Reduce bandwidth usage +# ---------------------------------------------------------------------- + + # Compress HTML, CSS, JavaScript, JSON + AddOutputFilterByType DEFLATE text/html + AddOutputFilterByType DEFLATE text/css + AddOutputFilterByType DEFLATE text/javascript + AddOutputFilterByType DEFLATE application/javascript + AddOutputFilterByType DEFLATE application/json + AddOutputFilterByType DEFLATE text/xml + AddOutputFilterByType DEFLATE application/xml + AddOutputFilterByType DEFLATE image/svg+xml + + # Don't compress already-compressed formats + SetEnvIfNoCase Request_URI \.(?:gif|jpe?g|png|webp|mp3|webm|m4a)$ no-gzip + + +# ---------------------------------------------------------------------- +# Caching - Optimize load times +# ---------------------------------------------------------------------- + + ExpiresActive On + ExpiresDefault "access plus 1 day" + + # Audio files - cache for 1 year (change infrequently) + ExpiresByType audio/webm "access plus 1 year" + ExpiresByType audio/mpeg "access plus 1 year" + ExpiresByType audio/mp4 "access plus 1 year" + + # Images - cache for 1 year + ExpiresByType image/jpeg "access plus 1 year" + ExpiresByType image/png "access plus 1 year" + ExpiresByType image/svg+xml "access plus 1 year" + ExpiresByType image/webp "access plus 1 year" + + # JSON data - cache for 1 week + ExpiresByType application/json "access plus 1 week" + + # CSS and JavaScript - cache for 1 month + ExpiresByType text/css "access plus 1 month" + ExpiresByType application/javascript "access plus 1 month" + ExpiresByType text/javascript "access plus 1 month" + + # HTML - cache for 1 hour + ExpiresByType text/html "access plus 1 hour" + + # Manifest and XML - cache for 1 week + ExpiresByType application/manifest+json "access plus 1 week" + ExpiresByType text/xml "access plus 1 week" + + +# ---------------------------------------------------------------------- +# Cache-Control Headers +# ---------------------------------------------------------------------- + + # Audio files - immutable, long cache + + Header set Cache-Control "public, max-age=31536000, immutable" + + + # Images - long cache + + Header set Cache-Control "public, max-age=31536000, immutable" + + + # JavaScript and CSS - long cache with version query params for cache busting + + Header set Cache-Control "public, max-age=31536000, immutable" + + + # HTML - short cache, must revalidate + + Header set Cache-Control "public, max-age=3600, must-revalidate" + + + +# ---------------------------------------------------------------------- +# Security Headers +# ---------------------------------------------------------------------- + + # Prevent MIME type sniffing + Header set X-Content-Type-Options "nosniff" + + # Enable XSS protection + Header set X-XSS-Protection "1; mode=block" + + # Prevent clickjacking (allow iframes from same origin) + Header set X-Frame-Options "SAMEORIGIN" + + # Referrer policy + Header set Referrer-Policy "strict-origin-when-cross-origin" + + +# ---------------------------------------------------------------------- +# URL Rewriting - For Single Page Application routing +# ---------------------------------------------------------------------- + + RewriteEngine On + RewriteBase / + + # If requesting a directory, serve index.html + RewriteCond %{REQUEST_FILENAME} -d + RewriteRule ^(.*)$ $1/index.html [L] + + # Don't rewrite if file exists + RewriteCond %{REQUEST_FILENAME} !-f + RewriteCond %{REQUEST_FILENAME} !-d + + # Rewrite everything else to index.html for client-side routing + # Comment out if not using client-side routing + # RewriteRule . /index.html [L] + + +# ---------------------------------------------------------------------- +# Error Pages (Optional) +# ---------------------------------------------------------------------- +# ErrorDocument 404 /404.html +# ErrorDocument 500 /500.html + +# ---------------------------------------------------------------------- +# Disable Directory Browsing +# ---------------------------------------------------------------------- +Options -Indexes + +# ---------------------------------------------------------------------- +# Force HTTPS (Uncomment when SSL certificate is installed) +# ---------------------------------------------------------------------- +# +# RewriteEngine On +# RewriteCond %{HTTPS} off +# RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301] +# + +# ---------------------------------------------------------------------- +# Performance - Keep-Alive +# ---------------------------------------------------------------------- + + Header set Connection keep-alive + diff --git a/public/assets/machines/merengue.xml b/public/assets/machines/merengue.xml index 3018fe5..4785b9b 100644 --- a/public/assets/machines/merengue.xml +++ b/public/assets/machines/merengue.xml @@ -1,4 +1,4 @@ - + diff --git a/public/assets/machines/salsa.xml b/public/assets/machines/salsa.xml index 6c8a70c..1439f3a 100644 --- a/public/assets/machines/salsa.xml +++ b/public/assets/machines/salsa.xml @@ -1,4 +1,4 @@ - diff --git a/public/favicon.ico b/public/favicon.ico deleted file mode 100644 index caaad75..0000000 Binary files a/public/favicon.ico and /dev/null differ diff --git a/public/manifest.json b/public/manifest.json deleted file mode 100644 index 52606ab..0000000 --- a/public/manifest.json +++ /dev/null @@ -1,51 +0,0 @@ -{ - "name": "Salsa Beat Machine", - "short_name": "Salsa Rhythm", - "theme_color": "#d20000", - "background_color": "#fafafa", - "display": "standalone", - "scope": "/", - "start_url": "/", - "icons": [ - { - "src": "assets/icons/icon-72x72.png", - "sizes": "72x72", - "type": "image/png" - }, - { - "src": "assets/icons/icon-96x96.png", - "sizes": "96x96", - "type": "image/png" - }, - { - "src": "assets/icons/icon-128x128.png", - "sizes": "128x128", - "type": "image/png" - }, - { - "src": "assets/icons/icon-144x144.png", - "sizes": "144x144", - "type": "image/png" - }, - { - "src": "assets/icons/icon-152x152.png", - "sizes": "152x152", - "type": "image/png" - }, - { - "src": "assets/icons/icon-192x192.png", - "sizes": "192x192", - "type": "image/png" - }, - { - "src": "assets/icons/icon-384x384.png", - "sizes": "384x384", - "type": "image/png" - }, - { - "src": "assets/icons/icon-512x512.png", - "sizes": "512x512", - "type": "image/png" - } - ] -} diff --git a/public/widget.js b/public/widget.js new file mode 100644 index 0000000..3b74aa6 --- /dev/null +++ b/public/widget.js @@ -0,0 +1,253 @@ +(function(Bs){"use strict";function Fs(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var fl={exports:{}},Ar={},dl={exports:{}},G={},Us;function up(){if(Us)return G;Us=1;var i=Symbol.for("react.element"),l=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),p=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),y=Symbol.for("react.suspense"),C=Symbol.for("react.memo"),P=Symbol.for("react.lazy"),R=Symbol.iterator;function L(_){return _===null||typeof _!="object"?null:(_=R&&_[R]||_["@@iterator"],typeof _=="function"?_:null)}var Y={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},le=Object.assign,H={};function K(_,x,Q){this.props=_,this.context=x,this.refs=H,this.updater=Q||Y}K.prototype.isReactComponent={},K.prototype.setState=function(_,x){if(typeof _!="object"&&typeof _!="function"&&_!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,_,x,"setState")},K.prototype.forceUpdate=function(_){this.updater.enqueueForceUpdate(this,_,"forceUpdate")};function Ve(){}Ve.prototype=K.prototype;function $e(_,x,Q){this.props=_,this.context=x,this.refs=H,this.updater=Q||Y}var Ne=$e.prototype=new Ve;Ne.constructor=$e,le(Ne,K.prototype),Ne.isPureReactComponent=!0;var re=Array.isArray,Se=Object.prototype.hasOwnProperty,he={current:null},Te={key:!0,ref:!0,__self:!0,__source:!0};function ct(_,x,Q){var X,ee={},te=null,se=null;if(x!=null)for(X in x.ref!==void 0&&(se=x.ref),x.key!==void 0&&(te=""+x.key),x)Se.call(x,X)&&!Te.hasOwnProperty(X)&&(ee[X]=x[X]);var ie=arguments.length-2;if(ie===1)ee.children=Q;else if(1>>1,x=M[_];if(0>>1;_c(ee,D))tec(se,ee)?(M[_]=se,M[te]=D,_=te):(M[_]=ee,M[X]=D,_=X);else if(tec(se,D))M[_]=se,M[te]=D,_=te;else break e}}return V}function c(M,V){var D=M.sortIndex-V.sortIndex;return D!==0?D:M.id-V.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;i.unstable_now=function(){return d.now()}}else{var p=Date,g=p.now();i.unstable_now=function(){return p.now()-g}}var y=[],C=[],P=1,R=null,L=3,Y=!1,le=!1,H=!1,K=typeof setTimeout=="function"?setTimeout:null,Ve=typeof clearTimeout=="function"?clearTimeout:null,$e=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Ne(M){for(var V=o(C);V!==null;){if(V.callback===null)s(C);else if(V.startTime<=M)s(C),V.sortIndex=V.expirationTime,l(y,V);else break;V=o(C)}}function re(M){if(H=!1,Ne(M),!le)if(o(y)!==null)le=!0,We(Se);else{var V=o(C);V!==null&&ge(re,V.startTime-M)}}function Se(M,V){le=!1,H&&(H=!1,Ve(ct),ct=-1),Y=!0;var D=L;try{for(Ne(V),R=o(y);R!==null&&(!(R.expirationTime>V)||M&&!bn());){var _=R.callback;if(typeof _=="function"){R.callback=null,L=R.priorityLevel;var x=_(R.expirationTime<=V);V=i.unstable_now(),typeof x=="function"?R.callback=x:R===o(y)&&s(y),Ne(V)}else s(y);R=o(y)}if(R!==null)var Q=!0;else{var X=o(C);X!==null&&ge(re,X.startTime-V),Q=!1}return Q}finally{R=null,L=D,Y=!1}}var he=!1,Te=null,ct=-1,ln=5,Qt=-1;function bn(){return!(i.unstable_now()-QtM||125_?(M.sortIndex=D,l(C,M),o(y)===null&&M===o(C)&&(H?(Ve(ct),ct=-1):H=!0,ge(re,D-_))):(M.sortIndex=x,l(y,M),le||Y||(le=!0,We(Se))),M},i.unstable_shouldYield=bn,i.unstable_wrapCallback=function(M){var V=L;return function(){var D=L;L=V;try{return M.apply(this,arguments)}finally{L=D}}}})(ml)),ml}var Ks;function fp(){return Ks||(Ks=1,vl.exports=cp()),vl.exports}var Qs;function dp(){if(Qs)return Ue;Qs=1;var i=Pi(),l=fp();function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),y=Object.prototype.hasOwnProperty,C=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,P={},R={};function L(e){return y.call(R,e)?!0:y.call(P,e)?!1:C.test(e)?R[e]=!0:(P[e]=!0,!1)}function Y(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function le(e,t,n,r){if(t===null||typeof t>"u"||Y(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function H(e,t,n,r,u,a,f){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=u,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=f}var K={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){K[e]=new H(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];K[t]=new H(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){K[e]=new H(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){K[e]=new H(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){K[e]=new H(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){K[e]=new H(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){K[e]=new H(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){K[e]=new H(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){K[e]=new H(e,5,!1,e.toLowerCase(),null,!1,!1)});var Ve=/[\-:]([a-z])/g;function $e(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Ve,$e);K[t]=new H(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Ve,$e);K[t]=new H(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Ve,$e);K[t]=new H(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){K[e]=new H(e,1,!1,e.toLowerCase(),null,!1,!1)}),K.xlinkHref=new H("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){K[e]=new H(e,1,!1,e.toLowerCase(),null,!0,!0)});function Ne(e,t,n,r){var u=K.hasOwnProperty(t)?K[t]:null;(u!==null?u.type!==0:r||!(2h||u[f]!==a[h]){var v=` +`+u[f].replace(" at new "," at ");return e.displayName&&v.includes("")&&(v=v.replace("",e.displayName)),v}while(1<=f&&0<=h);break}}}finally{Q=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?x(e):""}function ee(e){switch(e.tag){case 5:return x(e.type);case 16:return x("Lazy");case 13:return x("Suspense");case 19:return x("SuspenseList");case 0:case 2:case 15:return e=X(e.type,!1),e;case 11:return e=X(e.type.render,!1),e;case 1:return e=X(e.type,!0),e;default:return""}}function te(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Te:return"Fragment";case he:return"Portal";case ln:return"Profiler";case ct:return"StrictMode";case Ze:return"Suspense";case St:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case bn:return(e.displayName||"Context")+".Consumer";case Qt:return(e._context.displayName||"Context")+".Provider";case Lt:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case bt:return t=e.displayName||null,t!==null?t:te(e.type)||"Memo";case We:t=e._payload,e=e._init;try{return te(e(t))}catch{}}return null}function se(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return te(t);case 8:return t===ct?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ie(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function de(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function et(e){var t=de(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var u=n.get,a=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return u.call(this)},set:function(f){r=""+f,a.call(this,f)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(f){r=""+f},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function eo(e){e._valueTracker||(e._valueTracker=et(e))}function dc(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=de(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function to(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Kl(e,t){var n=t.checked;return D({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function pc(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ie(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function hc(e,t){t=t.checked,t!=null&&Ne(e,"checked",t,!1)}function Ql(e,t){hc(e,t);var n=ie(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Gl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Gl(e,t.type,ie(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function vc(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Gl(e,t,n){(t!=="number"||to(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Vr=Array.isArray;function rr(e,t,n,r){if(e=e.options,t){t={};for(var u=0;u"+t.valueOf().toString()+"",t=no.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function $r(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Wr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Dv=["Webkit","ms","Moz","O"];Object.keys(Wr).forEach(function(e){Dv.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wr[t]=Wr[e]})});function Sc(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Wr.hasOwnProperty(e)&&Wr[e]?(""+t).trim():t+"px"}function Ec(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,u=Sc(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,u):e[n]=u}}var zv=D({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ql(e,t){if(t){if(zv[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(o(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(o(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(o(61))}if(t.style!=null&&typeof t.style!="object")throw Error(o(62))}}function Jl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Zl=null;function eu(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var tu=null,ir=null,or=null;function kc(e){if(e=di(e)){if(typeof tu!="function")throw Error(o(280));var t=e.stateNode;t&&(t=Co(t),tu(e.stateNode,e.type,t))}}function xc(e){ir?or?or.push(e):or=[e]:ir=e}function Oc(){if(ir){var e=ir,t=or;if(or=ir=null,kc(e),t)for(e=0;e>>=0,e===0?32:31-(Gv(e)/Yv|0)|0}var uo=64,so=4194304;function Gr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ao(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,u=e.suspendedLanes,a=e.pingedLanes,f=n&268435455;if(f!==0){var h=f&~u;h!==0?r=Gr(h):(a&=f,a!==0&&(r=Gr(a)))}else f=n&~u,f!==0?r=Gr(f):a!==0&&(r=Gr(a));if(r===0)return 0;if(t!==0&&t!==r&&(t&u)===0&&(u=r&-r,a=t&-t,u>=a||u===16&&(a&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Yr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Et(t),e[t]=n}function Zv(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=ri),Zc=" ",ef=!1;function tf(e,t){switch(e){case"keyup":return Pm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function nf(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var sr=!1;function Nm(e,t){switch(e){case"compositionend":return nf(t);case"keypress":return t.which!==32?null:(ef=!0,Zc);case"textInput":return e=t.data,e===Zc&&ef?null:e;default:return null}}function Tm(e,t){if(sr)return e==="compositionend"||!_u&&tf(e,t)?(e=Qc(),vo=pu=fn=null,sr=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=cf(n)}}function df(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?df(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function pf(){for(var e=window,t=to();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=to(e.document)}return t}function Eu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Bm(e){var t=pf(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&df(n.ownerDocument.documentElement,n)){if(r!==null&&Eu(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var u=n.textContent.length,a=Math.min(r.start,u);r=r.end===void 0?a:Math.min(r.end,u),!e.extend&&a>r&&(u=r,r=a,a=u),u=ff(n,a);var f=ff(n,r);u&&f&&(e.rangeCount!==1||e.anchorNode!==u.node||e.anchorOffset!==u.offset||e.focusNode!==f.node||e.focusOffset!==f.offset)&&(t=t.createRange(),t.setStart(u.node,u.offset),e.removeAllRanges(),a>r?(e.addRange(t),e.extend(f.node,f.offset)):(t.setEnd(f.node,f.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,ar=null,ku=null,ui=null,xu=!1;function hf(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;xu||ar==null||ar!==to(r)||(r=ar,"selectionStart"in r&&Eu(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),ui&&li(ui,r)||(ui=r,r=ko(ku,"onSelect"),0hr||(e.current=Du[hr],Du[hr]=null,hr--)}function ae(e,t){hr++,Du[hr]=e.current,e.current=t}var vn={},je=hn(vn),He=hn(!1),In=vn;function vr(e,t){var n=e.type.contextTypes;if(!n)return vn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var u={},a;for(a in n)u[a]=t[a];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=u),u}function Ke(e){return e=e.childContextTypes,e!=null}function Po(){fe(He),fe(je)}function Nf(e,t,n){if(je.current!==vn)throw Error(o(168));ae(je,t),ae(He,n)}function Tf(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var u in r)if(!(u in t))throw Error(o(108,se(e)||"Unknown",u));return D({},n,r)}function Ao(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||vn,In=je.current,ae(je,e),ae(He,He.current),!0}function Rf(e,t,n){var r=e.stateNode;if(!r)throw Error(o(169));n?(e=Tf(e,t,In),r.__reactInternalMemoizedMergedChildContext=e,fe(He),fe(je),ae(je,e)):fe(He),ae(He,n)}var Yt=null,No=!1,zu=!1;function Mf(e){Yt===null?Yt=[e]:Yt.push(e)}function qm(e){No=!0,Mf(e)}function mn(){if(!zu&&Yt!==null){zu=!0;var e=0,t=oe;try{var n=Yt;for(oe=1;e>=f,u-=f,Xt=1<<32-Et(t)+u|n<W?(Ae=U,U=null):Ae=U.sibling;var ne=k(w,U,S[W],N);if(ne===null){U===null&&(U=Ae);break}e&&U&&ne.alternate===null&&t(w,U),m=a(ne,m,W),F===null?B=ne:F.sibling=ne,F=ne,U=Ae}if(W===S.length)return n(w,U),pe&&Fn(w,W),B;if(U===null){for(;WW?(Ae=U,U=null):Ae=U.sibling;var On=k(w,U,ne.value,N);if(On===null){U===null&&(U=Ae);break}e&&U&&On.alternate===null&&t(w,U),m=a(On,m,W),F===null?B=On:F.sibling=On,F=On,U=Ae}if(ne.done)return n(w,U),pe&&Fn(w,W),B;if(U===null){for(;!ne.done;W++,ne=S.next())ne=A(w,ne.value,N),ne!==null&&(m=a(ne,m,W),F===null?B=ne:F.sibling=ne,F=ne);return pe&&Fn(w,W),B}for(U=r(w,U);!ne.done;W++,ne=S.next())ne=j(U,w,W,ne.value,N),ne!==null&&(e&&ne.alternate!==null&&U.delete(ne.key===null?W:ne.key),m=a(ne,m,W),F===null?B=ne:F.sibling=ne,F=ne);return e&&U.forEach(function(Rg){return t(w,Rg)}),pe&&Fn(w,W),B}function we(w,m,S,N){if(typeof S=="object"&&S!==null&&S.type===Te&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case Se:e:{for(var B=S.key,F=m;F!==null;){if(F.key===B){if(B=S.type,B===Te){if(F.tag===7){n(w,F.sibling),m=u(F,S.props.children),m.return=w,w=m;break e}}else if(F.elementType===B||typeof B=="object"&&B!==null&&B.$$typeof===We&&If(B)===F.type){n(w,F.sibling),m=u(F,S.props),m.ref=pi(w,F,S),m.return=w,w=m;break e}n(w,F);break}else t(w,F);F=F.sibling}S.type===Te?(m=Gn(S.props.children,w.mode,N,S.key),m.return=w,w=m):(N=rl(S.type,S.key,S.props,null,w.mode,N),N.ref=pi(w,m,S),N.return=w,w=N)}return f(w);case he:e:{for(F=S.key;m!==null;){if(m.key===F)if(m.tag===4&&m.stateNode.containerInfo===S.containerInfo&&m.stateNode.implementation===S.implementation){n(w,m.sibling),m=u(m,S.children||[]),m.return=w,w=m;break e}else{n(w,m);break}else t(w,m);m=m.sibling}m=Ls(S,w.mode,N),m.return=w,w=m}return f(w);case We:return F=S._init,we(w,m,F(S._payload),N)}if(Vr(S))return z(w,m,S,N);if(V(S))return I(w,m,S,N);jo(w,S)}return typeof S=="string"&&S!==""||typeof S=="number"?(S=""+S,m!==null&&m.tag===6?(n(w,m.sibling),m=u(m,S),m.return=w,w=m):(n(w,m),m=js(S,w.mode,N),m.return=w,w=m),f(w)):n(w,m)}return we}var _r=Bf(!0),Ff=Bf(!1),Lo=hn(null),bo=null,wr=null,$u=null;function Wu(){$u=wr=bo=null}function Hu(e){var t=Lo.current;fe(Lo),e._currentValue=t}function Ku(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Sr(e,t){bo=e,$u=wr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Qe=!0),e.firstContext=null)}function pt(e){var t=e._currentValue;if($u!==e)if(e={context:e,memoizedValue:t,next:null},wr===null){if(bo===null)throw Error(o(308));wr=e,bo.dependencies={lanes:0,firstContext:e}}else wr=wr.next=e;return t}var Un=null;function Qu(e){Un===null?Un=[e]:Un.push(e)}function Uf(e,t,n,r){var u=t.interleaved;return u===null?(n.next=n,Qu(t)):(n.next=u.next,u.next=n),t.interleaved=n,Jt(e,r)}function Jt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var gn=!1;function Gu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Vf(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Zt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function yn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(J&2)!==0){var u=r.pending;return u===null?t.next=t:(t.next=u.next,u.next=t),r.pending=t,Jt(e,n)}return u=r.interleaved,u===null?(t.next=t,Qu(r)):(t.next=u.next,u.next=t),r.interleaved=t,Jt(e,n)}function Do(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,su(e,n)}}function $f(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var u=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var f={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};a===null?u=a=f:a=a.next=f,n=n.next}while(n!==null);a===null?u=a=t:a=a.next=t}else u=a=t;n={baseState:r.baseState,firstBaseUpdate:u,lastBaseUpdate:a,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function zo(e,t,n,r){var u=e.updateQueue;gn=!1;var a=u.firstBaseUpdate,f=u.lastBaseUpdate,h=u.shared.pending;if(h!==null){u.shared.pending=null;var v=h,E=v.next;v.next=null,f===null?a=E:f.next=E,f=v;var O=e.alternate;O!==null&&(O=O.updateQueue,h=O.lastBaseUpdate,h!==f&&(h===null?O.firstBaseUpdate=E:h.next=E,O.lastBaseUpdate=v))}if(a!==null){var A=u.baseState;f=0,O=E=v=null,h=a;do{var k=h.lane,j=h.eventTime;if((r&k)===k){O!==null&&(O=O.next={eventTime:j,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});e:{var z=e,I=h;switch(k=t,j=n,I.tag){case 1:if(z=I.payload,typeof z=="function"){A=z.call(j,A,k);break e}A=z;break e;case 3:z.flags=z.flags&-65537|128;case 0:if(z=I.payload,k=typeof z=="function"?z.call(j,A,k):z,k==null)break e;A=D({},A,k);break e;case 2:gn=!0}}h.callback!==null&&h.lane!==0&&(e.flags|=64,k=u.effects,k===null?u.effects=[h]:k.push(h))}else j={eventTime:j,lane:k,tag:h.tag,payload:h.payload,callback:h.callback,next:null},O===null?(E=O=j,v=A):O=O.next=j,f|=k;if(h=h.next,h===null){if(h=u.shared.pending,h===null)break;k=h,h=k.next,k.next=null,u.lastBaseUpdate=k,u.shared.pending=null}}while(!0);if(O===null&&(v=A),u.baseState=v,u.firstBaseUpdate=E,u.lastBaseUpdate=O,t=u.shared.interleaved,t!==null){u=t;do f|=u.lane,u=u.next;while(u!==t)}else a===null&&(u.shared.lanes=0);Wn|=f,e.lanes=f,e.memoizedState=A}}function Wf(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Zu.transition;Zu.transition={};try{e(!1),t()}finally{oe=n,Zu.transition=r}}function ad(){return ht().memoizedState}function tg(e,t,n){var r=En(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},cd(e))fd(t,n);else if(n=Uf(e,t,n,r),n!==null){var u=Ie();At(n,e,r,u),dd(n,t,r)}}function ng(e,t,n){var r=En(e),u={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(cd(e))fd(t,u);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var f=t.lastRenderedState,h=a(f,n);if(u.hasEagerState=!0,u.eagerState=h,kt(h,f)){var v=t.interleaved;v===null?(u.next=u,Qu(t)):(u.next=v.next,v.next=u),t.interleaved=u;return}}catch{}n=Uf(e,t,u,r),n!==null&&(u=Ie(),At(n,e,r,u),dd(n,t,r))}}function cd(e){var t=e.alternate;return e===me||t!==null&&t===me}function fd(e,t){gi=Fo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function dd(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,su(e,n)}}var $o={readContext:pt,useCallback:Le,useContext:Le,useEffect:Le,useImperativeHandle:Le,useInsertionEffect:Le,useLayoutEffect:Le,useMemo:Le,useReducer:Le,useRef:Le,useState:Le,useDebugValue:Le,useDeferredValue:Le,useTransition:Le,useMutableSource:Le,useSyncExternalStore:Le,useId:Le,unstable_isNewReconciler:!1},rg={readContext:pt,useCallback:function(e,t){return Bt().memoizedState=[e,t===void 0?null:t],e},useContext:pt,useEffect:td,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Uo(4194308,4,id.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Uo(4194308,4,e,t)},useInsertionEffect:function(e,t){return Uo(4,2,e,t)},useMemo:function(e,t){var n=Bt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Bt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=tg.bind(null,me,e),[r.memoizedState,e]},useRef:function(e){var t=Bt();return e={current:e},t.memoizedState=e},useState:Zf,useDebugValue:ls,useDeferredValue:function(e){return Bt().memoizedState=e},useTransition:function(){var e=Zf(!1),t=e[0];return e=eg.bind(null,e[1]),Bt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=me,u=Bt();if(pe){if(n===void 0)throw Error(o(407));n=n()}else{if(n=t(),Pe===null)throw Error(o(349));($n&30)!==0||Gf(r,t,n)}u.memoizedState=n;var a={value:n,getSnapshot:t};return u.queue=a,td(Xf.bind(null,r,a,e),[e]),r.flags|=2048,wi(9,Yf.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=Bt(),t=Pe.identifierPrefix;if(pe){var n=qt,r=Xt;n=(r&~(1<<32-Et(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=yi++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=f.createElement(n,{is:r.is}):(e=f.createElement(n),n==="select"&&(f=e,r.multiple?f.multiple=!0:r.size&&(f.size=r.size))):e=f.createElementNS(e,n),e[zt]=t,e[fi]=r,Md(e,t,!1,!1),t.stateNode=e;e:{switch(f=Jl(n,r),n){case"dialog":ce("cancel",e),ce("close",e),u=r;break;case"iframe":case"object":case"embed":ce("load",e),u=r;break;case"video":case"audio":for(u=0;uCr&&(t.flags|=128,r=!0,Si(a,!1),t.lanes=4194304)}else{if(!r)if(e=Io(f),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Si(a,!0),a.tail===null&&a.tailMode==="hidden"&&!f.alternate&&!pe)return be(t),null}else 2*_e()-a.renderingStartTime>Cr&&n!==1073741824&&(t.flags|=128,r=!0,Si(a,!1),t.lanes=4194304);a.isBackwards?(f.sibling=t.child,t.child=f):(n=a.last,n!==null?n.sibling=f:t.child=f,a.last=f)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=_e(),t.sibling=null,n=ve.current,ae(ve,r?n&1|2:n&1),t):(be(t),null);case 22:case 23:return Ts(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(it&1073741824)!==0&&(be(t),t.subtreeFlags&6&&(t.flags|=8192)):be(t),null;case 24:return null;case 25:return null}throw Error(o(156,t.tag))}function fg(e,t){switch(Bu(t),t.tag){case 1:return Ke(t.type)&&Po(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Er(),fe(He),fe(je),Ju(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Xu(t),null;case 13:if(fe(ve),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(o(340));yr()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return fe(ve),null;case 4:return Er(),null;case 10:return Hu(t.type._context),null;case 22:case 23:return Ts(),null;case 24:return null;default:return null}}var Qo=!1,De=!1,dg=typeof WeakSet=="function"?WeakSet:Set,b=null;function xr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ye(e,t,r)}else n.current=null}function ys(e,t,n){try{n()}catch(r){ye(e,t,r)}}var bd=!1;function pg(e,t){if(Tu=po,e=pf(),Eu(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var u=r.anchorOffset,a=r.focusNode;r=r.focusOffset;try{n.nodeType,a.nodeType}catch{n=null;break e}var f=0,h=-1,v=-1,E=0,O=0,A=e,k=null;t:for(;;){for(var j;A!==n||u!==0&&A.nodeType!==3||(h=f+u),A!==a||r!==0&&A.nodeType!==3||(v=f+r),A.nodeType===3&&(f+=A.nodeValue.length),(j=A.firstChild)!==null;)k=A,A=j;for(;;){if(A===e)break t;if(k===n&&++E===u&&(h=f),k===a&&++O===r&&(v=f),(j=A.nextSibling)!==null)break;A=k,k=A.parentNode}A=j}n=h===-1||v===-1?null:{start:h,end:v}}else n=null}n=n||{start:0,end:0}}else n=null;for(Ru={focusedElem:e,selectionRange:n},po=!1,b=t;b!==null;)if(t=b,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,b=e;else for(;b!==null;){t=b;try{var z=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(z!==null){var I=z.memoizedProps,we=z.memoizedState,w=t.stateNode,m=w.getSnapshotBeforeUpdate(t.elementType===t.type?I:Ot(t.type,I),we);w.__reactInternalSnapshotBeforeUpdate=m}break;case 3:var S=t.stateNode.containerInfo;S.nodeType===1?S.textContent="":S.nodeType===9&&S.documentElement&&S.removeChild(S.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(o(163))}}catch(N){ye(t,t.return,N)}if(e=t.sibling,e!==null){e.return=t.return,b=e;break}b=t.return}return z=bd,bd=!1,z}function Ei(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var u=r=r.next;do{if((u.tag&e)===e){var a=u.destroy;u.destroy=void 0,a!==void 0&&ys(t,n,a)}u=u.next}while(u!==r)}}function Go(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function _s(e){var t=e.ref;if(t!==null){var n=e.stateNode;e.tag,e=n,typeof t=="function"?t(e):t.current=e}}function Dd(e){var t=e.alternate;t!==null&&(e.alternate=null,Dd(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[zt],delete t[fi],delete t[bu],delete t[Ym],delete t[Xm])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function zd(e){return e.tag===5||e.tag===3||e.tag===4}function Id(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||zd(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ws(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Oo));else if(r!==4&&(e=e.child,e!==null))for(ws(e,t,n),e=e.sibling;e!==null;)ws(e,t,n),e=e.sibling}function Ss(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ss(e,t,n),e=e.sibling;e!==null;)Ss(e,t,n),e=e.sibling}var Re=null,Ct=!1;function _n(e,t,n){for(n=n.child;n!==null;)Bd(e,t,n),n=n.sibling}function Bd(e,t,n){if(Dt&&typeof Dt.onCommitFiberUnmount=="function")try{Dt.onCommitFiberUnmount(lo,n)}catch{}switch(n.tag){case 5:De||xr(n,t);case 6:var r=Re,u=Ct;Re=null,_n(e,t,n),Re=r,Ct=u,Re!==null&&(Ct?(e=Re,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Re.removeChild(n.stateNode));break;case 18:Re!==null&&(Ct?(e=Re,n=n.stateNode,e.nodeType===8?Lu(e.parentNode,n):e.nodeType===1&&Lu(e,n),ei(e)):Lu(Re,n.stateNode));break;case 4:r=Re,u=Ct,Re=n.stateNode.containerInfo,Ct=!0,_n(e,t,n),Re=r,Ct=u;break;case 0:case 11:case 14:case 15:if(!De&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){u=r=r.next;do{var a=u,f=a.destroy;a=a.tag,f!==void 0&&((a&2)!==0||(a&4)!==0)&&ys(n,t,f),u=u.next}while(u!==r)}_n(e,t,n);break;case 1:if(!De&&(xr(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(h){ye(n,t,h)}_n(e,t,n);break;case 21:_n(e,t,n);break;case 22:n.mode&1?(De=(r=De)||n.memoizedState!==null,_n(e,t,n),De=r):_n(e,t,n);break;default:_n(e,t,n)}}function Fd(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new dg),t.forEach(function(r){var u=Eg.bind(null,e,r);n.has(r)||(n.add(r),r.then(u,u))})}}function Pt(e,t){var n=t.deletions;if(n!==null)for(var r=0;ru&&(u=f),r&=~a}if(r=u,r=_e()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*vg(r/1960))-r,10e?16:e,Sn===null)var r=!1;else{if(e=Sn,Sn=null,Zo=0,(J&6)!==0)throw Error(o(331));var u=J;for(J|=4,b=e.current;b!==null;){var a=b,f=a.child;if((b.flags&16)!==0){var h=a.deletions;if(h!==null){for(var v=0;v_e()-xs?Kn(e,0):ks|=n),Ye(e,t)}function Zd(e,t){t===0&&((e.mode&1)===0?t=1:(t=so,so<<=1,(so&130023424)===0&&(so=4194304)));var n=Ie();e=Jt(e,t),e!==null&&(Yr(e,t,n),Ye(e,n))}function Sg(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Zd(e,n)}function Eg(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,u=e.memoizedState;u!==null&&(n=u.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(o(314))}r!==null&&r.delete(t),Zd(e,n)}var ep;ep=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||He.current)Qe=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Qe=!1,ag(e,t,n);Qe=(e.flags&131072)!==0}else Qe=!1,pe&&(t.flags&1048576)!==0&&jf(t,Ro,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ko(e,t),e=t.pendingProps;var u=vr(t,je.current);Sr(t,n),u=ts(null,t,r,e,u,n);var a=ns();return t.flags|=1,typeof u=="object"&&u!==null&&typeof u.render=="function"&&u.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,Ke(r)?(a=!0,Ao(t)):a=!1,t.memoizedState=u.state!==null&&u.state!==void 0?u.state:null,Gu(t),u.updater=Wo,t.stateNode=u,u._reactInternals=t,ss(t,r,e,n),t=ds(null,t,r,!0,a,n)):(t.tag=0,pe&&a&&Iu(t),ze(null,t,u,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ko(e,t),e=t.pendingProps,u=r._init,r=u(r._payload),t.type=r,u=t.tag=xg(r),e=Ot(r,e),u){case 0:t=fs(null,t,r,e,n);break e;case 1:t=Cd(null,t,r,e,n);break e;case 11:t=Sd(null,t,r,e,n);break e;case 14:t=Ed(null,t,r,Ot(r.type,e),n);break e}throw Error(o(306,r,""))}return t;case 0:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),fs(e,t,r,u,n);case 1:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Cd(e,t,r,u,n);case 3:e:{if(Pd(t),e===null)throw Error(o(387));r=t.pendingProps,a=t.memoizedState,u=a.element,Vf(e,t),zo(t,r,null,n);var f=t.memoizedState;if(r=f.element,a.isDehydrated)if(a={element:r,isDehydrated:!1,cache:f.cache,pendingSuspenseBoundaries:f.pendingSuspenseBoundaries,transitions:f.transitions},t.updateQueue.baseState=a,t.memoizedState=a,t.flags&256){u=kr(Error(o(423)),t),t=Ad(e,t,r,n,u);break e}else if(r!==u){u=kr(Error(o(424)),t),t=Ad(e,t,r,n,u);break e}else for(rt=pn(t.stateNode.containerInfo.firstChild),nt=t,pe=!0,xt=null,n=Ff(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(yr(),r===u){t=en(e,t,n);break e}ze(e,t,r,n)}t=t.child}return t;case 5:return Hf(t),e===null&&Uu(t),r=t.type,u=t.pendingProps,a=e!==null?e.memoizedProps:null,f=u.children,Mu(r,u)?f=null:a!==null&&Mu(r,a)&&(t.flags|=32),Od(e,t),ze(e,t,f,n),t.child;case 6:return e===null&&Uu(t),null;case 13:return Nd(e,t,n);case 4:return Yu(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=_r(t,null,r,n):ze(e,t,r,n),t.child;case 11:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Sd(e,t,r,u,n);case 7:return ze(e,t,t.pendingProps,n),t.child;case 8:return ze(e,t,t.pendingProps.children,n),t.child;case 12:return ze(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,u=t.pendingProps,a=t.memoizedProps,f=u.value,ae(Lo,r._currentValue),r._currentValue=f,a!==null)if(kt(a.value,f)){if(a.children===u.children&&!He.current){t=en(e,t,n);break e}}else for(a=t.child,a!==null&&(a.return=t);a!==null;){var h=a.dependencies;if(h!==null){f=a.child;for(var v=h.firstContext;v!==null;){if(v.context===r){if(a.tag===1){v=Zt(-1,n&-n),v.tag=2;var E=a.updateQueue;if(E!==null){E=E.shared;var O=E.pending;O===null?v.next=v:(v.next=O.next,O.next=v),E.pending=v}}a.lanes|=n,v=a.alternate,v!==null&&(v.lanes|=n),Ku(a.return,n,t),h.lanes|=n;break}v=v.next}}else if(a.tag===10)f=a.type===t.type?null:a.child;else if(a.tag===18){if(f=a.return,f===null)throw Error(o(341));f.lanes|=n,h=f.alternate,h!==null&&(h.lanes|=n),Ku(f,n,t),f=a.sibling}else f=a.child;if(f!==null)f.return=a;else for(f=a;f!==null;){if(f===t){f=null;break}if(a=f.sibling,a!==null){a.return=f.return,f=a;break}f=f.return}a=f}ze(e,t,u.children,n),t=t.child}return t;case 9:return u=t.type,r=t.pendingProps.children,Sr(t,n),u=pt(u),r=r(u),t.flags|=1,ze(e,t,r,n),t.child;case 14:return r=t.type,u=Ot(r,t.pendingProps),u=Ot(r.type,u),Ed(e,t,r,u,n);case 15:return kd(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Ko(e,t),t.tag=1,Ke(r)?(e=!0,Ao(t)):e=!1,Sr(t,n),hd(t,r,u),ss(t,r,u,n),ds(null,t,r,!0,e,n);case 19:return Rd(e,t,n);case 22:return xd(e,t,n)}throw Error(o(156,t.tag))};function tp(e,t){return jc(e,t)}function kg(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function mt(e,t,n,r){return new kg(e,t,n,r)}function Ms(e){return e=e.prototype,!(!e||!e.isReactComponent)}function xg(e){if(typeof e=="function")return Ms(e)?1:0;if(e!=null){if(e=e.$$typeof,e===Lt)return 11;if(e===bt)return 14}return 2}function xn(e,t){var n=e.alternate;return n===null?(n=mt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function rl(e,t,n,r,u,a){var f=2;if(r=e,typeof e=="function")Ms(e)&&(f=1);else if(typeof e=="string")f=5;else e:switch(e){case Te:return Gn(n.children,u,a,t);case ct:f=8,u|=8;break;case ln:return e=mt(12,n,t,u|2),e.elementType=ln,e.lanes=a,e;case Ze:return e=mt(13,n,t,u),e.elementType=Ze,e.lanes=a,e;case St:return e=mt(19,n,t,u),e.elementType=St,e.lanes=a,e;case ge:return il(n,u,a,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Qt:f=10;break e;case bn:f=9;break e;case Lt:f=11;break e;case bt:f=14;break e;case We:f=16,r=null;break e}throw Error(o(130,e==null?e:typeof e,""))}return t=mt(f,n,t,u),t.elementType=e,t.type=r,t.lanes=a,t}function Gn(e,t,n,r){return e=mt(7,e,r,t),e.lanes=n,e}function il(e,t,n,r){return e=mt(22,e,r,t),e.elementType=ge,e.lanes=n,e.stateNode={isHidden:!1},e}function js(e,t,n){return e=mt(6,e,null,t),e.lanes=n,e}function Ls(e,t,n){return t=mt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Og(e,t,n,r,u){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=uu(0),this.expirationTimes=uu(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=uu(0),this.identifierPrefix=r,this.onRecoverableError=u,this.mutableSourceEagerHydrationData=null}function bs(e,t,n,r,u,a,f,h,v){return e=new Og(e,t,n,h,v),t===1?(t=1,a===!0&&(t|=8)):t=0,a=mt(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Gu(a),e}function Cg(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(l){console.error(l)}}return i(),hl.exports=dp(),hl.exports}var Xs;function pp(){if(Xs)return Ai;Xs=1;var i=Ys();return Ai.createRoot=i.createRoot,Ai.hydrateRoot=i.hydrateRoot,Ai}var hp=pp();const vp=Fs(hp);function q(i){for(var l=arguments.length,o=new Array(l>1?l-1:0),s=1;si.length)&&(l=i.length);for(var o=0,s=Array(l);o=i.length?{done:!0}:{done:!1,value:i[s++]}}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function An(){return An=Object.assign?Object.assign.bind():function(i){for(var l=1;ls&&(s=g.dependenciesState_)}for(o.length=c,i.newObserving_=null,d=l.length;d--;){var y=l[d];y.diffValue===0&&ka(y,i),y.diffValue=0}for(;c--;){var C=o[c];C.diffValue===1&&(C.diffValue=0,_h(C,i))}s!==Z.UP_TO_DATE_&&(i.dependenciesState_=s,i.onBecomeStale_())}function Cl(i){var l=i.observing_;i.observing_=[];for(var o=l.length;o--;)ka(l[o],i);i.dependenciesState_=Z.NOT_TRACKING_}function wa(i){var l=Mn();try{return i()}finally{Wt(l)}}function Mn(){var i=T.trackingDerivation;return T.trackingDerivation=null,i}function Wt(i){T.trackingDerivation=i}function Pl(i){var l=T.allowStateReads;return T.allowStateReads=i,l}function jr(i){T.allowStateReads=i}function Sa(i){if(i.dependenciesState_!==Z.UP_TO_DATE_){i.dependenciesState_=Z.UP_TO_DATE_;for(var l=i.observing_,o=l.length;o--;)l[o].lowestObserverState_=Z.UP_TO_DATE_}}var Wi=function(){this.version=6,this.UNCHANGED={},this.trackingDerivation=null,this.trackingContext=null,this.runId=0,this.mobxGuid=0,this.inBatch=0,this.pendingUnobservations=[],this.pendingReactions=[],this.isRunningReactions=!1,this.allowStateChanges=!1,this.allowStateReads=!0,this.enforceActions=!0,this.spyListeners=[],this.globalReactionErrorHandlers=[],this.computedRequiresReaction=!1,this.reactionRequiresObservable=!1,this.observableRequiresReaction=!1,this.disableErrorBoundaries=!1,this.suppressReactionErrors=!1,this.useProxies=!0,this.verifyProxies=!1,this.safeDescriptors=!0},Hi=!0,Ea=!1,T=(function(){var i=Ni();return i.__mobxInstanceCount>0&&!i.__mobxGlobals&&(Hi=!1),i.__mobxGlobals&&i.__mobxGlobals.version!==new Wi().version&&(Hi=!1),Hi?i.__mobxGlobals?(i.__mobxInstanceCount+=1,i.__mobxGlobals.UNCHANGED||(i.__mobxGlobals.UNCHANGED={}),i.__mobxGlobals):(i.__mobxInstanceCount=1,i.__mobxGlobals=new Wi):(setTimeout(function(){Ea||q(35)},1),new Wi)})();function yh(){if((T.pendingReactions.length||T.inBatch||T.isRunningReactions)&&q(36),Ea=!0,Hi){var i=Ni();--i.__mobxInstanceCount===0&&(i.__mobxGlobals=void 0),T=new Wi}}function _h(i,l){i.observers_.add(l),i.lowestObserverState_>l.dependenciesState_&&(i.lowestObserverState_=l.dependenciesState_)}function ka(i,l){i.observers_.delete(l),i.observers_.size===0&&xa(i)}function xa(i){i.isPendingUnobservation===!1&&(i.isPendingUnobservation=!0,T.pendingUnobservations.push(i))}function lt(){T.inBatch++}function ut(){if(--T.inBatch===0){Pa();for(var i=T.pendingUnobservations,l=0;l0&&xa(i),!1)}function Ca(i){i.lowestObserverState_!==Z.STALE_&&(i.lowestObserverState_=Z.STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.UP_TO_DATE_&&l.onBecomeStale_(),l.dependenciesState_=Z.STALE_}))}function wh(i){i.lowestObserverState_!==Z.STALE_&&(i.lowestObserverState_=Z.STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.POSSIBLY_STALE_?l.dependenciesState_=Z.STALE_:l.dependenciesState_===Z.UP_TO_DATE_&&(i.lowestObserverState_=Z.UP_TO_DATE_)}))}function Sh(i){i.lowestObserverState_===Z.UP_TO_DATE_&&(i.lowestObserverState_=Z.POSSIBLY_STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.UP_TO_DATE_&&(l.dependenciesState_=Z.POSSIBLY_STALE_,l.onBecomeStale_())}))}var Ht=(function(){function i(o,s,c,d){o===void 0&&(o="Reaction"),this.name_=void 0,this.onInvalidate_=void 0,this.errorHandler_=void 0,this.requiresObservable_=void 0,this.observing_=[],this.newObserving_=[],this.dependenciesState_=Z.NOT_TRACKING_,this.runId_=0,this.unboundDepsCount_=0,this.flags_=0,this.isTracing_=Ui.NONE,this.name_=o,this.onInvalidate_=s,this.errorHandler_=c,this.requiresObservable_=d}var l=i.prototype;return l.onBecomeStale_=function(){this.schedule_()},l.schedule_=function(){this.isScheduled||(this.isScheduled=!0,T.pendingReactions.push(this),Pa())},l.runReaction_=function(){if(!this.isDisposed){lt(),this.isScheduled=!1;var s=T.trackingContext;if(T.trackingContext=this,Ol(this)){this.isTrackPending=!0;try{this.onInvalidate_()}catch(c){this.reportExceptionInDerivation_(c)}}T.trackingContext=s,ut()}},l.track=function(s){if(!this.isDisposed){lt(),this.isRunning=!0;var c=T.trackingContext;T.trackingContext=this;var d=_a(this,s,void 0);T.trackingContext=c,this.isRunning=!1,this.isTrackPending=!1,this.isDisposed&&Cl(this),$i(d)&&this.reportExceptionInDerivation_(d.cause),ut()}},l.reportExceptionInDerivation_=function(s){var c=this;if(this.errorHandler_){this.errorHandler_(s,this);return}if(T.disableErrorBoundaries)throw s;var d="[mobx] uncaught error in '"+this+"'";T.suppressReactionErrors||console.error(d,s),T.globalReactionErrorHandlers.forEach(function(p){return p(s,c)})},l.dispose=function(){this.isDisposed||(this.isDisposed=!0,this.isRunning||(lt(),Cl(this),ut()))},l.getDisposer_=function(s){var c=this,d=function p(){c.dispose(),s==null||s.removeEventListener==null||s.removeEventListener("abort",p)};return s==null||s.addEventListener==null||s.addEventListener("abort",d),d[$]=this,d},l.toString=function(){return"Reaction["+this.name_+"]"},l.trace=function(s){},Jn(i,[{key:"isDisposed",get:function(){return Xe(this.flags_,i.isDisposedMask_)},set:function(s){this.flags_=qe(this.flags_,i.isDisposedMask_,s)}},{key:"isScheduled",get:function(){return Xe(this.flags_,i.isScheduledMask_)},set:function(s){this.flags_=qe(this.flags_,i.isScheduledMask_,s)}},{key:"isTrackPending",get:function(){return Xe(this.flags_,i.isTrackPendingMask_)},set:function(s){this.flags_=qe(this.flags_,i.isTrackPendingMask_,s)}},{key:"isRunning",get:function(){return Xe(this.flags_,i.isRunningMask_)},set:function(s){this.flags_=qe(this.flags_,i.isRunningMask_,s)}},{key:"diffValue",get:function(){return Xe(this.flags_,i.diffValueMask_)?1:0},set:function(s){this.flags_=qe(this.flags_,i.diffValueMask_,s===1)}}])})();Ht.isDisposedMask_=1,Ht.isScheduledMask_=2,Ht.isTrackPendingMask_=4,Ht.isRunningMask_=8,Ht.diffValueMask_=16;var Eh=100,Al=function(l){return l()};function Pa(){T.inBatch>0||T.isRunningReactions||Al(kh)}function kh(){T.isRunningReactions=!0;for(var i=T.pendingReactions,l=0;i.length>0;){++l===Eh&&(console.error("[mobx] cycle in reaction: "+i[0]),i.splice(0));for(var o=i.splice(0),s=0,c=o.length;s0&&(l.dependencies=Fh(i.observing_).map(ba)),l}function Fh(i){return Array.from(new Set(i))}var Uh=0;function Da(){this.message="FLOW_CANCELLED"}Da.prototype=Object.create(Error.prototype);var Rl=aa("flow"),Vh=aa("flow.bound",{bound:!0}),tr=Object.assign(function(l,o){if(Rr(o))return Rl.decorate_20223_(l,o);if(Cn(o))return Tr(l,o,Rl);var s=l,c=s.name||"",d=function(){var g=this,y=arguments,C=++Uh,P=er(c+" - runid: "+C+" - init",s).apply(g,y),R,L=void 0,Y=new Promise(function(le,H){var K=0;R=H;function Ve(re){L=void 0;var Se;try{Se=er(c+" - runid: "+C+" - yield "+K++,P.next).call(P,re)}catch(he){return H(he)}Ne(Se)}function $e(re){L=void 0;var Se;try{Se=er(c+" - runid: "+C+" - yield "+K++,P.throw).call(P,re)}catch(he){return H(he)}Ne(Se)}function Ne(re){if(ot(re?.then)){re.then(Ne,H);return}return re.done?le(re.value):(L=Promise.resolve(re.value),L.then(Ve,$e))}Ve(void 0)});return Y.cancel=er(c+" - runid: "+C+" - cancel",function(){try{L&&za(L);var le=P.return(void 0),H=Promise.resolve(le.value);H.then(Yn,Yn),za(H),R(new Da)}catch(K){R(K)}}),Y};return d.isMobXFlow=!0,d},Rl);tr.bound=Rt(Vh);function za(i){ot(i.cancel)&&i.cancel()}function zr(i){return i?.isMobXFlow===!0}function $h(i,l){return i?qi(i)||!!i[$]||_l(i)||Ki(i)||Fi(i):!1}function Ia(i){return $h(i)}function Ml(i,l,o,s){return ot(o)?Hh(i,l,o,s):Wh(i,l,o)}function Wh(i,l,o){return Ji(i).observe_(l,o)}function Hh(i,l,o,s){return Ji(i,l).observe_(o,s)}function Kt(i,l){l===void 0&&(l=void 0),lt();try{return i.apply(l)}finally{ut()}}function nr(i){return i[$]}var Kh={has:function(l,o){return nr(l).has_(o)},get:function(l,o){return nr(l).get_(o)},set:function(l,o,s){var c;return Cn(o)?(c=nr(l).set_(o,s,!0))!=null?c:!0:!1},deleteProperty:function(l,o){var s;return Cn(o)?(s=nr(l).delete_(o,!0))!=null?s:!0:!1},defineProperty:function(l,o,s){var c;return(c=nr(l).defineProperty_(o,s))!=null?c:!0},ownKeys:function(l){return nr(l).ownKeys_()},preventExtensions:function(l){q(13)}};function Qh(i,l){var o,s;return Zs(),i=Ln(i,l),(s=(o=i[$]).proxy_)!=null?s:o.proxy_=new Proxy(i,Kh)}function st(i){return i.interceptors_!==void 0&&i.interceptors_.length>0}function Ir(i,l){var o=i.interceptors_||(i.interceptors_=[]);return o.push(l),ea(function(){var s=o.indexOf(l);s!==-1&&o.splice(s,1)})}function at(i,l){var o=Mn();try{for(var s=[].concat(i.interceptors_||[]),c=0,d=s.length;c0}function Br(i,l){var o=i.changeListeners_||(i.changeListeners_=[]);return o.push(l),ea(function(){var s=o.indexOf(l);s!==-1&&o.splice(s,1)})}function _t(i,l){var o=Mn(),s=i.changeListeners_;if(s){s=s.slice();for(var c=0,d=s.length;c0?s.map(this.dehancer):s},l.intercept_=function(s){return Ir(this,s)},l.observe_=function(s,c){return c===void 0&&(c=!1),c&&s({observableKind:"array",object:this.proxy_,debugObjectName:this.atom_.name_,type:"splice",index:0,added:this.values_.slice(),addedCount:this.values_.length,removed:[],removedCount:0}),Br(this,s)},l.getArrayLength_=function(){return this.atom_.reportObserved(),this.values_.length},l.setArrayLength_=function(s){(typeof s!="number"||isNaN(s)||s<0)&&q("Out of range: "+s);var c=this.values_.length;if(s!==c)if(s>c){for(var d=new Array(s-c),p=0;p0&&Ja(s+c+1)},l.spliceWithArray_=function(s,c,d){var p=this;this.atom_;var g=this.values_.length;if(s===void 0?s=0:s>g?s=g:s<0&&(s=Math.max(0,g+s)),arguments.length===1?c=g-s:c==null?c=0:c=Math.max(0,Math.min(c,g-s)),d===void 0&&(d=gl),st(this)){var y=at(this,{object:this.proxy_,type:Ba,index:s,removedCount:c,added:d});if(!y)return gl;c=y.removedCount,d=y.added}if(d=d.length===0?d:d.map(function(R){return p.enhancer_(R,void 0)}),this.legacyMode_){var C=d.length-c;this.updateArrayLength_(g,C)}var P=this.spliceItemsIntoValues_(s,c,d);return(c!==0||d.length!==0)&&this.notifyArraySplice_(s,d,P),this.dehanceValues_(P)},l.spliceItemsIntoValues_=function(s,c,d){if(d.length=this.values_.length){console.warn("[mobx] Out of bounds read: "+s);return}return this.atom_.reportObserved(),this.dehanceValue_(this.values_[s])},l.set_=function(s,c){var d=this.values_;if(this.legacyMode_&&s>d.length&&q(17,s,d.length),s2?s-2:0),d=2;d-1?(this.splice(s,1),!0):!1}};ue("at",Je),ue("concat",Je),ue("flat",Je),ue("includes",Je),ue("indexOf",Je),ue("join",Je),ue("lastIndexOf",Je),ue("slice",Je),ue("toString",Je),ue("toLocaleString",Je),ue("toSorted",Je),ue("toSpliced",Je),ue("with",Je),ue("every",wt),ue("filter",wt),ue("find",wt),ue("findIndex",wt),ue("findLast",wt),ue("findLastIndex",wt),ue("flatMap",wt),ue("forEach",wt),ue("map",wt),ue("some",wt),ue("toReversed",wt),ue("reduce",Fa),ue("reduceRight",Fa);function ue(i,l){typeof Array.prototype[i]=="function"&&(Gi[i]=l(i))}function Je(i){return function(){var l=this[$];l.atom_.reportObserved();var o=l.dehanceValues_(l.values_);return o[i].apply(o,arguments)}}function wt(i){return function(l,o){var s=this,c=this[$];c.atom_.reportObserved();var d=c.dehanceValues_(c.values_);return d[i](function(p,g){return l.call(o,p,g,s)})}}function Fa(i){return function(){var l=this,o=this[$];o.atom_.reportObserved();var s=o.dehanceValues_(o.values_),c=arguments[0];return arguments[0]=function(d,p,g){return c(d,p,g,l)},s[i].apply(s,arguments)}}var Zh=Pn("ObservableArrayAdministration",Ll);function Yi(i){return Mi(i)&&Zh(i[$])}var ev={},rn="add",Xi="delete",Ua=(function(){function i(o,s,c){var d=this;s===void 0&&(s=Nn),c===void 0&&(c="ObservableMap"),this.enhancer_=void 0,this.name_=void 0,this[$]=ev,this.data_=void 0,this.hasMap_=void 0,this.keysAtom_=void 0,this.interceptors_=void 0,this.changeListeners_=void 0,this.dehancer=void 0,this.enhancer_=s,this.name_=c,ot(Map)||q(18),on(function(){d.keysAtom_=ua("ObservableMap.keys()"),d.data_=new Map,d.hasMap_=new Map,o&&d.merge(o)})}var l=i.prototype;return l.has_=function(s){return this.data_.has(s)},l.has=function(s){var c=this;if(!T.trackingDerivation)return this.has_(s);var d=this.hasMap_.get(s);if(!d){var p=d=new Rn(this.has_(s),Li,"ObservableMap.key?",!1);this.hasMap_.set(s,p),Ma(p,function(){return c.hasMap_.delete(s)})}return d.get()},l.set=function(s,c){var d=this.has_(s);if(st(this)){var p=at(this,{type:d?Mt:rn,object:this,newValue:c,name:s});if(!p)return this;c=p.newValue}return d?this.updateValue_(s,c):this.addValue_(s,c),this},l.delete=function(s){var c=this;if(this.keysAtom_,st(this)){var d=at(this,{type:Xi,object:this,name:s});if(!d)return!1}if(this.has_(s)){var p=Lr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:Xi,object:this,oldValue:this.data_.get(s).value_,name:s}:null;return Kt(function(){var C;c.keysAtom_.reportChanged(),(C=c.hasMap_.get(s))==null||C.setNewValue_(!1);var P=c.data_.get(s);P.setNewValue_(void 0),c.data_.delete(s)}),g&&_t(this,y),!0}return!1},l.updateValue_=function(s,c){var d=this.data_.get(s);if(c=d.prepareNewValue_(c),c!==T.UNCHANGED){var p=Lr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:Mt,object:this,oldValue:d.value_,name:s,newValue:c}:null;d.setNewValue_(c),g&&_t(this,y)}},l.addValue_=function(s,c){var d=this;this.keysAtom_,Kt(function(){var C,P=new Rn(c,d.enhancer_,"ObservableMap.key",!1);d.data_.set(s,P),c=P.value_,(C=d.hasMap_.get(s))==null||C.setNewValue_(!0),d.keysAtom_.reportChanged()});var p=Lr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:rn,object:this,name:s,newValue:c}:null;g&&_t(this,y)},l.get=function(s){return this.has(s)?this.dehanceValue_(this.data_.get(s).get()):this.dehanceValue_(void 0)},l.dehanceValue_=function(s){return this.dehancer!==void 0?this.dehancer(s):s},l.keys=function(){return this.keysAtom_.reportObserved(),this.data_.keys()},l.values=function(){var s=this,c=this.keys();return Va({next:function(){var p=c.next(),g=p.done,y=p.value;return{done:g,value:g?void 0:s.get(y)}}})},l.entries=function(){var s=this,c=this.keys();return Va({next:function(){var p=c.next(),g=p.done,y=p.value;return{done:g,value:g?void 0:[y,s.get(y)]}}})},l[Symbol.iterator]=function(){return this.entries()},l.forEach=function(s,c){for(var d=Zn(this),p;!(p=d()).done;){var g=p.value,y=g[0],C=g[1];s.call(c,C,y,this)}},l.merge=function(s){var c=this;return jn(s)&&(s=new Map(s)),Kt(function(){Ut(s)?wp(s).forEach(function(d){return c.set(d,s[d])}):Array.isArray(s)?s.forEach(function(d){var p=d[0],g=d[1];return c.set(p,g)}):Xn(s)?(_p(s)||q(19,s),s.forEach(function(d,p){return c.set(p,d)})):s!=null&&q(20,s)}),this},l.clear=function(){var s=this;Kt(function(){wa(function(){for(var c=Zn(s.keys()),d;!(d=c()).done;){var p=d.value;s.delete(p)}})})},l.replace=function(s){var c=this;return Kt(function(){for(var d=tv(s),p=new Map,g=!1,y=Zn(c.data_.keys()),C;!(C=y()).done;){var P=C.value;if(!d.has(P)){var R=c.delete(P);if(R)g=!0;else{var L=c.data_.get(P);p.set(P,L)}}}for(var Y=Zn(d.entries()),le;!(le=Y()).done;){var H=le.value,K=H[0],Ve=H[1],$e=c.data_.has(K);if(c.set(K,Ve),c.data_.has(K)){var Ne=c.data_.get(K);p.set(K,Ne),$e||(g=!0)}}if(!g)if(c.data_.size!==p.size)c.keysAtom_.reportChanged();else for(var re=c.data_.keys(),Se=p.keys(),he=re.next(),Te=Se.next();!he.done;){if(he.value!==Te.value){c.keysAtom_.reportChanged();break}he=re.next(),Te=Se.next()}c.data_=p}),this},l.toString=function(){return"[object ObservableMap]"},l.toJSON=function(){return Array.from(this)},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},Jn(i,[{key:"size",get:function(){return this.keysAtom_.reportObserved(),this.data_.size}},{key:Symbol.toStringTag,get:function(){return"Map"}}])})(),jn=Pn("ObservableMap",Ua);function Va(i){return i[Symbol.toStringTag]="MapIterator",Bl(i)}function tv(i){if(Xn(i)||jn(i))return i;if(Array.isArray(i))return new Map(i);if(Ut(i)){var l=new Map;for(var o in i)l.set(o,i[o]);return l}else return q(21,i)}var nv={},$a=(function(){function i(o,s,c){var d=this;s===void 0&&(s=Nn),c===void 0&&(c="ObservableSet"),this.name_=void 0,this[$]=nv,this.data_=new Set,this.atom_=void 0,this.changeListeners_=void 0,this.interceptors_=void 0,this.dehancer=void 0,this.enhancer_=void 0,this.name_=c,ot(Set)||q(22),this.enhancer_=function(p,g){return s(p,g,c)},on(function(){d.atom_=ua(d.name_),o&&d.replace(o)})}var l=i.prototype;return l.dehanceValue_=function(s){return this.dehancer!==void 0?this.dehancer(s):s},l.clear=function(){var s=this;Kt(function(){wa(function(){for(var c=Zn(s.data_.values()),d;!(d=c()).done;){var p=d.value;s.delete(p)}})})},l.forEach=function(s,c){for(var d=Zn(this),p;!(p=d()).done;){var g=p.value;s.call(c,g,g,this)}},l.add=function(s){var c=this;if(this.atom_,st(this)){var d=at(this,{type:rn,object:this,newValue:s});if(!d)return this;s=d.newValue}if(!this.has(s)){Kt(function(){c.data_.add(c.enhancer_(s,void 0)),c.atom_.reportChanged()});var p=!1,g=yt(this),y=g||p?{observableKind:"set",debugObjectName:this.name_,type:rn,object:this,newValue:s}:null;g&&_t(this,y)}return this},l.delete=function(s){var c=this;if(st(this)){var d=at(this,{type:Xi,object:this,oldValue:s});if(!d)return!1}if(this.has(s)){var p=!1,g=yt(this),y=g||p?{observableKind:"set",debugObjectName:this.name_,type:Xi,object:this,oldValue:s}:null;return Kt(function(){c.atom_.reportChanged(),c.data_.delete(s)}),g&&_t(this,y),!0}return!1},l.has=function(s){return this.atom_.reportObserved(),this.data_.has(this.dehanceValue_(s))},l.entries=function(){var s=this.values();return Wa({next:function(){var d=s.next(),p=d.value,g=d.done;return g?{value:void 0,done:g}:{value:[p,p],done:g}}})},l.keys=function(){return this.values()},l.values=function(){this.atom_.reportObserved();var s=this,c=this.data_.values();return Wa({next:function(){var p=c.next(),g=p.value,y=p.done;return y?{value:void 0,done:y}:{value:s.dehanceValue_(g),done:y}}})},l.intersection=function(s){if(Vt(s)&&!jt(s))return s.intersection(this);var c=new Set(this);return c.intersection(s)},l.union=function(s){if(Vt(s)&&!jt(s))return s.union(this);var c=new Set(this);return c.union(s)},l.difference=function(s){return new Set(this).difference(s)},l.symmetricDifference=function(s){if(Vt(s)&&!jt(s))return s.symmetricDifference(this);var c=new Set(this);return c.symmetricDifference(s)},l.isSubsetOf=function(s){return new Set(this).isSubsetOf(s)},l.isSupersetOf=function(s){return new Set(this).isSupersetOf(s)},l.isDisjointFrom=function(s){if(Vt(s)&&!jt(s))return s.isDisjointFrom(this);var c=new Set(this);return c.isDisjointFrom(s)},l.replace=function(s){var c=this;return jt(s)&&(s=new Set(s)),Kt(function(){Array.isArray(s)?(c.clear(),s.forEach(function(d){return c.add(d)})):Vt(s)?(c.clear(),s.forEach(function(d){return c.add(d)})):s!=null&&q("Cannot initialize set from "+s)}),this},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},l.toJSON=function(){return Array.from(this)},l.toString=function(){return"[object ObservableSet]"},l[Symbol.iterator]=function(){return this.values()},Jn(i,[{key:"size",get:function(){return this.atom_.reportObserved(),this.data_.size}},{key:Symbol.toStringTag,get:function(){return"Set"}}])})(),jt=Pn("ObservableSet",$a);function Wa(i){return i[Symbol.toStringTag]="SetIterator",Bl(i)}var Ha=Object.create(null),Ka="remove",Qa=(function(){function i(o,s,c,d){s===void 0&&(s=new Map),d===void 0&&(d=Xp),this.target_=void 0,this.values_=void 0,this.name_=void 0,this.defaultAnnotation_=void 0,this.keysAtom_=void 0,this.changeListeners_=void 0,this.interceptors_=void 0,this.proxy_=void 0,this.isPlainObject_=void 0,this.appliedAnnotations_=void 0,this.pendingKeys_=void 0,this.target_=o,this.values_=s,this.name_=c,this.defaultAnnotation_=d,this.keysAtom_=new nn("ObservableObject.keys"),this.isPlainObject_=Ut(this.target_)}var l=i.prototype;return l.getObservablePropValue_=function(s){return this.values_.get(s).get()},l.setObservablePropValue_=function(s,c){var d=this.values_.get(s);if(d instanceof gt)return d.set(c),!0;if(st(this)){var p=at(this,{type:Mt,object:this.proxy_||this.target_,name:s,newValue:c});if(!p)return null;c=p.newValue}if(c=d.prepareNewValue_(c),c!==T.UNCHANGED){var g=yt(this),y=!1,C=g||y?{type:Mt,observableKind:"object",debugObjectName:this.name_,object:this.proxy_||this.target_,oldValue:d.value_,name:s,newValue:c}:null;d.setNewValue_(c),g&&_t(this,C)}return!0},l.get_=function(s){return T.trackingDerivation&&!$t(this.target_,s)&&this.has_(s),this.target_[s]},l.set_=function(s,c,d){return d===void 0&&(d=!1),$t(this.target_,s)?this.values_.has(s)?this.setObservablePropValue_(s,c):d?Reflect.set(this.target_,s,c):(this.target_[s]=c,!0):this.extend_(s,{value:c,enumerable:!0,writable:!0,configurable:!0},this.defaultAnnotation_,d)},l.has_=function(s){if(!T.trackingDerivation)return s in this.target_;this.pendingKeys_||(this.pendingKeys_=new Map);var c=this.pendingKeys_.get(s);return c||(c=new Rn(s in this.target_,Li,"ObservableObject.key?",!1),this.pendingKeys_.set(s,c)),c.get()},l.make_=function(s,c){if(c===!0&&(c=this.defaultAnnotation_),c!==!1){if(!(s in this.target_)){var d;if((d=this.target_[Tt])!=null&&d[s])return;q(1,c.annotationType_,this.name_+"."+s.toString())}for(var p=this.target_;p&&p!==Ri;){var g=Ti(p,s);if(g){var y=c.make_(this,s,g,p);if(y===0)return;if(y===1)break}p=Object.getPrototypeOf(p)}Ya(this,c,s)}},l.extend_=function(s,c,d,p){if(p===void 0&&(p=!1),d===!0&&(d=this.defaultAnnotation_),d===!1)return this.defineProperty_(s,c,p);var g=d.extend_(this,s,c,p);return g&&Ya(this,d,s),g},l.defineProperty_=function(s,c,d){d===void 0&&(d=!1),this.keysAtom_;try{lt();var p=this.delete_(s);if(!p)return p;if(st(this)){var g=at(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:c.value});if(!g)return null;var y=g.newValue;c.value!==y&&(c=An({},c,{value:y}))}if(d){if(!Reflect.defineProperty(this.target_,s,c))return!1}else Nt(this.target_,s,c);this.notifyPropertyAddition_(s,c.value)}finally{ut()}return!0},l.defineObservableProperty_=function(s,c,d,p){p===void 0&&(p=!1),this.keysAtom_;try{lt();var g=this.delete_(s);if(!g)return g;if(st(this)){var y=at(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:c});if(!y)return null;c=y.newValue}var C=Ga(s),P={configurable:T.safeDescriptors?this.isPlainObject_:!0,enumerable:!0,get:C.get,set:C.set};if(p){if(!Reflect.defineProperty(this.target_,s,P))return!1}else Nt(this.target_,s,P);var R=new Rn(c,d,"ObservableObject.key",!1);this.values_.set(s,R),this.notifyPropertyAddition_(s,R.value_)}finally{ut()}return!0},l.defineComputedProperty_=function(s,c,d){d===void 0&&(d=!1),this.keysAtom_;try{lt();var p=this.delete_(s);if(!p)return p;if(st(this)){var g=at(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:void 0});if(!g)return null}c.name||(c.name="ObservableObject.key"),c.context=this.proxy_||this.target_;var y=Ga(s),C={configurable:T.safeDescriptors?this.isPlainObject_:!0,enumerable:!1,get:y.get,set:y.set};if(d){if(!Reflect.defineProperty(this.target_,s,C))return!1}else Nt(this.target_,s,C);this.values_.set(s,new gt(c)),this.notifyPropertyAddition_(s,void 0)}finally{ut()}return!0},l.delete_=function(s,c){if(c===void 0&&(c=!1),this.keysAtom_,!$t(this.target_,s))return!0;if(st(this)){var d=at(this,{object:this.proxy_||this.target_,name:s,type:Ka});if(!d)return null}try{var p;lt();var g=yt(this),y=!1,C=this.values_.get(s),P=void 0;if(!C&&(g||y)){var R;P=(R=Ti(this.target_,s))==null?void 0:R.value}if(c){if(!Reflect.deleteProperty(this.target_,s))return!1}else delete this.target_[s];if(C&&(this.values_.delete(s),C instanceof Rn&&(P=C.value_),Ca(C)),this.keysAtom_.reportChanged(),(p=this.pendingKeys_)==null||(p=p.get(s))==null||p.set(s in this.target_),g||y){var L={type:Ka,observableKind:"object",object:this.proxy_||this.target_,debugObjectName:this.name_,oldValue:P,name:s};g&&_t(this,L)}}finally{ut()}return!0},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},l.notifyPropertyAddition_=function(s,c){var d,p=yt(this),g=!1;if(p||g){var y=p||g?{type:rn,observableKind:"object",debugObjectName:this.name_,object:this.proxy_||this.target_,name:s,newValue:c}:null;p&&_t(this,y)}(d=this.pendingKeys_)==null||(d=d.get(s))==null||d.set(!0),this.keysAtom_.reportChanged()},l.ownKeys_=function(){return this.keysAtom_.reportObserved(),qn(this.target_)},l.keys_=function(){return this.keysAtom_.reportObserved(),Object.keys(this.target_)},i})();function Ln(i,l){var o;if($t(i,$))return i;var s=(o=l?.name)!=null?o:"ObservableObject",c=new Qa(i,new Map,String(s),uh(l));return Nr(i,$,c),i}var rv=Pn("ObservableObjectAdministration",Qa);function Ga(i){return Ha[i]||(Ha[i]={get:function(){return this[$].getObservablePropValue_(i)},set:function(o){return this[$].setObservablePropValue_(i,o)}})}function qi(i){return Mi(i)?rv(i[$]):!1}function Ya(i,l,o){var s;(s=i.target_[Tt])==null||delete s[o]}var iv=qa(0),ov=(function(){var i=!1,l={};return Object.defineProperty(l,"0",{set:function(){i=!0}}),Object.create(l)[0]=1,i===!1})(),bl=0,Xa=function(){};function lv(i,l){Object.setPrototypeOf?Object.setPrototypeOf(i.prototype,l):i.prototype.__proto__!==void 0?i.prototype.__proto__=l:i.prototype=l}lv(Xa,Array.prototype);var Dl=(function(i){function l(s,c,d,p){var g;return d===void 0&&(d="ObservableArray"),p===void 0&&(p=!1),g=i.call(this)||this,on(function(){var y=new Ll(d,c,p,!0);y.proxy_=g,na(g,$,y),s&&s.length&&g.spliceWithArray(0,0,s),ov&&Object.defineProperty(g,"0",iv)}),g}la(l,i);var o=l.prototype;return o.concat=function(){this[$].atom_.reportObserved();for(var c=arguments.length,d=new Array(c),p=0;pbl){for(var l=bl;l=0&&o++;break}i=tc(i),l=tc(l);var g=p==="[object Array]";if(!g){if(typeof i!="object"||typeof l!="object")return!1;var y=i.constructor,C=l.constructor;if(y!==C&&!(ot(y)&&y instanceof y&&ot(C)&&C instanceof C)&&"constructor"in i&&"constructor"in l)return!1}if(o===0)return!1;o<0&&(o=-1),s=s||[],c=c||[];for(var P=s.length;P--;)if(s[P]===i)return c[P]===l;if(s.push(i),c.push(l),g){if(P=i.length,P!==l.length)return!1;for(;P--;)if(!Il(i[P],l[P],o-1,s,c))return!1}else{var R=Object.keys(i),L=R.length;if(Object.keys(l).length!==L)return!1;for(var Y=0;Y"u"&&q("MobX requires global '"+i+"' to be available or polyfilled")}),typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__=="object"&&__MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobx({spy:Oh,extras:{getDebugName:zl},$mobx:$});function rc(){return{bpm:180,keyNote:0,instruments:[],flavor:"Salsa"}}class fv{constructor(){this.NS_BEAT_MACHINE="http://www.salsabeatmachine.org/xns/bm",this.NS_INSTRUMENTS="http://www.salsabeatmachine.org/xns/instruments"}loadMachine(l){const o=l.getElementsByTagNameNS(this.NS_BEAT_MACHINE,"Machine")[0],s=rc(),c={bpm:d=>s.bpm=parseInt(d.textContent,10),keyNote:d=>s.keyNote=parseInt(d.textContent,10),flavor:d=>s.flavor=d.textContent,instrumentList:d=>s.instruments=this.processInstrumentList(d)};return this.childElements(o).forEach(d=>{const p=c[d.localName];p&&d.namespaceURI===this.NS_BEAT_MACHINE&&p(d)}),s}loadInstrument(l){const o=this.childElements(l).filter(p=>p.namespaceURI===this.NS_INSTRUMENTS);function s(p,g){const y=o.filter(C=>C.localName===p)[0];return y&&y.textContent?y.textContent:g}const c={id:(l.localName||"").toLowerCase(),title:l.getAttribute("title")||"",enabled:s("enabled","false")==="true",activeProgram:parseInt(s("activeProgram","0"),10),programs:[],respectsClave:s("respectsClave","false")==="true",keyedInstrument:s("keyedInstrument","false")==="true",pitchOffset:parseInt(s("pitchOffset","0"),10),playBothHands:s("playBothHands","false")==="true",leftHandPitchOffset:parseInt(s("leftHandPitchOffset","0"),10),volume:parseFloat(s("volume","1.0"))},d=o.filter(p=>p.localName==="programs")[0];return d&&(c.programs=this.childElements(d).filter(p=>p.namespaceURI===this.NS_BEAT_MACHINE&&p.localName==="Program").map(p=>this.loadProgram(p))),c}loadProgram(l){return{title:l.getAttribute("title")||"",length:parseInt(l.getAttribute("length"),10)||0,notes:this.childElements(l).filter(o=>o.namespaceURI===this.NS_BEAT_MACHINE&&o.localName==="Note").map(o=>({index:parseInt(o.getAttribute("index"),10)||0,pitch:parseInt(o.getAttribute("pitch"),10)||0,velocity:parseFloat(o.getAttribute("velocity"))||void 0}))}}processInstrumentList(l){return this.childElements(l).filter(o=>o.namespaceURI===this.NS_INSTRUMENTS).map(o=>this.loadInstrument(o))}childElements(l){return Array.from(l.childNodes).filter(o=>o.nodeType===o.ELEMENT_NODE)}}let Zi="";function ic(i){Zi=i.endsWith("/")?i.slice(0,-1):i}function dv(){return Zi}async function pv(i){let l=i.endsWith(".xml")?`/assets/machines/${i}`:`/assets/machines/${i}.xml`;Zi&&(l=`${Zi}${l}`);const o=await fetch(l);if(!o.ok)throw new Error(`Failed to load machine from ${l}: ${o.statusText}`);const s=await o.text(),c=new DOMParser().parseFromString(s,"text/xml");return new fv().loadMachine(c)}if(!Fe.useState)throw new Error("mobx-react-lite requires React with Hooks support");if(!Gh)throw new Error("mobx-react-lite@3 requires mobx at least version 6 to be available");var hv=Ys();function vv(i){i()}function mv(i){i||(i=vv),Ih({reactionScheduler:i})}function gv(i){return Bh(i)}var yv=1e4,_v=1e4,wv=(function(){function i(l){var o=this;Object.defineProperty(this,"finalize",{enumerable:!0,configurable:!0,writable:!0,value:l}),Object.defineProperty(this,"registrations",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"sweepTimeout",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"sweep",{enumerable:!0,configurable:!0,writable:!0,value:function(s){s===void 0&&(s=yv),clearTimeout(o.sweepTimeout),o.sweepTimeout=void 0;var c=Date.now();o.registrations.forEach(function(d,p){c-d.registeredAt>=s&&(o.finalize(d.value),o.registrations.delete(p))}),o.registrations.size>0&&o.scheduleSweep()}}),Object.defineProperty(this,"finalizeAllImmediately",{enumerable:!0,configurable:!0,writable:!0,value:function(){o.sweep(0)}})}return Object.defineProperty(i.prototype,"register",{enumerable:!1,configurable:!0,writable:!0,value:function(l,o,s){this.registrations.set(s,{value:o,registeredAt:Date.now()}),this.scheduleSweep()}}),Object.defineProperty(i.prototype,"unregister",{enumerable:!1,configurable:!0,writable:!0,value:function(l){this.registrations.delete(l)}}),Object.defineProperty(i.prototype,"scheduleSweep",{enumerable:!1,configurable:!0,writable:!0,value:function(){this.sweepTimeout===void 0&&(this.sweepTimeout=setTimeout(this.sweep,_v))}}),i})(),Sv=typeof FinalizationRegistry<"u"?FinalizationRegistry:wv,Fl=new Sv(function(i){var l;(l=i.reaction)===null||l===void 0||l.dispose(),i.reaction=null}),Ul={exports:{}},Vl={};var oc;function Ev(){if(oc)return Vl;oc=1;var i=Pi();function l(R,L){return R===L&&(R!==0||1/R===1/L)||R!==R&&L!==L}var o=typeof Object.is=="function"?Object.is:l,s=i.useState,c=i.useEffect,d=i.useLayoutEffect,p=i.useDebugValue;function g(R,L){var Y=L(),le=s({inst:{value:Y,getSnapshot:L}}),H=le[0].inst,K=le[1];return d(function(){H.value=Y,H.getSnapshot=L,y(H)&&K({inst:H})},[R,Y,L]),c(function(){return y(H)&&K({inst:H}),R(function(){y(H)&&K({inst:H})})},[R]),p(Y),Y}function y(R){var L=R.getSnapshot;R=R.value;try{var Y=L();return!o(R,Y)}catch{return!0}}function C(R,L){return L()}var P=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?C:g;return Vl.useSyncExternalStore=i.useSyncExternalStore!==void 0?i.useSyncExternalStore:P,Vl}var lc;function kv(){return lc||(lc=1,Ul.exports=Ev()),Ul.exports}var xv=kv();function uc(i){i.reaction=new Ht("observer".concat(i.name),function(){var l;i.stateVersion=Symbol(),(l=i.onStoreChange)===null||l===void 0||l.call(i)})}function Ov(i,l){l===void 0&&(l="observed");var o=pl.useRef(null);if(!o.current){var s={reaction:null,onStoreChange:null,stateVersion:Symbol(),name:l,subscribe:function(g){return Fl.unregister(s),s.onStoreChange=g,s.reaction||(uc(s),s.stateVersion=Symbol()),function(){var y;s.onStoreChange=null,(y=s.reaction)===null||y===void 0||y.dispose(),s.reaction=null}},getSnapshot:function(){return s.stateVersion}};o.current=s}var c=o.current;c.reaction||(uc(c),Fl.register(o,c,c)),pl.useDebugValue(c.reaction,gv),xv.useSyncExternalStore(c.subscribe,c.getSnapshot,c.getSnapshot);var d,p;if(c.reaction.track(function(){try{d=i()}catch(g){p=g}}),p)throw p;return d}var $l,Wl,sc=typeof Symbol=="function"&&Symbol.for,Cv=(Wl=($l=Object.getOwnPropertyDescriptor(function(){},"name"))===null||$l===void 0?void 0:$l.configurable)!==null&&Wl!==void 0?Wl:!1,ac=sc?Symbol.for("react.forward_ref"):typeof Fe.forwardRef=="function"&&Fe.forwardRef(function(i){return null}).$$typeof,cc=sc?Symbol.for("react.memo"):typeof Fe.memo=="function"&&Fe.memo(function(i){return null}).$$typeof;function Pv(i,l){var o;if(cc&&i.$$typeof===cc)throw new Error("[mobx-react-lite] You are trying to use `observer` on a function component wrapped in either another `observer` or `React.memo`. The observer already applies 'React.memo' for you.");var s=(o=void 0)!==null&&o!==void 0?o:!1,c=i,d=i.displayName||i.name;if(ac&&i.$$typeof===ac&&(s=!0,c=i.render,typeof c!="function"))throw new Error("[mobx-react-lite] `render` property of ForwardRef was not a function");var p=function(g,y){return Ov(function(){return c(g,y)},d)};return p.displayName=i.displayName,Cv&&Object.defineProperty(p,"name",{value:i.name,writable:!0,configurable:!0}),i.contextTypes&&(p.contextTypes=i.contextTypes),s&&(p=Fe.forwardRef(p)),p=Fe.memo(p),Nv(i,p),p}var Av={$$typeof:!0,render:!0,compare:!0,type:!0,displayName:!0};function Nv(i,l){Object.keys(i).forEach(function(o){Av[o]||Object.defineProperty(l,o,Object.getOwnPropertyDescriptor(i,o))})}var Hl;mv(hv.unstable_batchedUpdates),Hl=Fl.finalizeAllImmediately;class Tv{constructor(l,o){this.context=l,this.instrument=o,this.gainMap={},this.scheduledSamples=new Map,this.gainNode=l.createGain(),this.gainNode.connect(this.context.destination),this.disposer=Ra(()=>{this.gainNode.gain.value=this.instrument.enabled?this.instrument.volume:0})}reset(l=!1){for(const[o,s]of Array.from(this.scheduledSamples.entries()))(l||s>=this.context.currentTime)&&o.stop();this.scheduledSamples.clear()}createNoteDestination(l){if(typeof l!="number"||l===1)return this.gainNode;if(!this.gainMap[l]){const o=this.context.createGain();o.connect(this.gainNode),o.gain.value=l,this.gainMap[l]=o}return this.gainMap[l]}registerSample(l,o){l.addEventListener("ended",()=>{this.scheduledSamples.delete(l)}),this.scheduledSamples.set(l,o)}dispose(){this.disposer(),this.reset(!0)}}class Rv{constructor(l){this.mixer=l,this.nextSampleIndex=0,this.animationFrameRequest=null,this.audioTimeDelta=0,this.machineDisposers=[],this.instrumentPlayers=new Map,this.mixerNotReadyLogged=!1,this.interval=null,this._machine=rc(),this.beat=0,this.stopAllInstruments=(o=!1)=>{for(const s of Array.from(this.instrumentPlayers.values()))s.reset(o)},this.scheduleBuffers=()=>{const o=this.mixer.context;if(o&&this.mixer.ready){this.mixerNotReadyLogged=!1;const s=this.beatTime/2,c=this.getBeatIndex();for(;this.nextSampleIndex-c*2<64;){const d=this.nextSampleIndex;this.machine.instruments.forEach(p=>{const g=this.getInstrumentPlayer(o,p);this.instrumentNotes(p,d).forEach(y=>{this.mixer.play(y.sampleName,g,d*s-this.audioTimeDelta,y.velocity)})}),this.nextSampleIndex++}}else this.mixerNotReadyLogged||(console.log("Mixer not ready yet - context:",!!o,"ready:",this.mixer.ready),this.mixerNotReadyLogged=!0);this.interval=window.setTimeout(()=>this.scheduleBuffers(),1e3)},Yh(this)}async init(){await this.mixer.init(),console.log("BeatEngine initialized - AudioBackend state:",{ready:this.mixer.ready,hasContext:!!this.mixer.context})}get machine(){return this._machine}set machine(l){if(l!==this._machine){if(this._machine=l,this.machineDisposers.forEach(o=>o()),this.machineDisposers=[],this.instrumentPlayers.forEach(o=>o.dispose()),this.instrumentPlayers.clear(),!this.machine)return;this.playing&&(this.stop(),this.play()),this.machineDisposers.push(Ml(this.machine,"bpm",({oldValue:o,newValue:s})=>{if(this.playing){this.interval&&clearTimeout(this.interval);const c=this.mixer.getCurrentTime();if(o==null||s==null||o===0||s===0){console.warn("⚠️ Ugyldige verdier for BPM endring: oldValue =",o,", newValue =",s);return}this.audioTimeDelta=(c+(this.audioTimeDelta||0))*(o/s)-c,isNaN(this.audioTimeDelta)&&(console.warn("⚠️ audioTimeDelta ble NaN — setter den til 0."),this.audioTimeDelta=0),this.nextSampleIndex=Math.ceil(this.getBeatIndex()*2),this.stopAllInstruments(),this.scheduleBuffers()}}),Ml(this.machine,"keyNote",()=>{if(this.playing)for(const o of this.machine.instruments){if(!o.keyedInstrument)continue;const s=this.instrumentPlayers.get(o);s&&this.rescheduleInstrument(o,s)}}))}}play(){this.mixer.context?.resume(),this.scheduleBuffers(),this.beatTick()}stop(){this.interval&&(clearTimeout(this.interval),this.interval=null),this.animationFrameRequest&&(cancelAnimationFrame(this.animationFrameRequest),this.animationFrameRequest=null),this.stopAllInstruments(!0),this.mixer.reset(),this.audioTimeDelta=0,this.nextSampleIndex=0}get playing(){return this.interval!=null}get beatTime(){const l=60/this.machine.bpm;return this.machine.flavor==="Merengue"?l/2:l}getBeatIndex(){return(this.mixer.getCurrentTime()+this.audioTimeDelta)/this.beatTime}getInstrumentPlayer(l,o){const s=this.instrumentPlayers.get(o);if(s)return s;{const c=new Tv(l,o);return this.machineDisposers.push(Ml(o,"activeProgram",()=>this.rescheduleInstrument(o,c))),this.instrumentPlayers.set(o,c),c}}rescheduleInstrument(l,o){o.reset();const s=this.beatTime/2;for(let c=Math.ceil(this.getBeatIndex()*2);c{this.mixer.play(d.sampleName,o,c*s-this.audioTimeDelta,d.velocity)})}instrumentNotes(l,o){const s=[];if(l.enabled){const c=l.programs[l.activeProgram];o%=c.length,c.notes.filter(d=>d.index===o).forEach(d=>{let p=d.pitch;l.keyedInstrument&&(p+=this.machine.keyNote),d.hand!=="left"&&(s.push({sampleName:l.id+"-"+(p+l.pitchOffset),velocity:d.velocity}),d.pianoTonic&&s.push({sampleName:l.id+"-"+(p+l.pitchOffset+12),velocity:d.velocity})),l.playBothHands&&d.hand!=="right"&&s.push({sampleName:l.id+"-"+(p+l.leftHandPitchOffset),velocity:d.velocity})})}return s}beatTick(){this.beat=this.getBeatIndex(),this.animationFrameRequest=requestAnimationFrame(()=>this.beatTick())}}class Mv{constructor(l=""){this.baseUrl=l,this.zeroTime=null,this.ready=!1;const s=typeof MediaSource<"u"&&MediaSource.isTypeSupported('audio/webm;codecs="vorbis"')?"/assets/audio/main.webm":"/assets/audio/main.mp3";this.audioFormat=l?`${l}/${s}`:s,console.log("AudioBackend created - will load audio after init()")}async init(l=typeof AudioContext<"u"?new AudioContext:void 0){if(this._context=l,this._context){this.zeroTime=this._context.currentTime,console.log("🎵 AudioBackend initialized — zeroTime set to:",this.zeroTime);try{await Promise.all([this.loadBank(this.audioFormat),this.loadBankDescriptor("/assets/audio/main.json")]),console.log("✅ Audio files loaded successfully")}catch(o){console.error("❌ Error loading audio files:",o),console.error("Please ensure the following files exist in public/assets/audio/:"),console.error(" - main.webm (or main.mp3)"),console.error(" - main.json"),console.error("Download them from: https://www.salsabeatmachine.org/assets/audio/")}}}get context(){return this._context}async loadBank(l){try{console.log("Loading audio bank from:",l);const o=await fetch(l);if(!o.ok)throw new Error(`Failed to fetch ${l}: ${o.status} ${o.statusText}`);const s=await o.arrayBuffer();if(!this.context)throw new Error("AudioContext not initialized - cannot decode audio");this.buffer=await new Promise((c,d)=>{this.context?.decodeAudioData(s,c,d)}),this.ready=!0,console.log("✅ Audio bank loaded successfully")}catch(o){throw console.error("❌ Failed to load audio bank:",o),o}}async loadBankDescriptor(l){try{const o=this.baseUrl&&!l.startsWith("http")?`${this.baseUrl}/${l}`:l;console.log("Loading bank descriptor from:",o);const s=await fetch(o);if(!s.ok)throw new Error(`Failed to fetch ${o}: ${s.status} ${s.statusText}`);this.bankDescriptor=await s.json(),console.log("✅ Bank descriptor loaded successfully")}catch(o){throw console.error("❌ Failed to load bank descriptor:",o),o}}play(l,o,s,c){const d=this.context.createBufferSource();d.connect(o.createNoteDestination(c)),d.buffer=this.buffer;const p=this.bankDescriptor[l];this.zeroTime===null&&(this.zeroTime=this.context.currentTime,console.log("⚠️ Warning: zeroTime was null — setting it now to:",this.zeroTime));const g=this.zeroTime+s;d.start(g,p[1]/44100,p[2]/44100),o.registerSample(d,g)}reset(){this.zeroTime=null}getCurrentTime(){return this.zeroTime==null?(console.warn("⏱️ Warning: zeroTime is null, returning 0 for current time."),0):this.context.currentTime-this.zeroTime}}function jv(){if(typeof document>"u"||document.getElementById("beat-machine-widget-styles"))return;const i=document.createElement("style");i.id="beat-machine-widget-styles",i.textContent=` + .beat-widget { + display: inline-flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(255, 201, 71, 0.3); + padding: 0; + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(255, 201, 71, 0.5); + } + + .beat-widget .playButton:active { + transform: scale(0.95); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #FF8C69 0%, #FF6B4A 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #FF9933; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 153, 51, 0.25); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .widgetContent { + display: flex; + align-items: center; + gap: 1rem; + } + + .beat-widget .footer { + margin-top: 0.25rem; + text-align: center; + font-size: 0.65rem; + opacity: 0.6; + line-height: 1; + } + + .beat-widget .footer a { + color: inherit; + text-decoration: none; + transition: opacity 0.2s ease; + } + + .beat-widget .footer a:hover { + opacity: 1; + text-decoration: underline; + } + `,document.head.appendChild(i)}const fc=Pv(({machine:i,instruments:l,initialBpm:o,autoplay:s=!1})=>{const[c,d]=Fe.useState(!1),[p,g]=Fe.useState(null);Fe.useEffect(()=>{jv()},[]),Fe.useEffect(()=>((async()=>{const R=dv(),L=new Mv(R),Y=new Rv(L);await Y.init(),g(Y)})(),()=>{p&&p.stop()}),[]),Fe.useEffect(()=>{l&&l.length>0&&i.instruments.forEach(P=>{P.enabled=l.includes(P.id)})},[l,i]),Fe.useEffect(()=>{o!==void 0&&i.bpm!==o&&(i.bpm=o)},[o,i]);const y=()=>{p&&(c?(p.stop(),d(!1)):(p.machine=i,p.play(),d(!0)))},C=P=>{i.bpm=parseInt(P.target.value,10)};return Be.jsxs("div",{className:"beat-widget",children:[Be.jsxs("div",{className:"widgetContent",children:[Be.jsx("button",{className:`playButton ${c?"playing":""}`,onClick:y,disabled:!p,"aria-label":c?"Pause":"Play",children:c?Be.jsx("svg",{viewBox:"0 0 24 24",children:Be.jsx("path",{d:"M6 4h4v16H6V4zm8 0h4v16h-4V4z"})}):Be.jsx("svg",{viewBox:"0 0 24 24",children:Be.jsx("path",{d:"M8 5v14l11-7z"})})}),Be.jsxs("div",{className:"bpmControl",children:[Be.jsxs("label",{className:"bpmLabel",children:[i.bpm," BPM"]}),Be.jsx("input",{type:"range",className:"slider",min:"60",max:"200",value:i.bpm,onChange:C,"aria-label":"Tempo"})]})]}),Be.jsx("div",{className:"footer",children:Be.jsx("a",{href:"https://beat.salsanor.no",target:"_blank",rel:"noopener noreferrer",children:"Powered by SalsaNor Beat"})})]})});typeof process>"u"&&(window.process={env:{NODE_ENV:"production"}});function Lv(){if(document.getElementById("beat-machine-widget-styles"))return;const i=document.createElement("style");i.id="beat-machine-widget-styles",i.textContent=` + .beat-widget { + display: inline-flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(255, 201, 71, 0.3); + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(255, 201, 71, 0.5); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #FF8C69 0%, #FF6B4A 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #FF9933; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 153, 51, 0.25); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .widgetContent { + display: flex; + align-items: center; + gap: 1rem; + } + + .beat-widget .footer { + margin-top: 0.25rem; + text-align: center; + font-size: 0.65rem; + opacity: 0.6; + line-height: 1; + } + + .beat-widget .footer a { + color: inherit; + text-decoration: none; + transition: opacity 0.2s ease; + } + + .beat-widget .footer a:hover { + opacity: 1; + text-decoration: underline; + } + `,document.head.appendChild(i)}class bv{constructor(){this.instances=new Map}async init(l="[data-beat-widget]"){Lv();const o=document.querySelectorAll(l),s=Array.from(o);for(const c of s){if(!(c instanceof HTMLElement))continue;const d=this.parseConfig(c);await this.create(c,d)}}parseConfig(l){const o=l.dataset.instruments?.split(",").map(g=>g.trim())||[],s=l.dataset.bpm?parseInt(l.dataset.bpm,10):120,c=l.dataset.machine||"salsa",d=l.dataset.autoplay==="true",p={};return l.dataset.programs&&l.dataset.programs.split(",").forEach(y=>{const[C,P]=y.split(":").map(R=>R.trim());C&&P&&(p[C]=parseInt(P,10))}),{instruments:o,programs:p,bpm:s,machine:c,autoplay:d}}async create(l,o){const s=o.machine||"salsa",c=await pv(s),d=Oe(c);o.bpm&&(d.bpm=o.bpm),o.programs&&d.instruments.forEach(y=>{o.programs[y.id]!==void 0&&(y.activeProgram=o.programs[y.id])});const p=vp.createRoot(l),g={play:()=>{},pause:()=>{},stop:()=>{},setBPM:y=>{d.bpm=y},destroy:()=>{p.unmount(),this.instances.delete(l)}};return p.render(Be.jsx(pl.StrictMode,{children:Be.jsx(fc,{machine:d,instruments:o.instruments,initialBpm:o.bpm,autoplay:o.autoplay})})),this.instances.set(l,{root:p,instance:g}),g}destroy(l){const o=this.instances.get(l);o&&o.instance.destroy()}}const Ur=new bv;window.BeatMachineWidget={init:i=>Ur.init(i),create:(i,l)=>Ur.create(i,l),destroy:i=>Ur.destroy(i),setBaseUrl:i=>{ic(i),console.log("✅ Beat Machine Widget base URL set to:",i)}},window.BEAT_MACHINE_BASE_URL&&(ic(window.BEAT_MACHINE_BASE_URL),console.log("✅ Beat Machine Widget loaded with base URL:",window.BEAT_MACHINE_BASE_URL)),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{Ur.init()}):Ur.init(),Bs.WidgetCompact=fc,Object.defineProperty(Bs,Symbol.toStringTag,{value:"Module"})})(this.BeatMachineWidget=this.BeatMachineWidget||{}); diff --git a/services/load-machine.ts b/services/load-machine.ts index 76820aa..ae411b2 100644 --- a/services/load-machine.ts +++ b/services/load-machine.ts @@ -1,6 +1,6 @@ import { promises as fs } from 'fs'; import { resolve } from 'path'; -import { DOMParser } from 'xmldom'; +import { DOMParser } from '@xmldom/xmldom'; import { MachineXMLLoader } from '../engine/machine-xml-loader.service'; export async function loadMachine(fileName: string) { @@ -8,5 +8,5 @@ export async function loadMachine(fileName: string) { const machineXml = await fs.readFile(fullPath, 'utf-8'); const xml = new DOMParser().parseFromString(machineXml, 'text/xml'); const loader = new MachineXMLLoader(); - return JSON.parse(JSON.stringify(loader.loadMachine(xml))); + return JSON.parse(JSON.stringify(loader.loadMachine(xml as unknown as Document))); } diff --git a/src/services/load-machine-client.ts b/src/services/load-machine-client.ts new file mode 100644 index 0000000..b7ce1be --- /dev/null +++ b/src/services/load-machine-client.ts @@ -0,0 +1,41 @@ +import { MachineXMLLoader } from '../../engine/machine-xml-loader.service'; +import { IMachine } from '../../engine/machine-interfaces'; + +// Global config for cross-domain widget hosting +let widgetBaseUrl = ''; + +export function setWidgetBaseUrl(url: string) { + widgetBaseUrl = url.endsWith('/') ? url.slice(0, -1) : url; +} + +export function getWidgetBaseUrl(): string { + return widgetBaseUrl; +} + +/** + * Client-side version of loadMachine that uses fetch instead of fs + * Supports both same-origin and cross-origin loading + */ +export async function loadMachineClient(machineNameOrPath: string): Promise { + // If it's just a name, construct the full path + let url = machineNameOrPath.endsWith('.xml') + ? `/assets/machines/${machineNameOrPath}` + : `/assets/machines/${machineNameOrPath}.xml`; + + // Prepend base URL if configured (for cross-domain usage) + if (widgetBaseUrl) { + url = `${widgetBaseUrl}${url}`; + } + + const response = await fetch(url); + + if (!response.ok) { + throw new Error(`Failed to load machine from ${url}: ${response.statusText}`); + } + + const machineXml = await response.text(); + const xml = new DOMParser().parseFromString(machineXml, 'text/xml'); + const loader = new MachineXMLLoader(); + + return loader.loadMachine(xml); +} diff --git a/src/widget/WidgetCompact.module.css b/src/widget/WidgetCompact.module.css new file mode 100644 index 0000000..f671d3b --- /dev/null +++ b/src/widget/WidgetCompact.module.css @@ -0,0 +1,141 @@ +.widget { + display: inline-flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.5rem 1rem; + background: transparent; + border-radius: 2rem; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif; + transition: all 0.2s ease; +} + +.widgetContent { + display: flex; + align-items: center; + gap: 1rem; +} + +.playButton { + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + padding: 0; + border: none; + border-radius: 50%; + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + color: rgba(255, 255, 255, 0.95); + cursor: pointer; + transition: all 0.2s ease; + font-size: 0; +} + +.playButton:hover { + background: rgba(255, 255, 255, 0.25); + transform: scale(1.05); +} + +.playButton:active { + transform: scale(0.95); +} + +.playButton:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.playButton svg { + width: 20px; + height: 20px; + fill: currentColor; +} + +.playButton.playing { + background: rgba(255, 201, 71, 0.2); + color: #FFC947; +} + +.bpmControl { + display: flex; + align-items: center; + gap: 0.5rem; + min-width: 150px; +} + +.bpmLabel { + font-size: 0.875rem; + color: #FF9933; + font-weight: 600; + white-space: nowrap; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); +} + +.slider { + flex: 1; + height: 4px; + background: rgba(255, 153, 51, 0.25); + border-radius: 9999px; + outline: none; + -webkit-appearance: none; + appearance: none; + cursor: pointer; +} + +.slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 14px; + height: 14px; + background: #FF9933; + border-radius: 50%; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + transition: all 0.2s ease; +} + +.slider::-webkit-slider-thumb:hover { + background: #FFC947; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 0 12px rgba(255, 153, 51, 0.6); + transform: scale(1.1); +} + +.slider::-moz-range-thumb { + width: 14px; + height: 14px; + background: #FF9933; + border: none; + border-radius: 50%; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + transition: all 0.2s ease; +} + +.slider::-moz-range-thumb:hover { + background: #FFC947; + box-shadow: 0 2px 6px rgba(0, 0, 0, 0.3), 0 0 12px rgba(255, 153, 51, 0.6); + transform: scale(1.1); +} + +.footer { + margin-top: 0.25rem; + text-align: center; + font-size: 0.65rem; + opacity: 0.6; + line-height: 1; +} + +.footer a { + color: inherit; + text-decoration: none; + transition: opacity 0.2s ease; +} + +.footer a:hover { + opacity: 1; + text-decoration: underline; +} diff --git a/src/widget/WidgetCompact.tsx b/src/widget/WidgetCompact.tsx new file mode 100644 index 0000000..0fb2d00 --- /dev/null +++ b/src/widget/WidgetCompact.tsx @@ -0,0 +1,257 @@ +'use client'; + +import { observer } from 'mobx-react-lite'; +import { useState, useEffect } from 'react'; +import { IMachine } from '../../engine/machine-interfaces'; +import { BeatEngine } from '../../engine/beat-engine'; +import { AudioBackend } from '../../engine/audio-backend'; +import { getWidgetBaseUrl } from '../services/load-machine-client'; + +// Inject widget CSS into the page +function injectWidgetCSS() { + if (typeof document === 'undefined') return; // SSR guard + if (document.getElementById('beat-machine-widget-styles')) { + return; // Already injected + } + + const style = document.createElement('style'); + style.id = 'beat-machine-widget-styles'; + style.textContent = ` + .beat-widget { + display: inline-flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(255, 201, 71, 0.3); + padding: 0; + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(255, 201, 71, 0.5); + } + + .beat-widget .playButton:active { + transform: scale(0.95); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #FF8C69 0%, #FF6B4A 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #FF9933; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 153, 51, 0.25); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .widgetContent { + display: flex; + align-items: center; + gap: 1rem; + } + + .beat-widget .footer { + margin-top: 0.25rem; + text-align: center; + font-size: 0.65rem; + opacity: 0.6; + line-height: 1; + } + + .beat-widget .footer a { + color: inherit; + text-decoration: none; + transition: opacity 0.2s ease; + } + + .beat-widget .footer a:hover { + opacity: 1; + text-decoration: underline; + } + `; + document.head.appendChild(style); +} + +interface WidgetCompactProps { + machine: IMachine; + instruments?: string[]; + initialBpm?: number; + autoplay?: boolean; +} + +export const WidgetCompact = observer(({ machine, instruments, initialBpm, autoplay = false }: WidgetCompactProps) => { + const [isPlaying, setIsPlaying] = useState(false); + const [engine, setEngine] = useState(null); + + // Inject CSS on mount + useEffect(() => { + injectWidgetCSS(); + }, []); + + useEffect(() => { + const initEngine = async () => { + const baseUrl = getWidgetBaseUrl(); + const mixer = new AudioBackend(baseUrl); + const newEngine = new BeatEngine(mixer); + await newEngine.init(); + setEngine(newEngine); + }; + + initEngine(); + + return () => { + if (engine) { + engine.stop(); + } + }; + }, []); + + // Filter instruments if specified + useEffect(() => { + if (instruments && instruments.length > 0) { + machine.instruments.forEach(instrument => { + instrument.enabled = instruments.includes(instrument.id); + }); + } + }, [instruments, machine]); + + // Set initial BPM only if explicitly provided and different from machine's current BPM + useEffect(() => { + if (initialBpm !== undefined && machine.bpm !== initialBpm) { + machine.bpm = initialBpm; + } + }, [initialBpm, machine]); + + const togglePlay = () => { + if (!engine) return; + + if (isPlaying) { + engine.stop(); + setIsPlaying(false); + } else { + engine.machine = machine; + engine.play(); + setIsPlaying(true); + } + }; + + const handleBpmChange = (e: React.ChangeEvent) => { + machine.bpm = parseInt(e.target.value, 10); + }; + + return ( +
+
+ + +
+ + +
+
+ + +
+ ); +}); diff --git a/src/widget/widget-entry.tsx b/src/widget/widget-entry.tsx new file mode 100644 index 0000000..5d94f54 --- /dev/null +++ b/src/widget/widget-entry.tsx @@ -0,0 +1,292 @@ +// Polyfill for process global (needed for MobX in browser) +if (typeof process === 'undefined') { + (window as any).process = { env: { NODE_ENV: 'production' } }; +} + +import React from 'react'; +import ReactDOM from 'react-dom/client'; +import { observable } from 'mobx'; +import { loadMachineClient, setWidgetBaseUrl, getWidgetBaseUrl } from '../services/load-machine-client'; +import { WidgetCompact } from './WidgetCompact'; +import { WidgetConfig, WidgetInstance } from './widget-types'; + +// Inject widget CSS into the page +function injectWidgetCSS() { + if (document.getElementById('beat-machine-widget-styles')) { + return; // Already injected + } + + const style = document.createElement('style'); + style.id = 'beat-machine-widget-styles'; + style.textContent = ` + .beat-widget { + display: inline-flex; + flex-direction: column; + gap: 0.25rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #FFC947 0%, #FF9933 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(255, 201, 71, 0.3); + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(255, 201, 71, 0.5); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #FF8C69 0%, #FF6B4A 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #FF9933; + text-align: center; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 153, 51, 0.25); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: #FF9933; + cursor: pointer; + border: none; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2), 0 0 8px rgba(255, 153, 51, 0.4); + } + + .beat-widget .widgetContent { + display: flex; + align-items: center; + gap: 1rem; + } + + .beat-widget .footer { + margin-top: 0.25rem; + text-align: center; + font-size: 0.65rem; + opacity: 0.6; + line-height: 1; + } + + .beat-widget .footer a { + color: inherit; + text-decoration: none; + transition: opacity 0.2s ease; + } + + .beat-widget .footer a:hover { + opacity: 1; + text-decoration: underline; + } + `; + document.head.appendChild(style); +} + +class BeatMachineWidget { + private instances: Map = new Map(); + + async init(selector: string = '[data-beat-widget]') { + // Inject CSS first + injectWidgetCSS(); + + const elements = document.querySelectorAll(selector); + + // Convert NodeList to Array to avoid iteration issues + const elementsArray = Array.from(elements); + + for (const element of elementsArray) { + if (!(element instanceof HTMLElement)) continue; + + const config = this.parseConfig(element); + await this.create(element, config); + } + } + + private parseConfig(element: HTMLElement): WidgetConfig { + const instruments = element.dataset.instruments?.split(',').map(s => s.trim()) || []; + const bpm = element.dataset.bpm ? parseInt(element.dataset.bpm, 10) : 120; + const machine = (element.dataset.machine || 'salsa') as 'salsa' | 'merengue'; + const autoplay = element.dataset.autoplay === 'true'; + + // Parse programs: data-programs="clave:1,cowbell:2" + const programs: Record = {}; + if (element.dataset.programs) { + const programPairs = element.dataset.programs.split(','); + programPairs.forEach(pair => { + const [instrumentId, programIndex] = pair.split(':').map(s => s.trim()); + if (instrumentId && programIndex) { + programs[instrumentId] = parseInt(programIndex, 10); + } + }); + } + + return { instruments, programs, bpm, machine, autoplay }; + } + + async create(element: HTMLElement, config: WidgetConfig): Promise { + // Load machine + const machineName = config.machine || 'salsa'; + const machineData = await loadMachineClient(machineName); + const machine = observable(machineData); + + // Set initial BPM from config if provided + if (config.bpm) { + machine.bpm = config.bpm; + } + + // Set instrument programs if specified + if (config.programs) { + machine.instruments.forEach(instrument => { + if (config.programs![instrument.id] !== undefined) { + instrument.activeProgram = config.programs![instrument.id]; + } + }); + } + + // Create React root + const root = ReactDOM.createRoot(element); + + // Create instance API + const instance: WidgetInstance = { + play: () => { + // Will be controlled by component + }, + pause: () => { + // Will be controlled by component + }, + stop: () => { + // Will be controlled by component + }, + setBPM: (bpm: number) => { + machine.bpm = bpm; + }, + destroy: () => { + root.unmount(); + this.instances.delete(element); + } + }; + + // Render widget + root.render( + + + + ); + + this.instances.set(element, { root, instance }); + return instance; + } + + destroy(element: HTMLElement) { + const widget = this.instances.get(element); + if (widget) { + widget.instance.destroy(); + } + } +} + +// Create global instance +const widgetManager = new BeatMachineWidget(); + +// Expose to window +declare global { + interface Window { + BeatMachineWidget: { + init: (selector?: string) => Promise; + create: (element: HTMLElement, config: WidgetConfig) => Promise; + destroy: (element: HTMLElement) => void; + setBaseUrl: (url: string) => void; + }; + } +} + +window.BeatMachineWidget = { + init: (selector?: string) => widgetManager.init(selector), + create: (element: HTMLElement, config: WidgetConfig) => widgetManager.create(element, config), + destroy: (element: HTMLElement) => widgetManager.destroy(element), + setBaseUrl: (url: string) => { + setWidgetBaseUrl(url); + console.log('✅ Beat Machine Widget base URL set to:', url); + } +}; + +// Check for global base URL configuration +if ((window as any).BEAT_MACHINE_BASE_URL) { + setWidgetBaseUrl((window as any).BEAT_MACHINE_BASE_URL); + console.log('✅ Beat Machine Widget loaded with base URL:', (window as any).BEAT_MACHINE_BASE_URL); +} + +// Auto-initialize on DOM ready +if (document.readyState === 'loading') { + document.addEventListener('DOMContentLoaded', () => { + widgetManager.init(); + }); +} else { + widgetManager.init(); +} + +export { WidgetCompact }; diff --git a/src/widget/widget-types.ts b/src/widget/widget-types.ts new file mode 100644 index 0000000..fa15712 --- /dev/null +++ b/src/widget/widget-types.ts @@ -0,0 +1,15 @@ +export interface WidgetConfig { + instruments: string[]; + programs?: Record; // instrumentId -> programIndex + bpm?: number; + machine?: 'salsa' | 'merengue'; + autoplay?: boolean; +} + +export interface WidgetInstance { + play: () => void; + pause: () => void; + stop: () => void; + setBPM: (bpm: number) => void; + destroy: () => void; +} diff --git a/styles/abstracts/_functions.scss b/styles/abstracts/_functions.scss new file mode 100644 index 0000000..0526341 --- /dev/null +++ b/styles/abstracts/_functions.scss @@ -0,0 +1,213 @@ +// ============================================ +// SCSS Functions +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +@use 'sass:math'; +@use 'sass:color'; +@use '../tokens/spacing' as *; + +// ====== Spacing Function ====== + +/// Get spacing value by key from the spacing map +/// @param {Number} $key - The spacing key (0-32) +/// @return {String} - The spacing value in rem +/// @example scss - Usage +/// padding: spacing(4); // 1rem +@function spacing($key) { + @if map-has-key($spacing, $key) { + @return map-get($spacing, $key); + } @else { + @warn "Spacing key `#{$key}` not found in spacing map."; + @return null; + } +} + +// ====== Color Manipulation Functions ====== + +/// Lighten a color by mixing it with white +/// @param {Color} $color - The color to lighten +/// @param {Number} $percentage - The percentage to lighten (0-100) +/// @return {Color} - The lightened color +/// @example scss - Usage +/// background: tint(#667eea, 20%); // Lighter purple +@function tint($color, $percentage) { + @return mix(white, $color, $percentage); +} + +/// Darken a color by mixing it with black +/// @param {Color} $color - The color to darken +/// @param {Number} $percentage - The percentage to darken (0-100) +/// @return {Color} - The darkened color +/// @example scss - Usage +/// background: shade(#667eea, 20%); // Darker purple +@function shade($color, $percentage) { + @return mix(black, $color, $percentage); +} + +/// Add transparency to a color +/// @param {Color} $color - The color to modify +/// @param {Number} $alpha - The alpha value (0-1) +/// @return {Color} - The color with transparency +/// @example scss - Usage +/// background: alpha(#667eea, 0.5); // 50% transparent purple +@function alpha($color, $alpha) { + @return rgba($color, $alpha); +} + +// ====== Unit Conversion Functions ====== + +/// Convert pixels to rem units +/// @param {Number} $pixels - The pixel value to convert +/// @param {Number} $base - The base font size (default: 16px) +/// @return {String} - The rem value +/// @example scss - Usage +/// font-size: rem(24px); // 1.5rem +@function rem($pixels, $base: 16px) { + @if unitless($pixels) { + $pixels: $pixels * 1px; + } + @if unitless($base) { + $base: $base * 1px; + } + @return math.div($pixels, $base) * 1rem; +} + +/// Convert pixels to em units +/// @param {Number} $pixels - The pixel value to convert +/// @param {Number} $base - The base font size (default: 16px) +/// @return {String} - The em value +/// @example scss - Usage +/// margin: em(24px); // 1.5em +@function em($pixels, $base: 16px) { + @if unitless($pixels) { + $pixels: $pixels * 1px; + } + @if unitless($base) { + $base: $base * 1px; + } + @return math.div($pixels, $base) * 1em; +} + +/// Strip unit from a value +/// @param {Number} $value - The value with unit +/// @return {Number} - The unitless value +/// @example scss - Usage +/// $unitless: strip-unit(24px); // 24 +@function strip-unit($value) { + @if type-of($value) == 'number' and not unitless($value) { + @return math.div($value, ($value * 0 + 1)); + } + @return $value; +} + +// ====== Math Functions ====== + +/// Constrain a value between a minimum and maximum +/// @param {Number} $min - The minimum value +/// @param {Number} $value - The value to constrain +/// @param {Number} $max - The maximum value +/// @return {Number} - The constrained value +/// @example scss - Usage +/// width: clamp-value(200px, 50%, 600px); +@function clamp-value($min, $value, $max) { + @return max($min, min($value, $max)); +} + +// ====== Utility Functions ====== + +/// Check if a value is a valid CSS length +/// @param {*} $value - The value to check +/// @return {Boolean} - True if valid length +@function is-length($value) { + @return type-of($value) == 'number' and index('em' 'rem' 'px' 'pt' 'cm' 'mm' 'in' 'pc' 'ex' 'ch' 'vw' 'vh' 'vmin' 'vmax' '%', unit($value)) != null; +} + +/// Get a value from a nested map +/// @param {Map} $map - The map to search +/// @param {Arglist} $keys - The keys to traverse +/// @return {*} - The value or null +/// @example scss - Usage +/// $value: map-deep-get($theme, 'colors', 'primary'); +@function map-deep-get($map, $keys...) { + @each $key in $keys { + @if type-of($map) != 'map' { + @return null; + } + $map: map-get($map, $key); + } + @return $map; +} + +/// Calculate the luminance of a color +/// @param {Color} $color - The color to analyze +/// @return {Number} - The luminance value (0-1) +@function luminance($color) { + $red: color.red($color) / 255; + $green: color.green($color) / 255; + $blue: color.blue($color) / 255; + + // Apply gamma correction + @if $red <= 0.03928 { + $red: $red / 12.92; + } @else { + $red: math.pow(($red + 0.055) / 1.055, 2.4); + } + @if $green <= 0.03928 { + $green: $green / 12.92; + } @else { + $green: math.pow(($green + 0.055) / 1.055, 2.4); + } + @if $blue <= 0.03928 { + $blue: $blue / 12.92; + } @else { + $blue: math.pow(($blue + 0.055) / 1.055, 2.4); + } + + @return 0.2126 * $red + 0.7152 * $green + 0.0722 * $blue; +} + +/// Calculate contrast ratio between two colors +/// @param {Color} $color1 - First color +/// @param {Color} $color2 - Second color +/// @return {Number} - The contrast ratio +@function contrast-ratio($color1, $color2) { + $l1: luminance($color1); + $l2: luminance($color2); + + @if $l1 > $l2 { + @return math.div($l1 + 0.05, $l2 + 0.05); + } @else { + @return math.div($l2 + 0.05, $l1 + 0.05); + } +} + +/// Choose a contrasting text color (black or white) based on background +/// @param {Color} $background - The background color +/// @return {Color} - Either black or white +@function text-contrast($background) { + $light-contrast: contrast-ratio($background, white); + $dark-contrast: contrast-ratio($background, black); + + @if $light-contrast > $dark-contrast { + @return white; + } @else { + @return black; + } +} + +// ====== Z-Index Function ====== + +/// Get z-index value by key +/// @param {String} $key - The z-index key +/// @return {Number} - The z-index value +/// @example scss - Usage +/// z-index: z($key: 'modal'); +@function z($key) { + @if map-has-key($z-index, $key) { + @return map-get($z-index, $key); + } @else { + @warn "Z-index key `#{$key}` not found."; + @return 0; + } +} diff --git a/styles/abstracts/_mixins-modules.scss b/styles/abstracts/_mixins-modules.scss new file mode 100644 index 0000000..7fe91c5 --- /dev/null +++ b/styles/abstracts/_mixins-modules.scss @@ -0,0 +1,102 @@ +// ============================================ +// SCSS Mixins for CSS Modules +// SalsaNor Beat Machine - Glassmorphic Theme +// (No token imports to avoid :root conflicts) +// ============================================ + +// ====== Glassmorphism Mixins ====== + +@mixin glass-light { + background: rgba(255, 255, 255, 0.1); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.15); + box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.05); +} + +@mixin glass-medium { + background: rgba(255, 255, 255, 0.15); + backdrop-filter: blur(20px); + -webkit-backdrop-filter: blur(20px); + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 4px 16px rgba(0, 0, 0, 0.2), 0 2px 8px rgba(0, 0, 0, 0.1); +} + +@mixin glass-dark { + background: rgba(255, 255, 255, 0.2); + backdrop-filter: blur(30px); + -webkit-backdrop-filter: blur(30px); + border: 1px solid rgba(255, 255, 255, 0.25); + box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.15); +} + +// ====== Responsive Breakpoint Mixins ====== + +$breakpoints: ( + mobile: 480px, + tablet: 640px, + desktop: 1024px, + wide: 1536px, +); + +@mixin respond-to($breakpoint) { + @if map-has-key($breakpoints, $breakpoint) { + @media (min-width: map-get($breakpoints, $breakpoint)) { + @content; + } + } +} + +// ====== Hover Effect Mixins ====== + +@mixin hover-lift($distance: -2px) { + transition: transform 0.2s ease; + + &:hover { + transform: translateY($distance); + } +} + +// ====== Animation Mixins ====== + +@mixin smooth-transition($properties: all, $duration: 0.3s, $easing: cubic-bezier(0.4, 0, 0.2, 1)) { + transition: $properties $duration $easing; +} + +// ====== Focus State Mixins ====== + +@mixin focus-visible { + &:focus-visible { + outline: 2px solid #FFC947; + outline-offset: 2px; + box-shadow: 0 0 0 4px rgba(0, 245, 255, 0.2); + } +} + +// ====== Typography Mixins ====== + +@mixin heading-1 { + font-family: 'Merriweather', Georgia, 'Times New Roman', serif; + font-size: clamp(2rem, 4vw + 1rem, 3.5rem); + font-weight: 700; + line-height: 1.2; + letter-spacing: -0.025em; + color: rgba(255, 255, 255, 0.95); +} + +// ====== Grid Layout Mixins ====== + +@mixin responsive-grid($columns-desktop: 3, $columns-tablet: 2, $columns-mobile: 1) { + display: grid; + gap: 1rem; + grid-template-columns: repeat($columns-mobile, 1fr); + + @include respond-to('tablet') { + gap: 1.5rem; + grid-template-columns: repeat($columns-tablet, 1fr); + } + + @include respond-to('desktop') { + grid-template-columns: repeat($columns-desktop, 1fr); + } +} diff --git a/styles/abstracts/_mixins.scss b/styles/abstracts/_mixins.scss new file mode 100644 index 0000000..310e0dd --- /dev/null +++ b/styles/abstracts/_mixins.scss @@ -0,0 +1,580 @@ +// ============================================ +// SCSS Mixins +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +@use '../tokens/colors' as *; +@use '../tokens/spacing' as *; +@use '../tokens/shadows' as *; +@use '../tokens/typography' as *; +@use './functions' as *; + +// ====== Glassmorphism Mixins ====== + +/// Light glass effect +/// @example scss - Usage +/// .card { @include glass-light; } +@mixin glass-light { + background: rgba(255, 255, 255, $glass-opacity-light); + backdrop-filter: blur($glass-blur-sm); + -webkit-backdrop-filter: blur($glass-blur-sm); + border: 1px solid rgba(255, 255, 255, $glass-border-opacity-light); + box-shadow: $shadow-glass-sm; +} + +/// Medium glass effect (default) +/// @example scss - Usage +/// .card { @include glass-medium; } +@mixin glass-medium { + background: rgba(255, 255, 255, $glass-opacity-medium); + backdrop-filter: blur($glass-blur-md); + -webkit-backdrop-filter: blur($glass-blur-md); + border: 1px solid rgba(255, 255, 255, $glass-border-opacity-medium); + box-shadow: $shadow-glass-md; +} + +/// Dark glass effect +/// @example scss - Usage +/// .card { @include glass-dark; } +@mixin glass-dark { + background: rgba(255, 255, 255, $glass-opacity-dark); + backdrop-filter: blur($glass-blur-lg); + -webkit-backdrop-filter: blur($glass-blur-lg); + border: 1px solid rgba(255, 255, 255, $glass-border-opacity-dark); + box-shadow: $shadow-glass-lg; +} + +/// Glass effect with custom parameters +/// @param {Number} $opacity - Background opacity (0-1) +/// @param {String} $blur - Backdrop blur amount +/// @param {Number} $border-opacity - Border opacity (0-1) +@mixin glass-custom($opacity: 0.15, $blur: 20px, $border-opacity: 0.2) { + background: rgba(255, 255, 255, $opacity); + backdrop-filter: blur($blur); + -webkit-backdrop-filter: blur($blur); + border: 1px solid rgba(255, 255, 255, $border-opacity); + box-shadow: $shadow-glass-md; +} + +// ====== Responsive Breakpoint Mixins ====== + +/// Respond to breakpoint +/// @param {String} $breakpoint - The breakpoint name +/// @example scss - Usage +/// @include respond-to('tablet') { display: grid; } +@mixin respond-to($breakpoint) { + @if map-has-key($breakpoints, $breakpoint) { + @media (min-width: map-get($breakpoints, $breakpoint)) { + @content; + } + } @else { + @warn "Breakpoint `#{$breakpoint}` not found."; + } +} + +/// Mobile first (default styles, no media query needed) +@mixin mobile { + @content; +} + +/// Tablet and up +@mixin tablet { + @include respond-to('tablet') { + @content; + } +} + +/// Desktop and up +@mixin desktop { + @include respond-to('desktop') { + @content; + } +} + +/// Wide screens +@mixin wide { + @include respond-to('wide') { + @content; + } +} + +/// Mobile only (max-width) +@mixin mobile-only { + @media (max-width: #{map-get($breakpoints, 'tablet') - 1px}) { + @content; + } +} + +/// Tablet only (between mobile and desktop) +@mixin tablet-only { + @media (min-width: map-get($breakpoints, 'tablet')) and (max-width: #{map-get($breakpoints, 'desktop') - 1px}) { + @content; + } +} + +// ====== Focus State Mixins ====== + +/// Modern focus visible state with cyan glow +/// @example scss - Usage +/// button { @include focus-visible; } +@mixin focus-visible { + &:focus-visible { + outline: $focus-ring-width solid $focus-color; + outline-offset: $focus-ring-offset; + box-shadow: 0 0 0 4px rgba(0, 245, 255, $focus-ring-opacity); + } +} + +/// Legacy focus state (for older browsers) +@mixin focus-legacy { + &:focus { + outline: $focus-ring-width solid $focus-color; + outline-offset: $focus-ring-offset; + } +} + +/// Remove default focus outline +@mixin no-focus-outline { + &:focus { + outline: none; + } +} + +// ====== Hover Effect Mixins ====== + +/// Lift effect on hover +/// @param {String} $distance - Distance to lift (default: -2px) +/// @example scss - Usage +/// .card { @include hover-lift; } +@mixin hover-lift($distance: $hover-lift) { + transition: transform 0.2s ease; + + &:hover { + transform: translateY($distance); + } +} + +/// Glow effect on hover +/// @param {Color} $color - The glow color +/// @param {String} $size - Glow size (sm, md, lg, xl) +/// @example scss - Usage +/// .button { @include hover-glow($color-primary, 'md'); } +@mixin hover-glow($color, $size: 'md') { + transition: box-shadow 0.2s ease; + + &:hover { + @if $size == 'sm' { + box-shadow: 0 0 10px rgba($color, 0.6); + } @else if $size == 'md' { + box-shadow: 0 0 15px rgba($color, 0.7); + } @else if $size == 'lg' { + box-shadow: 0 0 20px rgba($color, 0.8); + } @else if $size == 'xl' { + box-shadow: 0 0 30px rgba($color, 0.9); + } + } +} + +/// Scale effect on hover +/// @param {Number} $scale - Scale factor (default: 1.05) +@mixin hover-scale($scale: 1.05) { + transition: transform 0.2s ease; + + &:hover { + transform: scale($scale); + } +} + +/// Brighten effect on hover +@mixin hover-brighten { + transition: filter 0.2s ease; + + &:hover { + filter: brightness(1.1); + } +} + +// ====== Animation Mixins ====== + +/// Smooth transition with custom properties +/// @param {String} $properties - CSS properties to transition +/// @param {String} $duration - Transition duration (default: 0.3s) +/// @param {String} $easing - Timing function (default: ease) +/// @example scss - Usage +/// @include smooth-transition(all, 0.3s, ease); +@mixin smooth-transition($properties: all, $duration: 0.3s, $easing: cubic-bezier(0.4, 0, 0.2, 1)) { + transition: $properties $duration $easing; +} + +/// Pulse animation +/// @param {String} $duration - Animation duration +@mixin pulse-animation($duration: 0.3s) { + animation: pulse $duration ease-out; + + @keyframes pulse { + 0%, 100% { transform: scale(1); } + 50% { transform: scale(1.1); } + } +} + +/// Fade in animation +/// @param {String} $duration - Animation duration +@mixin fade-in($duration: 0.3s) { + animation: fadeIn $duration ease-in; + + @keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } + } +} + +/// Slide in from bottom +/// @param {String} $duration - Animation duration +@mixin slide-in-bottom($duration: 0.4s) { + animation: slideInBottom $duration ease-out; + + @keyframes slideInBottom { + from { + opacity: 0; + transform: translateY(20px); + } + to { + opacity: 1; + transform: translateY(0); + } + } +} + +// ====== Typography Mixins ====== + +/// Heading 1 style +@mixin heading-1 { + font-family: $font-family-display; + font-size: $font-size-h1; + font-weight: $font-weight-bold; + line-height: $line-height-heading; + letter-spacing: $letter-spacing-tight; + color: $text-primary; +} + +/// Heading 2 style +@mixin heading-2 { + font-family: $font-family-ui; + font-size: $font-size-h2; + font-weight: $font-weight-semibold; + line-height: $line-height-heading; + letter-spacing: $letter-spacing-tight; + color: $text-primary; +} + +/// Heading 3 style +@mixin heading-3 { + font-family: $font-family-ui; + font-size: $font-size-h3; + font-weight: $font-weight-semibold; + line-height: $line-height-snug; + color: $text-primary; +} + +/// Body text style +@mixin body-text { + font-family: $font-family-ui; + font-size: $font-size-body; + font-weight: $font-weight-regular; + line-height: $line-height-body; + color: $text-secondary; +} + +/// Small text style +@mixin small-text { + font-family: $font-family-ui; + font-size: $font-size-small; + font-weight: $font-weight-regular; + line-height: $line-height-normal; + color: $text-muted; +} + +/// Monospace text style +@mixin mono-text { + font-family: $font-family-mono; + font-size: $font-size-body-small; + font-weight: $font-weight-regular; + line-height: $line-height-normal; +} + +// ====== Utility Mixins ====== + +/// Center content with flexbox +/// @example scss - Usage +/// .container { @include flex-center; } +@mixin flex-center { + display: flex; + align-items: center; + justify-content: center; +} + +/// Absolute center positioning +/// @example scss - Usage +/// .modal { @include absolute-center; } +@mixin absolute-center { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); +} + +/// Truncate text with ellipsis +/// @example scss - Usage +/// .label { @include truncate-text; } +@mixin truncate-text { + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/// Multi-line text truncation +/// @param {Number} $lines - Number of lines to show +@mixin line-clamp($lines: 2) { + display: -webkit-box; + -webkit-line-clamp: $lines; + -webkit-box-orient: vertical; + overflow: hidden; +} + +/// Clearfix for floats +@mixin clearfix { + &::after { + content: ''; + display: table; + clear: both; + } +} + +/// Aspect ratio box +/// @param {Number} $width - Width ratio +/// @param {Number} $height - Height ratio +@mixin aspect-ratio($width: 1, $height: 1) { + aspect-ratio: $width / $height; + + @supports not (aspect-ratio: 1 / 1) { + &::before { + content: ''; + display: block; + padding-top: percentage($height / $width); + } + } +} + +// ====== Grid Layout Mixins ====== + +/// Responsive grid layout +/// @param {Number} $columns-desktop - Columns on desktop +/// @param {Number} $columns-tablet - Columns on tablet +/// @param {Number} $columns-mobile - Columns on mobile +/// @example scss - Usage +/// @include responsive-grid(3, 2, 1); +@mixin responsive-grid($columns-desktop: 3, $columns-tablet: 2, $columns-mobile: 1) { + display: grid; + gap: $grid-gap-mobile; + grid-template-columns: repeat($columns-mobile, 1fr); + + @include tablet { + gap: $grid-gap; + grid-template-columns: repeat($columns-tablet, 1fr); + } + + @include desktop { + grid-template-columns: repeat($columns-desktop, 1fr); + } +} + +/// Auto-fit grid with minimum column width +/// @param {String} $min-width - Minimum column width +/// @param {String} $gap - Gap between items +@mixin auto-fit-grid($min-width: 280px, $gap: $grid-gap) { + display: grid; + grid-template-columns: repeat(auto-fit, minmax($min-width, 1fr)); + gap: $gap; +} + +// ====== Accessibility Mixins ====== + +/// Screen reader only (visually hidden but accessible) +/// @example scss - Usage +/// .sr-only { @include sr-only; } +@mixin sr-only { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +} + +/// Undo screen reader only +@mixin not-sr-only { + position: static; + width: auto; + height: auto; + padding: 0; + margin: 0; + overflow: visible; + clip: auto; + white-space: normal; +} + +/// Respect reduced motion preference +/// @example scss - Usage +/// @include reduced-motion { animation: none; } +@mixin reduced-motion { + @media (prefers-reduced-motion: reduce) { + @content; + } +} + +/// High contrast mode support +@mixin high-contrast { + @media (prefers-contrast: high) { + @content; + } +} + +// ====== Button Mixins ====== + +/// Base button styles +@mixin button-base { + display: inline-flex; + align-items: center; + justify-content: center; + padding: spacing(3) spacing(6); + border: none; + border-radius: $radius-md; + font-family: $font-family-ui; + font-size: $font-size-body; + font-weight: $font-weight-semibold; + line-height: 1; + text-decoration: none; + cursor: pointer; + user-select: none; + @include smooth-transition(all, 0.2s, ease); + + &:disabled { + opacity: $disabled-opacity; + cursor: not-allowed; + } +} + +/// Primary button style +@mixin button-primary { + @include button-base; + background: $gradient-primary; + color: white; + box-shadow: $shadow-button-primary; + + &:hover:not(:disabled) { + transform: translateY(-2px); + box-shadow: $shadow-button-primary-hover; + } + + &:active:not(:disabled) { + transform: translateY(0); + } + + @include focus-visible; +} + +/// Ghost button style +@mixin button-ghost { + @include button-base; + @include glass-light; + color: $text-primary; + + &:hover:not(:disabled) { + background: rgba(255, 255, 255, $hover-bg-opacity); + } + + @include focus-visible; +} + +/// Icon button style +@mixin button-icon { + @include flex-center; + width: 2.5rem; + height: 2.5rem; + padding: spacing(2); + border: none; + border-radius: $radius-md; + background: rgba(255, 255, 255, 0.1); + color: $text-primary; + cursor: pointer; + @include smooth-transition(all, 0.2s, ease); + + &:hover:not(:disabled) { + background: rgba(255, 255, 255, 0.2); + } + + &:disabled { + opacity: $disabled-opacity; + cursor: not-allowed; + } + + @include focus-visible; +} + +// ====== Form Element Mixins ====== + +/// Input field base styles +@mixin input-base { + width: 100%; + padding: spacing(3) spacing(4); + background: rgba(255, 255, 255, 0.1); + border: 1px solid $border-default; + border-radius: $radius-md; + font-family: $font-family-ui; + font-size: $font-size-body; + color: $text-primary; + @include smooth-transition(all, 0.2s, ease); + + &::placeholder { + color: $text-muted; + } + + &:hover:not(:disabled) { + border-color: $border-hover; + } + + &:focus { + outline: none; + border-color: $border-focus; + box-shadow: $shadow-focus; + } + + &:disabled { + opacity: $disabled-opacity; + cursor: not-allowed; + } +} + +// ====== Card Mixins ====== + +/// Card base styles +@mixin card { + @include glass-medium; + padding: spacing(6); + border-radius: $radius-lg; + @include smooth-transition(all, 0.3s, ease); +} + +/// Interactive card with hover effect +@mixin card-interactive { + @include card; + cursor: pointer; + + &:hover { + transform: translateY(-4px); + box-shadow: $shadow-glass-card-hover; + } +} diff --git a/styles/base/_globals.scss b/styles/base/_globals.scss new file mode 100644 index 0000000..90fdff1 --- /dev/null +++ b/styles/base/_globals.scss @@ -0,0 +1,19 @@ +@use '../tokens/colors' as *; +@use '../tokens/typography' as *; + +body { + background: linear-gradient(135deg, $bg-color-dark, $bg-color-mid, $bg-color-light); + background-attachment: fixed; + min-height: 100vh; +} + +// Accessibility - reduced motion +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + } +} diff --git a/styles/base/_reset.scss b/styles/base/_reset.scss new file mode 100644 index 0000000..ef84e42 --- /dev/null +++ b/styles/base/_reset.scss @@ -0,0 +1,19 @@ +*, +*::before, +*::after { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +html { + font-size: 16px; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +body { + font-family: var(--font-family-ui); + color: var(--text-primary); + line-height: var(--line-height-normal); +} diff --git a/styles/globals.scss b/styles/globals.scss new file mode 100644 index 0000000..b913dc9 --- /dev/null +++ b/styles/globals.scss @@ -0,0 +1,16 @@ +// =================================================== +// Global Stylesheet - The SalsaNor Beat Machine +// =================================================== + +// Import design system tokens and utilities +@use 'tokens/colors'; +@use 'tokens/typography'; +@use 'tokens/spacing'; +@use 'tokens/shadows'; +@use 'abstracts/functions'; +@use 'abstracts/mixins'; +@use 'base/reset'; +@use 'base/globals'; + +// Google Fonts - imported separately via @import +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&family=Merriweather:wght@300;400&display=swap'); diff --git a/styles/main.scss b/styles/main.scss new file mode 100644 index 0000000..6df644f --- /dev/null +++ b/styles/main.scss @@ -0,0 +1,148 @@ +// ============================================ +// Main SCSS Entry Point +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +// Import order matters! +// 1. Tokens (design system primitives) +// 2. Abstracts (functions and mixins) +// 3. Base styles (to be added in future) +// 4. Components (to be added in future) +// 5. Layout (to be added in future) + +// ====== 1. Design Tokens ====== +@import 'tokens/colors'; +@import 'tokens/typography'; +@import 'tokens/spacing'; +@import 'tokens/shadows'; + +// ====== 2. Abstracts ====== +@import 'abstracts/functions'; +@import 'abstracts/mixins'; + +// ====== 3. Base Styles (Future) ====== +// @import 'base/reset'; +// @import 'base/typography'; +// @import 'base/utilities'; + +// ====== 4. Components (Future) ====== +// @import 'components/buttons'; +// @import 'components/forms'; +// @import 'components/cards'; +// @import 'components/sequencer'; +// @import 'components/beat-indicator'; + +// ====== 5. Layout (Future) ====== +// @import 'layout/grid'; +// @import 'layout/containers'; +// @import 'layout/header'; +// @import 'layout/footer'; + +// ====== Global Base Styles ====== + +// Apply smooth scrolling +html { + scroll-behavior: smooth; + + @media (prefers-reduced-motion: reduce) { + scroll-behavior: auto; + } +} + +// Apply cosmic background gradient +body { + margin: 0; + padding: 0; + min-height: 100vh; + background: $bg-gradient-cosmic; + background-attachment: fixed; + font-family: $font-family-ui; + color: $text-primary; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; +} + +// Universal box-sizing +*, +*::before, +*::after { + box-sizing: border-box; +} + +// Respect reduced motion preferences +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} + +// Focus visible styles for keyboard navigation +:focus-visible { + outline: $focus-ring-width solid $focus-color; + outline-offset: $focus-ring-offset; +} + +// Remove default focus outline for mouse users +:focus:not(:focus-visible) { + outline: none; +} + +// Improve text rendering +body, +input, +button, +textarea, +select { + font-synthesis: none; + text-rendering: optimizeLegibility; +} + +// Ensure images are responsive by default +img, +picture, +video, +canvas, +svg { + display: block; + max-width: 100%; + height: auto; +} + +// Remove default button styles +button { + font: inherit; + color: inherit; + background: none; + border: none; + cursor: pointer; +} + +// Improve form element consistency +input, +button, +textarea, +select { + font: inherit; +} + +// Prevent text overflow +p, +h1, +h2, +h3, +h4, +h5, +h6 { + overflow-wrap: break-word; +} + +// Root stacking context for specific components +#root, +#__next { + isolation: isolate; +} diff --git a/styles/tokens/_colors.scss b/styles/tokens/_colors.scss new file mode 100644 index 0000000..b34f0d8 --- /dev/null +++ b/styles/tokens/_colors.scss @@ -0,0 +1,185 @@ +// ============================================ +// Color Design Tokens +// SalsaNor Beat Machine - SalsaNor Brand Colors +// Based on SalsaNor logo gradient palette +// ============================================ + +// ====== Background Gradients ====== + +// Dark base with subtle warmth to complement orange accents +$bg-gradient-cosmic: linear-gradient(135deg, #1a1410 0%, #2d1f1a 50%, #221814 100%); +$bg-color-dark: #1a1410; +$bg-color-mid: #2d1f1a; +$bg-color-light: #221814; + +// ====== Glass Material Colors ====== + +// Opacity values for glass surfaces +$glass-opacity-light: 0.1; +$glass-opacity-medium: 0.15; +$glass-opacity-dark: 0.2; + +// Backdrop blur levels +$glass-blur-sm: 10px; +$glass-blur-md: 20px; +$glass-blur-lg: 30px; + +// Border opacity for glass edges +$glass-border-opacity-light: 0.15; +$glass-border-opacity-medium: 0.2; +$glass-border-opacity-dark: 0.25; + +// ====== Primary Brand Colors (from SalsaNor Logo) ====== + +// Primary Orange - main brand color +$color-primary: #FF9933; +$color-primary-light: #FFB347; +$color-primary-dark: #E17856; + +// Golden Yellow - bright accent from logo +$color-golden: #FFC947; +$color-golden-light: #FFD966; +$color-golden-dark: #E6A500; + +// Coral/Peach - warm secondary from logo +$color-coral: #FF8C69; +$color-coral-light: #FFA885; +$color-coral-dark: #E07856; + +// Complementary accent (cool contrast to warm palette) +$color-accent-cool: #4A90E2; + +// Gradient accents based on logo +$gradient-primary: linear-gradient(135deg, $color-golden 0%, $color-primary 50%, $color-coral 100%); +$gradient-warm: linear-gradient(135deg, $color-primary 0%, $color-coral 100%); +$gradient-bright: linear-gradient(135deg, $color-golden 0%, $color-primary 100%); + +// ====== Semantic Colors ====== + +// Success - keeping green as it's universal +$color-success: #10b981; +$color-success-light: #34d399; +$color-success-dark: #059669; + +// Warning - using golden yellow from brand +$color-warning: $color-golden; +$color-warning-light: $color-golden-light; +$color-warning-dark: $color-golden-dark; + +// Error - warm red that complements orange palette +$color-error: #E74C3C; +$color-error-light: #EC7063; +$color-error-dark: #C0392B; + +// Info - using cool accent for contrast +$color-info: $color-accent-cool; +$color-info-light: #73B3F5; +$color-info-dark: #2471C9; + +// ====== Interactive State Colors ====== + +// Hover states +$hover-bg-opacity: 0.25; +$hover-border-opacity: 0.3; +$hover-lift: -2px; + +// Active states +$active-bg-opacity: 0.3; +$active-scale: 0.98; + +// Focus states - using golden yellow for visibility +$focus-color: $color-golden; +$focus-ring-opacity: 0.3; +$focus-ring-width: 2px; +$focus-ring-offset: 2px; + +// Disabled states +$disabled-opacity: 0.5; + +// ====== Text Colors ====== + +$text-primary: rgba(255, 255, 255, 0.95); +$text-secondary: rgba(255, 255, 255, 0.75); +$text-muted: rgba(255, 255, 255, 0.5); +$text-disabled: rgba(255, 255, 255, 0.3); + +// ====== Border Colors ====== + +$border-default: rgba(255, 153, 51, 0.15); // Subtle orange tint +$border-hover: rgba(255, 201, 71, 0.3); // Golden on hover +$border-focus: $color-golden; +$border-accent: rgba(255, 153, 51, 0.5); // Primary orange + +// ====== Export as CSS Custom Properties ====== + +:root { + // Background + --bg-gradient-cosmic: #{$bg-gradient-cosmic}; + --bg-color-dark: #{$bg-color-dark}; + --bg-color-mid: #{$bg-color-mid}; + --bg-color-light: #{$bg-color-light}; + + // Glass material + --glass-opacity-light: #{$glass-opacity-light}; + --glass-opacity-medium: #{$glass-opacity-medium}; + --glass-opacity-dark: #{$glass-opacity-dark}; + --glass-blur-sm: #{$glass-blur-sm}; + --glass-blur-md: #{$glass-blur-md}; + --glass-blur-lg: #{$glass-blur-lg}; + --glass-border-opacity-light: #{$glass-border-opacity-light}; + --glass-border-opacity-medium: #{$glass-border-opacity-medium}; + --glass-border-opacity-dark: #{$glass-border-opacity-dark}; + + // Primary brand colors + --color-primary: #{$color-primary}; + --color-primary-light: #{$color-primary-light}; + --color-primary-dark: #{$color-primary-dark}; + --color-golden: #{$color-golden}; + --color-golden-light: #{$color-golden-light}; + --color-golden-dark: #{$color-golden-dark}; + --color-coral: #{$color-coral}; + --color-coral-light: #{$color-coral-light}; + --color-coral-dark: #{$color-coral-dark}; + --color-accent-cool: #{$color-accent-cool}; + + // Gradients + --gradient-primary: #{$gradient-primary}; + --gradient-warm: #{$gradient-warm}; + --gradient-bright: #{$gradient-bright}; + + // Semantic colors + --color-success: #{$color-success}; + --color-success-light: #{$color-success-light}; + --color-success-dark: #{$color-success-dark}; + --color-warning: #{$color-warning}; + --color-warning-light: #{$color-warning-light}; + --color-warning-dark: #{$color-warning-dark}; + --color-error: #{$color-error}; + --color-error-light: #{$color-error-light}; + --color-error-dark: #{$color-error-dark}; + --color-info: #{$color-info}; + --color-info-light: #{$color-info-light}; + --color-info-dark: #{$color-info-dark}; + + // Interactive states + --hover-bg-opacity: #{$hover-bg-opacity}; + --hover-border-opacity: #{$hover-border-opacity}; + --active-bg-opacity: #{$active-bg-opacity}; + --focus-color: #{$focus-color}; + --focus-ring-opacity: #{$focus-ring-opacity}; + --focus-ring-width: #{$focus-ring-width}; + --focus-ring-offset: #{$focus-ring-offset}; + --disabled-opacity: #{$disabled-opacity}; + + // Text colors + --text-primary: #{$text-primary}; + --text-secondary: #{$text-secondary}; + --text-muted: #{$text-muted}; + --text-disabled: #{$text-disabled}; + + // Border colors + --border-default: #{$border-default}; + --border-hover: #{$border-hover}; + --border-focus: #{$border-focus}; + --border-accent: #{$border-accent}; +} diff --git a/styles/tokens/_shadows.scss b/styles/tokens/_shadows.scss new file mode 100644 index 0000000..aaafc8f --- /dev/null +++ b/styles/tokens/_shadows.scss @@ -0,0 +1,154 @@ +// ============================================ +// Shadow Design Tokens +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +// ====== Glass Shadow Variants ====== + +// Small glass shadow +$shadow-glass-sm: 0 2px 8px rgba(0, 0, 0, 0.1), + 0 1px 3px rgba(0, 0, 0, 0.05); + +// Medium glass shadow +$shadow-glass-md: 0 4px 16px rgba(0, 0, 0, 0.2), + 0 2px 8px rgba(0, 0, 0, 0.1); + +// Large glass shadow +$shadow-glass-lg: 0 8px 32px rgba(0, 0, 0, 0.3), + 0 4px 16px rgba(0, 0, 0, 0.15); + +// Extra large glass shadow +$shadow-glass-xl: 0 12px 48px rgba(0, 0, 0, 0.4), + 0 6px 24px rgba(0, 0, 0, 0.2); + +// ====== Glow Effects ====== + +// Cyan glow (for focus, solo states) +$glow-cyan-sm: 0 0 10px rgba(0, 245, 255, 0.4); +$glow-cyan-md: 0 0 15px rgba(0, 245, 255, 0.6); +$glow-cyan-lg: 0 0 20px rgba(0, 245, 255, 0.8); +$glow-cyan-xl: 0 0 30px rgba(0, 245, 255, 1); + +// Purple glow (for primary actions) +$glow-purple-sm: 0 0 10px rgba(102, 126, 234, 0.4); +$glow-purple-md: 0 0 15px rgba(102, 126, 234, 0.6); +$glow-purple-lg: 0 0 20px rgba(102, 126, 234, 0.8); +$glow-purple-xl: 0 0 30px rgba(102, 126, 234, 1); + +// Pink glow (for accents) +$glow-pink-sm: 0 0 10px rgba(240, 147, 251, 0.4); +$glow-pink-md: 0 0 15px rgba(240, 147, 251, 0.6); +$glow-pink-lg: 0 0 20px rgba(240, 147, 251, 0.8); +$glow-pink-xl: 0 0 30px rgba(240, 147, 251, 1); + +// Green glow (for success states) +$glow-green-sm: 0 0 10px rgba(16, 185, 129, 0.4); +$glow-green-md: 0 0 15px rgba(16, 185, 129, 0.6); +$glow-green-lg: 0 0 20px rgba(16, 185, 129, 0.8); +$glow-green-xl: 0 0 30px rgba(16, 185, 129, 1); + +// ====== Standard Shadows (fallback) ====== + +// Small shadow +$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); + +// Default shadow +$shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1), + 0 2px 4px -1px rgba(0, 0, 0, 0.06); + +// Large shadow +$shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1), + 0 4px 6px -2px rgba(0, 0, 0, 0.05); + +// Extra large shadow +$shadow-xl: 0 20px 25px -5px rgba(0, 0, 0, 0.1), + 0 10px 10px -5px rgba(0, 0, 0, 0.04); + +// 2X large shadow +$shadow-2xl: 0 25px 50px -12px rgba(0, 0, 0, 0.25); + +// Inner shadow +$shadow-inner: inset 0 2px 4px 0 rgba(0, 0, 0, 0.06); + +// ====== Backdrop Blur Levels ====== + +$backdrop-blur-sm: blur(4px); +$backdrop-blur-md: blur(12px); +$backdrop-blur-lg: blur(20px); +$backdrop-blur-xl: blur(40px); + +// ====== Combined Effects ====== + +// Glass card with glow on hover +$shadow-glass-card: $shadow-glass-lg; +$shadow-glass-card-hover: $shadow-glass-lg, $glow-purple-md; + +// Active step in sequencer +$shadow-step-active: $shadow-glass-sm, $glow-purple-md; + +// Playing step animation +$shadow-step-playing: $shadow-glass-md, $glow-cyan-lg; + +// Focus ring shadow +$shadow-focus: 0 0 0 3px rgba(0, 245, 255, 0.3); + +// Button primary shadow +$shadow-button-primary: 0 4px 15px rgba(102, 126, 234, 0.4); +$shadow-button-primary-hover: 0 6px 20px rgba(102, 126, 234, 0.5); + +// ====== Export as CSS Custom Properties ====== + +:root { + // Glass shadows + --shadow-glass-sm: #{$shadow-glass-sm}; + --shadow-glass-md: #{$shadow-glass-md}; + --shadow-glass-lg: #{$shadow-glass-lg}; + --shadow-glass-xl: #{$shadow-glass-xl}; + + // Cyan glows + --glow-cyan-sm: #{$glow-cyan-sm}; + --glow-cyan-md: #{$glow-cyan-md}; + --glow-cyan-lg: #{$glow-cyan-lg}; + --glow-cyan-xl: #{$glow-cyan-xl}; + + // Purple glows + --glow-purple-sm: #{$glow-purple-sm}; + --glow-purple-md: #{$glow-purple-md}; + --glow-purple-lg: #{$glow-purple-lg}; + --glow-purple-xl: #{$glow-purple-xl}; + + // Pink glows + --glow-pink-sm: #{$glow-pink-sm}; + --glow-pink-md: #{$glow-pink-md}; + --glow-pink-lg: #{$glow-pink-lg}; + --glow-pink-xl: #{$glow-pink-xl}; + + // Green glows + --glow-green-sm: #{$glow-green-sm}; + --glow-green-md: #{$glow-green-md}; + --glow-green-lg: #{$glow-green-lg}; + --glow-green-xl: #{$glow-green-xl}; + + // Standard shadows + --shadow-sm: #{$shadow-sm}; + --shadow-md: #{$shadow-md}; + --shadow-lg: #{$shadow-lg}; + --shadow-xl: #{$shadow-xl}; + --shadow-2xl: #{$shadow-2xl}; + --shadow-inner: #{$shadow-inner}; + + // Backdrop blur + --backdrop-blur-sm: #{$backdrop-blur-sm}; + --backdrop-blur-md: #{$backdrop-blur-md}; + --backdrop-blur-lg: #{$backdrop-blur-lg}; + --backdrop-blur-xl: #{$backdrop-blur-xl}; + + // Combined effects + --shadow-glass-card: #{$shadow-glass-card}; + --shadow-glass-card-hover: #{$shadow-glass-card-hover}; + --shadow-step-active: #{$shadow-step-active}; + --shadow-step-playing: #{$shadow-step-playing}; + --shadow-focus: #{$shadow-focus}; + --shadow-button-primary: #{$shadow-button-primary}; + --shadow-button-primary-hover: #{$shadow-button-primary-hover}; +} diff --git a/styles/tokens/_spacing.scss b/styles/tokens/_spacing.scss new file mode 100644 index 0000000..2c26dcb --- /dev/null +++ b/styles/tokens/_spacing.scss @@ -0,0 +1,215 @@ +// ============================================ +// Spacing & Layout Design Tokens +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +// ====== Spacing Scale (8px Grid) ====== + +$spacing: ( + 0: 0, + 1: 0.25rem, // 4px + 2: 0.5rem, // 8px + 3: 0.75rem, // 12px + 4: 1rem, // 16px + 5: 1.25rem, // 20px + 6: 1.5rem, // 24px + 7: 1.75rem, // 28px + 8: 2rem, // 32px + 10: 2.5rem, // 40px + 12: 3rem, // 48px + 14: 3.5rem, // 56px + 16: 4rem, // 64px + 20: 5rem, // 80px + 24: 6rem, // 96px + 32: 8rem, // 128px +); + +// Quick access variables +$spacing-0: map-get($spacing, 0); +$spacing-1: map-get($spacing, 1); +$spacing-2: map-get($spacing, 2); +$spacing-3: map-get($spacing, 3); +$spacing-4: map-get($spacing, 4); +$spacing-5: map-get($spacing, 5); +$spacing-6: map-get($spacing, 6); +$spacing-7: map-get($spacing, 7); +$spacing-8: map-get($spacing, 8); +$spacing-10: map-get($spacing, 10); +$spacing-12: map-get($spacing, 12); +$spacing-14: map-get($spacing, 14); +$spacing-16: map-get($spacing, 16); +$spacing-20: map-get($spacing, 20); +$spacing-24: map-get($spacing, 24); +$spacing-32: map-get($spacing, 32); + +// ====== Border Radius ====== + +$radius: ( + none: 0, + sm: 0.375rem, // 6px + md: 0.5rem, // 8px + lg: 0.75rem, // 12px + xl: 1rem, // 16px + 2xl: 1.5rem, // 24px + 3xl: 2rem, // 32px + full: 9999px, // Circular/pill +); + +// Quick access variables +$radius-none: map-get($radius, none); +$radius-sm: map-get($radius, sm); +$radius-md: map-get($radius, md); +$radius-lg: map-get($radius, lg); +$radius-xl: map-get($radius, xl); +$radius-2xl: map-get($radius, 2xl); +$radius-3xl: map-get($radius, 3xl); +$radius-full: map-get($radius, full); + +// ====== Container Sizes ====== + +$containers: ( + xs: 20rem, // 320px + sm: 24rem, // 384px + md: 28rem, // 448px + lg: 32rem, // 512px + xl: 36rem, // 576px + 2xl: 42rem, // 672px + 3xl: 48rem, // 768px + 4xl: 56rem, // 896px + 5xl: 64rem, // 1024px + 6xl: 72rem, // 1152px + 7xl: 80rem, // 1280px + full: 100%, +); + +// Quick access variables +$container-xs: map-get($containers, xs); +$container-sm: map-get($containers, sm); +$container-md: map-get($containers, md); +$container-lg: map-get($containers, lg); +$container-xl: map-get($containers, xl); +$container-2xl: map-get($containers, 2xl); +$container-3xl: map-get($containers, 3xl); +$container-4xl: map-get($containers, 4xl); +$container-5xl: map-get($containers, 5xl); +$container-6xl: map-get($containers, 6xl); +$container-7xl: map-get($containers, 7xl); + +// ====== Responsive Breakpoints ====== + +$breakpoints: ( + mobile: 480px, + tablet: 640px, + tablet-lg: 768px, + desktop: 1024px, + desktop-lg: 1280px, + wide: 1536px, +); + +// Quick access variables +$breakpoint-mobile: map-get($breakpoints, mobile); +$breakpoint-tablet: map-get($breakpoints, tablet); +$breakpoint-tablet-lg: map-get($breakpoints, tablet-lg); +$breakpoint-desktop: map-get($breakpoints, desktop); +$breakpoint-desktop-lg: map-get($breakpoints, desktop-lg); +$breakpoint-wide: map-get($breakpoints, wide); + +// ====== Grid Configuration ====== + +$grid-columns: 12; +$grid-gap: $spacing-6; +$grid-gap-mobile: $spacing-4; + +// ====== Z-Index Scale ====== + +$z-index: ( + behind: -1, + base: 0, + dropdown: 1000, + sticky: 1100, + fixed: 1200, + modal-backdrop: 1300, + modal: 1400, + popover: 1500, + tooltip: 1600, +); + +// Quick access variables +$z-behind: map-get($z-index, behind); +$z-base: map-get($z-index, base); +$z-dropdown: map-get($z-index, dropdown); +$z-sticky: map-get($z-index, sticky); +$z-fixed: map-get($z-index, fixed); +$z-modal-backdrop: map-get($z-index, modal-backdrop); +$z-modal: map-get($z-index, modal); +$z-popover: map-get($z-index, popover); +$z-tooltip: map-get($z-index, tooltip); + +// ====== Export as CSS Custom Properties ====== + +:root { + // Spacing scale + --spacing-0: #{$spacing-0}; + --spacing-1: #{$spacing-1}; + --spacing-2: #{$spacing-2}; + --spacing-3: #{$spacing-3}; + --spacing-4: #{$spacing-4}; + --spacing-5: #{$spacing-5}; + --spacing-6: #{$spacing-6}; + --spacing-7: #{$spacing-7}; + --spacing-8: #{$spacing-8}; + --spacing-10: #{$spacing-10}; + --spacing-12: #{$spacing-12}; + --spacing-14: #{$spacing-14}; + --spacing-16: #{$spacing-16}; + --spacing-20: #{$spacing-20}; + --spacing-24: #{$spacing-24}; + --spacing-32: #{$spacing-32}; + + // Border radius + --radius-none: #{$radius-none}; + --radius-sm: #{$radius-sm}; + --radius-md: #{$radius-md}; + --radius-lg: #{$radius-lg}; + --radius-xl: #{$radius-xl}; + --radius-2xl: #{$radius-2xl}; + --radius-3xl: #{$radius-3xl}; + --radius-full: #{$radius-full}; + + // Containers + --container-xs: #{$container-xs}; + --container-sm: #{$container-sm}; + --container-md: #{$container-md}; + --container-lg: #{$container-lg}; + --container-xl: #{$container-xl}; + --container-2xl: #{$container-2xl}; + --container-3xl: #{$container-3xl}; + --container-4xl: #{$container-4xl}; + --container-5xl: #{$container-5xl}; + --container-6xl: #{$container-6xl}; + --container-7xl: #{$container-7xl}; + + // Breakpoints (for JS access) + --breakpoint-mobile: #{$breakpoint-mobile}; + --breakpoint-tablet: #{$breakpoint-tablet}; + --breakpoint-tablet-lg: #{$breakpoint-tablet-lg}; + --breakpoint-desktop: #{$breakpoint-desktop}; + --breakpoint-desktop-lg: #{$breakpoint-desktop-lg}; + --breakpoint-wide: #{$breakpoint-wide}; + + // Grid + --grid-columns: #{$grid-columns}; + --grid-gap: #{$grid-gap}; + --grid-gap-mobile: #{$grid-gap-mobile}; + + // Z-index + --z-behind: #{$z-behind}; + --z-base: #{$z-base}; + --z-dropdown: #{$z-dropdown}; + --z-sticky: #{$z-sticky}; + --z-fixed: #{$z-fixed}; + --z-modal-backdrop: #{$z-modal-backdrop}; + --z-modal: #{$z-modal}; + --z-popover: #{$z-popover}; + --z-tooltip: #{$z-tooltip}; +} diff --git a/styles/tokens/_typography.scss b/styles/tokens/_typography.scss new file mode 100644 index 0000000..7165591 --- /dev/null +++ b/styles/tokens/_typography.scss @@ -0,0 +1,132 @@ +// ============================================ +// Typography Design Tokens +// SalsaNor Beat Machine - Glassmorphic Theme +// ============================================ + +// ====== Font Families ====== + +// UI Font Stack (Inter) +$font-family-ui: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', + 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', + 'Helvetica Neue', sans-serif; + +// Display Font (Merriweather) +$font-family-display: 'Merriweather', Georgia, 'Times New Roman', serif; + +// Monospace Font (Fira Code) +$font-family-mono: 'Fira Code', 'Monaco', 'Consolas', 'Courier New', monospace; + +// ====== Fluid Font Sizes (using clamp) ====== + +// Headings with responsive scaling +$font-size-h1: clamp(2rem, 4vw + 1rem, 3.5rem); // 32px - 56px +$font-size-h2: clamp(1.75rem, 3vw + 0.5rem, 2.5rem); // 28px - 40px +$font-size-h3: clamp(1.5rem, 2vw + 0.5rem, 2rem); // 24px - 32px +$font-size-h4: clamp(1.25rem, 1.5vw + 0.5rem, 1.75rem); // 20px - 28px +$font-size-h5: clamp(1.125rem, 1vw + 0.5rem, 1.5rem); // 18px - 24px +$font-size-h6: clamp(1rem, 1vw + 0.25rem, 1.25rem); // 16px - 20px + +// Body text +$font-size-body-large: clamp(1.125rem, 1vw + 0.5rem, 1.25rem); // 18px - 20px +$font-size-body: clamp(0.875rem, 0.5vw + 0.5rem, 1rem); // 14px - 16px +$font-size-body-small: clamp(0.8125rem, 0.5vw + 0.375rem, 0.9375rem); // 13px - 15px + +// UI elements +$font-size-small: clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem); // 12px - 14px +$font-size-xs: clamp(0.6875rem, 0.5vw + 0.125rem, 0.8125rem); // 11px - 13px + +// ====== Fixed Font Sizes (for specific cases) ====== + +$font-size-base: 1rem; // 16px +$font-size-lg: 1.125rem; // 18px +$font-size-xl: 1.25rem; // 20px +$font-size-2xl: 1.5rem; // 24px +$font-size-3xl: 1.875rem; // 30px +$font-size-4xl: 2.25rem; // 36px +$font-size-5xl: 3rem; // 48px +$font-size-6xl: 3.75rem; // 60px + +// ====== Font Weights ====== + +$font-weight-light: 300; +$font-weight-regular: 400; +$font-weight-medium: 500; +$font-weight-semibold: 600; +$font-weight-bold: 700; + +// ====== Line Heights ====== + +$line-height-tight: 1.2; +$line-height-snug: 1.375; +$line-height-normal: 1.5; +$line-height-relaxed: 1.6; +$line-height-loose: 1.8; + +// Specific for elements +$line-height-heading: $line-height-tight; +$line-height-body: $line-height-relaxed; +$line-height-ui: $line-height-normal; + +// ====== Letter Spacing ====== + +$letter-spacing-tight: -0.025em; +$letter-spacing-normal: 0; +$letter-spacing-wide: 0.025em; +$letter-spacing-wider: 0.05em; +$letter-spacing-widest: 0.1em; + +// ====== Export as CSS Custom Properties ====== + +:root { + // Font families + --font-family-ui: #{$font-family-ui}; + --font-family-display: #{$font-family-display}; + --font-family-mono: #{$font-family-mono}; + + // Fluid font sizes + --font-size-h1: #{$font-size-h1}; + --font-size-h2: #{$font-size-h2}; + --font-size-h3: #{$font-size-h3}; + --font-size-h4: #{$font-size-h4}; + --font-size-h5: #{$font-size-h5}; + --font-size-h6: #{$font-size-h6}; + --font-size-body-large: #{$font-size-body-large}; + --font-size-body: #{$font-size-body}; + --font-size-body-small: #{$font-size-body-small}; + --font-size-small: #{$font-size-small}; + --font-size-xs: #{$font-size-xs}; + + // Fixed font sizes + --font-size-base: #{$font-size-base}; + --font-size-lg: #{$font-size-lg}; + --font-size-xl: #{$font-size-xl}; + --font-size-2xl: #{$font-size-2xl}; + --font-size-3xl: #{$font-size-3xl}; + --font-size-4xl: #{$font-size-4xl}; + --font-size-5xl: #{$font-size-5xl}; + --font-size-6xl: #{$font-size-6xl}; + + // Font weights + --font-weight-light: #{$font-weight-light}; + --font-weight-regular: #{$font-weight-regular}; + --font-weight-medium: #{$font-weight-medium}; + --font-weight-semibold: #{$font-weight-semibold}; + --font-weight-bold: #{$font-weight-bold}; + + // Line heights + --line-height-tight: #{$line-height-tight}; + --line-height-snug: #{$line-height-snug}; + --line-height-normal: #{$line-height-normal}; + --line-height-relaxed: #{$line-height-relaxed}; + --line-height-loose: #{$line-height-loose}; + --line-height-heading: #{$line-height-heading}; + --line-height-body: #{$line-height-body}; + --line-height-ui: #{$line-height-ui}; + + // Letter spacing + --letter-spacing-tight: #{$letter-spacing-tight}; + --letter-spacing-normal: #{$letter-spacing-normal}; + --letter-spacing-wide: #{$letter-spacing-wide}; + --letter-spacing-wider: #{$letter-spacing-wider}; + --letter-spacing-widest: #{$letter-spacing-widest}; +} diff --git a/styles/tokens/tokens.json b/styles/tokens/tokens.json new file mode 100644 index 0000000..2fddeea --- /dev/null +++ b/styles/tokens/tokens.json @@ -0,0 +1,153 @@ +{ + "colors": { + "background": { + "dark": "#0f0c29", + "mid": "#302b63", + "light": "#24243e", + "gradient": "linear-gradient(135deg, #0f0c29 0%, #302b63 50%, #24243e 100%)" + }, + "glass": { + "opacityLight": 0.1, + "opacityMedium": 0.15, + "opacityDark": 0.2, + "blurSm": "10px", + "blurMd": "20px", + "blurLg": "30px" + }, + "accent": { + "primary": "#667eea", + "primaryLight": "#8b9fef", + "primaryDark": "#4d5ed7", + "cyan": "#00f5ff", + "cyanLight": "#4dfbff", + "cyanDark": "#00c7d4", + "pink": "#f093fb", + "pinkLight": "#f5b5fc", + "pinkDark": "#e76af0", + "purpleAccent": "#764ba2" + }, + "semantic": { + "success": "#10b981", + "successLight": "#34d399", + "successDark": "#059669", + "warning": "#f59e0b", + "warningLight": "#fbbf24", + "warningDark": "#d97706", + "error": "#ef4444", + "errorLight": "#f87171", + "errorDark": "#dc2626", + "info": "#3b82f6", + "infoLight": "#60a5fa", + "infoDark": "#2563eb" + }, + "text": { + "primary": "rgba(255, 255, 255, 0.95)", + "secondary": "rgba(255, 255, 255, 0.75)", + "muted": "rgba(255, 255, 255, 0.5)", + "disabled": "rgba(255, 255, 255, 0.3)" + }, + "border": { + "default": "rgba(255, 255, 255, 0.15)", + "hover": "rgba(255, 255, 255, 0.3)", + "focus": "#00f5ff", + "accent": "rgba(102, 126, 234, 0.5)" + } + }, + "spacing": { + "0": "0", + "1": "0.25rem", + "2": "0.5rem", + "3": "0.75rem", + "4": "1rem", + "5": "1.25rem", + "6": "1.5rem", + "7": "1.75rem", + "8": "2rem", + "10": "2.5rem", + "12": "3rem", + "14": "3.5rem", + "16": "4rem", + "20": "5rem", + "24": "6rem", + "32": "8rem" + }, + "typography": { + "fontFamily": { + "ui": "'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', sans-serif", + "display": "'Merriweather', Georgia, 'Times New Roman', serif", + "mono": "'Fira Code', 'Monaco', 'Consolas', 'Courier New', monospace" + }, + "fontSize": { + "xs": "clamp(0.6875rem, 0.5vw + 0.125rem, 0.8125rem)", + "sm": "clamp(0.75rem, 0.5vw + 0.25rem, 0.875rem)", + "base": "clamp(0.875rem, 0.5vw + 0.5rem, 1rem)", + "lg": "clamp(1.125rem, 1vw + 0.5rem, 1.25rem)", + "xl": "clamp(1.25rem, 1.5vw + 0.5rem, 1.75rem)", + "2xl": "clamp(1.5rem, 2vw + 0.5rem, 2rem)", + "3xl": "clamp(1.75rem, 3vw + 0.5rem, 2.5rem)", + "4xl": "clamp(2rem, 4vw + 1rem, 3.5rem)" + }, + "fontWeight": { + "light": 300, + "regular": 400, + "medium": 500, + "semibold": 600, + "bold": 700 + }, + "lineHeight": { + "tight": 1.2, + "snug": 1.375, + "normal": 1.5, + "relaxed": 1.6, + "loose": 1.8 + }, + "letterSpacing": { + "tight": "-0.025em", + "normal": "0", + "wide": "0.025em", + "wider": "0.05em", + "widest": "0.1em" + } + }, + "borderRadius": { + "none": "0", + "sm": "0.375rem", + "md": "0.5rem", + "lg": "0.75rem", + "xl": "1rem", + "2xl": "1.5rem", + "3xl": "2rem", + "full": "9999px" + }, + "breakpoints": { + "mobile": "480px", + "tablet": "640px", + "tabletLg": "768px", + "desktop": "1024px", + "desktopLg": "1280px", + "wide": "1536px" + }, + "shadows": { + "glassSm": "0 2px 8px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.05)", + "glassMd": "0 4px 16px rgba(0, 0, 0, 0.2), 0 2px 8px rgba(0, 0, 0, 0.1)", + "glassLg": "0 8px 32px rgba(0, 0, 0, 0.3), 0 4px 16px rgba(0, 0, 0, 0.15)", + "glassXl": "0 12px 48px rgba(0, 0, 0, 0.4), 0 6px 24px rgba(0, 0, 0, 0.2)", + "glowCyan": "0 0 15px rgba(0, 245, 255, 0.6)", + "glowPurple": "0 0 15px rgba(102, 126, 234, 0.6)", + "glowPink": "0 0 15px rgba(240, 147, 251, 0.6)", + "focus": "0 0 0 3px rgba(0, 245, 255, 0.3)" + }, + "animation": { + "duration": { + "fast": "150ms", + "normal": "250ms", + "slow": "400ms" + }, + "easing": { + "standard": "cubic-bezier(0.4, 0, 0.2, 1)", + "easeOut": "cubic-bezier(0, 0, 0.2, 1)", + "easeIn": "cubic-bezier(0.4, 0, 1, 1)", + "spring": "cubic-bezier(0.68, -0.55, 0.265, 1.55)" + } + } +} diff --git a/tsconfig.json b/tsconfig.json index 29ff76e..9729e20 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,7 +1,11 @@ { "compilerOptions": { "target": "es5", - "lib": ["dom", "dom.iterable", "esnext"], + "lib": [ + "dom", + "dom.iterable", + "esnext" + ], "allowJs": true, "skipLibCheck": true, "strict": true, @@ -13,8 +17,16 @@ "moduleResolution": "node", "resolveJsonModule": true, "isolatedModules": true, - "jsx": "preserve" + "jsx": "preserve", + "incremental": true }, - "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"], - "exclude": ["node_modules"] + "include": [ + "next-env.d.ts", + "**/*.ts", + "**/*.tsx" + ], + "exclude": [ + "node_modules", + "vite.config.ts" + ] } diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..4bebeb6 --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,40 @@ +import { defineConfig } from 'vite'; +import react from '@vitejs/plugin-react'; +import path from 'path'; + +export default defineConfig({ + plugins: [react()], + define: { + 'process.env': '{}', + 'process.env.NODE_ENV': '"production"', + 'global': 'window', + }, + build: { + outDir: 'public', + emptyOutDir: false, // Don't delete assets when building + lib: { + entry: path.resolve(__dirname, 'src/widget/widget-entry.tsx'), + name: 'BeatMachineWidget', + fileName: () => { + // Generate timestamp-based version for cache busting + const timestamp = Date.now(); + return `widget.js?v=${timestamp}`; + }, + formats: ['iife'], + }, + rollupOptions: { + external: [], + output: { + globals: {}, + extend: true, // Extend window object instead of replacing + // Generate clean widget.js filename (query params are stripped) + entryFileNames: 'widget.js', + }, + }, + }, + resolve: { + alias: { + '@': path.resolve(__dirname, './src'), + }, + }, +}); diff --git a/widget.js b/widget.js new file mode 100644 index 0000000..f439091 --- /dev/null +++ b/widget.js @@ -0,0 +1,201 @@ +(function(Bs){"use strict";function Us(i){return i&&i.__esModule&&Object.prototype.hasOwnProperty.call(i,"default")?i.default:i}var fl={exports:{}},Ar={},dl={exports:{}},G={};var Fs;function up(){if(Fs)return G;Fs=1;var i=Symbol.for("react.element"),l=Symbol.for("react.portal"),o=Symbol.for("react.fragment"),s=Symbol.for("react.strict_mode"),c=Symbol.for("react.profiler"),d=Symbol.for("react.provider"),p=Symbol.for("react.context"),g=Symbol.for("react.forward_ref"),y=Symbol.for("react.suspense"),P=Symbol.for("react.memo"),C=Symbol.for("react.lazy"),R=Symbol.iterator;function j(_){return _===null||typeof _!="object"?null:(_=R&&_[R]||_["@@iterator"],typeof _=="function"?_:null)}var Y={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},le=Object.assign,H={};function K(_,x,Q){this.props=_,this.context=x,this.refs=H,this.updater=Q||Y}K.prototype.isReactComponent={},K.prototype.setState=function(_,x){if(typeof _!="object"&&typeof _!="function"&&_!=null)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,_,x,"setState")},K.prototype.forceUpdate=function(_){this.updater.enqueueForceUpdate(this,_,"forceUpdate")};function Fe(){}Fe.prototype=K.prototype;function Ve(_,x,Q){this.props=_,this.context=x,this.refs=H,this.updater=Q||Y}var Ne=Ve.prototype=new Fe;Ne.constructor=Ve,le(Ne,K.prototype),Ne.isPureReactComponent=!0;var re=Array.isArray,Se=Object.prototype.hasOwnProperty,he={current:null},Te={key:!0,ref:!0,__self:!0,__source:!0};function at(_,x,Q){var X,ee={},te=null,se=null;if(x!=null)for(X in x.ref!==void 0&&(se=x.ref),x.key!==void 0&&(te=""+x.key),x)Se.call(x,X)&&!Te.hasOwnProperty(X)&&(ee[X]=x[X]);var ie=arguments.length-2;if(ie===1)ee.children=Q;else if(1>>1,x=M[_];if(0>>1;_c(ee,D))tec(se,ee)?(M[_]=se,M[te]=D,_=te):(M[_]=ee,M[X]=D,_=X);else if(tec(se,D))M[_]=se,M[te]=D,_=te;else break e}}return V}function c(M,V){var D=M.sortIndex-V.sortIndex;return D!==0?D:M.id-V.id}if(typeof performance=="object"&&typeof performance.now=="function"){var d=performance;i.unstable_now=function(){return d.now()}}else{var p=Date,g=p.now();i.unstable_now=function(){return p.now()-g}}var y=[],P=[],C=1,R=null,j=3,Y=!1,le=!1,H=!1,K=typeof setTimeout=="function"?setTimeout:null,Fe=typeof clearTimeout=="function"?clearTimeout:null,Ve=typeof setImmediate<"u"?setImmediate:null;typeof navigator<"u"&&navigator.scheduling!==void 0&&navigator.scheduling.isInputPending!==void 0&&navigator.scheduling.isInputPending.bind(navigator.scheduling);function Ne(M){for(var V=o(P);V!==null;){if(V.callback===null)s(P);else if(V.startTime<=M)s(P),V.sortIndex=V.expirationTime,l(y,V);else break;V=o(P)}}function re(M){if(H=!1,Ne(M),!le)if(o(y)!==null)le=!0,$e(Se);else{var V=o(P);V!==null&&ge(re,V.startTime-M)}}function Se(M,V){le=!1,H&&(H=!1,Fe(at),at=-1),Y=!0;var D=j;try{for(Ne(V),R=o(y);R!==null&&(!(R.expirationTime>V)||M&&!bn());){var _=R.callback;if(typeof _=="function"){R.callback=null,j=R.priorityLevel;var x=_(R.expirationTime<=V);V=i.unstable_now(),typeof x=="function"?R.callback=x:R===o(y)&&s(y),Ne(V)}else s(y);R=o(y)}if(R!==null)var Q=!0;else{var X=o(P);X!==null&&ge(re,X.startTime-V),Q=!1}return Q}finally{R=null,j=D,Y=!1}}var he=!1,Te=null,at=-1,ln=5,Qt=-1;function bn(){return!(i.unstable_now()-QtM||125_?(M.sortIndex=D,l(P,M),o(y)===null&&M===o(P)&&(H?(Fe(at),at=-1):H=!0,ge(re,D-_))):(M.sortIndex=x,l(y,M),le||Y||(le=!0,$e(Se))),M},i.unstable_shouldYield=bn,i.unstable_wrapCallback=function(M){var V=j;return function(){var D=j;j=V;try{return M.apply(this,arguments)}finally{j=D}}}})(ml)),ml}var Ks;function fp(){return Ks||(Ks=1,vl.exports=cp()),vl.exports}var Qs;function dp(){if(Qs)return Ue;Qs=1;var i=Ci(),l=fp();function o(e){for(var t="https://reactjs.org/docs/error-decoder.html?invariant="+e,n=1;n"u"||typeof window.document>"u"||typeof window.document.createElement>"u"),y=Object.prototype.hasOwnProperty,P=/^[:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD][:A-Z_a-z\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02FF\u0370-\u037D\u037F-\u1FFF\u200C-\u200D\u2070-\u218F\u2C00-\u2FEF\u3001-\uD7FF\uF900-\uFDCF\uFDF0-\uFFFD\-.0-9\u00B7\u0300-\u036F\u203F-\u2040]*$/,C={},R={};function j(e){return y.call(R,e)?!0:y.call(C,e)?!1:P.test(e)?R[e]=!0:(C[e]=!0,!1)}function Y(e,t,n,r){if(n!==null&&n.type===0)return!1;switch(typeof t){case"function":case"symbol":return!0;case"boolean":return r?!1:n!==null?!n.acceptsBooleans:(e=e.toLowerCase().slice(0,5),e!=="data-"&&e!=="aria-");default:return!1}}function le(e,t,n,r){if(t===null||typeof t>"u"||Y(e,t,n,r))return!0;if(r)return!1;if(n!==null)switch(n.type){case 3:return!t;case 4:return t===!1;case 5:return isNaN(t);case 6:return isNaN(t)||1>t}return!1}function H(e,t,n,r,u,a,f){this.acceptsBooleans=t===2||t===3||t===4,this.attributeName=r,this.attributeNamespace=u,this.mustUseProperty=n,this.propertyName=e,this.type=t,this.sanitizeURL=a,this.removeEmptyString=f}var K={};"children dangerouslySetInnerHTML defaultValue defaultChecked innerHTML suppressContentEditableWarning suppressHydrationWarning style".split(" ").forEach(function(e){K[e]=new H(e,0,!1,e,null,!1,!1)}),[["acceptCharset","accept-charset"],["className","class"],["htmlFor","for"],["httpEquiv","http-equiv"]].forEach(function(e){var t=e[0];K[t]=new H(t,1,!1,e[1],null,!1,!1)}),["contentEditable","draggable","spellCheck","value"].forEach(function(e){K[e]=new H(e,2,!1,e.toLowerCase(),null,!1,!1)}),["autoReverse","externalResourcesRequired","focusable","preserveAlpha"].forEach(function(e){K[e]=new H(e,2,!1,e,null,!1,!1)}),"allowFullScreen async autoFocus autoPlay controls default defer disabled disablePictureInPicture disableRemotePlayback formNoValidate hidden loop noModule noValidate open playsInline readOnly required reversed scoped seamless itemScope".split(" ").forEach(function(e){K[e]=new H(e,3,!1,e.toLowerCase(),null,!1,!1)}),["checked","multiple","muted","selected"].forEach(function(e){K[e]=new H(e,3,!0,e,null,!1,!1)}),["capture","download"].forEach(function(e){K[e]=new H(e,4,!1,e,null,!1,!1)}),["cols","rows","size","span"].forEach(function(e){K[e]=new H(e,6,!1,e,null,!1,!1)}),["rowSpan","start"].forEach(function(e){K[e]=new H(e,5,!1,e.toLowerCase(),null,!1,!1)});var Fe=/[\-:]([a-z])/g;function Ve(e){return e[1].toUpperCase()}"accent-height alignment-baseline arabic-form baseline-shift cap-height clip-path clip-rule color-interpolation color-interpolation-filters color-profile color-rendering dominant-baseline enable-background fill-opacity fill-rule flood-color flood-opacity font-family font-size font-size-adjust font-stretch font-style font-variant font-weight glyph-name glyph-orientation-horizontal glyph-orientation-vertical horiz-adv-x horiz-origin-x image-rendering letter-spacing lighting-color marker-end marker-mid marker-start overline-position overline-thickness paint-order panose-1 pointer-events rendering-intent shape-rendering stop-color stop-opacity strikethrough-position strikethrough-thickness stroke-dasharray stroke-dashoffset stroke-linecap stroke-linejoin stroke-miterlimit stroke-opacity stroke-width text-anchor text-decoration text-rendering underline-position underline-thickness unicode-bidi unicode-range units-per-em v-alphabetic v-hanging v-ideographic v-mathematical vector-effect vert-adv-y vert-origin-x vert-origin-y word-spacing writing-mode xmlns:xlink x-height".split(" ").forEach(function(e){var t=e.replace(Fe,Ve);K[t]=new H(t,1,!1,e,null,!1,!1)}),"xlink:actuate xlink:arcrole xlink:role xlink:show xlink:title xlink:type".split(" ").forEach(function(e){var t=e.replace(Fe,Ve);K[t]=new H(t,1,!1,e,"http://www.w3.org/1999/xlink",!1,!1)}),["xml:base","xml:lang","xml:space"].forEach(function(e){var t=e.replace(Fe,Ve);K[t]=new H(t,1,!1,e,"http://www.w3.org/XML/1998/namespace",!1,!1)}),["tabIndex","crossOrigin"].forEach(function(e){K[e]=new H(e,1,!1,e.toLowerCase(),null,!1,!1)}),K.xlinkHref=new H("xlinkHref",1,!1,"xlink:href","http://www.w3.org/1999/xlink",!0,!1),["src","href","action","formAction"].forEach(function(e){K[e]=new H(e,1,!1,e.toLowerCase(),null,!0,!0)});function Ne(e,t,n,r){var u=K.hasOwnProperty(t)?K[t]:null;(u!==null?u.type!==0:r||!(2h||u[f]!==a[h]){var v=` +`+u[f].replace(" at new "," at ");return e.displayName&&v.includes("")&&(v=v.replace("",e.displayName)),v}while(1<=f&&0<=h);break}}}finally{Q=!1,Error.prepareStackTrace=n}return(e=e?e.displayName||e.name:"")?x(e):""}function ee(e){switch(e.tag){case 5:return x(e.type);case 16:return x("Lazy");case 13:return x("Suspense");case 19:return x("SuspenseList");case 0:case 2:case 15:return e=X(e.type,!1),e;case 11:return e=X(e.type.render,!1),e;case 1:return e=X(e.type,!0),e;default:return""}}function te(e){if(e==null)return null;if(typeof e=="function")return e.displayName||e.name||null;if(typeof e=="string")return e;switch(e){case Te:return"Fragment";case he:return"Portal";case ln:return"Profiler";case at:return"StrictMode";case Je:return"Suspense";case St:return"SuspenseList"}if(typeof e=="object")switch(e.$$typeof){case bn:return(e.displayName||"Context")+".Consumer";case Qt:return(e._context.displayName||"Context")+".Provider";case jt:var t=e.render;return e=e.displayName,e||(e=t.displayName||t.name||"",e=e!==""?"ForwardRef("+e+")":"ForwardRef"),e;case bt:return t=e.displayName||null,t!==null?t:te(e.type)||"Memo";case $e:t=e._payload,e=e._init;try{return te(e(t))}catch{}}return null}function se(e){var t=e.type;switch(e.tag){case 24:return"Cache";case 9:return(t.displayName||"Context")+".Consumer";case 10:return(t._context.displayName||"Context")+".Provider";case 18:return"DehydratedFragment";case 11:return e=t.render,e=e.displayName||e.name||"",t.displayName||(e!==""?"ForwardRef("+e+")":"ForwardRef");case 7:return"Fragment";case 5:return t;case 4:return"Portal";case 3:return"Root";case 6:return"Text";case 16:return te(t);case 8:return t===at?"StrictMode":"Mode";case 22:return"Offscreen";case 12:return"Profiler";case 21:return"Scope";case 13:return"Suspense";case 19:return"SuspenseList";case 25:return"TracingMarker";case 1:case 0:case 17:case 2:case 14:case 15:if(typeof t=="function")return t.displayName||t.name||null;if(typeof t=="string")return t}return null}function ie(e){switch(typeof e){case"boolean":case"number":case"string":case"undefined":return e;case"object":return e;default:return""}}function de(e){var t=e.type;return(e=e.nodeName)&&e.toLowerCase()==="input"&&(t==="checkbox"||t==="radio")}function Ze(e){var t=de(e)?"checked":"value",n=Object.getOwnPropertyDescriptor(e.constructor.prototype,t),r=""+e[t];if(!e.hasOwnProperty(t)&&typeof n<"u"&&typeof n.get=="function"&&typeof n.set=="function"){var u=n.get,a=n.set;return Object.defineProperty(e,t,{configurable:!0,get:function(){return u.call(this)},set:function(f){r=""+f,a.call(this,f)}}),Object.defineProperty(e,t,{enumerable:n.enumerable}),{getValue:function(){return r},setValue:function(f){r=""+f},stopTracking:function(){e._valueTracker=null,delete e[t]}}}}function eo(e){e._valueTracker||(e._valueTracker=Ze(e))}function dc(e){if(!e)return!1;var t=e._valueTracker;if(!t)return!0;var n=t.getValue(),r="";return e&&(r=de(e)?e.checked?"true":"false":e.value),e=r,e!==n?(t.setValue(e),!0):!1}function to(e){if(e=e||(typeof document<"u"?document:void 0),typeof e>"u")return null;try{return e.activeElement||e.body}catch{return e.body}}function Kl(e,t){var n=t.checked;return D({},t,{defaultChecked:void 0,defaultValue:void 0,value:void 0,checked:n??e._wrapperState.initialChecked})}function pc(e,t){var n=t.defaultValue==null?"":t.defaultValue,r=t.checked!=null?t.checked:t.defaultChecked;n=ie(t.value!=null?t.value:n),e._wrapperState={initialChecked:r,initialValue:n,controlled:t.type==="checkbox"||t.type==="radio"?t.checked!=null:t.value!=null}}function hc(e,t){t=t.checked,t!=null&&Ne(e,"checked",t,!1)}function Ql(e,t){hc(e,t);var n=ie(t.value),r=t.type;if(n!=null)r==="number"?(n===0&&e.value===""||e.value!=n)&&(e.value=""+n):e.value!==""+n&&(e.value=""+n);else if(r==="submit"||r==="reset"){e.removeAttribute("value");return}t.hasOwnProperty("value")?Gl(e,t.type,n):t.hasOwnProperty("defaultValue")&&Gl(e,t.type,ie(t.defaultValue)),t.checked==null&&t.defaultChecked!=null&&(e.defaultChecked=!!t.defaultChecked)}function vc(e,t,n){if(t.hasOwnProperty("value")||t.hasOwnProperty("defaultValue")){var r=t.type;if(!(r!=="submit"&&r!=="reset"||t.value!==void 0&&t.value!==null))return;t=""+e._wrapperState.initialValue,n||t===e.value||(e.value=t),e.defaultValue=t}n=e.name,n!==""&&(e.name=""),e.defaultChecked=!!e._wrapperState.initialChecked,n!==""&&(e.name=n)}function Gl(e,t,n){(t!=="number"||to(e.ownerDocument)!==e)&&(n==null?e.defaultValue=""+e._wrapperState.initialValue:e.defaultValue!==""+n&&(e.defaultValue=""+n))}var Vr=Array.isArray;function rr(e,t,n,r){if(e=e.options,t){t={};for(var u=0;u"+t.valueOf().toString()+"",t=no.firstChild;e.firstChild;)e.removeChild(e.firstChild);for(;t.firstChild;)e.appendChild(t.firstChild)}});function $r(e,t){if(t){var n=e.firstChild;if(n&&n===e.lastChild&&n.nodeType===3){n.nodeValue=t;return}}e.textContent=t}var Wr={animationIterationCount:!0,aspectRatio:!0,borderImageOutset:!0,borderImageSlice:!0,borderImageWidth:!0,boxFlex:!0,boxFlexGroup:!0,boxOrdinalGroup:!0,columnCount:!0,columns:!0,flex:!0,flexGrow:!0,flexPositive:!0,flexShrink:!0,flexNegative:!0,flexOrder:!0,gridArea:!0,gridRow:!0,gridRowEnd:!0,gridRowSpan:!0,gridRowStart:!0,gridColumn:!0,gridColumnEnd:!0,gridColumnSpan:!0,gridColumnStart:!0,fontWeight:!0,lineClamp:!0,lineHeight:!0,opacity:!0,order:!0,orphans:!0,tabSize:!0,widows:!0,zIndex:!0,zoom:!0,fillOpacity:!0,floodOpacity:!0,stopOpacity:!0,strokeDasharray:!0,strokeDashoffset:!0,strokeMiterlimit:!0,strokeOpacity:!0,strokeWidth:!0},Dv=["Webkit","ms","Moz","O"];Object.keys(Wr).forEach(function(e){Dv.forEach(function(t){t=t+e.charAt(0).toUpperCase()+e.substring(1),Wr[t]=Wr[e]})});function Sc(e,t,n){return t==null||typeof t=="boolean"||t===""?"":n||typeof t!="number"||t===0||Wr.hasOwnProperty(e)&&Wr[e]?(""+t).trim():t+"px"}function Ec(e,t){e=e.style;for(var n in t)if(t.hasOwnProperty(n)){var r=n.indexOf("--")===0,u=Sc(n,t[n],r);n==="float"&&(n="cssFloat"),r?e.setProperty(n,u):e[n]=u}}var zv=D({menuitem:!0},{area:!0,base:!0,br:!0,col:!0,embed:!0,hr:!0,img:!0,input:!0,keygen:!0,link:!0,meta:!0,param:!0,source:!0,track:!0,wbr:!0});function ql(e,t){if(t){if(zv[e]&&(t.children!=null||t.dangerouslySetInnerHTML!=null))throw Error(o(137,e));if(t.dangerouslySetInnerHTML!=null){if(t.children!=null)throw Error(o(60));if(typeof t.dangerouslySetInnerHTML!="object"||!("__html"in t.dangerouslySetInnerHTML))throw Error(o(61))}if(t.style!=null&&typeof t.style!="object")throw Error(o(62))}}function Jl(e,t){if(e.indexOf("-")===-1)return typeof t.is=="string";switch(e){case"annotation-xml":case"color-profile":case"font-face":case"font-face-src":case"font-face-uri":case"font-face-format":case"font-face-name":case"missing-glyph":return!1;default:return!0}}var Zl=null;function eu(e){return e=e.target||e.srcElement||window,e.correspondingUseElement&&(e=e.correspondingUseElement),e.nodeType===3?e.parentNode:e}var tu=null,ir=null,or=null;function kc(e){if(e=di(e)){if(typeof tu!="function")throw Error(o(280));var t=e.stateNode;t&&(t=Po(t),tu(e.stateNode,e.type,t))}}function xc(e){ir?or?or.push(e):or=[e]:ir=e}function Oc(){if(ir){var e=ir,t=or;if(or=ir=null,kc(e),t)for(e=0;e>>=0,e===0?32:31-(Gv(e)/Yv|0)|0}var uo=64,so=4194304;function Gr(e){switch(e&-e){case 1:return 1;case 2:return 2;case 4:return 4;case 8:return 8;case 16:return 16;case 32:return 32;case 64:case 128:case 256:case 512:case 1024:case 2048:case 4096:case 8192:case 16384:case 32768:case 65536:case 131072:case 262144:case 524288:case 1048576:case 2097152:return e&4194240;case 4194304:case 8388608:case 16777216:case 33554432:case 67108864:return e&130023424;case 134217728:return 134217728;case 268435456:return 268435456;case 536870912:return 536870912;case 1073741824:return 1073741824;default:return e}}function ao(e,t){var n=e.pendingLanes;if(n===0)return 0;var r=0,u=e.suspendedLanes,a=e.pingedLanes,f=n&268435455;if(f!==0){var h=f&~u;h!==0?r=Gr(h):(a&=f,a!==0&&(r=Gr(a)))}else f=n&~u,f!==0?r=Gr(f):a!==0&&(r=Gr(a));if(r===0)return 0;if(t!==0&&t!==r&&(t&u)===0&&(u=r&-r,a=t&-t,u>=a||u===16&&(a&4194240)!==0))return t;if((r&4)!==0&&(r|=n&16),t=e.entangledLanes,t!==0)for(e=e.entanglements,t&=r;0n;n++)t.push(e);return t}function Yr(e,t,n){e.pendingLanes|=t,t!==536870912&&(e.suspendedLanes=0,e.pingedLanes=0),e=e.eventTimes,t=31-Et(t),e[t]=n}function Zv(e,t){var n=e.pendingLanes&~t;e.pendingLanes=t,e.suspendedLanes=0,e.pingedLanes=0,e.expiredLanes&=t,e.mutableReadLanes&=t,e.entangledLanes&=t,t=e.entanglements;var r=e.eventTimes;for(e=e.expirationTimes;0=ri),Zc=" ",ef=!1;function tf(e,t){switch(e){case"keyup":return Cm.indexOf(t.keyCode)!==-1;case"keydown":return t.keyCode!==229;case"keypress":case"mousedown":case"focusout":return!0;default:return!1}}function nf(e){return e=e.detail,typeof e=="object"&&"data"in e?e.data:null}var sr=!1;function Nm(e,t){switch(e){case"compositionend":return nf(t);case"keypress":return t.which!==32?null:(ef=!0,Zc);case"textInput":return e=t.data,e===Zc&&ef?null:e;default:return null}}function Tm(e,t){if(sr)return e==="compositionend"||!_u&&tf(e,t)?(e=Qc(),vo=pu=fn=null,sr=!1,e):null;switch(e){case"paste":return null;case"keypress":if(!(t.ctrlKey||t.altKey||t.metaKey)||t.ctrlKey&&t.altKey){if(t.char&&1=t)return{node:n,offset:t-e};e=r}e:{for(;n;){if(n.nextSibling){n=n.nextSibling;break e}n=n.parentNode}n=void 0}n=cf(n)}}function df(e,t){return e&&t?e===t?!0:e&&e.nodeType===3?!1:t&&t.nodeType===3?df(e,t.parentNode):"contains"in e?e.contains(t):e.compareDocumentPosition?!!(e.compareDocumentPosition(t)&16):!1:!1}function pf(){for(var e=window,t=to();t instanceof e.HTMLIFrameElement;){try{var n=typeof t.contentWindow.location.href=="string"}catch{n=!1}if(n)e=t.contentWindow;else break;t=to(e.document)}return t}function Eu(e){var t=e&&e.nodeName&&e.nodeName.toLowerCase();return t&&(t==="input"&&(e.type==="text"||e.type==="search"||e.type==="tel"||e.type==="url"||e.type==="password")||t==="textarea"||e.contentEditable==="true")}function Bm(e){var t=pf(),n=e.focusedElem,r=e.selectionRange;if(t!==n&&n&&n.ownerDocument&&df(n.ownerDocument.documentElement,n)){if(r!==null&&Eu(n)){if(t=r.start,e=r.end,e===void 0&&(e=t),"selectionStart"in n)n.selectionStart=t,n.selectionEnd=Math.min(e,n.value.length);else if(e=(t=n.ownerDocument||document)&&t.defaultView||window,e.getSelection){e=e.getSelection();var u=n.textContent.length,a=Math.min(r.start,u);r=r.end===void 0?a:Math.min(r.end,u),!e.extend&&a>r&&(u=r,r=a,a=u),u=ff(n,a);var f=ff(n,r);u&&f&&(e.rangeCount!==1||e.anchorNode!==u.node||e.anchorOffset!==u.offset||e.focusNode!==f.node||e.focusOffset!==f.offset)&&(t=t.createRange(),t.setStart(u.node,u.offset),e.removeAllRanges(),a>r?(e.addRange(t),e.extend(f.node,f.offset)):(t.setEnd(f.node,f.offset),e.addRange(t)))}}for(t=[],e=n;e=e.parentNode;)e.nodeType===1&&t.push({element:e,left:e.scrollLeft,top:e.scrollTop});for(typeof n.focus=="function"&&n.focus(),n=0;n=document.documentMode,ar=null,ku=null,ui=null,xu=!1;function hf(e,t,n){var r=n.window===n?n.document:n.nodeType===9?n:n.ownerDocument;xu||ar==null||ar!==to(r)||(r=ar,"selectionStart"in r&&Eu(r)?r={start:r.selectionStart,end:r.selectionEnd}:(r=(r.ownerDocument&&r.ownerDocument.defaultView||window).getSelection(),r={anchorNode:r.anchorNode,anchorOffset:r.anchorOffset,focusNode:r.focusNode,focusOffset:r.focusOffset}),ui&&li(ui,r)||(ui=r,r=ko(ku,"onSelect"),0hr||(e.current=Du[hr],Du[hr]=null,hr--)}function ae(e,t){hr++,Du[hr]=e.current,e.current=t}var vn={},Le=hn(vn),We=hn(!1),In=vn;function vr(e,t){var n=e.type.contextTypes;if(!n)return vn;var r=e.stateNode;if(r&&r.__reactInternalMemoizedUnmaskedChildContext===t)return r.__reactInternalMemoizedMaskedChildContext;var u={},a;for(a in n)u[a]=t[a];return r&&(e=e.stateNode,e.__reactInternalMemoizedUnmaskedChildContext=t,e.__reactInternalMemoizedMaskedChildContext=u),u}function He(e){return e=e.childContextTypes,e!=null}function Co(){fe(We),fe(Le)}function Nf(e,t,n){if(Le.current!==vn)throw Error(o(168));ae(Le,t),ae(We,n)}function Tf(e,t,n){var r=e.stateNode;if(t=t.childContextTypes,typeof r.getChildContext!="function")return n;r=r.getChildContext();for(var u in r)if(!(u in t))throw Error(o(108,se(e)||"Unknown",u));return D({},n,r)}function Ao(e){return e=(e=e.stateNode)&&e.__reactInternalMemoizedMergedChildContext||vn,In=Le.current,ae(Le,e),ae(We,We.current),!0}function Rf(e,t,n){var r=e.stateNode;if(!r)throw Error(o(169));n?(e=Tf(e,t,In),r.__reactInternalMemoizedMergedChildContext=e,fe(We),fe(Le),ae(Le,e)):fe(We),ae(We,n)}var Yt=null,No=!1,zu=!1;function Mf(e){Yt===null?Yt=[e]:Yt.push(e)}function qm(e){No=!0,Mf(e)}function mn(){if(!zu&&Yt!==null){zu=!0;var e=0,t=oe;try{var n=Yt;for(oe=1;e>=f,u-=f,Xt=1<<32-Et(t)+u|n<W?(Ae=F,F=null):Ae=F.sibling;var ne=k(w,F,S[W],N);if(ne===null){F===null&&(F=Ae);break}e&&F&&ne.alternate===null&&t(w,F),m=a(ne,m,W),U===null?B=ne:U.sibling=ne,U=ne,F=Ae}if(W===S.length)return n(w,F),pe&&Un(w,W),B;if(F===null){for(;WW?(Ae=F,F=null):Ae=F.sibling;var On=k(w,F,ne.value,N);if(On===null){F===null&&(F=Ae);break}e&&F&&On.alternate===null&&t(w,F),m=a(On,m,W),U===null?B=On:U.sibling=On,U=On,F=Ae}if(ne.done)return n(w,F),pe&&Un(w,W),B;if(F===null){for(;!ne.done;W++,ne=S.next())ne=A(w,ne.value,N),ne!==null&&(m=a(ne,m,W),U===null?B=ne:U.sibling=ne,U=ne);return pe&&Un(w,W),B}for(F=r(w,F);!ne.done;W++,ne=S.next())ne=L(F,w,W,ne.value,N),ne!==null&&(e&&ne.alternate!==null&&F.delete(ne.key===null?W:ne.key),m=a(ne,m,W),U===null?B=ne:U.sibling=ne,U=ne);return e&&F.forEach(function(Rg){return t(w,Rg)}),pe&&Un(w,W),B}function we(w,m,S,N){if(typeof S=="object"&&S!==null&&S.type===Te&&S.key===null&&(S=S.props.children),typeof S=="object"&&S!==null){switch(S.$$typeof){case Se:e:{for(var B=S.key,U=m;U!==null;){if(U.key===B){if(B=S.type,B===Te){if(U.tag===7){n(w,U.sibling),m=u(U,S.props.children),m.return=w,w=m;break e}}else if(U.elementType===B||typeof B=="object"&&B!==null&&B.$$typeof===$e&&If(B)===U.type){n(w,U.sibling),m=u(U,S.props),m.ref=pi(w,U,S),m.return=w,w=m;break e}n(w,U);break}else t(w,U);U=U.sibling}S.type===Te?(m=Gn(S.props.children,w.mode,N,S.key),m.return=w,w=m):(N=rl(S.type,S.key,S.props,null,w.mode,N),N.ref=pi(w,m,S),N.return=w,w=N)}return f(w);case he:e:{for(U=S.key;m!==null;){if(m.key===U)if(m.tag===4&&m.stateNode.containerInfo===S.containerInfo&&m.stateNode.implementation===S.implementation){n(w,m.sibling),m=u(m,S.children||[]),m.return=w,w=m;break e}else{n(w,m);break}else t(w,m);m=m.sibling}m=js(S,w.mode,N),m.return=w,w=m}return f(w);case $e:return U=S._init,we(w,m,U(S._payload),N)}if(Vr(S))return z(w,m,S,N);if(V(S))return I(w,m,S,N);Lo(w,S)}return typeof S=="string"&&S!==""||typeof S=="number"?(S=""+S,m!==null&&m.tag===6?(n(w,m.sibling),m=u(m,S),m.return=w,w=m):(n(w,m),m=Ls(S,w.mode,N),m.return=w,w=m),f(w)):n(w,m)}return we}var _r=Bf(!0),Uf=Bf(!1),jo=hn(null),bo=null,wr=null,$u=null;function Wu(){$u=wr=bo=null}function Hu(e){var t=jo.current;fe(jo),e._currentValue=t}function Ku(e,t,n){for(;e!==null;){var r=e.alternate;if((e.childLanes&t)!==t?(e.childLanes|=t,r!==null&&(r.childLanes|=t)):r!==null&&(r.childLanes&t)!==t&&(r.childLanes|=t),e===n)break;e=e.return}}function Sr(e,t){bo=e,$u=wr=null,e=e.dependencies,e!==null&&e.firstContext!==null&&((e.lanes&t)!==0&&(Ke=!0),e.firstContext=null)}function dt(e){var t=e._currentValue;if($u!==e)if(e={context:e,memoizedValue:t,next:null},wr===null){if(bo===null)throw Error(o(308));wr=e,bo.dependencies={lanes:0,firstContext:e}}else wr=wr.next=e;return t}var Fn=null;function Qu(e){Fn===null?Fn=[e]:Fn.push(e)}function Ff(e,t,n,r){var u=t.interleaved;return u===null?(n.next=n,Qu(t)):(n.next=u.next,u.next=n),t.interleaved=n,Jt(e,r)}function Jt(e,t){e.lanes|=t;var n=e.alternate;for(n!==null&&(n.lanes|=t),n=e,e=e.return;e!==null;)e.childLanes|=t,n=e.alternate,n!==null&&(n.childLanes|=t),n=e,e=e.return;return n.tag===3?n.stateNode:null}var gn=!1;function Gu(e){e.updateQueue={baseState:e.memoizedState,firstBaseUpdate:null,lastBaseUpdate:null,shared:{pending:null,interleaved:null,lanes:0},effects:null}}function Vf(e,t){e=e.updateQueue,t.updateQueue===e&&(t.updateQueue={baseState:e.baseState,firstBaseUpdate:e.firstBaseUpdate,lastBaseUpdate:e.lastBaseUpdate,shared:e.shared,effects:e.effects})}function Zt(e,t){return{eventTime:e,lane:t,tag:0,payload:null,callback:null,next:null}}function yn(e,t,n){var r=e.updateQueue;if(r===null)return null;if(r=r.shared,(J&2)!==0){var u=r.pending;return u===null?t.next=t:(t.next=u.next,u.next=t),r.pending=t,Jt(e,n)}return u=r.interleaved,u===null?(t.next=t,Qu(r)):(t.next=u.next,u.next=t),r.interleaved=t,Jt(e,n)}function Do(e,t,n){if(t=t.updateQueue,t!==null&&(t=t.shared,(n&4194240)!==0)){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,su(e,n)}}function $f(e,t){var n=e.updateQueue,r=e.alternate;if(r!==null&&(r=r.updateQueue,n===r)){var u=null,a=null;if(n=n.firstBaseUpdate,n!==null){do{var f={eventTime:n.eventTime,lane:n.lane,tag:n.tag,payload:n.payload,callback:n.callback,next:null};a===null?u=a=f:a=a.next=f,n=n.next}while(n!==null);a===null?u=a=t:a=a.next=t}else u=a=t;n={baseState:r.baseState,firstBaseUpdate:u,lastBaseUpdate:a,shared:r.shared,effects:r.effects},e.updateQueue=n;return}e=n.lastBaseUpdate,e===null?n.firstBaseUpdate=t:e.next=t,n.lastBaseUpdate=t}function zo(e,t,n,r){var u=e.updateQueue;gn=!1;var a=u.firstBaseUpdate,f=u.lastBaseUpdate,h=u.shared.pending;if(h!==null){u.shared.pending=null;var v=h,E=v.next;v.next=null,f===null?a=E:f.next=E,f=v;var O=e.alternate;O!==null&&(O=O.updateQueue,h=O.lastBaseUpdate,h!==f&&(h===null?O.firstBaseUpdate=E:h.next=E,O.lastBaseUpdate=v))}if(a!==null){var A=u.baseState;f=0,O=E=v=null,h=a;do{var k=h.lane,L=h.eventTime;if((r&k)===k){O!==null&&(O=O.next={eventTime:L,lane:0,tag:h.tag,payload:h.payload,callback:h.callback,next:null});e:{var z=e,I=h;switch(k=t,L=n,I.tag){case 1:if(z=I.payload,typeof z=="function"){A=z.call(L,A,k);break e}A=z;break e;case 3:z.flags=z.flags&-65537|128;case 0:if(z=I.payload,k=typeof z=="function"?z.call(L,A,k):z,k==null)break e;A=D({},A,k);break e;case 2:gn=!0}}h.callback!==null&&h.lane!==0&&(e.flags|=64,k=u.effects,k===null?u.effects=[h]:k.push(h))}else L={eventTime:L,lane:k,tag:h.tag,payload:h.payload,callback:h.callback,next:null},O===null?(E=O=L,v=A):O=O.next=L,f|=k;if(h=h.next,h===null){if(h=u.shared.pending,h===null)break;k=h,h=k.next,k.next=null,u.lastBaseUpdate=k,u.shared.pending=null}}while(!0);if(O===null&&(v=A),u.baseState=v,u.firstBaseUpdate=E,u.lastBaseUpdate=O,t=u.shared.interleaved,t!==null){u=t;do f|=u.lane,u=u.next;while(u!==t)}else a===null&&(u.shared.lanes=0);Wn|=f,e.lanes=f,e.memoizedState=A}}function Wf(e,t,n){if(e=t.effects,t.effects=null,e!==null)for(t=0;tn?n:4,e(!0);var r=Zu.transition;Zu.transition={};try{e(!1),t()}finally{oe=n,Zu.transition=r}}function ad(){return pt().memoizedState}function tg(e,t,n){var r=En(e);if(n={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null},cd(e))fd(t,n);else if(n=Ff(e,t,n,r),n!==null){var u=Ie();At(n,e,r,u),dd(n,t,r)}}function ng(e,t,n){var r=En(e),u={lane:r,action:n,hasEagerState:!1,eagerState:null,next:null};if(cd(e))fd(t,u);else{var a=e.alternate;if(e.lanes===0&&(a===null||a.lanes===0)&&(a=t.lastRenderedReducer,a!==null))try{var f=t.lastRenderedState,h=a(f,n);if(u.hasEagerState=!0,u.eagerState=h,kt(h,f)){var v=t.interleaved;v===null?(u.next=u,Qu(t)):(u.next=v.next,v.next=u),t.interleaved=u;return}}catch{}n=Ff(e,t,u,r),n!==null&&(u=Ie(),At(n,e,r,u),dd(n,t,r))}}function cd(e){var t=e.alternate;return e===me||t!==null&&t===me}function fd(e,t){gi=Uo=!0;var n=e.pending;n===null?t.next=t:(t.next=n.next,n.next=t),e.pending=t}function dd(e,t,n){if((n&4194240)!==0){var r=t.lanes;r&=e.pendingLanes,n|=r,t.lanes=n,su(e,n)}}var $o={readContext:dt,useCallback:je,useContext:je,useEffect:je,useImperativeHandle:je,useInsertionEffect:je,useLayoutEffect:je,useMemo:je,useReducer:je,useRef:je,useState:je,useDebugValue:je,useDeferredValue:je,useTransition:je,useMutableSource:je,useSyncExternalStore:je,useId:je,unstable_isNewReconciler:!1},rg={readContext:dt,useCallback:function(e,t){return Bt().memoizedState=[e,t===void 0?null:t],e},useContext:dt,useEffect:td,useImperativeHandle:function(e,t,n){return n=n!=null?n.concat([e]):null,Fo(4194308,4,id.bind(null,t,e),n)},useLayoutEffect:function(e,t){return Fo(4194308,4,e,t)},useInsertionEffect:function(e,t){return Fo(4,2,e,t)},useMemo:function(e,t){var n=Bt();return t=t===void 0?null:t,e=e(),n.memoizedState=[e,t],e},useReducer:function(e,t,n){var r=Bt();return t=n!==void 0?n(t):t,r.memoizedState=r.baseState=t,e={pending:null,interleaved:null,lanes:0,dispatch:null,lastRenderedReducer:e,lastRenderedState:t},r.queue=e,e=e.dispatch=tg.bind(null,me,e),[r.memoizedState,e]},useRef:function(e){var t=Bt();return e={current:e},t.memoizedState=e},useState:Zf,useDebugValue:ls,useDeferredValue:function(e){return Bt().memoizedState=e},useTransition:function(){var e=Zf(!1),t=e[0];return e=eg.bind(null,e[1]),Bt().memoizedState=e,[t,e]},useMutableSource:function(){},useSyncExternalStore:function(e,t,n){var r=me,u=Bt();if(pe){if(n===void 0)throw Error(o(407));n=n()}else{if(n=t(),Ce===null)throw Error(o(349));($n&30)!==0||Gf(r,t,n)}u.memoizedState=n;var a={value:n,getSnapshot:t};return u.queue=a,td(Xf.bind(null,r,a,e),[e]),r.flags|=2048,wi(9,Yf.bind(null,r,a,n,t),void 0,null),n},useId:function(){var e=Bt(),t=Ce.identifierPrefix;if(pe){var n=qt,r=Xt;n=(r&~(1<<32-Et(r)-1)).toString(32)+n,t=":"+t+"R"+n,n=yi++,0<\/script>",e=e.removeChild(e.firstChild)):typeof r.is=="string"?e=f.createElement(n,{is:r.is}):(e=f.createElement(n),n==="select"&&(f=e,r.multiple?f.multiple=!0:r.size&&(f.size=r.size))):e=f.createElementNS(e,n),e[zt]=t,e[fi]=r,Md(e,t,!1,!1),t.stateNode=e;e:{switch(f=Jl(n,r),n){case"dialog":ce("cancel",e),ce("close",e),u=r;break;case"iframe":case"object":case"embed":ce("load",e),u=r;break;case"video":case"audio":for(u=0;uPr&&(t.flags|=128,r=!0,Si(a,!1),t.lanes=4194304)}else{if(!r)if(e=Io(f),e!==null){if(t.flags|=128,r=!0,n=e.updateQueue,n!==null&&(t.updateQueue=n,t.flags|=4),Si(a,!0),a.tail===null&&a.tailMode==="hidden"&&!f.alternate&&!pe)return be(t),null}else 2*_e()-a.renderingStartTime>Pr&&n!==1073741824&&(t.flags|=128,r=!0,Si(a,!1),t.lanes=4194304);a.isBackwards?(f.sibling=t.child,t.child=f):(n=a.last,n!==null?n.sibling=f:t.child=f,a.last=f)}return a.tail!==null?(t=a.tail,a.rendering=t,a.tail=t.sibling,a.renderingStartTime=_e(),t.sibling=null,n=ve.current,ae(ve,r?n&1|2:n&1),t):(be(t),null);case 22:case 23:return Ts(),r=t.memoizedState!==null,e!==null&&e.memoizedState!==null!==r&&(t.flags|=8192),r&&(t.mode&1)!==0?(rt&1073741824)!==0&&(be(t),t.subtreeFlags&6&&(t.flags|=8192)):be(t),null;case 24:return null;case 25:return null}throw Error(o(156,t.tag))}function fg(e,t){switch(Bu(t),t.tag){case 1:return He(t.type)&&Co(),e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 3:return Er(),fe(We),fe(Le),Ju(),e=t.flags,(e&65536)!==0&&(e&128)===0?(t.flags=e&-65537|128,t):null;case 5:return Xu(t),null;case 13:if(fe(ve),e=t.memoizedState,e!==null&&e.dehydrated!==null){if(t.alternate===null)throw Error(o(340));yr()}return e=t.flags,e&65536?(t.flags=e&-65537|128,t):null;case 19:return fe(ve),null;case 4:return Er(),null;case 10:return Hu(t.type._context),null;case 22:case 23:return Ts(),null;case 24:return null;default:return null}}var Qo=!1,De=!1,dg=typeof WeakSet=="function"?WeakSet:Set,b=null;function xr(e,t){var n=e.ref;if(n!==null)if(typeof n=="function")try{n(null)}catch(r){ye(e,t,r)}else n.current=null}function ys(e,t,n){try{n()}catch(r){ye(e,t,r)}}var bd=!1;function pg(e,t){if(Tu=po,e=pf(),Eu(e)){if("selectionStart"in e)var n={start:e.selectionStart,end:e.selectionEnd};else e:{n=(n=e.ownerDocument)&&n.defaultView||window;var r=n.getSelection&&n.getSelection();if(r&&r.rangeCount!==0){n=r.anchorNode;var u=r.anchorOffset,a=r.focusNode;r=r.focusOffset;try{n.nodeType,a.nodeType}catch{n=null;break e}var f=0,h=-1,v=-1,E=0,O=0,A=e,k=null;t:for(;;){for(var L;A!==n||u!==0&&A.nodeType!==3||(h=f+u),A!==a||r!==0&&A.nodeType!==3||(v=f+r),A.nodeType===3&&(f+=A.nodeValue.length),(L=A.firstChild)!==null;)k=A,A=L;for(;;){if(A===e)break t;if(k===n&&++E===u&&(h=f),k===a&&++O===r&&(v=f),(L=A.nextSibling)!==null)break;A=k,k=A.parentNode}A=L}n=h===-1||v===-1?null:{start:h,end:v}}else n=null}n=n||{start:0,end:0}}else n=null;for(Ru={focusedElem:e,selectionRange:n},po=!1,b=t;b!==null;)if(t=b,e=t.child,(t.subtreeFlags&1028)!==0&&e!==null)e.return=t,b=e;else for(;b!==null;){t=b;try{var z=t.alternate;if((t.flags&1024)!==0)switch(t.tag){case 0:case 11:case 15:break;case 1:if(z!==null){var I=z.memoizedProps,we=z.memoizedState,w=t.stateNode,m=w.getSnapshotBeforeUpdate(t.elementType===t.type?I:Ot(t.type,I),we);w.__reactInternalSnapshotBeforeUpdate=m}break;case 3:var S=t.stateNode.containerInfo;S.nodeType===1?S.textContent="":S.nodeType===9&&S.documentElement&&S.removeChild(S.documentElement);break;case 5:case 6:case 4:case 17:break;default:throw Error(o(163))}}catch(N){ye(t,t.return,N)}if(e=t.sibling,e!==null){e.return=t.return,b=e;break}b=t.return}return z=bd,bd=!1,z}function Ei(e,t,n){var r=t.updateQueue;if(r=r!==null?r.lastEffect:null,r!==null){var u=r=r.next;do{if((u.tag&e)===e){var a=u.destroy;u.destroy=void 0,a!==void 0&&ys(t,n,a)}u=u.next}while(u!==r)}}function Go(e,t){if(t=t.updateQueue,t=t!==null?t.lastEffect:null,t!==null){var n=t=t.next;do{if((n.tag&e)===e){var r=n.create;n.destroy=r()}n=n.next}while(n!==t)}}function _s(e){var t=e.ref;if(t!==null){var n=e.stateNode;e.tag,e=n,typeof t=="function"?t(e):t.current=e}}function Dd(e){var t=e.alternate;t!==null&&(e.alternate=null,Dd(t)),e.child=null,e.deletions=null,e.sibling=null,e.tag===5&&(t=e.stateNode,t!==null&&(delete t[zt],delete t[fi],delete t[bu],delete t[Ym],delete t[Xm])),e.stateNode=null,e.return=null,e.dependencies=null,e.memoizedProps=null,e.memoizedState=null,e.pendingProps=null,e.stateNode=null,e.updateQueue=null}function zd(e){return e.tag===5||e.tag===3||e.tag===4}function Id(e){e:for(;;){for(;e.sibling===null;){if(e.return===null||zd(e.return))return null;e=e.return}for(e.sibling.return=e.return,e=e.sibling;e.tag!==5&&e.tag!==6&&e.tag!==18;){if(e.flags&2||e.child===null||e.tag===4)continue e;e.child.return=e,e=e.child}if(!(e.flags&2))return e.stateNode}}function ws(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.nodeType===8?n.parentNode.insertBefore(e,t):n.insertBefore(e,t):(n.nodeType===8?(t=n.parentNode,t.insertBefore(e,n)):(t=n,t.appendChild(e)),n=n._reactRootContainer,n!=null||t.onclick!==null||(t.onclick=Oo));else if(r!==4&&(e=e.child,e!==null))for(ws(e,t,n),e=e.sibling;e!==null;)ws(e,t,n),e=e.sibling}function Ss(e,t,n){var r=e.tag;if(r===5||r===6)e=e.stateNode,t?n.insertBefore(e,t):n.appendChild(e);else if(r!==4&&(e=e.child,e!==null))for(Ss(e,t,n),e=e.sibling;e!==null;)Ss(e,t,n),e=e.sibling}var Re=null,Pt=!1;function _n(e,t,n){for(n=n.child;n!==null;)Bd(e,t,n),n=n.sibling}function Bd(e,t,n){if(Dt&&typeof Dt.onCommitFiberUnmount=="function")try{Dt.onCommitFiberUnmount(lo,n)}catch{}switch(n.tag){case 5:De||xr(n,t);case 6:var r=Re,u=Pt;Re=null,_n(e,t,n),Re=r,Pt=u,Re!==null&&(Pt?(e=Re,n=n.stateNode,e.nodeType===8?e.parentNode.removeChild(n):e.removeChild(n)):Re.removeChild(n.stateNode));break;case 18:Re!==null&&(Pt?(e=Re,n=n.stateNode,e.nodeType===8?ju(e.parentNode,n):e.nodeType===1&&ju(e,n),ei(e)):ju(Re,n.stateNode));break;case 4:r=Re,u=Pt,Re=n.stateNode.containerInfo,Pt=!0,_n(e,t,n),Re=r,Pt=u;break;case 0:case 11:case 14:case 15:if(!De&&(r=n.updateQueue,r!==null&&(r=r.lastEffect,r!==null))){u=r=r.next;do{var a=u,f=a.destroy;a=a.tag,f!==void 0&&((a&2)!==0||(a&4)!==0)&&ys(n,t,f),u=u.next}while(u!==r)}_n(e,t,n);break;case 1:if(!De&&(xr(n,t),r=n.stateNode,typeof r.componentWillUnmount=="function"))try{r.props=n.memoizedProps,r.state=n.memoizedState,r.componentWillUnmount()}catch(h){ye(n,t,h)}_n(e,t,n);break;case 21:_n(e,t,n);break;case 22:n.mode&1?(De=(r=De)||n.memoizedState!==null,_n(e,t,n),De=r):_n(e,t,n);break;default:_n(e,t,n)}}function Ud(e){var t=e.updateQueue;if(t!==null){e.updateQueue=null;var n=e.stateNode;n===null&&(n=e.stateNode=new dg),t.forEach(function(r){var u=Eg.bind(null,e,r);n.has(r)||(n.add(r),r.then(u,u))})}}function Ct(e,t){var n=t.deletions;if(n!==null)for(var r=0;ru&&(u=f),r&=~a}if(r=u,r=_e()-r,r=(120>r?120:480>r?480:1080>r?1080:1920>r?1920:3e3>r?3e3:4320>r?4320:1960*vg(r/1960))-r,10e?16:e,Sn===null)var r=!1;else{if(e=Sn,Sn=null,Zo=0,(J&6)!==0)throw Error(o(331));var u=J;for(J|=4,b=e.current;b!==null;){var a=b,f=a.child;if((b.flags&16)!==0){var h=a.deletions;if(h!==null){for(var v=0;v_e()-xs?Kn(e,0):ks|=n),Ge(e,t)}function Zd(e,t){t===0&&((e.mode&1)===0?t=1:(t=so,so<<=1,(so&130023424)===0&&(so=4194304)));var n=Ie();e=Jt(e,t),e!==null&&(Yr(e,t,n),Ge(e,n))}function Sg(e){var t=e.memoizedState,n=0;t!==null&&(n=t.retryLane),Zd(e,n)}function Eg(e,t){var n=0;switch(e.tag){case 13:var r=e.stateNode,u=e.memoizedState;u!==null&&(n=u.retryLane);break;case 19:r=e.stateNode;break;default:throw Error(o(314))}r!==null&&r.delete(t),Zd(e,n)}var ep;ep=function(e,t,n){if(e!==null)if(e.memoizedProps!==t.pendingProps||We.current)Ke=!0;else{if((e.lanes&n)===0&&(t.flags&128)===0)return Ke=!1,ag(e,t,n);Ke=(e.flags&131072)!==0}else Ke=!1,pe&&(t.flags&1048576)!==0&&Lf(t,Ro,t.index);switch(t.lanes=0,t.tag){case 2:var r=t.type;Ko(e,t),e=t.pendingProps;var u=vr(t,Le.current);Sr(t,n),u=ts(null,t,r,e,u,n);var a=ns();return t.flags|=1,typeof u=="object"&&u!==null&&typeof u.render=="function"&&u.$$typeof===void 0?(t.tag=1,t.memoizedState=null,t.updateQueue=null,He(r)?(a=!0,Ao(t)):a=!1,t.memoizedState=u.state!==null&&u.state!==void 0?u.state:null,Gu(t),u.updater=Wo,t.stateNode=u,u._reactInternals=t,ss(t,r,e,n),t=ds(null,t,r,!0,a,n)):(t.tag=0,pe&&a&&Iu(t),ze(null,t,u,n),t=t.child),t;case 16:r=t.elementType;e:{switch(Ko(e,t),e=t.pendingProps,u=r._init,r=u(r._payload),t.type=r,u=t.tag=xg(r),e=Ot(r,e),u){case 0:t=fs(null,t,r,e,n);break e;case 1:t=Pd(null,t,r,e,n);break e;case 11:t=Sd(null,t,r,e,n);break e;case 14:t=Ed(null,t,r,Ot(r.type,e),n);break e}throw Error(o(306,r,""))}return t;case 0:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),fs(e,t,r,u,n);case 1:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Pd(e,t,r,u,n);case 3:e:{if(Cd(t),e===null)throw Error(o(387));r=t.pendingProps,a=t.memoizedState,u=a.element,Vf(e,t),zo(t,r,null,n);var f=t.memoizedState;if(r=f.element,a.isDehydrated)if(a={element:r,isDehydrated:!1,cache:f.cache,pendingSuspenseBoundaries:f.pendingSuspenseBoundaries,transitions:f.transitions},t.updateQueue.baseState=a,t.memoizedState=a,t.flags&256){u=kr(Error(o(423)),t),t=Ad(e,t,r,n,u);break e}else if(r!==u){u=kr(Error(o(424)),t),t=Ad(e,t,r,n,u);break e}else for(nt=pn(t.stateNode.containerInfo.firstChild),tt=t,pe=!0,xt=null,n=Uf(t,null,r,n),t.child=n;n;)n.flags=n.flags&-3|4096,n=n.sibling;else{if(yr(),r===u){t=en(e,t,n);break e}ze(e,t,r,n)}t=t.child}return t;case 5:return Hf(t),e===null&&Fu(t),r=t.type,u=t.pendingProps,a=e!==null?e.memoizedProps:null,f=u.children,Mu(r,u)?f=null:a!==null&&Mu(r,a)&&(t.flags|=32),Od(e,t),ze(e,t,f,n),t.child;case 6:return e===null&&Fu(t),null;case 13:return Nd(e,t,n);case 4:return Yu(t,t.stateNode.containerInfo),r=t.pendingProps,e===null?t.child=_r(t,null,r,n):ze(e,t,r,n),t.child;case 11:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Sd(e,t,r,u,n);case 7:return ze(e,t,t.pendingProps,n),t.child;case 8:return ze(e,t,t.pendingProps.children,n),t.child;case 12:return ze(e,t,t.pendingProps.children,n),t.child;case 10:e:{if(r=t.type._context,u=t.pendingProps,a=t.memoizedProps,f=u.value,ae(jo,r._currentValue),r._currentValue=f,a!==null)if(kt(a.value,f)){if(a.children===u.children&&!We.current){t=en(e,t,n);break e}}else for(a=t.child,a!==null&&(a.return=t);a!==null;){var h=a.dependencies;if(h!==null){f=a.child;for(var v=h.firstContext;v!==null;){if(v.context===r){if(a.tag===1){v=Zt(-1,n&-n),v.tag=2;var E=a.updateQueue;if(E!==null){E=E.shared;var O=E.pending;O===null?v.next=v:(v.next=O.next,O.next=v),E.pending=v}}a.lanes|=n,v=a.alternate,v!==null&&(v.lanes|=n),Ku(a.return,n,t),h.lanes|=n;break}v=v.next}}else if(a.tag===10)f=a.type===t.type?null:a.child;else if(a.tag===18){if(f=a.return,f===null)throw Error(o(341));f.lanes|=n,h=f.alternate,h!==null&&(h.lanes|=n),Ku(f,n,t),f=a.sibling}else f=a.child;if(f!==null)f.return=a;else for(f=a;f!==null;){if(f===t){f=null;break}if(a=f.sibling,a!==null){a.return=f.return,f=a;break}f=f.return}a=f}ze(e,t,u.children,n),t=t.child}return t;case 9:return u=t.type,r=t.pendingProps.children,Sr(t,n),u=dt(u),r=r(u),t.flags|=1,ze(e,t,r,n),t.child;case 14:return r=t.type,u=Ot(r,t.pendingProps),u=Ot(r.type,u),Ed(e,t,r,u,n);case 15:return kd(e,t,t.type,t.pendingProps,n);case 17:return r=t.type,u=t.pendingProps,u=t.elementType===r?u:Ot(r,u),Ko(e,t),t.tag=1,He(r)?(e=!0,Ao(t)):e=!1,Sr(t,n),hd(t,r,u),ss(t,r,u,n),ds(null,t,r,!0,e,n);case 19:return Rd(e,t,n);case 22:return xd(e,t,n)}throw Error(o(156,t.tag))};function tp(e,t){return Lc(e,t)}function kg(e,t,n,r){this.tag=e,this.key=n,this.sibling=this.child=this.return=this.stateNode=this.type=this.elementType=null,this.index=0,this.ref=null,this.pendingProps=t,this.dependencies=this.memoizedState=this.updateQueue=this.memoizedProps=null,this.mode=r,this.subtreeFlags=this.flags=0,this.deletions=null,this.childLanes=this.lanes=0,this.alternate=null}function vt(e,t,n,r){return new kg(e,t,n,r)}function Ms(e){return e=e.prototype,!(!e||!e.isReactComponent)}function xg(e){if(typeof e=="function")return Ms(e)?1:0;if(e!=null){if(e=e.$$typeof,e===jt)return 11;if(e===bt)return 14}return 2}function xn(e,t){var n=e.alternate;return n===null?(n=vt(e.tag,t,e.key,e.mode),n.elementType=e.elementType,n.type=e.type,n.stateNode=e.stateNode,n.alternate=e,e.alternate=n):(n.pendingProps=t,n.type=e.type,n.flags=0,n.subtreeFlags=0,n.deletions=null),n.flags=e.flags&14680064,n.childLanes=e.childLanes,n.lanes=e.lanes,n.child=e.child,n.memoizedProps=e.memoizedProps,n.memoizedState=e.memoizedState,n.updateQueue=e.updateQueue,t=e.dependencies,n.dependencies=t===null?null:{lanes:t.lanes,firstContext:t.firstContext},n.sibling=e.sibling,n.index=e.index,n.ref=e.ref,n}function rl(e,t,n,r,u,a){var f=2;if(r=e,typeof e=="function")Ms(e)&&(f=1);else if(typeof e=="string")f=5;else e:switch(e){case Te:return Gn(n.children,u,a,t);case at:f=8,u|=8;break;case ln:return e=vt(12,n,t,u|2),e.elementType=ln,e.lanes=a,e;case Je:return e=vt(13,n,t,u),e.elementType=Je,e.lanes=a,e;case St:return e=vt(19,n,t,u),e.elementType=St,e.lanes=a,e;case ge:return il(n,u,a,t);default:if(typeof e=="object"&&e!==null)switch(e.$$typeof){case Qt:f=10;break e;case bn:f=9;break e;case jt:f=11;break e;case bt:f=14;break e;case $e:f=16,r=null;break e}throw Error(o(130,e==null?e:typeof e,""))}return t=vt(f,n,t,u),t.elementType=e,t.type=r,t.lanes=a,t}function Gn(e,t,n,r){return e=vt(7,e,r,t),e.lanes=n,e}function il(e,t,n,r){return e=vt(22,e,r,t),e.elementType=ge,e.lanes=n,e.stateNode={isHidden:!1},e}function Ls(e,t,n){return e=vt(6,e,null,t),e.lanes=n,e}function js(e,t,n){return t=vt(4,e.children!==null?e.children:[],e.key,t),t.lanes=n,t.stateNode={containerInfo:e.containerInfo,pendingChildren:null,implementation:e.implementation},t}function Og(e,t,n,r,u){this.tag=t,this.containerInfo=e,this.finishedWork=this.pingCache=this.current=this.pendingChildren=null,this.timeoutHandle=-1,this.callbackNode=this.pendingContext=this.context=null,this.callbackPriority=0,this.eventTimes=uu(0),this.expirationTimes=uu(-1),this.entangledLanes=this.finishedLanes=this.mutableReadLanes=this.expiredLanes=this.pingedLanes=this.suspendedLanes=this.pendingLanes=0,this.entanglements=uu(0),this.identifierPrefix=r,this.onRecoverableError=u,this.mutableSourceEagerHydrationData=null}function bs(e,t,n,r,u,a,f,h,v){return e=new Og(e,t,n,h,v),t===1?(t=1,a===!0&&(t|=8)):t=0,a=vt(3,null,null,t),e.current=a,a.stateNode=e,a.memoizedState={element:r,isDehydrated:n,cache:null,transitions:null,pendingSuspenseBoundaries:null},Gu(a),e}function Pg(e,t,n){var r=3"u"||typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE!="function"))try{__REACT_DEVTOOLS_GLOBAL_HOOK__.checkDCE(i)}catch(l){console.error(l)}}return i(),hl.exports=dp(),hl.exports}var Xs;function pp(){if(Xs)return Ai;Xs=1;var i=Ys();return Ai.createRoot=i.createRoot,Ai.hydrateRoot=i.hydrateRoot,Ai}var hp=pp();const vp=Us(hp);function q(i){for(var l=arguments.length,o=new Array(l>1?l-1:0),s=1;si.length)&&(l=i.length);for(var o=0,s=Array(l);o=i.length?{done:!0}:{done:!1,value:i[s++]}}}throw new TypeError(`Invalid attempt to iterate non-iterable instance. +In order to be iterable, non-array objects must have a [Symbol.iterator]() method.`)}function An(){return An=Object.assign?Object.assign.bind():function(i){for(var l=1;ls&&(s=g.dependenciesState_)}for(o.length=c,i.newObserving_=null,d=l.length;d--;){var y=l[d];y.diffValue===0&&ka(y,i),y.diffValue=0}for(;c--;){var P=o[c];P.diffValue===1&&(P.diffValue=0,_h(P,i))}s!==Z.UP_TO_DATE_&&(i.dependenciesState_=s,i.onBecomeStale_())}function Pl(i){var l=i.observing_;i.observing_=[];for(var o=l.length;o--;)ka(l[o],i);i.dependenciesState_=Z.NOT_TRACKING_}function wa(i){var l=Mn();try{return i()}finally{Wt(l)}}function Mn(){var i=T.trackingDerivation;return T.trackingDerivation=null,i}function Wt(i){T.trackingDerivation=i}function Cl(i){var l=T.allowStateReads;return T.allowStateReads=i,l}function Lr(i){T.allowStateReads=i}function Sa(i){if(i.dependenciesState_!==Z.UP_TO_DATE_){i.dependenciesState_=Z.UP_TO_DATE_;for(var l=i.observing_,o=l.length;o--;)l[o].lowestObserverState_=Z.UP_TO_DATE_}}var Wi=function(){this.version=6,this.UNCHANGED={},this.trackingDerivation=null,this.trackingContext=null,this.runId=0,this.mobxGuid=0,this.inBatch=0,this.pendingUnobservations=[],this.pendingReactions=[],this.isRunningReactions=!1,this.allowStateChanges=!1,this.allowStateReads=!0,this.enforceActions=!0,this.spyListeners=[],this.globalReactionErrorHandlers=[],this.computedRequiresReaction=!1,this.reactionRequiresObservable=!1,this.observableRequiresReaction=!1,this.disableErrorBoundaries=!1,this.suppressReactionErrors=!1,this.useProxies=!0,this.verifyProxies=!1,this.safeDescriptors=!0},Hi=!0,Ea=!1,T=(function(){var i=Ni();return i.__mobxInstanceCount>0&&!i.__mobxGlobals&&(Hi=!1),i.__mobxGlobals&&i.__mobxGlobals.version!==new Wi().version&&(Hi=!1),Hi?i.__mobxGlobals?(i.__mobxInstanceCount+=1,i.__mobxGlobals.UNCHANGED||(i.__mobxGlobals.UNCHANGED={}),i.__mobxGlobals):(i.__mobxInstanceCount=1,i.__mobxGlobals=new Wi):(setTimeout(function(){Ea||q(35)},1),new Wi)})();function yh(){if((T.pendingReactions.length||T.inBatch||T.isRunningReactions)&&q(36),Ea=!0,Hi){var i=Ni();--i.__mobxInstanceCount===0&&(i.__mobxGlobals=void 0),T=new Wi}}function _h(i,l){i.observers_.add(l),i.lowestObserverState_>l.dependenciesState_&&(i.lowestObserverState_=l.dependenciesState_)}function ka(i,l){i.observers_.delete(l),i.observers_.size===0&&xa(i)}function xa(i){i.isPendingUnobservation===!1&&(i.isPendingUnobservation=!0,T.pendingUnobservations.push(i))}function ot(){T.inBatch++}function lt(){if(--T.inBatch===0){Ca();for(var i=T.pendingUnobservations,l=0;l0&&xa(i),!1)}function Pa(i){i.lowestObserverState_!==Z.STALE_&&(i.lowestObserverState_=Z.STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.UP_TO_DATE_&&l.onBecomeStale_(),l.dependenciesState_=Z.STALE_}))}function wh(i){i.lowestObserverState_!==Z.STALE_&&(i.lowestObserverState_=Z.STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.POSSIBLY_STALE_?l.dependenciesState_=Z.STALE_:l.dependenciesState_===Z.UP_TO_DATE_&&(i.lowestObserverState_=Z.UP_TO_DATE_)}))}function Sh(i){i.lowestObserverState_===Z.UP_TO_DATE_&&(i.lowestObserverState_=Z.POSSIBLY_STALE_,i.observers_.forEach(function(l){l.dependenciesState_===Z.UP_TO_DATE_&&(l.dependenciesState_=Z.POSSIBLY_STALE_,l.onBecomeStale_())}))}var Ht=(function(){function i(o,s,c,d){o===void 0&&(o="Reaction"),this.name_=void 0,this.onInvalidate_=void 0,this.errorHandler_=void 0,this.requiresObservable_=void 0,this.observing_=[],this.newObserving_=[],this.dependenciesState_=Z.NOT_TRACKING_,this.runId_=0,this.unboundDepsCount_=0,this.flags_=0,this.isTracing_=Fi.NONE,this.name_=o,this.onInvalidate_=s,this.errorHandler_=c,this.requiresObservable_=d}var l=i.prototype;return l.onBecomeStale_=function(){this.schedule_()},l.schedule_=function(){this.isScheduled||(this.isScheduled=!0,T.pendingReactions.push(this),Ca())},l.runReaction_=function(){if(!this.isDisposed){ot(),this.isScheduled=!1;var s=T.trackingContext;if(T.trackingContext=this,Ol(this)){this.isTrackPending=!0;try{this.onInvalidate_()}catch(c){this.reportExceptionInDerivation_(c)}}T.trackingContext=s,lt()}},l.track=function(s){if(!this.isDisposed){ot(),this.isRunning=!0;var c=T.trackingContext;T.trackingContext=this;var d=_a(this,s,void 0);T.trackingContext=c,this.isRunning=!1,this.isTrackPending=!1,this.isDisposed&&Pl(this),$i(d)&&this.reportExceptionInDerivation_(d.cause),lt()}},l.reportExceptionInDerivation_=function(s){var c=this;if(this.errorHandler_){this.errorHandler_(s,this);return}if(T.disableErrorBoundaries)throw s;var d="[mobx] uncaught error in '"+this+"'";T.suppressReactionErrors||console.error(d,s),T.globalReactionErrorHandlers.forEach(function(p){return p(s,c)})},l.dispose=function(){this.isDisposed||(this.isDisposed=!0,this.isRunning||(ot(),Pl(this),lt()))},l.getDisposer_=function(s){var c=this,d=function p(){c.dispose(),s==null||s.removeEventListener==null||s.removeEventListener("abort",p)};return s==null||s.addEventListener==null||s.addEventListener("abort",d),d[$]=this,d},l.toString=function(){return"Reaction["+this.name_+"]"},l.trace=function(s){},Jn(i,[{key:"isDisposed",get:function(){return Ye(this.flags_,i.isDisposedMask_)},set:function(s){this.flags_=Xe(this.flags_,i.isDisposedMask_,s)}},{key:"isScheduled",get:function(){return Ye(this.flags_,i.isScheduledMask_)},set:function(s){this.flags_=Xe(this.flags_,i.isScheduledMask_,s)}},{key:"isTrackPending",get:function(){return Ye(this.flags_,i.isTrackPendingMask_)},set:function(s){this.flags_=Xe(this.flags_,i.isTrackPendingMask_,s)}},{key:"isRunning",get:function(){return Ye(this.flags_,i.isRunningMask_)},set:function(s){this.flags_=Xe(this.flags_,i.isRunningMask_,s)}},{key:"diffValue",get:function(){return Ye(this.flags_,i.diffValueMask_)?1:0},set:function(s){this.flags_=Xe(this.flags_,i.diffValueMask_,s===1)}}])})();Ht.isDisposedMask_=1,Ht.isScheduledMask_=2,Ht.isTrackPendingMask_=4,Ht.isRunningMask_=8,Ht.diffValueMask_=16;var Eh=100,Al=function(l){return l()};function Ca(){T.inBatch>0||T.isRunningReactions||Al(kh)}function kh(){T.isRunningReactions=!0;for(var i=T.pendingReactions,l=0;i.length>0;){++l===Eh&&(console.error("[mobx] cycle in reaction: "+i[0]),i.splice(0));for(var o=i.splice(0),s=0,c=o.length;s0&&(l.dependencies=Uh(i.observing_).map(ba)),l}function Uh(i){return Array.from(new Set(i))}var Fh=0;function Da(){this.message="FLOW_CANCELLED"}Da.prototype=Object.create(Error.prototype);var Rl=aa("flow"),Vh=aa("flow.bound",{bound:!0}),tr=Object.assign(function(l,o){if(Rr(o))return Rl.decorate_20223_(l,o);if(Pn(o))return Tr(l,o,Rl);var s=l,c=s.name||"",d=function(){var g=this,y=arguments,P=++Fh,C=er(c+" - runid: "+P+" - init",s).apply(g,y),R,j=void 0,Y=new Promise(function(le,H){var K=0;R=H;function Fe(re){j=void 0;var Se;try{Se=er(c+" - runid: "+P+" - yield "+K++,C.next).call(C,re)}catch(he){return H(he)}Ne(Se)}function Ve(re){j=void 0;var Se;try{Se=er(c+" - runid: "+P+" - yield "+K++,C.throw).call(C,re)}catch(he){return H(he)}Ne(Se)}function Ne(re){if(it(re?.then)){re.then(Ne,H);return}return re.done?le(re.value):(j=Promise.resolve(re.value),j.then(Fe,Ve))}Fe(void 0)});return Y.cancel=er(c+" - runid: "+P+" - cancel",function(){try{j&&za(j);var le=C.return(void 0),H=Promise.resolve(le.value);H.then(Yn,Yn),za(H),R(new Da)}catch(K){R(K)}}),Y};return d.isMobXFlow=!0,d},Rl);tr.bound=Rt(Vh);function za(i){it(i.cancel)&&i.cancel()}function zr(i){return i?.isMobXFlow===!0}function $h(i,l){return i?qi(i)||!!i[$]||_l(i)||Ki(i)||Ui(i):!1}function Ia(i){return $h(i)}function Ml(i,l,o,s){return it(o)?Hh(i,l,o,s):Wh(i,l,o)}function Wh(i,l,o){return Ji(i).observe_(l,o)}function Hh(i,l,o,s){return Ji(i,l).observe_(o,s)}function Kt(i,l){l===void 0&&(l=void 0),ot();try{return i.apply(l)}finally{lt()}}function nr(i){return i[$]}var Kh={has:function(l,o){return nr(l).has_(o)},get:function(l,o){return nr(l).get_(o)},set:function(l,o,s){var c;return Pn(o)?(c=nr(l).set_(o,s,!0))!=null?c:!0:!1},deleteProperty:function(l,o){var s;return Pn(o)?(s=nr(l).delete_(o,!0))!=null?s:!0:!1},defineProperty:function(l,o,s){var c;return(c=nr(l).defineProperty_(o,s))!=null?c:!0},ownKeys:function(l){return nr(l).ownKeys_()},preventExtensions:function(l){q(13)}};function Qh(i,l){var o,s;return Zs(),i=jn(i,l),(s=(o=i[$]).proxy_)!=null?s:o.proxy_=new Proxy(i,Kh)}function ut(i){return i.interceptors_!==void 0&&i.interceptors_.length>0}function Ir(i,l){var o=i.interceptors_||(i.interceptors_=[]);return o.push(l),ea(function(){var s=o.indexOf(l);s!==-1&&o.splice(s,1)})}function st(i,l){var o=Mn();try{for(var s=[].concat(i.interceptors_||[]),c=0,d=s.length;c0}function Br(i,l){var o=i.changeListeners_||(i.changeListeners_=[]);return o.push(l),ea(function(){var s=o.indexOf(l);s!==-1&&o.splice(s,1)})}function _t(i,l){var o=Mn(),s=i.changeListeners_;if(s){s=s.slice();for(var c=0,d=s.length;c0?s.map(this.dehancer):s},l.intercept_=function(s){return Ir(this,s)},l.observe_=function(s,c){return c===void 0&&(c=!1),c&&s({observableKind:"array",object:this.proxy_,debugObjectName:this.atom_.name_,type:"splice",index:0,added:this.values_.slice(),addedCount:this.values_.length,removed:[],removedCount:0}),Br(this,s)},l.getArrayLength_=function(){return this.atom_.reportObserved(),this.values_.length},l.setArrayLength_=function(s){(typeof s!="number"||isNaN(s)||s<0)&&q("Out of range: "+s);var c=this.values_.length;if(s!==c)if(s>c){for(var d=new Array(s-c),p=0;p0&&Ja(s+c+1)},l.spliceWithArray_=function(s,c,d){var p=this;this.atom_;var g=this.values_.length;if(s===void 0?s=0:s>g?s=g:s<0&&(s=Math.max(0,g+s)),arguments.length===1?c=g-s:c==null?c=0:c=Math.max(0,Math.min(c,g-s)),d===void 0&&(d=gl),ut(this)){var y=st(this,{object:this.proxy_,type:Ba,index:s,removedCount:c,added:d});if(!y)return gl;c=y.removedCount,d=y.added}if(d=d.length===0?d:d.map(function(R){return p.enhancer_(R,void 0)}),this.legacyMode_){var P=d.length-c;this.updateArrayLength_(g,P)}var C=this.spliceItemsIntoValues_(s,c,d);return(c!==0||d.length!==0)&&this.notifyArraySplice_(s,d,C),this.dehanceValues_(C)},l.spliceItemsIntoValues_=function(s,c,d){if(d.length=this.values_.length){console.warn("[mobx] Out of bounds read: "+s);return}return this.atom_.reportObserved(),this.dehanceValue_(this.values_[s])},l.set_=function(s,c){var d=this.values_;if(this.legacyMode_&&s>d.length&&q(17,s,d.length),s2?s-2:0),d=2;d-1?(this.splice(s,1),!0):!1}};ue("at",qe),ue("concat",qe),ue("flat",qe),ue("includes",qe),ue("indexOf",qe),ue("join",qe),ue("lastIndexOf",qe),ue("slice",qe),ue("toString",qe),ue("toLocaleString",qe),ue("toSorted",qe),ue("toSpliced",qe),ue("with",qe),ue("every",wt),ue("filter",wt),ue("find",wt),ue("findIndex",wt),ue("findLast",wt),ue("findLastIndex",wt),ue("flatMap",wt),ue("forEach",wt),ue("map",wt),ue("some",wt),ue("toReversed",wt),ue("reduce",Ua),ue("reduceRight",Ua);function ue(i,l){typeof Array.prototype[i]=="function"&&(Gi[i]=l(i))}function qe(i){return function(){var l=this[$];l.atom_.reportObserved();var o=l.dehanceValues_(l.values_);return o[i].apply(o,arguments)}}function wt(i){return function(l,o){var s=this,c=this[$];c.atom_.reportObserved();var d=c.dehanceValues_(c.values_);return d[i](function(p,g){return l.call(o,p,g,s)})}}function Ua(i){return function(){var l=this,o=this[$];o.atom_.reportObserved();var s=o.dehanceValues_(o.values_),c=arguments[0];return arguments[0]=function(d,p,g){return c(d,p,g,l)},s[i].apply(s,arguments)}}var Zh=Cn("ObservableArrayAdministration",jl);function Yi(i){return Mi(i)&&Zh(i[$])}var ev={},rn="add",Xi="delete",Fa=(function(){function i(o,s,c){var d=this;s===void 0&&(s=Nn),c===void 0&&(c="ObservableMap"),this.enhancer_=void 0,this.name_=void 0,this[$]=ev,this.data_=void 0,this.hasMap_=void 0,this.keysAtom_=void 0,this.interceptors_=void 0,this.changeListeners_=void 0,this.dehancer=void 0,this.enhancer_=s,this.name_=c,it(Map)||q(18),on(function(){d.keysAtom_=ua("ObservableMap.keys()"),d.data_=new Map,d.hasMap_=new Map,o&&d.merge(o)})}var l=i.prototype;return l.has_=function(s){return this.data_.has(s)},l.has=function(s){var c=this;if(!T.trackingDerivation)return this.has_(s);var d=this.hasMap_.get(s);if(!d){var p=d=new Rn(this.has_(s),ji,"ObservableMap.key?",!1);this.hasMap_.set(s,p),Ma(p,function(){return c.hasMap_.delete(s)})}return d.get()},l.set=function(s,c){var d=this.has_(s);if(ut(this)){var p=st(this,{type:d?Mt:rn,object:this,newValue:c,name:s});if(!p)return this;c=p.newValue}return d?this.updateValue_(s,c):this.addValue_(s,c),this},l.delete=function(s){var c=this;if(this.keysAtom_,ut(this)){var d=st(this,{type:Xi,object:this,name:s});if(!d)return!1}if(this.has_(s)){var p=jr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:Xi,object:this,oldValue:this.data_.get(s).value_,name:s}:null;return Kt(function(){var P;c.keysAtom_.reportChanged(),(P=c.hasMap_.get(s))==null||P.setNewValue_(!1);var C=c.data_.get(s);C.setNewValue_(void 0),c.data_.delete(s)}),g&&_t(this,y),!0}return!1},l.updateValue_=function(s,c){var d=this.data_.get(s);if(c=d.prepareNewValue_(c),c!==T.UNCHANGED){var p=jr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:Mt,object:this,oldValue:d.value_,name:s,newValue:c}:null;d.setNewValue_(c),g&&_t(this,y)}},l.addValue_=function(s,c){var d=this;this.keysAtom_,Kt(function(){var P,C=new Rn(c,d.enhancer_,"ObservableMap.key",!1);d.data_.set(s,C),c=C.value_,(P=d.hasMap_.get(s))==null||P.setNewValue_(!0),d.keysAtom_.reportChanged()});var p=jr(),g=yt(this),y=g||p?{observableKind:"map",debugObjectName:this.name_,type:rn,object:this,name:s,newValue:c}:null;g&&_t(this,y)},l.get=function(s){return this.has(s)?this.dehanceValue_(this.data_.get(s).get()):this.dehanceValue_(void 0)},l.dehanceValue_=function(s){return this.dehancer!==void 0?this.dehancer(s):s},l.keys=function(){return this.keysAtom_.reportObserved(),this.data_.keys()},l.values=function(){var s=this,c=this.keys();return Va({next:function(){var p=c.next(),g=p.done,y=p.value;return{done:g,value:g?void 0:s.get(y)}}})},l.entries=function(){var s=this,c=this.keys();return Va({next:function(){var p=c.next(),g=p.done,y=p.value;return{done:g,value:g?void 0:[y,s.get(y)]}}})},l[Symbol.iterator]=function(){return this.entries()},l.forEach=function(s,c){for(var d=Zn(this),p;!(p=d()).done;){var g=p.value,y=g[0],P=g[1];s.call(c,P,y,this)}},l.merge=function(s){var c=this;return Ln(s)&&(s=new Map(s)),Kt(function(){Ft(s)?wp(s).forEach(function(d){return c.set(d,s[d])}):Array.isArray(s)?s.forEach(function(d){var p=d[0],g=d[1];return c.set(p,g)}):Xn(s)?(_p(s)||q(19,s),s.forEach(function(d,p){return c.set(p,d)})):s!=null&&q(20,s)}),this},l.clear=function(){var s=this;Kt(function(){wa(function(){for(var c=Zn(s.keys()),d;!(d=c()).done;){var p=d.value;s.delete(p)}})})},l.replace=function(s){var c=this;return Kt(function(){for(var d=tv(s),p=new Map,g=!1,y=Zn(c.data_.keys()),P;!(P=y()).done;){var C=P.value;if(!d.has(C)){var R=c.delete(C);if(R)g=!0;else{var j=c.data_.get(C);p.set(C,j)}}}for(var Y=Zn(d.entries()),le;!(le=Y()).done;){var H=le.value,K=H[0],Fe=H[1],Ve=c.data_.has(K);if(c.set(K,Fe),c.data_.has(K)){var Ne=c.data_.get(K);p.set(K,Ne),Ve||(g=!0)}}if(!g)if(c.data_.size!==p.size)c.keysAtom_.reportChanged();else for(var re=c.data_.keys(),Se=p.keys(),he=re.next(),Te=Se.next();!he.done;){if(he.value!==Te.value){c.keysAtom_.reportChanged();break}he=re.next(),Te=Se.next()}c.data_=p}),this},l.toString=function(){return"[object ObservableMap]"},l.toJSON=function(){return Array.from(this)},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},Jn(i,[{key:"size",get:function(){return this.keysAtom_.reportObserved(),this.data_.size}},{key:Symbol.toStringTag,get:function(){return"Map"}}])})(),Ln=Cn("ObservableMap",Fa);function Va(i){return i[Symbol.toStringTag]="MapIterator",Bl(i)}function tv(i){if(Xn(i)||Ln(i))return i;if(Array.isArray(i))return new Map(i);if(Ft(i)){var l=new Map;for(var o in i)l.set(o,i[o]);return l}else return q(21,i)}var nv={},$a=(function(){function i(o,s,c){var d=this;s===void 0&&(s=Nn),c===void 0&&(c="ObservableSet"),this.name_=void 0,this[$]=nv,this.data_=new Set,this.atom_=void 0,this.changeListeners_=void 0,this.interceptors_=void 0,this.dehancer=void 0,this.enhancer_=void 0,this.name_=c,it(Set)||q(22),this.enhancer_=function(p,g){return s(p,g,c)},on(function(){d.atom_=ua(d.name_),o&&d.replace(o)})}var l=i.prototype;return l.dehanceValue_=function(s){return this.dehancer!==void 0?this.dehancer(s):s},l.clear=function(){var s=this;Kt(function(){wa(function(){for(var c=Zn(s.data_.values()),d;!(d=c()).done;){var p=d.value;s.delete(p)}})})},l.forEach=function(s,c){for(var d=Zn(this),p;!(p=d()).done;){var g=p.value;s.call(c,g,g,this)}},l.add=function(s){var c=this;if(this.atom_,ut(this)){var d=st(this,{type:rn,object:this,newValue:s});if(!d)return this;s=d.newValue}if(!this.has(s)){Kt(function(){c.data_.add(c.enhancer_(s,void 0)),c.atom_.reportChanged()});var p=!1,g=yt(this),y=g||p?{observableKind:"set",debugObjectName:this.name_,type:rn,object:this,newValue:s}:null;g&&_t(this,y)}return this},l.delete=function(s){var c=this;if(ut(this)){var d=st(this,{type:Xi,object:this,oldValue:s});if(!d)return!1}if(this.has(s)){var p=!1,g=yt(this),y=g||p?{observableKind:"set",debugObjectName:this.name_,type:Xi,object:this,oldValue:s}:null;return Kt(function(){c.atom_.reportChanged(),c.data_.delete(s)}),g&&_t(this,y),!0}return!1},l.has=function(s){return this.atom_.reportObserved(),this.data_.has(this.dehanceValue_(s))},l.entries=function(){var s=this.values();return Wa({next:function(){var d=s.next(),p=d.value,g=d.done;return g?{value:void 0,done:g}:{value:[p,p],done:g}}})},l.keys=function(){return this.values()},l.values=function(){this.atom_.reportObserved();var s=this,c=this.data_.values();return Wa({next:function(){var p=c.next(),g=p.value,y=p.done;return y?{value:void 0,done:y}:{value:s.dehanceValue_(g),done:y}}})},l.intersection=function(s){if(Vt(s)&&!Lt(s))return s.intersection(this);var c=new Set(this);return c.intersection(s)},l.union=function(s){if(Vt(s)&&!Lt(s))return s.union(this);var c=new Set(this);return c.union(s)},l.difference=function(s){return new Set(this).difference(s)},l.symmetricDifference=function(s){if(Vt(s)&&!Lt(s))return s.symmetricDifference(this);var c=new Set(this);return c.symmetricDifference(s)},l.isSubsetOf=function(s){return new Set(this).isSubsetOf(s)},l.isSupersetOf=function(s){return new Set(this).isSupersetOf(s)},l.isDisjointFrom=function(s){if(Vt(s)&&!Lt(s))return s.isDisjointFrom(this);var c=new Set(this);return c.isDisjointFrom(s)},l.replace=function(s){var c=this;return Lt(s)&&(s=new Set(s)),Kt(function(){Array.isArray(s)?(c.clear(),s.forEach(function(d){return c.add(d)})):Vt(s)?(c.clear(),s.forEach(function(d){return c.add(d)})):s!=null&&q("Cannot initialize set from "+s)}),this},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},l.toJSON=function(){return Array.from(this)},l.toString=function(){return"[object ObservableSet]"},l[Symbol.iterator]=function(){return this.values()},Jn(i,[{key:"size",get:function(){return this.atom_.reportObserved(),this.data_.size}},{key:Symbol.toStringTag,get:function(){return"Set"}}])})(),Lt=Cn("ObservableSet",$a);function Wa(i){return i[Symbol.toStringTag]="SetIterator",Bl(i)}var Ha=Object.create(null),Ka="remove",Qa=(function(){function i(o,s,c,d){s===void 0&&(s=new Map),d===void 0&&(d=Xp),this.target_=void 0,this.values_=void 0,this.name_=void 0,this.defaultAnnotation_=void 0,this.keysAtom_=void 0,this.changeListeners_=void 0,this.interceptors_=void 0,this.proxy_=void 0,this.isPlainObject_=void 0,this.appliedAnnotations_=void 0,this.pendingKeys_=void 0,this.target_=o,this.values_=s,this.name_=c,this.defaultAnnotation_=d,this.keysAtom_=new nn("ObservableObject.keys"),this.isPlainObject_=Ft(this.target_)}var l=i.prototype;return l.getObservablePropValue_=function(s){return this.values_.get(s).get()},l.setObservablePropValue_=function(s,c){var d=this.values_.get(s);if(d instanceof gt)return d.set(c),!0;if(ut(this)){var p=st(this,{type:Mt,object:this.proxy_||this.target_,name:s,newValue:c});if(!p)return null;c=p.newValue}if(c=d.prepareNewValue_(c),c!==T.UNCHANGED){var g=yt(this),y=!1,P=g||y?{type:Mt,observableKind:"object",debugObjectName:this.name_,object:this.proxy_||this.target_,oldValue:d.value_,name:s,newValue:c}:null;d.setNewValue_(c),g&&_t(this,P)}return!0},l.get_=function(s){return T.trackingDerivation&&!$t(this.target_,s)&&this.has_(s),this.target_[s]},l.set_=function(s,c,d){return d===void 0&&(d=!1),$t(this.target_,s)?this.values_.has(s)?this.setObservablePropValue_(s,c):d?Reflect.set(this.target_,s,c):(this.target_[s]=c,!0):this.extend_(s,{value:c,enumerable:!0,writable:!0,configurable:!0},this.defaultAnnotation_,d)},l.has_=function(s){if(!T.trackingDerivation)return s in this.target_;this.pendingKeys_||(this.pendingKeys_=new Map);var c=this.pendingKeys_.get(s);return c||(c=new Rn(s in this.target_,ji,"ObservableObject.key?",!1),this.pendingKeys_.set(s,c)),c.get()},l.make_=function(s,c){if(c===!0&&(c=this.defaultAnnotation_),c!==!1){if(!(s in this.target_)){var d;if((d=this.target_[Tt])!=null&&d[s])return;q(1,c.annotationType_,this.name_+"."+s.toString())}for(var p=this.target_;p&&p!==Ri;){var g=Ti(p,s);if(g){var y=c.make_(this,s,g,p);if(y===0)return;if(y===1)break}p=Object.getPrototypeOf(p)}Ya(this,c,s)}},l.extend_=function(s,c,d,p){if(p===void 0&&(p=!1),d===!0&&(d=this.defaultAnnotation_),d===!1)return this.defineProperty_(s,c,p);var g=d.extend_(this,s,c,p);return g&&Ya(this,d,s),g},l.defineProperty_=function(s,c,d){d===void 0&&(d=!1),this.keysAtom_;try{ot();var p=this.delete_(s);if(!p)return p;if(ut(this)){var g=st(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:c.value});if(!g)return null;var y=g.newValue;c.value!==y&&(c=An({},c,{value:y}))}if(d){if(!Reflect.defineProperty(this.target_,s,c))return!1}else Nt(this.target_,s,c);this.notifyPropertyAddition_(s,c.value)}finally{lt()}return!0},l.defineObservableProperty_=function(s,c,d,p){p===void 0&&(p=!1),this.keysAtom_;try{ot();var g=this.delete_(s);if(!g)return g;if(ut(this)){var y=st(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:c});if(!y)return null;c=y.newValue}var P=Ga(s),C={configurable:T.safeDescriptors?this.isPlainObject_:!0,enumerable:!0,get:P.get,set:P.set};if(p){if(!Reflect.defineProperty(this.target_,s,C))return!1}else Nt(this.target_,s,C);var R=new Rn(c,d,"ObservableObject.key",!1);this.values_.set(s,R),this.notifyPropertyAddition_(s,R.value_)}finally{lt()}return!0},l.defineComputedProperty_=function(s,c,d){d===void 0&&(d=!1),this.keysAtom_;try{ot();var p=this.delete_(s);if(!p)return p;if(ut(this)){var g=st(this,{object:this.proxy_||this.target_,name:s,type:rn,newValue:void 0});if(!g)return null}c.name||(c.name="ObservableObject.key"),c.context=this.proxy_||this.target_;var y=Ga(s),P={configurable:T.safeDescriptors?this.isPlainObject_:!0,enumerable:!1,get:y.get,set:y.set};if(d){if(!Reflect.defineProperty(this.target_,s,P))return!1}else Nt(this.target_,s,P);this.values_.set(s,new gt(c)),this.notifyPropertyAddition_(s,void 0)}finally{lt()}return!0},l.delete_=function(s,c){if(c===void 0&&(c=!1),this.keysAtom_,!$t(this.target_,s))return!0;if(ut(this)){var d=st(this,{object:this.proxy_||this.target_,name:s,type:Ka});if(!d)return null}try{var p;ot();var g=yt(this),y=!1,P=this.values_.get(s),C=void 0;if(!P&&(g||y)){var R;C=(R=Ti(this.target_,s))==null?void 0:R.value}if(c){if(!Reflect.deleteProperty(this.target_,s))return!1}else delete this.target_[s];if(P&&(this.values_.delete(s),P instanceof Rn&&(C=P.value_),Pa(P)),this.keysAtom_.reportChanged(),(p=this.pendingKeys_)==null||(p=p.get(s))==null||p.set(s in this.target_),g||y){var j={type:Ka,observableKind:"object",object:this.proxy_||this.target_,debugObjectName:this.name_,oldValue:C,name:s};g&&_t(this,j)}}finally{lt()}return!0},l.observe_=function(s,c){return Br(this,s)},l.intercept_=function(s){return Ir(this,s)},l.notifyPropertyAddition_=function(s,c){var d,p=yt(this),g=!1;if(p||g){var y=p||g?{type:rn,observableKind:"object",debugObjectName:this.name_,object:this.proxy_||this.target_,name:s,newValue:c}:null;p&&_t(this,y)}(d=this.pendingKeys_)==null||(d=d.get(s))==null||d.set(!0),this.keysAtom_.reportChanged()},l.ownKeys_=function(){return this.keysAtom_.reportObserved(),qn(this.target_)},l.keys_=function(){return this.keysAtom_.reportObserved(),Object.keys(this.target_)},i})();function jn(i,l){var o;if($t(i,$))return i;var s=(o=l?.name)!=null?o:"ObservableObject",c=new Qa(i,new Map,String(s),uh(l));return Nr(i,$,c),i}var rv=Cn("ObservableObjectAdministration",Qa);function Ga(i){return Ha[i]||(Ha[i]={get:function(){return this[$].getObservablePropValue_(i)},set:function(o){return this[$].setObservablePropValue_(i,o)}})}function qi(i){return Mi(i)?rv(i[$]):!1}function Ya(i,l,o){var s;(s=i.target_[Tt])==null||delete s[o]}var iv=qa(0),ov=(function(){var i=!1,l={};return Object.defineProperty(l,"0",{set:function(){i=!0}}),Object.create(l)[0]=1,i===!1})(),bl=0,Xa=function(){};function lv(i,l){Object.setPrototypeOf?Object.setPrototypeOf(i.prototype,l):i.prototype.__proto__!==void 0?i.prototype.__proto__=l:i.prototype=l}lv(Xa,Array.prototype);var Dl=(function(i){function l(s,c,d,p){var g;return d===void 0&&(d="ObservableArray"),p===void 0&&(p=!1),g=i.call(this)||this,on(function(){var y=new jl(d,c,p,!0);y.proxy_=g,na(g,$,y),s&&s.length&&g.spliceWithArray(0,0,s),ov&&Object.defineProperty(g,"0",iv)}),g}la(l,i);var o=l.prototype;return o.concat=function(){this[$].atom_.reportObserved();for(var c=arguments.length,d=new Array(c),p=0;pbl){for(var l=bl;l=0&&o++;break}i=tc(i),l=tc(l);var g=p==="[object Array]";if(!g){if(typeof i!="object"||typeof l!="object")return!1;var y=i.constructor,P=l.constructor;if(y!==P&&!(it(y)&&y instanceof y&&it(P)&&P instanceof P)&&"constructor"in i&&"constructor"in l)return!1}if(o===0)return!1;o<0&&(o=-1),s=s||[],c=c||[];for(var C=s.length;C--;)if(s[C]===i)return c[C]===l;if(s.push(i),c.push(l),g){if(C=i.length,C!==l.length)return!1;for(;C--;)if(!Il(i[C],l[C],o-1,s,c))return!1}else{var R=Object.keys(i),j=R.length;if(Object.keys(l).length!==j)return!1;for(var Y=0;Y"u"&&q("MobX requires global '"+i+"' to be available or polyfilled")}),typeof __MOBX_DEVTOOLS_GLOBAL_HOOK__=="object"&&__MOBX_DEVTOOLS_GLOBAL_HOOK__.injectMobx({spy:Oh,extras:{getDebugName:zl},$mobx:$});function rc(){return{bpm:180,keyNote:0,instruments:[],flavor:"Salsa"}}class fv{constructor(){this.NS_BEAT_MACHINE="http://www.salsabeatmachine.org/xns/bm",this.NS_INSTRUMENTS="http://www.salsabeatmachine.org/xns/instruments"}loadMachine(l){const o=l.getElementsByTagNameNS(this.NS_BEAT_MACHINE,"Machine")[0],s=rc(),c={bpm:d=>s.bpm=parseInt(d.textContent,10),keyNote:d=>s.keyNote=parseInt(d.textContent,10),flavor:d=>s.flavor=d.textContent,instrumentList:d=>s.instruments=this.processInstrumentList(d)};return this.childElements(o).forEach(d=>{const p=c[d.localName];p&&d.namespaceURI===this.NS_BEAT_MACHINE&&p(d)}),s}loadInstrument(l){const o=this.childElements(l).filter(p=>p.namespaceURI===this.NS_INSTRUMENTS);function s(p,g){const y=o.filter(P=>P.localName===p)[0];return y&&y.textContent?y.textContent:g}const c={id:(l.localName||"").toLowerCase(),title:l.getAttribute("title")||"",enabled:s("enabled","false")==="true",activeProgram:parseInt(s("activeProgram","0"),10),programs:[],respectsClave:s("respectsClave","false")==="true",keyedInstrument:s("keyedInstrument","false")==="true",pitchOffset:parseInt(s("pitchOffset","0"),10),playBothHands:s("playBothHands","false")==="true",leftHandPitchOffset:parseInt(s("leftHandPitchOffset","0"),10),volume:parseFloat(s("volume","1.0"))},d=o.filter(p=>p.localName==="programs")[0];return d&&(c.programs=this.childElements(d).filter(p=>p.namespaceURI===this.NS_BEAT_MACHINE&&p.localName==="Program").map(p=>this.loadProgram(p))),c}loadProgram(l){return{title:l.getAttribute("title")||"",length:parseInt(l.getAttribute("length"),10)||0,notes:this.childElements(l).filter(o=>o.namespaceURI===this.NS_BEAT_MACHINE&&o.localName==="Note").map(o=>({index:parseInt(o.getAttribute("index"),10)||0,pitch:parseInt(o.getAttribute("pitch"),10)||0,velocity:parseFloat(o.getAttribute("velocity"))||void 0}))}}processInstrumentList(l){return this.childElements(l).filter(o=>o.namespaceURI===this.NS_INSTRUMENTS).map(o=>this.loadInstrument(o))}childElements(l){return Array.from(l.childNodes).filter(o=>o.nodeType===o.ELEMENT_NODE)}}let Zi="";function ic(i){Zi=i.endsWith("/")?i.slice(0,-1):i}function dv(){return Zi}async function pv(i){let l=i.endsWith(".xml")?`/assets/machines/${i}`:`/assets/machines/${i}.xml`;Zi&&(l=`${Zi}${l}`);const o=await fetch(l);if(!o.ok)throw new Error(`Failed to load machine from ${l}: ${o.statusText}`);const s=await o.text(),c=new DOMParser().parseFromString(s,"text/xml");return new fv().loadMachine(c)}if(!Be.useState)throw new Error("mobx-react-lite requires React with Hooks support");if(!Gh)throw new Error("mobx-react-lite@3 requires mobx at least version 6 to be available");var hv=Ys();function vv(i){i()}function mv(i){i||(i=vv),Ih({reactionScheduler:i})}function gv(i){return Bh(i)}var yv=1e4,_v=1e4,wv=(function(){function i(l){var o=this;Object.defineProperty(this,"finalize",{enumerable:!0,configurable:!0,writable:!0,value:l}),Object.defineProperty(this,"registrations",{enumerable:!0,configurable:!0,writable:!0,value:new Map}),Object.defineProperty(this,"sweepTimeout",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"sweep",{enumerable:!0,configurable:!0,writable:!0,value:function(s){s===void 0&&(s=yv),clearTimeout(o.sweepTimeout),o.sweepTimeout=void 0;var c=Date.now();o.registrations.forEach(function(d,p){c-d.registeredAt>=s&&(o.finalize(d.value),o.registrations.delete(p))}),o.registrations.size>0&&o.scheduleSweep()}}),Object.defineProperty(this,"finalizeAllImmediately",{enumerable:!0,configurable:!0,writable:!0,value:function(){o.sweep(0)}})}return Object.defineProperty(i.prototype,"register",{enumerable:!1,configurable:!0,writable:!0,value:function(l,o,s){this.registrations.set(s,{value:o,registeredAt:Date.now()}),this.scheduleSweep()}}),Object.defineProperty(i.prototype,"unregister",{enumerable:!1,configurable:!0,writable:!0,value:function(l){this.registrations.delete(l)}}),Object.defineProperty(i.prototype,"scheduleSweep",{enumerable:!1,configurable:!0,writable:!0,value:function(){this.sweepTimeout===void 0&&(this.sweepTimeout=setTimeout(this.sweep,_v))}}),i})(),Sv=typeof FinalizationRegistry<"u"?FinalizationRegistry:wv,Ul=new Sv(function(i){var l;(l=i.reaction)===null||l===void 0||l.dispose(),i.reaction=null}),Fl={exports:{}},Vl={};var oc;function Ev(){if(oc)return Vl;oc=1;var i=Ci();function l(R,j){return R===j&&(R!==0||1/R===1/j)||R!==R&&j!==j}var o=typeof Object.is=="function"?Object.is:l,s=i.useState,c=i.useEffect,d=i.useLayoutEffect,p=i.useDebugValue;function g(R,j){var Y=j(),le=s({inst:{value:Y,getSnapshot:j}}),H=le[0].inst,K=le[1];return d(function(){H.value=Y,H.getSnapshot=j,y(H)&&K({inst:H})},[R,Y,j]),c(function(){return y(H)&&K({inst:H}),R(function(){y(H)&&K({inst:H})})},[R]),p(Y),Y}function y(R){var j=R.getSnapshot;R=R.value;try{var Y=j();return!o(R,Y)}catch{return!0}}function P(R,j){return j()}var C=typeof window>"u"||typeof window.document>"u"||typeof window.document.createElement>"u"?P:g;return Vl.useSyncExternalStore=i.useSyncExternalStore!==void 0?i.useSyncExternalStore:C,Vl}var lc;function kv(){return lc||(lc=1,Fl.exports=Ev()),Fl.exports}var xv=kv();function uc(i){i.reaction=new Ht("observer".concat(i.name),function(){var l;i.stateVersion=Symbol(),(l=i.onStoreChange)===null||l===void 0||l.call(i)})}function Ov(i,l){l===void 0&&(l="observed");var o=pl.useRef(null);if(!o.current){var s={reaction:null,onStoreChange:null,stateVersion:Symbol(),name:l,subscribe:function(g){return Ul.unregister(s),s.onStoreChange=g,s.reaction||(uc(s),s.stateVersion=Symbol()),function(){var y;s.onStoreChange=null,(y=s.reaction)===null||y===void 0||y.dispose(),s.reaction=null}},getSnapshot:function(){return s.stateVersion}};o.current=s}var c=o.current;c.reaction||(uc(c),Ul.register(o,c,c)),pl.useDebugValue(c.reaction,gv),xv.useSyncExternalStore(c.subscribe,c.getSnapshot,c.getSnapshot);var d,p;if(c.reaction.track(function(){try{d=i()}catch(g){p=g}}),p)throw p;return d}var $l,Wl,sc=typeof Symbol=="function"&&Symbol.for,Pv=(Wl=($l=Object.getOwnPropertyDescriptor(function(){},"name"))===null||$l===void 0?void 0:$l.configurable)!==null&&Wl!==void 0?Wl:!1,ac=sc?Symbol.for("react.forward_ref"):typeof Be.forwardRef=="function"&&Be.forwardRef(function(i){return null}).$$typeof,cc=sc?Symbol.for("react.memo"):typeof Be.memo=="function"&&Be.memo(function(i){return null}).$$typeof;function Cv(i,l){var o;if(cc&&i.$$typeof===cc)throw new Error("[mobx-react-lite] You are trying to use `observer` on a function component wrapped in either another `observer` or `React.memo`. The observer already applies 'React.memo' for you.");var s=(o=void 0)!==null&&o!==void 0?o:!1,c=i,d=i.displayName||i.name;if(ac&&i.$$typeof===ac&&(s=!0,c=i.render,typeof c!="function"))throw new Error("[mobx-react-lite] `render` property of ForwardRef was not a function");var p=function(g,y){return Ov(function(){return c(g,y)},d)};return p.displayName=i.displayName,Pv&&Object.defineProperty(p,"name",{value:i.name,writable:!0,configurable:!0}),i.contextTypes&&(p.contextTypes=i.contextTypes),s&&(p=Be.forwardRef(p)),p=Be.memo(p),Nv(i,p),p}var Av={$$typeof:!0,render:!0,compare:!0,type:!0,displayName:!0};function Nv(i,l){Object.keys(i).forEach(function(o){Av[o]||Object.defineProperty(l,o,Object.getOwnPropertyDescriptor(i,o))})}var Hl;mv(hv.unstable_batchedUpdates),Hl=Ul.finalizeAllImmediately;class Tv{constructor(l,o){this.context=l,this.instrument=o,this.gainMap={},this.scheduledSamples=new Map,this.gainNode=l.createGain(),this.gainNode.connect(this.context.destination),this.disposer=Ra(()=>{this.gainNode.gain.value=this.instrument.enabled?this.instrument.volume:0})}reset(l=!1){for(const[o,s]of Array.from(this.scheduledSamples.entries()))(l||s>=this.context.currentTime)&&o.stop();this.scheduledSamples.clear()}createNoteDestination(l){if(typeof l!="number"||l===1)return this.gainNode;if(!this.gainMap[l]){const o=this.context.createGain();o.connect(this.gainNode),o.gain.value=l,this.gainMap[l]=o}return this.gainMap[l]}registerSample(l,o){l.addEventListener("ended",()=>{this.scheduledSamples.delete(l)}),this.scheduledSamples.set(l,o)}dispose(){this.disposer(),this.reset(!0)}}class Rv{constructor(l){this.mixer=l,this.nextSampleIndex=0,this.animationFrameRequest=null,this.audioTimeDelta=0,this.machineDisposers=[],this.instrumentPlayers=new Map,this.mixerNotReadyLogged=!1,this.interval=null,this._machine=rc(),this.beat=0,this.stopAllInstruments=(o=!1)=>{for(const s of Array.from(this.instrumentPlayers.values()))s.reset(o)},this.scheduleBuffers=()=>{const o=this.mixer.context;if(o&&this.mixer.ready){this.mixerNotReadyLogged=!1;const s=this.beatTime/2,c=this.getBeatIndex();for(;this.nextSampleIndex-c*2<64;){const d=this.nextSampleIndex;this.machine.instruments.forEach(p=>{const g=this.getInstrumentPlayer(o,p);this.instrumentNotes(p,d).forEach(y=>{this.mixer.play(y.sampleName,g,d*s-this.audioTimeDelta,y.velocity)})}),this.nextSampleIndex++}}else this.mixerNotReadyLogged||(console.log("Mixer not ready yet - context:",!!o,"ready:",this.mixer.ready),this.mixerNotReadyLogged=!0);this.interval=window.setTimeout(()=>this.scheduleBuffers(),1e3)},Yh(this)}async init(){await this.mixer.init(),console.log("BeatEngine initialized - AudioBackend state:",{ready:this.mixer.ready,hasContext:!!this.mixer.context})}get machine(){return this._machine}set machine(l){if(l!==this._machine){if(this._machine=l,this.machineDisposers.forEach(o=>o()),this.machineDisposers=[],this.instrumentPlayers.forEach(o=>o.dispose()),this.instrumentPlayers.clear(),!this.machine)return;this.playing&&(this.stop(),this.play()),this.machineDisposers.push(Ml(this.machine,"bpm",({oldValue:o,newValue:s})=>{if(this.playing){this.interval&&clearTimeout(this.interval);const c=this.mixer.getCurrentTime();if(o==null||s==null||o===0||s===0){console.warn("⚠️ Ugyldige verdier for BPM endring: oldValue =",o,", newValue =",s);return}this.audioTimeDelta=(c+(this.audioTimeDelta||0))*(o/s)-c,isNaN(this.audioTimeDelta)&&(console.warn("⚠️ audioTimeDelta ble NaN — setter den til 0."),this.audioTimeDelta=0),this.nextSampleIndex=Math.ceil(this.getBeatIndex()*2),this.stopAllInstruments(),this.scheduleBuffers()}}),Ml(this.machine,"keyNote",()=>{if(this.playing)for(const o of this.machine.instruments){if(!o.keyedInstrument)continue;const s=this.instrumentPlayers.get(o);s&&this.rescheduleInstrument(o,s)}}))}}play(){this.mixer.context?.resume(),this.scheduleBuffers(),this.beatTick()}stop(){this.interval&&(clearTimeout(this.interval),this.interval=null),this.animationFrameRequest&&(cancelAnimationFrame(this.animationFrameRequest),this.animationFrameRequest=null),this.stopAllInstruments(!0),this.mixer.reset(),this.audioTimeDelta=0,this.nextSampleIndex=0}get playing(){return this.interval!=null}get beatTime(){const l=60/this.machine.bpm;return this.machine.flavor==="Merengue"?l/2:l}getBeatIndex(){return(this.mixer.getCurrentTime()+this.audioTimeDelta)/this.beatTime}getInstrumentPlayer(l,o){const s=this.instrumentPlayers.get(o);if(s)return s;{const c=new Tv(l,o);return this.machineDisposers.push(Ml(o,"activeProgram",()=>this.rescheduleInstrument(o,c))),this.instrumentPlayers.set(o,c),c}}rescheduleInstrument(l,o){o.reset();const s=this.beatTime/2;for(let c=Math.ceil(this.getBeatIndex()*2);c{this.mixer.play(d.sampleName,o,c*s-this.audioTimeDelta,d.velocity)})}instrumentNotes(l,o){const s=[];if(l.enabled){const c=l.programs[l.activeProgram];o%=c.length,c.notes.filter(d=>d.index===o).forEach(d=>{let p=d.pitch;l.keyedInstrument&&(p+=this.machine.keyNote),d.hand!=="left"&&(s.push({sampleName:l.id+"-"+(p+l.pitchOffset),velocity:d.velocity}),d.pianoTonic&&s.push({sampleName:l.id+"-"+(p+l.pitchOffset+12),velocity:d.velocity})),l.playBothHands&&d.hand!=="right"&&s.push({sampleName:l.id+"-"+(p+l.leftHandPitchOffset),velocity:d.velocity})})}return s}beatTick(){this.beat=this.getBeatIndex(),this.animationFrameRequest=requestAnimationFrame(()=>this.beatTick())}}class Mv{constructor(l=""){this.baseUrl=l,this.zeroTime=null,this.ready=!1;const s=typeof MediaSource<"u"&&MediaSource.isTypeSupported('audio/webm;codecs="vorbis"')?"/assets/audio/main.webm":"/assets/audio/main.mp3";this.audioFormat=l?`${l}/${s}`:s,console.log("AudioBackend created - will load audio after init()")}async init(l=typeof AudioContext<"u"?new AudioContext:void 0){if(this._context=l,this._context){this.zeroTime=this._context.currentTime,console.log("🎵 AudioBackend initialized — zeroTime set to:",this.zeroTime);try{await Promise.all([this.loadBank(this.audioFormat),this.loadBankDescriptor("/assets/audio/main.json")]),console.log("✅ Audio files loaded successfully")}catch(o){console.error("❌ Error loading audio files:",o),console.error("Please ensure the following files exist in public/assets/audio/:"),console.error(" - main.webm (or main.mp3)"),console.error(" - main.json"),console.error("Download them from: https://www.salsabeatmachine.org/assets/audio/")}}}get context(){return this._context}async loadBank(l){try{console.log("Loading audio bank from:",l);const o=await fetch(l);if(!o.ok)throw new Error(`Failed to fetch ${l}: ${o.status} ${o.statusText}`);const s=await o.arrayBuffer();if(!this.context)throw new Error("AudioContext not initialized - cannot decode audio");this.buffer=await new Promise((c,d)=>{this.context?.decodeAudioData(s,c,d)}),this.ready=!0,console.log("✅ Audio bank loaded successfully")}catch(o){throw console.error("❌ Failed to load audio bank:",o),o}}async loadBankDescriptor(l){try{const o=this.baseUrl&&!l.startsWith("http")?`${this.baseUrl}/${l}`:l;console.log("Loading bank descriptor from:",o);const s=await fetch(o);if(!s.ok)throw new Error(`Failed to fetch ${o}: ${s.status} ${s.statusText}`);this.bankDescriptor=await s.json(),console.log("✅ Bank descriptor loaded successfully")}catch(o){throw console.error("❌ Failed to load bank descriptor:",o),o}}play(l,o,s,c){const d=this.context.createBufferSource();d.connect(o.createNoteDestination(c)),d.buffer=this.buffer;const p=this.bankDescriptor[l];this.zeroTime===null&&(this.zeroTime=this.context.currentTime,console.log("⚠️ Warning: zeroTime was null — setting it now to:",this.zeroTime));const g=this.zeroTime+s;d.start(g,p[1]/44100,p[2]/44100),o.registerSample(d,g)}reset(){this.zeroTime=null}getCurrentTime(){return this.zeroTime==null?(console.warn("⏱️ Warning: zeroTime is null, returning 0 for current time."),0):this.context.currentTime-this.zeroTime}}function Lv(){if(typeof document>"u"||document.getElementById("beat-machine-widget-styles"))return;const i=document.createElement("style");i.id="beat-machine-widget-styles",i.textContent=` + .beat-widget { + display: inline-flex; + align-items: center; + gap: 1rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(0, 245, 255, 0.3); + padding: 0; + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(0, 245, 255, 0.5); + } + + .beat-widget .playButton:active { + transform: scale(0.95); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #00f5ff; + text-align: center; + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 255, 255, 0.2); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + cursor: pointer; + box-shadow: 0 2px 6px rgba(0, 245, 255, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + cursor: pointer; + border: none; + box-shadow: 0 2px 6px rgba(0, 245, 255, 0.4); + } + `,document.head.appendChild(i)}const fc=Cv(({machine:i,instruments:l,initialBpm:o,autoplay:s=!1})=>{const[c,d]=Be.useState(!1),[p,g]=Be.useState(null);Be.useEffect(()=>{Lv()},[]),Be.useEffect(()=>((async()=>{const R=dv(),j=new Mv(R),Y=new Rv(j);await Y.init(),g(Y)})(),()=>{p&&p.stop()}),[]),Be.useEffect(()=>{l&&l.length>0&&i.instruments.forEach(C=>{C.enabled=l.includes(C.id)})},[l,i]),Be.useEffect(()=>{o!==void 0&&i.bpm!==o&&(i.bpm=o)},[o,i]);const y=()=>{p&&(c?(p.stop(),d(!1)):(p.machine=i,p.play(),d(!0)))},P=C=>{i.bpm=parseInt(C.target.value,10)};return mt.jsxs("div",{className:"beat-widget",children:[mt.jsx("button",{className:`playButton ${c?"playing":""}`,onClick:y,disabled:!p,"aria-label":c?"Pause":"Play",children:c?mt.jsx("svg",{viewBox:"0 0 24 24",children:mt.jsx("path",{d:"M6 4h4v16H6V4zm8 0h4v16h-4V4z"})}):mt.jsx("svg",{viewBox:"0 0 24 24",children:mt.jsx("path",{d:"M8 5v14l11-7z"})})}),mt.jsxs("div",{className:"bpmControl",children:[mt.jsxs("label",{className:"bpmLabel",children:[i.bpm," BPM"]}),mt.jsx("input",{type:"range",className:"slider",min:"60",max:"200",value:i.bpm,onChange:P,"aria-label":"Tempo"})]})]})});function jv(){if(document.getElementById("beat-machine-widget-styles"))return;const i=document.createElement("style");i.id="beat-machine-widget-styles",i.textContent=` + .beat-widget { + display: inline-flex; + align-items: center; + gap: 1rem; + padding: 0.75rem 1.25rem; + background: linear-gradient(135deg, rgba(102, 126, 234, 0.1), rgba(0, 245, 255, 0.1)); + border: 1px solid rgba(0, 245, 255, 0.3); + border-radius: 0.75rem; + backdrop-filter: blur(10px); + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', sans-serif; + } + + .beat-widget .playButton { + width: 3rem; + height: 3rem; + border-radius: 50%; + border: none; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + color: white; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.3s ease; + box-shadow: 0 4px 12px rgba(0, 245, 255, 0.3); + } + + .beat-widget .playButton:hover { + transform: scale(1.05); + box-shadow: 0 6px 16px rgba(0, 245, 255, 0.5); + } + + .beat-widget .playButton:disabled { + opacity: 0.5; + cursor: not-allowed; + } + + .beat-widget .playButton.playing { + background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%); + } + + .beat-widget .playButton svg { + width: 1.25rem; + height: 1.25rem; + fill: currentColor; + } + + .beat-widget .bpmControl { + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 120px; + } + + .beat-widget .bpmLabel { + font-size: 0.875rem; + font-weight: 600; + color: #00f5ff; + text-align: center; + } + + .beat-widget .slider { + width: 100%; + height: 6px; + border-radius: 3px; + background: rgba(255, 255, 255, 0.2); + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .beat-widget .slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + cursor: pointer; + box-shadow: 0 2px 6px rgba(0, 245, 255, 0.4); + } + + .beat-widget .slider::-moz-range-thumb { + width: 16px; + height: 16px; + border-radius: 50%; + background: linear-gradient(135deg, #667eea 0%, #00f5ff 100%); + cursor: pointer; + border: none; + box-shadow: 0 2px 6px rgba(0, 245, 255, 0.4); + } + `,document.head.appendChild(i)}class bv{constructor(){this.instances=new Map}async init(l="[data-beat-widget]"){jv();const o=document.querySelectorAll(l),s=Array.from(o);for(const c of s){if(!(c instanceof HTMLElement))continue;const d=this.parseConfig(c);await this.create(c,d)}}parseConfig(l){const o=l.dataset.instruments?.split(",").map(g=>g.trim())||[],s=l.dataset.bpm?parseInt(l.dataset.bpm,10):120,c=l.dataset.machine||"salsa",d=l.dataset.autoplay==="true",p={};return l.dataset.programs&&l.dataset.programs.split(",").forEach(y=>{const[P,C]=y.split(":").map(R=>R.trim());P&&C&&(p[P]=parseInt(C,10))}),{instruments:o,programs:p,bpm:s,machine:c,autoplay:d}}async create(l,o){const s=o.machine||"salsa",c=await pv(s),d=Oe(c);o.bpm&&(d.bpm=o.bpm),o.programs&&d.instruments.forEach(y=>{o.programs[y.id]!==void 0&&(y.activeProgram=o.programs[y.id])});const p=vp.createRoot(l),g={play:()=>{},pause:()=>{},stop:()=>{},setBPM:y=>{d.bpm=y},destroy:()=>{p.unmount(),this.instances.delete(l)}};return p.render(mt.jsx(pl.StrictMode,{children:mt.jsx(fc,{machine:d,instruments:o.instruments,initialBpm:o.bpm,autoplay:o.autoplay})})),this.instances.set(l,{root:p,instance:g}),g}destroy(l){const o=this.instances.get(l);o&&o.instance.destroy()}}const Fr=new bv;window.BeatMachineWidget={init:i=>Fr.init(i),create:(i,l)=>Fr.create(i,l),destroy:i=>Fr.destroy(i),setBaseUrl:i=>{ic(i),console.log("✅ Beat Machine Widget base URL set to:",i)}},window.BEAT_MACHINE_BASE_URL&&(ic(window.BEAT_MACHINE_BASE_URL),console.log("✅ Beat Machine Widget loaded with base URL:",window.BEAT_MACHINE_BASE_URL)),document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{Fr.init()}):Fr.init(),Bs.WidgetCompact=fc,Object.defineProperty(Bs,Symbol.toStringTag,{value:"Module"})})(this.BeatMachineWidget=this.BeatMachineWidget||{});