diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md
index 11e3435..a6127ed 100644
--- a/.github/PULL_REQUEST_TEMPLATE.md
+++ b/.github/PULL_REQUEST_TEMPLATE.md
@@ -57,7 +57,7 @@ Requise pour toute modification de documentation :
Requise si la PR ne concerne pas uniquement la documentation :
- [ ] `npm ci --no-audit --no-fund` réussit sans modifier le lockfile.
-- [ ] `npm run check` réussit.
+- [ ] `npm run check` réussit, y compris le build et les tests du GUI.
- [ ] Aucun changement involontaire n'apparaît dans les profils générés.
- [ ] Le GUI a été testé sans erreur ni avertissement inattendu dans la console,
s'il est affecté.
diff --git a/.github/workflows/validate.yml b/.github/workflows/validate.yml
index b49b647..1ff0067 100644
--- a/.github/workflows/validate.yml
+++ b/.github/workflows/validate.yml
@@ -22,6 +22,9 @@ jobs:
with:
node-version: 22
cache: npm
+ cache-dependency-path: |
+ package-lock.json
+ prototype/package-lock.json
- name: Install locked dependencies
run: npm ci --no-audit --no-fund
- name: Validate manifests, documentation and tests
diff --git a/.gitignore b/.gitignore
index a4f6a31..df31c4b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,13 @@
/work/
node_modules/
*.log
+
+# Local GUI builds and design-review artifacts
+/prototype/dist/
+/prototype/audit-ux-ui/
+/prototype/design-reference/
+/prototype/qa/
+/prototype/design-qa.md
+/prototype/public/assets/ai-controller-generated*.png
+/prototype/public/assets/ai-controller-glass-v3.png
+/prototype/public/assets/glass-background-v1.png
diff --git a/README.md b/README.md
index 78eec6d..576c07f 100644
--- a/README.md
+++ b/README.md
@@ -31,6 +31,7 @@ Le dépôt contient désormais :
- un manifeste portable V1 et des schémas réutilisables ;
- un mapping physique Claude avec couleur, cadran, joystick et AppSense ;
- une représentation SVG originale du Codex Micro ;
+- un configurateur graphique local lancé par `npm run configure` ;
- un outil Node.js de diagnostic, inventaire, sauvegarde, dry-run, sanitation et
rollback ;
- un générateur local de profile Input `0.17.3` qui préserve le layer natif et
@@ -44,9 +45,11 @@ L'analyse du package officiel confirme des commandes **Import layer** et
attendue. Le vrai fichier Claude n'est volontairement pas fabriqué : ses objets
internes doivent provenir d'un export réel du Codex Micro.
-Le preset est `hardware-observed` : le générateur a été validé sur un export
-Input `0.17.3`, mais le round-trip d'un artefact `*-layer.json` et la checklist
-matérielle complète restent ouverts.
+Le manifeste V1 est `hardware-observed` : le générateur a été validé sur un
+export Input `0.17.3`, mais le round-trip d'un artefact `*-layer.json` et la
+checklist matérielle complète restent ouverts. Le fichier logique
+`macos.example.json` reste séparément `proposal-not-applied` : ce n'est pas un
+preset universel à importer.
## Mapping Claude proposé
@@ -69,6 +72,10 @@ AppSense et `Auto detect`.

