diff --git a/.gitattributes b/.gitattributes
deleted file mode 100644
index dfe0770..0000000
--- a/.gitattributes
+++ /dev/null
@@ -1,2 +0,0 @@
-# Auto detect text files and perform LF normalization
-* text=auto
diff --git a/.github/workflows/pr-build.yml b/.github/workflows/pr-build.yml
new file mode 100644
index 0000000..bd1371e
--- /dev/null
+++ b/.github/workflows/pr-build.yml
@@ -0,0 +1,44 @@
+name: Build
+
+on:
+ pull_request:
+ branches:
+ - '**'
+ paths-ignore:
+ - '**.md'
+ - '**.adoc'
+ - 'docs/**'
+ - '.github/**'
+ - '.husky/**'
+ - 'LICENSE'
+ - 'COPYRIGHT'
+ - '.prettierignore'
+ - '.gitignore'
+
+concurrency:
+ group: build-${{ github.event.pull_request.number }}
+ cancel-in-progress: true
+
+jobs:
+ build:
+ name: Next.js Build Check
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: lts/*
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Build
+ run: pnpm build
diff --git a/.github/workflows/pr-lint.yml b/.github/workflows/pr-lint.yml
new file mode 100644
index 0000000..2fca289
--- /dev/null
+++ b/.github/workflows/pr-lint.yml
@@ -0,0 +1,34 @@
+name: Lint
+
+on:
+ pull_request:
+ branches:
+ - '**'
+
+concurrency:
+ group: lint-${{ github.event.pull_request.number }}
+ cancel-in-progress: true
+
+jobs:
+ lint:
+ name: ESLint Check
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Setup pnpm
+ uses: pnpm/action-setup@v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: lts/*
+ cache: pnpm
+
+ - name: Install dependencies
+ run: pnpm install --frozen-lockfile
+
+ - name: Run ESLint
+ run: pnpm lint
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..99ed15f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,64 @@
+# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
+
+# dependencies
+/node_modules
+/.pnp
+.pnp.*
+.yarn/*
+!.yarn/patches
+!.yarn/plugins
+!.yarn/releases
+!.yarn/versions
+
+# testing
+/coverage
+
+# next.js
+/.next/
+/out/
+
+# production
+/build
+
+# misc
+.DS_Store
+*.pem
+
+# debug
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+.pnpm-debug.log*
+
+# env files (can opt-in for committing if needed)
+.env*
+!.env.example
+
+# local
+attached_assets
+old-site
+src/app/~test/locale
+*.zip
+.agents
+.vercel
+*.log
+
+# Editor / IDE
+.idea/
+.replit
+replit.nix
+*.suo
+*.ntvs*
+*.njsproj
+*.sln
+*.sw?
+
+# typescript
+*.tsbuildinfo
+next-env.d.ts
+
+# playwright
+/test-results/
+/playwright-report/
+/blob-report/
+/playwright/.cache/
diff --git a/.hintrc b/.hintrc
deleted file mode 100644
index 768aee6..0000000
--- a/.hintrc
+++ /dev/null
@@ -1,27 +0,0 @@
-{
- "extends": [
- "development"
- ],
- "hints": {
- "no-inline-styles": "off",
- "axe/forms": [
- "default",
- {
- "label": "off"
- }
- ],
- "axe/name-role-value": [
- "default",
- {
- "button-name": "off"
- }
- ],
- "axe/aria": [
- "default",
- {
- "aria-required-parent": "off"
- }
- ],
- "button-type": "off"
- }
-}
\ No newline at end of file
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100644
index 0000000..5fcfc75
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,3 @@
+#!/usr/bin/env sh
+
+pnpm lint-staged
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..27b9377
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,34 @@
+# Dependencies
+node_modules
+
+# Next.js
+.next
+out
+build
+dist
+
+# Generated files
+next-env.d.ts
+*.d.ts
+*.tsbuildinfo
+
+# Config & lock files
+package-lock.json
+yarn.lock
+pnpm-lock.yaml
+
+# Env files
+.env
+.env.*
+
+# Public assets
+public
+
+# Cache / local
+.cache
+.turbo
+.local
+.agents
+old-site/
+
+tsconfig.json
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
new file mode 100644
index 0000000..7428885
--- /dev/null
+++ b/.prettierrc
@@ -0,0 +1,12 @@
+{
+ "semi": true,
+ "singleQuote": true,
+ "tabWidth": 2,
+ "useTabs": false,
+ "trailingComma": "all",
+ "printWidth": 100,
+ "bracketSpacing": true,
+ "bracketSameLine": false,
+ "arrowParens": "always",
+ "endOfLine": "lf"
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
deleted file mode 100644
index 5480842..0000000
--- a/.vscode/settings.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "kiroAgent.configureMCP": "Disabled"
-}
\ No newline at end of file
diff --git a/404.html b/404.html
deleted file mode 100644
index abf6249..0000000
--- a/404.html
+++ /dev/null
@@ -1,48 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 404
-
-
-
- 404 Not Found
- Whoops! That page doesn't exist.
- Go to Home
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/COPYRIGHT b/COPYRIGHT
new file mode 100644
index 0000000..bfee0d5
--- /dev/null
+++ b/COPYRIGHT
@@ -0,0 +1,10 @@
+OpenTune Web
+
+Copyright (C) 2024 Arturo Cervantes
+Copyright (C) 2026 Rajnish Kumar
+
+This project is licensed under the GNU General Public License v3.0
+(GPL-3.0).
+
+For license terms, permissions, and conditions, refer to the LICENSE
+file included with this repository.
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
index e62ec04..f288702 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,4 @@
-GNU GENERAL PUBLIC LICENSE
+ GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc.
diff --git a/PdP.html b/PdP.html
deleted file mode 100644
index c1a9614..0000000
--- a/PdP.html
+++ /dev/null
@@ -1,198 +0,0 @@
-
-
-
-
-
-
-
-
-
-
- OpenTune - Política de Privacidad
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 1. Recopilación de Información
-
- En OpenTune, recopilamos información personal limitada necesaria para
- proporcionar nuestros servicios de música. Esto puede incluir su nombre, dirección de correo
- electrónico, preferencias de música y datos de uso.
-
-
-
-
- 2. Uso de la Información
-
- La información recopilada se utiliza para mejorar la experiencia de
- usuario y brindar contenido personalizado. Podemos utilizar esta información para recomendar
- música, crear listas de reproducción personalizadas y comunicarnos con usted sobre
- actualizaciones y novedades en la plataforma.
-
-
-
-
- 3. Compartir Información
-
- No compartimos información personal con terceros sin su
- consentimiento, excepto en situaciones legales donde esté permitido por la ley. Podemos
- compartir información de forma anónima para realizar análisis y mejorar nuestros servicios.
-
-
-
-
- 4. Seguridad
-
- Implementamos medidas de seguridad para proteger su información
- personal contra accesos no autorizados, uso indebido o divulgación. Utilizamos cifrado de datos
- y otras tecnologías para garantizar la seguridad de sus datos en nuestra plataforma.
-
-
-
-
- 5. Cookies y Tecnologías Similares
-
- Utilizamos cookies y tecnologías similares para mejorar su experiencia
- en OpenTune. Estas tecnologías nos permiten recordar sus preferencias, analizar el rendimiento
- de la plataforma y personalizar el contenido que se le muestra.
-
-
-
-
- Información importante: Puede optar por no aceptar cookies
- en la configuración de su navegador, pero esto puede afectar el funcionamiento de ciertas
- características de la aplicación.
-
-
-
-
-
-
- 6. Cambios en la Política de Privacidad
-
- Nos reservamos el derecho de modificar nuestra Política de Privacidad
- en cualquier momento. Cualquier cambio en la política se publicará en esta página. Le
- recomendamos revisar periódicamente esta política para mantenerse informado sobre cómo
- protegemos su información.
-
-
-
-
-
-
-
-
Estamos aquí para
- ayudar. Contáctenos si necesita más información.
-
-
- Contáctenos
-
-
-
-
-
-
-
-
-
-
© 2024 OpenTune™ . Todos los derechos reservados.
-
-
-
-
\ No newline at end of file
diff --git a/README.adoc b/README.adoc
new file mode 100644
index 0000000..b8f92ad
--- /dev/null
+++ b/README.adoc
@@ -0,0 +1,140 @@
+= OpenTune Web
+:toc: macro
+:toc-title: Table of Contents
+:icons: font
+:experimental:
+:source-highlighter: highlight.js
+
+image:https://img.shields.io/badge/License-GPL--3.0-blue.svg[License: GPL v3, link=https://www.gnu.org/licenses/gpl-3.0]
+image:https://img.shields.io/badge/Next.js-16-black?logo=next.js[Next.js, link=https://nextjs.org/]
+image:https://img.shields.io/badge/Tailwind_CSS-4.3-38B2AC?logo=tailwind-css[Tailwind CSS, link=https://tailwindcss.com/]
+image:https://img.shields.io/badge/pnpm-11.5.3-orange?logo=pnpm[pnpm, link=https://pnpm.io/]
+image:https://img.shields.io/badge/PRs-welcome-brightgreen.svg[PRs Welcome]
+
+Official website and landing page for *OpenTune*, an advanced YouTube Music client for Android featuring Material Design 3.
+
+🚀 **Explore the website at: https://opentune.netlify.app[opentune.netlify.app]**
+
+---
+
+toc::[]
+
+== 📖 Overview
+
+OpenTune Web serves as the primary hub for the OpenTune project. It provides users with:
+* Detailed feature showcases.
+* Visual previews (screenshots) of the Android application.
+* Direct download links for stable releases.
+* Access to the community, support, and contribution guidelines.
+
+== 📱 OpenTune Android App Features
+
+OpenTune is built to provide a premium music experience without the limitations of the official app.
+
+* **Ad-Free:** No interruptions, just pure music.
+* **Background Playback:** Listen while using other apps or with the screen off.
+* **Material Design 3:** A modern, clean, and dynamic UI that follows the latest design trends.
+* **Synced Lyrics:** Real-time lyrics for an immersive listening experience.
+* **Offline Mode:** Download tracks and manage your local library efficiently.
+* **Account Integration:** Seamlessly sync your YouTube Music library and preferences.
+* **Advanced Audio:** Smart silence skipping, volume normalization, and playback speed/pitch control.
+* **Android Auto:** Full integration for a safe music experience in your vehicle.
+
+== 🛠️ Web Tech Stack
+
+The website is engineered for speed, responsiveness, and global accessibility.
+
+[cols="1,3"]
+|===
+| Component | Technology
+
+| **Framework**
+| Next.js 16 (App Router)
+
+| **Styling**
+| Tailwind CSS 4.3
+
+| **Internationalization**
+| `next-intl`
+
+| **Package Manager**
+| pnpm
+
+| **Icons**
+| Custom SVG System + Lucide React
+
+| **Deployment**
+| Netlify
+|===
+
+== 🚀 Getting Started (Development)
+
+To run the project locally for development or testing:
+
+. **Clone the repository:**
+[source,bash]
+----
+git clone https://github.com/arturo254/opentune-web.git
+cd opentune-web
+----
+
+. **Install dependencies:**
+[source,bash]
+----
+pnpm install
+----
+
+. **Start the development server:**
+[source,bash]
+----
+pnpm dev
+----
+
+. **Open the site:**
+Navigate to `http://localhost:3000`.
+
+== 🌍 Localization
+
+OpenTune Web is available in several languages. Translations are managed via JSON files in `src/messages/`.
+
+* 🇺🇸 **English:** `en.json` (Source of truth)
+* 🇪🇸 **Spanish:** `es.json`
+* 🇮🇳 **Hindi:** `hi.json`
+* 🇵🇹 **Portuguese:** `pt.json`
+
+[NOTE]
+====
+To add a new language, create a copy of the English messages file:
+
+`cp -f src/messages/en.json src/messages/[lang].json`
+
+Then translate the messages in the newly created file.
+====
+
+== 🤝 Contributing
+
+We love contributions! Whether you're fixing a bug, suggesting a feature, or helping with translations.
+
+1. **Fork** the repository.
+2. Create your **Feature Branch** (`git switch -c feature/AmazingFeature`).
+3. **Commit** your changes (`git commit -m 'feat: Add some AmazingFeature'`).
+4. **Push** to the branch (`git push origin feature/AmazingFeature`).
+5. Open a **Pull Request**.
+
+== 🔗 Related Projects
+
+* **OpenTune Android App:** image:https://img.shields.io/github/v/release/Arturo254/OpenTune?logo=android&label=AndroidApp&color=181717&labelColor=41444a[Arturo254/OpenTune, link=https://github.com/Arturo254/OpenTune/]
+* **Documentation:** image:https://img.shields.io/badge/Documentation-Read_Now-blue?logo=gitbook[Docs, link=https://opentune.gitbook.io/]
+
+== 📜 License
+
+This project is licensed under the **GPL-3.0 License**. See the `LICENSE` file for more information.
+
+== 💎 Credits
+
+* **Project Founder & Lead Designer:** https://github.com/Arturo254[Arturo Cervantes]
+* **Web Lead Developer:** https://github.com/RajnishKMehta[Rajnish Kumar]
+* **Contributors:** A huge thanks to all the community members helping build OpenTune!
+
+---
+_Built with passion for the Open Source Community._
diff --git a/README.md b/README.md
deleted file mode 100644
index d321ec0..0000000
--- a/README.md
+++ /dev/null
@@ -1,131 +0,0 @@
-# OpenTune
-
-
-
-
- **Un cliente de YouTube Music con Material Design 3, para Android**
-
- [](https://android.com)
- [](https://m3.material.io/)
- [](./LICENSE)
- [](https://github.com/Arturo254/OpenTune/stargazers)
-
- [📱 Descargar APK](https://github.com/Arturo254/OpenTune/releases) • [🌐 Sitio Web](https://opentune.arturodev.com) • [📋 Reportar Bug](https://opentune.arturodev.com/from.html)
-
-
-## ✨ Características Principales
-
-### 🎨 **Diseño Moderno**
-- **Material Design 3**: Interfaz moderna con colores vivos y transiciones suaves
-- **Tema adaptable**: Soporte para modo claro y oscuro
-- **Interfaz intuitiva**: Navegación simplificada y personalizable
-
-### 🎵 **Funciones Musicales**
-- **Reproducción de alta calidad**: Audio optimizado para diferentes dispositivos
-- **Búsqueda avanzada**: Explora un amplio catálogo musical
-- **Recomendaciones inteligentes**: Basadas en tus preferencias y hábitos
-- **Listas de reproducción**: Crea y organiza tu música favorita
-- **Descargas offline**: Escucha sin conexión a internet
-
-### 🔧 **Funcionalidades Avanzadas**
-- **Control por voz**: Maneja la reproducción con comandos de voz
-- **Soporte Chromecast**: Transmite a dispositivos externos
-- **Conectividad Bluetooth**: Reproduce en dispositivos inalámbricos
-- **Gestión de biblioteca**: Organización avanzada con filtros
-- **Sincronización**: Integración completa con tu cuenta de YouTube Music
-
-### 🌍 **Soporte Internacional**
-- **Multilingüe**: Español, English, Português
-- **Localización cultural**: Traducciones precisas y relevantes
-- **Actualizaciones continuas**: Mejoras regulares y nuevas funciones
-
-## 📱 Capturas de Pantalla
-
-
-
-
-
-## 🚀 Instalación
-
-### Android
-1. Descarga el APK desde [Releases](https://github.com/Arturo254/OpenTune/releases)
-2. Habilita la instalación de aplicaciones de fuentes desconocidas
-3. Instala el archivo APK descargado
-4. ¡Disfruta de OpenTune!
-
-### Windows
-1. Descarga el archivo EXE desde [Releases](https://github.com/Arturo254/OpenTune/releases)
-2. Ejecuta el instalador
-3. Sigue las instrucciones de instalación
-
-## 🛠️ Tecnologías Utilizadas
-
-- **Android**: Kotlin
-- **UI/UX**: Material Design 3
-- **Backend**: API de YouTube Music
-- **Web**: HTML5, CSS3, JavaScript
-- **Frameworks**: BeerCSS, Material Icons
-
-## 📋 Requisitos del Sistema
-
-### Android
-- Android 6.0 (API nivel 23) o superior
-- 50 MB de espacio libre
-- Conexión a internet para streaming
-
-### Windows
-- Windows 10 o superior
-- 100 MB de espacio libre
-- Conexión a internet
-
-## 🤝 Contribuir
-
-¡Las contribuciones son bienvenidas! Si quieres contribuir al proyecto:
-
-1. Haz fork del repositorio
-2. Crea una rama para tu feature (`git checkout -b feature/nueva-caracteristica`)
-3. Commit tus cambios (`git commit -am 'Añadir nueva característica'`)
-4. Push a la rama (`git push origin feature/nueva-caracteristica`)
-5. Abre un Pull Request
-
-### 🐛 Reportar Problemas
-- [Reportar un bug](https://opentune.netlify.app/from.html)
-- [Solicitar una característica](https://opentune.netlify.app/from)
-
-## 📞 Contacto y Soporte
-
-- **Desarrollador**: [Arthur Dev Studio](https://g.dev/Arturo254)
-- **WhatsApp**: [+55 76 8479-25](https://wa.me/5576847925)
-- **GitHub**: [@Arturo254](https://github.com/Arturo254)
-- **Sitio Web**: [opentune.netlify.app](https://opentune.netlfy.app)
-
-## 📄 Licencia
-
-Este proyecto está licenciado bajo la Licencia MIT. Consulta el archivo [LICENSE](./LICENSE) para más detalles.
-
-## 🙏 Agradecimientos
-
-- Gracias a todos los [contribuidores](https://opentune.netlify.app/contribuidores/contribuidores) que han hecho posible este proyecto
-- Material Design team por las guías de diseño
-- Comunidad de YouTube Music por el feedback
-
-## 📊 Estadísticas del Proyecto
-
-
-
-
-
-
-
-
-
-
----
-
-
-
-**¿Te gusta OpenTune? ¡Dale una ⭐ al repositorio!**
-
-© 2024 [Arturo.inc™](https://github.com/Arturo254). Todos los derechos reservados.
-
-
diff --git a/SECURITY.md b/SECURITY.md
deleted file mode 100644
index 034e848..0000000
--- a/SECURITY.md
+++ /dev/null
@@ -1,21 +0,0 @@
-# Security Policy
-
-## Supported Versions
-
-Use this section to tell people about which versions of your project are
-currently being supported with security updates.
-
-| Version | Supported |
-| ------- | ------------------ |
-| 5.1.x | :white_check_mark: |
-| 5.0.x | :x: |
-| 4.0.x | :white_check_mark: |
-| < 4.0 | :x: |
-
-## Reporting a Vulnerability
-
-Use this section to tell people how to report a vulnerability.
-
-Tell them where to go, how often they can expect to get an update on a
-reported vulnerability, what to expect if the vulnerability is accepted or
-declined, etc.
diff --git a/beta.html b/beta.html
deleted file mode 100644
index 8bd5188..0000000
--- a/beta.html
+++ /dev/null
@@ -1,58 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Beta Program
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/contribuidores/contribuidores -en.html b/contribuidores/contribuidores -en.html
deleted file mode 100644
index 237b80f..0000000
--- a/contribuidores/contribuidores -en.html
+++ /dev/null
@@ -1,656 +0,0 @@
-
-
-
-
-
-
- Contributors — OpenTune
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Español
-
-
- English
-
-
- Português (BR)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Arturo Cervantes
-
Lead Developer
-
- engineering
- Lead Dev
-
-
-
-
-
-
-
-
Fabiano Júnior
-
Portuguese (BR) Translator
-
- translate
- Translation BR
-
-
-
-
-
-
-
-
Imgbot
-
Image Optimization
-
- image
- Bot
-
-
-
-
-
-
-
-
-
fork_right
-
Want to contribute?
-
Fork the repository and submit a pull request. All contributions are welcome!
-
- add
- Fork Repository
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/contribuidores/contribuidores-Pr_Br.html b/contribuidores/contribuidores-Pr_Br.html
deleted file mode 100644
index d5726bc..0000000
--- a/contribuidores/contribuidores-Pr_Br.html
+++ /dev/null
@@ -1,676 +0,0 @@
-
-
-
-
-
-
- Colaboradores — OpenTune
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- Español
-
-
- English
-
-
- Português (BR)
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Arturo Cervantes
-
Desenvolvedor Principal
-
- engineering
- Dev Principal
-
-
-
-
-
-
-
-
Fabiano Júnior
-
Tradutor para o Português (BR)
-
- translate
- Tradução BR
-
-
-
-
-
-
-
-
Imgbot
-
Otimização de Imagens
-
- image
- Bot
-
-
-
-
-
-
-
-
รiłѵα'.ぁ™
-
Tradução e correções para o Português (BR)
-
- edit
- Correções BR
-
-
-
-
-
-
-
-
-
fork_right
-
Quer contribuir?
-
Faça um fork do repositório e envie seu pull request. Toda ajuda é bem-vinda!
-
- add
- Fazer Fork
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/contribuidores/contribuidores.html b/contribuidores/contribuidores.html
deleted file mode 100644
index ed59ba5..0000000
--- a/contribuidores/contribuidores.html
+++ /dev/null
@@ -1,365 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- OpenTune
-
- code
- settings
-
-
-
-
-
-
-
- volunteer_activism
- Excelencia de Código Abierto
-
- Comunidad y Colaboradores
- OpenTune es construido por la
- comunidad,
- para la comunidad. Cada commit, reporte de error y traducción nos acerca a la mejor experiencia de música de
- código
- abierto.
-
-
-
-
-
- star
- ---
- Estrellas
-
-
- fork_right
- ---
- Bifurcaciones
-
-
- error_outline
- ---
- Incidencias Abiertas
-
-
- history
- ---
- Total de Commits
-
-
-
-
-
-
-
-
-
-
-
-
-
Cómo Ayudar
-
Elige tu camino y ayúdanos a hacer OpenTune aún
- mejor.
-
-
-
-
- terminal
- Programación
-
-
Implementa nuevas funciones o corrige errores
- en nuestra base de código en Kotlin.
-
Explorar
- Incidencias
-
-
-
- palette
- Diseño UI/UX
-
-
Mejora la implementación de Material 3 y el
- acabado visual.
-
Unirse
- a Figma
-
-
-
- translate
- Traducción
-
-
Ayúdanos a localizar OpenTune para los amantes
- de la música en todo el mundo.
-
Traducir
-
-
-
- bug_report
- Reportar Errores
-
-
¿Encontraste un fallo? Los reportes detallados
- nos ayudan a mantener la estabilidad.
-
Abrir
- Incidencia
-
-
-
-
-
-
-
-
-
-
-
-
- home
- Inicio
-
-
- groups
- Comunidad
-
-
-
-
-
-
\ No newline at end of file
diff --git a/css/styles.css b/css/styles.css
deleted file mode 100644
index b6b2826..0000000
--- a/css/styles.css
+++ /dev/null
@@ -1,750 +0,0 @@
-/* ══════════════════════════════════════════════
- OpenTune Web — Custom Styles
- Complementa Tailwind CSS con estilos glassmórficos,
- dialogs, animaciones y componentes personalizados.
- ══════════════════════════════════════════════ */
-
-/* ─── Base ─── */
-html {
- scroll-behavior: smooth;
-}
-
-*, ::before, ::after {
- box-sizing: border-box;
-}
-
-a {
- text-decoration: none;
-}
-
-/* ─── Material Symbols ─── */
-.material-symbols-outlined {
- font-family: 'Material Symbols Outlined';
- font-weight: normal;
- font-style: normal;
- font-size: 24px;
- line-height: 1;
- letter-spacing: normal;
- text-transform: none;
- display: inline-block;
- white-space: nowrap;
- word-wrap: normal;
- direction: ltr;
- -webkit-font-smoothing: antialiased;
- font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
- vertical-align: middle;
-}
-
-/* ─── Glass Card ─── */
-.glass-card {
- background: rgba(20, 19, 23, 0.45);
- backdrop-filter: blur(24px);
- -webkit-backdrop-filter: blur(24px);
- border: 1px solid rgba(255, 255, 255, 0.06);
-}
-
-/* ─── Ambient Glow ─── */
-.ambient-glow {
- box-shadow: 0 0 40px 0 rgba(208, 188, 255, 0.15);
-}
-
-/* ─── Feature Icon Wrapper ─── */
-.feature-icon-wrap {
- width: 48px;
- height: 48px;
- border-radius: 9999px;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 16px;
- flex-shrink: 0;
-}
-
-/* ─── Version Chips ─── */
-.version-chip {
- display: inline-flex;
- align-items: center;
- padding: 4px 12px;
- border-radius: 9999px;
- background-color: rgba(43, 41, 45, 0.8);
- color: #cac4d0;
- font-size: 11px;
- font-weight: 500;
- font-family: 'Be Vietnam Pro', sans-serif;
- line-height: 16px;
-}
-
-.version-chip--stable {
- background-color: rgba(239, 184, 200, 0.18);
- color: #ffd9e3;
-}
-
-.version-chip--beta {
- background-color: rgba(74, 67, 89, 0.6);
- color: #ccc2dc;
-}
-
-.version-chip--alpha {
- background-color: rgba(208, 188, 255, 0.15);
- color: #e9ddff;
-}
-
-.version-chip--latest {
- background-color: rgba(208, 188, 255, 0.18);
- color: #d0bcff;
-}
-
-/* ─── Footer Chips ─── */
-.footer-chip {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- padding: 8px 16px;
- border-radius: 9999px;
- border: 1px solid rgba(73, 69, 79, 0.5);
- color: #948f9a;
- font-size: 13px;
- font-family: 'Be Vietnam Pro', sans-serif;
- font-weight: 500;
- transition: all 0.2s cubic-bezier(0.2, 0, 0, 1);
-}
-
-.footer-chip:hover {
- border-color: rgba(208, 188, 255, 0.4);
- color: #d0bcff;
- background: rgba(208, 188, 255, 0.07);
- transform: translateY(-1px);
-}
-
-/* ─── Developer Chip Animation ─── */
-.developer-chip {
- animation: shine 3.5s ease-in-out infinite;
-}
-
-@keyframes shine {
- 0%, 100% { opacity: 1; transform: scale(1); }
- 50% { opacity: 0.8; transform: scale(1.04); }
-}
-
-/* ══════════════════════════════════════════════
- GLASS DIALOGS
- ══════════════════════════════════════════════ */
-
-.glass-dialog {
- position: fixed;
- inset: 0;
- margin: auto;
- width: min(90vw, 480px);
- max-height: 85vh;
- border: none;
- border-radius: 2rem;
- background: rgba(18, 17, 22, 0.88);
- backdrop-filter: blur(32px);
- -webkit-backdrop-filter: blur(32px);
- border: 1px solid rgba(255, 255, 255, 0.08);
- color: #e5e1e7;
- overflow: hidden;
- padding: 0;
- box-shadow:
- 0 30px 70px rgba(0, 0, 0, 0.55),
- 0 0 0 1px rgba(208, 188, 255, 0.06),
- inset 0 1px 0 rgba(255, 255, 255, 0.05);
-}
-
-.glass-dialog--large {
- width: min(90vw, 600px);
-}
-
-.glass-dialog::backdrop {
- background: rgba(0, 0, 0, 0.72);
- backdrop-filter: blur(10px);
- -webkit-backdrop-filter: blur(10px);
-}
-
-/* Dialog layout */
-.dialog-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24px 24px 0;
- gap: 12px;
-}
-
-.dialog-content {
- padding: 20px 24px;
-}
-
-.dialog-content--scroll {
- overflow-y: auto;
- max-height: 58vh;
-}
-
-.dialog-content--scroll::-webkit-scrollbar {
- width: 4px;
-}
-.dialog-content--scroll::-webkit-scrollbar-track {
- background: transparent;
-}
-.dialog-content--scroll::-webkit-scrollbar-thumb {
- background: rgba(208, 188, 255, 0.25);
- border-radius: 2px;
-}
-
-.dialog-actions {
- display: flex;
- justify-content: flex-end;
- gap: 8px;
- padding: 0 24px 24px;
-}
-
-/* Dialog buttons */
-.dialog-icon-btn {
- width: 36px;
- height: 36px;
- border: none;
- border-radius: 9999px;
- background: transparent;
- color: #cac4d0;
- cursor: pointer;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: background 0.2s;
- flex-shrink: 0;
-}
-
-.dialog-icon-btn:hover {
- background: rgba(255, 255, 255, 0.08);
-}
-
-.dialog-text-btn {
- background: transparent;
- border: none;
- color: #d0bcff;
- font-family: 'Be Vietnam Pro', sans-serif;
- font-size: 14px;
- font-weight: 500;
- padding: 10px 20px;
- border-radius: 9999px;
- cursor: pointer;
- transition: background 0.2s;
-}
-
-.dialog-text-btn:hover {
- background: rgba(208, 188, 255, 0.1);
-}
-
-.dialog-filled-btn {
- background: #d0bcff;
- border: none;
- color: #37265e;
- font-family: 'Be Vietnam Pro', sans-serif;
- font-size: 14px;
- font-weight: 500;
- padding: 10px 24px;
- border-radius: 9999px;
- cursor: pointer;
- transition: all 0.2s;
-}
-
-.dialog-filled-btn:hover {
- filter: brightness(1.1);
-}
-
-.dialog-filled-btn:active {
- transform: scale(0.96);
-}
-
-/* Dialog list items (language) */
-.dialog-list-item {
- display: flex;
- align-items: center;
- gap: 12px;
- padding: 12px 16px;
- border-radius: 1rem;
- color: #e5e1e7;
- font-family: 'Be Vietnam Pro', sans-serif;
- transition: background 0.2s;
- cursor: pointer;
-}
-
-.dialog-list-item:hover {
- background: rgba(255, 255, 255, 0.06);
-}
-
-.flag-icon {
- width: 24px;
- height: 18px;
- border-radius: 3px;
- object-fit: cover;
- flex-shrink: 0;
-}
-
-/* ══════════════════════════════════════════════
- LOADING INDICATOR
- ══════════════════════════════════════════════ */
-
-.loading-indicator {
- display: flex;
- justify-content: center;
- align-items: center;
- padding: 56px 40px;
-}
-
-.circular-progress {
- width: 36px;
- height: 36px;
- border: 3px solid rgba(208, 188, 255, 0.18);
- border-top: 3px solid #d0bcff;
- border-radius: 50%;
- animation: spin 0.85s linear infinite;
-}
-
-@keyframes spin {
- to { transform: rotate(360deg); }
-}
-
-/* ══════════════════════════════════════════════
- MARKDOWN BODY (changelog)
- ══════════════════════════════════════════════ */
-
-.markdown-body h1,
-.markdown-body h2,
-.markdown-body h3 {
- color: #e5e1e7;
- font-family: 'Epilogue', sans-serif;
- margin: 20px 0 10px;
-}
-
-.markdown-body h1 { font-size: 1.4rem; font-weight: 700; }
-.markdown-body h2 { font-size: 1.15rem; font-weight: 600; }
-.markdown-body h3 { font-size: 1rem; font-weight: 600; }
-
-.markdown-body p {
- color: #cac4d0;
- line-height: 1.75;
- margin: 8px 0;
-}
-
-.markdown-body ul,
-.markdown-body ol {
- color: #cac4d0;
- padding-left: 22px;
- margin: 8px 0;
- line-height: 1.75;
-}
-
-.markdown-body li {
- margin: 4px 0;
-}
-
-.markdown-body code {
- background: rgba(208, 188, 255, 0.12);
- color: #d0bcff;
- padding: 2px 8px;
- border-radius: 6px;
- font-size: 0.82em;
- font-family: ui-monospace, 'Courier New', monospace;
-}
-
-.markdown-body pre {
- background: rgba(14, 14, 17, 0.7);
- border: 1px solid rgba(73, 69, 79, 0.4);
- border-radius: 12px;
- padding: 16px;
- overflow-x: auto;
- margin: 12px 0;
-}
-
-.markdown-body pre code {
- background: transparent;
- padding: 0;
- color: #e5e1e7;
-}
-
-.markdown-body a {
- color: #d0bcff;
- text-decoration: underline;
- text-underline-offset: 3px;
-}
-
-.markdown-body a:hover {
- color: #e9ddff;
-}
-
-.markdown-body hr {
- border: none;
- border-top: 1px solid rgba(73, 69, 79, 0.5);
- margin: 20px 0;
-}
-
-/* Changelog meta header */
-.changelog-meta {
- display: flex;
- flex-direction: column;
- gap: 8px;
- margin-bottom: 20px;
- padding-bottom: 16px;
- border-bottom: 1px solid rgba(73, 69, 79, 0.4);
-}
-
-.changelog-date {
- font-size: 13px;
- color: #948f9a;
- font-family: 'Be Vietnam Pro', sans-serif;
-}
-
-/* ══════════════════════════════════════════════
- VERSION LIST (versions dialog)
- ══════════════════════════════════════════════ */
-
-.version-list-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 14px 0;
- gap: 12px;
- border-bottom: 1px solid rgba(73, 69, 79, 0.3);
-}
-
-.version-list-item:last-child {
- border-bottom: none;
-}
-
-.version-list-info {
- display: flex;
- flex-direction: column;
- gap: 6px;
- flex: 1;
- min-width: 0;
-}
-
-.version-meta-row {
- display: flex;
- align-items: center;
- gap: 6px;
- flex-wrap: wrap;
-}
-
-.version-tag {
- font-size: 15px;
- font-weight: 500;
- color: #e5e1e7;
- font-family: 'Be Vietnam Pro', sans-serif;
-}
-
-.version-date {
- font-size: 12px;
- color: #948f9a;
- font-family: 'Be Vietnam Pro', sans-serif;
-}
-
-.version-dl-btn {
- display: inline-flex;
- align-items: center;
- gap: 6px;
- background: rgba(43, 41, 45, 0.8);
- color: #e5e1e7;
- border: 1px solid rgba(73, 69, 79, 0.4);
- padding: 8px 16px;
- border-radius: 9999px;
- font-size: 13px;
- font-weight: 500;
- font-family: 'Be Vietnam Pro', sans-serif;
- text-decoration: none;
- transition: all 0.2s;
- flex-shrink: 0;
- white-space: nowrap;
-}
-
-.version-dl-btn:hover {
- background: rgba(53, 52, 56, 0.9);
- border-color: rgba(208, 188, 255, 0.3);
- color: #d0bcff;
-}
-
-/* ══════════════════════════════════════════════
- DISABLED CARD (Windows)
- ══════════════════════════════════════════════ */
-
-.disabled-card {
- position: relative;
- overflow: hidden;
- cursor: not-allowed;
- user-select: none;
- -webkit-user-select: none;
-}
-
-/* Diagonal stripe overlay */
-.disabled-card::before {
- content: '';
- position: absolute;
- inset: 0;
- background: repeating-linear-gradient(
- -45deg,
- transparent,
- transparent 14px,
- rgba(255, 255, 255, 0.018) 14px,
- rgba(255, 255, 255, 0.018) 28px
- );
- pointer-events: none;
- z-index: 0;
-}
-
-.disabled-card__inner {
- position: relative;
- z-index: 1;
- opacity: 0.5;
- pointer-events: none;
-}
-
-/* ══════════════════════════════════════════════
- SCROLLBAR (global)
- ══════════════════════════════════════════════ */
-
-::-webkit-scrollbar {
- width: 6px;
-}
-
-::-webkit-scrollbar-track {
- background: transparent;
-}
-
-::-webkit-scrollbar-thumb {
- background: rgba(208, 188, 255, 0.2);
- border-radius: 3px;
-}
-
-::-webkit-scrollbar-thumb:hover {
- background: rgba(208, 188, 255, 0.38);
-}
-
-/* ══════════════════════════════════════════════
- FOCUS VISIBLE
- ══════════════════════════════════════════════ */
-
-:focus-visible {
- outline: 2px solid #d0bcff;
- outline-offset: 2px;
- border-radius: 4px;
-}
-
-/* ══════════════════════════════════════════════
- LIGHT MODE OVERRIDES
- (activa con root.classList.add('light-mode'))
- ══════════════════════════════════════════════ */
-
-:root.light-mode {
- --lm-bg: #FEF7FF;
- --lm-on-bg: #1D1B20;
- --lm-surface: #FEF7FF;
- --lm-on-surface: #1D1B20;
- --lm-sc-low: #F7F2FA;
- --lm-sc: #F3EDF7;
- --lm-sc-high: #ECE6F0;
- --lm-sc-highest: #E6E0E9;
- --lm-primary: #6750A4;
- --lm-on-primary: #FFFFFF;
- --lm-pc: #EADDFF;
- --lm-on-pc: #21005D;
- --lm-sec-c: #E8DEF8;
- --lm-on-sec-c: #1D192B;
- --lm-on-sv: #49454F;
- --lm-ov: #CAC4D0;
-}
-
-:root.light-mode body {
- background-color: var(--lm-bg);
- color: var(--lm-on-bg);
-}
-
-:root.light-mode .bg-background { background-color: var(--lm-bg) !important; }
-:root.light-mode .bg-surface { background-color: var(--lm-surface) !important; }
-:root.light-mode .bg-surface-container-lowest { background-color: var(--lm-sc-low) !important; }
-:root.light-mode .bg-surface-container-high { background-color: var(--lm-sc-high) !important; }
-:root.light-mode .bg-surface-container-highest{ background-color: var(--lm-sc-highest) !important; }
-:root.light-mode .bg-primary-container { background-color: var(--lm-pc) !important; }
-:root.light-mode .bg-secondary-container { background-color: var(--lm-sec-c) !important; }
-:root.light-mode .bg-primary { background-color: var(--lm-primary) !important; }
-
-:root.light-mode .text-on-surface { color: var(--lm-on-surface) !important; }
-:root.light-mode .text-on-surface-variant { color: var(--lm-on-sv) !important; }
-:root.light-mode .text-on-background { color: var(--lm-on-bg) !important; }
-:root.light-mode .text-on-primary { color: var(--lm-on-primary) !important; }
-:root.light-mode .text-on-primary-container { color: var(--lm-on-pc) !important; }
-:root.light-mode .text-on-secondary-container { color: var(--lm-on-sec-c) !important; }
-:root.light-mode .text-primary { color: var(--lm-primary) !important; }
-:root.light-mode .text-violet-300 { color: var(--lm-primary) !important; }
-:root.light-mode .text-slate-400 { color: var(--lm-on-sv) !important; }
-:root.light-mode .text-slate-500 { color: #79747E !important; }
-:root.light-mode .text-slate-100 { color: var(--lm-on-surface) !important; }
-:root.light-mode .text-surface { color: var(--lm-surface) !important; }
-
-:root.light-mode .border-outline-variant { border-color: var(--lm-ov) !important; }
-:root.light-mode .border-white\/10 { border-color: rgba(0, 0, 0, 0.08) !important; }
-:root.light-mode .border-white\/5 { border-color: rgba(0, 0, 0, 0.05) !important; }
-
-:root.light-mode .bg-slate-950\/65 { background-color: rgba(254, 247, 255, 0.82) !important; }
-:root.light-mode .bg-slate-950 { background-color: #F7F2FA !important; }
-
-:root.light-mode .glass-card {
- background: rgba(255, 255, 255, 0.65);
- border-color: rgba(0, 0, 0, 0.06);
-}
-
-:root.light-mode .glass-dialog {
- background: rgba(240, 236, 248, 0.92);
- border-color: rgba(0, 0, 0, 0.08);
- color: #1D1B20;
-}
-
-:root.light-mode .glass-dialog::backdrop {
- background: rgba(0, 0, 0, 0.42);
-}
-
-:root.light-mode .dialog-text-btn { color: var(--lm-primary); }
-:root.light-mode .dialog-text-btn:hover { background: rgba(103, 80, 164, 0.1); }
-:root.light-mode .dialog-icon-btn { color: var(--lm-on-sv); }
-:root.light-mode .dialog-icon-btn:hover { background: rgba(0,0,0,0.06); }
-:root.light-mode .dialog-list-item { color: var(--lm-on-surface); }
-:root.light-mode .dialog-list-item:hover { background: rgba(0,0,0,0.05); }
-:root.light-mode .dialog-filled-btn { background: var(--lm-primary); color: var(--lm-on-primary); }
-
-:root.light-mode .footer-chip {
- border-color: rgba(0, 0, 0, 0.12);
- color: #49454F;
-}
-:root.light-mode .footer-chip:hover {
- border-color: var(--lm-primary);
- color: var(--lm-primary);
- background: rgba(103, 80, 164, 0.06);
-}
-
-:root.light-mode .circular-progress {
- border-color: rgba(103, 80, 164, 0.18);
- border-top-color: var(--lm-primary);
-}
-
-:root.light-mode .version-chip {
- background-color: rgba(231, 224, 236, 0.9);
- color: #49454F;
-}
-
-:root.light-mode .version-dl-btn {
- background: rgba(231, 224, 236, 0.8);
- border-color: rgba(0, 0, 0, 0.1);
- color: #1D1B20;
-}
-:root.light-mode .version-dl-btn:hover {
- border-color: var(--lm-primary);
- color: var(--lm-primary);
-}
-
-:root.light-mode .markdown-body h1,
-:root.light-mode .markdown-body h2,
-:root.light-mode .markdown-body h3 { color: #1D1B20; }
-:root.light-mode .markdown-body p,
-:root.light-mode .markdown-body li { color: #49454F; }
-:root.light-mode .markdown-body code {
- background: rgba(103, 80, 164, 0.1);
- color: var(--lm-primary);
-}
-:root.light-mode .markdown-body a { color: var(--lm-primary); }
-
-:root.light-mode .ambient-glow {
- box-shadow: 0 0 40px 0 rgba(103, 80, 164, 0.18);
-}
-
-:root.light-mode .from-surface {
- --tw-gradient-from: #FEF7FF var(--tw-gradient-from-position);
-}
-
-/* ══════════════════════════════════════════════
- ACCESSIBILITY & PERFORMANCE
- ══════════════════════════════════════════════ */
-
-@media (prefers-reduced-motion: reduce) {
- *, ::before, ::after {
- animation-duration: 0.01ms !important;
- transition-duration: 0.01ms !important;
- }
-}
-
-@media (prefers-contrast: high) {
- .glass-card {
- border-width: 2px;
- border-color: rgba(255, 255, 255, 0.25);
- }
- .glass-dialog {
- border-width: 2px;
- }
-}
-
-/* ══════════════════════════════════════════════
- RESPONSIVE FIXES
- ══════════════════════════════════════════════ */
-
-@media (max-width: 640px) {
- .glass-dialog {
- width: 92vw;
- border-radius: 1.5rem;
- max-height: 88vh;
- }
-
- .dialog-header {
- padding: 20px 18px 0;
- }
-
- .dialog-content {
- padding: 16px 18px;
- }
-
- .dialog-actions {
- padding: 0 18px 20px;
- }
-}
-
-@media (max-width: 480px) {
- .version-list-item {
- flex-direction: column;
- align-items: flex-start;
- }
-
- .version-dl-btn {
- width: 100%;
- justify-content: center;
- }
-}
-
-/* ─── Screenshots Accordion ─── */
-#screenshots-content {
- max-height: 2000px;
- opacity: 1;
- overflow: hidden;
- transition: max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1),
- opacity 0.3s ease,
- margin 0.3s ease;
-}
-
-#screenshots-content.collapsed {
- max-height: 0 !important;
- opacity: 0;
- margin: 0;
- padding: 0;
-}
-
-#screenshots-icon {
- transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
-}
-
-#screenshots-icon.rotated {
- transform: rotate(180deg);
-}
-
-#screenshots-header:hover #screenshots-toggle {
- background: rgba(255, 255, 255, 0.08);
-}
-
- @keyframes spin {
- to { transform: rotate(360deg); }
- }
\ No newline at end of file
diff --git a/en.html b/en.html
deleted file mode 100644
index 42305fa..0000000
--- a/en.html
+++ /dev/null
@@ -1,741 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- OpenTune
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
You will be redirected outside of OpenTune Web to an
- external demo provided by Appetize.io.
-
-
- Cancel
- Continue
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- code
- Dev By Arthur Dev Studio
-
-
-
OpenTune
-
- A YouTube Music client with Material Design 3, for Android.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Features
-
- Discover all the features that make OpenTune the best client for your music.
-
-
-
-
-
-
-
- music_note
-
-
YouTube Music Client with Material Design 3
-
-
Provides a modern and elegant
- YouTube Music experience on Android, with an intuitive and optimized interface.
-
-
-
-
- palette
-
-
Modern and Attractive Design
-
Based on Material Design 3, with
- vivid colors, modern typography, and smooth transitions.
-
-
-
-
- settings
-
-
Intuitive and Customizable Interface
-
Simplified interface for quick
- access to all features, with customization options.
-
-
-
-
- search
-
-
Explore and Discover Music
-
Access to a vast music catalog
- with advanced search and smart recommendations.
-
-
-
-
- volume_up
-
-
High-Quality Playback
-
High-quality music, optimized
- for different devices and network conditions.
-
-
-
-
- playlist_add
-
-
Playlists and Downloads
-
Create and organize playlists,
- and download songs to listen offline.
-
-
-
-
- sync
-
-
YouTube Music Integration
-
Sync your YouTube Music account
- to access your libraries and receive notifications.
-
-
-
-
- build
-
-
Advanced Features
-
Voice and gesture playback
- control, with Chromecast and Bluetooth support.
-
-
-
-
- folder
-
-
Efficient Library Management
-
Organize your music with
- advanced filtering and categorization tools.
-
-
-
-
- translate
-
-
Multilingual Support
-
Available in multiple languages
- with accurate translations and culturally relevant localization.
-
-
-
-
- update
-
-
Continuous Updates
-
Regular updates with new
- features, security improvements, and optimization.
-
-
-
-
- headphones
-
-
Optimized User Experience
-
Intuitive navigation and agile
- performance with a user-friendly interface that enhances the experience.
-
-
-
-
-
-
-
-
-
-
COMMUNITY
- DRIVEN
-
Open Source at its Heart
-
OpenTune is built by music lovers for music
- lovers. Contribute, customize, and help us build the best open-source player for Android.
-
-
-
-
-
-
-
-
-
-
Arturo254
-
Lead Developer
-
-
-
-
- verified
-
-
-
Latest stable version
-
Loading...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- support_agent
-
-
Need Help?
-
- Submit a request or report an issue directly to the development team.
-
-
- send
- Request Help
-
-
-
-
-
-
-
-
-
-
Downloads
-
- Download the latest version of OpenTune for your platform.
-
-
-
-
-
-
-
-
-
- android
-
-
-
Android
-
Download the latest stable version.
-
-
-
-
- Stable Version
- Loading...
-
-
-
-
- list_alt
- View Changes
-
-
- history
- Previous Versions
-
-
-
-
-
-
-
-
-
-
-
-
-
- laptop_windows
-
-
-
Windows
-
Development is currently closed.
-
-
-
-
-
- Closed
- Not Available
-
-
-
-
- list_alt
- View Changes
-
-
- history
- Previous Versions
-
-
-
-
-
- block
- Not Available
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
OpenTune
-
© 2024 Arturo.inc™ .
- All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Licensed under GPL-3.0 Open Source License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/eslint.config.mjs b/eslint.config.mjs
new file mode 100644
index 0000000..2b23ca9
--- /dev/null
+++ b/eslint.config.mjs
@@ -0,0 +1,44 @@
+import { defineConfig, globalIgnores } from 'eslint/config';
+import nextVitals from 'eslint-config-next/core-web-vitals';
+import nextTs from 'eslint-config-next/typescript';
+
+export default defineConfig([
+ ...nextVitals,
+ ...nextTs,
+
+ {
+ rules: {
+ // Allow console.warn() and console.error()
+ 'no-console': [
+ 'warn',
+ {
+ allow: ['warn', 'error'],
+ },
+ ],
+
+ // Prevent == and !=
+ eqeqeq: ['error', 'always'],
+
+ // Always require braces
+ curly: ['error', 'all'],
+
+ // Catch unused variables
+ '@typescript-eslint/no-unused-vars': [
+ 'error',
+ {
+ argsIgnorePattern: '^\\$',
+ varsIgnorePattern: '^\\$',
+ caughtErrorsIgnorePattern: '^\\$',
+ },
+ ],
+
+ // Discourage unnecessary state updates inside useEffect
+ 'react-hooks/set-state-in-effect': 'warn',
+
+ // Prefer const when variable isn't reassigned
+ 'prefer-const': 'error',
+ },
+ },
+
+ globalIgnores(['.next/**', 'out/**', 'build/**', 'next-env.d.ts', '.local/**']),
+]);
diff --git a/from.html b/from.html
deleted file mode 100644
index bf9ada2..0000000
--- a/from.html
+++ /dev/null
@@ -1,308 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Enviar Comentario
-
Envía tu problema o describe tu solicitud
-
-
-
-
-
-
-
verified_user
-
-
Privacidad Asegurada
-
Tus datos están protegidos y solo se usarán para
- contactarte sobre tu reporte.
-
-
-
-
schedule
-
-
Tiempo de Respuesta
-
Normalmente respondemos en mas de 24 horas
- debido a la alta demanda de solicitudes.
-
-
-
-
-
-
-
- home
- Home
-
-
- feedback
- Support
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/index.html b/index.html
deleted file mode 100644
index a1571dc..0000000
--- a/index.html
+++ /dev/null
@@ -1,744 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- OpenTune
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Serás redirigido fuera de OpenTune Web a una demostración
- externa proporcionada por Appetize.io.
-
-
- Cancelar
- Continuar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- code
- Dev By Arthur Dev Studio
-
-
-
OpenTune
-
- Un cliente de YouTube Music con Material Design 3, para Android.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Características
-
- Descubre todas las funciones que hacen de OpenTune el mejor cliente para tu música.
-
-
-
-
-
-
-
- music_note
-
-
Cliente de YouTube Music con Material Design 3
-
-
Ofrece una experiencia moderna y
- elegante de YouTube Music en Android, con una interfaz intuitiva y optimizada.
-
-
-
-
- palette
-
-
Diseño Moderno y Atractivo
-
Basado en Material Design 3, con
- colores vivos, tipografía moderna y transiciones suaves.
-
-
-
-
- settings
-
-
Interfaz Intuitiva y Personalizable
-
Interfaz simplificada para un acceso
- rápido a todas las funciones, con opciones de personalización.
-
-
-
-
- search
-
-
Explora y Descubre Música
-
Acceso a un amplio catálogo musical
- con búsqueda avanzada y recomendaciones inteligentes.
-
-
-
-
- volume_up
-
-
Reproducción de Alta Calidad
-
Música de alta calidad, optimizada
- para diferentes dispositivos y condiciones de red.
-
-
-
-
- playlist_add
-
-
Listas de Reproducción y Descargas
-
Crea y organiza listas de
- reproducción, y descarga canciones para escuchar sin conexión.
-
-
-
-
- sync
-
-
Integración con YouTube Music
-
Sincroniza tu cuenta de YouTube Music
- para acceder a tus bibliotecas y recibir notificaciones.
-
-
-
-
- build
-
-
Funcionalidades Avanzadas
-
Control de reproducción con voz,
- gestos y soporte para Chromecast y Bluetooth.
-
-
-
-
- folder
-
-
Gestión Eficiente de la Biblioteca
-
Organiza tu música con herramientas
- avanzadas de filtrado y categorización.
-
-
-
-
- translate
-
-
Soporte Multilingüe
-
Disponible en varios idiomas con
- traducciones precisas y localización culturalmente relevante.
-
-
-
-
- update
-
-
Actualizaciones Continuas
-
Actualizaciones regulares con nuevas
- funciones, mejoras de seguridad y optimización.
-
-
-
-
- headphones
-
-
Experiencia de Usuario Optimizada
-
Navegación intuitiva y rendimiento
- ágil con una interfaz amigable que mejora la experiencia.
-
-
-
-
-
-
-
-
-
-
COMMUNITY
- DRIVEN
-
Open Source at its Heart
-
OpenTune es construido por amantes de la música
- para amantes de la música. Contribuye, personaliza y ayúdanos a construir el mejor reproductor
- de código abierto para Android.
-
-
-
-
-
-
-
-
-
-
Arturo254
-
Lead Developer
-
-
-
-
- verified
-
-
-
Última versión estable
-
Cargando...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Reproductor Inmersivo
-
-
-
-
Biblioteca Inteligente
-
-
-
-
Configuración Avanzada
-
-
-
-
-
-
-
-
-
-
-
- support_agent
-
-
¿Necesitas ayuda?
-
- Envía una solicitud o reporta un problema directamente al equipo de desarrollo.
-
-
- send
- Solicitar ayuda
-
-
-
-
-
-
-
-
-
-
Descargas
-
- Descarga la versión más reciente de OpenTune para tu plataforma.
-
-
-
-
-
-
-
-
-
- android
-
-
-
Android
-
Descarga la última versión estable.
-
-
-
-
- Versión Estable
- Cargando...
-
-
-
-
- list_alt
- Ver cambios
-
-
- history
- Versiones anteriores
-
-
-
-
-
-
-
-
-
-
-
-
-
- laptop_windows
-
-
-
Windows
-
El desarrollo está cerrado actualmente.
-
-
-
-
-
- Cerrado
- No disponible
-
-
-
-
- list_alt
- Ver cambios
-
-
- history
- Versiones anteriores
-
-
-
-
-
- block
- No disponible
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
OpenTune
-
© 2024 Arturo.inc™ .
- All Rights Reserved.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Licensed under GPL-3.0 Open Source License.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/knip.config.ts b/knip.config.ts
new file mode 100644
index 0000000..024f9b3
--- /dev/null
+++ b/knip.config.ts
@@ -0,0 +1,17 @@
+import type { KnipConfig } from 'knip';
+
+const config: KnipConfig = {
+ project: ['src/**/*.{ts,tsx}', 'src/**/*.{test,spec}.{ts,tsx}'],
+
+ entry: ['src/app/**/*.{ts,tsx}', 'src/*.{ts,tsx}'],
+
+ ignore: ['src/app/~test/locale/**/*.{ts,tsx}', 'src/app/~test/locale/*.{ts,tsx}'],
+
+ ignoreDependencies: ['tailwindcss'],
+
+ // Uncomment for production mode analysis (excludes tests/dev)
+ // productionEntry: ['src/app/**/*.{ts,tsx}!'],
+ // productionProject: ['src/**/*.{ts,tsx}!', '!src/**/*.{test,spec,stories}.{ts,tsx}'],
+};
+
+export default config;
diff --git a/next.config.ts b/next.config.ts
new file mode 100644
index 0000000..46be800
--- /dev/null
+++ b/next.config.ts
@@ -0,0 +1,16 @@
+import type { NextConfig } from 'next';
+import createNextIntlPlugin from 'next-intl/plugin';
+
+const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts');
+
+const nextConfig: NextConfig = {
+ images: {
+ remotePatterns: [
+ { protocol: 'https', hostname: 'avatars.githubusercontent.com' },
+ { protocol: 'https', hostname: 'flagicons.lipis.dev' },
+ { protocol: 'https', hostname: 'flowbite.s3.amazonaws.com' },
+ ],
+ },
+};
+
+export default withNextIntl(nextConfig);
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..4ba2d5e
--- /dev/null
+++ b/package.json
@@ -0,0 +1,53 @@
+{
+ "name": "opentune-web",
+ "version": "0.0.0",
+ "private": true,
+ "author": {
+ "name": "Rajnish Kumar",
+ "email": "RajnishKMehta@proton.me",
+ "url": "https://github.com/RajnishKMehta"
+ },
+ "packageManager": "pnpm@11.6.0",
+ "scripts": {
+ "prepare": "husky",
+ "dev": "next dev",
+ "build": "next build",
+ "start": "next start",
+ "lint": "eslint",
+ "lint:fix": "eslint . --fix",
+ "format": "prettier . --write",
+ "format:check": "prettier . --check",
+ "format:list-diff": "prettier . --list-different"
+ },
+ "lint-staged": {
+ "*.{js,jsx,ts,tsx,mjs,cjs}": [
+ "eslint --fix --max-warnings=0",
+ "prettier --write"
+ ],
+ "*.{json,jsonc,md,mdx,css,scss,yml,yaml,html,toml,svg}": [
+ "prettier --write"
+ ]
+ },
+ "dependencies": {
+ "lucide-react": "^1.18.0",
+ "marked": "^18.0.5",
+ "next": "16.2.9",
+ "next-intl": "^4.13.0",
+ "react": "19.2.7",
+ "react-dom": "19.2.7"
+ },
+ "devDependencies": {
+ "@tailwindcss/postcss": "^4.3",
+ "@types/node": "^22",
+ "@types/react": "^19",
+ "@types/react-dom": "^19",
+ "eslint": "^9.39.4",
+ "eslint-config-next": "^16.2.9",
+ "husky": "^9.1.7",
+ "knip": "^6.16.1",
+ "lint-staged": "^17.0.7",
+ "prettier": "^3.8.4",
+ "tailwindcss": "^4.3",
+ "typescript": "^6"
+ }
+}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
new file mode 100644
index 0000000..72bdfbf
--- /dev/null
+++ b/pnpm-lock.yaml
@@ -0,0 +1,5281 @@
+lockfileVersion: '9.0'
+
+settings:
+ autoInstallPeers: true
+ excludeLinksFromLockfile: false
+
+importers:
+
+ .:
+ dependencies:
+ lucide-react:
+ specifier: ^1.18.0
+ version: 1.18.0(react@19.2.7)
+ marked:
+ specifier: ^18.0.5
+ version: 18.0.5
+ next:
+ specifier: 16.2.9
+ version: 16.2.9(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ next-intl:
+ specifier: ^4.13.0
+ version: 4.13.0(next@16.2.9(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@6.0.3)
+ react:
+ specifier: 19.2.7
+ version: 19.2.7
+ react-dom:
+ specifier: 19.2.7
+ version: 19.2.7(react@19.2.7)
+ devDependencies:
+ '@tailwindcss/postcss':
+ specifier: ^4.3
+ version: 4.3.0
+ '@types/node':
+ specifier: ^22
+ version: 22.19.21
+ '@types/react':
+ specifier: ^19
+ version: 19.2.17
+ '@types/react-dom':
+ specifier: ^19
+ version: 19.2.3(@types/react@19.2.17)
+ eslint:
+ specifier: ^9.39.4
+ version: 9.39.4(jiti@2.7.0)
+ eslint-config-next:
+ specifier: ^16.2.9
+ version: 16.2.9(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ husky:
+ specifier: ^9.1.7
+ version: 9.1.7
+ knip:
+ specifier: ^6.16.1
+ version: 6.16.1
+ lint-staged:
+ specifier: ^17.0.7
+ version: 17.0.7
+ prettier:
+ specifier: ^3.8.4
+ version: 3.8.4
+ tailwindcss:
+ specifier: ^4.3
+ version: 4.3.0
+ typescript:
+ specifier: ^6
+ version: 6.0.3
+
+packages:
+
+ '@alloc/quick-lru@5.2.0':
+ resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==}
+ engines: {node: '>=10'}
+
+ '@babel/code-frame@7.29.7':
+ resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/compat-data@7.29.7':
+ resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/core@7.29.7':
+ resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/generator@7.29.7':
+ resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-compilation-targets@7.29.7':
+ resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-globals@7.29.7':
+ resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-imports@7.29.7':
+ resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-module-transforms@7.29.7':
+ resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==}
+ engines: {node: '>=6.9.0'}
+ peerDependencies:
+ '@babel/core': ^7.0.0
+
+ '@babel/helper-string-parser@7.29.7':
+ resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-identifier@7.29.7':
+ resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helper-validator-option@7.29.7':
+ resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/helpers@7.29.7':
+ resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/parser@7.29.7':
+ resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ '@babel/template@7.29.7':
+ resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/traverse@7.29.7':
+ resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==}
+ engines: {node: '>=6.9.0'}
+
+ '@babel/types@7.29.7':
+ resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==}
+ engines: {node: '>=6.9.0'}
+
+ '@emnapi/core@1.10.0':
+ resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==}
+
+ '@emnapi/runtime@1.10.0':
+ resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==}
+
+ '@emnapi/runtime@1.11.0':
+ resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==}
+
+ '@emnapi/wasi-threads@1.2.1':
+ resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
+
+ '@eslint-community/eslint-utils@4.9.1':
+ resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+ peerDependencies:
+ eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
+
+ '@eslint-community/regexpp@4.12.2':
+ resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==}
+ engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0}
+
+ '@eslint/config-array@0.21.2':
+ resolution: {integrity: sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/config-helpers@0.4.2':
+ resolution: {integrity: sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/core@0.17.0':
+ resolution: {integrity: sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/eslintrc@3.3.5':
+ resolution: {integrity: sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/js@9.39.4':
+ resolution: {integrity: sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/object-schema@2.1.7':
+ resolution: {integrity: sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@eslint/plugin-kit@0.4.1':
+ resolution: {integrity: sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@formatjs/fast-memoize@3.1.6':
+ resolution: {integrity: sha512-H5aexk1Le7T9TPmscacZ+1pR6CTa2n1wq+HDVGXhH8TzUlQQpeXzZs91dRtmFHrbeNbjPFPfQujUqm7MHgVoXQ==}
+
+ '@formatjs/icu-messageformat-parser@3.5.11':
+ resolution: {integrity: sha512-NVsuNsc2dUVG9+4HBJ/srScxtA/18LqGgwtop/tuN/OIBjVl6QA+0KhfZQddDD9sEh2LeVjLFPGVU3ixa3blcA==}
+
+ '@formatjs/icu-skeleton-parser@2.1.10':
+ resolution: {integrity: sha512-XuSva+8ZGawk8VnD5VD6UeH8KarQ/Z022zgjHDoHmlNiAewstXuuzXc0Hk5pGFSdG+nNw5bfJKXqj1ZXHn9yUA==}
+
+ '@formatjs/intl-localematcher@0.8.10':
+ resolution: {integrity: sha512-P/IC3qws3jH+1fEs+o0RIFgXKRaQlFehjS5W0FPAqdo6hgzawLl+eD0q0JjheQ3XtoOe5n8WSYfX06KQZI/QJA==}
+
+ '@humanfs/core@0.19.2':
+ resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/node@0.16.8':
+ resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanfs/types@0.15.0':
+ resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==}
+ engines: {node: '>=18.18.0'}
+
+ '@humanwhocodes/module-importer@1.0.1':
+ resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==}
+ engines: {node: '>=12.22'}
+
+ '@humanwhocodes/retry@0.4.3':
+ resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==}
+ engines: {node: '>=18.18'}
+
+ '@img/colour@1.1.0':
+ resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==}
+ engines: {node: '>=18'}
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ resolution: {integrity: sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-darwin-x64@0.34.5':
+ resolution: {integrity: sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ resolution: {integrity: sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ resolution: {integrity: sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ resolution: {integrity: sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ resolution: {integrity: sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ resolution: {integrity: sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ resolution: {integrity: sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ resolution: {integrity: sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ resolution: {integrity: sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ resolution: {integrity: sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ resolution: {integrity: sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linux-arm64@0.34.5':
+ resolution: {integrity: sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-arm@0.34.5':
+ resolution: {integrity: sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ resolution: {integrity: sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ resolution: {integrity: sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-s390x@0.34.5':
+ resolution: {integrity: sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linux-x64@0.34.5':
+ resolution: {integrity: sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ resolution: {integrity: sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ resolution: {integrity: sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@img/sharp-wasm32@0.34.5':
+ resolution: {integrity: sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [wasm32]
+
+ '@img/sharp-win32-arm64@0.34.5':
+ resolution: {integrity: sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@img/sharp-win32-ia32@0.34.5':
+ resolution: {integrity: sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@img/sharp-win32-x64@0.34.5':
+ resolution: {integrity: sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@jridgewell/gen-mapping@0.3.13':
+ resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+ '@jridgewell/remapping@2.3.5':
+ resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+ '@jridgewell/resolve-uri@3.1.2':
+ resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+ engines: {node: '>=6.0.0'}
+
+ '@jridgewell/sourcemap-codec@1.5.5':
+ resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
+ '@napi-rs/wasm-runtime@1.1.5':
+ resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==}
+ peerDependencies:
+ '@emnapi/core': ^1.7.1
+ '@emnapi/runtime': ^1.7.1
+
+ '@next/env@16.2.9':
+ resolution: {integrity: sha512-ki5VxxXfzD/9TDe13wyeTKIjQTAwBVpnr8KhRDUr8ltMUq1/NBpWNT5tiPoxiGl+PHM4X2ahSOiPk6iAimIzPg==}
+
+ '@next/eslint-plugin-next@16.2.9':
+ resolution: {integrity: sha512-UZi8+YT/MLgTC9nrrn2Xd4lBYv1B7lVmtWHfPcthAI5Tt/C1LuDe6DfmtCtJ+WQod3ksY4VrKSvk3oMVAnL7qw==}
+
+ '@next/swc-darwin-arm64@16.2.9':
+ resolution: {integrity: sha512-HkfxNYUCmcct0Xsqib5KxqMSHV4AHJq857BNRchyBDs4YS19aHzVfn1kDuBYKqLLQBjXgnkIsjV2Kd4d2wzYhw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@next/swc-darwin-x64@16.2.9':
+ resolution: {integrity: sha512-7IAtK4MeybpqRV9GRABWEhJ62mOS+rzWOzOTFie4cSEtm12xsoOMJRcECoZx3FHPzFAqN/IJtHqWAFOLfl152w==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@next/swc-linux-arm64-gnu@16.2.9':
+ resolution: {integrity: sha512-hBD75iWpUtkL9SmQmcRhmLomn9jgkPzCEkbOcLgHymPEKzv+6ONy13RRiIEz/iEObjkS2Jlb5gYS2XGoS3X4rw==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-arm64-musl@16.2.9':
+ resolution: {integrity: sha512-qZTI3pf9SGc/obr8NkQAekBxmp1QK+kVm+VAf3BALLfFAj+1kUhkTxmrWpVos9R/UYIA8AWX2p6cGI5WdwzVUA==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-linux-x64-gnu@16.2.9':
+ resolution: {integrity: sha512-xm0HfRNX+UkH4R3c18ynswjj5o5uEj/7iI9p9omdtTSIsRCzQqkGMA+10nzJ4EHnYC3as65IMhbbl5fWRUWHYg==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@next/swc-linux-x64-musl@16.2.9':
+ resolution: {integrity: sha512-QumimHkGEG6vM3PfEDWKyKen03NcqLOkeKB1EfcPe7VxzmEiCa4jNnMyBn/US5zcd/VE1CI+O8Ovb3lfjVHfGw==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@next/swc-win32-arm64-msvc@16.2.9':
+ resolution: {integrity: sha512-hzQpKZvw8rAwI6A2uQh6SacCSvNAXaIkPNsWwzqqfRiIMiXMfH936skDhz1OO6KpvdKkJrgHHtqQOq5PIXOvdQ==}
+ engines: {node: '>= 10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@next/swc-win32-x64-msvc@16.2.9':
+ resolution: {integrity: sha512-qr2VL3Ce5QrwgO2yh1ujSBawrimjVKX8FGF/cOynmdYKJY0BdHpGVNIRK1tqONB10Vkm25Ub1BD2bkjWs4+96w==}
+ engines: {node: '>= 10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@nodelib/fs.scandir@2.1.5':
+ resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.stat@2.0.5':
+ resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
+ engines: {node: '>= 8'}
+
+ '@nodelib/fs.walk@1.2.8':
+ resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
+ engines: {node: '>= 8'}
+
+ '@nolyfill/is-core-module@1.0.39':
+ resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==}
+ engines: {node: '>=12.4.0'}
+
+ '@oxc-parser/binding-android-arm-eabi@0.133.0':
+ resolution: {integrity: sha512-l/44caGse+VpnY9gx0yvvc5QnnG3yG1FO3KZgYvNL1GZrfK86zIwAOgGEVlxDyRymzrU/KHiblPFpevKOmJmUA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [android]
+
+ '@oxc-parser/binding-android-arm64@0.133.0':
+ resolution: {integrity: sha512-KUHmPMziLBp4u+zbrLdB7iWS7KshuZe+RAp7ELnY9SI9nNXBZ+dp8fiBqWOxhXqn+FQg3a4UcQhwmsJOKV8Jjg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxc-parser/binding-darwin-arm64@0.133.0':
+ resolution: {integrity: sha512-q8dWmnU/8ea2tga9w2f1PinQ5rcMPDUGkF64T189b65YMjUomET4oy5oRldOr4AwOQkneOG/Zttnz1Dvrc62wg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxc-parser/binding-darwin-x64@0.133.0':
+ resolution: {integrity: sha512-cOKeIELIB2bJnCKwqx4Rdj+1Lss/U6uCbLxRySZrhyOOQa1flKhwZFjEHRHxk8fU1NKmhK5OnTdPQ4CpjuFuVw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxc-parser/binding-freebsd-x64@0.133.0':
+ resolution: {integrity: sha512-OpaSv4pW3KgFrMYQxTaS0aOE4T1DQF3qZE/4B6uqqv1KgPWWd4UQhJALi8PJPX1RRV5K7ThKXRfF7qGg2+3l1A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.133.0':
+ resolution: {integrity: sha512-JGK1wlGrGwxBIlVSF7KWTX1/ru6BEtf28fRROztDRkLfiW+Kxa4onnriezMIiogfn9hVw2KzYcKiLjkLR2ns8A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-arm-musleabihf@0.133.0':
+ resolution: {integrity: sha512-yuZO533Ftonxn/iyoqQzURzLQHMspvsIyfiCSNi1t/ER4eIQaR0SsmUOUm5b/lmSig7IWIUa5/BrbEkAPwcilQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-parser/binding-linux-arm64-gnu@0.133.0':
+ resolution: {integrity: sha512-hvpbqT5pN2rR+3+xtWeizwfR/aZ0vGceg6TqYMl+ToxMpk9/tmnX7kSvQnfEUkoua8mhogzvIKsAkn0wxgblBA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-parser/binding-linux-arm64-musl@0.133.0':
+ resolution: {integrity: sha512-wJQGamIosQBoJHW9+S5XxrtKRo3eyJxsnS1XCPrqN0LHi8uw1pTqqTfn3t/NVuvbBg7Pumn4ez9Eidgcn0xbEg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-parser/binding-linux-ppc64-gnu@0.133.0':
+ resolution: {integrity: sha512-Koaz32/O5+abIfrNGdyndgRvdOZ9jEf5/z3Ep9h3h2QWpdDiUQpVwgH0OcMXCs+l9aXxPLtkupqyVig9W6FDKw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-parser/binding-linux-riscv64-gnu@0.133.0':
+ resolution: {integrity: sha512-R4vOjWzxhnNWHnVLeiB6jNuIifdy9vcMXZGPc7StXcxBovI+U2zg1QhZ9o8OjV80oGivs1lX5NfPLzk4IPqlRA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-parser/binding-linux-riscv64-musl@0.133.0':
+ resolution: {integrity: sha512-iwgBNUTHiMdxARLYuM0SBlnYeb19iw1Ea5M+4ERZupCsBMLArti6FyZ6UfFjJxIiTDr2oW2DGQFxlQVQ/dW9rA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-parser/binding-linux-s390x-gnu@0.133.0':
+ resolution: {integrity: sha512-ZwZNo8FZmB/gVfboQl+wXilBigGl+6nQQs+nITOeAP/HcAOjiHl6XZJL9F/KXNEspODQcbjAiyjUbeCJd9a0fA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-parser/binding-linux-x64-gnu@0.133.0':
+ resolution: {integrity: sha512-govCvWx1dBlED3uu4qXctxpRcouu9I8Kn+DBktGCl760JtlGJzc9l/OmPJKlYWSbrRqKkMZehNeZ/4Wfma7uSA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-parser/binding-linux-x64-musl@0.133.0':
+ resolution: {integrity: sha512-ssTlpXD5Mq9uCssDJPzlRWqBt4Y7Zzd9i+XZhWmK/9Y6KUIuAxVYTYiI8lxcGWi0+3/Cz4A8q9UrD4NK9Y2j7g==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-parser/binding-openharmony-arm64@0.133.0':
+ resolution: {integrity: sha512-51aByfXhPtLEdWG4a2Ihdw6cPWV1ei1AarALpFdDP8MLWDLE2NuUMgbo3DERR2Kt8fT/ok1GUvBiLxVGke9uUQ==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxc-parser/binding-wasm32-wasi@0.133.0':
+ resolution: {integrity: sha512-2e16tkKp+wDO2GTAmXfxbBcCmGEaFPIJEIRBBmVKNVXSc8/fJsSIaBGyFTPHM9ST5GNWgJcYIt94rDTks+PLwA==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [wasm32]
+
+ '@oxc-parser/binding-win32-arm64-msvc@0.133.0':
+ resolution: {integrity: sha512-KPTNDKbxH1cglrqTyVeXHb4Pk4oksz8EcE1/v8zqU7N4UXbiHfA/IwtXZ2U77fnRAWBbgVkl/lZbL7o3hRdejg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxc-parser/binding-win32-ia32-msvc@0.133.0':
+ resolution: {integrity: sha512-Una1bNYv9zCavQrfnDR9wuZVB3itLjCEH4Oz7i6CwAJN/Xq9b+zbbcxmvdkKvvJt4Ngc/MBmIYlbLo3zS4TQ0A==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [ia32]
+ os: [win32]
+
+ '@oxc-parser/binding-win32-x64-msvc@0.133.0':
+ resolution: {integrity: sha512-kjBhCiOGSYTwDJQuuZa7a94JbP8htWu7J0X1KwH74kV2K5eYf6eyJRYmkpCDvr0XEL8tMxYI4WU1VekblFCLgg==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ cpu: [x64]
+ os: [win32]
+
+ '@oxc-project/types@0.133.0':
+ resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==}
+
+ '@oxc-resolver/binding-android-arm-eabi@11.20.0':
+ resolution: {integrity: sha512-IjfWOXRgJFNdORDl+Uf1aibNgZY2guOD3zmOhx1BGVb/MIiqlFTdmjpQNplSN58lhWehnX4UNqC3QwpUo8pjJg==}
+ cpu: [arm]
+ os: [android]
+
+ '@oxc-resolver/binding-android-arm64@11.20.0':
+ resolution: {integrity: sha512-QqslZAuFQG8Q9xm7JuIn8JUbvywhSBMVhuQHtYW+auirZJloS41oxUUaBXk7uUhZJgp44c5zQLeVvmFaDQB+2Q==}
+ cpu: [arm64]
+ os: [android]
+
+ '@oxc-resolver/binding-darwin-arm64@11.20.0':
+ resolution: {integrity: sha512-MUcavykj2ewlR+kc5arpg4tC2RvzJkUxWtNv74pf7lcNk00GpIpN43vXMj+j6r4eMmfZhlb8hueKoIb8e9kAGQ==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@oxc-resolver/binding-darwin-x64@11.20.0':
+ resolution: {integrity: sha512-BGB16nRUK5Etiv//ihPyzj8Lj1px0mhh4YIfe0FDf045ywknfSm0GEbiRESpr6Q4K82AvnyaRIhhluHByvS4bg==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@oxc-resolver/binding-freebsd-x64@11.20.0':
+ resolution: {integrity: sha512-JZgtePaqj3qmD5XFHJaSLWzHRxQu0LaPkdoM1KJXYADvAaa83ijXHclV3ej3CueeW0wxfIAbGCZVP45J0CA7uQ==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0':
+ resolution: {integrity: sha512-hOQ/p3ry3v3SchUBXicrrnszaI/UmYzM4wtS4RGfwgVUX7a+HbyQSzJ5aOzu+o6XZkFkS3ZXN4PZAzhOb77OSg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0':
+ resolution: {integrity: sha512-2ArPksaw0AqeuGBfoS715VF+JvJQAhD2niWgjE5hVO+L+nAfikVQopvngCMX9x4BD8itWoQ3dnikrQyl5Ho5Jg==}
+ cpu: [arm]
+ os: [linux]
+
+ '@oxc-resolver/binding-linux-arm64-gnu@11.20.0':
+ resolution: {integrity: sha512-0bJnmYFp62JdZ4nVMDUZ/C58BCZOCcqgKtnUlp7L9Ojf/czIN+3j72YlLPeWLkzlr6SlYvIQA4SGV/HyO0d+qg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-resolver/binding-linux-arm64-musl@11.20.0':
+ resolution: {integrity: sha512-wKHHzPKZo7Ufhv/Bt6yxT7FOgnIgW4gwXcJUipkShGp68W3wGVqvr1Sr0fY65lN0Oy6y41+g2kIDvkgZaMMUkw==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0':
+ resolution: {integrity: sha512-RN8goF7Ie0B79L4i4G6OeBocTgSC56vJbQ65VJje+oXnldVpLnOU7j/AQ/dP94TcCS+Yh6WG8u3Qt4ETteXFNQ==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0':
+ resolution: {integrity: sha512-5l1yU6/xQEqLZRzxqmMxJfWPslpwCmBsdDGaBvABPehxquCXDC7dd7oraNdKSJUMDXSM7VvVj8H2D2FTjU7oWw==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-resolver/binding-linux-riscv64-musl@11.20.0':
+ resolution: {integrity: sha512-xHEvkbgz6UC+A3JOyDQy76LkUaxsNSfIr3/GV8slwZsnuooJiIB34gzJfsyvR4JdCYNUUPsRJc/w/oWkODu+hg==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-resolver/binding-linux-s390x-gnu@11.20.0':
+ resolution: {integrity: sha512-aWPDUUmSeyHvlW+SoEUd+JIJsQhVhu6a5tBpDRMu058naPAchTgAVGCFy35zjbnFlt0i8hLWziff6HX0D3LU4g==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-resolver/binding-linux-x64-gnu@11.20.0':
+ resolution: {integrity: sha512-x2YeSimvhJjKLVD8KSu8f/rqU1potcdEMkApIPJqjZWN7c2Fpt4g2X32WDg1p+XDAmyT7nuQGe0vnhvXeLbH+g==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@oxc-resolver/binding-linux-x64-musl@11.20.0':
+ resolution: {integrity: sha512-kcRLEIxpZefeYfLChjpgFf3ilBzRDZ+yobMrpRsQlSrxuFGtm3U6PMU7AaEpMqo3NfDGVyJJseAjnRLzMFHjwQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@oxc-resolver/binding-openharmony-arm64@11.20.0':
+ resolution: {integrity: sha512-HHcfnApSZGtKhTiHqe8OZruOZe5XuFQH5/E0Yhj3u8fnFvzkM4/k6WjacUf4SvA0SPEAbfbgYmVPuo0VX/fIBQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@oxc-resolver/binding-wasm32-wasi@11.20.0':
+ resolution: {integrity: sha512-Tn0y1XOFYHNfK1wp1Z5QK8Rcld/bsOwRISQXfqAZ5IBpv8Gz1IvV39fUWNprqNdRizgcvFhOzWwFun2zkJsyBg==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@oxc-resolver/binding-win32-arm64-msvc@11.20.0':
+ resolution: {integrity: sha512-qPi25YNPe4YenS8MgsQU2+bIFHxxpLx1LVna2444cEHqNPhNjvWf9zqj4aWE43H9LpAsTmkkAlA3eL5ElBU3mA==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@oxc-resolver/binding-win32-x64-msvc@11.20.0':
+ resolution: {integrity: sha512-Wb14jWEW8huH6It9F6sXd9vrYmIS7pMrgkU6sxpLxkP+9z+wRgs71hUEhRpcn8FOXAFa27FVWfY2tRpbfTzfLw==}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher-android-arm64@2.5.6':
+ resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ '@parcel/watcher-darwin-arm64@2.5.6':
+ resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@parcel/watcher-darwin-x64@2.5.6':
+ resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@parcel/watcher-freebsd-x64@2.5.6':
+ resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@parcel/watcher-linux-arm-glibc@2.5.6':
+ resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm-musl@2.5.6':
+ resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.6':
+ resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-arm64-musl@2.5.6':
+ resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-linux-x64-glibc@2.5.6':
+ resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@parcel/watcher-linux-x64-musl@2.5.6':
+ resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@parcel/watcher-win32-arm64@2.5.6':
+ resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@parcel/watcher-win32-ia32@2.5.6':
+ resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@parcel/watcher-win32-x64@2.5.6':
+ resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==}
+ engines: {node: '>= 10.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ '@parcel/watcher@2.5.6':
+ resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==}
+ engines: {node: '>= 10.0.0'}
+
+ '@rtsao/scc@1.1.0':
+ resolution: {integrity: sha512-zt6OdqaDoOnJ1ZYsCYGt9YmWzDXl4vQdKTyJev62gFhRGKdx7mcT54V9KIjg+d2wi9EXsPvAPKe7i7WjfVWB8g==}
+
+ '@schummar/icu-type-parser@1.21.5':
+ resolution: {integrity: sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==}
+
+ '@swc/core-darwin-arm64@1.15.41':
+ resolution: {integrity: sha512-kREh6J5paQFvP3i7f/4FbqRNOJREutVFVOkder4GVyCBQ39YmER55cW/y1NNjwrchzFqgYswFn0mMDCqbqKzrw==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@swc/core-darwin-x64@1.15.41':
+ resolution: {integrity: sha512-N8B56ESFazZAWZyIkecADSPCwlLEinW7QLMEeotCpv4J7VXwfH+OLkmRL8o96UZ+1355fwHxDTS6/wK7yucvkA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@swc/core-linux-arm-gnueabihf@1.15.41':
+ resolution: {integrity: sha512-6XrId2fyle0mS5xxON8rU84mPd2Cq1kDJRj+4BnQKTd7u+2kSA6Ww+JkOP0iTNqOqt9OXhPOEAjBHAuonWcdCg==}
+ engines: {node: '>=10'}
+ cpu: [arm]
+ os: [linux]
+
+ '@swc/core-linux-arm64-gnu@1.15.41':
+ resolution: {integrity: sha512-ynLIarxlkVnqHn1D0fKOVht6mNU5ks6lrH+MY3kkS+XFaGGgDxFZVjWKJlkYTKm3RCvBTfA8Ng5fLufXheMRKQ==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-arm64-musl@1.15.41':
+ resolution: {integrity: sha512-dXu/5vd4gh8symyhRF+4G7gOPkjmb4pONhh7sl+6GSiW0LOKZlfu5kXmyFbTz9smOT7jgr002qY9b1nujjXt2A==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@swc/core-linux-ppc64-gnu@1.15.41':
+ resolution: {integrity: sha512-XGO6zVPXoPE0gf/XnI4jBbafNT13AYgoh6ns0JCSdOetI/kqVf0vhpz7NuNgAzZrMVCsmieqjPoTwViDgh4mOQ==}
+ engines: {node: '>=10'}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-s390x-gnu@1.15.41':
+ resolution: {integrity: sha512-0WUglRwyZtW+iMi7J3iFdrCxreZZIKf4egTwEQfIYRsqFax69A0OrFj+NIoFSE03xBT/IFRrg+S8K6f9Ky+4hA==}
+ engines: {node: '>=10'}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-x64-gnu@1.15.41':
+ resolution: {integrity: sha512-VxkuQK59c0tHm6uJZCUrS3cyA2JhGGfdU6e41SZz0x/JS+4Sm7C1mIc97In14vkZJopEt7yXA2TouCqZDSygEA==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@swc/core-linux-x64-musl@1.15.41':
+ resolution: {integrity: sha512-/0qXIu1ZxggLuovLb22vFfKHq2AA4n6Whw5UwmVCHk4pkw7KWnPIQpMCEqUMPsNkFJig7PPp/TSYFu8ZEb2rtQ==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@swc/core-win32-arm64-msvc@1.15.41':
+ resolution: {integrity: sha512-Y481sMNZM6rECh9VO4+y26N1lWEDAyxnBZskUf37fl90uHE946VHfmiVQWT0uMFOhyJJFovGTRuF4W82dwewUg==}
+ engines: {node: '>=10'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@swc/core-win32-ia32-msvc@1.15.41':
+ resolution: {integrity: sha512-BAchBD5qeUzy3hiPSLJtaaoSm4blCLyYffOF1bGE4ETcV+OisqjUAwDQMJj++4bTpvMCDzwC+Bj3PmQyBCtscw==}
+ engines: {node: '>=10'}
+ cpu: [ia32]
+ os: [win32]
+
+ '@swc/core-win32-x64-msvc@1.15.41':
+ resolution: {integrity: sha512-WOkA+fJ/ViVBQDsSV9JC52NACTe5PhlurA6viASDZGb7HR3KS01ZG7RZ+Bg6SVQFIoq3gSbTsskQVe6EbHFAYw==}
+ engines: {node: '>=10'}
+ cpu: [x64]
+ os: [win32]
+
+ '@swc/core@1.15.41':
+ resolution: {integrity: sha512-03nQq/082QRJJiOvp3FGbgxTGyyxMxohPTjhk/W9bD2J0tk4ukITI7goOhOO2WbaHn/lsPmo/zf8+DIXhwpgYQ==}
+ engines: {node: '>=10'}
+ peerDependencies:
+ '@swc/helpers': '>=0.5.17'
+ peerDependenciesMeta:
+ '@swc/helpers':
+ optional: true
+
+ '@swc/counter@0.1.3':
+ resolution: {integrity: sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==}
+
+ '@swc/helpers@0.5.15':
+ resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==}
+
+ '@swc/types@0.1.26':
+ resolution: {integrity: sha512-lyMwd7WGgG79RS7EERZV3T8wMdmPq3xwyg+1nmAM64kIhx5yl+juO2PYIHb7vTiPgPCj8LYjsNV2T5wiQHUEaw==}
+
+ '@tailwindcss/node@4.3.0':
+ resolution: {integrity: sha512-aFb4gUhFOgdh9AXo4IzBEOzBkkAxm9VigwDJnMIYv3lcfXCJVesNfbEaBl4BNgVRyid92AmdviqwBUBRKSeY3g==}
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ resolution: {integrity: sha512-TJPiq67tKlLuObP6RkwvVGDoxCMBVtDgKkLfa/uyj7/FyxvQwHS+UOnVrXXgbEsfUaMgiVvC4KbJnRr26ho4Ng==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [android]
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ resolution: {integrity: sha512-oMN/WZRb+SO37BmUElEgeEWuU8E/HXRkiODxJxLe1UTHVXLrdVSgfaJV7pSlhRGMSOiXLuxTIjfsF3wYvz8cgQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ resolution: {integrity: sha512-N6CUmu4a6bKVADfw77p+iw6Yd9Q3OBhe0veaDX+QazfuVYlQsHfDgxBrsjQ/IW+zywL8mTrNd0SdJT/zgtvMdA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [darwin]
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ resolution: {integrity: sha512-zDL5hBkQdH5C6MpqbK3gQAgP80tsMwSI26vjOzjJtNCMUo0lFgOItzHKBIupOZNQxt3ouPH7RPhvNhiTfCe5CQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ resolution: {integrity: sha512-R06HdNi7A7OEoMsf6d4tjZ71RCWnZQPHj2mnotSFURjNLdBC+cIgXQ7l81CqeoiQftjf6OOblxXMInMgN2VzMA==}
+ engines: {node: '>= 20'}
+ cpu: [arm]
+ os: [linux]
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ resolution: {integrity: sha512-qTJHELX8jetjhRQHCLilkVLmybpzNQAtaI/gaoVoidn/ufbNDbAo8KlK2J+yPoc8wQxvDxCmh/5lr8nC1+lTbg==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ resolution: {integrity: sha512-Z6sukiQsngnWO+l39X4pPbiWT81IC+PLKF+PHxIlyZbGNb9MODfYlXEVlFvej5BOZInWX01kVyzeLvHsXhfczQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ resolution: {integrity: sha512-DRNdQRpSGzRGfARVuVkxvM8Q12nh19l4BF/G7zGA1oe+9wcC6saFBHTISrpIcKzhiXtSrlSrluCfvMuledoCTQ==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ resolution: {integrity: sha512-Z0IADbDo8bh6I7h2IQMx601AdXBLfFpEdUotft86evd/8ZPflZe9COPO8Q1vw+pfLWIUo9zN/JGZvwuAJqduqg==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ resolution: {integrity: sha512-HNZGOUxEmElksYR7S6sC5jTeNGpobAsy9u7Gu0AskJ8/20FR9GqebUyB+HBcU/ax6BHuiuJi+Oda4B+YX6H1yA==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+ bundledDependencies:
+ - '@napi-rs/wasm-runtime'
+ - '@emnapi/core'
+ - '@emnapi/runtime'
+ - '@tybys/wasm-util'
+ - '@emnapi/wasi-threads'
+ - tslib
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ resolution: {integrity: sha512-Pe+RPVTi1T+qymuuRpcdvwSVZjnll/f7n8gBxMMh3xLTctMDKqpdfGimbMyioqtLhUYZxdJ9wGNhV7MKHvgZsQ==}
+ engines: {node: '>= 20'}
+ cpu: [arm64]
+ os: [win32]
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ resolution: {integrity: sha512-Mvrf2kXW/yeW/OTezZlCGOirXRcUuLIBx/5Y12BaPM7wJoryG6dfS/NJL8aBPqtTEx/Vm4T4vKzFUcKDT+TKUA==}
+ engines: {node: '>= 20'}
+ cpu: [x64]
+ os: [win32]
+
+ '@tailwindcss/oxide@4.3.0':
+ resolution: {integrity: sha512-F7HZGBeN9I0/AuuJS5PwcD8xayx5ri5GhjYUDBEVYUkexyA/giwbDNjRVrxSezE3T250OU2K/wp/ltWx3UOefg==}
+ engines: {node: '>= 20'}
+
+ '@tailwindcss/postcss@4.3.0':
+ resolution: {integrity: sha512-Jm05Tjx+9yCLGv5qw1c+84Psds8MnyrEQYCB+FFk2lgGiUjlRqdxke4mVTuYrj2xnVZqKim2Apr5ySuQRYAw/w==}
+
+ '@tybys/wasm-util@0.10.2':
+ resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==}
+
+ '@types/estree@1.0.9':
+ resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
+
+ '@types/json-schema@7.0.15':
+ resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==}
+
+ '@types/json5@0.0.29':
+ resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==}
+
+ '@types/node@22.19.21':
+ resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==}
+
+ '@types/react-dom@19.2.3':
+ resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==}
+ peerDependencies:
+ '@types/react': ^19.2.0
+
+ '@types/react@19.2.17':
+ resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==}
+
+ '@typescript-eslint/eslint-plugin@8.61.0':
+ resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ '@typescript-eslint/parser': ^8.61.0
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/parser@8.61.0':
+ resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/project-service@8.61.0':
+ resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/scope-manager@8.61.0':
+ resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/tsconfig-utils@8.61.0':
+ resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/type-utils@8.61.0':
+ resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/types@8.61.0':
+ resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@typescript-eslint/typescript-estree@8.61.0':
+ resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/utils@8.61.0':
+ resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ '@typescript-eslint/visitor-keys@8.61.0':
+ resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ '@unrs/resolver-binding-android-arm-eabi@1.12.2':
+ resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==}
+ cpu: [arm]
+ os: [android]
+
+ '@unrs/resolver-binding-android-arm64@1.12.2':
+ resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==}
+ cpu: [arm64]
+ os: [android]
+
+ '@unrs/resolver-binding-darwin-arm64@1.12.2':
+ resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==}
+ cpu: [arm64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-darwin-x64@1.12.2':
+ resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==}
+ cpu: [x64]
+ os: [darwin]
+
+ '@unrs/resolver-binding-freebsd-x64@1.12.2':
+ resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==}
+ cpu: [x64]
+ os: [freebsd]
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2':
+ resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2':
+ resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==}
+ cpu: [arm]
+ os: [linux]
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.12.2':
+ resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.12.2':
+ resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-loong64-gnu@1.12.2':
+ resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-loong64-musl@1.12.2':
+ resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==}
+ cpu: [loong64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2':
+ resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==}
+ cpu: [ppc64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2':
+ resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.12.2':
+ resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==}
+ cpu: [riscv64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.12.2':
+ resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==}
+ cpu: [s390x]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.12.2':
+ resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ '@unrs/resolver-binding-linux-x64-musl@1.12.2':
+ resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ '@unrs/resolver-binding-openharmony-arm64@1.12.2':
+ resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==}
+ cpu: [arm64]
+ os: [openharmony]
+
+ '@unrs/resolver-binding-wasm32-wasi@1.12.2':
+ resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==}
+ engines: {node: '>=14.0.0'}
+ cpu: [wasm32]
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.12.2':
+ resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==}
+ cpu: [arm64]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.12.2':
+ resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==}
+ cpu: [ia32]
+ os: [win32]
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.12.2':
+ resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==}
+ cpu: [x64]
+ os: [win32]
+
+ acorn-jsx@5.3.2:
+ resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
+ peerDependencies:
+ acorn: ^6.0.0 || ^7.0.0 || ^8.0.0
+
+ acorn@8.17.0:
+ resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==}
+ engines: {node: '>=0.4.0'}
+ hasBin: true
+
+ ajv@6.15.0:
+ resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==}
+
+ ansi-escapes@7.3.0:
+ resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==}
+ engines: {node: '>=18'}
+
+ ansi-regex@6.2.2:
+ resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==}
+ engines: {node: '>=12'}
+
+ ansi-styles@4.3.0:
+ resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==}
+ engines: {node: '>=8'}
+
+ ansi-styles@6.2.3:
+ resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==}
+ engines: {node: '>=12'}
+
+ argparse@2.0.1:
+ resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
+
+ aria-query@5.3.2:
+ resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==}
+ engines: {node: '>= 0.4'}
+
+ array-buffer-byte-length@1.0.2:
+ resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==}
+ engines: {node: '>= 0.4'}
+
+ array-includes@3.1.9:
+ resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlast@1.2.5:
+ resolution: {integrity: sha512-CVvd6FHg1Z3POpBLxO6E6zr+rSKEQ9L6rZHAaY7lLfhKsWYUBBOuMs0e9o24oopj6H+geRCX0YJ+TJLBK2eHyQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.findlastindex@1.2.6:
+ resolution: {integrity: sha512-F/TKATkzseUExPlfvmwQKGITM3DGTK+vkAsCZoDc5daVygbJBnjEUCbgkAvVFsgfXfX4YIqZ/27G3k3tdXrTxQ==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flat@1.3.3:
+ resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.flatmap@1.3.3:
+ resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==}
+ engines: {node: '>= 0.4'}
+
+ array.prototype.tosorted@1.1.4:
+ resolution: {integrity: sha512-p6Fx8B7b7ZhL/gmUsAy0D15WhvDccw3mnGNbZpi3pmeJdxtWsj2jEaI4Y6oo3XiHfzuSgPwKc04MYt6KgvC/wA==}
+ engines: {node: '>= 0.4'}
+
+ arraybuffer.prototype.slice@1.0.4:
+ resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==}
+ engines: {node: '>= 0.4'}
+
+ ast-types-flow@0.0.8:
+ resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==}
+
+ async-function@1.0.0:
+ resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==}
+ engines: {node: '>= 0.4'}
+
+ available-typed-arrays@1.0.7:
+ resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==}
+ engines: {node: '>= 0.4'}
+
+ axe-core@4.12.1:
+ resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==}
+ engines: {node: '>=4'}
+
+ axobject-query@4.1.0:
+ resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==}
+ engines: {node: '>= 0.4'}
+
+ balanced-match@1.0.2:
+ resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==}
+
+ balanced-match@4.0.4:
+ resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==}
+ engines: {node: 18 || 20 || >=22}
+
+ baseline-browser-mapping@2.10.36:
+ resolution: {integrity: sha512-lVq/Df7LXlO79MVaaUHztSwWiG9oXoWHlgvNS51v8Dpd4+G4/VIy6qYePTw31nAVls33nUtnfezYeLkYAak9dg==}
+ engines: {node: '>=6.0.0'}
+ hasBin: true
+
+ brace-expansion@1.1.15:
+ resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==}
+
+ brace-expansion@5.0.6:
+ resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==}
+ engines: {node: 18 || 20 || >=22}
+
+ braces@3.0.3:
+ resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==}
+ engines: {node: '>=8'}
+
+ browserslist@4.28.2:
+ resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==}
+ engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
+ hasBin: true
+
+ call-bind-apply-helpers@1.0.2:
+ resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bind@1.0.9:
+ resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==}
+ engines: {node: '>= 0.4'}
+
+ call-bound@1.0.4:
+ resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==}
+ engines: {node: '>= 0.4'}
+
+ callsites@3.1.0:
+ resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==}
+ engines: {node: '>=6'}
+
+ caniuse-lite@1.0.30001799:
+ resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==}
+
+ chalk@4.1.2:
+ resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
+ engines: {node: '>=10'}
+
+ cli-cursor@5.0.0:
+ resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==}
+ engines: {node: '>=18'}
+
+ cli-truncate@5.2.0:
+ resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==}
+ engines: {node: '>=20'}
+
+ client-only@0.0.1:
+ resolution: {integrity: sha512-IV3Ou0jSMzZrd3pZ48nLkT9DA7Ag1pnPzaiQhpW7c3RbcqqzvzzVu+L8gfqMp/8IM2MQtSiqaCxrrcfu8I8rMA==}
+
+ color-convert@2.0.1:
+ resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==}
+ engines: {node: '>=7.0.0'}
+
+ color-name@1.1.4:
+ resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
+
+ concat-map@0.0.1:
+ resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==}
+
+ convert-source-map@2.0.0:
+ resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==}
+
+ cross-spawn@7.0.6:
+ resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
+ engines: {node: '>= 8'}
+
+ csstype@3.2.3:
+ resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
+
+ damerau-levenshtein@1.0.8:
+ resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==}
+
+ data-view-buffer@1.0.2:
+ resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-length@1.0.2:
+ resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==}
+ engines: {node: '>= 0.4'}
+
+ data-view-byte-offset@1.0.1:
+ resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==}
+ engines: {node: '>= 0.4'}
+
+ debug@3.2.7:
+ resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ debug@4.4.3:
+ resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==}
+ engines: {node: '>=6.0'}
+ peerDependencies:
+ supports-color: '*'
+ peerDependenciesMeta:
+ supports-color:
+ optional: true
+
+ deep-is@0.1.4:
+ resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==}
+
+ define-data-property@1.1.4:
+ resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==}
+ engines: {node: '>= 0.4'}
+
+ define-properties@1.2.1:
+ resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==}
+ engines: {node: '>= 0.4'}
+
+ detect-libc@2.1.2:
+ resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
+ engines: {node: '>=8'}
+
+ doctrine@2.1.0:
+ resolution: {integrity: sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==}
+ engines: {node: '>=0.10.0'}
+
+ dunder-proto@1.0.1:
+ resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
+ engines: {node: '>= 0.4'}
+
+ electron-to-chromium@1.5.372:
+ resolution: {integrity: sha512-M3yhbAlilnwqC8D21t28UCDGHyitShTmmLRU/H+b74P6Ski16Nb9HONYEaVpMj/pwC7BEo5B95FpjODLCWbtfA==}
+
+ emoji-regex@10.6.0:
+ resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==}
+
+ emoji-regex@9.2.2:
+ resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==}
+
+ enhanced-resolve@5.24.0:
+ resolution: {integrity: sha512-SkE2t82KlkkxQRVMVLAGKxLfORGQfrkx5dkj+vlgXRVNEdPc4eZcR+J/Fvj8C+yKSFH5L0q3NFlyufOVQnCcYQ==}
+ engines: {node: '>=10.13.0'}
+
+ environment@1.1.0:
+ resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==}
+ engines: {node: '>=18'}
+
+ es-abstract@1.24.2:
+ resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==}
+ engines: {node: '>= 0.4'}
+
+ es-define-property@1.0.1:
+ resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==}
+ engines: {node: '>= 0.4'}
+
+ es-errors@1.3.0:
+ resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
+ engines: {node: '>= 0.4'}
+
+ es-iterator-helpers@1.3.3:
+ resolution: {integrity: sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==}
+ engines: {node: '>= 0.4'}
+
+ es-object-atoms@1.1.2:
+ resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==}
+ engines: {node: '>= 0.4'}
+
+ es-set-tostringtag@2.1.0:
+ resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
+ engines: {node: '>= 0.4'}
+
+ es-shim-unscopables@1.1.0:
+ resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==}
+ engines: {node: '>= 0.4'}
+
+ es-to-primitive@1.3.0:
+ resolution: {integrity: sha512-w+5mJ3GuFL+NjVtJlvydShqE1eN3h3PbI7/5LAsYJP/2qtuMXjfL2LpHSRqo4b4eSF5K/DH1JXKUAHSB2UW50g==}
+ engines: {node: '>= 0.4'}
+
+ escalade@3.2.0:
+ resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==}
+ engines: {node: '>=6'}
+
+ escape-string-regexp@4.0.0:
+ resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
+ engines: {node: '>=10'}
+
+ eslint-config-next@16.2.9:
+ resolution: {integrity: sha512-olGtBrs07bQchpaJWeqbk9GaMoU0oGmN/pYNEBXSbfgKngb5uHnPe37X6tVeh6DJfaWFQildvinGEOrolo5fmw==}
+ peerDependencies:
+ eslint: '>=9.0.0'
+ typescript: '>=3.3.1'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ eslint-import-resolver-node@0.3.10:
+ resolution: {integrity: sha512-tRrKqFyCaKict5hOd244sL6EQFNycnMQnBe+j8uqGNXYzsImGbGUU4ibtoaBmv5FLwJwcFJNeg1GeVjQfbMrDQ==}
+
+ eslint-import-resolver-typescript@3.10.1:
+ resolution: {integrity: sha512-A1rHYb06zjMGAxdLSkN2fXPBwuSaQ0iO5M/hdyS0Ajj1VBaRp0sPD3dn1FhME3c/JluGFbwSxyCfqdSbtQLAHQ==}
+ engines: {node: ^14.18.0 || >=16.0.0}
+ peerDependencies:
+ eslint: '*'
+ eslint-plugin-import: '*'
+ eslint-plugin-import-x: '*'
+ peerDependenciesMeta:
+ eslint-plugin-import:
+ optional: true
+ eslint-plugin-import-x:
+ optional: true
+
+ eslint-module-utils@2.13.0:
+ resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: '*'
+ eslint-import-resolver-node: '*'
+ eslint-import-resolver-typescript: '*'
+ eslint-import-resolver-webpack: '*'
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+ eslint:
+ optional: true
+ eslint-import-resolver-node:
+ optional: true
+ eslint-import-resolver-typescript:
+ optional: true
+ eslint-import-resolver-webpack:
+ optional: true
+
+ eslint-plugin-import@2.32.0:
+ resolution: {integrity: sha512-whOE1HFo/qJDyX4SnXzP4N6zOWn79WhnCUY/iDR0mPfQZO8wcYE4JClzI2oZrhBnnMUCBCHZhO6VQyoBU95mZA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ '@typescript-eslint/parser': '*'
+ eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 || ^9
+ peerDependenciesMeta:
+ '@typescript-eslint/parser':
+ optional: true
+
+ eslint-plugin-jsx-a11y@6.10.2:
+ resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==}
+ engines: {node: '>=4.0'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9
+
+ eslint-plugin-react-hooks@7.1.1:
+ resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==}
+ engines: {node: '>=18'}
+ peerDependencies:
+ eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0
+
+ eslint-plugin-react@7.37.5:
+ resolution: {integrity: sha512-Qteup0SqU15kdocexFNAJMvCJEfa2xUKNV4CC1xsVMrIIqEy3SQ/rqyxCWNzfrd3/ldy6HMlD2e0JDVpDg2qIA==}
+ engines: {node: '>=4'}
+ peerDependencies:
+ eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9.7
+
+ eslint-scope@8.4.0:
+ resolution: {integrity: sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@3.4.3:
+ resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==}
+ engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
+
+ eslint-visitor-keys@4.2.1:
+ resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ eslint-visitor-keys@5.0.1:
+ resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==}
+ engines: {node: ^20.19.0 || ^22.13.0 || >=24}
+
+ eslint@9.39.4:
+ resolution: {integrity: sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ hasBin: true
+ peerDependencies:
+ jiti: '*'
+ peerDependenciesMeta:
+ jiti:
+ optional: true
+
+ espree@10.4.0:
+ resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+
+ esquery@1.7.0:
+ resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==}
+ engines: {node: '>=0.10'}
+
+ esrecurse@4.3.0:
+ resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==}
+ engines: {node: '>=4.0'}
+
+ estraverse@5.3.0:
+ resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==}
+ engines: {node: '>=4.0'}
+
+ esutils@2.0.3:
+ resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==}
+ engines: {node: '>=0.10.0'}
+
+ eventemitter3@5.0.4:
+ resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==}
+
+ fast-deep-equal@3.1.3:
+ resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==}
+
+ fast-glob@3.3.1:
+ resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==}
+ engines: {node: '>=8.6.0'}
+
+ fast-json-stable-stringify@2.1.0:
+ resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
+
+ fast-levenshtein@2.0.6:
+ resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
+
+ fastq@1.20.1:
+ resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==}
+
+ fd-package-json@2.0.0:
+ resolution: {integrity: sha512-jKmm9YtsNXN789RS/0mSzOC1NUq9mkVd65vbSSVsKdjGvYXBuE4oWe2QOEoFeRmJg+lPuZxpmrfFclNhoRMneQ==}
+
+ fdir@6.5.0:
+ resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==}
+ engines: {node: '>=12.0.0'}
+ peerDependencies:
+ picomatch: ^3 || ^4
+ peerDependenciesMeta:
+ picomatch:
+ optional: true
+
+ file-entry-cache@8.0.0:
+ resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==}
+ engines: {node: '>=16.0.0'}
+
+ fill-range@7.1.1:
+ resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
+ engines: {node: '>=8'}
+
+ find-up@5.0.0:
+ resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==}
+ engines: {node: '>=10'}
+
+ flat-cache@4.0.1:
+ resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==}
+ engines: {node: '>=16'}
+
+ flatted@3.4.2:
+ resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==}
+
+ for-each@0.3.5:
+ resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==}
+ engines: {node: '>= 0.4'}
+
+ formatly@0.3.0:
+ resolution: {integrity: sha512-9XNj/o4wrRFyhSMJOvsuyMwy8aUfBaZ1VrqHVfohyXf0Sw0e+yfKG+xZaY3arGCOMdwFsqObtzVOc1gU9KiT9w==}
+ engines: {node: '>=18.3.0'}
+ hasBin: true
+
+ function-bind@1.1.2:
+ resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==}
+
+ function.prototype.name@1.2.0:
+ resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==}
+ engines: {node: '>= 0.4'}
+
+ functions-have-names@1.2.3:
+ resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==}
+
+ generator-function@2.0.1:
+ resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==}
+ engines: {node: '>= 0.4'}
+
+ gensync@1.0.0-beta.2:
+ resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
+ engines: {node: '>=6.9.0'}
+
+ get-east-asian-width@1.6.0:
+ resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==}
+ engines: {node: '>=18'}
+
+ get-intrinsic@1.3.0:
+ resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==}
+ engines: {node: '>= 0.4'}
+
+ get-proto@1.0.1:
+ resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==}
+ engines: {node: '>= 0.4'}
+
+ get-symbol-description@1.1.0:
+ resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==}
+ engines: {node: '>= 0.4'}
+
+ get-tsconfig@4.14.0:
+ resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==}
+
+ glob-parent@5.1.2:
+ resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==}
+ engines: {node: '>= 6'}
+
+ glob-parent@6.0.2:
+ resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
+ engines: {node: '>=10.13.0'}
+
+ globals@14.0.0:
+ resolution: {integrity: sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==}
+ engines: {node: '>=18'}
+
+ globals@16.4.0:
+ resolution: {integrity: sha512-ob/2LcVVaVGCYN+r14cnwnoDPUufjiYgSqRhiFD0Q1iI4Odora5RE8Iv1D24hAz5oMophRGkGz+yuvQmmUMnMw==}
+ engines: {node: '>=18'}
+
+ globalthis@1.0.4:
+ resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==}
+ engines: {node: '>= 0.4'}
+
+ gopd@1.2.0:
+ resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==}
+ engines: {node: '>= 0.4'}
+
+ graceful-fs@4.2.11:
+ resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
+
+ has-bigints@1.1.0:
+ resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==}
+ engines: {node: '>= 0.4'}
+
+ has-flag@4.0.0:
+ resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==}
+ engines: {node: '>=8'}
+
+ has-property-descriptors@1.0.2:
+ resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==}
+
+ has-proto@1.2.0:
+ resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==}
+ engines: {node: '>= 0.4'}
+
+ has-symbols@1.1.0:
+ resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==}
+ engines: {node: '>= 0.4'}
+
+ has-tostringtag@1.0.2:
+ resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==}
+ engines: {node: '>= 0.4'}
+
+ hasown@2.0.4:
+ resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==}
+ engines: {node: '>= 0.4'}
+
+ hermes-estree@0.25.1:
+ resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==}
+
+ hermes-parser@0.25.1:
+ resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==}
+
+ husky@9.1.7:
+ resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==}
+ engines: {node: '>=18'}
+ hasBin: true
+
+ icu-minify@4.13.0:
+ resolution: {integrity: sha512-SIFMeUHZJjzS5RvIGvybKvWoHjDm9cGVEs2EpJ8PmywOdJLWyblPm7TdPLLoUtkJtwQD7iGhl2WMptZ+N0on+w==}
+
+ ignore@5.3.2:
+ resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==}
+ engines: {node: '>= 4'}
+
+ ignore@7.0.5:
+ resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==}
+ engines: {node: '>= 4'}
+
+ import-fresh@3.3.1:
+ resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==}
+ engines: {node: '>=6'}
+
+ imurmurhash@0.1.4:
+ resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==}
+ engines: {node: '>=0.8.19'}
+
+ internal-slot@1.1.0:
+ resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==}
+ engines: {node: '>= 0.4'}
+
+ intl-messageformat@11.2.8:
+ resolution: {integrity: sha512-l323RCl3qJDVQ8U9j74ut/hVMdg3VPsOHpVMDvFfz9qiq4dPO5ooVYFNVUzzrpgG39a+RLzcXyJb8VFgIU+tUA==}
+
+ is-array-buffer@3.0.5:
+ resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==}
+ engines: {node: '>= 0.4'}
+
+ is-async-function@2.1.1:
+ resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==}
+ engines: {node: '>= 0.4'}
+
+ is-bigint@1.1.0:
+ resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==}
+ engines: {node: '>= 0.4'}
+
+ is-boolean-object@1.2.2:
+ resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==}
+ engines: {node: '>= 0.4'}
+
+ is-bun-module@2.0.0:
+ resolution: {integrity: sha512-gNCGbnnnnFAUGKeZ9PdbyeGYJqewpmc2aKHUEMO5nQPWU9lOmv7jcmQIv+qHD8fXW6W7qfuCwX4rY9LNRjXrkQ==}
+
+ is-callable@1.2.7:
+ resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==}
+ engines: {node: '>= 0.4'}
+
+ is-core-module@2.16.2:
+ resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==}
+ engines: {node: '>= 0.4'}
+
+ is-data-view@1.0.2:
+ resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==}
+ engines: {node: '>= 0.4'}
+
+ is-date-object@1.1.0:
+ resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==}
+ engines: {node: '>= 0.4'}
+
+ is-document.all@1.0.0:
+ resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==}
+ engines: {node: '>= 0.4'}
+
+ is-extglob@2.1.1:
+ resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
+ engines: {node: '>=0.10.0'}
+
+ is-finalizationregistry@1.1.1:
+ resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==}
+ engines: {node: '>= 0.4'}
+
+ is-fullwidth-code-point@5.1.0:
+ resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==}
+ engines: {node: '>=18'}
+
+ is-generator-function@1.1.2:
+ resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==}
+ engines: {node: '>= 0.4'}
+
+ is-glob@4.0.3:
+ resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==}
+ engines: {node: '>=0.10.0'}
+
+ is-map@2.0.3:
+ resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==}
+ engines: {node: '>= 0.4'}
+
+ is-negative-zero@2.0.3:
+ resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==}
+ engines: {node: '>= 0.4'}
+
+ is-number-object@1.1.1:
+ resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==}
+ engines: {node: '>= 0.4'}
+
+ is-number@7.0.0:
+ resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
+ engines: {node: '>=0.12.0'}
+
+ is-regex@1.2.1:
+ resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==}
+ engines: {node: '>= 0.4'}
+
+ is-set@2.0.3:
+ resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==}
+ engines: {node: '>= 0.4'}
+
+ is-shared-array-buffer@1.0.4:
+ resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==}
+ engines: {node: '>= 0.4'}
+
+ is-string@1.1.1:
+ resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==}
+ engines: {node: '>= 0.4'}
+
+ is-symbol@1.1.1:
+ resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==}
+ engines: {node: '>= 0.4'}
+
+ is-typed-array@1.1.15:
+ resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==}
+ engines: {node: '>= 0.4'}
+
+ is-weakmap@2.0.2:
+ resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==}
+ engines: {node: '>= 0.4'}
+
+ is-weakref@1.1.1:
+ resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==}
+ engines: {node: '>= 0.4'}
+
+ is-weakset@2.0.4:
+ resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==}
+ engines: {node: '>= 0.4'}
+
+ isarray@2.0.5:
+ resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==}
+
+ isexe@2.0.0:
+ resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
+
+ iterator.prototype@1.1.5:
+ resolution: {integrity: sha512-H0dkQoCa3b2VEeKQBOxFph+JAbcrQdE7KC0UkqwpLmv2EC4P41QXP+rqo9wYodACiG5/WM5s9oDApTU8utwj9g==}
+ engines: {node: '>= 0.4'}
+
+ jiti@2.7.0:
+ resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
+ hasBin: true
+
+ js-tokens@4.0.0:
+ resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
+
+ js-yaml@4.2.0:
+ resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
+ hasBin: true
+
+ jsesc@3.1.0:
+ resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ json-buffer@3.0.1:
+ resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==}
+
+ json-schema-traverse@0.4.1:
+ resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==}
+
+ json-stable-stringify-without-jsonify@1.0.1:
+ resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==}
+
+ json5@1.0.2:
+ resolution: {integrity: sha512-g1MWMLBiz8FKi1e4w0UyVL3w+iJceWAFBAaBnnGKOpNa5f8TLktkbre1+s6oICydWAm+HRUGTmI+//xv2hvXYA==}
+ hasBin: true
+
+ json5@2.2.3:
+ resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==}
+ engines: {node: '>=6'}
+ hasBin: true
+
+ jsx-ast-utils@3.3.5:
+ resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
+ engines: {node: '>=4.0'}
+
+ keyv@4.5.4:
+ resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==}
+
+ knip@6.16.1:
+ resolution: {integrity: sha512-TKMn1rxgH6h9vXR9Y0B+Cq7AdPTr9EI02IwoT65NzqYUkvoDQAaJ/aPybiFpAhZ1px6cNYYwXf86iHkBgzCo9w==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+ hasBin: true
+
+ language-subtag-registry@0.3.23:
+ resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==}
+
+ language-tags@1.0.9:
+ resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==}
+ engines: {node: '>=0.10'}
+
+ levn@0.4.1:
+ resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==}
+ engines: {node: '>= 0.8.0'}
+
+ lightningcss-android-arm64@1.32.0:
+ resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [android]
+
+ lightningcss-darwin-arm64@1.32.0:
+ resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [darwin]
+
+ lightningcss-darwin-x64@1.32.0:
+ resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [darwin]
+
+ lightningcss-freebsd-x64@1.32.0:
+ resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [freebsd]
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm]
+ os: [linux]
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [glibc]
+
+ lightningcss-linux-x64-musl@1.32.0:
+ resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [linux]
+ libc: [musl]
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [arm64]
+ os: [win32]
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==}
+ engines: {node: '>= 12.0.0'}
+ cpu: [x64]
+ os: [win32]
+
+ lightningcss@1.32.0:
+ resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==}
+ engines: {node: '>= 12.0.0'}
+
+ lint-staged@17.0.7:
+ resolution: {integrity: sha512-JrSobt+tW3rH8IOMi8tDZd3foorM5yPEkLD/V2NxobgHrFfHWGee4MOLVuZeScgxftEwbHrPHIFA/ZL+nUJeuA==}
+ engines: {node: '>=22.22.1'}
+ hasBin: true
+
+ listr2@10.2.1:
+ resolution: {integrity: sha512-7I5knELsJKTUjXG+A6BkKAiGkW1i25fNa/xlUl9hFtk15WbE9jndA89xu5FzQKrY5llajE1hfZZFMILXkDHk/Q==}
+ engines: {node: '>=22.13.0'}
+
+ locate-path@6.0.0:
+ resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==}
+ engines: {node: '>=10'}
+
+ lodash.merge@4.6.2:
+ resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
+
+ log-update@6.1.0:
+ resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==}
+ engines: {node: '>=18'}
+
+ loose-envify@1.4.0:
+ resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==}
+ hasBin: true
+
+ lru-cache@5.1.1:
+ resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==}
+
+ lucide-react@1.18.0:
+ resolution: {integrity: sha512-LZDb7H/0YfM+RJncD0hDQRCAu+vSGODqpe35TuVI8EuXaRjkczbsx7p8dY4J87F/MUSj6bpYqeI8nw8qXaAdmA==}
+ peerDependencies:
+ react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0
+
+ magic-string@0.30.21:
+ resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
+
+ marked@18.0.5:
+ resolution: {integrity: sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w==}
+ engines: {node: '>= 20'}
+ hasBin: true
+
+ math-intrinsics@1.1.0:
+ resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==}
+ engines: {node: '>= 0.4'}
+
+ merge2@1.4.1:
+ resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
+ engines: {node: '>= 8'}
+
+ micromatch@4.0.8:
+ resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==}
+ engines: {node: '>=8.6'}
+
+ mimic-function@5.0.1:
+ resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==}
+ engines: {node: '>=18'}
+
+ minimatch@10.2.5:
+ resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==}
+ engines: {node: 18 || 20 || >=22}
+
+ minimatch@3.1.5:
+ resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==}
+
+ minimist@1.2.8:
+ resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==}
+
+ ms@2.1.3:
+ resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==}
+
+ nanoid@3.3.12:
+ resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==}
+ engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
+ hasBin: true
+
+ napi-postinstall@0.3.4:
+ resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==}
+ engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
+ hasBin: true
+
+ natural-compare@1.4.0:
+ resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==}
+
+ negotiator@1.0.0:
+ resolution: {integrity: sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==}
+ engines: {node: '>= 0.6'}
+
+ next-intl-swc-plugin-extractor@4.13.0:
+ resolution: {integrity: sha512-6S/fJI0KXvLCL8nhBo9P8eGaJPzmwJBTCzX0NaUIj0VyU8U89d//T+vjMLdNIXl5MlLaYH7B9MbAjb8Mvu+tqQ==}
+
+ next-intl@4.13.0:
+ resolution: {integrity: sha512-OvNq2v5XLx4EkQOsAhVE9g+6zdb83XHusADCXXtIW4LILYnjEVaeINdr1lkVWKSjzwNUiMSlH5N4K0OQTRiv6A==}
+ peerDependencies:
+ next: ^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0
+ react: ^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+ typescript: '*'
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
+ next@16.2.9:
+ resolution: {integrity: sha512-MEOJiq/UvuezAdqVSceHbqDgZt1kDw2tpGVOlsdIoJsQdbN2JY2hpVG4xnXGkbdJUOEWhnRfiu/O4Hpc9Juwww==}
+ engines: {node: '>=20.9.0'}
+ hasBin: true
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+ '@playwright/test': ^1.51.1
+ 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-addon-api@7.1.1:
+ resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==}
+
+ node-exports-info@1.6.0:
+ resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==}
+ engines: {node: '>= 0.4'}
+
+ node-releases@2.0.47:
+ resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==}
+ engines: {node: '>=18'}
+
+ object-assign@4.1.1:
+ resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==}
+ engines: {node: '>=0.10.0'}
+
+ object-inspect@1.13.4:
+ resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==}
+ engines: {node: '>= 0.4'}
+
+ object-keys@1.1.1:
+ resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==}
+ engines: {node: '>= 0.4'}
+
+ object.assign@4.1.7:
+ resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==}
+ engines: {node: '>= 0.4'}
+
+ object.entries@1.1.9:
+ resolution: {integrity: sha512-8u/hfXFRBD1O0hPUjioLhoWFHRmt6tKA4/vZPyckBr18l1KE9uHrFaFaUi8MDRTpi4uak2goyPTSNJLXX2k2Hw==}
+ engines: {node: '>= 0.4'}
+
+ object.fromentries@2.0.8:
+ resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==}
+ engines: {node: '>= 0.4'}
+
+ object.groupby@1.0.3:
+ resolution: {integrity: sha512-+Lhy3TQTuzXI5hevh8sBGqbmurHbbIjAi0Z4S63nthVLmLxfbj4T54a4CfZrXIrt9iP4mVAPYMo/v99taj3wjQ==}
+ engines: {node: '>= 0.4'}
+
+ object.values@1.2.1:
+ resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==}
+ engines: {node: '>= 0.4'}
+
+ onetime@7.0.0:
+ resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==}
+ engines: {node: '>=18'}
+
+ optionator@0.9.4:
+ resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==}
+ engines: {node: '>= 0.8.0'}
+
+ own-keys@1.0.1:
+ resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==}
+ engines: {node: '>= 0.4'}
+
+ oxc-parser@0.133.0:
+ resolution: {integrity: sha512-661RSx+ZcjBmjBYid+Fpp/2F5EbtildpeoZh5HdgnGs+jZ03nqQEQW8yGkt4BGyOC3OMPDQQRl8M5kqD2/g6jw==}
+ engines: {node: ^20.19.0 || >=22.12.0}
+
+ oxc-resolver@11.20.0:
+ resolution: {integrity: sha512-CblytBiV/a/ZXY34dsVU2NxhIOxMXst8CvDCtyBelVITgd7PLrKzbEbA6oKLdPjvDKDzCiW48qzmzZ+mYaqn+g==}
+
+ p-limit@3.1.0:
+ resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==}
+ engines: {node: '>=10'}
+
+ p-locate@5.0.0:
+ resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==}
+ engines: {node: '>=10'}
+
+ parent-module@1.0.1:
+ resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==}
+ engines: {node: '>=6'}
+
+ path-exists@4.0.0:
+ resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==}
+ engines: {node: '>=8'}
+
+ path-key@3.1.1:
+ resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
+ engines: {node: '>=8'}
+
+ path-parse@1.0.7:
+ resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==}
+
+ picocolors@1.1.1:
+ resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==}
+
+ picomatch@2.3.2:
+ resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==}
+ engines: {node: '>=8.6'}
+
+ picomatch@4.0.4:
+ resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==}
+ engines: {node: '>=12'}
+
+ po-parser@2.1.1:
+ resolution: {integrity: sha512-ECF4zHLbUItpUgE3OTtLKlPjeBN+fKEczj2zYjDfCGOzicNs0GK3Vg2IoAYwx7LH/XYw43fZQP6xnZ4TkNxSLQ==}
+
+ possible-typed-array-names@1.1.0:
+ resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==}
+ engines: {node: '>= 0.4'}
+
+ postcss@8.4.31:
+ resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ postcss@8.5.15:
+ resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==}
+ engines: {node: ^10 || ^12 || >=14}
+
+ prelude-ls@1.2.1:
+ resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==}
+ engines: {node: '>= 0.8.0'}
+
+ prettier@3.8.4:
+ resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==}
+ engines: {node: '>=14'}
+ hasBin: true
+
+ prop-types@15.8.1:
+ resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==}
+
+ punycode@2.3.1:
+ resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==}
+ engines: {node: '>=6'}
+
+ queue-microtask@1.2.3:
+ resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
+
+ react-dom@19.2.7:
+ resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==}
+ peerDependencies:
+ react: ^19.2.7
+
+ react-is@16.13.1:
+ resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
+
+ react@19.2.7:
+ resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==}
+ engines: {node: '>=0.10.0'}
+
+ reflect.getprototypeof@1.0.10:
+ resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==}
+ engines: {node: '>= 0.4'}
+
+ regexp.prototype.flags@1.5.4:
+ resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==}
+ engines: {node: '>= 0.4'}
+
+ resolve-from@4.0.0:
+ resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==}
+ engines: {node: '>=4'}
+
+ resolve-pkg-maps@1.0.0:
+ resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==}
+
+ resolve@2.0.0-next.7:
+ resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==}
+ engines: {node: '>= 0.4'}
+ hasBin: true
+
+ restore-cursor@5.1.0:
+ resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==}
+ engines: {node: '>=18'}
+
+ reusify@1.1.0:
+ resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==}
+ engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
+
+ rfdc@1.4.1:
+ resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==}
+
+ run-parallel@1.2.0:
+ resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
+
+ safe-array-concat@1.1.4:
+ resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==}
+ engines: {node: '>=0.4'}
+
+ safe-push-apply@1.0.0:
+ resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==}
+ engines: {node: '>= 0.4'}
+
+ safe-regex-test@1.1.0:
+ resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==}
+ engines: {node: '>= 0.4'}
+
+ scheduler@0.27.0:
+ resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==}
+
+ semver@6.3.1:
+ resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==}
+ hasBin: true
+
+ semver@7.8.4:
+ resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==}
+ engines: {node: '>=10'}
+ hasBin: true
+
+ set-function-length@1.2.2:
+ resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==}
+ engines: {node: '>= 0.4'}
+
+ set-function-name@2.0.2:
+ resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==}
+ engines: {node: '>= 0.4'}
+
+ set-proto@1.0.0:
+ resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==}
+ engines: {node: '>= 0.4'}
+
+ sharp@0.34.5:
+ resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==}
+ engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0}
+
+ shebang-command@2.0.0:
+ resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
+ engines: {node: '>=8'}
+
+ shebang-regex@3.0.0:
+ resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
+ engines: {node: '>=8'}
+
+ side-channel-list@1.0.1:
+ resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-map@1.0.1:
+ resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==}
+ engines: {node: '>= 0.4'}
+
+ side-channel-weakmap@1.0.2:
+ resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==}
+ engines: {node: '>= 0.4'}
+
+ side-channel@1.1.1:
+ resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==}
+ engines: {node: '>= 0.4'}
+
+ signal-exit@4.1.0:
+ resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==}
+ engines: {node: '>=14'}
+
+ slice-ansi@7.1.2:
+ resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==}
+ engines: {node: '>=18'}
+
+ slice-ansi@8.0.0:
+ resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==}
+ engines: {node: '>=20'}
+
+ smol-toml@1.6.1:
+ resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==}
+ engines: {node: '>= 18'}
+
+ source-map-js@1.2.1:
+ resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==}
+ engines: {node: '>=0.10.0'}
+
+ stable-hash@0.0.5:
+ resolution: {integrity: sha512-+L3ccpzibovGXFK+Ap/f8LOS0ahMrHTf3xu7mMLSpEGU0EO9ucaysSylKo9eRDFNhWve/y275iPmIZ4z39a9iA==}
+
+ stop-iteration-iterator@1.1.0:
+ resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==}
+ engines: {node: '>= 0.4'}
+
+ string-argv@0.3.2:
+ resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==}
+ engines: {node: '>=0.6.19'}
+
+ string-width@7.2.0:
+ resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==}
+ engines: {node: '>=18'}
+
+ string-width@8.2.1:
+ resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==}
+ engines: {node: '>=20'}
+
+ string.prototype.includes@2.0.1:
+ resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.matchall@4.0.12:
+ resolution: {integrity: sha512-6CC9uyBL+/48dYizRf7H7VAYCMCNTBeM78x/VTUe9bFEaxBepPJDa1Ow99LqI/1yF7kuy7Q3cQsYMrcjGUcskA==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.repeat@1.0.0:
+ resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==}
+
+ string.prototype.trim@1.2.11:
+ resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimend@1.0.10:
+ resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==}
+ engines: {node: '>= 0.4'}
+
+ string.prototype.trimstart@1.0.8:
+ resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==}
+ engines: {node: '>= 0.4'}
+
+ strip-ansi@7.2.0:
+ resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==}
+ engines: {node: '>=12'}
+
+ strip-bom@3.0.0:
+ resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==}
+ engines: {node: '>=4'}
+
+ strip-json-comments@3.1.1:
+ resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==}
+ engines: {node: '>=8'}
+
+ strip-json-comments@5.0.3:
+ resolution: {integrity: sha512-1tB5mhVo7U+ETBKNf92xT4hrQa3pm0MZ0PQvuDnWgAAGHDsfp4lPSpiS6psrSiet87wyGPh9ft6wmhOMQ0hDiw==}
+ engines: {node: '>=14.16'}
+
+ styled-jsx@5.1.6:
+ resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==}
+ engines: {node: '>= 12.0.0'}
+ peerDependencies:
+ '@babel/core': '*'
+ babel-plugin-macros: '*'
+ 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
+
+ supports-color@7.2.0:
+ resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==}
+ engines: {node: '>=8'}
+
+ supports-preserve-symlinks-flag@1.0.0:
+ resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==}
+ engines: {node: '>= 0.4'}
+
+ tailwindcss@4.3.0:
+ resolution: {integrity: sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==}
+
+ tapable@2.3.3:
+ resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
+ engines: {node: '>=6'}
+
+ tinyexec@1.2.4:
+ resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==}
+ engines: {node: '>=18'}
+
+ tinyglobby@0.2.17:
+ resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==}
+ engines: {node: '>=12.0.0'}
+
+ to-regex-range@5.0.1:
+ resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==}
+ engines: {node: '>=8.0'}
+
+ ts-api-utils@2.5.0:
+ resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==}
+ engines: {node: '>=18.12'}
+ peerDependencies:
+ typescript: '>=4.8.4'
+
+ tsconfig-paths@3.15.0:
+ resolution: {integrity: sha512-2Ac2RgzDe/cn48GvOe3M+o82pEFewD3UPbyoUHHdKasHwJKjds4fLXWf/Ux5kATBKN20oaFGu+jbElp1pos0mg==}
+
+ tslib@2.8.1:
+ resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==}
+
+ type-check@0.4.0:
+ resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
+ engines: {node: '>= 0.8.0'}
+
+ typed-array-buffer@1.0.3:
+ resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-length@1.0.3:
+ resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-byte-offset@1.0.4:
+ resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==}
+ engines: {node: '>= 0.4'}
+
+ typed-array-length@1.0.8:
+ resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==}
+ engines: {node: '>= 0.4'}
+
+ typescript-eslint@8.61.0:
+ resolution: {integrity: sha512-8y31Rd0eGTrDKqhy6vT0HtzhN+YLjQizwX3aA3hPXP/ynSfnrBXcQY5IzsP9/DM7+klX4IUncZZjkchP0z+rUw==}
+ engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
+ peerDependencies:
+ eslint: ^8.57.0 || ^9.0.0 || ^10.0.0
+ typescript: '>=4.8.4 <6.1.0'
+
+ typescript@6.0.3:
+ resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==}
+ engines: {node: '>=14.17'}
+ hasBin: true
+
+ unbash@3.0.0:
+ resolution: {integrity: sha512-FeFPZ/WFT0mbRCuydiZzpPFlrYN8ZUpphQKoq4EeElVIYjYyGzPMxQR/simUwCOJIyVhpFk4RbtyO7RuMpMnHA==}
+ engines: {node: '>=14'}
+
+ unbox-primitive@1.1.0:
+ resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==}
+ engines: {node: '>= 0.4'}
+
+ undici-types@6.21.0:
+ resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
+
+ unrs-resolver@1.12.2:
+ resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==}
+
+ update-browserslist-db@1.2.3:
+ resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==}
+ hasBin: true
+ peerDependencies:
+ browserslist: '>= 4.21.0'
+
+ uri-js@4.4.1:
+ resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==}
+
+ use-intl@4.13.0:
+ resolution: {integrity: sha512-fAFDrWaASxlhXOipcOyb5VDD+YONqj6+8O8EcG/J7RBoOUF3A8YahRWLN+mBxYMrlMQB8N6Voqk5X+YC+HSL0A==}
+ peerDependencies:
+ react: ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0
+
+ walk-up-path@4.0.0:
+ resolution: {integrity: sha512-3hu+tD8YzSLGuFYtPRb48vdhKMi0KQV5sn+uWr8+7dMEq/2G/dtLrdDinkLjqq5TIbIBjYJ4Ax/n3YiaW7QM8A==}
+ engines: {node: 20 || >=22}
+
+ which-boxed-primitive@1.1.1:
+ resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==}
+ engines: {node: '>= 0.4'}
+
+ which-builtin-type@1.2.1:
+ resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==}
+ engines: {node: '>= 0.4'}
+
+ which-collection@1.0.2:
+ resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==}
+ engines: {node: '>= 0.4'}
+
+ which-typed-array@1.1.22:
+ resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==}
+ engines: {node: '>= 0.4'}
+
+ which@2.0.2:
+ resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
+ engines: {node: '>= 8'}
+ hasBin: true
+
+ word-wrap@1.2.5:
+ resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
+ engines: {node: '>=0.10.0'}
+
+ wrap-ansi@10.0.0:
+ resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==}
+ engines: {node: '>=20'}
+
+ wrap-ansi@9.0.2:
+ resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==}
+ engines: {node: '>=18'}
+
+ yallist@3.1.1:
+ resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
+
+ yaml@2.9.0:
+ resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==}
+ engines: {node: '>= 14.6'}
+ hasBin: true
+
+ yocto-queue@0.1.0:
+ resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==}
+ engines: {node: '>=10'}
+
+ zod-validation-error@4.0.2:
+ resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==}
+ engines: {node: '>=18.0.0'}
+ peerDependencies:
+ zod: ^3.25.0 || ^4.0.0
+
+ zod@4.4.3:
+ resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==}
+
+snapshots:
+
+ '@alloc/quick-lru@5.2.0': {}
+
+ '@babel/code-frame@7.29.7':
+ dependencies:
+ '@babel/helper-validator-identifier': 7.29.7
+ js-tokens: 4.0.0
+ picocolors: 1.1.1
+
+ '@babel/compat-data@7.29.7': {}
+
+ '@babel/core@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-compilation-targets': 7.29.7
+ '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7)
+ '@babel/helpers': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/remapping': 2.3.5
+ convert-source-map: 2.0.0
+ debug: 4.4.3
+ gensync: 1.0.0-beta.2
+ json5: 2.2.3
+ semver: 6.3.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/generator@7.29.7':
+ dependencies:
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+ jsesc: 3.1.0
+
+ '@babel/helper-compilation-targets@7.29.7':
+ dependencies:
+ '@babel/compat-data': 7.29.7
+ '@babel/helper-validator-option': 7.29.7
+ browserslist: 4.28.2
+ lru-cache: 5.1.1
+ semver: 6.3.1
+
+ '@babel/helper-globals@7.29.7': {}
+
+ '@babel/helper-module-imports@7.29.7':
+ dependencies:
+ '@babel/traverse': 7.29.7
+ '@babel/types': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)':
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/helper-module-imports': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+ '@babel/traverse': 7.29.7
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/helper-string-parser@7.29.7': {}
+
+ '@babel/helper-validator-identifier@7.29.7': {}
+
+ '@babel/helper-validator-option@7.29.7': {}
+
+ '@babel/helpers@7.29.7':
+ dependencies:
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/parser@7.29.7':
+ dependencies:
+ '@babel/types': 7.29.7
+
+ '@babel/template@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/types': 7.29.7
+
+ '@babel/traverse@7.29.7':
+ dependencies:
+ '@babel/code-frame': 7.29.7
+ '@babel/generator': 7.29.7
+ '@babel/helper-globals': 7.29.7
+ '@babel/parser': 7.29.7
+ '@babel/template': 7.29.7
+ '@babel/types': 7.29.7
+ debug: 4.4.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@babel/types@7.29.7':
+ dependencies:
+ '@babel/helper-string-parser': 7.29.7
+ '@babel/helper-validator-identifier': 7.29.7
+
+ '@emnapi/core@1.10.0':
+ dependencies:
+ '@emnapi/wasi-threads': 1.2.1
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.10.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/runtime@1.11.0':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@emnapi/wasi-threads@1.2.1':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@eslint-community/eslint-utils@4.9.1(eslint@9.39.4(jiti@2.7.0))':
+ dependencies:
+ eslint: 9.39.4(jiti@2.7.0)
+ eslint-visitor-keys: 3.4.3
+
+ '@eslint-community/regexpp@4.12.2': {}
+
+ '@eslint/config-array@0.21.2':
+ dependencies:
+ '@eslint/object-schema': 2.1.7
+ debug: 4.4.3
+ minimatch: 3.1.5
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/config-helpers@0.4.2':
+ dependencies:
+ '@eslint/core': 0.17.0
+
+ '@eslint/core@0.17.0':
+ dependencies:
+ '@types/json-schema': 7.0.15
+
+ '@eslint/eslintrc@3.3.5':
+ dependencies:
+ ajv: 6.15.0
+ debug: 4.4.3
+ espree: 10.4.0
+ globals: 14.0.0
+ ignore: 5.3.2
+ import-fresh: 3.3.1
+ js-yaml: 4.2.0
+ minimatch: 3.1.5
+ strip-json-comments: 3.1.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@eslint/js@9.39.4': {}
+
+ '@eslint/object-schema@2.1.7': {}
+
+ '@eslint/plugin-kit@0.4.1':
+ dependencies:
+ '@eslint/core': 0.17.0
+ levn: 0.4.1
+
+ '@formatjs/fast-memoize@3.1.6': {}
+
+ '@formatjs/icu-messageformat-parser@3.5.11':
+ dependencies:
+ '@formatjs/icu-skeleton-parser': 2.1.10
+
+ '@formatjs/icu-skeleton-parser@2.1.10': {}
+
+ '@formatjs/intl-localematcher@0.8.10':
+ dependencies:
+ '@formatjs/fast-memoize': 3.1.6
+
+ '@humanfs/core@0.19.2':
+ dependencies:
+ '@humanfs/types': 0.15.0
+
+ '@humanfs/node@0.16.8':
+ dependencies:
+ '@humanfs/core': 0.19.2
+ '@humanfs/types': 0.15.0
+ '@humanwhocodes/retry': 0.4.3
+
+ '@humanfs/types@0.15.0': {}
+
+ '@humanwhocodes/module-importer@1.0.1': {}
+
+ '@humanwhocodes/retry@0.4.3': {}
+
+ '@img/colour@1.1.0':
+ optional: true
+
+ '@img/sharp-darwin-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-darwin-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-libvips-darwin-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-darwin-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-arm@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-ppc64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-riscv64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-s390x@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linux-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-arm64@1.2.4':
+ optional: true
+
+ '@img/sharp-libvips-linuxmusl-x64@1.2.4':
+ optional: true
+
+ '@img/sharp-linux-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-arm@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-ppc64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-riscv64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-s390x@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ optional: true
+
+ '@img/sharp-linux-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-arm64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ optional: true
+
+ '@img/sharp-linuxmusl-x64@0.34.5':
+ optionalDependencies:
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ optional: true
+
+ '@img/sharp-wasm32@0.34.5':
+ dependencies:
+ '@emnapi/runtime': 1.11.0
+ optional: true
+
+ '@img/sharp-win32-arm64@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-ia32@0.34.5':
+ optional: true
+
+ '@img/sharp-win32-x64@0.34.5':
+ optional: true
+
+ '@jridgewell/gen-mapping@0.3.13':
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/remapping@2.3.5':
+ dependencies:
+ '@jridgewell/gen-mapping': 0.3.13
+ '@jridgewell/trace-mapping': 0.3.31
+
+ '@jridgewell/resolve-uri@3.1.2': {}
+
+ '@jridgewell/sourcemap-codec@1.5.5': {}
+
+ '@jridgewell/trace-mapping@0.3.31':
+ dependencies:
+ '@jridgewell/resolve-uri': 3.1.2
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@tybys/wasm-util': 0.10.2
+ optional: true
+
+ '@next/env@16.2.9': {}
+
+ '@next/eslint-plugin-next@16.2.9':
+ dependencies:
+ fast-glob: 3.3.1
+
+ '@next/swc-darwin-arm64@16.2.9':
+ optional: true
+
+ '@next/swc-darwin-x64@16.2.9':
+ optional: true
+
+ '@next/swc-linux-arm64-gnu@16.2.9':
+ optional: true
+
+ '@next/swc-linux-arm64-musl@16.2.9':
+ optional: true
+
+ '@next/swc-linux-x64-gnu@16.2.9':
+ optional: true
+
+ '@next/swc-linux-x64-musl@16.2.9':
+ optional: true
+
+ '@next/swc-win32-arm64-msvc@16.2.9':
+ optional: true
+
+ '@next/swc-win32-x64-msvc@16.2.9':
+ optional: true
+
+ '@nodelib/fs.scandir@2.1.5':
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ run-parallel: 1.2.0
+
+ '@nodelib/fs.stat@2.0.5': {}
+
+ '@nodelib/fs.walk@1.2.8':
+ dependencies:
+ '@nodelib/fs.scandir': 2.1.5
+ fastq: 1.20.1
+
+ '@nolyfill/is-core-module@1.0.39': {}
+
+ '@oxc-parser/binding-android-arm-eabi@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-android-arm64@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-darwin-arm64@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-darwin-x64@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-freebsd-x64@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm-gnueabihf@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm-musleabihf@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm64-gnu@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-arm64-musl@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-ppc64-gnu@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-riscv64-gnu@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-riscv64-musl@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-s390x-gnu@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-x64-gnu@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-linux-x64-musl@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-openharmony-arm64@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-wasm32-wasi@0.133.0':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ optional: true
+
+ '@oxc-parser/binding-win32-arm64-msvc@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-win32-ia32-msvc@0.133.0':
+ optional: true
+
+ '@oxc-parser/binding-win32-x64-msvc@0.133.0':
+ optional: true
+
+ '@oxc-project/types@0.133.0': {}
+
+ '@oxc-resolver/binding-android-arm-eabi@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-android-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-darwin-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-darwin-x64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-freebsd-x64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm-gnueabihf@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm-musleabihf@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-arm64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-ppc64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-riscv64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-riscv64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-s390x-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-x64-gnu@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-linux-x64-musl@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-openharmony-arm64@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-wasm32-wasi@11.20.0':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ optional: true
+
+ '@oxc-resolver/binding-win32-arm64-msvc@11.20.0':
+ optional: true
+
+ '@oxc-resolver/binding-win32-x64-msvc@11.20.0':
+ optional: true
+
+ '@parcel/watcher-android-arm64@2.5.6':
+ optional: true
+
+ '@parcel/watcher-darwin-arm64@2.5.6':
+ optional: true
+
+ '@parcel/watcher-darwin-x64@2.5.6':
+ optional: true
+
+ '@parcel/watcher-freebsd-x64@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-arm-glibc@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-arm-musl@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-glibc@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-arm64-musl@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-x64-glibc@2.5.6':
+ optional: true
+
+ '@parcel/watcher-linux-x64-musl@2.5.6':
+ optional: true
+
+ '@parcel/watcher-win32-arm64@2.5.6':
+ optional: true
+
+ '@parcel/watcher-win32-ia32@2.5.6':
+ optional: true
+
+ '@parcel/watcher-win32-x64@2.5.6':
+ optional: true
+
+ '@parcel/watcher@2.5.6':
+ dependencies:
+ detect-libc: 2.1.2
+ is-glob: 4.0.3
+ node-addon-api: 7.1.1
+ picomatch: 4.0.4
+ optionalDependencies:
+ '@parcel/watcher-android-arm64': 2.5.6
+ '@parcel/watcher-darwin-arm64': 2.5.6
+ '@parcel/watcher-darwin-x64': 2.5.6
+ '@parcel/watcher-freebsd-x64': 2.5.6
+ '@parcel/watcher-linux-arm-glibc': 2.5.6
+ '@parcel/watcher-linux-arm-musl': 2.5.6
+ '@parcel/watcher-linux-arm64-glibc': 2.5.6
+ '@parcel/watcher-linux-arm64-musl': 2.5.6
+ '@parcel/watcher-linux-x64-glibc': 2.5.6
+ '@parcel/watcher-linux-x64-musl': 2.5.6
+ '@parcel/watcher-win32-arm64': 2.5.6
+ '@parcel/watcher-win32-ia32': 2.5.6
+ '@parcel/watcher-win32-x64': 2.5.6
+
+ '@rtsao/scc@1.1.0': {}
+
+ '@schummar/icu-type-parser@1.21.5': {}
+
+ '@swc/core-darwin-arm64@1.15.41':
+ optional: true
+
+ '@swc/core-darwin-x64@1.15.41':
+ optional: true
+
+ '@swc/core-linux-arm-gnueabihf@1.15.41':
+ optional: true
+
+ '@swc/core-linux-arm64-gnu@1.15.41':
+ optional: true
+
+ '@swc/core-linux-arm64-musl@1.15.41':
+ optional: true
+
+ '@swc/core-linux-ppc64-gnu@1.15.41':
+ optional: true
+
+ '@swc/core-linux-s390x-gnu@1.15.41':
+ optional: true
+
+ '@swc/core-linux-x64-gnu@1.15.41':
+ optional: true
+
+ '@swc/core-linux-x64-musl@1.15.41':
+ optional: true
+
+ '@swc/core-win32-arm64-msvc@1.15.41':
+ optional: true
+
+ '@swc/core-win32-ia32-msvc@1.15.41':
+ optional: true
+
+ '@swc/core-win32-x64-msvc@1.15.41':
+ optional: true
+
+ '@swc/core@1.15.41':
+ dependencies:
+ '@swc/counter': 0.1.3
+ '@swc/types': 0.1.26
+ optionalDependencies:
+ '@swc/core-darwin-arm64': 1.15.41
+ '@swc/core-darwin-x64': 1.15.41
+ '@swc/core-linux-arm-gnueabihf': 1.15.41
+ '@swc/core-linux-arm64-gnu': 1.15.41
+ '@swc/core-linux-arm64-musl': 1.15.41
+ '@swc/core-linux-ppc64-gnu': 1.15.41
+ '@swc/core-linux-s390x-gnu': 1.15.41
+ '@swc/core-linux-x64-gnu': 1.15.41
+ '@swc/core-linux-x64-musl': 1.15.41
+ '@swc/core-win32-arm64-msvc': 1.15.41
+ '@swc/core-win32-ia32-msvc': 1.15.41
+ '@swc/core-win32-x64-msvc': 1.15.41
+
+ '@swc/counter@0.1.3': {}
+
+ '@swc/helpers@0.5.15':
+ dependencies:
+ tslib: 2.8.1
+
+ '@swc/types@0.1.26':
+ dependencies:
+ '@swc/counter': 0.1.3
+
+ '@tailwindcss/node@4.3.0':
+ dependencies:
+ '@jridgewell/remapping': 2.3.5
+ enhanced-resolve: 5.24.0
+ jiti: 2.7.0
+ lightningcss: 1.32.0
+ magic-string: 0.30.21
+ source-map-js: 1.2.1
+ tailwindcss: 4.3.0
+
+ '@tailwindcss/oxide-android-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-arm64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-darwin-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-freebsd-x64@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm-gnueabihf@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-arm64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-gnu@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-linux-x64-musl@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-wasm32-wasi@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-arm64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide-win32-x64-msvc@4.3.0':
+ optional: true
+
+ '@tailwindcss/oxide@4.3.0':
+ optionalDependencies:
+ '@tailwindcss/oxide-android-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-arm64': 4.3.0
+ '@tailwindcss/oxide-darwin-x64': 4.3.0
+ '@tailwindcss/oxide-freebsd-x64': 4.3.0
+ '@tailwindcss/oxide-linux-arm-gnueabihf': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-arm64-musl': 4.3.0
+ '@tailwindcss/oxide-linux-x64-gnu': 4.3.0
+ '@tailwindcss/oxide-linux-x64-musl': 4.3.0
+ '@tailwindcss/oxide-wasm32-wasi': 4.3.0
+ '@tailwindcss/oxide-win32-arm64-msvc': 4.3.0
+ '@tailwindcss/oxide-win32-x64-msvc': 4.3.0
+
+ '@tailwindcss/postcss@4.3.0':
+ dependencies:
+ '@alloc/quick-lru': 5.2.0
+ '@tailwindcss/node': 4.3.0
+ '@tailwindcss/oxide': 4.3.0
+ postcss: 8.5.15
+ tailwindcss: 4.3.0
+
+ '@tybys/wasm-util@0.10.2':
+ dependencies:
+ tslib: 2.8.1
+ optional: true
+
+ '@types/estree@1.0.9': {}
+
+ '@types/json-schema@7.0.15': {}
+
+ '@types/json5@0.0.29': {}
+
+ '@types/node@22.19.21':
+ dependencies:
+ undici-types: 6.21.0
+
+ '@types/react-dom@19.2.3(@types/react@19.2.17)':
+ dependencies:
+ '@types/react': 19.2.17
+
+ '@types/react@19.2.17':
+ dependencies:
+ csstype: 3.2.3
+
+ '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@eslint-community/regexpp': 4.12.2
+ '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/type-utils': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.61.0
+ eslint: 9.39.4(jiti@2.7.0)
+ ignore: 7.0.5
+ natural-compare: 1.4.0
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/visitor-keys': 8.61.0
+ debug: 4.4.3
+ eslint: 9.39.4(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/project-service@8.61.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.61.0
+ debug: 4.4.3
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/scope-manager@8.61.0':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/visitor-keys': 8.61.0
+
+ '@typescript-eslint/tsconfig-utils@8.61.0(typescript@6.0.3)':
+ dependencies:
+ typescript: 6.0.3
+
+ '@typescript-eslint/type-utils@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ debug: 4.4.3
+ eslint: 9.39.4(jiti@2.7.0)
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/types@8.61.0': {}
+
+ '@typescript-eslint/typescript-estree@8.61.0(typescript@6.0.3)':
+ dependencies:
+ '@typescript-eslint/project-service': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/visitor-keys': 8.61.0
+ debug: 4.4.3
+ minimatch: 10.2.5
+ semver: 7.8.4
+ tinyglobby: 0.2.17
+ ts-api-utils: 2.5.0(typescript@6.0.3)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/utils@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)':
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ '@typescript-eslint/scope-manager': 8.61.0
+ '@typescript-eslint/types': 8.61.0
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ eslint: 9.39.4(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@typescript-eslint/visitor-keys@8.61.0':
+ dependencies:
+ '@typescript-eslint/types': 8.61.0
+ eslint-visitor-keys: 5.0.1
+
+ '@unrs/resolver-binding-android-arm-eabi@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-android-arm64@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-arm64@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-darwin-x64@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-freebsd-x64@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-arm64-musl@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-loong64-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-loong64-musl@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-riscv64-musl@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-s390x-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-gnu@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-linux-x64-musl@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-openharmony-arm64@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-wasm32-wasi@1.12.2':
+ dependencies:
+ '@emnapi/core': 1.10.0
+ '@emnapi/runtime': 1.10.0
+ '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)
+ optional: true
+
+ '@unrs/resolver-binding-win32-arm64-msvc@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-win32-ia32-msvc@1.12.2':
+ optional: true
+
+ '@unrs/resolver-binding-win32-x64-msvc@1.12.2':
+ optional: true
+
+ acorn-jsx@5.3.2(acorn@8.17.0):
+ dependencies:
+ acorn: 8.17.0
+
+ acorn@8.17.0: {}
+
+ ajv@6.15.0:
+ dependencies:
+ fast-deep-equal: 3.1.3
+ fast-json-stable-stringify: 2.1.0
+ json-schema-traverse: 0.4.1
+ uri-js: 4.4.1
+
+ ansi-escapes@7.3.0:
+ dependencies:
+ environment: 1.1.0
+
+ ansi-regex@6.2.2: {}
+
+ ansi-styles@4.3.0:
+ dependencies:
+ color-convert: 2.0.1
+
+ ansi-styles@6.2.3: {}
+
+ argparse@2.0.1: {}
+
+ aria-query@5.3.2: {}
+
+ array-buffer-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ is-array-buffer: 3.0.5
+
+ array-includes@3.1.9:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-object-atoms: 1.1.2
+ get-intrinsic: 1.3.0
+ is-string: 1.1.1
+ math-intrinsics: 1.1.0
+
+ array.prototype.findlast@1.2.5:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.findlastindex@1.2.6:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flat@1.3.3:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.flatmap@1.3.3:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-shim-unscopables: 1.1.0
+
+ array.prototype.tosorted@1.1.4:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-shim-unscopables: 1.1.0
+
+ arraybuffer.prototype.slice@1.0.4:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ is-array-buffer: 3.0.5
+
+ ast-types-flow@0.0.8: {}
+
+ async-function@1.0.0: {}
+
+ available-typed-arrays@1.0.7:
+ dependencies:
+ possible-typed-array-names: 1.1.0
+
+ axe-core@4.12.1: {}
+
+ axobject-query@4.1.0: {}
+
+ balanced-match@1.0.2: {}
+
+ balanced-match@4.0.4: {}
+
+ baseline-browser-mapping@2.10.36: {}
+
+ brace-expansion@1.1.15:
+ dependencies:
+ balanced-match: 1.0.2
+ concat-map: 0.0.1
+
+ brace-expansion@5.0.6:
+ dependencies:
+ balanced-match: 4.0.4
+
+ braces@3.0.3:
+ dependencies:
+ fill-range: 7.1.1
+
+ browserslist@4.28.2:
+ dependencies:
+ baseline-browser-mapping: 2.10.36
+ caniuse-lite: 1.0.30001799
+ electron-to-chromium: 1.5.372
+ node-releases: 2.0.47
+ update-browserslist-db: 1.2.3(browserslist@4.28.2)
+
+ call-bind-apply-helpers@1.0.2:
+ dependencies:
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+
+ call-bind@1.0.9:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ get-intrinsic: 1.3.0
+ set-function-length: 1.2.2
+
+ call-bound@1.0.4:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ get-intrinsic: 1.3.0
+
+ callsites@3.1.0: {}
+
+ caniuse-lite@1.0.30001799: {}
+
+ chalk@4.1.2:
+ dependencies:
+ ansi-styles: 4.3.0
+ supports-color: 7.2.0
+
+ cli-cursor@5.0.0:
+ dependencies:
+ restore-cursor: 5.1.0
+
+ cli-truncate@5.2.0:
+ dependencies:
+ slice-ansi: 8.0.0
+ string-width: 8.2.1
+
+ client-only@0.0.1: {}
+
+ color-convert@2.0.1:
+ dependencies:
+ color-name: 1.1.4
+
+ color-name@1.1.4: {}
+
+ concat-map@0.0.1: {}
+
+ convert-source-map@2.0.0: {}
+
+ cross-spawn@7.0.6:
+ dependencies:
+ path-key: 3.1.1
+ shebang-command: 2.0.0
+ which: 2.0.2
+
+ csstype@3.2.3: {}
+
+ damerau-levenshtein@1.0.8: {}
+
+ data-view-buffer@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-length@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ data-view-byte-offset@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-data-view: 1.0.2
+
+ debug@3.2.7:
+ dependencies:
+ ms: 2.1.3
+
+ debug@4.4.3:
+ dependencies:
+ ms: 2.1.3
+
+ deep-is@0.1.4: {}
+
+ define-data-property@1.1.4:
+ dependencies:
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ define-properties@1.2.1:
+ dependencies:
+ define-data-property: 1.1.4
+ has-property-descriptors: 1.0.2
+ object-keys: 1.1.1
+
+ detect-libc@2.1.2: {}
+
+ doctrine@2.1.0:
+ dependencies:
+ esutils: 2.0.3
+
+ dunder-proto@1.0.1:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-errors: 1.3.0
+ gopd: 1.2.0
+
+ electron-to-chromium@1.5.372: {}
+
+ emoji-regex@10.6.0: {}
+
+ emoji-regex@9.2.2: {}
+
+ enhanced-resolve@5.24.0:
+ dependencies:
+ graceful-fs: 4.2.11
+ tapable: 2.3.3
+
+ environment@1.1.0: {}
+
+ es-abstract@1.24.2:
+ dependencies:
+ array-buffer-byte-length: 1.0.2
+ arraybuffer.prototype.slice: 1.0.4
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ data-view-buffer: 1.0.2
+ data-view-byte-length: 1.0.2
+ data-view-byte-offset: 1.0.1
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ es-set-tostringtag: 2.1.0
+ es-to-primitive: 1.3.0
+ function.prototype.name: 1.2.0
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ get-symbol-description: 1.1.0
+ globalthis: 1.0.4
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+ has-proto: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.4
+ internal-slot: 1.1.0
+ is-array-buffer: 3.0.5
+ is-callable: 1.2.7
+ is-data-view: 1.0.2
+ is-negative-zero: 2.0.3
+ is-regex: 1.2.1
+ is-set: 2.0.3
+ is-shared-array-buffer: 1.0.4
+ is-string: 1.1.1
+ is-typed-array: 1.1.15
+ is-weakref: 1.1.1
+ math-intrinsics: 1.1.0
+ object-inspect: 1.13.4
+ object-keys: 1.1.1
+ object.assign: 4.1.7
+ own-keys: 1.0.1
+ regexp.prototype.flags: 1.5.4
+ safe-array-concat: 1.1.4
+ safe-push-apply: 1.0.0
+ safe-regex-test: 1.1.0
+ set-proto: 1.0.0
+ stop-iteration-iterator: 1.1.0
+ string.prototype.trim: 1.2.11
+ string.prototype.trimend: 1.0.10
+ 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.8
+ unbox-primitive: 1.1.0
+ which-typed-array: 1.1.22
+
+ es-define-property@1.0.1: {}
+
+ es-errors@1.3.0: {}
+
+ es-iterator-helpers@1.3.3:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-set-tostringtag: 2.1.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.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
+ internal-slot: 1.1.0
+ iterator.prototype: 1.1.5
+ math-intrinsics: 1.1.0
+
+ es-object-atoms@1.1.2:
+ dependencies:
+ es-errors: 1.3.0
+
+ es-set-tostringtag@2.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.4
+
+ es-shim-unscopables@1.1.0:
+ dependencies:
+ hasown: 2.0.4
+
+ es-to-primitive@1.3.0:
+ dependencies:
+ is-callable: 1.2.7
+ is-date-object: 1.1.0
+ is-symbol: 1.1.1
+
+ escalade@3.2.0: {}
+
+ escape-string-regexp@4.0.0: {}
+
+ eslint-config-next@16.2.9(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3):
+ dependencies:
+ '@next/eslint-plugin-next': 16.2.9
+ eslint: 9.39.4(jiti@2.7.0)
+ eslint-import-resolver-node: 0.3.10
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0))
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0))
+ eslint-plugin-jsx-a11y: 6.10.2(eslint@9.39.4(jiti@2.7.0))
+ eslint-plugin-react: 7.37.5(eslint@9.39.4(jiti@2.7.0))
+ eslint-plugin-react-hooks: 7.1.1(eslint@9.39.4(jiti@2.7.0))
+ globals: 16.4.0
+ typescript-eslint: 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ optionalDependencies:
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - '@typescript-eslint/parser'
+ - eslint-import-resolver-webpack
+ - eslint-plugin-import-x
+ - supports-color
+
+ eslint-import-resolver-node@0.3.10:
+ dependencies:
+ debug: 3.2.7
+ is-core-module: 2.16.2
+ resolve: 2.0.0-next.7
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-import-resolver-typescript@3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ '@nolyfill/is-core-module': 1.0.39
+ debug: 4.4.3
+ eslint: 9.39.4(jiti@2.7.0)
+ get-tsconfig: 4.14.0
+ is-bun-module: 2.0.0
+ stable-hash: 0.0.5
+ tinyglobby: 0.2.17
+ unrs-resolver: 1.12.2
+ optionalDependencies:
+ eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ debug: 3.2.7
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ eslint: 9.39.4(jiti@2.7.0)
+ eslint-import-resolver-node: 0.3.10
+ eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@9.39.4(jiti@2.7.0))
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ '@rtsao/scc': 1.1.0
+ array-includes: 3.1.9
+ array.prototype.findlastindex: 1.2.6
+ array.prototype.flat: 1.3.3
+ array.prototype.flatmap: 1.3.3
+ debug: 3.2.7
+ doctrine: 2.1.0
+ eslint: 9.39.4(jiti@2.7.0)
+ eslint-import-resolver-node: 0.3.10
+ eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@9.39.4(jiti@2.7.0))
+ hasown: 2.0.4
+ is-core-module: 2.16.2
+ is-glob: 4.0.3
+ minimatch: 3.1.5
+ object.fromentries: 2.0.8
+ object.groupby: 1.0.3
+ object.values: 1.2.1
+ semver: 6.3.1
+ string.prototype.trimend: 1.0.10
+ tsconfig-paths: 3.15.0
+ optionalDependencies:
+ '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ transitivePeerDependencies:
+ - eslint-import-resolver-typescript
+ - eslint-import-resolver-webpack
+ - supports-color
+
+ eslint-plugin-jsx-a11y@6.10.2(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ aria-query: 5.3.2
+ array-includes: 3.1.9
+ array.prototype.flatmap: 1.3.3
+ ast-types-flow: 0.0.8
+ axe-core: 4.12.1
+ axobject-query: 4.1.0
+ damerau-levenshtein: 1.0.8
+ emoji-regex: 9.2.2
+ eslint: 9.39.4(jiti@2.7.0)
+ hasown: 2.0.4
+ jsx-ast-utils: 3.3.5
+ language-tags: 1.0.9
+ minimatch: 3.1.5
+ object.fromentries: 2.0.8
+ safe-regex-test: 1.1.0
+ string.prototype.includes: 2.0.1
+
+ eslint-plugin-react-hooks@7.1.1(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ '@babel/core': 7.29.7
+ '@babel/parser': 7.29.7
+ eslint: 9.39.4(jiti@2.7.0)
+ hermes-parser: 0.25.1
+ zod: 4.4.3
+ zod-validation-error: 4.0.2(zod@4.4.3)
+ transitivePeerDependencies:
+ - supports-color
+
+ eslint-plugin-react@7.37.5(eslint@9.39.4(jiti@2.7.0)):
+ dependencies:
+ array-includes: 3.1.9
+ 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.3.3
+ eslint: 9.39.4(jiti@2.7.0)
+ estraverse: 5.3.0
+ hasown: 2.0.4
+ jsx-ast-utils: 3.3.5
+ minimatch: 3.1.5
+ 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.7
+ semver: 6.3.1
+ string.prototype.matchall: 4.0.12
+ string.prototype.repeat: 1.0.0
+
+ eslint-scope@8.4.0:
+ dependencies:
+ esrecurse: 4.3.0
+ estraverse: 5.3.0
+
+ eslint-visitor-keys@3.4.3: {}
+
+ eslint-visitor-keys@4.2.1: {}
+
+ eslint-visitor-keys@5.0.1: {}
+
+ eslint@9.39.4(jiti@2.7.0):
+ dependencies:
+ '@eslint-community/eslint-utils': 4.9.1(eslint@9.39.4(jiti@2.7.0))
+ '@eslint-community/regexpp': 4.12.2
+ '@eslint/config-array': 0.21.2
+ '@eslint/config-helpers': 0.4.2
+ '@eslint/core': 0.17.0
+ '@eslint/eslintrc': 3.3.5
+ '@eslint/js': 9.39.4
+ '@eslint/plugin-kit': 0.4.1
+ '@humanfs/node': 0.16.8
+ '@humanwhocodes/module-importer': 1.0.1
+ '@humanwhocodes/retry': 0.4.3
+ '@types/estree': 1.0.9
+ ajv: 6.15.0
+ chalk: 4.1.2
+ cross-spawn: 7.0.6
+ debug: 4.4.3
+ escape-string-regexp: 4.0.0
+ eslint-scope: 8.4.0
+ eslint-visitor-keys: 4.2.1
+ espree: 10.4.0
+ esquery: 1.7.0
+ esutils: 2.0.3
+ fast-deep-equal: 3.1.3
+ file-entry-cache: 8.0.0
+ find-up: 5.0.0
+ glob-parent: 6.0.2
+ ignore: 5.3.2
+ imurmurhash: 0.1.4
+ is-glob: 4.0.3
+ json-stable-stringify-without-jsonify: 1.0.1
+ lodash.merge: 4.6.2
+ minimatch: 3.1.5
+ natural-compare: 1.4.0
+ optionator: 0.9.4
+ optionalDependencies:
+ jiti: 2.7.0
+ transitivePeerDependencies:
+ - supports-color
+
+ espree@10.4.0:
+ dependencies:
+ acorn: 8.17.0
+ acorn-jsx: 5.3.2(acorn@8.17.0)
+ eslint-visitor-keys: 4.2.1
+
+ esquery@1.7.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ esrecurse@4.3.0:
+ dependencies:
+ estraverse: 5.3.0
+
+ estraverse@5.3.0: {}
+
+ esutils@2.0.3: {}
+
+ eventemitter3@5.0.4: {}
+
+ fast-deep-equal@3.1.3: {}
+
+ fast-glob@3.3.1:
+ dependencies:
+ '@nodelib/fs.stat': 2.0.5
+ '@nodelib/fs.walk': 1.2.8
+ glob-parent: 5.1.2
+ merge2: 1.4.1
+ micromatch: 4.0.8
+
+ fast-json-stable-stringify@2.1.0: {}
+
+ fast-levenshtein@2.0.6: {}
+
+ fastq@1.20.1:
+ dependencies:
+ reusify: 1.1.0
+
+ fd-package-json@2.0.0:
+ dependencies:
+ walk-up-path: 4.0.0
+
+ fdir@6.5.0(picomatch@4.0.4):
+ optionalDependencies:
+ picomatch: 4.0.4
+
+ file-entry-cache@8.0.0:
+ dependencies:
+ flat-cache: 4.0.1
+
+ fill-range@7.1.1:
+ dependencies:
+ to-regex-range: 5.0.1
+
+ find-up@5.0.0:
+ dependencies:
+ locate-path: 6.0.0
+ path-exists: 4.0.0
+
+ flat-cache@4.0.1:
+ dependencies:
+ flatted: 3.4.2
+ keyv: 4.5.4
+
+ flatted@3.4.2: {}
+
+ for-each@0.3.5:
+ dependencies:
+ is-callable: 1.2.7
+
+ formatly@0.3.0:
+ dependencies:
+ fd-package-json: 2.0.0
+
+ function-bind@1.1.2: {}
+
+ function.prototype.name@1.2.0:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+ hasown: 2.0.4
+ is-callable: 1.2.7
+ is-document.all: 1.0.0
+
+ functions-have-names@1.2.3: {}
+
+ generator-function@2.0.1: {}
+
+ gensync@1.0.0-beta.2: {}
+
+ get-east-asian-width@1.6.0: {}
+
+ get-intrinsic@1.3.0:
+ dependencies:
+ call-bind-apply-helpers: 1.0.2
+ es-define-property: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ function-bind: 1.1.2
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ hasown: 2.0.4
+ math-intrinsics: 1.1.0
+
+ get-proto@1.0.1:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-object-atoms: 1.1.2
+
+ get-symbol-description@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+
+ get-tsconfig@4.14.0:
+ dependencies:
+ resolve-pkg-maps: 1.0.0
+
+ glob-parent@5.1.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ glob-parent@6.0.2:
+ dependencies:
+ is-glob: 4.0.3
+
+ globals@14.0.0: {}
+
+ globals@16.4.0: {}
+
+ globalthis@1.0.4:
+ dependencies:
+ define-properties: 1.2.1
+ gopd: 1.2.0
+
+ gopd@1.2.0: {}
+
+ graceful-fs@4.2.11: {}
+
+ has-bigints@1.1.0: {}
+
+ has-flag@4.0.0: {}
+
+ has-property-descriptors@1.0.2:
+ dependencies:
+ es-define-property: 1.0.1
+
+ has-proto@1.2.0:
+ dependencies:
+ dunder-proto: 1.0.1
+
+ has-symbols@1.1.0: {}
+
+ has-tostringtag@1.0.2:
+ dependencies:
+ has-symbols: 1.1.0
+
+ hasown@2.0.4:
+ dependencies:
+ function-bind: 1.1.2
+
+ hermes-estree@0.25.1: {}
+
+ hermes-parser@0.25.1:
+ dependencies:
+ hermes-estree: 0.25.1
+
+ husky@9.1.7: {}
+
+ icu-minify@4.13.0:
+ dependencies:
+ '@formatjs/icu-messageformat-parser': 3.5.11
+
+ ignore@5.3.2: {}
+
+ ignore@7.0.5: {}
+
+ import-fresh@3.3.1:
+ dependencies:
+ parent-module: 1.0.1
+ resolve-from: 4.0.0
+
+ imurmurhash@0.1.4: {}
+
+ internal-slot@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ hasown: 2.0.4
+ side-channel: 1.1.1
+
+ intl-messageformat@11.2.8:
+ dependencies:
+ '@formatjs/fast-memoize': 3.1.6
+ '@formatjs/icu-messageformat-parser': 3.5.11
+
+ is-array-buffer@3.0.5:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ is-async-function@2.1.1:
+ dependencies:
+ async-function: 1.0.0
+ call-bound: 1.0.4
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-bigint@1.1.0:
+ dependencies:
+ has-bigints: 1.1.0
+
+ is-boolean-object@1.2.2:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-bun-module@2.0.0:
+ dependencies:
+ semver: 7.8.4
+
+ is-callable@1.2.7: {}
+
+ is-core-module@2.16.2:
+ dependencies:
+ hasown: 2.0.4
+
+ is-data-view@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ is-typed-array: 1.1.15
+
+ is-date-object@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-document.all@1.0.0:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-extglob@2.1.1: {}
+
+ is-finalizationregistry@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-fullwidth-code-point@5.1.0:
+ dependencies:
+ get-east-asian-width: 1.6.0
+
+ is-generator-function@1.1.2:
+ dependencies:
+ call-bound: 1.0.4
+ generator-function: 2.0.1
+ get-proto: 1.0.1
+ has-tostringtag: 1.0.2
+ safe-regex-test: 1.1.0
+
+ is-glob@4.0.3:
+ dependencies:
+ is-extglob: 2.1.1
+
+ is-map@2.0.3: {}
+
+ is-negative-zero@2.0.3: {}
+
+ is-number-object@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-number@7.0.0: {}
+
+ is-regex@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+ hasown: 2.0.4
+
+ is-set@2.0.3: {}
+
+ is-shared-array-buffer@1.0.4:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-string@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-tostringtag: 1.0.2
+
+ is-symbol@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+ has-symbols: 1.1.0
+ safe-regex-test: 1.1.0
+
+ is-typed-array@1.1.15:
+ dependencies:
+ which-typed-array: 1.1.22
+
+ is-weakmap@2.0.2: {}
+
+ is-weakref@1.1.1:
+ dependencies:
+ call-bound: 1.0.4
+
+ is-weakset@2.0.4:
+ dependencies:
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+
+ isarray@2.0.5: {}
+
+ isexe@2.0.0: {}
+
+ iterator.prototype@1.1.5:
+ dependencies:
+ define-data-property: 1.1.4
+ es-object-atoms: 1.1.2
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ has-symbols: 1.1.0
+ set-function-name: 2.0.2
+
+ jiti@2.7.0: {}
+
+ js-tokens@4.0.0: {}
+
+ js-yaml@4.2.0:
+ dependencies:
+ argparse: 2.0.1
+
+ jsesc@3.1.0: {}
+
+ json-buffer@3.0.1: {}
+
+ json-schema-traverse@0.4.1: {}
+
+ json-stable-stringify-without-jsonify@1.0.1: {}
+
+ json5@1.0.2:
+ dependencies:
+ minimist: 1.2.8
+
+ json5@2.2.3: {}
+
+ jsx-ast-utils@3.3.5:
+ dependencies:
+ array-includes: 3.1.9
+ array.prototype.flat: 1.3.3
+ object.assign: 4.1.7
+ object.values: 1.2.1
+
+ keyv@4.5.4:
+ dependencies:
+ json-buffer: 3.0.1
+
+ knip@6.16.1:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ formatly: 0.3.0
+ get-tsconfig: 4.14.0
+ jiti: 2.7.0
+ oxc-parser: 0.133.0
+ oxc-resolver: 11.20.0
+ picomatch: 4.0.4
+ smol-toml: 1.6.1
+ strip-json-comments: 5.0.3
+ tinyglobby: 0.2.17
+ unbash: 3.0.0
+ yaml: 2.9.0
+ zod: 4.4.3
+
+ language-subtag-registry@0.3.23: {}
+
+ language-tags@1.0.9:
+ dependencies:
+ language-subtag-registry: 0.3.23
+
+ levn@0.4.1:
+ dependencies:
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+
+ lightningcss-android-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-arm64@1.32.0:
+ optional: true
+
+ lightningcss-darwin-x64@1.32.0:
+ optional: true
+
+ lightningcss-freebsd-x64@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm-gnueabihf@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-arm64-musl@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-gnu@1.32.0:
+ optional: true
+
+ lightningcss-linux-x64-musl@1.32.0:
+ optional: true
+
+ lightningcss-win32-arm64-msvc@1.32.0:
+ optional: true
+
+ lightningcss-win32-x64-msvc@1.32.0:
+ optional: true
+
+ lightningcss@1.32.0:
+ dependencies:
+ detect-libc: 2.1.2
+ optionalDependencies:
+ lightningcss-android-arm64: 1.32.0
+ lightningcss-darwin-arm64: 1.32.0
+ lightningcss-darwin-x64: 1.32.0
+ lightningcss-freebsd-x64: 1.32.0
+ lightningcss-linux-arm-gnueabihf: 1.32.0
+ lightningcss-linux-arm64-gnu: 1.32.0
+ lightningcss-linux-arm64-musl: 1.32.0
+ lightningcss-linux-x64-gnu: 1.32.0
+ lightningcss-linux-x64-musl: 1.32.0
+ lightningcss-win32-arm64-msvc: 1.32.0
+ lightningcss-win32-x64-msvc: 1.32.0
+
+ lint-staged@17.0.7:
+ dependencies:
+ listr2: 10.2.1
+ picomatch: 4.0.4
+ string-argv: 0.3.2
+ tinyexec: 1.2.4
+ optionalDependencies:
+ yaml: 2.9.0
+
+ listr2@10.2.1:
+ dependencies:
+ cli-truncate: 5.2.0
+ eventemitter3: 5.0.4
+ log-update: 6.1.0
+ rfdc: 1.4.1
+ wrap-ansi: 10.0.0
+
+ locate-path@6.0.0:
+ dependencies:
+ p-locate: 5.0.0
+
+ lodash.merge@4.6.2: {}
+
+ log-update@6.1.0:
+ dependencies:
+ ansi-escapes: 7.3.0
+ cli-cursor: 5.0.0
+ slice-ansi: 7.1.2
+ strip-ansi: 7.2.0
+ wrap-ansi: 9.0.2
+
+ loose-envify@1.4.0:
+ dependencies:
+ js-tokens: 4.0.0
+
+ lru-cache@5.1.1:
+ dependencies:
+ yallist: 3.1.1
+
+ lucide-react@1.18.0(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+
+ magic-string@0.30.21:
+ dependencies:
+ '@jridgewell/sourcemap-codec': 1.5.5
+
+ marked@18.0.5: {}
+
+ math-intrinsics@1.1.0: {}
+
+ merge2@1.4.1: {}
+
+ micromatch@4.0.8:
+ dependencies:
+ braces: 3.0.3
+ picomatch: 2.3.2
+
+ mimic-function@5.0.1: {}
+
+ minimatch@10.2.5:
+ dependencies:
+ brace-expansion: 5.0.6
+
+ minimatch@3.1.5:
+ dependencies:
+ brace-expansion: 1.1.15
+
+ minimist@1.2.8: {}
+
+ ms@2.1.3: {}
+
+ nanoid@3.3.12: {}
+
+ napi-postinstall@0.3.4: {}
+
+ natural-compare@1.4.0: {}
+
+ negotiator@1.0.0: {}
+
+ next-intl-swc-plugin-extractor@4.13.0: {}
+
+ next-intl@4.13.0(next@16.2.9(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7))(react@19.2.7)(typescript@6.0.3):
+ dependencies:
+ '@formatjs/intl-localematcher': 0.8.10
+ '@parcel/watcher': 2.5.6
+ '@swc/core': 1.15.41
+ icu-minify: 4.13.0
+ negotiator: 1.0.0
+ next: 16.2.9(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)
+ next-intl-swc-plugin-extractor: 4.13.0
+ po-parser: 2.1.1
+ react: 19.2.7
+ use-intl: 4.13.0(react@19.2.7)
+ optionalDependencies:
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - '@swc/helpers'
+
+ next@16.2.9(@babel/core@7.29.7)(react-dom@19.2.7(react@19.2.7))(react@19.2.7):
+ dependencies:
+ '@next/env': 16.2.9
+ '@swc/helpers': 0.5.15
+ baseline-browser-mapping: 2.10.36
+ caniuse-lite: 1.0.30001799
+ postcss: 8.4.31
+ react: 19.2.7
+ react-dom: 19.2.7(react@19.2.7)
+ styled-jsx: 5.1.6(@babel/core@7.29.7)(react@19.2.7)
+ optionalDependencies:
+ '@next/swc-darwin-arm64': 16.2.9
+ '@next/swc-darwin-x64': 16.2.9
+ '@next/swc-linux-arm64-gnu': 16.2.9
+ '@next/swc-linux-arm64-musl': 16.2.9
+ '@next/swc-linux-x64-gnu': 16.2.9
+ '@next/swc-linux-x64-musl': 16.2.9
+ '@next/swc-win32-arm64-msvc': 16.2.9
+ '@next/swc-win32-x64-msvc': 16.2.9
+ sharp: 0.34.5
+ transitivePeerDependencies:
+ - '@babel/core'
+ - babel-plugin-macros
+
+ node-addon-api@7.1.1: {}
+
+ node-exports-info@1.6.0:
+ dependencies:
+ array.prototype.flatmap: 1.3.3
+ es-errors: 1.3.0
+ object.entries: 1.1.9
+ semver: 6.3.1
+
+ node-releases@2.0.47: {}
+
+ object-assign@4.1.1: {}
+
+ object-inspect@1.13.4: {}
+
+ object-keys@1.1.1: {}
+
+ object.assign@4.1.7:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.2
+ has-symbols: 1.1.0
+ object-keys: 1.1.1
+
+ object.entries@1.1.9:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.2
+
+ object.fromentries@2.0.8:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-object-atoms: 1.1.2
+
+ object.groupby@1.0.3:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+
+ object.values@1.2.1:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.2
+
+ onetime@7.0.0:
+ dependencies:
+ mimic-function: 5.0.1
+
+ optionator@0.9.4:
+ dependencies:
+ deep-is: 0.1.4
+ fast-levenshtein: 2.0.6
+ levn: 0.4.1
+ prelude-ls: 1.2.1
+ type-check: 0.4.0
+ word-wrap: 1.2.5
+
+ own-keys@1.0.1:
+ dependencies:
+ get-intrinsic: 1.3.0
+ object-keys: 1.1.1
+ safe-push-apply: 1.0.0
+
+ oxc-parser@0.133.0:
+ dependencies:
+ '@oxc-project/types': 0.133.0
+ optionalDependencies:
+ '@oxc-parser/binding-android-arm-eabi': 0.133.0
+ '@oxc-parser/binding-android-arm64': 0.133.0
+ '@oxc-parser/binding-darwin-arm64': 0.133.0
+ '@oxc-parser/binding-darwin-x64': 0.133.0
+ '@oxc-parser/binding-freebsd-x64': 0.133.0
+ '@oxc-parser/binding-linux-arm-gnueabihf': 0.133.0
+ '@oxc-parser/binding-linux-arm-musleabihf': 0.133.0
+ '@oxc-parser/binding-linux-arm64-gnu': 0.133.0
+ '@oxc-parser/binding-linux-arm64-musl': 0.133.0
+ '@oxc-parser/binding-linux-ppc64-gnu': 0.133.0
+ '@oxc-parser/binding-linux-riscv64-gnu': 0.133.0
+ '@oxc-parser/binding-linux-riscv64-musl': 0.133.0
+ '@oxc-parser/binding-linux-s390x-gnu': 0.133.0
+ '@oxc-parser/binding-linux-x64-gnu': 0.133.0
+ '@oxc-parser/binding-linux-x64-musl': 0.133.0
+ '@oxc-parser/binding-openharmony-arm64': 0.133.0
+ '@oxc-parser/binding-wasm32-wasi': 0.133.0
+ '@oxc-parser/binding-win32-arm64-msvc': 0.133.0
+ '@oxc-parser/binding-win32-ia32-msvc': 0.133.0
+ '@oxc-parser/binding-win32-x64-msvc': 0.133.0
+
+ oxc-resolver@11.20.0:
+ optionalDependencies:
+ '@oxc-resolver/binding-android-arm-eabi': 11.20.0
+ '@oxc-resolver/binding-android-arm64': 11.20.0
+ '@oxc-resolver/binding-darwin-arm64': 11.20.0
+ '@oxc-resolver/binding-darwin-x64': 11.20.0
+ '@oxc-resolver/binding-freebsd-x64': 11.20.0
+ '@oxc-resolver/binding-linux-arm-gnueabihf': 11.20.0
+ '@oxc-resolver/binding-linux-arm-musleabihf': 11.20.0
+ '@oxc-resolver/binding-linux-arm64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-arm64-musl': 11.20.0
+ '@oxc-resolver/binding-linux-ppc64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-riscv64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-riscv64-musl': 11.20.0
+ '@oxc-resolver/binding-linux-s390x-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-x64-gnu': 11.20.0
+ '@oxc-resolver/binding-linux-x64-musl': 11.20.0
+ '@oxc-resolver/binding-openharmony-arm64': 11.20.0
+ '@oxc-resolver/binding-wasm32-wasi': 11.20.0
+ '@oxc-resolver/binding-win32-arm64-msvc': 11.20.0
+ '@oxc-resolver/binding-win32-x64-msvc': 11.20.0
+
+ p-limit@3.1.0:
+ dependencies:
+ yocto-queue: 0.1.0
+
+ p-locate@5.0.0:
+ dependencies:
+ p-limit: 3.1.0
+
+ parent-module@1.0.1:
+ dependencies:
+ callsites: 3.1.0
+
+ path-exists@4.0.0: {}
+
+ path-key@3.1.1: {}
+
+ path-parse@1.0.7: {}
+
+ picocolors@1.1.1: {}
+
+ picomatch@2.3.2: {}
+
+ picomatch@4.0.4: {}
+
+ po-parser@2.1.1: {}
+
+ possible-typed-array-names@1.1.0: {}
+
+ postcss@8.4.31:
+ dependencies:
+ nanoid: 3.3.12
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ postcss@8.5.15:
+ dependencies:
+ nanoid: 3.3.12
+ picocolors: 1.1.1
+ source-map-js: 1.2.1
+
+ prelude-ls@1.2.1: {}
+
+ prettier@3.8.4: {}
+
+ prop-types@15.8.1:
+ dependencies:
+ loose-envify: 1.4.0
+ object-assign: 4.1.1
+ react-is: 16.13.1
+
+ punycode@2.3.1: {}
+
+ queue-microtask@1.2.3: {}
+
+ react-dom@19.2.7(react@19.2.7):
+ dependencies:
+ react: 19.2.7
+ scheduler: 0.27.0
+
+ react-is@16.13.1: {}
+
+ react@19.2.7: {}
+
+ reflect.getprototypeof@1.0.10:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ get-intrinsic: 1.3.0
+ get-proto: 1.0.1
+ which-builtin-type: 1.2.1
+
+ regexp.prototype.flags@1.5.4:
+ dependencies:
+ call-bind: 1.0.9
+ 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
+
+ resolve-from@4.0.0: {}
+
+ resolve-pkg-maps@1.0.0: {}
+
+ resolve@2.0.0-next.7:
+ dependencies:
+ es-errors: 1.3.0
+ is-core-module: 2.16.2
+ node-exports-info: 1.6.0
+ object-keys: 1.1.1
+ path-parse: 1.0.7
+ supports-preserve-symlinks-flag: 1.0.0
+
+ restore-cursor@5.1.0:
+ dependencies:
+ onetime: 7.0.0
+ signal-exit: 4.1.0
+
+ reusify@1.1.0: {}
+
+ rfdc@1.4.1: {}
+
+ run-parallel@1.2.0:
+ dependencies:
+ queue-microtask: 1.2.3
+
+ safe-array-concat@1.1.4:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ get-intrinsic: 1.3.0
+ has-symbols: 1.1.0
+ isarray: 2.0.5
+
+ safe-push-apply@1.0.0:
+ dependencies:
+ es-errors: 1.3.0
+ isarray: 2.0.5
+
+ safe-regex-test@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-regex: 1.2.1
+
+ scheduler@0.27.0: {}
+
+ semver@6.3.1: {}
+
+ semver@7.8.4: {}
+
+ set-function-length@1.2.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ function-bind: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-property-descriptors: 1.0.2
+
+ set-function-name@2.0.2:
+ dependencies:
+ define-data-property: 1.1.4
+ es-errors: 1.3.0
+ functions-have-names: 1.2.3
+ has-property-descriptors: 1.0.2
+
+ set-proto@1.0.0:
+ dependencies:
+ dunder-proto: 1.0.1
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+
+ sharp@0.34.5:
+ dependencies:
+ '@img/colour': 1.1.0
+ detect-libc: 2.1.2
+ semver: 7.8.4
+ optionalDependencies:
+ '@img/sharp-darwin-arm64': 0.34.5
+ '@img/sharp-darwin-x64': 0.34.5
+ '@img/sharp-libvips-darwin-arm64': 1.2.4
+ '@img/sharp-libvips-darwin-x64': 1.2.4
+ '@img/sharp-libvips-linux-arm': 1.2.4
+ '@img/sharp-libvips-linux-arm64': 1.2.4
+ '@img/sharp-libvips-linux-ppc64': 1.2.4
+ '@img/sharp-libvips-linux-riscv64': 1.2.4
+ '@img/sharp-libvips-linux-s390x': 1.2.4
+ '@img/sharp-libvips-linux-x64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-arm64': 1.2.4
+ '@img/sharp-libvips-linuxmusl-x64': 1.2.4
+ '@img/sharp-linux-arm': 0.34.5
+ '@img/sharp-linux-arm64': 0.34.5
+ '@img/sharp-linux-ppc64': 0.34.5
+ '@img/sharp-linux-riscv64': 0.34.5
+ '@img/sharp-linux-s390x': 0.34.5
+ '@img/sharp-linux-x64': 0.34.5
+ '@img/sharp-linuxmusl-arm64': 0.34.5
+ '@img/sharp-linuxmusl-x64': 0.34.5
+ '@img/sharp-wasm32': 0.34.5
+ '@img/sharp-win32-arm64': 0.34.5
+ '@img/sharp-win32-ia32': 0.34.5
+ '@img/sharp-win32-x64': 0.34.5
+ optional: true
+
+ shebang-command@2.0.0:
+ dependencies:
+ shebang-regex: 3.0.0
+
+ shebang-regex@3.0.0: {}
+
+ side-channel-list@1.0.1:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-map@1.0.1:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+
+ side-channel-weakmap@1.0.2:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ get-intrinsic: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-map: 1.0.1
+
+ side-channel@1.1.1:
+ dependencies:
+ es-errors: 1.3.0
+ object-inspect: 1.13.4
+ side-channel-list: 1.0.1
+ side-channel-map: 1.0.1
+ side-channel-weakmap: 1.0.2
+
+ signal-exit@4.1.0: {}
+
+ slice-ansi@7.1.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ slice-ansi@8.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ is-fullwidth-code-point: 5.1.0
+
+ smol-toml@1.6.1: {}
+
+ source-map-js@1.2.1: {}
+
+ stable-hash@0.0.5: {}
+
+ stop-iteration-iterator@1.1.0:
+ dependencies:
+ es-errors: 1.3.0
+ internal-slot: 1.1.0
+
+ string-argv@0.3.2: {}
+
+ string-width@7.2.0:
+ dependencies:
+ emoji-regex: 10.6.0
+ get-east-asian-width: 1.6.0
+ strip-ansi: 7.2.0
+
+ string-width@8.2.1:
+ dependencies:
+ get-east-asian-width: 1.6.0
+ strip-ansi: 7.2.0
+
+ string.prototype.includes@2.0.1:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+
+ string.prototype.matchall@4.0.12:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-errors: 1.3.0
+ es-object-atoms: 1.1.2
+ get-intrinsic: 1.3.0
+ gopd: 1.2.0
+ has-symbols: 1.1.0
+ internal-slot: 1.1.0
+ regexp.prototype.flags: 1.5.4
+ set-function-name: 2.0.2
+ side-channel: 1.1.1
+
+ string.prototype.repeat@1.0.0:
+ dependencies:
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+
+ string.prototype.trim@1.2.11:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-data-property: 1.1.4
+ define-properties: 1.2.1
+ es-abstract: 1.24.2
+ es-object-atoms: 1.1.2
+ has-property-descriptors: 1.0.2
+ safe-regex-test: 1.1.0
+
+ string.prototype.trimend@1.0.10:
+ dependencies:
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.2
+
+ string.prototype.trimstart@1.0.8:
+ dependencies:
+ call-bind: 1.0.9
+ define-properties: 1.2.1
+ es-object-atoms: 1.1.2
+
+ strip-ansi@7.2.0:
+ dependencies:
+ ansi-regex: 6.2.2
+
+ strip-bom@3.0.0: {}
+
+ strip-json-comments@3.1.1: {}
+
+ strip-json-comments@5.0.3: {}
+
+ styled-jsx@5.1.6(@babel/core@7.29.7)(react@19.2.7):
+ dependencies:
+ client-only: 0.0.1
+ react: 19.2.7
+ optionalDependencies:
+ '@babel/core': 7.29.7
+
+ supports-color@7.2.0:
+ dependencies:
+ has-flag: 4.0.0
+
+ supports-preserve-symlinks-flag@1.0.0: {}
+
+ tailwindcss@4.3.0: {}
+
+ tapable@2.3.3: {}
+
+ tinyexec@1.2.4: {}
+
+ tinyglobby@0.2.17:
+ dependencies:
+ fdir: 6.5.0(picomatch@4.0.4)
+ picomatch: 4.0.4
+
+ to-regex-range@5.0.1:
+ dependencies:
+ is-number: 7.0.0
+
+ ts-api-utils@2.5.0(typescript@6.0.3):
+ dependencies:
+ typescript: 6.0.3
+
+ tsconfig-paths@3.15.0:
+ dependencies:
+ '@types/json5': 0.0.29
+ json5: 1.0.2
+ minimist: 1.2.8
+ strip-bom: 3.0.0
+
+ tslib@2.8.1: {}
+
+ type-check@0.4.0:
+ dependencies:
+ prelude-ls: 1.2.1
+
+ typed-array-buffer@1.0.3:
+ dependencies:
+ call-bound: 1.0.4
+ es-errors: 1.3.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-length@1.0.3:
+ dependencies:
+ call-bind: 1.0.9
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+
+ typed-array-byte-offset@1.0.4:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.9
+ for-each: 0.3.5
+ gopd: 1.2.0
+ has-proto: 1.2.0
+ is-typed-array: 1.1.15
+ reflect.getprototypeof: 1.0.10
+
+ typed-array-length@1.0.8:
+ dependencies:
+ call-bind: 1.0.9
+ for-each: 0.3.5
+ gopd: 1.2.0
+ is-typed-array: 1.1.15
+ possible-typed-array-names: 1.1.0
+ reflect.getprototypeof: 1.0.10
+
+ typescript-eslint@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3):
+ dependencies:
+ '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3))(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/parser': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ '@typescript-eslint/typescript-estree': 8.61.0(typescript@6.0.3)
+ '@typescript-eslint/utils': 8.61.0(eslint@9.39.4(jiti@2.7.0))(typescript@6.0.3)
+ eslint: 9.39.4(jiti@2.7.0)
+ typescript: 6.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ typescript@6.0.3: {}
+
+ unbash@3.0.0: {}
+
+ unbox-primitive@1.1.0:
+ dependencies:
+ call-bound: 1.0.4
+ has-bigints: 1.1.0
+ has-symbols: 1.1.0
+ which-boxed-primitive: 1.1.1
+
+ undici-types@6.21.0: {}
+
+ unrs-resolver@1.12.2:
+ dependencies:
+ napi-postinstall: 0.3.4
+ optionalDependencies:
+ '@unrs/resolver-binding-android-arm-eabi': 1.12.2
+ '@unrs/resolver-binding-android-arm64': 1.12.2
+ '@unrs/resolver-binding-darwin-arm64': 1.12.2
+ '@unrs/resolver-binding-darwin-x64': 1.12.2
+ '@unrs/resolver-binding-freebsd-x64': 1.12.2
+ '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2
+ '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2
+ '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-arm64-musl': 1.12.2
+ '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-loong64-musl': 1.12.2
+ '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2
+ '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-x64-gnu': 1.12.2
+ '@unrs/resolver-binding-linux-x64-musl': 1.12.2
+ '@unrs/resolver-binding-openharmony-arm64': 1.12.2
+ '@unrs/resolver-binding-wasm32-wasi': 1.12.2
+ '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2
+ '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2
+ '@unrs/resolver-binding-win32-x64-msvc': 1.12.2
+
+ update-browserslist-db@1.2.3(browserslist@4.28.2):
+ dependencies:
+ browserslist: 4.28.2
+ escalade: 3.2.0
+ picocolors: 1.1.1
+
+ uri-js@4.4.1:
+ dependencies:
+ punycode: 2.3.1
+
+ use-intl@4.13.0(react@19.2.7):
+ dependencies:
+ '@formatjs/fast-memoize': 3.1.6
+ '@schummar/icu-type-parser': 1.21.5
+ icu-minify: 4.13.0
+ intl-messageformat: 11.2.8
+ react: 19.2.7
+
+ walk-up-path@4.0.0: {}
+
+ which-boxed-primitive@1.1.1:
+ dependencies:
+ is-bigint: 1.1.0
+ is-boolean-object: 1.2.2
+ is-number-object: 1.1.1
+ is-string: 1.1.1
+ is-symbol: 1.1.1
+
+ which-builtin-type@1.2.1:
+ dependencies:
+ call-bound: 1.0.4
+ function.prototype.name: 1.2.0
+ has-tostringtag: 1.0.2
+ is-async-function: 2.1.1
+ is-date-object: 1.1.0
+ is-finalizationregistry: 1.1.1
+ is-generator-function: 1.1.2
+ is-regex: 1.2.1
+ is-weakref: 1.1.1
+ isarray: 2.0.5
+ which-boxed-primitive: 1.1.1
+ which-collection: 1.0.2
+ which-typed-array: 1.1.22
+
+ which-collection@1.0.2:
+ dependencies:
+ is-map: 2.0.3
+ is-set: 2.0.3
+ is-weakmap: 2.0.2
+ is-weakset: 2.0.4
+
+ which-typed-array@1.1.22:
+ dependencies:
+ available-typed-arrays: 1.0.7
+ call-bind: 1.0.9
+ call-bound: 1.0.4
+ for-each: 0.3.5
+ get-proto: 1.0.1
+ gopd: 1.2.0
+ has-tostringtag: 1.0.2
+
+ which@2.0.2:
+ dependencies:
+ isexe: 2.0.0
+
+ word-wrap@1.2.5: {}
+
+ wrap-ansi@10.0.0:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 8.2.1
+ strip-ansi: 7.2.0
+
+ wrap-ansi@9.0.2:
+ dependencies:
+ ansi-styles: 6.2.3
+ string-width: 7.2.0
+ strip-ansi: 7.2.0
+
+ yallist@3.1.1: {}
+
+ yaml@2.9.0: {}
+
+ yocto-queue@0.1.0: {}
+
+ zod-validation-error@4.0.2(zod@4.4.3):
+ dependencies:
+ zod: 4.4.3
+
+ zod@4.4.3: {}
diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml
new file mode 100644
index 0000000..4e6e06b
--- /dev/null
+++ b/pnpm-workspace.yaml
@@ -0,0 +1,22 @@
+allowBuilds:
+ '@parcel/watcher': true
+ '@swc/core': true
+ sharp: true
+ unrs-resolver: true
+ignoredBuiltDependencies:
+ - sharp
+ - unrs-resolver
+minimumReleaseAgeExclude:
+ - '@next/env@16.2.9'
+ - '@next/eslint-plugin-next@16.2.9'
+ - '@next/swc-darwin-arm64@16.2.9'
+ - '@next/swc-darwin-x64@16.2.9'
+ - '@next/swc-linux-arm64-gnu@16.2.9'
+ - '@next/swc-linux-arm64-musl@16.2.9'
+ - '@next/swc-linux-x64-gnu@16.2.9'
+ - '@next/swc-linux-x64-musl@16.2.9'
+ - '@next/swc-win32-arm64-msvc@16.2.9'
+ - '@next/swc-win32-x64-msvc@16.2.9'
+ - eslint-config-next@16.2.9
+ - next@16.2.9
+ - lucide-react@1.18.0
diff --git a/postcss.config.mjs b/postcss.config.mjs
new file mode 100644
index 0000000..297374d
--- /dev/null
+++ b/postcss.config.mjs
@@ -0,0 +1,7 @@
+const config = {
+ plugins: {
+ '@tailwindcss/postcss': {},
+ },
+};
+
+export default config;
diff --git a/pt_BR.html b/pt_BR.html
deleted file mode 100644
index 504f04e..0000000
--- a/pt_BR.html
+++ /dev/null
@@ -1,742 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
- OpenTune
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Você será redirecionado para fora do OpenTune Web para uma
- demonstração externa fornecida pelo Appetize.io.
-
-
- Cancelar
- Continuar
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- code
- Dev By Arthur Dev Studio
-
-
-
OpenTune
-
- Um cliente do YouTube Music com Material Design 3, para Android.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Recursos
-
- Descubra todos os recursos que fazem do OpenTune o melhor cliente para sua música.
-
-
-
-
-
-
-
- music_note
-
-
Cliente do YouTube Music com Material Design 3
-
-
Oferece uma experiência moderna e
- elegante do YouTube Music no Android, com uma interface intuitiva e otimizada.
-
-
-
-
- palette
-
-
Design Moderno e Atraente
-
Baseado no Material Design 3, com
- cores vibrantes, tipografia moderna e transições suaves.
-
-
-
-
- settings
-
-
Interface Intuitiva e Personalizável
-
Interface simplificada para acesso
- rápido a todas as funcionalidades, com opções de personalização.
-
-
-
-
- search
-
-
Explore e Descubra Música
-
Acesso a um vasto catálogo musical
- com busca avançada e recomendações inteligentes.
-
-
-
-
- volume_up
-
-
Reprodução de Alta Qualidade
-
Música de alta qualidade, otimizada
- para diferentes dispositivos e condições de rede.
-
-
-
-
- playlist_add
-
-
Playlists e Downloads
-
Crie e organize playlists,
- e baixe músicas para ouvir offline.
-
-
-
-
- sync
-
-
Integração com o YouTube Music
-
Sincronize sua conta do YouTube Music
- para acessar suas bibliotecas e receber notificações.
-
-
-
-
- build
-
-
Funcionalidades Avançadas
-
Controle de reprodução por voz e
- gestos, com suporte para Chromecast e Bluetooth.
-
-
-
-
- folder
-
-
Gerenciamento Eficiente da Biblioteca
-
Organize sua música com
- ferramentas avançadas de filtragem e categorização.
-
-
-
-
- translate
-
-
Suporte Multilíngue
-
Disponível em vários idiomas
- com traduções precisas e localização culturalmente relevante.
-
-
-
-
- update
-
-
Atualizações Contínuas
-
Atualizações regulares com novas
- funcionalidades, melhorias de segurança e otimização.
-
-
-
-
- headphones
-
-
Experiência do Usuário Otimizada
-
Navegação intuitiva e desempenho
- ágil com uma interface amigável que melhora a experiência.
-
-
-
-
-
-
-
-
-
-
IMPULSIONADO
- PELA COMUNIDADE
-
Código Aberto no Coração
-
O OpenTune é construído por amantes de música
- para amantes de música. Contribua, personalize e nos ajude a construir o melhor reprodutor
- de código aberto para Android.
-
-
-
-
-
-
-
-
-
-
Arturo254
-
Desenvolvedor Principal
-
-
-
-
- verified
-
-
-
Última versão estável
-
Carregando...
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Reprodutor Imersivo
-
-
-
-
Biblioteca Inteligente
-
-
-
-
Configurações Avançadas
-
-
-
-
-
-
-
-
-
-
-
- support_agent
-
-
Precisa de Ajuda?
-
- Envie uma solicitação ou relate um problema diretamente à equipe de desenvolvimento.
-
-
- send
- Solicitar Ajuda
-
-
-
-
-
-
-
-
-
-
Downloads
-
- Baixe a versão mais recente do OpenTune para sua plataforma.
-
-
-
-
-
-
-
-
-
- android
-
-
-
Android
-
Baixe a última versão estável.
-
-
-
-
- Versão Estável
- Carregando...
-
-
-
-
- list_alt
- Ver Alterações
-
-
- history
- Versões Anteriores
-
-
-
-
-
-
-
-
-
-
-
-
-
- laptop_windows
-
-
-
Windows
-
O desenvolvimento está fechado no momento.
-
-
-
-
-
- Fechado
- Indisponível
-
-
-
-
- list_alt
- Ver Alterações
-
-
- history
- Versões Anteriores
-
-
-
-
-
- block
- Indisponível
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
OpenTune
-
© 2024 Arturo.inc™ .
- Todos os Direitos Reservados.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Licenciado sob a Licença de Código Aberto GPL-3.0.
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/icon/apple-touch-icon.png b/public/icon/apple-touch-icon.png
similarity index 100%
rename from icon/apple-touch-icon.png
rename to public/icon/apple-touch-icon.png
diff --git a/icon/favicon.ico b/public/icon/favicon.ico
similarity index 100%
rename from icon/favicon.ico
rename to public/icon/favicon.ico
diff --git a/icon/icon-192-maskable.png b/public/icon/icon-192-maskable.png
similarity index 100%
rename from icon/icon-192-maskable.png
rename to public/icon/icon-192-maskable.png
diff --git a/icon/icon-192.png b/public/icon/icon-192.png
similarity index 100%
rename from icon/icon-192.png
rename to public/icon/icon-192.png
diff --git a/icon/icon-512-maskable.png b/public/icon/icon-512-maskable.png
similarity index 100%
rename from icon/icon-512-maskable.png
rename to public/icon/icon-512-maskable.png
diff --git a/icon/icon-512.png b/public/icon/icon-512.png
similarity index 100%
rename from icon/icon-512.png
rename to public/icon/icon-512.png
diff --git a/icon/opentune-black.svg b/public/icon/opentune-black.svg
similarity index 100%
rename from icon/opentune-black.svg
rename to public/icon/opentune-black.svg
diff --git a/img/ajustes.webp b/public/img/ajustes.webp
similarity index 100%
rename from img/ajustes.webp
rename to public/img/ajustes.webp
diff --git a/img/biblioteca.webp b/public/img/biblioteca.webp
similarity index 100%
rename from img/biblioteca.webp
rename to public/img/biblioteca.webp
diff --git a/img/mock.webp b/public/img/mock.webp
similarity index 100%
rename from img/mock.webp
rename to public/img/mock.webp
diff --git a/img/reproductor.webp b/public/img/reproductor.webp
similarity index 100%
rename from img/reproductor.webp
rename to public/img/reproductor.webp
diff --git a/script.js b/script.js
deleted file mode 100644
index a2fe5a2..0000000
--- a/script.js
+++ /dev/null
@@ -1,1057 +0,0 @@
-/**
- * OpenTune App - Main JavaScript
- * Versión: 2.0.0
- * Descripción: Script principal para la aplicación web OpenTune
- */
-
-const APP_CONFIG = {
- repositories: {
- android: {
- repo: 'Arturo254/OpenTune',
- currentVersion: '1.9.8',
- downloadFormat: '{version}/app-universal-release.apk',
- elements: {
- version: 'android-version-badge',
- download: 'android-download-btn',
- text: 'android-download-text',
- changelog: 'changelog-dialog',
- versions: 'versions-dialog',
- changelogContent: 'changelog-content',
- versionsList: 'versions-list'
- }
- },
- windows: {
- repo: 'Arturo254/OpenTune-Desktop',
- currentVersion: '1.9.0',
- downloadFormat: '{version}/OpenTune-Desktop-{version}.exe',
- elements: {
- version: 'currentVersionWindows',
- download: 'downloadBtnWindows',
- text: 'downloadTextWindows',
- changelog: 'changelogWindows',
- versions: 'versionsWindows',
- changelogContent: 'changelogContentWindows',
- versionsList: 'versionsListWindows'
- }
- }
- },
- urls: {
- api: { github: 'https://api.github.com/repos/' },
- demo: 'https://appetize.io/app/b_yb62tcjuqzqjvctnswv3krpnmm'
- },
- elements: {
- theme: { selector: 'themeSelector', icon: 'themeIcon' },
- language: { selector: 'language-btn', dialog: 'languageDialog', text: 'languageText' },
- warning: { button: 'triggerDialogButton', dialog: 'warningDialog', overlay: 'dialogOverlay', dismiss: 'dismissButton', proceed: 'proceedButton' },
- logo: 'logo'
- },
- checkInterval: 60 * 60 * 1000
-};
-
-class OpenTuneApp {
- constructor(config) {
- this.config = config;
- this.themeManager = new ThemeManager(config.elements.theme);
- this.languageManager = new LanguageManager(config.elements.language);
- this.dialogManager = new DialogManager(config.elements.warning, config.urls.demo);
- this.logoManager = new LogoManager(config.elements.logo);
- this.versionManager = new VersionManager(config.repositories);
- this.carouselManager = null;
- }
-
- init() {
- this.themeManager.init();
- this.languageManager.init();
- this.dialogManager.init();
- this.logoManager.init();
- this.versionManager.init();
- this.configureMarkdown();
- console.log('OpenTune App inicializada correctamente');
- }
-
- configureMarkdown() {
- if (typeof marked !== 'undefined') {
- marked.setOptions({ breaks: true, gfm: true, headerIds: false, sanitize: false });
- }
- }
-}
-
-class ThemeManager {
- constructor(elements) {
- this.selectorId = elements.selector;
- this.iconId = elements.icon;
- }
-
- init() {
- const selector = document.getElementById(this.selectorId);
- const icon = document.getElementById(this.iconId);
- if (!selector || !icon) return;
-
- this.selector = selector;
- this.icon = icon;
-
- // Detectar tema actual basado en las clases del root
- const isLightMode = document.documentElement.classList.contains('light-mode');
- this.updateIcon(isLightMode ? 'light' : 'dark');
-
- selector.addEventListener('click', () => this.toggleTheme());
- }
-
- applyTheme(theme) {
- const root = document.documentElement;
-
- if (theme === 'light') {
- root.classList.add('light-mode');
- localStorage.setItem('theme', 'light');
- } else {
- root.classList.remove('light-mode');
- localStorage.setItem('theme', 'dark');
- }
-
- this.updateIcon(theme);
-
- // Disparar evento para que otros componentes se actualicen si es necesario
- window.dispatchEvent(new CustomEvent('themeChanged', { detail: { theme } }));
- }
-
- updateIcon(theme) {
- if (this.icon) {
- this.icon.textContent = theme === 'dark' ? 'light_mode' : 'dark_mode';
- }
- }
-
- toggleTheme() {
- const isLightMode = document.documentElement.classList.contains('light-mode');
- this.applyTheme(isLightMode ? 'dark' : 'light');
- }
-}
-
-class LanguageManager {
- constructor(elements) {
- this.selectorId = elements.selector;
- this.dialogId = elements.dialog;
- this.textId = elements.text;
- }
-
- init() {
- const selector = document.getElementById(this.selectorId);
- const dialog = document.getElementById(this.dialogId);
- const text = document.getElementById(this.textId);
-
- if (!selector || !dialog) return;
-
- // Abrir diálogo al hacer clic en el botón
- selector.addEventListener('click', () => {
- dialog.showModal();
- });
-
- // Cerrar diálogo con el botón X o el botón Cancelar
- const closeButtons = dialog.querySelectorAll('[data-close]');
- closeButtons.forEach(btn => {
- btn.addEventListener('click', () => {
- dialog.close();
- });
- });
-
- // Cerrar al hacer clic fuera del diálogo
- dialog.addEventListener('click', (e) => {
- if (e.target === dialog) {
- dialog.close();
- }
- });
-
- // Configurar funciones globales para cambio de idioma (si existen)
- window.closeDialog = () => dialog.close();
- window.setLanguage = (language, url) => {
- if (text) text.textContent = language;
- dialog.close();
- window.location.href = url;
- };
- }
-}
-
-class DialogManager {
- constructor(elements, demoUrl) {
- this.elements = elements;
- this.demoUrl = demoUrl;
- }
-
- init() {
- const button = document.getElementById(this.elements.button);
- const dialog = document.getElementById(this.elements.dialog);
- const overlay = document.getElementById(this.elements.overlay);
- const dismissButton = document.getElementById(this.elements.dismiss);
- const proceedButton = document.getElementById(this.elements.proceed);
- if (!button || !dialog || !overlay) return;
-
- button.addEventListener('click', () => {
- dialog.showModal();
- overlay.style.display = 'block';
- });
-
- if (dismissButton) {
- dismissButton.addEventListener('click', () => {
- dialog.close();
- overlay.style.display = 'none';
- });
- }
-
- if (proceedButton) {
- proceedButton.addEventListener('click', () => {
- window.location.href = this.demoUrl;
- });
- }
- }
-}
-
-class LogoManager {
- constructor(element) {
- this.elementId = element;
- }
-
- init() {
- const logo = document.getElementById(this.elementId);
- if (!logo) return;
- logo.addEventListener('load', () => this.applyLogoEffects(logo));
- }
-
- applyLogoEffects(logo) {
- try {
- const canvas = document.createElement('canvas');
- const ctx = canvas.getContext('2d');
- const img = new Image();
- img.crossOrigin = 'Anonymous';
- img.src = logo.src;
- img.onload = () => {
- canvas.width = img.width;
- canvas.height = img.height;
- ctx.drawImage(img, 0, 0);
- try {
- const pixels = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
- let r = 0, g = 0, b = 0, total = 0;
- for (let i = 0; i < pixels.length; i += 4) { r += pixels[i]; g += pixels[i + 1]; b += pixels[i + 2]; total++; }
- logo.style.setProperty('--logo-glow-color', `rgb(${Math.floor(r / total)},${Math.floor(g / total)},${Math.floor(b / total)})`);
- } catch (e) {
- logo.style.setProperty('--logo-glow-color', 'rgba(255,255,255,0.5)');
- }
- };
- } catch (e) {
- console.error('Error al inicializar efectos del logo:', e);
- }
- }
-}
-
-class VersionManager {
- constructor(repositories) {
- this.repositories = repositories;
- this.handlers = {};
- }
-
- init() {
- Object.entries(this.repositories).forEach(([platform, config]) => {
- if (platform === 'android') this.handlers[platform] = new AndroidVersionHandler(config);
- else if (platform === 'windows') this.handlers[platform] = new WindowsVersionHandler(config);
- if (this.handlers[platform]) this.handlers[platform].init();
- });
- }
-}
-
-class BaseVersionHandler {
- constructor(config) {
- this.repo = config.repo;
- this.currentVersion = config.currentVersion;
- this.latestVersion = config.currentVersion;
- this.downloadFormat = config.downloadFormat;
- this.elements = config.elements;
- }
-
- async init() {
- const versionEl = document.getElementById(this.elements.version);
- const downloadBtn = document.getElementById(this.elements.download);
- if (!versionEl && !downloadBtn) return;
- await this.checkNewVersion();
- setInterval(() => this.checkNewVersion(), APP_CONFIG.checkInterval);
- }
-
- async checkNewVersion() {
- const versionEl = document.getElementById(this.elements.version);
- const downloadText = document.getElementById(this.elements.text);
- try {
- const response = await fetch(`${APP_CONFIG.urls.api.github}${this.repo}/releases/latest`);
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const data = await response.json();
- this.latestVersion = data.tag_name;
- if (versionEl) versionEl.textContent = data.tag_name;
- this.updateDownloadButton();
- return data.tag_name;
- } catch (error) {
- console.error('Error al verificar versión:', error);
- if (versionEl) versionEl.textContent = this.currentVersion;
- return null;
- }
- }
-
- updateDownloadButton() {
- const downloadBtn = document.getElementById(this.elements.download);
- const downloadText = document.getElementById(this.elements.text);
- if (!downloadBtn) return;
- const version = this.latestVersion || this.currentVersion;
- const url = `https://github.com/${this.repo}/releases/download/${this.downloadFormat.replace(/\{version\}/g, version)}`;
- downloadBtn.href = url;
- if (downloadText && this.latestVersion && this.latestVersion !== this.currentVersion) {
- downloadText.textContent = `Nueva versión (${this.latestVersion})`;
- }
- }
-}
-
-class AndroidVersionHandler extends BaseVersionHandler {
- constructor(config) {
- super(config);
- this.initDialogs();
- }
-
- initDialogs() {
- const changelogDialog = document.getElementById(this.elements.changelog);
- const versionsDialog = document.getElementById(this.elements.versions);
-
- if (changelogDialog) {
- window.changelog = {
- show: () => { changelogDialog.showModal(); this.loadChangelog(); },
- close: () => changelogDialog.close()
- };
- }
-
- if (versionsDialog) {
- window.versions = {
- show: () => { versionsDialog.showModal(); this.loadVersions(); },
- close: () => versionsDialog.close()
- };
- }
- }
-
- async loadChangelog() {
- const content = document.getElementById(this.elements.changelogContent);
- if (!content) return;
- content.innerHTML = '';
- try {
- const response = await fetch(`${APP_CONFIG.urls.api.github}${this.repo}/releases/latest`);
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const data = await response.json();
- if (!data.body) throw new Error('Sin notas de versión');
- const date = new Date(data.published_at).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' });
- content.innerHTML = `
-
-
${date}
-
${marked.parse(data.body)}
-
`;
- } catch (error) {
- content.innerHTML = `Error: ${error.message}
`;
- }
- }
-
- async loadVersions() {
- const list = document.getElementById(this.elements.versionsList);
- if (!list) return;
- list.innerHTML = '';
- try {
- const response = await fetch(`${APP_CONFIG.urls.api.github}${this.repo}/releases`);
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const releases = await response.json();
- list.innerHTML = releases.map((release, i) => {
- const type = release.prerelease ? 'beta' : (release.tag_name.toLowerCase().includes('alpha') ? 'alpha' : 'stable');
- const label = type === 'stable' ? 'Estable' : (type === 'beta' ? 'Beta' : 'Alpha');
- const date = new Date(release.published_at).toLocaleDateString('es-ES', { year: 'numeric', month: 'long', day: 'numeric' });
- const downloadUrl = release.assets[0]?.browser_download_url || '#';
- return `
- `;
- }).join('');
- } catch (error) {
- list.innerHTML = `Error: ${error.message}
`;
- }
- }
-}
-
-class WindowsVersionHandler extends BaseVersionHandler {
- constructor(config) {
- super(config);
- this.initDialogs();
- }
-
- initDialogs() {
- const changelogDialog = document.getElementById(this.elements.changelog);
- const versionsDialog = document.getElementById(this.elements.versions);
-
- if (changelogDialog) {
- window.showChangelogWindows = () => { changelogDialog.showModal(); this.loadChangelog(); };
- window.closeChangelogWindows = () => changelogDialog.close();
- }
- if (versionsDialog) {
- window.showVersionsWindows = () => { versionsDialog.showModal(); this.loadVersions(); };
- window.closeVersionsWindows = () => versionsDialog.close();
- }
- }
-
- async loadChangelog() {
- const content = document.getElementById(this.elements.changelogContent);
- if (!content) return;
- content.innerHTML = '';
- try {
- const response = await fetch(`${APP_CONFIG.urls.api.github}${this.repo}/releases/latest`);
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const data = await response.json();
- content.innerHTML = `${marked.parse(data.body || 'Sin notas disponibles.')}
`;
- } catch (error) {
- content.innerHTML = `Error: ${error.message}
`;
- }
- }
-
- async loadVersions() {
- const list = document.getElementById(this.elements.versionsList);
- if (!list) return;
- list.innerHTML = '';
- try {
- const response = await fetch(`${APP_CONFIG.urls.api.github}${this.repo}/releases`);
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
- const releases = await response.json();
- list.innerHTML = releases.map(release => `
- `).join('');
- } catch (error) {
- list.innerHTML = `Error: ${error.message}
`;
- }
- }
-}
-
-class CarouselManager {
- constructor() {
- this.images = document.querySelectorAll('.carousel-image');
- this.popupOverlay = document.getElementById('popupOverlay');
- this.popupImage = document.getElementById('popupImage');
- this.startX = 0;
- }
-
- init() {
- if (!this.images.length || !this.popupOverlay) return;
- this.images.forEach(img => {
- img.addEventListener('click', e => {
- this.popupImage.src = e.target.src;
- this.popupOverlay.style.display = 'flex';
- });
- });
- this.popupOverlay.addEventListener('click', e => {
- if (e.target === this.popupOverlay) this.popupOverlay.style.display = 'none';
- });
- const closeBtn = document.getElementById('closePopup');
- if (closeBtn) closeBtn.addEventListener('click', () => { this.popupOverlay.style.display = 'none'; });
- this.popupOverlay.addEventListener('touchstart', e => { this.startX = e.touches[0].clientX; });
- this.popupOverlay.addEventListener('touchend', e => {
- const diff = this.startX - e.changedTouches[0].clientX;
- if (Math.abs(diff) > 50) diff > 0 ? this.nextImage() : this.prevImage();
- });
- }
-
- nextImage() {
- const idx = Array.from(this.images).findIndex(img => img.src === this.popupImage.src);
- this.popupImage.src = this.images[(idx + 1) % this.images.length].src;
- }
-
- prevImage() {
- const idx = Array.from(this.images).findIndex(img => img.src === this.popupImage.src);
- this.popupImage.src = this.images[(idx - 1 + this.images.length) % this.images.length].src;
- }
-}
-
-document.addEventListener('DOMContentLoaded', () => {
- const app = new OpenTuneApp(APP_CONFIG);
- app.init();
-
- if (document.querySelectorAll('.carousel-image').length && document.getElementById('popupOverlay')) {
- app.carouselManager = new CarouselManager();
- app.carouselManager.init();
- }
-});
-
-
-// Mostrar versión en el badge OSS
-(async function updateOssVersion() {
- const badge = document.getElementById('oss-version-badge');
- if (!badge) return;
-
- try {
- const response = await fetch('https://api.github.com/repos/Arturo254/OpenTune/releases/latest');
- const data = await response.json();
- badge.textContent = data.tag_name || 'Error';
- } catch (error) {
- console.error('Error al cargar versión:', error);
- badge.textContent = 'Error';
- }
-})();
-
-
-// ============================================
-// CONFIGURACIÓN DE DIÁLOGOS ANDROID
-// ============================================
-document.addEventListener('DOMContentLoaded', function () {
- // Elementos de la tarjeta Android
- const changelogTrigger = document.getElementById('changelog-trigger');
- const versionsTrigger = document.getElementById('versions-trigger');
- const changelogDialog = document.getElementById('changelog-dialog');
- const versionsDialog = document.getElementById('versions-dialog');
- const closeChangelogBtn = document.getElementById('close-changelog-btn');
- const closeVersionsBtn = document.getElementById('close-versions-btn');
-
- // Función para abrir changelog
- if (changelogTrigger && changelogDialog) {
- changelogTrigger.addEventListener('click', function () {
- changelogDialog.showModal();
- // Cargar el changelog automáticamente
- loadChangelogContent();
- });
- }
-
- // Función para abrir versiones
- if (versionsTrigger && versionsDialog) {
- versionsTrigger.addEventListener('click', function () {
- versionsDialog.showModal();
- // Cargar las versiones automáticamente
- loadVersionsList();
- });
- }
-
- // Cerrar diálogos con los botones X
- if (closeChangelogBtn && changelogDialog) {
- closeChangelogBtn.addEventListener('click', function () {
- changelogDialog.close();
- });
- }
-
- if (closeVersionsBtn && versionsDialog) {
- closeVersionsBtn.addEventListener('click', function () {
- versionsDialog.close();
- });
- }
-
- // Cerrar diálogo haciendo clic fuera (opcional)
- if (changelogDialog) {
- changelogDialog.addEventListener('click', function (e) {
- if (e.target === changelogDialog) {
- changelogDialog.close();
- }
- });
- }
-
- if (versionsDialog) {
- versionsDialog.addEventListener('click', function (e) {
- if (e.target === versionsDialog) {
- versionsDialog.close();
- }
- });
- }
-});
-
-// Función para cargar el changelog
-async function loadChangelogContent() {
- const content = document.getElementById('changelog-content');
- if (!content) return;
-
- content.innerHTML = '';
-
- try {
- const response = await fetch('https://api.github.com/repos/Arturo254/OpenTune/releases/latest');
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
-
- const data = await response.json();
- const date = new Date(data.published_at).toLocaleDateString('es-ES', {
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- });
-
- // Verificar si marked está disponible
- const markdownContent = typeof marked !== 'undefined' ? marked.parse(data.body || 'Sin notas disponibles.') : data.body || 'Sin notas disponibles.';
-
- content.innerHTML = `
-
- 📅 ${date}
-
- ${markdownContent}
- `;
- } catch (error) {
- content.innerHTML = `Error al cargar cambios: ${error.message}
`;
- }
-}
-
-// Función para cargar lista de versiones
-async function loadVersionsList() {
- const list = document.getElementById('versions-list');
- if (!list) return;
-
- list.innerHTML = '';
-
- try {
- const response = await fetch('https://api.github.com/repos/Arturo254/OpenTune/releases');
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
-
- const releases = await response.json();
-
- if (releases.length === 0) {
- list.innerHTML = 'No hay versiones disponibles.
';
- return;
- }
-
- list.innerHTML = releases.map((release, index) => {
- const date = new Date(release.published_at).toLocaleDateString('es-ES', {
- year: 'numeric',
- month: 'long',
- day: 'numeric'
- });
-
- const downloadUrl = release.assets[0]?.browser_download_url || '#';
- const isLatest = index === 0;
-
- return `
-
- `;
- }).join('');
-
- } catch (error) {
- list.innerHTML = `Error al cargar versiones: ${error.message}
`;
- }
-}
-
-// Diagnóstico del LanguageManager
-document.addEventListener('DOMContentLoaded', function () {
- const langBtn = document.getElementById('languageSelector');
- const langDialog = document.getElementById('language-dialog');
-
- console.log('Botón idioma:', langBtn);
- console.log('Diálogo idioma:', langDialog);
-
- if (langBtn && langDialog) {
- // Forzar el evento manualmente
- langBtn.addEventListener('click', function (e) {
- e.preventDefault();
- console.log('Botón clickeado - abriendo diálogo');
- langDialog.showModal();
- });
- } else {
- console.error('No se encontraron los elementos de idioma');
- }
-});
-
-// ============================================
-// ACCORDION PARA LA SECCIÓN DE SCREENSHOTS
-// ============================================
-document.addEventListener('DOMContentLoaded', function () {
- const screenshotsContent = document.getElementById('screenshots-content');
- const screenshotsToggle = document.getElementById('screenshots-toggle');
- const screenshotsIcon = document.getElementById('screenshots-icon');
-
- if (screenshotsContent && screenshotsToggle) {
-
- const savedState = localStorage.getItem('screenshotsCollapsed');
-
- if (savedState === 'false') {
- // Si el usuario lo expandió antes, lo abrimos
- screenshotsContent.classList.remove('collapsed');
- if (screenshotsIcon) screenshotsIcon.classList.remove('rotated');
- }
-
-
- screenshotsToggle.addEventListener('click', function () {
- screenshotsContent.classList.toggle('collapsed');
- if (screenshotsIcon) screenshotsIcon.classList.toggle('rotated');
-
- // Guardar estado
- const isCollapsed = screenshotsContent.classList.contains('collapsed');
- localStorage.setItem('screenshotsCollapsed', isCollapsed);
- });
- }
-});
-
-// ============================================
-// OBTENER DATOS REALES DE GITHUB API (VERSIÓN MEJORADA) (Contribuidores.html)
-// ============================================
-const REPO_OWNER = 'Arturo254';
-const REPO_NAME = 'OpenTune';
-
-async function fetchGitHubStats() {
- try {
- // Obtener datos del repositorio
- const repoResponse = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}`);
- if (!repoResponse.ok) throw new Error('Error al obtener repositorio');
- const repoData = await repoResponse.json();
-
- // Stars
- document.querySelector('#stats-stars .font-headline-md').textContent = formatNumber(repoData.stargazers_count);
-
- // Forks
- document.querySelector('#stats-forks .font-headline-md').textContent = formatNumber(repoData.forks_count);
-
- // Issues
- document.querySelector('#stats-issues .font-headline-md').textContent = formatNumber(repoData.open_issues_count);
-
- // Total Commits - usando el endpoint de commits
- await fetchTotalCommits();
-
- } catch (error) {
- console.error('Error:', error);
- document.querySelectorAll('.stats-number').forEach(el => el.textContent = 'Error');
- }
-}
-
-async function fetchTotalCommits() {
- try {
- // Método: obtener la página 1 con per_page=1 y leer el header Link
- const response = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/commits?per_page=1`);
- const linkHeader = response.headers.get('Link');
-
- let totalCommits = 'N/A';
- if (linkHeader) {
- const lastPageMatch = linkHeader.match(/page=(\d+)>; rel="last"/);
- if (lastPageMatch) {
- totalCommits = formatNumber(parseInt(lastPageMatch[1]));
- }
- }
-
- document.querySelector('#stats-commits .font-headline-md').textContent = totalCommits;
- } catch (error) {
- console.error('Error al obtener commits:', error);
- document.querySelector('#stats-commits .font-headline-md').textContent = 'N/A';
- }
-}
-
-function formatNumber(num) {
- if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
- if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
- return num.toString();
-}
-
-// Ejecutar
-document.addEventListener('DOMContentLoaded', fetchGitHubStats);
-
-// ============================================
-// OBTENER DATOS REALES DE GITHUB API
-// ============================================
-
-function formatNumber(num) {
- if (num >= 1000000) return (num / 1000000).toFixed(1) + 'M';
- if (num >= 1000) return (num / 1000).toFixed(1) + 'k';
- return num.toString();
-}
-
-async function fetchGitHubStats() {
- try {
- const response = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}`);
- if (!response.ok) throw new Error('Error al obtener repositorio');
- const data = await response.json();
-
- document.querySelector('#stats-stars .font-headline-md').textContent = formatNumber(data.stargazers_count);
- document.querySelector('#stats-forks .font-headline-md').textContent = formatNumber(data.forks_count);
- document.querySelector('#stats-issues .font-headline-md').textContent = formatNumber(data.open_issues_count);
-
- // Total Commits
- const commitsResponse = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/commits?per_page=1`);
- const linkHeader = commitsResponse.headers.get('Link');
- let totalCommits = 'N/A';
- if (linkHeader) {
- const lastPageMatch = linkHeader.match(/page=(\d+)>; rel="last"/);
- if (lastPageMatch) totalCommits = formatNumber(parseInt(lastPageMatch[1]));
- }
- document.querySelector('#stats-commits .font-headline-md').textContent = totalCommits;
-
- } catch (error) {
- console.error('Error:', error);
- }
-}
-
-function getContributorRole(contributions, login) {
- if (login === 'Arturo254') return 'Lead Developer';
- if (contributions > 100) return 'Core Contributor';
- if (contributions > 30) return 'Major Contributor';
- if (contributions > 10) return 'Contributor';
- return 'Supporter';
-}
-
-async function fetchContributors() {
- const contributorsGrid = document.getElementById('contributors-grid');
- if (!contributorsGrid) return;
-
- try {
- const response = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/contributors?per_page=100`);
- if (!response.ok) throw new Error('Error al obtener contribuidores');
- const contributors = await response.json();
-
- if (contributors.length === 0) return;
-
- contributorsGrid.innerHTML = contributors.map(contributor => {
- const role = getContributorRole(contributor.contributions, contributor.login);
- return `
-
-
-
-
-
${contributor.login}
- ${contributor.contributions} commits
-
-
- ${role}
-
-
-
- open_in_new
-
-
- `;
- }).join('');
-
- } catch (error) {
- console.error('Error al cargar contribuidores:', error);
- }
-}
-
-async function fetchLatestActivity() {
- const activityContainer = document.getElementById('latest-activity');
- if (!activityContainer) return;
-
- try {
- const response = await fetch(`https://api.github.com/repos/${REPO_OWNER}/${REPO_NAME}/commits?per_page=4`);
- if (!response.ok) throw new Error('Error al obtener actividad');
- const commits = await response.json();
-
- activityContainer.innerHTML = commits.map(commit => `
-
-
-
-
${commit.commit.message.split('\n')[0].substring(0, 60)}
-
by ${commit.author?.login || commit.commit.author.name} • ${new Date(commit.commit.author.date).toLocaleDateString()}
-
-
- `).join('');
-
- } catch (error) {
- console.error('Error al cargar actividad:', error);
- activityContainer.innerHTML = 'Error loading activity
';
- }
-}
-
-document.addEventListener('DOMContentLoaded', () => {
- fetchGitHubStats();
- fetchContributors();
- fetchLatestActivity();
-});
-
-
-
-// ============================================
-// FORMULARIO DE CONTACTO - ENVÍO A FORMSPREE
-// ============================================
-
-// Esperar a que el DOM esté listo
-document.addEventListener('DOMContentLoaded', function () {
-
- // Seleccionar el formulario
- const contactForm = document.querySelector('form');
- const submitButton = contactForm?.querySelector('button[type="submit"]');
-
- // Crear elemento para mensaje de éxito
- const successMessage = document.createElement('div');
- successMessage.id = 'successMessage';
- successMessage.style.cssText = `
- display: none;
- text-align: center;
- padding: 40px 20px;
- background: rgba(208, 188, 255, 0.1);
- border-radius: 24px;
- border: 1px solid rgba(208, 188, 255, 0.3);
- margin-top: 20px;
- `;
- successMessage.innerHTML = `
- check_circle
- ¡Mensaje enviado!
- Gracias por contactarnos. Te responderemos pronto.
- Enviar otro mensaje
- `;
-
- // Insertar mensaje de éxito después del formulario
- if (contactForm) {
- contactForm.parentNode.appendChild(successMessage);
- }
-
- // Función para mostrar loading
- function setLoading(isLoading) {
- if (!submitButton) return;
- if (isLoading) {
- submitButton.disabled = true;
- submitButton.style.opacity = '0.7';
- submitButton.style.cursor = 'not-allowed';
- const originalText = submitButton.innerHTML;
- submitButton.setAttribute('data-original-text', originalText);
- submitButton.innerHTML = 'Enviando... hourglass_empty ';
-
- // Agregar animación spin si no existe
- if (!document.querySelector('#spin-style')) {
- const style = document.createElement('style');
- style.id = 'spin-style';
- style.textContent = '@keyframes spin { to { transform: rotate(360deg); } }';
- document.head.appendChild(style);
- }
- } else {
- submitButton.disabled = false;
- submitButton.style.opacity = '1';
- submitButton.style.cursor = 'pointer';
- const originalText = submitButton.getAttribute('data-original-text');
- if (originalText) {
- submitButton.innerHTML = originalText;
- }
- }
- }
-
- // Manejar envío del formulario
- if (contactForm) {
- contactForm.addEventListener('submit', async function (e) {
- e.preventDefault();
-
- // Obtener valores
- const nombreInput = document.querySelector('input[placeholder="Escribe tu nombre aquí"]');
- const emailInput = document.querySelector('input[type="email"]');
- const descripcionTextarea = document.querySelector('textarea');
- const tipoMensajeSelected = document.querySelector('input[name="message_type"]:checked');
-
- const nombre = nombreInput?.value || '';
- const email = emailInput?.value || '';
- const descripcion = descripcionTextarea?.value || '';
- const tipo_mensaje = tipoMensajeSelected?.closest('label')?.querySelector('.font-title-md')?.innerText || 'Comentario';
-
- // Validar
- if (!nombre || !email || !descripcion) {
- alert('Por favor, completa todos los campos.');
- return;
- }
-
- if (!email.includes('@')) {
- alert('Por favor, ingresa un email válido.');
- return;
- }
-
- // Mostrar loading
- setLoading(true);
-
- try {
- const formData = new FormData();
- formData.append('nombre', nombre);
- formData.append('email', email);
- formData.append('tipo_mensaje', tipo_mensaje);
- formData.append('descripcion', descripcion);
-
- const response = await fetch('https://formspree.io/f/xgvallrq', {
- method: 'POST',
- body: formData,
- headers: {
- 'Accept': 'application/json'
- }
- });
-
- if (response.ok) {
- // Ocultar formulario, mostrar éxito
- contactForm.style.display = 'none';
- successMessage.style.display = 'block';
- } else {
- throw new Error('Error en el envío');
- }
- } catch (error) {
- console.error('Error:', error);
- alert('Hubo un error al enviar tu mensaje. Por favor, inténtalo de nuevo.');
- } finally {
- setLoading(false);
- }
- });
- }
-});
-
-
-// ============================================
-// DEMO - DIÁLOGO DE ADVERTENCIA
-// ============================================
-document.addEventListener('DOMContentLoaded', function() {
- // Elementos
- const demoBtn = document.getElementById('hero-demo-btn');
- const warningDialog = document.getElementById('warning-dialog');
- const proceedBtn = document.getElementById('proceedBtn');
- const dismissBtn = document.getElementById('dismissBtn');
-
- // Abrir diálogo al hacer clic en "Probar Demo"
- if (demoBtn && warningDialog) {
- demoBtn.addEventListener('click', function(e) {
- e.preventDefault();
- warningDialog.showModal();
- });
- }
-
- // Ir a la demo al hacer clic en "Continuar"
- if (proceedBtn) {
- proceedBtn.addEventListener('click', function() {
- window.location.href = 'https://appetize.io/app/b_yb62tcjuqzqjvctnswv3krpnmm';
- });
- }
-
- // Cerrar diálogo al hacer clic en "Cancelar"
- if (dismissBtn) {
- dismissBtn.addEventListener('click', function() {
- warningDialog.close();
- });
- }
-
- // Cerrar diálogo al hacer clic fuera de él
- if (warningDialog) {
- warningDialog.addEventListener('click', function(e) {
- if (e.target === warningDialog) {
- warningDialog.close();
- }
- });
- }
-
- // Cerrar con la tecla ESC (por defecto funciona, pero lo aseguramos)
- if (warningDialog) {
- warningDialog.addEventListener('cancel', function(e) {
- warningDialog.close();
- });
- }
-});
\ No newline at end of file
diff --git a/src/app/[locale]/contributors/page.tsx b/src/app/[locale]/contributors/page.tsx
new file mode 100644
index 0000000..6f7ab0d
--- /dev/null
+++ b/src/app/[locale]/contributors/page.tsx
@@ -0,0 +1,44 @@
+import { setRequestLocale, getTranslations } from 'next-intl/server';
+import { routing } from '@/i18n/routing';
+import { type Locale } from '@config/locales';
+import Navbar from '@cmp/layout/Navbar';
+import Footer from '@cmp/layout/Footer';
+import ContributorsClient from '@cmp/sections/ContributorsClient';
+
+export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: 'metadata.contributors' });
+
+ return {
+ title: 'Contributors',
+ description: t('description'),
+ alternates: {
+ canonical: `/${locale}/contributors`,
+ },
+ };
+}
+
+export function generateStaticParams() {
+ return routing.locales.map((locale) => ({ locale }));
+}
+
+export default async function ContributorsPage({
+ params,
+}: {
+ params: Promise<{ locale: string }>;
+}) {
+ const { locale } = await params;
+ setRequestLocale(locale);
+
+ return (
+ <>
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/[locale]/layout.tsx b/src/app/[locale]/layout.tsx
new file mode 100644
index 0000000..b7dfcde
--- /dev/null
+++ b/src/app/[locale]/layout.tsx
@@ -0,0 +1,126 @@
+import { NextIntlClientProvider } from 'next-intl';
+import { getMessages, setRequestLocale, getTranslations } from 'next-intl/server';
+import { notFound } from 'next/navigation';
+import { routing } from '@/i18n/routing';
+import { type Locale } from '@config/locales';
+import Script from 'next/script';
+import { Epilogue, Be_Vietnam_Pro } from 'next/font/google';
+import '@/app/globals.css';
+import { Viewport } from 'next';
+import { DOMAIN, EXTERNAL_LINKS } from '@config/links';
+
+const epilogue = Epilogue({
+ subsets: ['latin'],
+ weight: ['400', '500', '600', '700', '800', '900'],
+ variable: '--font-epilogue-next',
+});
+
+const beVietnamPro = Be_Vietnam_Pro({
+ subsets: ['latin'],
+ weight: ['400', '500', '600'],
+ variable: '--font-be-vietnam-pro-next',
+});
+
+export const viewport: Viewport = {
+ themeColor: '#d0bcff',
+};
+
+export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: 'metadata.default' });
+
+ return {
+ title: {
+ template: `%s | OpenTune`,
+ default: t('title'),
+ },
+ description: t('description'),
+ metadataBase: new URL(DOMAIN),
+ alternates: {
+ canonical: `/${locale}`,
+ languages: Object.fromEntries(routing.locales.map((lang) => [lang, `/${lang}`])),
+ },
+ robots: {
+ index: true,
+ follow: true,
+ googleBot: {
+ index: true,
+ follow: true,
+ },
+ },
+ icons: {
+ icon: [
+ { url: '/icon/favicon.ico', sizes: 'any' },
+ { url: '/icon/icon-192.png', type: 'image/png', sizes: '192x192' },
+ { url: '/icon/icon-512.png', type: 'image/png', sizes: '512x512' },
+ ],
+ apple: [{ url: '/icon/apple-touch-icon.png', sizes: '180x180', type: 'image/png' }],
+ other: [
+ {
+ rel: 'mask-icon',
+ url: '/icon/opentune-black.svg',
+ },
+ ],
+ },
+ openGraph: {
+ type: 'website',
+ siteName: 'OpenTune',
+ title: t('title'),
+ description: t('description'),
+ images: [
+ {
+ url: '/images/og-image.png',
+ width: 1200,
+ height: 630,
+ alt: 'OpenTune - Material Design 3 YouTube Music Client',
+ },
+ ],
+ },
+ twitter: {
+ card: 'summary_large_image',
+ title: t('title'),
+ description: t('description'),
+ images: ['/images/og-image.png'],
+ },
+ };
+}
+
+export function generateStaticParams() {
+ return routing.locales.map((locale) => ({ locale }));
+}
+
+export default async function LocaleLayout({
+ children,
+ params,
+}: {
+ children: React.ReactNode;
+ params: Promise<{ locale: string }>;
+}) {
+ const { locale } = await params;
+
+ if (!routing.locales.includes(locale as Locale)) {
+ notFound();
+ }
+
+ setRequestLocale(locale);
+
+ const messages = await getMessages();
+
+ return (
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
diff --git a/src/app/[locale]/page.tsx b/src/app/[locale]/page.tsx
new file mode 100644
index 0000000..1ed9259
--- /dev/null
+++ b/src/app/[locale]/page.tsx
@@ -0,0 +1,61 @@
+import { setRequestLocale, getTranslations } from 'next-intl/server';
+import { routing } from '@/i18n/routing';
+import { type Locale } from '@config/locales';
+import Navbar from '@cmp/layout/Navbar';
+import Footer from '@cmp/layout/Footer';
+import Hero from '@cmp/sections/Hero';
+import Features from '@cmp/sections/Features';
+import OpenSource from '@cmp/sections/OpenSource';
+import Screenshots from '@cmp/sections/Screenshots';
+import SupportCTA from '@cmp/sections/SupportCTA';
+import Downloads from '@cmp/sections/Downloads';
+import { fetchLatestRelease, buildDownloadUrl, REPOS } from '@lib/github';
+
+export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: 'metadata.home' });
+
+ return {
+ title: t('title'),
+ description: t('description'),
+ alternates: {
+ canonical: `/${locale}`,
+ },
+ };
+}
+
+export function generateStaticParams() {
+ return routing.locales.map((locale) => ({ locale }));
+}
+
+export default async function HomePage({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ setRequestLocale(locale);
+
+ let release;
+ try {
+ release = await fetchLatestRelease(REPOS.android);
+ } catch (error) {
+ console.error('Failed to fetch latest release:', error);
+ release = null;
+ }
+ const version = release?.tag_name ?? 'latest';
+ const downloadUrl = version
+ ? buildDownloadUrl(REPOS.android, version)
+ : buildDownloadUrl(REPOS.android, 'latest');
+
+ return (
+ <>
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/[locale]/support/page.tsx b/src/app/[locale]/support/page.tsx
new file mode 100644
index 0000000..2eb503d
--- /dev/null
+++ b/src/app/[locale]/support/page.tsx
@@ -0,0 +1,38 @@
+import { setRequestLocale, getTranslations } from 'next-intl/server';
+import { routing } from '@/i18n/routing';
+import { type Locale } from '@config/locales';
+import Navbar from '@cmp/layout/Navbar';
+import Footer from '@cmp/layout/Footer';
+import SupportClient from '@cmp/sections/SupportClient';
+
+export async function generateMetadata({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ const t = await getTranslations({ locale, namespace: 'metadata.support' });
+
+ return {
+ title: t('title'),
+ description: t('description'),
+ alternates: {
+ canonical: `/${locale}/support`,
+ },
+ };
+}
+
+export function generateStaticParams() {
+ return routing.locales.map((locale) => ({ locale }));
+}
+
+export default async function SupportPage({ params }: { params: Promise<{ locale: string }> }) {
+ const { locale } = await params;
+ setRequestLocale(locale);
+
+ return (
+ <>
+
+
+
+
+
+ >
+ );
+}
diff --git a/src/app/api/github/releases/latest/route.ts b/src/app/api/github/releases/latest/route.ts
new file mode 100644
index 0000000..40a204b
--- /dev/null
+++ b/src/app/api/github/releases/latest/route.ts
@@ -0,0 +1,28 @@
+import { NextResponse } from 'next/server';
+import { EXTERNAL_LINKS } from '@config/links';
+import { REPOS, fetchWithTimeout } from '@lib/github';
+
+export async function GET() {
+ const token = process.env.GITHUB_TOKEN;
+ const headers: HeadersInit = {};
+ if (token) {
+ headers['Authorization'] = `token ${token}`;
+ }
+
+ try {
+ const res = await fetchWithTimeout(`${EXTERNAL_LINKS.GITHUB_API}/${REPOS.android}/releases/latest`, {
+ headers,
+ next: { revalidate: 3600 },
+ timeoutMs: 5000,
+ });
+
+ if (!res.ok) {
+ return NextResponse.json({ error: 'Failed to fetch from GitHub' }, { status: res.status });
+ }
+
+ const data = await res.json();
+ return NextResponse.json(data);
+ } catch ($error) {
+ return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
+ }
+}
diff --git a/src/app/api/github/releases/route.ts b/src/app/api/github/releases/route.ts
new file mode 100644
index 0000000..1f5e726
--- /dev/null
+++ b/src/app/api/github/releases/route.ts
@@ -0,0 +1,33 @@
+import { NextResponse } from 'next/server';
+import { EXTERNAL_LINKS } from '@config/links';
+import { REPOS, fetchWithTimeout } from '@lib/github';
+
+export async function GET(request: Request) {
+ const { searchParams } = new URL(request.url);
+ const rawPerPage = searchParams.get('per_page');
+ const parsedPerPage = parseInt(rawPerPage || '20', 10);
+ const perPage = isNaN(parsedPerPage) ? 20 : Math.min(Math.max(parsedPerPage, 1), 100);
+
+ const token = process.env.GITHUB_TOKEN;
+ const headers: HeadersInit = {};
+ if (token) {
+ headers['Authorization'] = `token ${token}`;
+ }
+
+ try {
+ const res = await fetchWithTimeout(`${EXTERNAL_LINKS.GITHUB_API}/${REPOS.android}/releases?per_page=${perPage}`, {
+ headers,
+ next: { revalidate: 3600 },
+ timeoutMs: 5000,
+ });
+
+ if (!res.ok) {
+ return NextResponse.json({ error: 'Failed to fetch from GitHub' }, { status: res.status });
+ }
+
+ const data = await res.json();
+ return NextResponse.json(data);
+ } catch ($error) {
+ return NextResponse.json({ error: 'Internal Server Error' }, { status: 500 });
+ }
+}
diff --git a/src/app/globals.css b/src/app/globals.css
new file mode 100644
index 0000000..228994c
--- /dev/null
+++ b/src/app/globals.css
@@ -0,0 +1,588 @@
+@import 'tailwindcss';
+
+/* ─── MD3 Design Tokens ─── */
+@theme {
+ --color-primary: #e9ddff;
+ --color-on-primary: #37265e;
+ --color-primary-container: #d0bcff;
+ --color-on-primary-container: #594983;
+ --color-primary-fixed: #e9ddff;
+ --color-primary-fixed-dim: #d0bcff;
+ --color-on-primary-fixed: #210f48;
+ --color-on-primary-fixed-variant: #4d3d76;
+ --color-inverse-primary: #665590;
+
+ --color-secondary: #ccc2dc;
+ --color-on-secondary: #332d41;
+ --color-secondary-container: #4a4359;
+ --color-on-secondary-container: #bab1ca;
+ --color-secondary-fixed: #e9def9;
+ --color-secondary-fixed-dim: #ccc2dc;
+ --color-on-secondary-fixed: #1e192c;
+ --color-on-secondary-fixed-variant: #4a4459;
+
+ --color-tertiary: #efb8c8;
+ --color-on-tertiary: #492532;
+ --color-tertiary-container: #633b48;
+ --color-on-tertiary-container: #ffd9e3;
+ --color-tertiary-fixed: #ffd9e3;
+ --color-tertiary-fixed-dim: #efb8c8;
+ --color-on-tertiary-fixed: #31111d;
+ --color-on-tertiary-fixed-variant: #633b49;
+
+ --color-error: #ffb4ab;
+ --color-on-error: #690005;
+ --color-error-container: #93000a;
+ --color-on-error-container: #ffdad6;
+
+ --color-background: #0e0e11;
+ --color-on-background: #e5e1e7;
+ --color-surface: #141317;
+ --color-on-surface: #e5e1e7;
+ --color-surface-variant: #49454f;
+ --color-on-surface-variant: #cac4d0;
+ --color-outline: #948f9a;
+ --color-outline-variant: #49454f;
+
+ --font-epilogue: var(--font-epilogue-next);
+ --font-be-vietnam-pro: var(--font-be-vietnam-pro-next);
+}
+
+@layer base {
+ html {
+ scroll-behavior: smooth;
+ }
+ body {
+ background-color: var(--color-background);
+ color: #e5e1e7;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ -webkit-font-smoothing: antialiased;
+ -moz-osx-font-smoothing: grayscale;
+ }
+}
+
+/* ─── Shared Components ─── */
+.glass-card {
+ background: rgba(43, 41, 45, 0.4);
+ backdrop-filter: blur(24px);
+ -webkit-backdrop-filter: blur(24px);
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
+ transition: all 0.3s cubic-bezier(0.2, 0, 0, 1);
+}
+
+.feature-icon-wrap {
+ width: 48px;
+ height: 48px;
+ border-radius: 16px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ margin-bottom: 20px;
+}
+
+/* ─── Chips ─── */
+.version-chip {
+ display: inline-flex;
+ align-items: center;
+ padding: 4px 12px;
+ border-radius: 9999px;
+ background-color: rgba(43, 41, 45, 0.8);
+ color: #cac4d0;
+ font-size: 11px;
+ font-weight: 500;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ line-height: 16px;
+}
+.version-chip--stable {
+ background-color: rgba(239, 184, 200, 0.18);
+ color: #ffd9e3;
+}
+.version-chip--beta {
+ background-color: rgba(74, 67, 89, 0.6);
+ color: #ccc2dc;
+}
+.version-chip--alpha {
+ background-color: rgba(208, 188, 255, 0.15);
+ color: #e9ddff;
+}
+.version-chip--latest {
+ background-color: rgba(208, 188, 255, 0.18);
+ color: #d0bcff;
+}
+
+/* ─── Footer Chips ─── */
+.footer-chip {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ padding: 8px 16px;
+ border-radius: 9999px;
+ border: 1px solid rgba(73, 69, 79, 0.5);
+ color: #948f9a;
+ font-size: 13px;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ font-weight: 500;
+ transition: all 0.2s cubic-bezier(0.2, 0, 0, 1);
+ text-decoration: none;
+}
+.footer-chip:hover {
+ border-color: rgba(208, 188, 255, 0.4);
+ color: #d0bcff;
+ background: rgba(208, 188, 255, 0.07);
+ transform: translateY(-1px);
+}
+
+/* ─── Developer Chip ─── */
+.developer-chip {
+ animation: shine 3.5s ease-in-out infinite;
+}
+@keyframes shine {
+ 0%,
+ 100% {
+ opacity: 1;
+ transform: scale(1);
+ }
+ 50% {
+ opacity: 0.8;
+ transform: scale(1.04);
+ }
+}
+
+/* ─── Glass Dialogs ─── */
+.glass-dialog {
+ position: fixed;
+ inset: 0;
+ margin: auto;
+ width: min(90vw, 480px);
+ max-height: 85vh;
+ border-radius: 2rem;
+ background: rgba(18, 17, 22, 0.88);
+ backdrop-filter: blur(32px);
+ -webkit-backdrop-filter: blur(32px);
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ color: #e5e1e7;
+ overflow: hidden;
+ padding: 0;
+ box-shadow:
+ 0 30px 70px rgba(0, 0, 0, 0.55),
+ 0 0 0 1px rgba(208, 188, 255, 0.06),
+ inset 0 1px 0 rgba(255, 255, 255, 0.05);
+}
+.glass-dialog--large {
+ width: min(90vw, 600px);
+}
+.glass-dialog::backdrop {
+ background: rgba(0, 0, 0, 0.72);
+ backdrop-filter: blur(10px);
+ -webkit-backdrop-filter: blur(10px);
+}
+.dialog-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 24px 24px 0;
+ gap: 12px;
+}
+.dialog-content {
+ padding: 20px 24px;
+}
+.dialog-content--scroll {
+ overflow-y: auto;
+ max-height: 58vh;
+}
+.dialog-content--scroll::-webkit-scrollbar {
+ width: 4px;
+}
+.dialog-content--scroll::-webkit-scrollbar-track {
+ background: transparent;
+}
+.dialog-content--scroll::-webkit-scrollbar-thumb {
+ background: rgba(208, 188, 255, 0.25);
+ border-radius: 2px;
+}
+.dialog-actions {
+ display: flex;
+ justify-content: flex-end;
+ gap: 8px;
+ padding: 0 24px 24px;
+}
+
+.dialog-icon-btn {
+ width: 36px;
+ height: 36px;
+ border: none;
+ border-radius: 9999px;
+ background: transparent;
+ color: #cac4d0;
+ cursor: pointer;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: background 0.2s;
+ flex-shrink: 0;
+}
+.dialog-icon-btn:hover {
+ background: rgba(255, 255, 255, 0.08);
+}
+.dialog-text-btn {
+ background: transparent;
+ border: none;
+ color: #d0bcff;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 10px 20px;
+ border-radius: 9999px;
+ cursor: pointer;
+ transition: background 0.2s;
+}
+.dialog-text-btn:hover {
+ background: rgba(208, 188, 255, 0.1);
+}
+.dialog-filled-btn {
+ background: #d0bcff;
+ border: none;
+ color: #37265e;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ font-size: 14px;
+ font-weight: 500;
+ padding: 10px 24px;
+ border-radius: 9999px;
+ cursor: pointer;
+ transition: all 0.2s;
+}
+.dialog-filled-btn:hover {
+ filter: brightness(1.1);
+}
+.dialog-filled-btn:active {
+ transform: scale(0.96);
+}
+
+.dialog-list-item {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+ padding: 12px 16px;
+ border-radius: 1rem;
+ color: #e5e1e7;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ transition: background 0.2s;
+ cursor: pointer;
+ text-decoration: none;
+}
+.dialog-list-item:hover {
+ background: rgba(255, 255, 255, 0.06);
+}
+.flag-icon {
+ width: 24px;
+ height: 18px;
+ border-radius: 3px;
+ object-fit: cover;
+ flex-shrink: 0;
+}
+
+/* ─── Loading ─── */
+.loading-indicator {
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 56px 40px;
+}
+.circular-progress {
+ width: 36px;
+ height: 36px;
+ border: 3px solid rgba(208, 188, 255, 0.18);
+ border-top: 3px solid #d0bcff;
+ border-radius: 50%;
+ animation: spin 0.85s linear infinite;
+}
+@keyframes spin {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* ─── Markdown Body ─── */
+.markdown-body h1,
+.markdown-body h2,
+.markdown-body h3 {
+ color: #e5e1e7;
+ font-family: var(--font-epilogue), sans-serif;
+ margin: 20px 0 10px;
+}
+.markdown-body h1 {
+ font-size: 1.4rem;
+ font-weight: 700;
+}
+.markdown-body h2 {
+ font-size: 1.15rem;
+ font-weight: 600;
+}
+.markdown-body h3 {
+ font-size: 1rem;
+ font-weight: 600;
+}
+.markdown-body p {
+ color: #cac4d0;
+ line-height: 1.75;
+ margin: 8px 0;
+}
+.markdown-body ul,
+.markdown-body ol {
+ color: #cac4d0;
+ padding-left: 22px;
+ margin: 8px 0;
+ line-height: 1.75;
+}
+.markdown-body li {
+ margin: 4px 0;
+}
+.markdown-body code {
+ background: rgba(208, 188, 255, 0.12);
+ color: #d0bcff;
+ padding: 2px 8px;
+ border-radius: 6px;
+ font-size: 0.82em;
+ font-family: ui-monospace, 'Courier New', monospace;
+}
+.markdown-body pre {
+ background: rgba(14, 14, 17, 0.7);
+ border: 1px solid rgba(73, 69, 79, 0.4);
+ border-radius: 12px;
+ padding: 16px;
+ overflow-x: auto;
+ margin: 12px 0;
+}
+.markdown-body pre code {
+ background: transparent;
+ padding: 0;
+ color: #e5e1e7;
+}
+.markdown-body a {
+ color: #d0bcff;
+ text-decoration: underline;
+ text-underline-offset: 3px;
+}
+.markdown-body a:hover {
+ color: #e9ddff;
+}
+.markdown-body hr {
+ border: none;
+ border-top: 1px solid rgba(73, 69, 79, 0.5);
+ margin: 20px 0;
+}
+.changelog-meta {
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ margin-bottom: 20px;
+ padding-bottom: 16px;
+ border-bottom: 1px solid rgba(73, 69, 79, 0.4);
+}
+.changelog-date {
+ font-size: 13px;
+ color: #948f9a;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+}
+
+/* ─── Version List ─── */
+.version-list-item {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 14px 0;
+ gap: 12px;
+ border-bottom: 1px solid rgba(73, 69, 79, 0.3);
+}
+.version-list-item:last-child {
+ border-bottom: none;
+}
+.version-list-info {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+ flex: 1;
+ min-width: 0;
+}
+.version-meta-row {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex-wrap: wrap;
+}
+.version-tag {
+ font-size: 15px;
+ font-weight: 500;
+ color: #e5e1e7;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+}
+.version-date {
+ font-size: 12px;
+ color: #948f9a;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+}
+.version-dl-btn {
+ display: inline-flex;
+ align-items: center;
+ gap: 6px;
+ background: rgba(43, 41, 45, 0.8);
+ color: #e5e1e7;
+ border: 1px solid rgba(73, 69, 79, 0.4);
+ padding: 8px 16px;
+ border-radius: 9999px;
+ font-size: 13px;
+ font-weight: 500;
+ font-family: var(--font-be-vietnam-pro), sans-serif;
+ text-decoration: none;
+ transition: all 0.2s;
+ flex-shrink: 0;
+ white-space: nowrap;
+}
+.version-dl-btn:hover {
+ background: rgba(53, 52, 56, 0.9);
+ border-color: rgba(208, 188, 255, 0.3);
+ color: #d0bcff;
+}
+
+/* ─── Disabled Card ─── */
+.disabled-card {
+ position: relative;
+ overflow: hidden;
+ cursor: not-allowed;
+ user-select: none;
+}
+.disabled-card::before {
+ content: '';
+ position: absolute;
+ inset: 0;
+ background: repeating-linear-gradient(
+ -45deg,
+ transparent,
+ transparent 14px,
+ rgba(255, 255, 255, 0.018) 14px,
+ rgba(255, 255, 255, 0.018) 28px
+ );
+ pointer-events: none;
+ z-index: 0;
+}
+.disabled-card__inner {
+ position: relative;
+ z-index: 1;
+ opacity: 0.5;
+ pointer-events: none;
+}
+
+/* ─── Screenshots Accordion ─── */
+#screenshots-content {
+ max-height: 2000px;
+ opacity: 1;
+ overflow: hidden;
+ transition:
+ max-height 0.4s cubic-bezier(0.4, 0, 0.2, 1),
+ opacity 0.3s ease,
+ margin 0.3s ease;
+}
+#screenshots-content.collapsed {
+ max-height: 0 !important;
+ opacity: 0;
+ margin: 0;
+ padding: 0;
+}
+#screenshots-icon {
+ transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1);
+}
+#screenshots-icon.rotated {
+ transform: rotate(180deg);
+}
+
+/* ─── Scrollbar ─── */
+::-webkit-scrollbar {
+ width: 6px;
+}
+::-webkit-scrollbar-track {
+ background: transparent;
+}
+::-webkit-scrollbar-thumb {
+ background: rgba(208, 188, 255, 0.2);
+ border-radius: 3px;
+}
+::-webkit-scrollbar-thumb:hover {
+ background: rgba(208, 188, 255, 0.38);
+}
+
+/* ─── Focus ─── */
+:focus-visible {
+ outline: 2px solid #d0bcff;
+ outline-offset: 2px;
+ border-radius: 4px;
+}
+
+/* ─── Reduced Motion ─── */
+@media (prefers-reduced-motion: reduce) {
+ *,
+ ::before,
+ ::after {
+ animation-duration: 0.01ms !important;
+ transition-duration: 0.01ms !important;
+ }
+}
+
+/* ─── High Contrast ─── */
+@media (prefers-contrast: high) {
+ .glass-card {
+ border-width: 2px;
+ border-color: rgba(255, 255, 255, 0.25);
+ }
+ .glass-dialog {
+ border-width: 2px;
+ }
+}
+
+/* ─── Mobile ─── */
+@media (max-width: 640px) {
+ .glass-dialog {
+ width: 92vw;
+ border-radius: 1.5rem;
+ max-height: 88vh;
+ }
+ .dialog-header {
+ padding: 20px 18px 0;
+ }
+ .dialog-content {
+ padding: 16px 18px;
+ }
+ .dialog-actions {
+ padding: 0 18px 20px;
+ }
+}
+@media (max-width: 480px) {
+ .version-list-item {
+ flex-direction: column;
+ align-items: flex-start;
+ }
+ .version-dl-btn {
+ width: 100%;
+ justify-content: center;
+ }
+}
+
+/* ─── Skeleton shimmer ─── */
+.skeleton-shimmer {
+ background: linear-gradient(
+ 90deg,
+ rgba(255, 255, 255, 0.05) 0%,
+ rgba(255, 255, 255, 0.1) 50%,
+ rgba(255, 255, 255, 0.05) 100%
+ );
+ background-size: 200% 100%;
+ animation: shimmer 1.5s ease-in-out infinite;
+}
+@keyframes shimmer {
+ 0% {
+ background-position: 200% 0;
+ }
+ 100% {
+ background-position: -200% 0;
+ }
+}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 0000000..49578da
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,3 @@
+export default function RootLayout({ children }: { children: React.ReactNode }) {
+ return children;
+}
diff --git a/src/app/not-found.tsx b/src/app/not-found.tsx
new file mode 100644
index 0000000..3435d05
--- /dev/null
+++ b/src/app/not-found.tsx
@@ -0,0 +1,63 @@
+import Link from 'next/link';
+import Image from 'next/image';
+import { AlertCircle, Home, ExternalLink } from '@icons';
+import { EXTERNAL_LINKS, PATHS } from '@config/links';
+import '@/app/globals.css';
+
+export default function NotFound() {
+ return (
+
+
+ {/* Text content */}
+
+
+
+ 404
+
+
+ Page not found
+
+
+ Whoops! The page you're looking for doesn't exist or has been moved.
+
+
+
+
+ {/* Illustration */}
+
+
+
+ );
+}
diff --git a/src/app/page.tsx b/src/app/page.tsx
new file mode 100644
index 0000000..677c094
--- /dev/null
+++ b/src/app/page.tsx
@@ -0,0 +1,6 @@
+import { redirect } from 'next/navigation';
+import { defaultLocale } from '@config/locales';
+
+export default function RootPage() {
+ redirect(`/${defaultLocale}`);
+}
diff --git a/src/app/robots.ts b/src/app/robots.ts
new file mode 100644
index 0000000..67da107
--- /dev/null
+++ b/src/app/robots.ts
@@ -0,0 +1,12 @@
+import { MetadataRoute } from 'next';
+import { DOMAIN } from '@config/links';
+
+export default function robots(): MetadataRoute.Robots {
+ return {
+ rules: {
+ userAgent: '*',
+ allow: '/',
+ },
+ sitemap: `${DOMAIN}/sitemap.xml`,
+ };
+}
diff --git a/src/app/sitemap.ts b/src/app/sitemap.ts
new file mode 100644
index 0000000..88f6a2e
--- /dev/null
+++ b/src/app/sitemap.ts
@@ -0,0 +1,34 @@
+import { MetadataRoute } from 'next';
+import { locales, defaultLocale } from '@config/locales';
+import { DOMAIN, PATHS } from '@config/links';
+
+export default function sitemap(): MetadataRoute.Sitemap {
+ const baseUrl = DOMAIN;
+ const routes = ['', PATHS.CONTRIBUTORS, PATHS.SUPPORT];
+
+ const sitemapEntries: MetadataRoute.Sitemap = [];
+
+ for (const route of routes) {
+ const languages: Record = {};
+ locales.forEach((l) => {
+ languages[l] = `${baseUrl}/${l}${route}`;
+ });
+ languages['x-default'] = `${baseUrl}/${defaultLocale}${route}`;
+
+ for (const locale of locales) {
+ const url = `${baseUrl}/${locale}${route}`;
+
+ sitemapEntries.push({
+ url,
+ lastModified: new Date(),
+ changeFrequency: 'weekly',
+ priority: route === '' ? 1.0 : 0.8,
+ alternates: {
+ languages,
+ },
+ });
+ }
+ }
+
+ return sitemapEntries;
+}
diff --git a/src/app/~test/icon/page.tsx b/src/app/~test/icon/page.tsx
new file mode 100644
index 0000000..9d33d4e
--- /dev/null
+++ b/src/app/~test/icon/page.tsx
@@ -0,0 +1,178 @@
+import { Android as Icon, MulOpentune as MulIcon } from '@icons';
+
+const name = 'Android';
+const mulName = 'MulOpentune';
+
+export default function IconTestPage() {
+ return (
+
+
+
+
+
+ {/* 1 — size= Prop */}
+
+
size= Prop
+
+ {[16, 24, 32, 48, 64].map((s) => (
+
+
+ {s}px
+
+ ))}
+
+
+
+ {/* 2 — Tailwind size-* Classes */}
+
+
Tailwind size-* Classes
+
+ {(['size-4', 'size-5', 'size-6', 'size-8', 'size-10'] as string[]).map((cls) => (
+
+ ))}
+
+
+
+ {/* 3 — Tailwind Colors via CSS vars */}
+
+
Tailwind Colors
+
+ {(
+ [
+ ['var(--color-red-400)', 'red-400'],
+ ['var(--color-green-400)', 'green-400'],
+ ['var(--color-blue-400)', 'blue-400'],
+ ['var(--color-purple-400)', 'purple'],
+ ['var(--color-amber-400)', 'amber-400'],
+ ['var(--color-cyan-400)', 'cyan-400'],
+ ] as [string, string][]
+ ).map(([token, label]) => (
+
+
+ {label}
+
+ ))}
+
+
+
+ {/* 4 — color= Prop */}
+
+
color= Prop
+
+ {(['#FF8C00', '#6366F1', '#F72585', '#FF00FF', '#00F5D4', '#84CC16'] as string[]).map(
+ (hex) => (
+
+
+ {hex}
+
+ ),
+ )}
+
+
+
+ {/* 5 — opacity via CSS */}
+
+
CSS opacity
+
+ {(
+ [
+ [1, '100%'],
+ [0.75, '75%'],
+ [0.5, '50%'],
+ [0.25, '25%'],
+ ] as [number, string][]
+ ).map(([opacity, label]) => (
+
+ ))}
+
+
+
+ {/* 6 — Theme Tokens */}
+
+
Theme Tokens
+
+ {(
+ [
+ ['var(--color-primary)', 'primary'],
+ ['var(--color-inverse-primary)', 'inverse'],
+ ['var(--color-secondary)', 'secndry'],
+ ['var(--color-tertiary)', 'tertiary'],
+ ['var(--color-error)', 'error'],
+ ['var(--color-error-container)', 'errorCont.'],
+ ] as [string, string][]
+ ).map(([token, label]) => (
+
+
+ {label}
+
+ ))}
+
+
+
+
+ {/* Default vs Mul */}
+
+
+ {name} vs {mulName}
+
+
+
+
+
+
+ {name}
+
+
+ Theme-aware · follows currentColor
+
+
+
+
+
+
+
+
+ {mulName}
+
+
+ Original artwork
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/app/~test/layout.tsx b/src/app/~test/layout.tsx
new file mode 100644
index 0000000..ad2e175
--- /dev/null
+++ b/src/app/~test/layout.tsx
@@ -0,0 +1,6 @@
+import '@test/style.css';
+import type { ReactNode } from 'react';
+
+export default function TestLayout({ children }: { children: ReactNode }) {
+ return <>{children}>;
+}
diff --git a/src/app/~test/style.css b/src/app/~test/style.css
new file mode 100644
index 0000000..4090351
--- /dev/null
+++ b/src/app/~test/style.css
@@ -0,0 +1,63 @@
+@import '../globals.css';
+/* ─── Test Environment Styles ─── */
+/* Scoped to ~test routes only. Never import this in globals.css. */
+
+.test-page {
+ min-height: 100vh;
+ background-color: var(--color-surface-container-lowest);
+ padding: 2rem 1rem;
+}
+
+.test-section {
+ background-color: var(--color-surface-container);
+ border: 1px solid var(--color-outline-variant);
+ border-radius: var(--radius);
+ padding: 1.25rem;
+}
+
+.test-section-title {
+ font-size: 0.75rem;
+ font-weight: 600;
+ letter-spacing: 0.08em;
+ text-transform: uppercase;
+ color: var(--color-on-surface-variant);
+ margin-bottom: 1rem;
+ font-family: ui-monospace, 'Courier New', monospace;
+}
+
+.test-label {
+ font-family: ui-monospace, 'Courier New', monospace;
+ font-size: 0.65rem;
+ color: var(--color-on-surface-variant);
+ margin-top: 0.375rem;
+ text-align: center;
+ opacity: 0.75;
+}
+
+.test-chip {
+ display: inline-flex;
+ align-items: center;
+ padding: 2px 8px;
+ border-radius: 9999px;
+ background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
+ color: var(--color-primary);
+ font-size: 0.65rem;
+ font-weight: 600;
+ font-family: ui-monospace, 'Courier New', monospace;
+ letter-spacing: 0.05em;
+}
+
+.test-hover-zone {
+ border: 1px dashed var(--color-outline-variant);
+ border-radius: var(--radius);
+ padding: 1.5rem;
+ transition:
+ border-color 0.2s ease,
+ background-color 0.2s ease;
+ cursor: default;
+}
+
+.test-hover-zone:hover {
+ border-color: var(--color-outline);
+ background-color: color-mix(in srgb, var(--color-primary) 4%, transparent);
+}
diff --git a/src/components/dialogs/ChangelogDialog.tsx b/src/components/dialogs/ChangelogDialog.tsx
new file mode 100644
index 0000000..249e1b9
--- /dev/null
+++ b/src/components/dialogs/ChangelogDialog.tsx
@@ -0,0 +1,113 @@
+'use client';
+
+import { forwardRef, useCallback, useState } from 'react';
+import { FileText, X } from '@icons';
+import { useTranslations } from 'next-intl';
+import type { GitHubRelease } from '@t/github';
+
+interface Props {
+ locale: string;
+}
+
+const ChangelogDialog = forwardRef(({ locale: $locale }, ref) => {
+ void $locale;
+ const t = useTranslations();
+ const [content, setContent] = useState('');
+ const [loading, setLoading] = useState(false);
+ const [date, setDate] = useState('');
+
+ const close = useCallback(() => {
+ if (ref && 'current' in ref) {
+ ref.current?.close();
+ }
+ }, [ref]);
+
+ const handleBackdrop = useCallback(
+ (e: React.MouseEvent) => {
+ if (ref && 'current' in ref && e.target === ref.current) {
+ close();
+ }
+ },
+ [ref, close],
+ );
+
+ const load = useCallback(async () => {
+ if (content) {
+ return;
+ }
+ setLoading(true);
+ try {
+ const res = await fetch("/api/github/releases/latest");
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}`);
+ }
+ const data = (await res.json()) as GitHubRelease;
+ const dateStr = new Date(data.published_at).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+ setDate(dateStr);
+ const body = data.body ?? 'No release notes available.';
+ const { marked } = await import('marked');
+ setContent(await marked.parse(body));
+ } catch {
+ setContent('Error loading changelog.
');
+ } finally {
+ setLoading(false);
+ }
+ }, [content]);
+
+ const open = useCallback(() => {
+ if (ref && 'current' in ref) {
+ ref.current?.showModal();
+ void load();
+ }
+ }, [ref, load]);
+
+ return (
+ <>
+
+
+ {t('downloads.changelog')}
+
+
+
+
+
+ {t('changelog.title')}
+
+
+
+
+
+
+ {loading ? (
+
+ ) : content ? (
+
+ {date && (
+
+ 📅 {date}
+
+ )}
+
+
+ ) : (
+
+ )}
+
+
+ >
+ );
+});
+
+ChangelogDialog.displayName = 'ChangelogDialog';
+export default ChangelogDialog;
diff --git a/src/components/dialogs/VersionsDialog.tsx b/src/components/dialogs/VersionsDialog.tsx
new file mode 100644
index 0000000..1d6d176
--- /dev/null
+++ b/src/components/dialogs/VersionsDialog.tsx
@@ -0,0 +1,151 @@
+'use client';
+
+import { forwardRef, useCallback, useState } from 'react';
+import { History, X, Download } from '@icons';
+import { useTranslations } from 'next-intl';
+import type { GitHubRelease } from '@t/github';
+import { buildDownloadUrl, REPOS } from '@lib/github';
+
+interface Props {
+ locale: string;
+}
+
+function getReleaseType(release: GitHubRelease): 'stable' | 'beta' | 'alpha' {
+ if (release.tag_name.toLowerCase().includes('alpha')) {
+ return 'alpha';
+ }
+ if (release.prerelease) {
+ return 'beta';
+ }
+ return 'stable';
+}
+
+const VersionsDialog = forwardRef(({ locale: $locale }, ref) => {
+ void $locale;
+ const t = useTranslations();
+ const [releases, setReleases] = useState([]);
+ const [loading, setLoading] = useState(false);
+
+ const close = useCallback(() => {
+ if (ref && 'current' in ref) {
+ ref.current?.close();
+ }
+ }, [ref]);
+
+ const handleBackdrop = useCallback(
+ (e: React.MouseEvent) => {
+ if (ref && 'current' in ref && e.target === ref.current) {
+ close();
+ }
+ },
+ [ref, close],
+ );
+
+ const load = useCallback(async () => {
+ if (releases.length > 0) {
+ return;
+ }
+ setLoading(true);
+ try {
+ const res = await fetch("/api/github/releases?per_page=20");
+ if (!res.ok) {
+ throw new Error(`HTTP ${res.status}`);
+ }
+ setReleases((await res.json()) as GitHubRelease[]);
+ } catch {
+ setReleases([]);
+ } finally {
+ setLoading(false);
+ }
+ }, [releases.length]);
+
+ const open = useCallback(() => {
+ if (ref && 'current' in ref) {
+ ref.current?.showModal();
+ void load();
+ }
+ }, [ref, load]);
+
+ const chipLabel = (type: 'stable' | 'beta' | 'alpha') => {
+ const map = { stable: t('versions.stable'), beta: 'Beta', alpha: 'Alpha' };
+ return map[type];
+ };
+
+ return (
+ <>
+
+
+ {t('downloads.versions')}
+
+
+
+
+
+ {t('versions.title')}
+
+
+
+
+
+
+ {loading ? (
+
+ ) : releases.length > 0 ? (
+
+ {releases.map((release, i) => {
+ const type = getReleaseType(release);
+ const dateStr = new Date(release.published_at).toLocaleDateString('en-US', {
+ year: 'numeric',
+ month: 'long',
+ day: 'numeric',
+ });
+ const url =
+ release.assets[0]?.browser_download_url ??
+ buildDownloadUrl(REPOS.android, release.tag_name);
+ return (
+
+ );
+ })}
+
+ ) : (
+
+ )}
+
+
+ >
+ );
+});
+
+VersionsDialog.displayName = 'VersionsDialog';
+export default VersionsDialog;
diff --git a/src/components/dialogs/WarningDialog.tsx b/src/components/dialogs/WarningDialog.tsx
new file mode 100644
index 0000000..614ed2b
--- /dev/null
+++ b/src/components/dialogs/WarningDialog.tsx
@@ -0,0 +1,56 @@
+'use client';
+
+import { useCallback, forwardRef } from 'react';
+import { TriangleAlert } from '@icons';
+import { useTranslations } from 'next-intl';
+import { EXTERNAL_LINKS } from '@config/links';
+
+const WarningDialog = forwardRef((_, ref) => {
+ const t = useTranslations();
+
+ const close = useCallback(() => {
+ if (ref && 'current' in ref) {
+ ref.current?.close();
+ }
+ }, [ref]);
+
+ const proceed = useCallback(() => {
+ window.location.href = EXTERNAL_LINKS.DEMO;
+ }, []);
+
+ const handleBackdrop = useCallback(
+ (e: React.MouseEvent) => {
+ if (ref && 'current' in ref && e.target === ref.current) {
+ close();
+ }
+ },
+ [ref, close],
+ );
+
+ return (
+
+
+
+
+
+
+
{t('warning.title')}
+
+
+
+
+
+ {t('warning.cancel')}
+
+
+ {t('warning.continue')}
+
+
+
+ );
+});
+
+WarningDialog.displayName = 'WarningDialog';
+export default WarningDialog;
diff --git a/src/components/icons/_base.tsx b/src/components/icons/_base.tsx
new file mode 100644
index 0000000..f8b5b58
--- /dev/null
+++ b/src/components/icons/_base.tsx
@@ -0,0 +1,2 @@
+export const DEFAULT_SIZE = 24;
+export const COLOR = 'currentColor';
diff --git a/src/components/icons/_index.ts b/src/components/icons/_index.ts
new file mode 100644
index 0000000..7f269d2
--- /dev/null
+++ b/src/components/icons/_index.ts
@@ -0,0 +1,9 @@
+export * from 'lucide-react';
+
+export { Opentune, MulOpentune } from '@icon/opentune';
+export { Android } from '@icon/android';
+export { Windows } from '@icon/windows';
+export { YouTubeMusic } from '@icon/youtube-music';
+export { StarPlus } from '@icon/star-plus';
+export { MobileCode } from '@icon/mobile-code';
+export { Crowdsource } from '@icon/crowdsource';
diff --git a/src/components/icons/_template.tsx b/src/components/icons/_template.tsx
new file mode 100644
index 0000000..d1ae47b
--- /dev/null
+++ b/src/components/icons/_template.tsx
@@ -0,0 +1,146 @@
+/**
+ * HOW TO CREATE A CUSTOM ICON
+ * ============================
+ *
+ * Copy this template and create a new file:
+ * src/components/icons/.tsx
+ * cmd: `mv src/components/icons/_template.tsx src/components/icons/.tsx`
+ *
+ * There are two variants:
+ *
+ * 1. [REQUIRED] Lucide-style — single-color, follows `currentColor`,
+ * supports size/color/className props — exactly like lucide-react icons.
+ * Naming: (e.g. Opentune, MyBrand)
+ *
+ * 2. [OPTIONAL] Mul (original icon) — has its own hardcoded gradient/colors,
+ * does not accept a `color` prop (fixed artwork).
+ * Naming: Mul (e.g. MulOpentune, MulMyBrand)
+ * Only create this when you also need a separate "branded / full-color"
+ * version of the icon.
+ *
+ * Mul(मूल) means "original"
+ *
+ * After exporting, add a line to src/components/icons/_index.ts:
+ * export { IconName, MulIconName } from '@icon/';
+ * (The Mul export is optional)
+ * ============================
+ */
+
+import { forwardRef, useId } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps, MulIconProps } from '@icon/_types';
+
+// ---------------------------------------------------------------------------
+// Put your icon name here (PascalCase)
+// ---------------------------------------------------------------------------
+const ICON_NAME = 'IconName'; // <-- CHANGE THIS
+
+// ---------------------------------------------------------------------------
+// [REQUIRED] Lucide-style icon
+//
+// - `size` : controls both width and height (default 24px)
+// - `color` : fill color; falls back to `currentColor` when undefined,
+// so it can be driven by CSS `color` / Tailwind `text-*`
+// - `className` : for Tailwind or CSS classes
+// - `...props` : any other SVG attributes (aria-*, role, onClick, etc.)
+// - `ref` : forwards an SVGSVGElement ref
+//
+// Set viewBox to match your SVG, e.g. "0 0 24 24" or "0 0 500 500"
+//
+// FILL vs STROKE:
+// Icons may be fill-based, stroke-based, or a combination of both —
+// depending on the original SVG artwork. Do not assume every icon uses
+// "fill". Preserve the style of the original SVG:
+// - Fill-based : set `fill={color ?? COLOR}` on the (as shown below)
+// - Stroke-based: remove the `fill` prop from , set `fill="none"` and
+// pass `stroke={color ?? COLOR}` on the inner paths instead
+// - Mixed : apply fill/stroke selectively on each child element
+//
+// ACCESSIBILITY:
+// - Decorative icons (purely visual, already labelled by surrounding text)
+// should be hidden from assistive technologies:
+//
+// - Icons that convey meaning on their own, or are interactive, need
+// explicit accessibility attributes, for example:
+//
+//
+// All of these props are forwarded automatically via `...props`.
+// ---------------------------------------------------------------------------
+export const IconName = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+ {/* Place your SVG content here — path, circle, rect, etc. */}
+
+
+ ),
+);
+IconName.displayName = ICON_NAME;
+
+// ---------------------------------------------------------------------------
+// [OPTIONAL] Mul (Multicolor) variant
+//
+// - `size` : sets both width and height at once (default 24px)
+// - `width` : overrides only the width (takes priority over size)
+// - `height` : overrides only the height (takes priority over size)
+// - No `color` prop — colors are hardcoded inside the SVG
+// (gradients, multiple fills, etc.)
+// - `...props` : any other SVG attributes (including accessibility props)
+//
+// UNIQUE IDs WITH useId():
+// SVG elements (gradients, clip paths, masks, filters, etc.) require
+// an `id` to be referenced elsewhere in the SVG. Static string IDs such as
+// "my-gradient" will collide when multiple instances of the same icon are
+// rendered on the same page, causing only the first definition to apply.
+// Use React's `useId()` to generate a unique ID per instance, then use it
+// in both the element and the corresponding reference (e.g. fill="url(#...)").
+//
+// If you do not need a multicolor variant, delete this entire block.
+// ---------------------------------------------------------------------------
+export const MulIconName = forwardRef(
+ ({ size = DEFAULT_SIZE, width, height, ...props }, ref) => {
+ const id = useId();
+ const gradId = `${id}-grad`;
+
+ return (
+
+
+
+
+
+
+
+
+ {/* Place your multicolor SVG content here */}
+
+
+ );
+ },
+);
+MulIconName.displayName = `Mul${ICON_NAME}`;
+
+// ---------------------------------------------------------------------------
+// Once your icon is done, please delete all the comments in this file —
+// including this block, the top doc-comment, and every inline comment above.
+// Keep only the imports, the ICON_NAME constant, and the component exports.
+// ---------------------------------------------------------------------------
diff --git a/src/components/icons/_types.ts b/src/components/icons/_types.ts
new file mode 100644
index 0000000..dbea778
--- /dev/null
+++ b/src/components/icons/_types.ts
@@ -0,0 +1,9 @@
+import type { SVGProps } from 'react';
+
+export interface IconProps extends SVGProps {
+ size?: number | string;
+}
+
+export interface MulIconProps extends Omit, 'color'> {
+ size?: number | string;
+}
diff --git a/src/components/icons/android.tsx b/src/components/icons/android.tsx
new file mode 100644
index 0000000..b35d382
--- /dev/null
+++ b/src/components/icons/android.tsx
@@ -0,0 +1,23 @@
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'Android';
+
+export const Android = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+ ),
+);
+Android.displayName = ICON_NAME;
diff --git a/src/components/icons/crowdsource.tsx b/src/components/icons/crowdsource.tsx
new file mode 100644
index 0000000..1a90ddc
--- /dev/null
+++ b/src/components/icons/crowdsource.tsx
@@ -0,0 +1,24 @@
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'Crowdsource';
+
+export const Crowdsource = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+ ),
+);
+
+Crowdsource.displayName = ICON_NAME;
diff --git a/src/components/icons/mobile-code.tsx b/src/components/icons/mobile-code.tsx
new file mode 100644
index 0000000..855988d
--- /dev/null
+++ b/src/components/icons/mobile-code.tsx
@@ -0,0 +1,23 @@
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'MobileCode';
+
+export const MobileCode = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+ ),
+);
+MobileCode.displayName = ICON_NAME;
diff --git a/src/components/icons/opentune.tsx b/src/components/icons/opentune.tsx
new file mode 100644
index 0000000..23de97c
--- /dev/null
+++ b/src/components/icons/opentune.tsx
@@ -0,0 +1,80 @@
+import { forwardRef, useId } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps, MulIconProps } from '@icon/_types';
+
+export const Opentune = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => {
+ const id = useId();
+ const clipId = `${id}-clip`;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+ );
+ },
+);
+Opentune.displayName = 'Opentune';
+
+export const MulOpentune = forwardRef(
+ ({ size = DEFAULT_SIZE, width, height, ...props }, ref) => {
+ const id = useId();
+ const gradId = `${id}-grad`;
+ const clipId = `${id}-clip`;
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ },
+);
+MulOpentune.displayName = 'MulOpentune';
diff --git a/src/components/icons/star-plus.tsx b/src/components/icons/star-plus.tsx
new file mode 100644
index 0000000..9a13792
--- /dev/null
+++ b/src/components/icons/star-plus.tsx
@@ -0,0 +1,46 @@
+// Remove when StarPlus is released in Lucide.
+// Ref: https://github.com/lucide-icons/lucide/pull/3918
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'StarPlus';
+
+export const StarPlus = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+
+
+ ),
+);
+
+StarPlus.displayName = ICON_NAME;
diff --git a/src/components/icons/windows.tsx b/src/components/icons/windows.tsx
new file mode 100644
index 0000000..bf5d92d
--- /dev/null
+++ b/src/components/icons/windows.tsx
@@ -0,0 +1,23 @@
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'Windows';
+
+export const Windows = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+ ),
+);
+Windows.displayName = ICON_NAME;
diff --git a/src/components/icons/youtube-music.tsx b/src/components/icons/youtube-music.tsx
new file mode 100644
index 0000000..21d268d
--- /dev/null
+++ b/src/components/icons/youtube-music.tsx
@@ -0,0 +1,23 @@
+import { forwardRef } from 'react';
+import { DEFAULT_SIZE, COLOR } from '@icon/_base';
+import type { IconProps } from '@icon/_types';
+
+const ICON_NAME = 'YouTubeMusic';
+
+export const YouTubeMusic = forwardRef(
+ ({ size = DEFAULT_SIZE, color, className, ...props }, ref) => (
+
+
+
+ ),
+);
+YouTubeMusic.displayName = ICON_NAME;
diff --git a/src/components/layout/Footer.tsx b/src/components/layout/Footer.tsx
new file mode 100644
index 0000000..22310c7
--- /dev/null
+++ b/src/components/layout/Footer.tsx
@@ -0,0 +1,107 @@
+import { useTranslations } from 'next-intl';
+import { MobileCode, Code, Scale, Phone, Crowdsource, LifeBuoy } from '@icons';
+import Link from 'next/link';
+import { type Locale } from '@config/locales';
+import { EXTERNAL_LINKS, PATHS } from '@config/links';
+
+export default function Footer({ locale }: { locale: Locale }) {
+ const t = useTranslations();
+
+ return (
+
+
+
+
+
OpenTune
+
+ {t('footer.copyright')}{' '}
+
+ Arturo.inc™
+
+ {'. '}
+ {t('footer.rights')}
+
+
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/layout/Navbar.tsx b/src/components/layout/Navbar.tsx
new file mode 100644
index 0000000..36420b1
--- /dev/null
+++ b/src/components/layout/Navbar.tsx
@@ -0,0 +1,158 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import { Link, usePathname } from '@/i18n/routing';
+import Image from 'next/image';
+import { useParams } from 'next/navigation';
+import { type Locale, localeConfig, locales } from '@config/locales';
+import { useCallback, useRef } from 'react';
+import { MulOpentune, X, Globe, ChevronDown, Download } from '@icons';
+import { EXTERNAL_LINKS, PATHS } from '@config/links';
+import { REPOS, buildDownloadUrl } from '@lib/github';
+
+export default function Navbar() {
+ const t = useTranslations();
+ const params = useParams();
+ const pathname = usePathname();
+ const locale = (params['locale'] as Locale) ?? 'en';
+ const dialogRef = useRef(null);
+
+ const openLangDialog = useCallback(() => {
+ dialogRef.current?.showModal();
+ }, []);
+
+ const closeLangDialog = useCallback(() => {
+ dialogRef.current?.close();
+ }, []);
+
+ const handleBackdropClick = useCallback(
+ (e: React.MouseEvent) => {
+ if (e.target === dialogRef.current) {
+ closeLangDialog();
+ }
+ },
+ [closeLangDialog],
+ );
+
+ return (
+ <>
+ {/* Language Dialog */}
+
+
+
+ {t('lang.select')}
+
+
+
+
+
+
+
+ {locales.map((loc) => {
+ const cfg = localeConfig[loc];
+ return (
+
+
+ {cfg.displayName}
+
+ );
+ })}
+
+
+
+
+ {/* Nav */}
+
+
+ {/* Logo */}
+
+ {/*
*/}
+
+
+ OpenTune
+
+
+
+ {/* Desktop Nav */}
+
+
+ {t('nav.features')}
+
+
+ {t('nav.screenshots')}
+
+
+ {t('footer.support')}
+
+
+ GitHub
+
+
+
+ {/* Actions */}
+
+
+
+ {locale.toUpperCase()}
+
+
+
+
+
+ {t('nav.download')}
+
+
+
+
+ >
+ );
+}
diff --git a/src/components/sections/ContributorsClient.tsx b/src/components/sections/ContributorsClient.tsx
new file mode 100644
index 0000000..2a1a36c
--- /dev/null
+++ b/src/components/sections/ContributorsClient.tsx
@@ -0,0 +1,353 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import Image from 'next/image';
+import { useEffect, useState } from 'react';
+import {
+ Heart,
+ Star,
+ GitFork,
+ OctagonAlert,
+ ArrowRight,
+ ExternalLink,
+ Palette,
+ Languages,
+ Users,
+ History,
+ Bug,
+ GitCommitHorizontal,
+ Code,
+} from '@icons';
+import MobileBottomNav from '@ui/MobileBottomNav';
+import { type Locale } from '@config/locales';
+import {
+ formatNumber,
+ getContributorRole,
+ REPOS,
+ fetchContributors,
+ fetchRecentCommits,
+ fetchRepo,
+ fetchTotalCommits,
+} from '@lib/github';
+import type { GitHubContributor, GitHubCommit } from '@t/github';
+import { EXTERNAL_LINKS } from '@config/links';
+
+interface Stats {
+ stars: string;
+ forks: string;
+ issues: string;
+ commits: string;
+}
+
+export default function ContributorsClient({ locale }: { locale: Locale }) {
+ const t = useTranslations();
+
+ const [stats, setStats] = useState({ stars: '—', forks: '—', issues: '—', commits: '—' });
+ const [contributors, setContributors] = useState([]);
+ const [commits, setCommits] = useState([]);
+ const [loadingContribs, setLoadingContribs] = useState(true);
+ const [loadingCommits, setLoadingCommits] = useState(true);
+
+ useEffect(() => {
+ void (async () => {
+ try {
+ const [repo, totalCommits] = await Promise.all([
+ fetchRepo(REPOS.android),
+ fetchTotalCommits(REPOS.android),
+ ]);
+ if (repo) {
+ setStats({
+ stars: formatNumber(repo.stargazers_count),
+ forks: formatNumber(repo.forks_count),
+ issues: formatNumber(repo.open_issues_count),
+ commits: totalCommits,
+ });
+ }
+ } catch {
+ /* silently fail */
+ }
+ })();
+ }, []);
+
+ useEffect(() => {
+ void (async () => {
+ try {
+ const data = await fetchContributors(REPOS.android);
+ setContributors(data.slice(0, 9));
+ } catch {
+ /* silently fail */
+ } finally {
+ setLoadingContribs(false);
+ }
+ })();
+ }, []);
+
+ useEffect(() => {
+ void (async () => {
+ try {
+ const data = await fetchRecentCommits(REPOS.android, 4);
+ setCommits(data);
+ } catch {
+ /* silently fail */
+ } finally {
+ setLoadingCommits(false);
+ }
+ })();
+ }, []);
+
+ const statsData = [
+ { id: 'stars', icon: Star, label: t('contributors.stat_stars'), value: stats.stars },
+ { id: 'forks', icon: GitFork, label: t('contributors.stat_forks'), value: stats.forks },
+ { id: 'issues', icon: OctagonAlert, label: t('contributors.stat_issues'), value: stats.issues },
+ { id: 'commits', icon: History, label: t('contributors.stat_commits'), value: stats.commits },
+ ];
+
+ type HelpItem = {
+ icon: typeof Code;
+ title: Parameters[0];
+ desc: Parameters[0];
+ cta: Parameters[0];
+ href?: string;
+ };
+
+ const helpItems: HelpItem[] = [
+ {
+ icon: Code,
+ title: 'contributors.help_code_title',
+ desc: 'contributors.help_code_desc',
+ cta: 'contributors.help_code_cta',
+ href: `${EXTERNAL_LINKS.GITHUB_BASE}/${REPOS.android}/issues`,
+ },
+ {
+ icon: Palette,
+ title: 'contributors.help_design_title',
+ desc: 'contributors.help_design_desc',
+ cta: 'contributors.help_design_cta',
+ },
+ {
+ icon: Languages,
+ title: 'contributors.help_translate_title',
+ desc: 'contributors.help_translate_desc',
+ cta: 'contributors.help_translate_cta',
+ },
+ {
+ icon: Bug,
+ title: 'contributors.help_report_title',
+ desc: 'contributors.help_report_desc',
+ cta: 'contributors.help_report_cta',
+ href: `${EXTERNAL_LINKS.GITHUB_BASE}/${REPOS.android}/issues/new`,
+ },
+ ];
+
+ return (
+
+
+ {/* Hero */}
+
+
+
+ {t('contributors.badge')}
+
+
+ {t('contributors.title')}
+
+ {t('contributors.subtitle')}
+
+
+ {/* Stats */}
+
+ {statsData.map((s) => (
+
+
+
+ {s.value}
+
+ {s.label}
+
+ ))}
+
+
+ {/* Contributors Grid */}
+
+
+
+
+ {loadingContribs
+ ? [1, 2, 3, 4, 5, 6].map((i) => (
+
+ ))
+ : contributors.map((c) => {
+ const role = getContributorRole(c.contributions, c.login);
+ return (
+
+ );
+ })}
+
+
+
+ {/* Bento: How to Help + Activity */}
+
+ {/* How to Help */}
+
+
+
+
+ {t('contributors.how_title')}
+
+
{t('contributors.how_subtitle')}
+
+
+ {helpItems.map((item) => (
+
+
+
+ {t(item.title)}
+
+
{t(item.desc)}
+ {item.href ? (
+
+ {t(item.cta)}
+
+ ) : (
+
+ {t(item.cta)}
+
+ )}
+
+ ))}
+
+
+
+ {/* Latest Activity */}
+
+
+ {t('contributors.activity_title')}
+
+
+ {loadingCommits ? (
+
+ ) : (
+ commits.map((commit) => (
+
+
+
+
+ {commit.commit.message.split('\n')[0]?.substring(0, 60) ?? ''}
+
+
+ by{' '}
+
+ {commit.author?.login ?? commit.commit.author.name}
+
+ {' • '}
+ {new Date(commit.commit.author.date).toLocaleDateString(locale)}
+
+
+
+ ))
+ )}
+
+
+ {t('contributors.activity_link')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/sections/Downloads.tsx b/src/components/sections/Downloads.tsx
new file mode 100644
index 0000000..f6ffbaa
--- /dev/null
+++ b/src/components/sections/Downloads.tsx
@@ -0,0 +1,122 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import { useRef } from 'react';
+import { Android, Windows, Download, FileText, History, Ban } from '@icons';
+import ChangelogDialog from '@dialog/ChangelogDialog';
+import VersionsDialog from '@dialog/VersionsDialog';
+import { type Locale } from '@config/locales';
+
+interface DownloadsProps {
+ locale: Locale;
+ version: string;
+ downloadUrl: string;
+}
+
+export default function Downloads({ locale, version, downloadUrl }: DownloadsProps) {
+ const t = useTranslations();
+ const changelogRef = useRef(null);
+ const versionsRef = useRef(null);
+
+ const finalDownloadUrl =
+ downloadUrl === '#'
+ ? 'https://github.com/Arturo254/OpenTune/releases/latest/download/app-universal-release.apk'
+ : downloadUrl;
+
+ return (
+
+
+
+
+ {t('downloads.title')}
+
+
{t('downloads.subtitle')}
+
+
+
+ {/* Android Card */}
+
+
+
+
+
Android
+
{t('downloads.android_subtitle')}
+
+
+
+
+ {t('downloads.stable')}
+ {version || '—'}
+
+
+
+
+
+
+
+
+
+
+ {/* Windows Card (Disabled) */}
+
+
+
+
+
+
+
+
+ {t('downloads.windows_title')}
+
+
{t('downloads.windows_subtitle')}
+
+
+
+
+ {t('downloads.closed')}
+ {t('downloads.unavailable')}
+
+
+
+
+
+ {t('downloads.changelog')}
+
+
+
+ {t('downloads.versions')}
+
+
+
+
+
+
+ {t('downloads.not_available')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/sections/Features.tsx b/src/components/sections/Features.tsx
new file mode 100644
index 0000000..65fbf81
--- /dev/null
+++ b/src/components/sections/Features.tsx
@@ -0,0 +1,137 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import {
+ YouTubeMusic,
+ Palette,
+ Settings,
+ Search,
+ Volume2,
+ ListPlus,
+ RefreshCw,
+ Wrench,
+ Folder,
+ Languages,
+ ArrowUpCircle,
+ Headphones,
+} from '@icons';
+import type { IconProps } from '@icon/_types';
+
+interface Feature {
+ icon: React.ComponentType;
+ titleKey: string;
+ descKey: string;
+ color: 'primary' | 'secondary' | 'tertiary';
+}
+
+const FEATURES: Feature[] = [
+ {
+ icon: YouTubeMusic,
+ titleKey: 'features.yt_title',
+ descKey: 'features.yt_desc',
+ color: 'primary',
+ },
+ {
+ icon: Palette,
+ titleKey: 'features.design_title',
+ descKey: 'features.design_desc',
+ color: 'secondary',
+ },
+ { icon: Settings, titleKey: 'features.ui_title', descKey: 'features.ui_desc', color: 'tertiary' },
+ {
+ icon: Search,
+ titleKey: 'features.explore_title',
+ descKey: 'features.explore_desc',
+ color: 'primary',
+ },
+ {
+ icon: Volume2,
+ titleKey: 'features.quality_title',
+ descKey: 'features.quality_desc',
+ color: 'secondary',
+ },
+ {
+ icon: ListPlus,
+ titleKey: 'features.playlist_title',
+ descKey: 'features.playlist_desc',
+ color: 'tertiary',
+ },
+ {
+ icon: RefreshCw,
+ titleKey: 'features.sync_title',
+ descKey: 'features.sync_desc',
+ color: 'primary',
+ },
+ {
+ icon: Wrench,
+ titleKey: 'features.advanced_title',
+ descKey: 'features.advanced_desc',
+ color: 'secondary',
+ },
+ {
+ icon: Folder,
+ titleKey: 'features.library_title',
+ descKey: 'features.library_desc',
+ color: 'tertiary',
+ },
+ {
+ icon: Languages,
+ titleKey: 'features.multilang_title',
+ descKey: 'features.multilang_desc',
+ color: 'primary',
+ },
+ {
+ icon: ArrowUpCircle,
+ titleKey: 'features.updates_title',
+ descKey: 'features.updates_desc',
+ color: 'secondary',
+ },
+ {
+ icon: Headphones,
+ titleKey: 'features.ux_title',
+ descKey: 'features.ux_desc',
+ color: 'tertiary',
+ },
+];
+
+const colorMap = {
+ primary: { wrap: 'bg-[#d0bcff]/30 text-[#e9ddff]' },
+ secondary: { wrap: 'bg-[#4a4359]/60 text-[#ccc2dc]' },
+ tertiary: { wrap: 'bg-[#efb8c8]/30 text-[#ffd9e3]' },
+};
+
+export default function Features() {
+ const t = useTranslations();
+
+ return (
+
+
+
+
+ {t('features.title')}
+
+
{t('features.subtitle')}
+
+
+
+ {FEATURES.map((f) => (
+
+
+
+
+
+ {t(f.titleKey as Parameters[0])}
+
+
+ {t(f.descKey as Parameters[0])}
+
+
+ ))}
+
+
+
+ );
+}
diff --git a/src/components/sections/Hero.tsx b/src/components/sections/Hero.tsx
new file mode 100644
index 0000000..aa4fea4
--- /dev/null
+++ b/src/components/sections/Hero.tsx
@@ -0,0 +1,82 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import Image from 'next/image';
+import Link from 'next/link';
+import { useCallback, useRef } from 'react';
+import { Code, ArrowBigDownDash, CirclePlay } from '@icons';
+import WarningDialog from '@dialog/WarningDialog';
+import { type Locale } from '@config/locales';
+import { PATHS } from '@config/links';
+
+export default function Hero({ locale }: { locale: Locale }) {
+ const t = useTranslations();
+ const warningRef = useRef(null);
+
+ const openDemo = useCallback((e: React.MouseEvent) => {
+ e.preventDefault();
+ warningRef.current?.showModal();
+ }, []);
+
+ return (
+ <>
+
+
+
+ {/* Background glow */}
+
+
+
+ {/* Text */}
+
+
+
+ {t('hero.badge')}
+
+
+
+ OpenTune
+
+
+ {t('hero.subtitle')}
+
+
+
+
+
+ {t('hero.download_apk')}
+
+
+
+ {t('hero.try_demo')}
+
+
+
+
+ {/* Mockup */}
+
+
+
+ >
+ );
+}
diff --git a/src/components/sections/OpenSource.tsx b/src/components/sections/OpenSource.tsx
new file mode 100644
index 0000000..2b755a6
--- /dev/null
+++ b/src/components/sections/OpenSource.tsx
@@ -0,0 +1,81 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import { useEffect, useState } from 'react';
+import Image from 'next/image';
+import { StarPlus, BadgeCheck } from '@icons';
+import { REPOS } from '@lib/github';
+import { EXTERNAL_LINKS } from '@config/links';
+
+const GITHUB = 'https://github.com/Arturo254/OpenTune';
+
+export default function OpenSource() {
+ const t = useTranslations();
+ const [version, setVersion] = useState('');
+
+ useEffect(() => {
+ fetch(`${EXTERNAL_LINKS.GITHUB_API}/${REPOS.android}/releases/latest`)
+ .then((r) => r.json())
+ .then((d: { tag_name?: string }) => setVersion(d.tag_name ?? 'N/A'))
+ .catch(() => setVersion('N/A'));
+ }, []);
+
+ return (
+
+
+
+
+ {t('oss.badge')}
+
+
+ {t('oss.title')}
+
+
{t('oss.body')}
+
+
+
+
+
+
+
+
+
+
Arturo254
+
{t('oss.lead')}
+
+
+
+
+
+
+
+
{t('oss.latest_version')}
+
{version || t('oss.loading')}
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/sections/Screenshots.tsx b/src/components/sections/Screenshots.tsx
new file mode 100644
index 0000000..500294e
--- /dev/null
+++ b/src/components/sections/Screenshots.tsx
@@ -0,0 +1,98 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import Image from 'next/image';
+import { useCallback, useState } from 'react';
+import { ChevronDown } from '@icons';
+
+const SHOTS = [
+ { src: '/img/reproductor.webp', labelKey: 'screenshots.player', alt: 'Player Screen' },
+ { src: '/img/biblioteca.webp', labelKey: 'screenshots.library', alt: 'Library Screen' },
+ { src: '/img/ajustes.webp', labelKey: 'screenshots.settings', alt: 'Settings Screen' },
+];
+
+export default function Screenshots() {
+ const t = useTranslations();
+
+ const [collapsed, setCollapsed] = useState(() => {
+ if (typeof window === 'undefined') {
+ return true;
+ }
+ return localStorage.getItem('screenshotsCollapsed') !== 'false';
+ });
+
+ const toggle = useCallback(() => {
+ setCollapsed((prev) => {
+ const next = !prev;
+ localStorage.setItem('screenshotsCollapsed', String(next));
+ return next;
+ });
+ }, []);
+
+ return (
+
+
+
+
(e.key === 'Enter' || e.key === ' ') && toggle()}
+ >
+
+
+ {t('screenshots.title')}
+
+
{t('screenshots.subtitle')}
+
+
{
+ e.stopPropagation();
+ toggle();
+ }}
+ >
+
+
+
+
+
+
+
+ {SHOTS.map((shot, i) => (
+
+
+
+
+
+ {t(shot.labelKey as Parameters[0])}
+
+
+ ))}
+
+
+
+
+ );
+}
diff --git a/src/components/sections/SupportCTA.tsx b/src/components/sections/SupportCTA.tsx
new file mode 100644
index 0000000..63f63c5
--- /dev/null
+++ b/src/components/sections/SupportCTA.tsx
@@ -0,0 +1,34 @@
+import { useTranslations } from 'next-intl';
+import Link from 'next/link';
+import { Headset, Send } from '@icons';
+import { type Locale } from '@config/locales';
+import { PATHS } from '@config/links';
+
+export default function SupportCTA({ locale }: { locale: Locale }) {
+ const t = useTranslations();
+
+ return (
+
+
+
+
+
+
+
+
+
+ {t('support.title')}
+
+
{t('support.body')}
+
+
+ {t('support.cta')}
+
+
+
+
+ );
+}
diff --git a/src/components/sections/SupportClient.tsx b/src/components/sections/SupportClient.tsx
new file mode 100644
index 0000000..17cc2d0
--- /dev/null
+++ b/src/components/sections/SupportClient.tsx
@@ -0,0 +1,294 @@
+'use client';
+
+import { useTranslations } from 'next-intl';
+import { type Locale } from '@config/locales';
+import { useCallback, useRef, useState } from 'react';
+import {
+ CheckCircle,
+ UserRound,
+ MessageSquareText,
+ PackagePlus,
+ ShieldCheck,
+ Mail,
+ Loader2,
+ Send,
+ Bug,
+ Clock,
+ MessageCircle,
+} from '@icons';
+import type { IconProps } from '@icon/_types';
+import MobileBottomNav from '@ui/MobileBottomNav';
+import { EXTERNAL_LINKS } from '@config/links';
+
+type MessageType = 'comment' | 'request' | 'report';
+
+const TYPE_ICONS: Record> = {
+ comment: MessageSquareText,
+ request: PackagePlus,
+ report: Bug,
+};
+
+const TYPE_COLORS: Record = {
+ comment: 'text-[#e9ddff] bg-[#e9ddff]/10',
+ request: 'text-[#ffd9e3] bg-[#ffd9e3]/10',
+ report: 'text-[#ffb4ab] bg-[#ffb4ab]/10',
+};
+
+const FORMSPREE_URL = EXTERNAL_LINKS.FORMSPREE;
+
+export default function SupportClient({ locale }: { locale: Locale }) {
+ const t = useTranslations();
+
+ const [messageType, setMessageType] = useState('comment');
+ const [loading, setLoading] = useState(false);
+ const [success, setSuccess] = useState(false);
+ const [error, setError] = useState('');
+
+ const nameRef = useRef(null);
+ const emailRef = useRef(null);
+ const descRef = useRef(null);
+
+ const handleSubmit = useCallback(
+ async (e: React.FormEvent) => {
+ e.preventDefault();
+ const name = nameRef.current?.value.trim() ?? '';
+ const email = emailRef.current?.value.trim() ?? '';
+ const desc = descRef.current?.value.trim() ?? '';
+
+ if (!name || !email || !desc) {
+ setError(t('support_page.validation_all'));
+ return;
+ }
+ const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+ if (!emailRegex.test(email)) {
+ setError(t('support_page.validation_email'));
+ return;
+ }
+
+ setError('');
+ setLoading(true);
+ try {
+ const form = new FormData();
+ form.append('nombre', name);
+ form.append('email', email);
+ form.append('tipo_mensaje', messageType);
+ form.append('descripcion', desc);
+
+ const controller = new AbortController();
+ const timeoutId = setTimeout(() => controller.abort(), 10000);
+
+ try {
+ const res = await fetch(FORMSPREE_URL, {
+ method: 'POST',
+ body: form,
+ headers: { Accept: 'application/json' },
+ signal: controller.signal,
+ });
+
+ if (res.ok) {
+ setSuccess(true);
+ } else {
+ throw new Error(t('support_page.failed_submit'));
+ }
+ } finally {
+ clearTimeout(timeoutId);
+ }
+ } catch {
+ setError(t('support_page.error_sending'));
+ } finally {
+ setLoading(false);
+ }
+ },
+ [messageType, t],
+ );
+
+ const typeKeys: MessageType[] = ['comment', 'request', 'report'];
+ const typeTitleKeys: Record = {
+ comment: 'support_page.comment',
+ request: 'support_page.request',
+ report: 'support_page.report',
+ };
+ const typeDescKeys: Record = {
+ comment: 'support_page.comment_desc',
+ request: 'support_page.request_desc',
+ report: 'support_page.report_desc',
+ };
+
+ return (
+
+
+
+
+ {t('support_page.title')}
+
+
{t('support_page.subtitle')}
+
+
+
+
+
+ {success ? (
+
+
+
+ {t('support_page.success_title')}
+
+
{t('support_page.success_body')}
+
setSuccess(false)}
+ className="bg-[#d0bcff] text-[#37265e] border-none px-6 py-3 rounded-full font-semibold cursor-pointer"
+ >
+ {t('support_page.success_reset')}
+
+
+ ) : (
+
{
+ void handleSubmit(e);
+ }}
+ >
+
+
+ {t('support_page.name_label')}
+
+
+
+
+
+
+
+
+
+ {t('support_page.type_label')}
+
+
+ {typeKeys.map((type) => {
+ const Icon = TYPE_ICONS[type];
+ return (
+
setMessageType(type)}
+ className={`h-full bg-[#1c1b1f] border p-6 rounded-2xl transition-all text-left ${messageType === type ? 'bg-[#4a4359] border-[#d0bcff]' : 'border-[#49454f]/30 hover:border-[#948f9a]'}`}
+ >
+
+
+
+
+
+ {t(typeTitleKeys[type] as Parameters[0])}
+
+
+ {t(typeDescKeys[type] as Parameters[0])}
+
+
+
+ );
+ })}
+
+
+
+
+
+ {t('support_page.desc_label')}
+
+
+
+
+
+
+ {t('support_page.email_label')}
+
+
+
+
+
+
+
+ {error && {error}
}
+
+
+
+ {loading ? (
+ <>
+ {t('support_page.sending')}
+
+ >
+ ) : (
+ <>
+ {t('support_page.submit')}
+
+ >
+ )}
+
+
+
+ )}
+
+
+
+
+
+
+
+ {t('support_page.privacy_title')}
+
+
{t('support_page.privacy_desc')}
+
+
+
+
+
+
+ {t('support_page.response_title')}
+
+
{t('support_page.response_desc')}
+
+
+
+
+
+
+
+ );
+}
diff --git a/src/components/ui/MobileBottomNav.tsx b/src/components/ui/MobileBottomNav.tsx
new file mode 100644
index 0000000..bb648ea
--- /dev/null
+++ b/src/components/ui/MobileBottomNav.tsx
@@ -0,0 +1,44 @@
+'use client';
+
+import Link from 'next/link';
+import { Home } from '@icons';
+import type { ComponentType } from 'react';
+
+interface MobileBottomNavProps {
+ /** href for the left Home button */
+ homeHref: string;
+ /** Label shown under the Home icon */
+ homeLabel: string;
+ /** Icon component for the right (active) tab — any lucide-react or custom icon */
+ activeIcon: ComponentType<{ size?: number | string; className?: string }>;
+ /** Label shown under the active icon */
+ activeLabel: string;
+}
+
+export default function MobileBottomNav({
+ homeHref,
+ homeLabel,
+ activeIcon: ActiveIcon,
+ activeLabel,
+}: MobileBottomNavProps) {
+ return (
+
+
+
+
+ {homeLabel}
+
+
+
+
+
+ );
+}
diff --git a/src/config/links.ts b/src/config/links.ts
new file mode 100644
index 0000000..1909519
--- /dev/null
+++ b/src/config/links.ts
@@ -0,0 +1,30 @@
+export const DOMAIN = 'https://opentune.netlify.app';
+
+export const EXTERNAL_LINKS = {
+ GITHUB_REPO: 'https://github.com/Arturo254/OpenTune',
+ GITHUB_DEV: 'https://github.com/Arturo254',
+ WHATSAPP: 'https://wa.me/+525576847925',
+ LICENSE: 'https://raw.githubusercontent.com/Arturo254/OpenTune/master/LICENSE',
+ DEMO: 'https://appetize.io/app/b_yb62tcjuqzqjvctnswv3krpnmm',
+ FORMSPREE: 'https://formspree.io/f/xgvallrq',
+ GTM: 'https://www.googletagmanager.com/gtag/js?id=G-6JTGMLLK8S',
+ GITHUB_API: 'https://api.github.com/repos',
+ GITHUB_BASE: 'https://github.com',
+ CONTRIBUTORS_GRAPH: 'https://github.com/Arturo254/OpenTune/graphs/contributors',
+ COMMITS_MAIN: 'https://github.com/Arturo254/OpenTune/commits/main',
+ AVATAR_GITHUB: 'https://avatars.githubusercontent.com/u/87346871',
+ FLOWBITE_404_IMG: 'https://flowbite.s3.amazonaws.com/blocks/marketing-ui/404/404-computer.svg',
+ FLAG_US: 'https://flagicons.lipis.dev/flags/4x3/us.svg',
+ FLAG_ES: 'https://flagicons.lipis.dev/flags/4x3/es.svg',
+ FLAG_IN: 'https://flagicons.lipis.dev/flags/4x3/in.svg',
+ FLAG_PT: 'https://flagicons.lipis.dev/flags/4x3/pt.svg',
+} as const;
+
+export const PATHS = {
+ HOME: '/',
+ SUPPORT: '/support',
+ CONTRIBUTORS: '/contributors',
+ FEATURES: '#features',
+ DOWNLOADS: '#downloads',
+ SCREENSHOTS: '#screenshots',
+} as const;
diff --git a/src/config/locales.ts b/src/config/locales.ts
new file mode 100644
index 0000000..8a53041
--- /dev/null
+++ b/src/config/locales.ts
@@ -0,0 +1,13 @@
+import { EXTERNAL_LINKS } from './links';
+
+export const locales = ['en', 'es', 'hi', 'pt'] as const;
+export type Locale = (typeof locales)[number];
+
+export const defaultLocale: Locale = 'en';
+
+export const localeConfig: Record = {
+ en: { id: 'en', displayName: 'English', flag: EXTERNAL_LINKS.FLAG_US },
+ es: { id: 'es', displayName: 'Español', flag: EXTERNAL_LINKS.FLAG_ES },
+ hi: { id: 'hi', displayName: 'हिन्दी', flag: EXTERNAL_LINKS.FLAG_IN },
+ pt: { id: 'pt', displayName: 'Português', flag: EXTERNAL_LINKS.FLAG_PT },
+};
diff --git a/src/i18n/request.ts b/src/i18n/request.ts
new file mode 100644
index 0000000..14782cf
--- /dev/null
+++ b/src/i18n/request.ts
@@ -0,0 +1,24 @@
+import { getRequestConfig } from 'next-intl/server';
+import { routing } from '@/i18n/routing';
+import { type Locale } from '@config/locales';
+import type { AbstractIntlMessages } from 'next-intl';
+
+import en from '../messages/en.json';
+import es from '../messages/es.json';
+import hi from '../messages/hi.json';
+import pt from '../messages/pt.json';
+
+const messages: Record = { en, es, hi, pt };
+
+export default getRequestConfig(async ({ requestLocale }) => {
+ let locale = (await requestLocale) as Locale | undefined;
+
+ if (!locale || !routing.locales.includes(locale)) {
+ locale = routing.defaultLocale;
+ }
+
+ return {
+ locale,
+ messages: messages[locale],
+ };
+});
diff --git a/src/i18n/routing.ts b/src/i18n/routing.ts
new file mode 100644
index 0000000..21d99b3
--- /dev/null
+++ b/src/i18n/routing.ts
@@ -0,0 +1,11 @@
+import { createNavigation } from 'next-intl/navigation';
+import { defineRouting } from 'next-intl/routing';
+import { locales, defaultLocale } from '@config/locales';
+
+export const routing = defineRouting({
+ locales,
+ defaultLocale,
+ localeCookie: true,
+});
+
+export const { Link, redirect, usePathname, useRouter, getPathname } = createNavigation(routing);
diff --git a/src/lib/github.ts b/src/lib/github.ts
new file mode 100644
index 0000000..294a719
--- /dev/null
+++ b/src/lib/github.ts
@@ -0,0 +1,156 @@
+import type { GitHubRelease, GitHubContributor, GitHubCommit, GitHubRepo } from '@t/github';
+import { EXTERNAL_LINKS } from '@config/links';
+
+const GITHUB_API = EXTERNAL_LINKS.GITHUB_API;
+
+export const REPOS = {
+ android: 'Arturo254/OpenTune',
+} as const;
+
+export function formatNumber(num: number): string {
+ if (num >= 1_000_000) {
+ return (num / 1_000_000).toFixed(1) + 'M';
+ }
+ if (num >= 1_000) {
+ return (num / 1_000).toFixed(1) + 'k';
+ }
+ return num.toString();
+}
+
+export async function fetchLatestRelease(repo: string): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}/releases/latest`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ if (!res.ok) {
+ return null;
+ }
+ return res.json() as Promise;
+ } catch {
+ return null;
+ }
+}
+
+export async function fetchAllReleases(repo: string): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}/releases?per_page=20`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ if (!res.ok) {
+ return [];
+ }
+ return res.json() as Promise;
+ } catch {
+ return [];
+ }
+}
+
+export async function fetchRepo(repo: string): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ if (!res.ok) {
+ return null;
+ }
+ return res.json() as Promise;
+ } catch {
+ return null;
+ }
+}
+
+export async function fetchContributors(repo: string): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}/contributors?per_page=100`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ if (!res.ok) {
+ return [];
+ }
+ return res.json() as Promise;
+ } catch {
+ return [];
+ }
+}
+
+export async function fetchRecentCommits(repo: string, count = 4): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}/commits?per_page=${count}`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ if (!res.ok) {
+ return [];
+ }
+ return res.json() as Promise;
+ } catch {
+ return [];
+ }
+}
+
+export async function fetchTotalCommits(repo: string): Promise {
+ try {
+ const res = await fetch(`${GITHUB_API}/${repo}/commits?per_page=1`, {
+ next: { revalidate: 3600 },
+ signal: AbortSignal.timeout(5000),
+ });
+ const link = res.headers.get('Link');
+ if (link) {
+ const match = link.match(/page=(\d+)>; rel="last"/);
+ if (match?.[1]) {
+ return formatNumber(parseInt(match[1], 10));
+ }
+ }
+ const data = await res.json();
+ return formatNumber(data.length);
+ } catch {
+ return 'N/A';
+ }
+}
+
+export function getContributorRole(contributions: number, login: string): string {
+ if (login === 'Arturo254') {
+ return 'Lead Developer';
+ }
+ if (contributions > 100) {
+ return 'Core Contributor';
+ }
+ if (contributions > 30) {
+ return 'Major Contributor';
+ }
+ if (contributions > 10) {
+ return 'Contributor';
+ }
+ return 'Supporter';
+}
+
+export function buildDownloadUrl(repo: string, version: string): string {
+ const base = EXTERNAL_LINKS.GITHUB_BASE;
+ if (version === 'latest') {
+ return `${base}/${repo}/releases/latest/download/app-universal-release.apk`;
+ }
+ return `${base}/${repo}/releases/download/${version}/app-universal-release.apk`;
+}
+
+export async function fetchWithTimeout(
+ input: string | URL | Request,
+ init?: RequestInit & { timeoutMs?: number }
+): Promise {
+ const { timeoutMs = 5000, ...fetchOptions } = init || {};
+
+ const controller = new AbortController();
+ const id = setTimeout(() => controller.abort(), timeoutMs);
+
+ try {
+ return await fetch(input, {
+ ...fetchOptions,
+ signal: controller.signal,
+ });
+ } finally {
+ clearTimeout(id);
+ }
+}
diff --git a/src/messages/en.json b/src/messages/en.json
new file mode 100644
index 0000000..aacf61b
--- /dev/null
+++ b/src/messages/en.json
@@ -0,0 +1,204 @@
+{
+ "nav": {
+ "features": "Features",
+ "screenshots": "Screenshots",
+ "downloads": "Downloads",
+ "download": "Download"
+ },
+ "lang": {
+ "title": "Language",
+ "select": "Select Language",
+ "close": "Close"
+ },
+ "hero": {
+ "badge": "Developed By Arthur Dev Studio",
+ "subtitle": "A YouTube Music client with Material Design 3, for Android.",
+ "download_apk": "Download APK",
+ "try_demo": "Try Demo"
+ },
+ "warning": {
+ "title": "Redirect Notice",
+ "body": "You will be redirected outside of OpenTune Web to an external demo provided by Appetize.io.",
+ "cancel": "Cancel",
+ "continue": "Continue"
+ },
+ "features": {
+ "title": "Features",
+ "subtitle": "Discover all the features that make OpenTune the best client for your music.",
+ "yt_title": "YouTube Music Client with Material Design 3",
+ "yt_desc": "Provides a modern and elegant YouTube Music experience on Android, with an intuitive and optimized interface.",
+ "design_title": "Modern and Attractive Design",
+ "design_desc": "Based on Material Design 3, with vivid colors, modern typography, and smooth transitions.",
+ "ui_title": "Intuitive and Customizable Interface",
+ "ui_desc": "Simplified interface for quick access to all features, with customization options.",
+ "explore_title": "Explore and Discover Music",
+ "explore_desc": "Access to a vast music catalog with advanced search and smart recommendations.",
+ "quality_title": "High-Quality Playback",
+ "quality_desc": "High-quality music, optimized for different devices and network conditions.",
+ "playlist_title": "Playlists and Downloads",
+ "playlist_desc": "Create and organize playlists, and download songs to listen offline.",
+ "sync_title": "YouTube Music Integration",
+ "sync_desc": "Sync your YouTube Music account to access your libraries and receive notifications.",
+ "advanced_title": "Advanced Features",
+ "advanced_desc": "Voice and gesture playback control, with Chromecast and Bluetooth support.",
+ "library_title": "Efficient Library Management",
+ "library_desc": "Organize your music with advanced filtering and categorization tools.",
+ "multilang_title": "Multilingual Support",
+ "multilang_desc": "Available in multiple languages with accurate translations and culturally relevant localization.",
+ "updates_title": "Continuous Updates",
+ "updates_desc": "Regular updates with new features, security improvements, and optimization.",
+ "ux_title": "Optimized User Experience",
+ "ux_desc": "Intuitive navigation and agile performance with a user-friendly interface that enhances the experience."
+ },
+ "oss": {
+ "badge": "COMMUNITY DRIVEN",
+ "title": "Open Source at its Heart",
+ "body": "OpenTune is built by music lovers for music lovers. Contribute, customize, and help us build the best open source player for Android.",
+ "star": "Star on GitHub",
+ "license": "GPL-3.0",
+ "lead": "Lead Developer",
+ "latest_version": "Latest stable version",
+ "loading": "Loading..."
+ },
+ "screenshots": {
+ "title": "The Interface",
+ "subtitle": "A glimpse at the OpenTune experience.",
+ "expand": "Expand/Collapse",
+ "player": "Immersive Player",
+ "library": "Smart Library",
+ "settings": "Advanced Settings"
+ },
+ "support": {
+ "title": "Need help?",
+ "body": "Submit a request or report an issue directly to the development team.",
+ "cta": "Request help"
+ },
+ "downloads": {
+ "title": "Downloads",
+ "subtitle": "Download the latest version of OpenTune for your platform.",
+ "android_subtitle": "Download the latest stable version.",
+ "stable": "Stable Version",
+ "loading": "Loading...",
+ "changelog": "View changes",
+ "versions": "Previous versions",
+ "download_apk": "Download APK",
+ "download": "Download",
+ "android_req": "Requires Android 8.0+",
+ "windows_title": "Windows",
+ "windows_subtitle": "Development is currently closed.",
+ "closed": "Closed",
+ "unavailable": "Unavailable",
+ "not_available": "Not Available"
+ },
+ "changelog": {
+ "title": "Changelog"
+ },
+ "versions": {
+ "title": "Previous Versions",
+ "stable": "Stable",
+ "latest": "Latest"
+ },
+ "footer": {
+ "copyright": "© 2026",
+ "rights": "All Rights Reserved.",
+ "dev": "Dev",
+ "source": "Source Code",
+ "license": "License",
+ "contact": "Contact",
+ "contributors": "Contributors",
+ "support": "Support",
+ "text": "Website designed by Arturo254 and developed by Rajnish."
+ },
+ "support_page": {
+ "title": "Send Feedback",
+ "subtitle": "Send your issue or describe your request",
+ "name_label": "Your name",
+ "name_placeholder": "Enter your name here",
+ "type_label": "What do you want to tell us?",
+ "comment": "Comment",
+ "comment_desc": "Share your opinion or experience",
+ "request": "Request",
+ "request_desc": "Request a new feature",
+ "report": "Report",
+ "report_desc": "Report a problem or bug",
+ "desc_label": "Description",
+ "desc_placeholder": "Tell us more details...",
+ "email_label": "Your email (for private response)",
+ "email_placeholder": "RajnishKMehta@proton.me",
+ "submit": "Send message",
+ "failed_submit": "Submit failed",
+ "sending": "Sending...",
+ "success_title": "Message sent!",
+ "success_body": "Thank you for contacting us. We'll get back to you soon.",
+ "success_reset": "Send another message",
+ "privacy_title": "Privacy Assured",
+ "privacy_desc": "Your data is protected and will only be used to contact you about your report.",
+ "response_title": "Response Time",
+ "response_desc": "We normally respond in more than 24 hours due to high demand for requests.",
+ "validation_all": "Please fill in all fields.",
+ "validation_email": "Please enter a valid email.",
+ "error_sending": "Error sending. Please try again.",
+ "nav_home": "Home",
+ "nav_support": "Support"
+ },
+ "contributors": {
+ "badge": "Open Source Excellence",
+ "title": "Community & Contributors",
+ "subtitle": "OpenTune is built by the community, for the community. Every commit, bug report, and translation brings us closer to the best open source music experience.",
+ "stat_stars": "Stars",
+ "stat_forks": "Forks",
+ "stat_issues": "Open Issues",
+ "stat_commits": "Total Commits",
+ "stat_contributors": "Contributors",
+ "top_title": "Top Contributors",
+ "view_all": "View All",
+ "how_title": "How to Help",
+ "how_subtitle": "Choose your path and help us make OpenTune even better.",
+ "help_code_title": "Coding",
+ "help_code_desc": "Implement new features or fix bugs in our Kotlin codebase.",
+ "help_code_cta": "Explore Issues",
+ "help_design_title": "UI/UX Design",
+ "help_design_desc": "Improve the Material 3 implementation and visual polish.",
+ "help_design_cta": "Join Figma",
+ "help_translate_title": "Translation",
+ "help_translate_desc": "Help us localize OpenTune for music lovers around the world.",
+ "help_translate_cta": "Translate",
+ "help_report_title": "Report Bugs",
+ "help_report_desc": "Found a flaw? Detailed reports help us maintain stability.",
+ "help_report_cta": "Open Issue",
+ "help_donate_title": "Donate",
+ "help_donate_desc": "Support the development of OpenTune with a donation.",
+ "help_donate_cta": "Donate",
+ "activity_title": "Latest Activity",
+ "activity_link": "View Activity on GitHub",
+ "error": "Error loading data",
+ "commits_label": "commits"
+ },
+ "not_found": {
+ "title": "404 Not Found",
+ "body": "Whoops! That page doesn't exist.",
+ "home": "Go to Home"
+ },
+ "common": {
+ "home": "Home",
+ "community": "Community"
+ },
+ "metadata": {
+ "default": {
+ "title": "OpenTune | Modern YouTube Music Client",
+ "description": "A YouTube Music client with Material Design 3 for Android. Open source, elegant, and built for music lovers."
+ },
+ "home": {
+ "title": "OpenTune - YouTube Music Client with Material Design 3",
+ "description": "OpenTune is a modern, elegant, and open-source YouTube Music client for Android, built with Material Design 3."
+ },
+ "contributors": {
+ "title": "Contributors | OpenTune",
+ "description": "Meet the community and contributors building the best open-source music experience for Android."
+ },
+ "support": {
+ "title": "Support | OpenTune",
+ "description": "Need help? Report issues, request features, or contact the development team."
+ }
+ }
+}
diff --git a/src/messages/es.json b/src/messages/es.json
new file mode 100644
index 0000000..bb2685f
--- /dev/null
+++ b/src/messages/es.json
@@ -0,0 +1,204 @@
+{
+ "nav": {
+ "features": "Características",
+ "screenshots": "Capturas",
+ "downloads": "Descargas",
+ "download": "Descargar"
+ },
+ "lang": {
+ "title": "Idioma",
+ "select": "Seleccionar idioma",
+ "close": "Cerrar"
+ },
+ "hero": {
+ "badge": "Desarrollado por Arthur Dev Studio",
+ "subtitle": "Un cliente de YouTube Music con Material Design 3, para Android.",
+ "download_apk": "Descargar APK",
+ "try_demo": "Probar Demo"
+ },
+ "warning": {
+ "title": "Aviso de Redirección",
+ "body": "Serás redirigido fuera de OpenTune Web a una demo externa proporcionada por Appetize.io.",
+ "cancel": "Cancelar",
+ "continue": "Continuar"
+ },
+ "features": {
+ "title": "Características",
+ "subtitle": "Descubre todas las funciones que hacen de OpenTune el mejor cliente para tu música.",
+ "yt_title": "Cliente de YouTube Music con Material Design 3",
+ "yt_desc": "Proporciona una experiencia de YouTube Music moderna y elegante en Android, con una interfaz intuitiva y optimizada.",
+ "design_title": "Diseño Moderno y Atractivo",
+ "design_desc": "Basado en Material Design 3, con colores vivos, tipografía moderna y transiciones fluidas.",
+ "ui_title": "Interfaz Intuitiva y Personalizable",
+ "ui_desc": "Interfaz simplificada para un acceso rápido a todas las funciones, con opciones de personalización.",
+ "explore_title": "Explora y Descubre Música",
+ "explore_desc": "Acceso a un vasto catálogo musical con búsqueda avanzada y recomendaciones inteligentes.",
+ "quality_title": "Reproducción de Alta Calidad",
+ "quality_desc": "Música de alta calidad, optimizada para diferentes dispositivos y condiciones de red.",
+ "playlist_title": "Listas de Reproducción y Descargas",
+ "playlist_desc": "Crea y organiza listas de reproducción, y descarga canciones para escuchar sin conexión.",
+ "sync_title": "Integración con YouTube Music",
+ "sync_desc": "Sincroniza tu cuenta de YouTube Music para acceder a tus bibliotecas y recibir notificaciones.",
+ "advanced_title": "Funciones Avanzadas",
+ "advanced_desc": "Control de reproducción por voz y gestos, con soporte para Chromecast y Bluetooth.",
+ "library_title": "Gestión Eficiente de la Biblioteca",
+ "library_desc": "Organiza tu música con herramientas avanzadas de filtrado y categorización.",
+ "multilang_title": "Soporte Multilingüe",
+ "multilang_desc": "Disponible en varios idiomas con traducciones precisas y localización culturalmente relevante.",
+ "updates_title": "Actualizaciones Continuas",
+ "updates_desc": "Actualizaciones regulares con nuevas funciones, mejoras de seguridad y optimización.",
+ "ux_title": "Experiencia de Usuario Optimizada",
+ "ux_desc": "Navegación intuitiva y rendimiento ágil con una interfaz amigable que mejora la experiencia."
+ },
+ "oss": {
+ "badge": "IMPULSADO POR LA COMUNIDAD",
+ "title": "Código Abierto en su Corazón",
+ "body": "OpenTune es creado por amantes de la música para amantes de la música. Contribuye, personaliza y ayúdanos a construir el mejor reproductor de código abierto para Android.",
+ "star": "Estrella en GitHub",
+ "license": "GPL-3.0",
+ "lead": "Desarrollador Principal",
+ "latest_version": "Última versión estable",
+ "loading": "Cargando..."
+ },
+ "screenshots": {
+ "title": "La Interfaz",
+ "subtitle": "Un vistazo a la experiencia OpenTune.",
+ "expand": "Expandir/Colapsar",
+ "player": "Reproductor Inmersivo",
+ "library": "Biblioteca Inteligente",
+ "settings": "Ajustes Avanzados"
+ },
+ "support": {
+ "title": "¿Necesitas ayuda?",
+ "body": "Envía una solicitud o informa de un problema directamente al equipo de desarrollo.",
+ "cta": "Solicitar ayuda"
+ },
+ "downloads": {
+ "title": "Descargas",
+ "subtitle": "Descarga la última versión de OpenTune para tu plataforma.",
+ "android_subtitle": "Descarga la última versión estable.",
+ "stable": "Versión Estable",
+ "loading": "Cargando...",
+ "changelog": "Ver cambios",
+ "versions": "Versiones anteriores",
+ "download_apk": "Descargar APK",
+ "download": "Descargar",
+ "android_req": "Requiere Android 8.0+",
+ "windows_title": "Windows",
+ "windows_subtitle": "El desarrollo está actualmente cerrado.",
+ "closed": "Cerrado",
+ "unavailable": "No disponible",
+ "not_available": "No Disponible"
+ },
+ "changelog": {
+ "title": "Historial de cambios"
+ },
+ "versions": {
+ "title": "Versiones Anteriores",
+ "stable": "Estable",
+ "latest": "Última"
+ },
+ "footer": {
+ "copyright": "© 2026",
+ "rights": "Todos los derechos reservados.",
+ "dev": "Desarrollo",
+ "source": "Código Fuente",
+ "license": "Licencia",
+ "contact": "Contacto",
+ "contributors": "Colaboradores",
+ "support": "Soporte",
+ "text": "Sitio web diseñado por Arturo254 y creado por Rajnish Kumar."
+ },
+ "support_page": {
+ "title": "Enviar Comentarios",
+ "subtitle": "Envía tu problema o describe tu solicitud",
+ "name_label": "Tu nombre",
+ "name_placeholder": "Introduce tu nombre aquí",
+ "type_label": "¿Qué quieres decirnos?",
+ "comment": "Comentario",
+ "comment_desc": "Comparte tu opinión o experiencia",
+ "request": "Solicitud",
+ "request_desc": "Solicitar una nueva función",
+ "report": "Reporte",
+ "report_desc": "Informar de un problema o error",
+ "desc_label": "Descripción",
+ "desc_placeholder": "Cuéntanos más detalles...",
+ "email_label": "Tu correo electrónico (para respuesta privada)",
+ "email_placeholder": "RajnishKMehta@ejemplo.com",
+ "submit": "Enviar mensaje",
+ "failed_submit": "Error al enviar",
+ "sending": "Enviando...",
+ "success_title": "¡Mensaje enviado!",
+ "success_body": "Gracias por contactarnos. Te responderemos pronto.",
+ "success_reset": "Enviar otro mensaje",
+ "privacy_title": "Privacidad Asegurada",
+ "privacy_desc": "Tus datos están protegidos y solo se usarán para contactarte sobre tu reporte.",
+ "response_title": "Tiempo de Respuesta",
+ "response_desc": "Normalmente respondemos en más de 24 horas debido a la alta demanda de solicitudes.",
+ "validation_all": "Por favor, completa todos los campos.",
+ "validation_email": "Por favor, introduce un correo electrónico válido.",
+ "error_sending": "Error al enviar. Por favor, inténtalo de nuevo.",
+ "nav_home": "Inicio",
+ "nav_support": "Soporte"
+ },
+ "contributors": {
+ "badge": "Excelencia en Código Abierto",
+ "title": "Comunidad y Colaboradores",
+ "subtitle": "OpenTune es construido por la comunidad, para la comunidad. Cada commit, reporte de error y traducción nos acerca a la mejor experiencia musical de código abierto.",
+ "stat_stars": "Estrellas",
+ "stat_forks": "Forks",
+ "stat_issues": "Problemas Abiertos",
+ "stat_commits": "Commits Totales",
+ "stat_contributors": "Colaboradores",
+ "top_title": "Principales Colaboradores",
+ "view_all": "Ver Todo",
+ "how_title": "Cómo Ayudar",
+ "how_subtitle": "Elige tu camino y ayúdanos a hacer OpenTune aún mejor.",
+ "help_code_title": "Programación",
+ "help_code_desc": "Implementa nuevas funciones o corrige errores en nuestro código base Kotlin.",
+ "help_code_cta": "Explorar Problemas",
+ "help_design_title": "Diseño UI/UX",
+ "help_design_desc": "Mejora la implementación de Material 3 y el acabado visual.",
+ "help_design_cta": "Unirse a Figma",
+ "help_translate_title": "Traducción",
+ "help_translate_desc": "Ayúdanos a localizar OpenTune para los amantes de la música de todo el mundo.",
+ "help_translate_cta": "Traducir",
+ "help_report_title": "Informar Errores",
+ "help_report_desc": "¿Encontraste un fallo? Los reportes detallados nos ayudan a mantener la estabilidad.",
+ "help_report_cta": "Abrir Problema",
+ "help_donate_title": "Donar",
+ "help_donate_desc": "Apoya el desarrollo de OpenTune con una donación.",
+ "help_donate_cta": "Donar",
+ "activity_title": "Actividad Reciente",
+ "activity_link": "Ver Actividad en GitHub",
+ "error": "Error al cargar datos",
+ "commits_label": "commits"
+ },
+ "not_found": {
+ "title": "404 No Encontrado",
+ "body": "¡Ups! Esa página no existe.",
+ "home": "Ir al Inicio"
+ },
+ "common": {
+ "home": "Inicio",
+ "community": "Comunidad"
+ },
+ "metadata": {
+ "default": {
+ "title": "OpenTune | Cliente Moderno de YouTube Music",
+ "description": "Un cliente de YouTube Music con Material Design 3 para Android. De código abierto, elegante y creado para amantes de la música."
+ },
+ "home": {
+ "title": "OpenTune - Cliente de YouTube Music con Material Design 3",
+ "description": "OpenTune es un cliente de YouTube Music para Android moderno, elegante y de código abierto, basado en Material Design 3."
+ },
+ "contributors": {
+ "title": "Colaboradores | OpenTune",
+ "description": "Conoce a la comunidad y los colaboradores que construyen la mejor experiencia musical de código abierto para Android."
+ },
+ "support": {
+ "title": "Soporte | OpenTune",
+ "description": "¿Necesitas ayuda? Informa de problemas, solicita funciones o contacta con el equipo de desarrollo."
+ }
+ }
+}
diff --git a/src/messages/hi.json b/src/messages/hi.json
new file mode 100644
index 0000000..88a6206
--- /dev/null
+++ b/src/messages/hi.json
@@ -0,0 +1,204 @@
+{
+ "nav": {
+ "features": "विशेषताएं",
+ "screenshots": "स्क्रीनशॉट",
+ "downloads": "डाउनलोड",
+ "download": "डाउनलोड करें"
+ },
+ "lang": {
+ "title": "भाषा",
+ "select": "भाषा चुनें",
+ "close": "बंद करें"
+ },
+ "hero": {
+ "badge": "अर्थर देव स्टूडियो द्वारा विकसित",
+ "subtitle": "Android के लिए मटेरियल डिजाइन 3 के साथ एक YouTube म्यूज़िक क्लाइंट।",
+ "download_apk": "APK डाउनलोड करें",
+ "try_demo": "डेमो आज़माएँ"
+ },
+ "warning": {
+ "title": "पुनर्निर्देशन सूचना",
+ "body": "आपको OpenTune Web से बाहर Appetize.io द्वारा प्रदान किए गए एक बाहरी डेमो पर पुनर्निर्देशित किया जाएगा।",
+ "cancel": "रद्द करें",
+ "continue": "जारी रखें"
+ },
+ "features": {
+ "title": "विशेषताएं",
+ "subtitle": "उन सभी विशेषताओं को जानें जो OpenTune को आपके संगीत के लिए सबसे अच्छा क्लाइंट बनाती हैं।",
+ "yt_title": "मटेरियल डिजाइन 3 के साथ YouTube म्यूज़िक क्लाइंट",
+ "yt_desc": "एक सहज और अनुकूलित इंटरफ़ेस के साथ Android पर एक आधुनिक और सुंदर YouTube म्यूज़िक अनुभव प्रदान करता है।",
+ "design_title": "आधुनिक और आकर्षक डिज़ाइन",
+ "design_desc": "मटेरियल डिजाइन 3 पर आधारित, जीवंत रंगों, आधुनिक टाइपोग्राफी और स्मूथ ट्रांज़िशन के साथ।",
+ "ui_title": "सहज और अनुकूलन योग्य इंटरफ़ेस",
+ "ui_desc": "अनुकूलन विकल्पों के साथ, सभी सुविधाओं तक त्वरित पहुंच के लिए सरलीकृत इंटरफ़ेस।",
+ "explore_title": "संगीत खोजें और एक्सप्लोर करें",
+ "explore_desc": "उन्नत खोज और स्मार्ट अनुशंसाओं के साथ एक विशाल संगीत कैटलॉग तक पहुंच।",
+ "quality_title": "उच्च गुणवत्ता वाला प्लेबैक",
+ "quality_desc": "उच्च गुणवत्ता वाला संगीत, विभिन्न उपकरणों और नेटवर्क स्थितियों के लिए अनुकूलित।",
+ "playlist_title": "प्लेलिस्ट और डाउनलोड",
+ "playlist_desc": "प्लेलिस्ट बनाएं और व्यवस्थित करें, और ऑफ़लाइन सुनने के लिए गाने डाउनलोड करें।",
+ "sync_title": "YouTube म्यूज़िक एकीकरण",
+ "sync_desc": "अपनी लाइब्रेरी तक पहुंचने और सूचनाएं प्राप्त करने के लिए अपने YouTube म्यूज़िक खाते को सिंक करें।",
+ "advanced_title": "उन्नत सुविधाएँ",
+ "advanced_desc": "Chromecast और ब्लूटूथ समर्थन के साथ, आवाज और जेस्चर प्लेबैक नियंत्रण।",
+ "library_title": "कुशल लाइब्रेरी प्रबंधन",
+ "library_desc": "उन्नत फ़िल्टरिंग और वर्गीकरण टूल के साथ अपने संगीत को व्यवस्थित करें।",
+ "multilang_title": "बहुभाषी समर्थन",
+ "multilang_desc": "सटीक अनुवाद और सांस्कृतिक रूप से प्रासंगिक स्थानीयकरण के साथ कई भाषाओं में उपलब्ध।",
+ "updates_title": "निरंतर अपडेट",
+ "updates_desc": "नई सुविधाओं, सुरक्षा सुधारों और अनुकूलन के साथ नियमित अपडेट।",
+ "ux_title": "अनुकूलित उपयोगकर्ता अनुभव",
+ "ux_desc": "एक उपयोगकर्ता के अनुकूल इंटरफ़ेस के साथ सहज नेविगेशन और तेज़ प्रदर्शन जो अनुभव को बढ़ाता है।"
+ },
+ "oss": {
+ "badge": "समुदाय द्वारा संचालित",
+ "title": "इसके केंद्र में ओपन सोर्स",
+ "body": "OpenTune संगीत प्रेमियों द्वारा संगीत प्रेमियों के लिए बनाया गया है। योगदान दें, कस्टमाइज़ करें और Android के लिए सबसे अच्छा ओपन सोर्स प्लेयर बनाने में हमारी मदद करें।",
+ "star": "GitHub पर स्टार दें",
+ "license": "GPL-3.0",
+ "lead": "लीड डेवलपर",
+ "latest_version": "नवीनतम स्थिर संस्करण",
+ "loading": "लोड हो रहा है..."
+ },
+ "screenshots": {
+ "title": "इंटरफ़ेस",
+ "subtitle": "OpenTune अनुभव की एक झलक।",
+ "expand": "विस्तार करें/छोटा करें",
+ "player": "इमर्सिव प्लेयर",
+ "library": "स्मार्ट लाइब्रेरी",
+ "settings": "उन्नत सेटिंग्स"
+ },
+ "support": {
+ "title": "क्या आपको मदद चाहिए?",
+ "body": "विकास टीम को सीधे अनुरोध भेजें या समस्या की रिपोर्ट करें।",
+ "cta": "मदद मांगें"
+ },
+ "downloads": {
+ "title": "डाउनलोड",
+ "subtitle": "अपने प्लेटफॉर्म के लिए OpenTune का नवीनतम संस्करण डाउनलोड करें।",
+ "android_subtitle": "नवीनतम स्थिर संस्करण डाउनलोड करें।",
+ "stable": "स्थिर संस्करण",
+ "loading": "लोड हो रहा है...",
+ "changelog": "बदलाव देखें",
+ "versions": "पिछले संस्करण",
+ "download_apk": "APK डाउनलोड करें",
+ "download": "डाउनलोड",
+ "android_req": "Android 8.0+ की आवश्यकता है",
+ "windows_title": "Windows",
+ "windows_subtitle": "विकास वर्तमान में बंद है।",
+ "closed": "बंद",
+ "unavailable": "अनुपलब्ध",
+ "not_available": "उपलब्ध नहीं"
+ },
+ "changelog": {
+ "title": "परिवर्तन विवरण"
+ },
+ "versions": {
+ "title": "पिछले संस्करण",
+ "stable": "स्थिर",
+ "latest": "नवीनतम"
+ },
+ "footer": {
+ "copyright": "© 2026",
+ "rights": "सर्वाधिकार सुरक्षित।",
+ "dev": "निर्माता",
+ "source": "स्रोत कोड",
+ "license": "अनुज्ञप्ति",
+ "contact": "संपर्क",
+ "contributors": "योगदानकर्ता",
+ "support": "सहायता",
+ "text": "वेबसाइट का डिज़ाइन Arturo254 द्वारा तथा निर्माण रजनीश कुमार द्वारा किया गया है।"
+ },
+ "support_page": {
+ "title": "फीडबैक भेजें",
+ "subtitle": "अपनी समस्या भेजें या अपने अनुरोध का वर्णन करें",
+ "name_label": "आपका नाम",
+ "name_placeholder": "अपना नाम यहाँ दर्ज करें",
+ "type_label": "आप हमें क्या बताना चाहते हैं?",
+ "comment": "टिप्पणी",
+ "comment_desc": "अपनी राय या अनुभव साझा करें",
+ "request": "अनुरोध",
+ "request_desc": "एक नई सुविधा का अनुरोध करें",
+ "report": "रिपोर्ट",
+ "report_desc": "किसी समस्या या बग की रिपोर्ट करें",
+ "desc_label": "विवरण",
+ "desc_placeholder": "हमें और विवरण बताएं...",
+ "email_label": "आपका ईमेल (निजी प्रतिक्रिया के लिए)",
+ "email_placeholder": "RajnishKMehta@proton.me",
+ "submit": "संदेश भेजें",
+ "failed_submit": "सबमिट विफल रहा",
+ "sending": "भेजा जा रहा है...",
+ "success_title": "संदेश भेज दिया गया!",
+ "success_body": "हमसे संपर्क करने के लिए धन्यवाद। हम जल्द ही आपसे संपर्क करेंगे।",
+ "success_reset": "एक और संदेश भेजें",
+ "privacy_title": "गोपनीयता सुनिश्चित",
+ "privacy_desc": "आपका डेटा सुरक्षित है और इसका उपयोग केवल आपकी रिपोर्ट के बारे में आपसे संपर्क करने के लिए किया जाएगा।",
+ "response_title": "प्रतिक्रिया समय",
+ "response_desc": "अनुरोधों की उच्च मांग के कारण हम आम तौर पर 24 घंटे से अधिक समय में जवाब देते हैं।",
+ "validation_all": "कृपया सभी फ़ील्ड भरें।",
+ "validation_email": "कृपया एक मान्य ईमेल दर्ज करें।",
+ "error_sending": "भेजने में त्रुटि। कृपया पुन: प्रयास करें।",
+ "nav_home": "होम",
+ "nav_support": "सहायता"
+ },
+ "contributors": {
+ "badge": "ओपन सोर्स उत्कृष्टता",
+ "title": "समुदाय और योगदानकर्ता",
+ "subtitle": "OpenTune समुदाय द्वारा, समुदाय के लिए बनाया गया है। प्रत्येक कमिट, बग रिपोर्ट और अनुवाद हमें सर्वश्रेष्ठ ओपन सोर्स संगीत अनुभव के करीब लाता है।",
+ "stat_stars": "सितारे",
+ "stat_forks": "फोर्क्स",
+ "stat_issues": "खुली समस्याएं",
+ "stat_commits": "कुल कमिट",
+ "stat_contributors": "योगदानकर्ता",
+ "top_title": "शीर्ष योगदानकर्ता",
+ "view_all": "सभी देखें",
+ "how_title": "कैसे मदद करें",
+ "how_subtitle": "अपना रास्ता चुनें और OpenTune को और बेहतर बनाने में हमारी मदद करें।",
+ "help_code_title": "कोडिंग",
+ "help_code_desc": "हमारे कोटलिन कोडबेस में नई सुविधाएँ लागू करें या बग ठीक करें।",
+ "help_code_cta": "समस्याएं खोजें",
+ "help_design_title": "UI/UX डिज़ाइन",
+ "help_design_desc": "मटेरियल 3 कार्यान्वयन और विज़ुअल पॉलिश में सुधार करें।",
+ "help_design_cta": "Figma में शामिल हों",
+ "help_translate_title": "अनुवाद",
+ "help_translate_desc": "दुनिया भर के संगीत प्रेमियों के लिए OpenTune को स्थानीयकृत करने में हमारी सहायता करें।",
+ "help_translate_cta": "अनुवाद करें",
+ "help_report_title": "बग रिपोर्ट करें",
+ "help_report_desc": "कोई खामी मिली? विस्तृत रिपोर्ट हमें स्थिरता बनाए रखने में मदद करती है।",
+ "help_report_cta": "समस्या खोलें",
+ "help_donate_title": "दान करें",
+ "help_donate_desc": "दान के साथ OpenTune के विकास का समर्थन करें।",
+ "help_donate_cta": "दान करें",
+ "activity_title": "नवीनतम गतिविधि",
+ "activity_link": "GitHub पर गतिविधि देखें",
+ "error": "डेटा लोड करने में त्रुटि",
+ "commits_label": "कमिट"
+ },
+ "not_found": {
+ "title": "404 नहीं मिला",
+ "body": "उफ़! वह पेज मौजूद नहीं है।",
+ "home": "होम पर जाएं"
+ },
+ "common": {
+ "home": "होम",
+ "community": "समुदाय"
+ },
+ "metadata": {
+ "default": {
+ "title": "OpenTune | आधुनिक YouTube म्यूज़िक क्लाइंट",
+ "description": "Android के लिए मटेरियल डिजाइन 3 के साथ एक YouTube म्यूज़िक क्लाइंट। ओपन सोर्स, सुंदर, और संगीत प्रेमियों के लिए बनाया गया।"
+ },
+ "home": {
+ "title": "OpenTune - मटेरियल डिजाइन 3 के साथ YouTube म्यूज़िक क्लाइंट",
+ "description": "OpenTune Android के लिए एक आधुनिक, सुंदर और ओपन-सोर्स YouTube म्यूज़िक क्लाइंट है, जो मटेरियल डिजाइन 3 के साथ बनाया गया है।"
+ },
+ "contributors": {
+ "title": "योगदानकर्ता | OpenTune",
+ "description": "Android के लिए सर्वश्रेष्ठ ओपन-सोर्स संगीत अनुभव बनाने वाले समुदाय और योगदानकर्ताओं से मिलें।"
+ },
+ "support": {
+ "title": "सहायता | OpenTune",
+ "description": "मदद चाहिए? समस्याओं की रिपोर्ट करें, सुविधाओं का अनुरोध करें, या विकास टीम से संपर्क करें।"
+ }
+ }
+}
diff --git a/src/messages/pt.json b/src/messages/pt.json
new file mode 100644
index 0000000..96ae1d1
--- /dev/null
+++ b/src/messages/pt.json
@@ -0,0 +1,204 @@
+{
+ "nav": {
+ "features": "Funcionalidades",
+ "screenshots": "Capturas de tela",
+ "downloads": "Downloads",
+ "download": "Baixar"
+ },
+ "lang": {
+ "title": "Idioma",
+ "select": "Selecionar Idioma",
+ "close": "Fechar"
+ },
+ "hero": {
+ "badge": "Desenvolvido por Arthur Dev Studio",
+ "subtitle": "Um cliente do YouTube Music com Material Design 3, para Android.",
+ "download_apk": "Baixar APK",
+ "try_demo": "Experimentar Demo"
+ },
+ "warning": {
+ "title": "Aviso de Redirecionamento",
+ "body": "Você será redirecionado para fora do OpenTune Web para uma demo externa fornecida pelo Appetize.io.",
+ "cancel": "Cancelar",
+ "continue": "Continuar"
+ },
+ "features": {
+ "title": "Funcionalidades",
+ "subtitle": "Descubra todas as funcionalidades que tornam o OpenTune o melhor cliente para sua música.",
+ "yt_title": "Cliente do YouTube Music com Material Design 3",
+ "yt_desc": "Oferece uma experiência moderna e elegante do YouTube Music no Android, com uma interface intuitiva e otimizada.",
+ "design_title": "Design Moderno e Atraente",
+ "design_desc": "Baseado no Material Design 3, com cores vivas, tipografia moderna e transições suaves.",
+ "ui_title": "Interface Intuitiva e Customizável",
+ "ui_desc": "Interface simplificada para acesso rápido a todas as funcionalidades, com opções de customização.",
+ "explore_title": "Explore e Descubra Músicas",
+ "explore_desc": "Acesso a um vasto catálogo de músicas com busca avançada e recomendações inteligentes.",
+ "quality_title": "Reprodução de Alta Qualidade",
+ "quality_desc": "Música de alta qualidade, otimizada para diferentes dispositivos e condições de rede.",
+ "playlist_title": "Playlists e Downloads",
+ "playlist_desc": "Crie e organize playlists, e baixe músicas para ouvir offline.",
+ "sync_title": "Integração com YouTube Music",
+ "sync_desc": "Sincronize sua conta do YouTube Music para acessar suas bibliotecas e receber notificações.",
+ "advanced_title": "Funcionalidades Avançadas",
+ "advanced_desc": "Controle de reprodução por voz e gestos, com suporte a Chromecast e Bluetooth.",
+ "library_title": "Gestão Eficiente da Biblioteca",
+ "library_desc": "Organize sua música com ferramentas avançadas de filtragem e categorização.",
+ "multilang_title": "Suporte Multilíngue",
+ "multilang_desc": "Disponível em vários idiomas com traduções precisas e localização culturalmente relevante.",
+ "updates_title": "Atualizações Contínuas",
+ "updates_desc": "Atualizações regulares com novas funcionalidades, melhorias de segurança e otimização.",
+ "ux_title": "Experiência do Usuário Otimizada",
+ "ux_desc": "Navegação intuitiva e performance ágil com uma interface amigável que aprimora a experiência."
+ },
+ "oss": {
+ "badge": "Impulsionado pela comunidade",
+ "title": "Código Aberto em seu Coração",
+ "body": "OpenTune é construído por amantes da música para amantes da música. Contribua, personalize e ajude-nos a construir o melhor player de código aberto para Android.",
+ "star": "Dar Star no GitHub",
+ "license": "GPL-3.0",
+ "lead": "Desenvolvedor Principal",
+ "latest_version": "Última versão estável",
+ "loading": "Carregando..."
+ },
+ "screenshots": {
+ "title": "A Interface",
+ "subtitle": "Um vislumbre da experiência OpenTune.",
+ "expand": "Expandir/Recolher",
+ "player": "Player Imersivo",
+ "library": "Biblioteca Inteligente",
+ "settings": "Configurações Avançadas"
+ },
+ "support": {
+ "title": "Precisa de ajuda?",
+ "body": "Envie uma solicitação ou relate um problema diretamente para a equipe de desenvolvimento.",
+ "cta": "Solicitar ajuda"
+ },
+ "downloads": {
+ "title": "Downloads",
+ "subtitle": "Baixe a versão mais recente do OpenTune para sua plataforma.",
+ "android_subtitle": "Baixe a versão estável mais recente.",
+ "stable": "Versão Estável",
+ "loading": "Carregando...",
+ "changelog": "Ver alterações",
+ "versions": "Versões anteriores",
+ "download_apk": "Baixar APK",
+ "download": "Baixar",
+ "android_req": "Requer Android 8.0+",
+ "windows_title": "Windows",
+ "windows_subtitle": "O desenvolvimento está atualmente fechado.",
+ "closed": "Fechado",
+ "unavailable": "Indisponível",
+ "not_available": "Não Disponível"
+ },
+ "changelog": {
+ "title": "Log de Alterações"
+ },
+ "versions": {
+ "title": "Versões Anteriores",
+ "stable": "Estável",
+ "latest": "Mais recente"
+ },
+ "footer": {
+ "copyright": "© 2026",
+ "rights": "Todos os Direitos Reservados.",
+ "dev": "Dev",
+ "source": "Código Fonte",
+ "license": "Licença",
+ "contact": "Contato",
+ "contributors": "Contribuidores",
+ "support": "Suporte",
+ "text": "Website projetado por Arturo254 e criado por Rajnish."
+ },
+ "support_page": {
+ "title": "Enviar Feedback",
+ "subtitle": "Envie seu problema ou descreva sua solicitação",
+ "name_label": "Seu nome",
+ "name_placeholder": "Digite seu nome aqui",
+ "type_label": "O que você quer nos dizer?",
+ "comment": "Comentário",
+ "comment_desc": "Compartilhe sua opinião ou experiência",
+ "request": "Solicitação",
+ "request_desc": "Solicitar uma nova funcionalidade",
+ "report": "Relatar",
+ "report_desc": "Relatar um problema ou bug",
+ "desc_label": "Descrição",
+ "desc_placeholder": "Conte-nos mais detalhes...",
+ "email_label": "Seu e-mail (para resposta privada)",
+ "email_placeholder": "RajnishKMehta@exemplo.com",
+ "submit": "Enviar mensagem",
+ "failed_submit": "Falha ao enviar",
+ "sending": "Enviando...",
+ "success_title": "Mensagem enviada!",
+ "success_body": "Obrigado por nos contatar. Entraremos em contato em breve.",
+ "success_reset": "Enviar outra mensagem",
+ "privacy_title": "Privacidade Garantida",
+ "privacy_desc": "Seus dados estão protegidos e serão usados apenas para contatá-lo sobre seu relato.",
+ "response_title": "Tempo de Resposta",
+ "response_desc": "Normalmente respondemos em mais de 24 horas devido à alta demanda de solicitações.",
+ "validation_all": "Por favor, preencha todos os campos.",
+ "validation_email": "Por favor, insira um e-mail válido.",
+ "error_sending": "Erro ao enviar. Por favor, tente novamente.",
+ "nav_home": "Início",
+ "nav_support": "Suporte"
+ },
+ "contributors": {
+ "badge": "Excelência em Código Aberto",
+ "title": "Comunidade e Contribuidores",
+ "subtitle": "OpenTune é construído pela comunidade, para a comunidade. Cada commit, relato de bug e tradução nos aproxima da melhor experiência de música em código aberto.",
+ "stat_stars": "Estrelas",
+ "stat_forks": "Forks",
+ "stat_issues": "Problemas Abertos",
+ "stat_commits": "Total de Commits",
+ "stat_contributors": "Contribuidores",
+ "top_title": "Principais Contribuidores",
+ "view_all": "Ver Todos",
+ "how_title": "Como Ajudar",
+ "how_subtitle": "Escolha seu caminho e ajude-nos a tornar o OpenTune ainda melhor.",
+ "help_code_title": "Programação",
+ "help_code_desc": "Implemente novas funcionalidades ou corrija bugs em nossa base de código Kotlin.",
+ "help_code_cta": "Explorar Problemas",
+ "help_design_title": "Design UI/UX",
+ "help_design_desc": "Melhore a implementação do Material 3 e o acabamento visual.",
+ "help_design_cta": "Participar no Figma",
+ "help_translate_title": "Tradução",
+ "help_translate_desc": "Ajude-nos a localizar o OpenTune para amantes da música em todo o mundo.",
+ "help_translate_cta": "Traduzir",
+ "help_report_title": "Relatar Bugs",
+ "help_report_desc": "Encontrou uma falha? Relatos detalhados nos ajudam a manter a estabilidade.",
+ "help_report_cta": "Abrir Problema",
+ "help_donate_title": "Doar",
+ "help_donate_desc": "Apoie o desenvolvimento do OpenTune com uma doação.",
+ "help_donate_cta": "Doar",
+ "activity_title": "Atividade Recente",
+ "activity_link": "Ver Atividade no GitHub",
+ "error": "Erro ao carregar dados",
+ "commits_label": "commits"
+ },
+ "not_found": {
+ "title": "404 Não Encontrado",
+ "body": "Ops! Essa página não existe.",
+ "home": "Ir para o Início"
+ },
+ "common": {
+ "home": "Início",
+ "community": "Comunidade"
+ },
+ "metadata": {
+ "default": {
+ "title": "OpenTune | Cliente Moderno de YouTube Music",
+ "description": "Um cliente do YouTube Music com Material Design 3 para Android. Código aberto, elegante e feito para amantes da música."
+ },
+ "home": {
+ "title": "OpenTune - Cliente de YouTube Music com Material Design 3",
+ "description": "OpenTune é um cliente de YouTube Music moderno, elegante e de código aberto para Android, construído com Material Design 3."
+ },
+ "contributors": {
+ "title": "Contribuidores | OpenTune",
+ "description": "Conheça a comunidade e os contribuidores que constroem a melhor experiência de música em código aberto para Android."
+ },
+ "support": {
+ "title": "Suporte | OpenTune",
+ "description": "Precisa de ajuda? Relate problemas, solicite funcionalidades ou contate a equipe de desenvolvimento."
+ }
+ }
+}
diff --git a/src/proxy.ts b/src/proxy.ts
new file mode 100644
index 0000000..7513e04
--- /dev/null
+++ b/src/proxy.ts
@@ -0,0 +1,75 @@
+import createMiddleware from 'next-intl/middleware';
+import { type NextRequest, NextResponse } from 'next/server';
+import { routing } from '@/i18n/routing';
+import { locales, defaultLocale, type Locale } from '@config/locales';
+
+const intlMiddleware = createMiddleware(routing);
+
+/** O(1) locale membership test */
+const LOCALE_SET = new Set(locales);
+
+/** Matches a leading locale segment: /en /en/path — not /english */
+const LOCALE_PREFIX_RE = new RegExp(`^/(${locales.join('|')})(/|$)`);
+
+/**
+ * Detect the user's preferred locale:
+ * 1. NEXT_LOCALE cookie
+ * 2. Accept-Language header
+ * 3. Site default
+ */
+function detectLocale(req: NextRequest): Locale {
+ const cookie = req.cookies.get('NEXT_LOCALE')?.value;
+ if (cookie && LOCALE_SET.has(cookie)) {
+ return cookie as Locale;
+ }
+
+ const acceptLang = req.headers.get('accept-language');
+ if (acceptLang) {
+ for (const seg of acceptLang.split(',')) {
+ const tag = seg.split(';')[0]!.trim().split('-')[0]!.toLowerCase();
+ if (LOCALE_SET.has(tag)) {
+ return tag as Locale;
+ }
+ }
+ }
+
+ return defaultLocale;
+}
+
+export default function middleware(req: NextRequest) {
+ const { pathname } = req.nextUrl;
+
+ // 1. Skip API routes
+ if (pathname.startsWith('/api')) {
+ return NextResponse.next();
+ }
+
+ // 2. Share-link handler — /?go= /?go=:lang/
+ if (pathname === '/') {
+ const go = req.nextUrl.searchParams.get('go');
+ if (go !== null) {
+ const raw = go.startsWith('/') ? go : `/${go}`;
+
+ // :lang placeholder → swap in the detected locale
+ if (raw === '/:lang' || raw.startsWith('/:lang/')) {
+ const locale = detectLocale(req);
+ return NextResponse.redirect(new URL(`/${locale}${raw.slice(6)}`, req.url));
+ }
+
+ // path already has a valid locale prefix → redirect as-is, no detection needed
+ if (LOCALE_PREFIX_RE.test(raw)) {
+ return NextResponse.redirect(new URL(raw, req.url));
+ }
+
+ // no locale → detect and prepend
+ const locale = detectLocale(req);
+ return NextResponse.redirect(new URL(`/${locale}${raw === '/' ? '' : raw}`, req.url));
+ }
+ }
+
+ return intlMiddleware(req);
+}
+
+export const config = {
+ matcher: ['/((?!_next|_vercel|.*\\..*|~test).*)'],
+};
diff --git a/src/types/github.ts b/src/types/github.ts
new file mode 100644
index 0000000..59db0b3
--- /dev/null
+++ b/src/types/github.ts
@@ -0,0 +1,32 @@
+export interface GitHubRelease {
+ tag_name: string;
+ published_at: string;
+ body: string | null;
+ prerelease: boolean;
+ assets: Array<{ browser_download_url: string }>;
+}
+
+export interface GitHubContributor {
+ login: string;
+ avatar_url: string;
+ html_url: string;
+ contributions: number;
+}
+
+export interface GitHubCommit {
+ sha: string;
+ commit: {
+ message: string;
+ author: {
+ name: string;
+ date: string;
+ };
+ };
+ author: { login: string } | null;
+}
+
+export interface GitHubRepo {
+ stargazers_count: number;
+ forks_count: number;
+ open_issues_count: number;
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..b4db2b2
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,49 @@
+{
+ "compilerOptions": {
+ "target": "ES2025",
+ "lib": ["dom", "dom.iterable", "ES2025"],
+ "allowJs": false,
+ "skipLibCheck": true,
+ "strict": true,
+ "noEmit": true,
+ "esModuleInterop": true,
+ "module": "esnext",
+ "moduleResolution": "bundler",
+ "resolveJsonModule": true,
+ "isolatedModules": true,
+ "jsx": "react-jsx",
+ "types": ["node"],
+ "noUnusedLocals": true,
+ "noUnusedParameters": true,
+ "noImplicitReturns": true,
+ "noFallthroughCasesInSwitch": true,
+ "noUncheckedIndexedAccess": true,
+ "noImplicitOverride": true,
+ "exactOptionalPropertyTypes": true,
+ "forceConsistentCasingInFileNames": true,
+ "incremental": true,
+ "plugins": [{ "name": "next" }],
+ "paths": {
+ "@/*": ["./src/*"],
+ "@lib/*": ["./src/lib/*"],
+ "@t/*": ["./src/types/*"],
+ "@config/*":["./src/config/*"],
+ "@msg/*": ["./src/messages/*"],
+ "@test/*": ["./src/app/~test/*"],
+ "@cmp/*": ["./src/components/*"],
+ "@ui/*": ["./src/components/ui/*"],
+ "@icon/*": ["./src/components/icons/*"],
+ "@dialog/*":["./src/components/dialogs/*"],
+ "@icons": ["./src/components/icons/_index.ts"]
+ }
+ },
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts",
+ ".next/dev/types/**/*.ts",
+ "**/*.mts"
+ ],
+ "exclude": ["node_modules"]
+}