+Les raccourcis globaux de Claude restent hors du layer AppSense : double appui
+sur Option pour la saisie rapide et Verr. Maj. pour la dictée globale. Ils
+doivent rester disponibles quand une autre application est au premier plan.
+
## Ce qui est interdit par défaut
- Retour/Entrée et envoi de message ;
@@ -93,6 +100,23 @@ actif.
Voir la [matrice de compatibilité](docs/compatibility.md) pour distinguer les
faits, tests de fixture et validations matérielles manquantes.
+## Configurateur graphique
+
+```sh
+npm run configure
+```
+
+La première ouverture peut installer les dépendances verrouillées du GUI, puis
+lance l'interface uniquement sur `127.0.0.1` et ouvre le navigateur.
+
+Le parcours demande un export officiel Work Louder Input qui contient
+exactement un layer `Claude`, hors index `0`, déjà lié à Claude Desktop avec
+AppSense. L'utilisateur peut vérifier et personnaliser les quatre touches, le
+cadran et le joystick, puis télécharger son propre
+`Claude-macOS-profile.json`. Le fichier source, le layer natif, les autres
+layers et les autres liens AppSense sont conservés. Le JSON logique public
+n'est jamais présenté comme directement importable.
+
## Démarrage sans modification
Prérequis : Node.js 18 ou version ultérieure. Les dépendances de validation
@@ -168,13 +192,17 @@ profiles/
assets/layout.svg aperçu original
artifacts/ futur export officiel assaini
scripts/
+ configure.mjs lancement local du configurateur graphique
+ prepare-gui.mjs préparation verrouillée des dépendances du GUI
build-input-profile.mjs génération locale du profile importable
input-layer.mjs diagnostic, sauvegarde et installation guidée
+ lib/ fonctions de validation et de preset
+ validate-profile.mjs contrat logique Claude historique
+ validate-presets.mjs invariants de la bibliothèque
+prototype/
+ src/ interface locale du configurateur
shared/
input-profile.mjs transformation canonique partagée avec le GUI
- lib/ fonctions testables
- validate-profile.mjs contrat Claude historique
- validate-presets.mjs invariants de la bibliothèque
tests/
input-layer.test.mjs sauvegarde, rollback, sanitation et idempotence
docs/
diff --git a/SECURITY.md b/SECURITY.md
index 0c3943c..67fa8e1 100644
--- a/SECURITY.md
+++ b/SECURITY.md
@@ -55,13 +55,17 @@ Tant que la piste n'est pas auditée :
## Dépendances et scripts
Le projet ne transmet pas les profils ou exports Work Louder à un service
-distant. Le générateur et les validateurs les traitent localement.
+distant. Le configurateur, le générateur et les validateurs les traitent
+localement, sans télémétrie applicative.
Une connexion réseau peut toutefois être utilisée par
`npm ci --no-audit --no-fund` pour télécharger depuis le registre configuré les
-versions verrouillées dans `package-lock.json`. Toute nouvelle dépendance ou
-communication distante doit être justifiée, verrouillée, documentée et auditée
-avant publication.
+versions verrouillées dans `package-lock.json`. Lors de leur première
+exécution, `npm run configure` et `npm run check` peuvent de même lancer
+`npm ci --ignore-scripts` dans `prototype/` si les dépendances du GUI sont
+absentes. Une fois installées, le traitement des profils reste local. Toute
+nouvelle dépendance ou communication distante doit être justifiée, verrouillée,
+documentée et auditée avant publication.
## Sauvegarde et restauration
diff --git a/docs/codex-micro/configuration.md b/docs/codex-micro/configuration.md
index e6a8114..319e5f7 100644
--- a/docs/codex-micro/configuration.md
+++ b/docs/codex-micro/configuration.md
@@ -15,6 +15,11 @@
Cette intégration utilise les raccourcis HID et AppSense. Elle ne dépend pas du
protocole expérimental Hardware Buddy.
+Le manifeste et le mapping V1 décrivent l'état observé du générateur. Le fichier
+logique `macos.example.json` reste `proposal-not-applied` et ne doit pas être
+importé tel quel. `npm run configure` fabrique uniquement un profile personnel
+à partir de l'export officiel de l'utilisateur.
+
## Préservation obligatoire
Le preset et l'outil imposent les règles suivantes :
@@ -83,10 +88,12 @@ obligatoire : il ne doit pas être supposé à partir de la seule documentation.
- `git push` ;
- déploiement ;
- terminal, shell ou commande destructive ;
-- raccourci global Saisie rapide.
+- raccourcis globaux Saisie rapide et Dictée.
Les raccourcis globaux sont volontairement hors du layer AppSense : ils doivent
-rester utilisables quand une autre application est au premier plan.
+rester utilisables quand une autre application est au premier plan. Sur la
+configuration documentée, il s'agit du double appui sur Option pour la saisie
+rapide et de Verr. Maj. pour la dictée globale.
## Partage officiel observé
diff --git a/docs/installation.md b/docs/installation.md
index 20b788e..cf08eb8 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -8,6 +8,29 @@ layer dans une copie et produit un nouveau `*-profile.json`.
Il ne clique jamais à votre place, ne lance pas `Reset settings`, ne flashe
aucun firmware et ne modifie aucun raccourci système.
+## Parcours GUI recommandé
+
+Depuis la racine du dépôt :
+
+```sh
+npm run configure
+```
+
+La première ouverture peut préparer les dépendances verrouillées de
+`prototype/`. Le configurateur s'ouvre ensuite localement dans le navigateur.
+
+1. exporter le profile actif depuis Work Louder Input ;
+2. déposer ce `*-profile.json` dans le configurateur ;
+3. vérifier l'unique layer `Claude`, son lien AppSense et l'index natif `0` ;
+4. personnaliser les quatre touches, le cadran et le joystick ;
+5. télécharger `Claude-macOS-profile.json` ;
+6. l'importer avec **Add New**, sans remplacer le profile source.
+
+La génération refuse un mauvais appareil, un layer Claude absent ou dupliqué,
+une cible à l'index `0` et un lien AppSense manquant. Elle conserve le layer
+natif, les autres layers, les autres profils et les autres liens AppSense. Le
+reste de ce guide décrit le même parcours avec les contrôles CLI détaillés.
+
## 1. Vérifier le dépôt
Depuis la copie locale :
diff --git a/package.json b/package.json
index 8625869..2327152 100644
--- a/package.json
+++ b/package.json
@@ -7,10 +7,14 @@
"node": ">=18"
},
"scripts": {
+ "configure": "node scripts/configure.mjs",
"build:profile": "node scripts/build-input-profile.mjs",
"validate": "node scripts/validate-profile.mjs && node scripts/validate-presets.mjs && node scripts/check-doc-links.mjs",
- "test": "node --test",
- "check": "npm run validate && npm test"
+ "test": "node --test tests/*.test.mjs",
+ "prepare:gui": "node scripts/prepare-gui.mjs",
+ "build:gui": "npm run prepare:gui && npm --prefix prototype run build",
+ "test:gui": "npm --prefix prototype run test:sites",
+ "check": "npm run validate && npm test && npm run build:gui && npm run test:gui"
},
"devDependencies": {
"ajv": "8.17.1"
diff --git a/profiles/claude-shortcuts/README.md b/profiles/claude-shortcuts/README.md
index c356915..d15bce8 100644
--- a/profiles/claude-shortcuts/README.md
+++ b/profiles/claude-shortcuts/README.md
@@ -16,16 +16,18 @@ lié avec AppSense et distinct du layer Codex natif protégé à l'index `0`.
- validation complète de toutes les commandes, de la perte de focus et du
rollback matériel : **encore requise**.
-Le statut reste `hardware-observed`. Aucun artefact layer ne doit être présenté
-comme prêt à l'emploi avant le round-trip matériel et la vérification de son
-SHA-256.
+Le manifeste V1 reste `hardware-observed`. Le contrat logique
+`macos.example.json` reste séparément `proposal-not-applied`. Aucun des deux ne
+doit être présenté comme un fichier universel prêt à importer, et aucun
+artefact layer ne doit être publié avant le round-trip matériel et la
+vérification de son SHA-256.
## Fichiers
```text
manifest.json identité, compatibilité, preuve et installation
mapping.json mapping physique et règles de sécurité
-macos.example.json contrat logique historique, aligné sur la V1
+macos.example.json proposition logique V0 non importable
schema.json schéma du contrat historique
assets/layout.svg représentation originale du clavier
artifacts/README.md porte d'entrée du futur export officiel
@@ -64,6 +66,20 @@ com.anthropic.claudefordesktop
La politique exige exactement un layer Claude et conserve son `linkedAppId`.
L'outil ne crée pas de second lien et ne modifie jamais les autres liens.
+## Configurateur local
+
+```sh
+npm run configure
+```
+
+Le GUI charge un export officiel appartenant à l'utilisateur, permet de
+personnaliser les contrôles sûrs et génère un nouveau
+`Claude-macOS-profile.json`. Seul ce fichier personnel est destiné au flux
+**Add New** d'Input ; le contrat logique du dépôt ne l'est pas.
+
+Le double appui sur Option pour la saisie rapide et Verr. Maj. pour la dictée
+globale restent hors du layer AppSense.
+
## Sécurité
Le validateur interdit dans les contrôles actifs : Retour/Entrée, envoi,
diff --git a/profiles/claude-shortcuts/macos.example.json b/profiles/claude-shortcuts/macos.example.json
index 321c1c7..73b7931 100644
--- a/profiles/claude-shortcuts/macos.example.json
+++ b/profiles/claude-shortcuts/macos.example.json
@@ -1,7 +1,7 @@
{
"$schema": "./schema.json",
- "id": "claude-desktop-macos-codex-micro-v1",
- "status": "hardware-observed",
+ "id": "claude-desktop-macos-codex-micro-v0",
+ "status": "proposal-not-applied",
"target": {
"platform": "macOS",
"appBundleId": "com.anthropic.claudefordesktop",
@@ -14,7 +14,7 @@
"preservation": {
"mode": "update-only-existing-layer",
"protectedLayerIndexes": [0],
- "unlistedControls": "no-action-in-new-layer",
+ "unlistedControls": "no-action-in-target-layer",
"touchLayerControl": "reserved",
"otherLayers": "unchanged",
"otherProfiles": "unchanged",
@@ -128,6 +128,11 @@
"action": "quick-entry",
"documentedShortcut": "DoublePressOption",
"reason": "Must remain reachable while another application is in the foreground."
+ },
+ {
+ "action": "voice-dictation",
+ "documentedShortcut": "CapsLock",
+ "reason": "Claude global dictation must remain independent from the AppSense-linked layer."
}
],
"excludedByDefault": [
diff --git a/profiles/claude-shortcuts/schema.json b/profiles/claude-shortcuts/schema.json
index aabc02c..704de63 100644
--- a/profiles/claude-shortcuts/schema.json
+++ b/profiles/claude-shortcuts/schema.json
@@ -1,7 +1,7 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://github.com/thannous/claude-codex-micro/raw/main/profiles/claude-shortcuts/schema.json",
- "title": "Codex Micro Claude shortcut profile v1",
+ "title": "Codex Micro Claude logical shortcut proposal v0",
"type": "object",
"additionalProperties": false,
"required": [
@@ -19,8 +19,8 @@
],
"properties": {
"$schema": { "type": "string" },
- "id": { "const": "claude-desktop-macos-codex-micro-v1" },
- "status": { "const": "hardware-observed" },
+ "id": { "const": "claude-desktop-macos-codex-micro-v0" },
+ "status": { "const": "proposal-not-applied" },
"target": {
"type": "object",
"additionalProperties": false,
@@ -68,7 +68,7 @@
"minContains": 1,
"uniqueItems": true
},
- "unlistedControls": { "const": "no-action-in-new-layer" },
+ "unlistedControls": { "const": "no-action-in-target-layer" },
"touchLayerControl": { "const": "reserved" },
"otherLayers": { "const": "unchanged" },
"otherProfiles": { "const": "unchanged" },
@@ -168,16 +168,56 @@
},
"outsideAppLinkedLayer": {
"type": "array",
- "minItems": 1,
- "items": {
- "type": "object",
- "additionalProperties": false,
- "required": ["action", "documentedShortcut", "reason"],
- "properties": {
- "action": { "const": "quick-entry" },
- "documentedShortcut": { "const": "DoublePressOption" },
- "reason": { "type": "string", "minLength": 1 }
+ "minItems": 2,
+ "maxItems": 2,
+ "uniqueItems": true,
+ "allOf": [
+ {
+ "contains": {
+ "type": "object",
+ "required": ["action"],
+ "properties": {
+ "action": { "const": "quick-entry" }
+ }
+ },
+ "minContains": 1,
+ "maxContains": 1
+ },
+ {
+ "contains": {
+ "type": "object",
+ "required": ["action"],
+ "properties": {
+ "action": { "const": "voice-dictation" }
+ }
+ },
+ "minContains": 1,
+ "maxContains": 1
}
+ ],
+ "items": {
+ "oneOf": [
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["action", "documentedShortcut", "reason"],
+ "properties": {
+ "action": { "const": "quick-entry" },
+ "documentedShortcut": { "const": "DoublePressOption" },
+ "reason": { "type": "string", "minLength": 1 }
+ }
+ },
+ {
+ "type": "object",
+ "additionalProperties": false,
+ "required": ["action", "documentedShortcut", "reason"],
+ "properties": {
+ "action": { "const": "voice-dictation" },
+ "documentedShortcut": { "const": "CapsLock" },
+ "reason": { "type": "string", "minLength": 1 }
+ }
+ }
+ ]
}
},
"excludedByDefault": {
diff --git a/prototype/.npmrc b/prototype/.npmrc
new file mode 100644
index 0000000..1223b5c
--- /dev/null
+++ b/prototype/.npmrc
@@ -0,0 +1,2 @@
+fund=false
+audit=false
diff --git a/prototype/.openai/hosting.json b/prototype/.openai/hosting.json
new file mode 100644
index 0000000..47c28cb
--- /dev/null
+++ b/prototype/.openai/hosting.json
@@ -0,0 +1,4 @@
+{
+ "d1": null,
+ "r2": null
+}
diff --git a/prototype/AGENTS.md b/prototype/AGENTS.md
new file mode 100644
index 0000000..f42028c
--- /dev/null
+++ b/prototype/AGENTS.md
@@ -0,0 +1,17 @@
+# Prototype Instructions
+
+Run the local server yourself and open the preview in the browser available to this environment. Do not give the user server-start instructions when you can run it.
+
+Before making substantial visual changes, use the Product Design plugin's `get-context` skill when the visual source is unclear or no longer matches the current goal. When the user gives durable prototype-specific design feedback, preferences, or decisions, record them in `AGENTS.md`.
+
+When implementing from a selected generated mock, treat that image as the source of truth for layout, component anatomy, density, spacing, color, typography, visible content, and hierarchy.
+
+Build app UI in `src/`. Keep `.openai/hosting.json`, `worker/index.js`, `scripts/prepare-sites-build.mjs`, and `tests/sites-worker.test.mjs` intact so the same local prototype can be handed to Sites. Before a Sites handoff, run `npm run build` and `npm run test:sites`; the build must leave `dist/client/index.html`, `dist/server/index.js`, and `dist/.openai/hosting.json`.
+
+The repository-level command `npm run configure` is the canonical entry point for end users. It must prepare missing GUI dependencies, start the local prototype, and open it in the browser.
+
+The default Claude mapping uses `Cmd+N` for a new session, user-confirmed `Cmd+D` for in-app voice, `Cmd+Shift+D` for the diff pane, and `Esc` to stop a response. Quick Entry (`double Option`) and its global dictation shortcut (`Caps Lock`) remain separate from the AppSense layer.
+
+The logical Claude V0 JSON remains `proposal-not-applied`. The GUI may generate a genuinely importable, personal Work Louder Input profile only from the user's own official Codex Micro backup. Validate the backup, preserve the native layer, other layers, and AppSense link, and create a separate `Claude macOS` profile. Never present that generated file as a universal public preset or a live agent integration.
+
+Keep the GUI focused on the user's primary job: select a physical control, assign a Claude action, review the mapping, and export it. Preserve the warm glass visual direction of the original single-screen configurator. Safety and proof-level caveats should stay concise and contextual; do not turn the interface into a compliance journey or a documentation dashboard.
diff --git a/prototype/index.html b/prototype/index.html
new file mode 100644
index 0000000..51af54b
--- /dev/null
+++ b/prototype/index.html
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+ Configurer Claude | Codex Micro
+
+
+
+
+
+
diff --git a/prototype/package-lock.json b/prototype/package-lock.json
new file mode 100644
index 0000000..610df70
--- /dev/null
+++ b/prototype/package-lock.json
@@ -0,0 +1,1677 @@
+{
+ "name": "prototype",
+ "version": "0.0.0",
+ "lockfileVersion": 3,
+ "requires": true,
+ "packages": {
+ "": {
+ "name": "prototype",
+ "version": "0.0.0",
+ "dependencies": {
+ "@vitejs/plugin-react": "5.0.4",
+ "lucide-react": "^1.27.0",
+ "react": "19.2.0",
+ "react-dom": "19.2.0",
+ "vite": "6.4.2"
+ },
+ "devDependencies": {}
+ },
+ "node_modules/@babel/code-frame": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
+ "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "js-tokens": "^4.0.0",
+ "picocolors": "^1.1.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/compat-data": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.7.tgz",
+ "integrity": "sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/core": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.7.tgz",
+ "integrity": "sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==",
+ "license": "MIT",
+ "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/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.1.0",
+ "gensync": "^1.0.0-beta.2",
+ "json5": "^2.2.3",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/babel"
+ }
+ },
+ "node_modules/@babel/generator": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.7.tgz",
+ "integrity": "sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7",
+ "@jridgewell/gen-mapping": "^0.3.12",
+ "@jridgewell/trace-mapping": "^0.3.28",
+ "jsesc": "^3.0.2"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-compilation-targets": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.29.7.tgz",
+ "integrity": "sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/compat-data": "^7.29.7",
+ "@babel/helper-validator-option": "^7.29.7",
+ "browserslist": "^4.24.0",
+ "lru-cache": "^5.1.1",
+ "semver": "^6.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-globals": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.29.7.tgz",
+ "integrity": "sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-imports": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.29.7.tgz",
+ "integrity": "sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/traverse": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-module-transforms": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.29.7.tgz",
+ "integrity": "sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-module-imports": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7",
+ "@babel/traverse": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0"
+ }
+ },
+ "node_modules/@babel/helper-plugin-utils": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.29.7.tgz",
+ "integrity": "sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-string-parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.29.7.tgz",
+ "integrity": "sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-identifier": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz",
+ "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helper-validator-option": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.29.7.tgz",
+ "integrity": "sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/helpers": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.7.tgz",
+ "integrity": "sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/template": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/parser": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.7.tgz",
+ "integrity": "sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.29.7"
+ },
+ "bin": {
+ "parser": "bin/babel-parser.js"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-self": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.29.7.tgz",
+ "integrity": "sha512-TL0hMc9xzy86VD31nUiwzd5otRAcyEPcsegCxolO0PvcXuH1v0kECe/UIznYFihpkvU5wg/jk4v0TTEFfm53fw==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/plugin-transform-react-jsx-source": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.29.7.tgz",
+ "integrity": "sha512-06IyK09H3wi4cGbhDBwp5gUGo0IKtnYa8tyTiephirPCK6fbobVGiXMMI5zLQ4aKEYP3wZ3ArU44o+8KMrSG/Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-plugin-utils": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ },
+ "peerDependencies": {
+ "@babel/core": "^7.0.0-0"
+ }
+ },
+ "node_modules/@babel/template": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.29.7.tgz",
+ "integrity": "sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/code-frame": "^7.29.7",
+ "@babel/parser": "^7.29.7",
+ "@babel/types": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/traverse": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.7.tgz",
+ "integrity": "sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==",
+ "license": "MIT",
+ "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.3.1"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@babel/types": {
+ "version": "7.29.7",
+ "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.7.tgz",
+ "integrity": "sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/helper-string-parser": "^7.29.7",
+ "@babel/helper-validator-identifier": "^7.29.7"
+ },
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/@esbuild/aix-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz",
+ "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz",
+ "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz",
+ "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/android-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz",
+ "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz",
+ "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/darwin-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz",
+ "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/freebsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz",
+ "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz",
+ "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz",
+ "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz",
+ "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-loong64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz",
+ "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-mips64el": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz",
+ "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==",
+ "cpu": [
+ "mips64el"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-ppc64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz",
+ "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-riscv64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz",
+ "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-s390x": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz",
+ "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/linux-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz",
+ "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/netbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz",
+ "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openbsd-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz",
+ "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/openharmony-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz",
+ "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/sunos-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz",
+ "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-arm64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz",
+ "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-ia32": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz",
+ "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@esbuild/win32-x64": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz",
+ "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
+ }
+ },
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
+ "license": "MIT",
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
+ }
+ },
+ "node_modules/@rolldown/pluginutils": {
+ "version": "1.0.0-beta.38",
+ "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.38.tgz",
+ "integrity": "sha512-N/ICGKleNhA5nc9XXQG/kkKHJ7S55u0x0XUJbbkmdCnFuoRkM1Il12q9q0eX19+M7KKUEPw/daUPIRnxhcxAIw==",
+ "license": "MIT"
+ },
+ "node_modules/@rollup/rollup-android-arm-eabi": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.62.2.tgz",
+ "integrity": "sha512-6o7ZLZK+BeenkZCFNDXqpbjw9bD6nuWonvS/lwQJp7NoVVxm6p3qE7qQ5jGuBjiFsgvqjD8mZAU5oWxTmbOeOg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-android-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.62.2.tgz",
+ "integrity": "sha512-BaH7BllCACHoH1LguOU56UItGfUWjujlO65kS9LAodViaN4bwIKd7oeW/ZHJ/4ljr/7MIiENnNy3HJ0zXv8Zkw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "android"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.62.2.tgz",
+ "integrity": "sha512-v39RCCvj4He82I9sFmk+M1VZ0PLM9sfsLVikjfx2hYBNALhrrOR2D3JjQA6AhlaSOgcR+RzrKY7e1+bT6SUO/A==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-darwin-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.62.2.tgz",
+ "integrity": "sha512-yl0y2vq3S3lHeuXhEdss6TWfKW8vkujImO12tn4ZkG/4oghr09LvdYm2RElVjokTQiUvDUGXLGsYeLqUMCKpGA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.62.2.tgz",
+ "integrity": "sha512-tT4pvt4qXD+vEoezupCWi+a1F0vvDiksiHc+PxRlYTOH1I6/X4id9jPxTP+Fg+545euaFT1jJVs4CEdHZAU1vw==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-freebsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.62.2.tgz",
+ "integrity": "sha512-6nU5F2wCW+qvCBhTn1pdIU3bzsIoF7EUwsCDRxilWGprQR6yd508YnH9+OKFCwpfS8pjZqDUmnCAr7exax0XCg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-gnueabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.62.2.tgz",
+ "integrity": "sha512-n1GJHPOvpIfhi3TmrCeh6S6URt9BFCt0KQE3qvexyGCTAKpR4Lg+eWvNZEqu7epxwus/8ElT3hacYEucm49SZg==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm-musleabihf": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.62.2.tgz",
+ "integrity": "sha512-JqgflS8wEB+UXV/vS1RpRbifGBeN4D5lz8D8oOFbFZw4vedvdOgCFAjfBmIMdW3yL10XpQQ0Ambepw6MXrhOnA==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.62.2.tgz",
+ "integrity": "sha512-wnFJkogWvN4jm/hQRF2UBaeUmk20j5+DmHvoyWii2b8HJDyvz1MF2OU/6ynXt2KR63rbZLWkFpoytpdc/yBuSA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-arm64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.62.2.tgz",
+ "integrity": "sha512-HVu2bp0zhvJ8xHEV9+UUs7S90VadmBSY3LcIMvozbPo4AuMGDWlz3ymHLHZPX4hR67TKTt8Qp5PJ5RBg/i+RMQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.62.2.tgz",
+ "integrity": "sha512-mQqqAV8QaoSgr9I2fKDLY2BAVvmKjWoGiu/cSYQonsLvtqwEn1E4QYfnCOcp5zoEqNhsDYin1s6jx/VJmrxlZg==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-loong64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.62.2.tgz",
+ "integrity": "sha512-IxKLoxCQ2IWi6bT2akyDUBGsOImDKB+sPp4EsTmwFQ/fMwpCKm8uLSSgP/Kx/QYUgKis6SEZ5/Nlhup0DIA0PQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.62.2.tgz",
+ "integrity": "sha512-Mk5ha2RQSgyFfmYYLkBpPnUk8D8FriBxesO1u9O75X0mHgXL1UQcH5Itl2lurWL2tj0RxV9b9tJgipac0hRY9A==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-ppc64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.62.2.tgz",
+ "integrity": "sha512-CjvEnqJL/0/TQ3TXX3OPIJ/kmBellrWd4heXUmHeJlTnmwjKpSJzoehLaL6Xk0ZnMHBu9dZuFADNOrtjF4v+2w==",
+ "cpu": [
+ "ppc64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.62.2.tgz",
+ "integrity": "sha512-1SiZbzwdkaDURsew/tSOrooKiYy7EQGT6m8ufavAi9NEyQb/6VuIxFXAL1fqa4iZe3g4NbNk4P7J32z2tw5Mgg==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-riscv64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.62.2.tgz",
+ "integrity": "sha512-nQts12zJ3NQRoE6uYljOH89v7szzLDvG2JD/vsX+vGXU8w/At1GowTZ5/7qeFQ8m7L55rpR8Okugnuo5bgjy2Q==",
+ "cpu": [
+ "riscv64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-s390x-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.62.2.tgz",
+ "integrity": "sha512-E9/ll019jhPIJgpzfZoIkBGhcz+kKNgVWYRY0zr9srBdPPFVpvOKW8VaJKUbeK+eZXyQF9ltME+Kk6affeaPgg==",
+ "cpu": [
+ "s390x"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-5BqxR/pshjey51iliyzTD5Xi3EN0aLmQ2lZ3lvefVV9c82BvrLo2/6OT55iifpWBufs6kdwWbuOKS841DrmK9A==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-linux-x64-musl": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.62.2.tgz",
+ "integrity": "sha512-uNN83XxQrRAh/w0/pmAfibcwyb6YWt4gP+dpnQKPVJshAloQ785ii8CT8ZCIxkGg9opVsvAlGhFitSm6D1Jjpg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "linux"
+ ]
+ },
+ "node_modules/@rollup/rollup-openbsd-x64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.62.2.tgz",
+ "integrity": "sha512-srjEIxSH3LRnJN6THczDHWQplqEMFiAJrTab0msUryh9kwNpkICf3Ea6q6MN/2cZwRFUNx5w+h6Hpi4QuHS6Zg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ]
+ },
+ "node_modules/@rollup/rollup-openharmony-arm64": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.62.2.tgz",
+ "integrity": "sha512-8hOJnxgbyObnCm5AlRA3A931xX19xq80RjVTKgJOvEKWqJruP/Uf12IbAOaDjjEXYRewwHLfmF0YRIdK3OwKWA==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "openharmony"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-arm64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.62.2.tgz",
+ "integrity": "sha512-mmF4AY1i0hG/bLWUctUq59gtmgaSIRa3cu/A3JFRp/sCNEme2bgDEiDS22P9FbnJB8NJNF4jPJiSP5RHQpUTDg==",
+ "cpu": [
+ "arm64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-ia32-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.62.2.tgz",
+ "integrity": "sha512-DZgkknc6jhHrk46V25vbAM0zZkyP0nSDkJB8/dRkLTxv470dOmWDqGoEJl/9A0dFfS7yE3REOwNDxpHwSLSt0Q==",
+ "cpu": [
+ "ia32"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-gnu": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.62.2.tgz",
+ "integrity": "sha512-T6xr6ucWSFto+VGajA8YH26LdpHRuP4YLHEKAtCWvJDOlnmWcDZVCI2Jmjr+IFHDlt2zRaTAKE4tfjTaWLgJBg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@rollup/rollup-win32-x64-msvc": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.62.2.tgz",
+ "integrity": "sha512-BfzEnDJOt9T8M989/lA37EcJgat01wLRnoi5dQf3QzOH7jzpqTAzdDbVfRljVr5r+jzKqpbHeyOfAaXxAd0PAA==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "win32"
+ ]
+ },
+ "node_modules/@types/babel__core": {
+ "version": "7.20.5",
+ "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz",
+ "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.20.7",
+ "@babel/types": "^7.20.7",
+ "@types/babel__generator": "*",
+ "@types/babel__template": "*",
+ "@types/babel__traverse": "*"
+ }
+ },
+ "node_modules/@types/babel__generator": {
+ "version": "7.27.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz",
+ "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__template": {
+ "version": "7.4.4",
+ "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz",
+ "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/parser": "^7.1.0",
+ "@babel/types": "^7.0.0"
+ }
+ },
+ "node_modules/@types/babel__traverse": {
+ "version": "7.28.0",
+ "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz",
+ "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/types": "^7.28.2"
+ }
+ },
+ "node_modules/@types/estree": {
+ "version": "1.0.9",
+ "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
+ "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==",
+ "license": "MIT"
+ },
+ "node_modules/@vitejs/plugin-react": {
+ "version": "5.0.4",
+ "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-5.0.4.tgz",
+ "integrity": "sha512-La0KD0vGkVkSk6K+piWDKRUyg8Rl5iAIKRMH0vMJI0Eg47bq1eOxmoObAaQG37WMW9MSyk7Cs8EIWwJC1PtzKA==",
+ "license": "MIT",
+ "dependencies": {
+ "@babel/core": "^7.28.4",
+ "@babel/plugin-transform-react-jsx-self": "^7.27.1",
+ "@babel/plugin-transform-react-jsx-source": "^7.27.1",
+ "@rolldown/pluginutils": "1.0.0-beta.38",
+ "@types/babel__core": "^7.20.5",
+ "react-refresh": "^0.17.0"
+ },
+ "engines": {
+ "node": "^20.19.0 || >=22.12.0"
+ },
+ "peerDependencies": {
+ "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
+ }
+ },
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.10.42",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.42.tgz",
+ "integrity": "sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==",
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
+ "node_modules/browserslist": {
+ "version": "4.28.5",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.5.tgz",
+ "integrity": "sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "baseline-browser-mapping": "^2.10.42",
+ "caniuse-lite": "^1.0.30001800",
+ "electron-to-chromium": "^1.5.387",
+ "node-releases": "^2.0.50",
+ "update-browserslist-db": "^1.2.3"
+ },
+ "bin": {
+ "browserslist": "cli.js"
+ },
+ "engines": {
+ "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
+ }
+ },
+ "node_modules/caniuse-lite": {
+ "version": "1.0.30001803",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001803.tgz",
+ "integrity": "sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/caniuse-lite"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "CC-BY-4.0"
+ },
+ "node_modules/convert-source-map": {
+ "version": "2.0.0",
+ "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+ "license": "MIT"
+ },
+ "node_modules/debug": {
+ "version": "4.4.3",
+ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
+ "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==",
+ "license": "MIT",
+ "dependencies": {
+ "ms": "^2.1.3"
+ },
+ "engines": {
+ "node": ">=6.0"
+ },
+ "peerDependenciesMeta": {
+ "supports-color": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/electron-to-chromium": {
+ "version": "1.5.389",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
+ "integrity": "sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==",
+ "license": "ISC"
+ },
+ "node_modules/esbuild": {
+ "version": "0.25.12",
+ "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
+ "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "bin": {
+ "esbuild": "bin/esbuild"
+ },
+ "engines": {
+ "node": ">=18"
+ },
+ "optionalDependencies": {
+ "@esbuild/aix-ppc64": "0.25.12",
+ "@esbuild/android-arm": "0.25.12",
+ "@esbuild/android-arm64": "0.25.12",
+ "@esbuild/android-x64": "0.25.12",
+ "@esbuild/darwin-arm64": "0.25.12",
+ "@esbuild/darwin-x64": "0.25.12",
+ "@esbuild/freebsd-arm64": "0.25.12",
+ "@esbuild/freebsd-x64": "0.25.12",
+ "@esbuild/linux-arm": "0.25.12",
+ "@esbuild/linux-arm64": "0.25.12",
+ "@esbuild/linux-ia32": "0.25.12",
+ "@esbuild/linux-loong64": "0.25.12",
+ "@esbuild/linux-mips64el": "0.25.12",
+ "@esbuild/linux-ppc64": "0.25.12",
+ "@esbuild/linux-riscv64": "0.25.12",
+ "@esbuild/linux-s390x": "0.25.12",
+ "@esbuild/linux-x64": "0.25.12",
+ "@esbuild/netbsd-arm64": "0.25.12",
+ "@esbuild/netbsd-x64": "0.25.12",
+ "@esbuild/openbsd-arm64": "0.25.12",
+ "@esbuild/openbsd-x64": "0.25.12",
+ "@esbuild/openharmony-arm64": "0.25.12",
+ "@esbuild/sunos-x64": "0.25.12",
+ "@esbuild/win32-arm64": "0.25.12",
+ "@esbuild/win32-ia32": "0.25.12",
+ "@esbuild/win32-x64": "0.25.12"
+ }
+ },
+ "node_modules/escalade": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
+ "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/fdir": {
+ "version": "6.5.0",
+ "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz",
+ "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "peerDependencies": {
+ "picomatch": "^3 || ^4"
+ },
+ "peerDependenciesMeta": {
+ "picomatch": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/fsevents": {
+ "version": "2.3.3",
+ "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+ "hasInstallScript": true,
+ "license": "MIT",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
+ }
+ },
+ "node_modules/gensync": {
+ "version": "1.0.0-beta.2",
+ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz",
+ "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=6.9.0"
+ }
+ },
+ "node_modules/js-tokens": {
+ "version": "4.0.0",
+ "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
+ "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==",
+ "license": "MIT"
+ },
+ "node_modules/jsesc": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
+ "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==",
+ "license": "MIT",
+ "bin": {
+ "jsesc": "bin/jsesc"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/json5": {
+ "version": "2.2.3",
+ "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
+ "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==",
+ "license": "MIT",
+ "bin": {
+ "json5": "lib/cli.js"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
+ "node_modules/lru-cache": {
+ "version": "5.1.1",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
+ "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
+ "license": "ISC",
+ "dependencies": {
+ "yallist": "^3.0.2"
+ }
+ },
+ "node_modules/lucide-react": {
+ "version": "1.27.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.27.0.tgz",
+ "integrity": "sha512-rJicGl/3Fly/E0rOH1YmPZ6e49JCnKknh1ox1vpHnkfjujAkKA6sqUZvH3MTAaXXjgexyUwgNwTJzTtYuAFYJw==",
+ "license": "ISC",
+ "peerDependencies": {
+ "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
+ }
+ },
+ "node_modules/ms": {
+ "version": "2.1.3",
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+ "license": "MIT"
+ },
+ "node_modules/nanoid": {
+ "version": "3.3.15",
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.15.tgz",
+ "integrity": "sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "bin": {
+ "nanoid": "bin/nanoid.cjs"
+ },
+ "engines": {
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
+ }
+ },
+ "node_modules/node-releases": {
+ "version": "2.0.50",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.50.tgz",
+ "integrity": "sha512-J6l92tKHX6w8Jy5nO1Vuc01NoIiRGi/d6qBKVxh+IQ8Cr3b6HbVNfKiF8ZpFKufTwpwxMmce2W3iQZ861ZRyTg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
+ },
+ "node_modules/picocolors": {
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz",
+ "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==",
+ "license": "ISC"
+ },
+ "node_modules/picomatch": {
+ "version": "4.0.5",
+ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz",
+ "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/jonschlinkert"
+ }
+ },
+ "node_modules/postcss": {
+ "version": "8.5.16",
+ "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.16.tgz",
+ "integrity": "sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/postcss/"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/postcss"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "nanoid": "^3.3.12",
+ "picocolors": "^1.1.1",
+ "source-map-js": "^1.2.1"
+ },
+ "engines": {
+ "node": "^10 || ^12 || >=14"
+ }
+ },
+ "node_modules/react": {
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react/-/react-19.2.0.tgz",
+ "integrity": "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/react-dom": {
+ "version": "19.2.0",
+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.0.tgz",
+ "integrity": "sha512-UlbRu4cAiGaIewkPyiRGJk0imDN2T3JjieT6spoL2UeSf5od4n5LB/mQ4ejmxhCFT1tYe8IvaFulzynWovsEFQ==",
+ "license": "MIT",
+ "dependencies": {
+ "scheduler": "^0.27.0"
+ },
+ "peerDependencies": {
+ "react": "^19.2.0"
+ }
+ },
+ "node_modules/react-refresh": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
+ "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/rollup": {
+ "version": "4.62.2",
+ "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
+ "integrity": "sha512-RFnrW4lhXA3s3eqHDZvN654g8OTjzRfqpIRJYczCGB6HzphckVAi/Qh4tbPUbRuDi7s1Llv8g/NspLkttY3gTA==",
+ "license": "MIT",
+ "dependencies": {
+ "@types/estree": "1.0.9"
+ },
+ "bin": {
+ "rollup": "dist/bin/rollup"
+ },
+ "engines": {
+ "node": ">=18.0.0",
+ "npm": ">=8.0.0"
+ },
+ "optionalDependencies": {
+ "@rollup/rollup-android-arm-eabi": "4.62.2",
+ "@rollup/rollup-android-arm64": "4.62.2",
+ "@rollup/rollup-darwin-arm64": "4.62.2",
+ "@rollup/rollup-darwin-x64": "4.62.2",
+ "@rollup/rollup-freebsd-arm64": "4.62.2",
+ "@rollup/rollup-freebsd-x64": "4.62.2",
+ "@rollup/rollup-linux-arm-gnueabihf": "4.62.2",
+ "@rollup/rollup-linux-arm-musleabihf": "4.62.2",
+ "@rollup/rollup-linux-arm64-gnu": "4.62.2",
+ "@rollup/rollup-linux-arm64-musl": "4.62.2",
+ "@rollup/rollup-linux-loong64-gnu": "4.62.2",
+ "@rollup/rollup-linux-loong64-musl": "4.62.2",
+ "@rollup/rollup-linux-ppc64-gnu": "4.62.2",
+ "@rollup/rollup-linux-ppc64-musl": "4.62.2",
+ "@rollup/rollup-linux-riscv64-gnu": "4.62.2",
+ "@rollup/rollup-linux-riscv64-musl": "4.62.2",
+ "@rollup/rollup-linux-s390x-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-gnu": "4.62.2",
+ "@rollup/rollup-linux-x64-musl": "4.62.2",
+ "@rollup/rollup-openbsd-x64": "4.62.2",
+ "@rollup/rollup-openharmony-arm64": "4.62.2",
+ "@rollup/rollup-win32-arm64-msvc": "4.62.2",
+ "@rollup/rollup-win32-ia32-msvc": "4.62.2",
+ "@rollup/rollup-win32-x64-gnu": "4.62.2",
+ "@rollup/rollup-win32-x64-msvc": "4.62.2",
+ "fsevents": "~2.3.2"
+ }
+ },
+ "node_modules/scheduler": {
+ "version": "0.27.0",
+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
+ "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==",
+ "license": "MIT"
+ },
+ "node_modules/semver": {
+ "version": "6.3.1",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
+ "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ }
+ },
+ "node_modules/source-map-js": {
+ "version": "1.2.1",
+ "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz",
+ "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==",
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">=0.10.0"
+ }
+ },
+ "node_modules/tinyglobby": {
+ "version": "0.2.17",
+ "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.17.tgz",
+ "integrity": "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==",
+ "license": "MIT",
+ "dependencies": {
+ "fdir": "^6.5.0",
+ "picomatch": "^4.0.4"
+ },
+ "engines": {
+ "node": ">=12.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/SuperchupuDev"
+ }
+ },
+ "node_modules/update-browserslist-db": {
+ "version": "1.2.3",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz",
+ "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==",
+ "funding": [
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/browserslist"
+ },
+ {
+ "type": "tidelift",
+ "url": "https://tidelift.com/funding/github/npm/browserslist"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/ai"
+ }
+ ],
+ "license": "MIT",
+ "dependencies": {
+ "escalade": "^3.2.0",
+ "picocolors": "^1.1.1"
+ },
+ "bin": {
+ "update-browserslist-db": "cli.js"
+ },
+ "peerDependencies": {
+ "browserslist": ">= 4.21.0"
+ }
+ },
+ "node_modules/vite": {
+ "version": "6.4.2",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-6.4.2.tgz",
+ "integrity": "sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==",
+ "license": "MIT",
+ "dependencies": {
+ "esbuild": "^0.25.0",
+ "fdir": "^6.4.4",
+ "picomatch": "^4.0.2",
+ "postcss": "^8.5.3",
+ "rollup": "^4.34.9",
+ "tinyglobby": "^0.2.13"
+ },
+ "bin": {
+ "vite": "bin/vite.js"
+ },
+ "engines": {
+ "node": "^18.0.0 || ^20.0.0 || >=22.0.0"
+ },
+ "funding": {
+ "url": "https://github.com/vitejs/vite?sponsor=1"
+ },
+ "optionalDependencies": {
+ "fsevents": "~2.3.3"
+ },
+ "peerDependencies": {
+ "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0",
+ "jiti": ">=1.21.0",
+ "less": "*",
+ "lightningcss": "^1.21.0",
+ "sass": "*",
+ "sass-embedded": "*",
+ "stylus": "*",
+ "sugarss": "*",
+ "terser": "^5.16.0",
+ "tsx": "^4.8.1",
+ "yaml": "^2.4.2"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ },
+ "jiti": {
+ "optional": true
+ },
+ "less": {
+ "optional": true
+ },
+ "lightningcss": {
+ "optional": true
+ },
+ "sass": {
+ "optional": true
+ },
+ "sass-embedded": {
+ "optional": true
+ },
+ "stylus": {
+ "optional": true
+ },
+ "sugarss": {
+ "optional": true
+ },
+ "terser": {
+ "optional": true
+ },
+ "tsx": {
+ "optional": true
+ },
+ "yaml": {
+ "optional": true
+ }
+ }
+ },
+ "node_modules/yallist": {
+ "version": "3.1.1",
+ "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
+ "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
+ "license": "ISC"
+ }
+ }
+}
diff --git a/prototype/package.json b/prototype/package.json
new file mode 100644
index 0000000..d5914c9
--- /dev/null
+++ b/prototype/package.json
@@ -0,0 +1,19 @@
+{
+ "name": "prototype",
+ "version": "0.0.0",
+ "private": true,
+ "type": "module",
+ "scripts": {
+ "dev": "vite",
+ "build": "vite build && node scripts/prepare-sites-build.mjs",
+ "preview": "vite preview",
+ "test:sites": "node --test tests/sites-worker.test.mjs"
+ },
+ "dependencies": {
+ "@vitejs/plugin-react": "5.0.4",
+ "lucide-react": "^1.27.0",
+ "react": "19.2.0",
+ "react-dom": "19.2.0",
+ "vite": "6.4.2"
+ }
+}
diff --git a/prototype/public/assets/ai-controller-claude-v1.png b/prototype/public/assets/ai-controller-claude-v1.png
new file mode 100644
index 0000000..21cd151
Binary files /dev/null and b/prototype/public/assets/ai-controller-claude-v1.png differ
diff --git a/prototype/public/assets/claude-glass-background-v1.png b/prototype/public/assets/claude-glass-background-v1.png
new file mode 100644
index 0000000..12348a2
Binary files /dev/null and b/prototype/public/assets/claude-glass-background-v1.png differ
diff --git a/prototype/scripts/prepare-sites-build.mjs b/prototype/scripts/prepare-sites-build.mjs
new file mode 100644
index 0000000..86a9e8c
--- /dev/null
+++ b/prototype/scripts/prepare-sites-build.mjs
@@ -0,0 +1,21 @@
+#!/usr/bin/env node
+import { copyFileSync, existsSync, mkdirSync } from "node:fs";
+import path from "node:path";
+import { fileURLToPath } from "node:url";
+
+const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
+const dist = path.join(root, "dist");
+const index = path.join(dist, "client", "index.html");
+const worker = path.join(root, "worker", "index.js");
+const hosting = path.join(root, ".openai", "hosting.json");
+
+for (const file of [index, worker, hosting]) {
+ if (!existsSync(file)) throw new Error("Missing Sites build input: " + file);
+}
+
+mkdirSync(path.join(dist, "server"), { recursive: true });
+mkdirSync(path.join(dist, ".openai"), { recursive: true });
+copyFileSync(worker, path.join(dist, "server", "index.js"));
+copyFileSync(hosting, path.join(dist, ".openai", "hosting.json"));
+
+console.log("Prepared Sites build: dist/server/index.js and dist/.openai/hosting.json");
diff --git a/prototype/src/App.jsx b/prototype/src/App.jsx
new file mode 100644
index 0000000..92a6de4
--- /dev/null
+++ b/prototype/src/App.jsx
@@ -0,0 +1,542 @@
+import { useEffect, useMemo, useRef, useState } from "react";
+import {
+ ArrowDownToLine,
+ Check,
+ ChevronRight,
+ Command,
+ Diff,
+ FileJson,
+ Mic,
+ MousePointer2,
+ Move,
+ RotateCcw,
+ ShieldCheck,
+ SlidersHorizontal,
+ Square,
+ X,
+} from "lucide-react";
+import {
+ buildInputProfile,
+ inspectInputProfile,
+} from "../../shared/input-profile.mjs";
+
+const CONTROLS = [
+ {
+ id: "joystick",
+ label: "Joystick",
+ shortLabel: "NAV",
+ type: "joystick",
+ x: 22.3,
+ y: 19.7,
+ w: 13.3,
+ h: 13.6,
+ },
+ {
+ id: "wheel",
+ label: "Molette",
+ shortLabel: "SCROLL",
+ type: "dial",
+ x: 65.7,
+ y: 20.1,
+ w: 12.2,
+ h: 13.2,
+ },
+ {
+ id: "key-1",
+ label: "Touche 1",
+ shortLabel: "K1",
+ type: "key",
+ x: 21.9,
+ y: 49.4,
+ w: 13.7,
+ h: 12.7,
+ },
+ {
+ id: "key-2",
+ label: "Touche 2",
+ shortLabel: "K2",
+ type: "key",
+ x: 35.5,
+ y: 49.4,
+ w: 13.7,
+ h: 12.7,
+ },
+ {
+ id: "key-3",
+ label: "Touche 3",
+ shortLabel: "K3",
+ type: "key",
+ x: 50.6,
+ y: 49.4,
+ w: 13.7,
+ h: 12.7,
+ },
+ {
+ id: "key-4",
+ label: "Touche 4",
+ shortLabel: "K4",
+ type: "key",
+ x: 64.9,
+ y: 49.4,
+ w: 13.7,
+ h: 12.7,
+ },
+];
+
+const ACTIONS = {
+ navigation: {
+ id: "navigation",
+ label: "Navigation",
+ description: "Se déplacer avec les flèches",
+ shortcut: "↑ → ↓ ←",
+ icon: Move,
+ controlTypes: ["joystick"],
+ exportLabel: "NAV",
+ action: {
+ type: "directional-keys",
+ up: "ArrowUp",
+ right: "ArrowRight",
+ down: "ArrowDown",
+ left: "ArrowLeft",
+ },
+ },
+ scroll: {
+ id: "scroll",
+ label: "Défilement",
+ description: "Faire défiler la conversation page par page",
+ shortcut: "Page ↑ Page ↓",
+ icon: MousePointer2,
+ controlTypes: ["dial"],
+ exportLabel: "SCROLL",
+ action: { type: "page-keys", counterClockwise: "PageUp", clockwise: "PageDown" },
+ },
+ newSession: {
+ id: "newSession",
+ label: "Nouvelle session",
+ description: "Ouvrir une nouvelle session Claude",
+ shortcut: "⌘ N",
+ icon: Command,
+ controlTypes: ["key"],
+ exportLabel: "NEW",
+ action: { type: "shortcut", keys: ["Command", "N"] },
+ },
+ voice: {
+ id: "voice",
+ label: "Mode vocal",
+ description: "Activer la conversation vocale",
+ shortcut: "⌘ D",
+ icon: Mic,
+ controlTypes: ["key"],
+ exportLabel: "VOICE",
+ action: { type: "shortcut", keys: ["Command", "D"] },
+ },
+ diff: {
+ id: "diff",
+ label: "Afficher le diff",
+ description: "Ouvrir ou masquer le panneau diff",
+ shortcut: "⌘ ⇧ D",
+ icon: Diff,
+ controlTypes: ["key"],
+ exportLabel: "DIFF",
+ action: { type: "shortcut", keys: ["Command", "Shift", "D"] },
+ },
+ stop: {
+ id: "stop",
+ label: "Arrêter la réponse",
+ description: "Interrompre la réponse en cours",
+ shortcut: "Esc",
+ icon: Square,
+ controlTypes: ["key"],
+ exportLabel: "ESC",
+ action: { type: "shortcut", keys: ["Escape"] },
+ },
+ none: {
+ id: "none",
+ label: "Aucune action",
+ description: "Laisser ce contrôle libre",
+ shortcut: "Non assigné",
+ icon: X,
+ controlTypes: ["key", "dial", "joystick"],
+ exportLabel: "NONE",
+ action: { type: "none" },
+ },
+};
+
+const DEFAULT_MAPPING = {
+ joystick: "navigation",
+ wheel: "scroll",
+ "key-1": "newSession",
+ "key-2": "voice",
+ "key-3": "diff",
+ "key-4": "stop",
+};
+
+export function App() {
+ const [mapping, setMapping] = useState(DEFAULT_MAPPING);
+ const [selectedControlId, setSelectedControlId] = useState("key-1");
+ const [panelOpen, setPanelOpen] = useState(false);
+ const [toast, setToast] = useState("");
+ const [sourceProfile, setSourceProfile] = useState(null);
+ const [sourceFileName, setSourceFileName] = useState("");
+ const [profileInfo, setProfileInfo] = useState(null);
+ const [profileError, setProfileError] = useState("");
+ const dialogRef = useRef(null);
+ const closeButtonRef = useRef(null);
+ const returnFocusRef = useRef(null);
+ const profileInputRef = useRef(null);
+
+ const selectedControl = useMemo(
+ () => CONTROLS.find((control) => control.id === selectedControlId) ?? CONTROLS[0],
+ [selectedControlId],
+ );
+ const selectedAction = ACTIONS[mapping[selectedControl.id]];
+ const availableActions = Object.values(ACTIONS).filter((action) =>
+ action.controlTypes.includes(selectedControl.type),
+ );
+
+ const openConfigurator = (controlId = selectedControlId) => {
+ returnFocusRef.current = document.activeElement;
+ setSelectedControlId(controlId);
+ setPanelOpen(true);
+ };
+
+ const closeConfigurator = () => setPanelOpen(false);
+
+ const assignAction = (actionId) => {
+ setMapping((current) => ({ ...current, [selectedControl.id]: actionId }));
+ };
+
+ const resetMapping = () => {
+ setMapping(DEFAULT_MAPPING);
+ setToast("Mapping Claude restauré.");
+ };
+
+ const loadSourceProfile = async (event) => {
+ const file = event.target.files?.[0];
+ event.target.value = "";
+ if (!file) return;
+
+ try {
+ const parsed = JSON.parse(await file.text());
+ const inspection = inspectInputProfile(parsed);
+ setSourceProfile(parsed);
+ setSourceFileName(file.name);
+ setProfileInfo(inspection);
+ setProfileError("");
+ setToast("Sauvegarde Input reconnue. Le profil peut être généré.");
+ } catch (error) {
+ setSourceProfile(null);
+ setSourceFileName("");
+ setProfileInfo(null);
+ setProfileError(error instanceof Error ? error.message : "Fichier Input invalide.");
+ }
+ };
+
+ const exportMapping = () => {
+ if (!sourceProfile) {
+ profileInputRef.current?.click();
+ setToast("Chargez d’abord la sauvegarde officielle de votre profil Input.");
+ return;
+ }
+
+ try {
+ const { profile } = buildInputProfile(sourceProfile, mapping);
+ const blob = new Blob([`${JSON.stringify(profile, null, 2)}\n`], {
+ type: "application/json",
+ });
+ const url = URL.createObjectURL(blob);
+ const anchor = document.createElement("a");
+ anchor.href = url;
+ anchor.download = "Claude-macOS-profile.json";
+ document.body.append(anchor);
+ anchor.click();
+ anchor.remove();
+ window.setTimeout(() => URL.revokeObjectURL(url), 1000);
+ setToast("Profil Input créé. Importez-le avec « Add New » dans Input.");
+ } catch (error) {
+ setProfileError(error instanceof Error ? error.message : "Génération impossible.");
+ }
+ };
+
+ useEffect(() => {
+ if (!toast) return undefined;
+ const timeout = window.setTimeout(() => setToast(""), 3000);
+ return () => window.clearTimeout(timeout);
+ }, [toast]);
+
+ useEffect(() => {
+ if (!panelOpen) {
+ if (returnFocusRef.current) {
+ window.requestAnimationFrame(() => returnFocusRef.current?.focus());
+ }
+ return undefined;
+ }
+
+ window.requestAnimationFrame(() => closeButtonRef.current?.focus());
+
+ const handleKeyDown = (event) => {
+ if (event.key === "Escape") {
+ event.preventDefault();
+ closeConfigurator();
+ return;
+ }
+
+ if (event.key !== "Tab" || !dialogRef.current) return;
+
+ const focusable = Array.from(
+ dialogRef.current.querySelectorAll(
+ 'button:not([disabled]), [href], [tabindex]:not([tabindex="-1"])',
+ ),
+ ).filter((element) => !element.hasAttribute("hidden"));
+ if (!focusable.length) return;
+
+ const first = focusable[0];
+ const last = focusable[focusable.length - 1];
+ if (event.shiftKey && document.activeElement === first) {
+ event.preventDefault();
+ last.focus();
+ } else if (!event.shiftKey && document.activeElement === last) {
+ event.preventDefault();
+ first.focus();
+ }
+ };
+
+ document.addEventListener("keydown", handleKeyDown);
+ return () => document.removeEventListener("keydown", handleKeyDown);
+ }, [panelOpen]);
+
+ return (
+
+
+
+
+
+
+
Contrôleur pour Claude Desktop
+
Vos raccourcis Claude, sous la main.
+
+ Choisissez une touche du Codex Micro et affectez-lui l’action Claude
+ que vous utilisez le plus.
+
+
+
+
+
+
+ {CONTROLS.map((control) => {
+ const action = ACTIONS[mapping[control.id]];
+ return (
+
openConfigurator(control.id)}
+ >
+ {action.exportLabel}
+
+ );
+ })}
+
+
+
openConfigurator("key-1")}>
+
+ Configurer les touches
+
+
+
+
+
+
+ Générer le JSON
+
+
+
+ {CONTROLS.map((control) => {
+ const action = ACTIONS[mapping[control.id]];
+ return (
+ openConfigurator(control.id)}>
+ {control.shortLabel}
+ {action.exportLabel}
+
+ );
+ })}
+
+
+
+ Votre sauvegarde reste dans ce navigateur. Le profil généré s’importe
+ ensuite avec « Add New » dans Work Louder Input.
+
+
+
+
+
+
+
+ Configuration Claude
+
{selectedControl.label}
+
+
+
+
+
+
+
+
+ {CONTROLS.map((control) => (
+ setSelectedControlId(control.id)}
+ >
+ {control.shortLabel}
+ {ACTIONS[mapping[control.id]].exportLabel}
+
+ ))}
+
+
+
+
+
Action
+ {selectedAction.shortcut}
+
+
+
+ {availableActions.map((action) => {
+ const Icon = action.icon;
+ const active = selectedAction.id === action.id;
+ return (
+ assignAction(action.id)}
+ >
+
+
+
+
+ {action.label}
+ {action.description}
+
+ {action.shortcut}
+
+ {active && }
+
+
+ );
+ })}
+
+
+
+
+
+
+ Le layer natif, les autres layers et le lien AppSense sont préservés.
+ L’import ajoute un nouveau profil « Claude macOS » : votre sauvegarde
+ reste disponible pour revenir en arrière.
+
+
+
+
+
+
+ Restaurer
+
+
+
+ {sourceProfile ? "Générer le profil" : "Charger le profil"}
+
+
+
+
+ {panelOpen && (
+
+ )}
+
+ {toast && (
+
+
+ {toast}
+
+ )}
+
+ );
+}
diff --git a/prototype/src/main.jsx b/prototype/src/main.jsx
new file mode 100644
index 0000000..78c0e05
--- /dev/null
+++ b/prototype/src/main.jsx
@@ -0,0 +1,10 @@
+import React from "react";
+import { createRoot } from "react-dom/client";
+import { App } from "./App.jsx";
+import "./styles.css";
+
+createRoot(document.getElementById("root")).render(
+
+
+ ,
+);
diff --git a/prototype/src/styles.css b/prototype/src/styles.css
new file mode 100644
index 0000000..17c8f06
--- /dev/null
+++ b/prototype/src/styles.css
@@ -0,0 +1,872 @@
+:root {
+ font-family: "Avenir Next", Avenir, "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: #453a36;
+ background: #f8eee2;
+ font-synthesis: none;
+ --ink: #453a36;
+ --muted: #786d68;
+ --soft: #9f9189;
+ --accent: #c95f43;
+ --accent-dark: #a94831;
+ --accent-soft: #f7ded4;
+ --panel: rgba(255, 251, 247, 0.94);
+ --line: rgba(84, 65, 56, 0.14);
+ --line-strong: rgba(84, 65, 56, 0.25);
+ --shadow: rgba(126, 73, 52, 0.2);
+}
+
+* {
+ box-sizing: border-box;
+}
+
+html {
+ scroll-behavior: smooth;
+}
+
+body {
+ min-width: 320px;
+ min-height: 100vh;
+ margin: 0;
+ background: #f8eee2;
+ color: var(--ink);
+ -webkit-font-smoothing: antialiased;
+}
+
+button,
+a {
+ color: inherit;
+ font: inherit;
+}
+
+button {
+ cursor: pointer;
+}
+
+button:focus-visible,
+a:focus-visible {
+ outline: 3px solid rgba(201, 95, 67, 0.42);
+ outline-offset: 3px;
+}
+
+.app-shell {
+ position: relative;
+ min-height: 100vh;
+ overflow: hidden;
+ background:
+ linear-gradient(rgba(255, 249, 241, 0.22), rgba(255, 244, 232, 0.28)),
+ url("/assets/claude-glass-background-v1.png") center / cover fixed;
+}
+
+.app-shell::after {
+ position: fixed;
+ z-index: 0;
+ inset: 0;
+ background: radial-gradient(circle at 50% 44%, transparent 0 28%, rgba(255, 247, 238, 0.22) 65%);
+ content: "";
+ pointer-events: none;
+}
+
+.page-content {
+ position: relative;
+ z-index: 1;
+ min-height: 100vh;
+}
+
+.topbar {
+ position: absolute;
+ z-index: 3;
+ top: 0;
+ right: 0;
+ left: 0;
+ display: flex;
+ max-width: 1180px;
+ align-items: center;
+ justify-content: space-between;
+ margin: 0 auto;
+ padding: 24px 28px;
+}
+
+.brand {
+ display: inline-flex;
+ align-items: center;
+ gap: 10px;
+ color: var(--ink);
+ font-size: 14px;
+ font-weight: 700;
+ text-decoration: none;
+}
+
+.brand-mark {
+ display: grid;
+ width: 30px;
+ height: 30px;
+ place-items: center;
+ border: 1px solid rgba(255, 255, 255, 0.7);
+ border-radius: 9px;
+ background: rgba(255, 250, 246, 0.62);
+ box-shadow: 0 8px 22px rgba(112, 70, 51, 0.12);
+ color: var(--accent);
+ font-size: 13px;
+}
+
+.local-status {
+ display: inline-flex;
+ align-items: center;
+ gap: 7px;
+ border: 1px solid rgba(255, 255, 255, 0.66);
+ border-radius: 999px;
+ background: rgba(255, 250, 246, 0.54);
+ padding: 7px 11px;
+ color: var(--muted);
+ font-size: 12px;
+ font-weight: 650;
+ backdrop-filter: blur(14px);
+}
+
+.local-status > span {
+ width: 6px;
+ height: 6px;
+ border-radius: 50%;
+ background: #65a87f;
+ box-shadow: 0 0 0 4px rgba(101, 168, 127, 0.13);
+}
+
+.hero {
+ display: flex;
+ min-height: 100dvh;
+ align-items: center;
+ flex-direction: column;
+ justify-content: center;
+ padding: 72px 24px 94px;
+ text-align: center;
+}
+
+.hero-copy {
+ position: relative;
+ z-index: 2;
+ max-width: 760px;
+}
+
+.eyebrow {
+ color: var(--accent);
+ font-size: 12px;
+ font-weight: 750;
+ letter-spacing: 0.14em;
+ text-transform: uppercase;
+}
+
+.hero h1 {
+ max-width: 760px;
+ margin: 14px auto 0;
+ color: #7c3f30;
+ font-size: clamp(46px, 5.5vw, 72px);
+ font-weight: 520;
+ letter-spacing: -0.06em;
+ line-height: 0.98;
+ text-wrap: balance;
+}
+
+.hero-copy p {
+ max-width: 590px;
+ margin: 20px auto 0;
+ color: var(--muted);
+ font-size: clamp(16px, 1.5vw, 19px);
+ letter-spacing: -0.015em;
+ line-height: 1.5;
+ text-wrap: pretty;
+}
+
+.device-stage {
+ position: relative;
+ width: min(510px, 72vh, 88vw);
+ margin-top: 20px;
+}
+
+.device-wrap {
+ position: relative;
+ width: 100%;
+ aspect-ratio: 1;
+}
+
+.device-image {
+ display: block;
+ width: 100%;
+ height: 100%;
+ object-fit: contain;
+ filter: drop-shadow(0 30px 40px rgba(119, 70, 50, 0.16));
+ user-select: none;
+}
+
+.key-hotspot {
+ position: absolute;
+ z-index: 2;
+ display: grid;
+ place-items: center;
+ border: 1px solid transparent;
+ border-radius: 12px;
+ background: transparent;
+ padding: 0;
+ transition:
+ background 160ms ease,
+ border-color 160ms ease,
+ box-shadow 160ms ease,
+ transform 160ms ease;
+}
+
+.key-hotspot--dial,
+.key-hotspot--joystick {
+ border-radius: 50%;
+}
+
+.key-hotspot:hover,
+.key-hotspot:focus-visible {
+ border-color: rgba(201, 95, 67, 0.74);
+ background: rgba(255, 255, 255, 0.2);
+ box-shadow: 0 0 0 5px rgba(201, 95, 67, 0.18);
+ outline: none;
+ transform: translateY(-1px);
+}
+
+.key-hotspot > span {
+ position: absolute;
+ top: calc(100% + 7px);
+ left: 50%;
+ width: max-content;
+ transform: translate(-50%, 4px);
+ border-radius: 7px;
+ background: rgba(69, 58, 54, 0.92);
+ padding: 5px 7px;
+ color: #fffaf6;
+ font: 750 9px/1 "SFMono-Regular", Consolas, monospace;
+ opacity: 0;
+ pointer-events: none;
+ transition: opacity 140ms ease, transform 140ms ease;
+}
+
+.key-hotspot:hover > span,
+.key-hotspot:focus-visible > span {
+ opacity: 1;
+ transform: translate(-50%, 0);
+}
+
+.configure-button {
+ position: absolute;
+ right: -62px;
+ bottom: 62px;
+ display: inline-flex;
+ width: min(240px, calc(100vw - 32px));
+ min-height: 50px;
+ align-items: center;
+ justify-content: center;
+ gap: 9px;
+ border: 1px solid rgba(255, 255, 255, 0.78);
+ border-radius: 999px;
+ background: rgba(255, 251, 247, 0.82);
+ padding: 0 17px;
+ color: var(--ink);
+ box-shadow:
+ 0 16px 42px rgba(112, 70, 51, 0.16),
+ inset 0 1px 0 rgba(255, 255, 255, 0.92);
+ font-size: 13px;
+ font-weight: 720;
+ backdrop-filter: blur(18px);
+ transition: background 160ms ease, box-shadow 160ms ease, transform 160ms ease;
+}
+
+.configure-button:hover {
+ background: rgba(255, 255, 255, 0.96);
+ box-shadow: 0 20px 48px rgba(112, 70, 51, 0.22);
+ transform: translateY(-2px);
+}
+
+.configure-button:active {
+ transform: translateY(0);
+}
+
+.generate-button {
+ position: relative;
+ z-index: 3;
+ display: inline-flex;
+ width: min(240px, calc(100vw - 32px));
+ min-height: 50px;
+ align-items: center;
+ justify-content: center;
+ gap: 8px;
+ margin-top: -44px;
+ border: 1px solid var(--accent);
+ border-radius: 999px;
+ background: var(--accent);
+ padding: 0 20px;
+ color: #fffaf6;
+ box-shadow: 0 14px 32px rgba(169, 72, 49, 0.24);
+ font-size: 13px;
+ font-weight: 750;
+ transition: background 160ms ease, box-shadow 160ms ease, transform 160ms ease;
+}
+
+.generate-button:hover {
+ background: var(--accent-dark);
+ box-shadow: 0 18px 38px rgba(169, 72, 49, 0.3);
+ transform: translateY(-2px);
+}
+
+.generate-button:active {
+ transform: translateY(0);
+}
+
+.mapping-preview {
+ display: flex;
+ max-width: calc(100vw - 32px);
+ align-items: center;
+ justify-content: center;
+ flex-wrap: wrap;
+ gap: 7px;
+ margin-top: 12px;
+}
+
+.mapping-preview button {
+ display: inline-flex;
+ min-height: 34px;
+ align-items: center;
+ gap: 7px;
+ border: 1px solid rgba(255, 255, 255, 0.68);
+ border-radius: 999px;
+ background: rgba(255, 250, 246, 0.52);
+ padding: 0 11px;
+ color: var(--muted);
+ backdrop-filter: blur(12px);
+ transition: background 150ms ease, transform 150ms ease;
+}
+
+.mapping-preview button:hover {
+ background: rgba(255, 255, 255, 0.88);
+ transform: translateY(-1px);
+}
+
+.mapping-preview span {
+ color: var(--soft);
+ font: 700 9px/1 "SFMono-Regular", Consolas, monospace;
+}
+
+.mapping-preview strong {
+ color: var(--ink);
+ font-size: 10px;
+}
+
+.quiet-note {
+ margin: 15px 0 0;
+ color: rgba(84, 65, 56, 0.66);
+ font-size: 11px;
+}
+
+.mapping-dialog {
+ position: fixed;
+ z-index: 30;
+ top: 12px;
+ right: 12px;
+ bottom: 12px;
+ display: flex;
+ width: min(470px, calc(100vw - 24px));
+ flex-direction: column;
+ overflow: hidden;
+ border: 1px solid rgba(255, 255, 255, 0.82);
+ border-radius: 28px;
+ background: var(--panel);
+ box-shadow: 0 32px 110px rgba(67, 51, 44, 0.28);
+ opacity: 0;
+ transform: translateX(calc(100% + 24px));
+ pointer-events: none;
+ transition:
+ opacity 180ms ease,
+ transform 280ms cubic-bezier(0.2, 0.9, 0.25, 1);
+ backdrop-filter: blur(28px) saturate(1.08);
+}
+
+.mapping-dialog.is-open {
+ opacity: 1;
+ transform: translateX(0);
+ pointer-events: auto;
+}
+
+.dialog-header {
+ display: flex;
+ align-items: flex-start;
+ justify-content: space-between;
+ border-bottom: 1px solid var(--line);
+ padding: 25px 25px 20px;
+}
+
+.dialog-header > div > span {
+ color: var(--accent);
+ font-size: 11px;
+ font-weight: 750;
+ letter-spacing: 0.11em;
+ text-transform: uppercase;
+}
+
+.dialog-header h2 {
+ margin: 6px 0 0;
+ font-size: 30px;
+ font-weight: 560;
+ letter-spacing: -0.045em;
+}
+
+.icon-button {
+ display: grid;
+ width: 44px;
+ height: 44px;
+ flex: none;
+ place-items: center;
+ border: 1px solid var(--line);
+ border-radius: 50%;
+ background: rgba(255, 255, 255, 0.58);
+ transition: background 150ms ease, transform 150ms ease;
+}
+
+.icon-button:hover {
+ background: #fff;
+ transform: rotate(4deg);
+}
+
+.dialog-scroll {
+ flex: 1;
+ overflow: auto;
+ padding: 22px 25px 28px;
+}
+
+.control-tabs {
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 7px;
+}
+
+.control-tabs button {
+ display: grid;
+ min-height: 60px;
+ align-content: center;
+ gap: 4px;
+ border: 1px solid var(--line);
+ border-radius: 14px;
+ background: rgba(255, 255, 255, 0.42);
+ padding: 9px;
+ text-align: left;
+ transition: background 150ms ease, border-color 150ms ease, transform 150ms ease;
+}
+
+.control-tabs button:hover {
+ border-color: var(--line-strong);
+ background: rgba(255, 255, 255, 0.78);
+ transform: translateY(-1px);
+}
+
+.control-tabs button.is-active {
+ border-color: rgba(201, 95, 67, 0.58);
+ background: var(--accent-soft);
+}
+
+.control-tabs span {
+ color: var(--soft);
+ font: 750 9px/1 "SFMono-Regular", Consolas, monospace;
+}
+
+.control-tabs strong {
+ overflow: hidden;
+ color: var(--ink);
+ font-size: 11px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.action-picker {
+ margin-top: 30px;
+}
+
+.section-label {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+}
+
+.section-label h3 {
+ margin: 0;
+ font-size: 13px;
+ font-weight: 750;
+}
+
+kbd {
+ border-radius: 7px;
+ background: rgba(91, 72, 64, 0.08);
+ padding: 6px 8px;
+ color: var(--muted);
+ font: 700 11px/1 "SFMono-Regular", Consolas, monospace;
+}
+
+.action-list {
+ display: grid;
+ gap: 7px;
+ margin-top: 12px;
+}
+
+.action-list button {
+ display: grid;
+ min-height: 67px;
+ align-items: center;
+ grid-template-columns: 38px minmax(0, 1fr) auto 20px;
+ gap: 10px;
+ border: 1px solid transparent;
+ border-radius: 16px;
+ background: rgba(255, 255, 255, 0.36);
+ padding: 10px 12px;
+ text-align: left;
+ transition: background 150ms ease, border-color 150ms ease, transform 150ms ease;
+}
+
+.action-list button:hover {
+ border-color: var(--line);
+ background: rgba(255, 255, 255, 0.78);
+ transform: translateX(2px);
+}
+
+.action-list button.is-active {
+ border-color: rgba(201, 95, 67, 0.34);
+ background: var(--accent-soft);
+}
+
+.action-icon {
+ display: grid;
+ width: 38px;
+ height: 38px;
+ place-items: center;
+ border-radius: 11px;
+ background: rgba(255, 255, 255, 0.66);
+ color: var(--accent);
+}
+
+.action-copy {
+ display: grid;
+ gap: 3px;
+}
+
+.action-copy strong {
+ font-size: 13px;
+}
+
+.action-copy small {
+ overflow: hidden;
+ color: var(--muted);
+ font-size: 11px;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.check-slot {
+ display: grid;
+ color: var(--accent);
+ place-items: center;
+}
+
+.panel-note {
+ margin: 24px 0 0;
+ border-top: 1px solid var(--line);
+ padding-top: 17px;
+ color: var(--muted);
+ font-size: 12px;
+ line-height: 1.5;
+}
+
+.profile-loader {
+ display: grid;
+ grid-template-columns: minmax(0, 1fr) auto;
+ align-items: center;
+ gap: 12px;
+ margin-top: 24px;
+ border: 1px solid var(--line);
+ border-radius: 17px;
+ background: rgba(255, 255, 255, 0.44);
+ padding: 13px;
+}
+
+.profile-loader-copy {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ gap: 10px;
+ text-align: left;
+}
+
+.profile-loader-copy > span:last-child {
+ display: grid;
+ min-width: 0;
+ gap: 3px;
+}
+
+.profile-loader-copy strong {
+ font-size: 12px;
+}
+
+.profile-loader-copy small {
+ overflow: hidden;
+ color: var(--muted);
+ font-size: 10px;
+ line-height: 1.4;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.profile-loader-icon {
+ display: grid;
+ width: 36px;
+ height: 36px;
+ flex: none;
+ place-items: center;
+ border-radius: 11px;
+ background: var(--accent-soft);
+ color: var(--accent);
+}
+
+.profile-load-button {
+ min-height: 36px;
+ border: 1px solid var(--line-strong);
+ border-radius: 999px;
+ background: rgba(255, 255, 255, 0.72);
+ padding: 0 12px;
+ color: var(--ink);
+ font-size: 10px;
+ font-weight: 750;
+ white-space: nowrap;
+}
+
+.profile-load-button:hover {
+ background: #fff;
+}
+
+.profile-error {
+ grid-column: 1 / -1;
+ margin: 0;
+ border-radius: 10px;
+ background: rgba(169, 72, 49, 0.09);
+ padding: 9px 10px;
+ color: var(--accent-dark);
+ font-size: 10px;
+ line-height: 1.4;
+}
+
+.dialog-footer {
+ display: grid;
+ grid-template-columns: 0.8fr 1.35fr;
+ gap: 8px;
+ border-top: 1px solid var(--line);
+ padding: 16px 25px 20px;
+}
+
+.dialog-footer button {
+ display: flex;
+ min-height: 48px;
+ align-items: center;
+ justify-content: center;
+ gap: 7px;
+ border-radius: 999px;
+ padding: 0 15px;
+ font-size: 12px;
+ font-weight: 750;
+ white-space: nowrap;
+ transition: background 150ms ease, box-shadow 150ms ease, transform 150ms ease;
+}
+
+.reset-button {
+ border: 1px solid var(--line-strong);
+ background: rgba(255, 255, 255, 0.5);
+}
+
+.reset-button:hover {
+ background: #fff;
+}
+
+.export-button {
+ border: 1px solid var(--accent);
+ background: var(--accent);
+ color: #fffaf6;
+ box-shadow: 0 12px 28px rgba(169, 72, 49, 0.22);
+}
+
+.export-button:hover {
+ background: var(--accent-dark);
+ box-shadow: 0 16px 32px rgba(169, 72, 49, 0.28);
+ transform: translateY(-1px);
+}
+
+.dialog-footer button:active {
+ transform: translateY(1px);
+}
+
+.dialog-scrim {
+ position: fixed;
+ z-index: 20;
+ inset: 0;
+ border: 0;
+ background: rgba(81, 68, 63, 0.24);
+ backdrop-filter: blur(5px);
+}
+
+.toast {
+ position: fixed;
+ z-index: 40;
+ bottom: 22px;
+ left: 50%;
+ display: flex;
+ max-width: min(520px, calc(100vw - 32px));
+ align-items: center;
+ gap: 8px;
+ transform: translateX(-50%);
+ border-radius: 14px;
+ background: #453a36;
+ padding: 13px 16px;
+ color: #fffaf6;
+ box-shadow: 0 18px 50px rgba(62, 46, 40, 0.28);
+ font-size: 13px;
+}
+
+@media (max-width: 760px) {
+ .topbar {
+ padding: 18px;
+ }
+
+ .local-status {
+ padding: 7px 9px;
+ }
+
+ .hero {
+ min-height: 100dvh;
+ padding: 78px 14px 54px;
+ }
+
+ .hero h1 {
+ font-size: clamp(40px, 11vw, 54px);
+ }
+
+ .hero-copy p {
+ font-size: 15px;
+ }
+
+ .device-stage {
+ width: min(460px, 96vw);
+ margin-top: 10px;
+ }
+
+ .configure-button {
+ position: static;
+ min-height: 50px;
+ margin: -34px auto 0;
+ }
+
+ .generate-button {
+ margin-top: 10px;
+ }
+
+ .mapping-preview {
+ margin-top: 15px;
+ }
+
+ .quiet-note {
+ max-width: 310px;
+ line-height: 1.4;
+ }
+
+ .mapping-dialog {
+ top: auto;
+ right: 8px;
+ bottom: 8px;
+ left: 8px;
+ width: auto;
+ max-height: 91dvh;
+ border-radius: 24px;
+ transform: translateY(calc(100% + 16px));
+ }
+
+ .mapping-dialog.is-open {
+ transform: translateY(0);
+ }
+
+ .dialog-header {
+ padding: 20px 18px 16px;
+ }
+
+ .dialog-scroll {
+ padding: 18px 18px 22px;
+ }
+
+ .dialog-footer {
+ padding: 13px 18px 16px;
+ }
+
+ .profile-loader {
+ grid-template-columns: 1fr;
+ }
+
+ .profile-load-button {
+ justify-self: start;
+ }
+}
+
+@media (max-width: 420px) {
+ .brand > span:last-child {
+ display: none;
+ }
+
+ .hero {
+ justify-content: flex-start;
+ }
+
+ .hero h1 {
+ font-size: 39px;
+ }
+
+ .device-stage {
+ width: min(400px, 108vw);
+ }
+
+ .mapping-preview button {
+ min-height: 31px;
+ padding: 0 9px;
+ }
+
+ .control-tabs {
+ grid-template-columns: repeat(3, 1fr);
+ }
+
+ .action-list button {
+ grid-template-columns: 36px minmax(0, 1fr) auto 18px;
+ gap: 8px;
+ padding: 9px;
+ }
+
+ .action-copy small {
+ max-width: 170px;
+ }
+
+ .dialog-footer {
+ grid-template-columns: 0.72fr 1.28fr;
+ }
+}
+
+@media (prefers-reduced-motion: reduce) {
+ html {
+ scroll-behavior: auto;
+ }
+
+ *,
+ *::before,
+ *::after {
+ transition-duration: 0.01ms !important;
+ }
+}
+
+@media (prefers-reduced-transparency: reduce) {
+ .local-status,
+ .configure-button,
+ .mapping-preview button,
+ .mapping-dialog {
+ background: #fffaf6;
+ backdrop-filter: none;
+ }
+}
diff --git a/prototype/tests/sites-worker.test.mjs b/prototype/tests/sites-worker.test.mjs
new file mode 100644
index 0000000..64f277e
--- /dev/null
+++ b/prototype/tests/sites-worker.test.mjs
@@ -0,0 +1,68 @@
+import assert from "node:assert/strict";
+import { access } from "node:fs/promises";
+import test from "node:test";
+import worker from "../worker/index.js";
+
+test("serves existing static assets without a fallback", async () => {
+ const calls = [];
+ const response = await worker.fetch(new Request("https://example.test/assets/app.js"), {
+ ASSETS: {
+ fetch: async (request) => {
+ calls.push(new URL(request.url).pathname);
+ return new Response("asset", { status: 200 });
+ },
+ },
+ });
+
+ assert.equal(response.status, 200);
+ assert.deepEqual(calls, ["/assets/app.js"]);
+});
+
+test("falls back to index.html for an unknown app route", async () => {
+ const calls = [];
+ const response = await worker.fetch(
+ new Request("https://example.test/flow/step-two?source=share", {
+ headers: { accept: "text/html" },
+ }),
+ {
+ ASSETS: {
+ fetch: async (request) => {
+ const url = new URL(request.url);
+ calls.push(url.pathname + url.search);
+ return new Response(url.pathname === "/index.html" ? "app" : "missing", {
+ status: url.pathname === "/index.html" ? 200 : 404,
+ });
+ },
+ },
+ },
+ );
+
+ assert.equal(response.status, 200);
+ assert.deepEqual(calls, ["/flow/step-two?source=share", "/index.html"]);
+});
+
+test("does not turn missing API or write requests into the app shell", async () => {
+ for (const request of [
+ new Request("https://example.test/api/missing", { headers: { accept: "application/json" } }),
+ new Request("https://example.test/flow", { method: "POST", headers: { accept: "text/html" } }),
+ ]) {
+ let calls = 0;
+ const response = await worker.fetch(request, {
+ ASSETS: {
+ fetch: async () => {
+ calls += 1;
+ return new Response("missing", { status: 404 });
+ },
+ },
+ });
+
+ assert.equal(response.status, 404);
+ assert.equal(calls, 1);
+ }
+});
+
+test("emits the files required by Sites packaging", async () => {
+ await access(new URL("../dist/client/index.html", import.meta.url));
+ await access(new URL("../dist/server/index.js", import.meta.url));
+ await access(new URL("../dist/.openai/hosting.json", import.meta.url));
+});
diff --git a/prototype/vite.config.mjs b/prototype/vite.config.mjs
new file mode 100644
index 0000000..888d6c6
--- /dev/null
+++ b/prototype/vite.config.mjs
@@ -0,0 +1,19 @@
+import { defineConfig } from "vite";
+import react from "@vitejs/plugin-react";
+
+export default defineConfig({
+ build: {
+ outDir: "dist/client",
+ },
+ optimizeDeps: {
+ include: ["react", "react-dom/client"],
+ },
+ server: {
+ host: "0.0.0.0",
+ allowedHosts: ["terminal.local"],
+ warmup: {
+ clientFiles: ["./src/main.jsx"],
+ },
+ },
+ plugins: [react()],
+});
diff --git a/prototype/worker/index.js b/prototype/worker/index.js
new file mode 100644
index 0000000..5f35412
--- /dev/null
+++ b/prototype/worker/index.js
@@ -0,0 +1,15 @@
+export default {
+ async fetch(request, env) {
+ const response = await env.ASSETS.fetch(request);
+ const acceptsHtml = request.headers.get("accept")?.includes("text/html");
+
+ if (response.status !== 404 || !acceptsHtml || !["GET", "HEAD"].includes(request.method)) {
+ return response;
+ }
+
+ const indexUrl = new URL(request.url);
+ indexUrl.pathname = "/index.html";
+ indexUrl.search = "";
+ return env.ASSETS.fetch(new Request(indexUrl, request));
+ },
+};
diff --git a/scripts/configure.mjs b/scripts/configure.mjs
new file mode 100644
index 0000000..484fcf0
--- /dev/null
+++ b/scripts/configure.mjs
@@ -0,0 +1,47 @@
+import { spawn } from "node:child_process";
+import { dirname, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import { ensureGuiDependencies } from "./lib/gui-dependencies.mjs";
+
+const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const guiDirectory = resolve(repositoryRoot, "prototype");
+const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
+
+try {
+ ensureGuiDependencies(guiDirectory);
+} catch (error) {
+ console.error(error.message);
+ process.exit(error.exitCode ?? 1);
+}
+
+console.log("Ouverture du configurateur Codex Micro…");
+
+const gui = spawn(
+ npmCommand,
+ [
+ "run",
+ "dev",
+ "--",
+ "--host",
+ "127.0.0.1",
+ "--open",
+ ...process.argv.slice(2),
+ ],
+ {
+ cwd: guiDirectory,
+ stdio: "inherit",
+ },
+);
+
+gui.on("error", (error) => {
+ console.error(`Impossible de lancer l’interface : ${error.message}`);
+ process.exit(1);
+});
+
+gui.on("exit", (code, signal) => {
+ if (signal) {
+ process.kill(process.pid, signal);
+ return;
+ }
+ process.exit(code ?? 0);
+});
diff --git a/scripts/lib/gui-dependencies.mjs b/scripts/lib/gui-dependencies.mjs
new file mode 100644
index 0000000..99e4857
--- /dev/null
+++ b/scripts/lib/gui-dependencies.mjs
@@ -0,0 +1,45 @@
+import { existsSync } from "node:fs";
+import { spawnSync } from "node:child_process";
+import { resolve } from "node:path";
+
+const npmCommand = process.platform === "win32" ? "npm.cmd" : "npm";
+
+export function ensureGuiDependencies(guiDirectory) {
+ const packageJson = resolve(guiDirectory, "package.json");
+ const viteExecutable = resolve(
+ guiDirectory,
+ "node_modules",
+ ".bin",
+ process.platform === "win32" ? "vite.cmd" : "vite",
+ );
+
+ if (!existsSync(packageJson)) {
+ throw new Error("Interface introuvable : prototype/package.json est absent.");
+ }
+
+ if (existsSync(viteExecutable)) {
+ return { installed: false };
+ }
+
+ console.log("Préparation des dépendances verrouillées de l’interface…");
+ const install = spawnSync(
+ npmCommand,
+ ["ci", "--ignore-scripts", "--no-audit", "--no-fund"],
+ {
+ cwd: guiDirectory,
+ stdio: "inherit",
+ },
+ );
+
+ if (install.error) {
+ throw new Error(`Impossible de préparer l’interface : ${install.error.message}`);
+ }
+
+ if (install.status !== 0) {
+ const error = new Error(`La préparation de l’interface a échoué avec le code ${install.status ?? 1}.`);
+ error.exitCode = install.status ?? 1;
+ throw error;
+ }
+
+ return { installed: true };
+}
diff --git a/scripts/prepare-gui.mjs b/scripts/prepare-gui.mjs
new file mode 100644
index 0000000..13ee590
--- /dev/null
+++ b/scripts/prepare-gui.mjs
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+import { dirname, resolve } from "node:path";
+import { fileURLToPath } from "node:url";
+import { ensureGuiDependencies } from "./lib/gui-dependencies.mjs";
+
+const repositoryRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
+const guiDirectory = resolve(repositoryRoot, "prototype");
+
+try {
+ const result = ensureGuiDependencies(guiDirectory);
+ console.log(result.installed ? "OK: dépendances GUI installées" : "OK: dépendances GUI déjà disponibles");
+} catch (error) {
+ console.error(error.message);
+ process.exitCode = error.exitCode ?? 1;
+}
diff --git a/scripts/validate-profile.mjs b/scripts/validate-profile.mjs
index d1eef95..70820db 100755
--- a/scripts/validate-profile.mjs
+++ b/scripts/validate-profile.mjs
@@ -15,7 +15,8 @@ function sameAction(actual, expected) {
return isDeepStrictEqual(actual, expected);
}
-if (profile.status !== "hardware-observed") errors.push("status must remain hardware-observed until the full hardware checklist passes");
+if (profile.id !== "claude-desktop-macos-codex-micro-v0") errors.push("logical profile id must remain the V0 proposal");
+if (profile.status !== "proposal-not-applied") errors.push("logical profile status must remain proposal-not-applied");
if (profile.target?.appBundleId !== "com.anthropic.claudefordesktop") errors.push("unexpected Claude bundle identifier");
if (profile.target?.observedInputVersion !== "0.17.3") errors.push("observed Input version must be 0.17.3");
if (!profile.target?.supportedInputVersions?.includes("0.17.3")) {
@@ -26,7 +27,7 @@ if (profile.target?.observedFirmwareVersion !== "v0.4.1") errors.push("observed
if (
profile.preservation?.mode !== "update-only-existing-layer" ||
!profile.preservation?.protectedLayerIndexes?.includes(0) ||
- profile.preservation?.unlistedControls !== "no-action-in-new-layer" ||
+ profile.preservation?.unlistedControls !== "no-action-in-target-layer" ||
profile.preservation?.touchLayerControl !== "reserved" ||
profile.preservation?.otherLayers !== "unchanged" ||
profile.preservation?.otherProfiles !== "unchanged" ||
@@ -115,7 +116,7 @@ if (!profile.unusedControls?.length || profile.unusedControls.some((entry) => ![
errors.push("unused controls must be explicitly safe");
}
-for (const action of ["quick-entry"]) {
+for (const action of ["quick-entry", "voice-dictation"]) {
if (!profile.outsideAppLinkedLayer?.some((entry) => entry.action === action)) errors.push(`${action} must remain outside the AppSense layer`);
}
for (const action of ["send-message", "permission-approve-or-deny", "delete", "git-push", "deploy", "destructive-command"]) {
@@ -126,5 +127,5 @@ if (errors.length) {
for (const error of errors) console.error(`ERROR: ${error}`);
process.exitCode = 1;
} else {
- console.log("OK: Claude V1 logical profile validated; layer 0 protected and sensitive actions excluded");
+ console.log("OK: Claude logical proposal validated; layer 0 protected and sensitive actions excluded");
}