README.md CHANGED
@@ -5,7 +5,8 @@ colorFrom: blue
5
  colorTo: purple
6
  sdk: docker
7
  pinned: true
8
- hf_oauth: true
 
9
  thumbnail: >-
10
  https://cdn-uploads.huggingface.co/production/uploads/671faa3a541a76b548647676/XWNDlOu0R4fHXR0kCW3Wd.png
11
  short_description: All about Reachy Mini, from building to getting started
 
5
  colorTo: purple
6
  sdk: docker
7
  pinned: true
8
+ tags:
9
+ - reachy_mini
10
  thumbnail: >-
11
  https://cdn-uploads.huggingface.co/production/uploads/671faa3a541a76b548647676/XWNDlOu0R4fHXR0kCW3Wd.png
12
  short_description: All about Reachy Mini, from building to getting started
index.html CHANGED
@@ -1,14 +1,6 @@
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
4
- <!-- Google Tag Manager -->
5
- <script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
6
- new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
7
- j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
8
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
9
- })(window,document,'script','dataLayer','GTM-WKKZHMJJ');</script>
10
- <!-- End Google Tag Manager -->
11
-
12
  <!-- Privacy-friendly analytics by Plausible -->
13
  <script async src="https://plausible.io/js/pa-4zj-sGpYxM8ggvg7a-Sjo.js"></script>
14
  <script>
@@ -22,11 +14,6 @@
22
  <title>Reachy Mini - Open Source Companion Robot</title>
23
  </head>
24
  <body>
25
- <!-- Google Tag Manager (noscript) -->
26
- <noscript><iframe src="https://www.googletagmanager.com/ns.html?id=GTM-WKKZHMJJ"
27
- height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
28
- <!-- End Google Tag Manager (noscript) -->
29
-
30
  <div id="root"></div>
31
  <script type="module" src="/src/main.jsx"></script>
32
  </body>
 
1
  <!DOCTYPE html>
2
  <html lang="en">
3
  <head>
 
 
 
 
 
 
 
 
4
  <!-- Privacy-friendly analytics by Plausible -->
5
  <script async src="https://plausible.io/js/pa-4zj-sGpYxM8ggvg7a-Sjo.js"></script>
6
  <script>
 
14
  <title>Reachy Mini - Open Source Companion Robot</title>
15
  </head>
16
  <body>
 
 
 
 
 
17
  <div id="root"></div>
18
  <script type="module" src="/src/main.jsx"></script>
19
  </body>
package-lock.json CHANGED
@@ -10,11 +10,9 @@
10
  "dependencies": {
11
  "@emotion/react": "^11.14.0",
12
  "@emotion/styled": "^11.14.1",
13
- "@huggingface/hub": "^2.8.1",
14
  "@mui/icons-material": "^7.3.6",
15
  "@mui/material": "^7.3.6",
16
  "@react-spring/web": "^10.0.3",
17
- "express": "^4.21.2",
18
  "framer-motion": "^12.23.26",
19
  "highlight.js": "^11.11.1",
20
  "react": "^19.2.0",
@@ -1072,30 +1070,6 @@
1072
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1073
  }
1074
  },
1075
- "node_modules/@huggingface/hub": {
1076
- "version": "2.8.1",
1077
- "resolved": "https://registry.npmjs.org/@huggingface/hub/-/hub-2.8.1.tgz",
1078
- "integrity": "sha512-VAsXdMiIHPteXQJhrwaBEiePTWiJ0zBSymHdnX4J+AijjNN0h3RzGfkKemXcu75gu/TmRLFY9l8+2Tkdmpis0w==",
1079
- "license": "MIT",
1080
- "dependencies": {
1081
- "@huggingface/tasks": "^0.19.82"
1082
- },
1083
- "bin": {
1084
- "hfjs": "dist/cli.js"
1085
- },
1086
- "engines": {
1087
- "node": ">=18"
1088
- },
1089
- "optionalDependencies": {
1090
- "cli-progress": "^3.12.0"
1091
- }
1092
- },
1093
- "node_modules/@huggingface/tasks": {
1094
- "version": "0.19.83",
1095
- "resolved": "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.19.83.tgz",
1096
- "integrity": "sha512-nBt3S6x+MWUTmfey1drQZRMuEopEbz2aEMUsoddfpCuzIYAMCsJDX7xeNuJnzvbVGis3gXXCRcLHVhFtHaaiyA==",
1097
- "license": "MIT"
1098
- },
1099
  "node_modules/@humanfs/core": {
1100
  "version": "0.19.1",
1101
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
@@ -2004,19 +1978,6 @@
2004
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
2005
  }
2006
  },
2007
- "node_modules/accepts": {
2008
- "version": "1.3.8",
2009
- "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
2010
- "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
2011
- "license": "MIT",
2012
- "dependencies": {
2013
- "mime-types": "~2.1.34",
2014
- "negotiator": "0.6.3"
2015
- },
2016
- "engines": {
2017
- "node": ">= 0.6"
2018
- }
2019
- },
2020
  "node_modules/acorn": {
2021
  "version": "8.15.0",
2022
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
@@ -2058,16 +2019,6 @@
2058
  "url": "https://github.com/sponsors/epoberezkin"
2059
  }
2060
  },
2061
- "node_modules/ansi-regex": {
2062
- "version": "5.0.1",
2063
- "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
2064
- "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
2065
- "license": "MIT",
2066
- "optional": true,
2067
- "engines": {
2068
- "node": ">=8"
2069
- }
2070
- },
2071
  "node_modules/ansi-styles": {
2072
  "version": "4.3.0",
2073
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
@@ -2091,12 +2042,6 @@
2091
  "dev": true,
2092
  "license": "Python-2.0"
2093
  },
2094
- "node_modules/array-flatten": {
2095
- "version": "1.1.1",
2096
- "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
2097
- "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==",
2098
- "license": "MIT"
2099
- },
2100
  "node_modules/babel-plugin-macros": {
2101
  "version": "3.1.0",
2102
  "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
@@ -2138,45 +2083,6 @@
2138
  "baseline-browser-mapping": "dist/cli.js"
2139
  }
2140
  },
2141
- "node_modules/body-parser": {
2142
- "version": "1.20.4",
2143
- "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz",
2144
- "integrity": "sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==",
2145
- "license": "MIT",
2146
- "dependencies": {
2147
- "bytes": "~3.1.2",
2148
- "content-type": "~1.0.5",
2149
- "debug": "2.6.9",
2150
- "depd": "2.0.0",
2151
- "destroy": "~1.2.0",
2152
- "http-errors": "~2.0.1",
2153
- "iconv-lite": "~0.4.24",
2154
- "on-finished": "~2.4.1",
2155
- "qs": "~6.14.0",
2156
- "raw-body": "~2.5.3",
2157
- "type-is": "~1.6.18",
2158
- "unpipe": "~1.0.0"
2159
- },
2160
- "engines": {
2161
- "node": ">= 0.8",
2162
- "npm": "1.2.8000 || >= 1.4.16"
2163
- }
2164
- },
2165
- "node_modules/body-parser/node_modules/debug": {
2166
- "version": "2.6.9",
2167
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
2168
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
2169
- "license": "MIT",
2170
- "dependencies": {
2171
- "ms": "2.0.0"
2172
- }
2173
- },
2174
- "node_modules/body-parser/node_modules/ms": {
2175
- "version": "2.0.0",
2176
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
2177
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
2178
- "license": "MIT"
2179
- },
2180
  "node_modules/brace-expansion": {
2181
  "version": "1.1.12",
2182
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
@@ -2223,44 +2129,6 @@
2223
  "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
2224
  }
2225
  },
2226
- "node_modules/bytes": {
2227
- "version": "3.1.2",
2228
- "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
2229
- "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
2230
- "license": "MIT",
2231
- "engines": {
2232
- "node": ">= 0.8"
2233
- }
2234
- },
2235
- "node_modules/call-bind-apply-helpers": {
2236
- "version": "1.0.2",
2237
- "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
2238
- "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
2239
- "license": "MIT",
2240
- "dependencies": {
2241
- "es-errors": "^1.3.0",
2242
- "function-bind": "^1.1.2"
2243
- },
2244
- "engines": {
2245
- "node": ">= 0.4"
2246
- }
2247
- },
2248
- "node_modules/call-bound": {
2249
- "version": "1.0.4",
2250
- "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
2251
- "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
2252
- "license": "MIT",
2253
- "dependencies": {
2254
- "call-bind-apply-helpers": "^1.0.2",
2255
- "get-intrinsic": "^1.3.0"
2256
- },
2257
- "engines": {
2258
- "node": ">= 0.4"
2259
- },
2260
- "funding": {
2261
- "url": "https://github.com/sponsors/ljharb"
2262
- }
2263
- },
2264
  "node_modules/callsites": {
2265
  "version": "3.1.0",
2266
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
@@ -2353,19 +2221,6 @@
2353
  "url": "https://github.com/sponsors/wooorm"
2354
  }
2355
  },
2356
- "node_modules/cli-progress": {
2357
- "version": "3.12.0",
2358
- "resolved": "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz",
2359
- "integrity": "sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==",
2360
- "license": "MIT",
2361
- "optional": true,
2362
- "dependencies": {
2363
- "string-width": "^4.2.3"
2364
- },
2365
- "engines": {
2366
- "node": ">=4"
2367
- }
2368
- },
2369
  "node_modules/clsx": {
2370
  "version": "2.1.1",
2371
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
@@ -2411,27 +2266,6 @@
2411
  "dev": true,
2412
  "license": "MIT"
2413
  },
2414
- "node_modules/content-disposition": {
2415
- "version": "0.5.4",
2416
- "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
2417
- "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
2418
- "license": "MIT",
2419
- "dependencies": {
2420
- "safe-buffer": "5.2.1"
2421
- },
2422
- "engines": {
2423
- "node": ">= 0.6"
2424
- }
2425
- },
2426
- "node_modules/content-type": {
2427
- "version": "1.0.5",
2428
- "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
2429
- "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
2430
- "license": "MIT",
2431
- "engines": {
2432
- "node": ">= 0.6"
2433
- }
2434
- },
2435
  "node_modules/convert-source-map": {
2436
  "version": "1.9.0",
2437
  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
@@ -2451,12 +2285,6 @@
2451
  "url": "https://opencollective.com/express"
2452
  }
2453
  },
2454
- "node_modules/cookie-signature": {
2455
- "version": "1.0.7",
2456
- "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz",
2457
- "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==",
2458
- "license": "MIT"
2459
- },
2460
  "node_modules/cosmiconfig": {
2461
  "version": "7.1.0",
2462
  "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
@@ -2539,15 +2367,6 @@
2539
  "dev": true,
2540
  "license": "MIT"
2541
  },
2542
- "node_modules/depd": {
2543
- "version": "2.0.0",
2544
- "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
2545
- "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
2546
- "license": "MIT",
2547
- "engines": {
2548
- "node": ">= 0.8"
2549
- }
2550
- },
2551
  "node_modules/dequal": {
2552
  "version": "2.0.3",
2553
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
@@ -2556,16 +2375,6 @@
2556
  "node": ">=6"
2557
  }
2558
  },
2559
- "node_modules/destroy": {
2560
- "version": "1.2.0",
2561
- "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
2562
- "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
2563
- "license": "MIT",
2564
- "engines": {
2565
- "node": ">= 0.8",
2566
- "npm": "1.2.8000 || >= 1.4.16"
2567
- }
2568
- },
2569
  "node_modules/devlop": {
2570
  "version": "1.1.0",
2571
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
@@ -2588,26 +2397,6 @@
2588
  "csstype": "^3.0.2"
2589
  }
2590
  },
2591
- "node_modules/dunder-proto": {
2592
- "version": "1.0.1",
2593
- "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
2594
- "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
2595
- "license": "MIT",
2596
- "dependencies": {
2597
- "call-bind-apply-helpers": "^1.0.1",
2598
- "es-errors": "^1.3.0",
2599
- "gopd": "^1.2.0"
2600
- },
2601
- "engines": {
2602
- "node": ">= 0.4"
2603
- }
2604
- },
2605
- "node_modules/ee-first": {
2606
- "version": "1.1.1",
2607
- "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
2608
- "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==",
2609
- "license": "MIT"
2610
- },
2611
  "node_modules/electron-to-chromium": {
2612
  "version": "1.5.267",
2613
  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
@@ -2615,22 +2404,6 @@
2615
  "dev": true,
2616
  "license": "ISC"
2617
  },
2618
- "node_modules/emoji-regex": {
2619
- "version": "8.0.0",
2620
- "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
2621
- "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
2622
- "license": "MIT",
2623
- "optional": true
2624
- },
2625
- "node_modules/encodeurl": {
2626
- "version": "2.0.0",
2627
- "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
2628
- "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
2629
- "license": "MIT",
2630
- "engines": {
2631
- "node": ">= 0.8"
2632
- }
2633
- },
2634
  "node_modules/error-ex": {
2635
  "version": "1.3.4",
2636
  "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
@@ -2640,36 +2413,6 @@
2640
  "is-arrayish": "^0.2.1"
2641
  }
2642
  },
2643
- "node_modules/es-define-property": {
2644
- "version": "1.0.1",
2645
- "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
2646
- "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
2647
- "license": "MIT",
2648
- "engines": {
2649
- "node": ">= 0.4"
2650
- }
2651
- },
2652
- "node_modules/es-errors": {
2653
- "version": "1.3.0",
2654
- "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
2655
- "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
2656
- "license": "MIT",
2657
- "engines": {
2658
- "node": ">= 0.4"
2659
- }
2660
- },
2661
- "node_modules/es-object-atoms": {
2662
- "version": "1.1.1",
2663
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
2664
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
2665
- "license": "MIT",
2666
- "dependencies": {
2667
- "es-errors": "^1.3.0"
2668
- },
2669
- "engines": {
2670
- "node": ">= 0.4"
2671
- }
2672
- },
2673
  "node_modules/esbuild": {
2674
  "version": "0.25.12",
2675
  "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
@@ -2722,12 +2465,6 @@
2722
  "node": ">=6"
2723
  }
2724
  },
2725
- "node_modules/escape-html": {
2726
- "version": "1.0.3",
2727
- "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
2728
- "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
2729
- "license": "MIT"
2730
- },
2731
  "node_modules/escape-string-regexp": {
2732
  "version": "4.0.0",
2733
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
@@ -2934,85 +2671,6 @@
2934
  "node": ">=0.10.0"
2935
  }
2936
  },
2937
- "node_modules/etag": {
2938
- "version": "1.8.1",
2939
- "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
2940
- "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
2941
- "license": "MIT",
2942
- "engines": {
2943
- "node": ">= 0.6"
2944
- }
2945
- },
2946
- "node_modules/express": {
2947
- "version": "4.22.1",
2948
- "resolved": "https://registry.npmjs.org/express/-/express-4.22.1.tgz",
2949
- "integrity": "sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==",
2950
- "license": "MIT",
2951
- "dependencies": {
2952
- "accepts": "~1.3.8",
2953
- "array-flatten": "1.1.1",
2954
- "body-parser": "~1.20.3",
2955
- "content-disposition": "~0.5.4",
2956
- "content-type": "~1.0.4",
2957
- "cookie": "~0.7.1",
2958
- "cookie-signature": "~1.0.6",
2959
- "debug": "2.6.9",
2960
- "depd": "2.0.0",
2961
- "encodeurl": "~2.0.0",
2962
- "escape-html": "~1.0.3",
2963
- "etag": "~1.8.1",
2964
- "finalhandler": "~1.3.1",
2965
- "fresh": "~0.5.2",
2966
- "http-errors": "~2.0.0",
2967
- "merge-descriptors": "1.0.3",
2968
- "methods": "~1.1.2",
2969
- "on-finished": "~2.4.1",
2970
- "parseurl": "~1.3.3",
2971
- "path-to-regexp": "~0.1.12",
2972
- "proxy-addr": "~2.0.7",
2973
- "qs": "~6.14.0",
2974
- "range-parser": "~1.2.1",
2975
- "safe-buffer": "5.2.1",
2976
- "send": "~0.19.0",
2977
- "serve-static": "~1.16.2",
2978
- "setprototypeof": "1.2.0",
2979
- "statuses": "~2.0.1",
2980
- "type-is": "~1.6.18",
2981
- "utils-merge": "1.0.1",
2982
- "vary": "~1.1.2"
2983
- },
2984
- "engines": {
2985
- "node": ">= 0.10.0"
2986
- },
2987
- "funding": {
2988
- "type": "opencollective",
2989
- "url": "https://opencollective.com/express"
2990
- }
2991
- },
2992
- "node_modules/express/node_modules/cookie": {
2993
- "version": "0.7.2",
2994
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz",
2995
- "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==",
2996
- "license": "MIT",
2997
- "engines": {
2998
- "node": ">= 0.6"
2999
- }
3000
- },
3001
- "node_modules/express/node_modules/debug": {
3002
- "version": "2.6.9",
3003
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
3004
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
3005
- "license": "MIT",
3006
- "dependencies": {
3007
- "ms": "2.0.0"
3008
- }
3009
- },
3010
- "node_modules/express/node_modules/ms": {
3011
- "version": "2.0.0",
3012
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
3013
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
3014
- "license": "MIT"
3015
- },
3016
  "node_modules/extend": {
3017
  "version": "3.0.2",
3018
  "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
@@ -3070,39 +2728,6 @@
3070
  "node": ">=16.0.0"
3071
  }
3072
  },
3073
- "node_modules/finalhandler": {
3074
- "version": "1.3.2",
3075
- "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz",
3076
- "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==",
3077
- "license": "MIT",
3078
- "dependencies": {
3079
- "debug": "2.6.9",
3080
- "encodeurl": "~2.0.0",
3081
- "escape-html": "~1.0.3",
3082
- "on-finished": "~2.4.1",
3083
- "parseurl": "~1.3.3",
3084
- "statuses": "~2.0.2",
3085
- "unpipe": "~1.0.0"
3086
- },
3087
- "engines": {
3088
- "node": ">= 0.8"
3089
- }
3090
- },
3091
- "node_modules/finalhandler/node_modules/debug": {
3092
- "version": "2.6.9",
3093
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
3094
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
3095
- "license": "MIT",
3096
- "dependencies": {
3097
- "ms": "2.0.0"
3098
- }
3099
- },
3100
- "node_modules/finalhandler/node_modules/ms": {
3101
- "version": "2.0.0",
3102
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
3103
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
3104
- "license": "MIT"
3105
- },
3106
  "node_modules/find-root": {
3107
  "version": "1.1.0",
3108
  "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
@@ -3147,15 +2772,6 @@
3147
  "dev": true,
3148
  "license": "ISC"
3149
  },
3150
- "node_modules/forwarded": {
3151
- "version": "0.2.0",
3152
- "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
3153
- "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
3154
- "license": "MIT",
3155
- "engines": {
3156
- "node": ">= 0.6"
3157
- }
3158
- },
3159
  "node_modules/framer-motion": {
3160
  "version": "12.23.26",
3161
  "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
@@ -3183,15 +2799,6 @@
3183
  }
3184
  }
3185
  },
3186
- "node_modules/fresh": {
3187
- "version": "0.5.2",
3188
- "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
3189
- "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
3190
- "license": "MIT",
3191
- "engines": {
3192
- "node": ">= 0.6"
3193
- }
3194
- },
3195
  "node_modules/fsevents": {
3196
  "version": "2.3.3",
3197
  "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
@@ -3225,43 +2832,6 @@
3225
  "node": ">=6.9.0"
3226
  }
3227
  },
3228
- "node_modules/get-intrinsic": {
3229
- "version": "1.3.0",
3230
- "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
3231
- "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
3232
- "license": "MIT",
3233
- "dependencies": {
3234
- "call-bind-apply-helpers": "^1.0.2",
3235
- "es-define-property": "^1.0.1",
3236
- "es-errors": "^1.3.0",
3237
- "es-object-atoms": "^1.1.1",
3238
- "function-bind": "^1.1.2",
3239
- "get-proto": "^1.0.1",
3240
- "gopd": "^1.2.0",
3241
- "has-symbols": "^1.1.0",
3242
- "hasown": "^2.0.2",
3243
- "math-intrinsics": "^1.1.0"
3244
- },
3245
- "engines": {
3246
- "node": ">= 0.4"
3247
- },
3248
- "funding": {
3249
- "url": "https://github.com/sponsors/ljharb"
3250
- }
3251
- },
3252
- "node_modules/get-proto": {
3253
- "version": "1.0.1",
3254
- "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
3255
- "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
3256
- "license": "MIT",
3257
- "dependencies": {
3258
- "dunder-proto": "^1.0.1",
3259
- "es-object-atoms": "^1.0.0"
3260
- },
3261
- "engines": {
3262
- "node": ">= 0.4"
3263
- }
3264
- },
3265
  "node_modules/glob-parent": {
3266
  "version": "6.0.2",
3267
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
@@ -3288,18 +2858,6 @@
3288
  "url": "https://github.com/sponsors/sindresorhus"
3289
  }
3290
  },
3291
- "node_modules/gopd": {
3292
- "version": "1.2.0",
3293
- "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
3294
- "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
3295
- "license": "MIT",
3296
- "engines": {
3297
- "node": ">= 0.4"
3298
- },
3299
- "funding": {
3300
- "url": "https://github.com/sponsors/ljharb"
3301
- }
3302
- },
3303
  "node_modules/has-flag": {
3304
  "version": "4.0.0",
3305
  "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
@@ -3310,18 +2868,6 @@
3310
  "node": ">=8"
3311
  }
3312
  },
3313
- "node_modules/has-symbols": {
3314
- "version": "1.1.0",
3315
- "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
3316
- "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
3317
- "license": "MIT",
3318
- "engines": {
3319
- "node": ">= 0.4"
3320
- },
3321
- "funding": {
3322
- "url": "https://github.com/sponsors/ljharb"
3323
- }
3324
- },
3325
  "node_modules/hasown": {
3326
  "version": "2.0.2",
3327
  "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
@@ -3443,38 +2989,6 @@
3443
  "url": "https://opencollective.com/unified"
3444
  }
3445
  },
3446
- "node_modules/http-errors": {
3447
- "version": "2.0.1",
3448
- "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz",
3449
- "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==",
3450
- "license": "MIT",
3451
- "dependencies": {
3452
- "depd": "~2.0.0",
3453
- "inherits": "~2.0.4",
3454
- "setprototypeof": "~1.2.0",
3455
- "statuses": "~2.0.2",
3456
- "toidentifier": "~1.0.1"
3457
- },
3458
- "engines": {
3459
- "node": ">= 0.8"
3460
- },
3461
- "funding": {
3462
- "type": "opencollective",
3463
- "url": "https://opencollective.com/express"
3464
- }
3465
- },
3466
- "node_modules/iconv-lite": {
3467
- "version": "0.4.24",
3468
- "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
3469
- "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
3470
- "license": "MIT",
3471
- "dependencies": {
3472
- "safer-buffer": ">= 2.1.2 < 3"
3473
- },
3474
- "engines": {
3475
- "node": ">=0.10.0"
3476
- }
3477
- },
3478
  "node_modules/ignore": {
3479
  "version": "5.3.2",
3480
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -3511,26 +3025,11 @@
3511
  "node": ">=0.8.19"
3512
  }
3513
  },
3514
- "node_modules/inherits": {
3515
- "version": "2.0.4",
3516
- "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
3517
- "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
3518
- "license": "ISC"
3519
- },
3520
  "node_modules/inline-style-parser": {
3521
  "version": "0.2.7",
3522
  "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
3523
  "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="
3524
  },
3525
- "node_modules/ipaddr.js": {
3526
- "version": "1.9.1",
3527
- "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
3528
- "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
3529
- "license": "MIT",
3530
- "engines": {
3531
- "node": ">= 0.10"
3532
- }
3533
- },
3534
  "node_modules/is-alphabetical": {
3535
  "version": "2.0.1",
3536
  "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
@@ -3593,16 +3092,6 @@
3593
  "node": ">=0.10.0"
3594
  }
3595
  },
3596
- "node_modules/is-fullwidth-code-point": {
3597
- "version": "3.0.0",
3598
- "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
3599
- "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
3600
- "license": "MIT",
3601
- "optional": true,
3602
- "engines": {
3603
- "node": ">=8"
3604
- }
3605
- },
3606
  "node_modules/is-glob": {
3607
  "version": "4.0.3",
3608
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
@@ -3821,15 +3310,6 @@
3821
  "url": "https://github.com/sponsors/wooorm"
3822
  }
3823
  },
3824
- "node_modules/math-intrinsics": {
3825
- "version": "1.1.0",
3826
- "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
3827
- "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
3828
- "license": "MIT",
3829
- "engines": {
3830
- "node": ">= 0.4"
3831
- }
3832
- },
3833
  "node_modules/mdast-util-find-and-replace": {
3834
  "version": "3.0.2",
3835
  "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
@@ -4096,33 +3576,6 @@
4096
  "url": "https://opencollective.com/unified"
4097
  }
4098
  },
4099
- "node_modules/media-typer": {
4100
- "version": "0.3.0",
4101
- "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
4102
- "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
4103
- "license": "MIT",
4104
- "engines": {
4105
- "node": ">= 0.6"
4106
- }
4107
- },
4108
- "node_modules/merge-descriptors": {
4109
- "version": "1.0.3",
4110
- "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
4111
- "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
4112
- "license": "MIT",
4113
- "funding": {
4114
- "url": "https://github.com/sponsors/sindresorhus"
4115
- }
4116
- },
4117
- "node_modules/methods": {
4118
- "version": "1.1.2",
4119
- "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
4120
- "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
4121
- "license": "MIT",
4122
- "engines": {
4123
- "node": ">= 0.6"
4124
- }
4125
- },
4126
  "node_modules/micromark": {
4127
  "version": "4.0.2",
4128
  "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
@@ -4658,39 +4111,6 @@
4658
  }
4659
  ]
4660
  },
4661
- "node_modules/mime": {
4662
- "version": "1.6.0",
4663
- "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
4664
- "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
4665
- "license": "MIT",
4666
- "bin": {
4667
- "mime": "cli.js"
4668
- },
4669
- "engines": {
4670
- "node": ">=4"
4671
- }
4672
- },
4673
- "node_modules/mime-db": {
4674
- "version": "1.52.0",
4675
- "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
4676
- "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
4677
- "license": "MIT",
4678
- "engines": {
4679
- "node": ">= 0.6"
4680
- }
4681
- },
4682
- "node_modules/mime-types": {
4683
- "version": "2.1.35",
4684
- "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
4685
- "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
4686
- "license": "MIT",
4687
- "dependencies": {
4688
- "mime-db": "1.52.0"
4689
- },
4690
- "engines": {
4691
- "node": ">= 0.6"
4692
- }
4693
- },
4694
  "node_modules/minimatch": {
4695
  "version": "3.1.2",
4696
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
@@ -4751,15 +4171,6 @@
4751
  "dev": true,
4752
  "license": "MIT"
4753
  },
4754
- "node_modules/negotiator": {
4755
- "version": "0.6.3",
4756
- "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
4757
- "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
4758
- "license": "MIT",
4759
- "engines": {
4760
- "node": ">= 0.6"
4761
- }
4762
- },
4763
  "node_modules/node-releases": {
4764
  "version": "2.0.27",
4765
  "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
@@ -4776,30 +4187,6 @@
4776
  "node": ">=0.10.0"
4777
  }
4778
  },
4779
- "node_modules/object-inspect": {
4780
- "version": "1.13.4",
4781
- "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
4782
- "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
4783
- "license": "MIT",
4784
- "engines": {
4785
- "node": ">= 0.4"
4786
- },
4787
- "funding": {
4788
- "url": "https://github.com/sponsors/ljharb"
4789
- }
4790
- },
4791
- "node_modules/on-finished": {
4792
- "version": "2.4.1",
4793
- "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
4794
- "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
4795
- "license": "MIT",
4796
- "dependencies": {
4797
- "ee-first": "1.1.1"
4798
- },
4799
- "engines": {
4800
- "node": ">= 0.8"
4801
- }
4802
- },
4803
  "node_modules/optionator": {
4804
  "version": "0.9.4",
4805
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
@@ -4903,15 +4290,6 @@
4903
  "url": "https://github.com/sponsors/sindresorhus"
4904
  }
4905
  },
4906
- "node_modules/parseurl": {
4907
- "version": "1.3.3",
4908
- "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
4909
- "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
4910
- "license": "MIT",
4911
- "engines": {
4912
- "node": ">= 0.8"
4913
- }
4914
- },
4915
  "node_modules/path-exists": {
4916
  "version": "4.0.0",
4917
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -4938,12 +4316,6 @@
4938
  "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
4939
  "license": "MIT"
4940
  },
4941
- "node_modules/path-to-regexp": {
4942
- "version": "0.1.12",
4943
- "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
4944
- "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
4945
- "license": "MIT"
4946
- },
4947
  "node_modules/path-type": {
4948
  "version": "4.0.0",
4949
  "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
@@ -5032,19 +4404,6 @@
5032
  "url": "https://github.com/sponsors/wooorm"
5033
  }
5034
  },
5035
- "node_modules/proxy-addr": {
5036
- "version": "2.0.7",
5037
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
5038
- "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
5039
- "license": "MIT",
5040
- "dependencies": {
5041
- "forwarded": "0.2.0",
5042
- "ipaddr.js": "1.9.1"
5043
- },
5044
- "engines": {
5045
- "node": ">= 0.10"
5046
- }
5047
- },
5048
  "node_modules/punycode": {
5049
  "version": "2.3.1",
5050
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -5055,45 +4414,6 @@
5055
  "node": ">=6"
5056
  }
5057
  },
5058
- "node_modules/qs": {
5059
- "version": "6.14.1",
5060
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz",
5061
- "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==",
5062
- "license": "BSD-3-Clause",
5063
- "dependencies": {
5064
- "side-channel": "^1.1.0"
5065
- },
5066
- "engines": {
5067
- "node": ">=0.6"
5068
- },
5069
- "funding": {
5070
- "url": "https://github.com/sponsors/ljharb"
5071
- }
5072
- },
5073
- "node_modules/range-parser": {
5074
- "version": "1.2.1",
5075
- "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
5076
- "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
5077
- "license": "MIT",
5078
- "engines": {
5079
- "node": ">= 0.6"
5080
- }
5081
- },
5082
- "node_modules/raw-body": {
5083
- "version": "2.5.3",
5084
- "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz",
5085
- "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==",
5086
- "license": "MIT",
5087
- "dependencies": {
5088
- "bytes": "~3.1.2",
5089
- "http-errors": "~2.0.1",
5090
- "iconv-lite": "~0.4.24",
5091
- "unpipe": "~1.0.0"
5092
- },
5093
- "engines": {
5094
- "node": ">= 0.8"
5095
- }
5096
- },
5097
  "node_modules/react": {
5098
  "version": "19.2.3",
5099
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
@@ -5362,32 +4682,6 @@
5362
  "fsevents": "~2.3.2"
5363
  }
5364
  },
5365
- "node_modules/safe-buffer": {
5366
- "version": "5.2.1",
5367
- "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
5368
- "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
5369
- "funding": [
5370
- {
5371
- "type": "github",
5372
- "url": "https://github.com/sponsors/feross"
5373
- },
5374
- {
5375
- "type": "patreon",
5376
- "url": "https://www.patreon.com/feross"
5377
- },
5378
- {
5379
- "type": "consulting",
5380
- "url": "https://feross.org/support"
5381
- }
5382
- ],
5383
- "license": "MIT"
5384
- },
5385
- "node_modules/safer-buffer": {
5386
- "version": "2.1.2",
5387
- "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
5388
- "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==",
5389
- "license": "MIT"
5390
- },
5391
  "node_modules/scheduler": {
5392
  "version": "0.27.0",
5393
  "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
@@ -5404,72 +4698,12 @@
5404
  "semver": "bin/semver.js"
5405
  }
5406
  },
5407
- "node_modules/send": {
5408
- "version": "0.19.2",
5409
- "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz",
5410
- "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==",
5411
- "license": "MIT",
5412
- "dependencies": {
5413
- "debug": "2.6.9",
5414
- "depd": "2.0.0",
5415
- "destroy": "1.2.0",
5416
- "encodeurl": "~2.0.0",
5417
- "escape-html": "~1.0.3",
5418
- "etag": "~1.8.1",
5419
- "fresh": "~0.5.2",
5420
- "http-errors": "~2.0.1",
5421
- "mime": "1.6.0",
5422
- "ms": "2.1.3",
5423
- "on-finished": "~2.4.1",
5424
- "range-parser": "~1.2.1",
5425
- "statuses": "~2.0.2"
5426
- },
5427
- "engines": {
5428
- "node": ">= 0.8.0"
5429
- }
5430
- },
5431
- "node_modules/send/node_modules/debug": {
5432
- "version": "2.6.9",
5433
- "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
5434
- "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
5435
- "license": "MIT",
5436
- "dependencies": {
5437
- "ms": "2.0.0"
5438
- }
5439
- },
5440
- "node_modules/send/node_modules/debug/node_modules/ms": {
5441
- "version": "2.0.0",
5442
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
5443
- "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==",
5444
- "license": "MIT"
5445
- },
5446
- "node_modules/serve-static": {
5447
- "version": "1.16.3",
5448
- "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz",
5449
- "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==",
5450
- "license": "MIT",
5451
- "dependencies": {
5452
- "encodeurl": "~2.0.0",
5453
- "escape-html": "~1.0.3",
5454
- "parseurl": "~1.3.3",
5455
- "send": "~0.19.1"
5456
- },
5457
- "engines": {
5458
- "node": ">= 0.8.0"
5459
- }
5460
- },
5461
  "node_modules/set-cookie-parser": {
5462
  "version": "2.7.2",
5463
  "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
5464
  "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
5465
  "license": "MIT"
5466
  },
5467
- "node_modules/setprototypeof": {
5468
- "version": "1.2.0",
5469
- "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
5470
- "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==",
5471
- "license": "ISC"
5472
- },
5473
  "node_modules/shebang-command": {
5474
  "version": "2.0.0",
5475
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
@@ -5493,78 +4727,6 @@
5493
  "node": ">=8"
5494
  }
5495
  },
5496
- "node_modules/side-channel": {
5497
- "version": "1.1.0",
5498
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
5499
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
5500
- "license": "MIT",
5501
- "dependencies": {
5502
- "es-errors": "^1.3.0",
5503
- "object-inspect": "^1.13.3",
5504
- "side-channel-list": "^1.0.0",
5505
- "side-channel-map": "^1.0.1",
5506
- "side-channel-weakmap": "^1.0.2"
5507
- },
5508
- "engines": {
5509
- "node": ">= 0.4"
5510
- },
5511
- "funding": {
5512
- "url": "https://github.com/sponsors/ljharb"
5513
- }
5514
- },
5515
- "node_modules/side-channel-list": {
5516
- "version": "1.0.0",
5517
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
5518
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
5519
- "license": "MIT",
5520
- "dependencies": {
5521
- "es-errors": "^1.3.0",
5522
- "object-inspect": "^1.13.3"
5523
- },
5524
- "engines": {
5525
- "node": ">= 0.4"
5526
- },
5527
- "funding": {
5528
- "url": "https://github.com/sponsors/ljharb"
5529
- }
5530
- },
5531
- "node_modules/side-channel-map": {
5532
- "version": "1.0.1",
5533
- "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
5534
- "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
5535
- "license": "MIT",
5536
- "dependencies": {
5537
- "call-bound": "^1.0.2",
5538
- "es-errors": "^1.3.0",
5539
- "get-intrinsic": "^1.2.5",
5540
- "object-inspect": "^1.13.3"
5541
- },
5542
- "engines": {
5543
- "node": ">= 0.4"
5544
- },
5545
- "funding": {
5546
- "url": "https://github.com/sponsors/ljharb"
5547
- }
5548
- },
5549
- "node_modules/side-channel-weakmap": {
5550
- "version": "1.0.2",
5551
- "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
5552
- "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
5553
- "license": "MIT",
5554
- "dependencies": {
5555
- "call-bound": "^1.0.2",
5556
- "es-errors": "^1.3.0",
5557
- "get-intrinsic": "^1.2.5",
5558
- "object-inspect": "^1.13.3",
5559
- "side-channel-map": "^1.0.1"
5560
- },
5561
- "engines": {
5562
- "node": ">= 0.4"
5563
- },
5564
- "funding": {
5565
- "url": "https://github.com/sponsors/ljharb"
5566
- }
5567
- },
5568
  "node_modules/source-map": {
5569
  "version": "0.5.7",
5570
  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
@@ -5593,30 +4755,6 @@
5593
  "url": "https://github.com/sponsors/wooorm"
5594
  }
5595
  },
5596
- "node_modules/statuses": {
5597
- "version": "2.0.2",
5598
- "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz",
5599
- "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==",
5600
- "license": "MIT",
5601
- "engines": {
5602
- "node": ">= 0.8"
5603
- }
5604
- },
5605
- "node_modules/string-width": {
5606
- "version": "4.2.3",
5607
- "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
5608
- "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
5609
- "license": "MIT",
5610
- "optional": true,
5611
- "dependencies": {
5612
- "emoji-regex": "^8.0.0",
5613
- "is-fullwidth-code-point": "^3.0.0",
5614
- "strip-ansi": "^6.0.1"
5615
- },
5616
- "engines": {
5617
- "node": ">=8"
5618
- }
5619
- },
5620
  "node_modules/stringify-entities": {
5621
  "version": "4.0.4",
5622
  "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
@@ -5630,19 +4768,6 @@
5630
  "url": "https://github.com/sponsors/wooorm"
5631
  }
5632
  },
5633
- "node_modules/strip-ansi": {
5634
- "version": "6.0.1",
5635
- "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
5636
- "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
5637
- "license": "MIT",
5638
- "optional": true,
5639
- "dependencies": {
5640
- "ansi-regex": "^5.0.1"
5641
- },
5642
- "engines": {
5643
- "node": ">=8"
5644
- }
5645
- },
5646
  "node_modules/strip-json-comments": {
5647
  "version": "3.1.1",
5648
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -5720,15 +4845,6 @@
5720
  "url": "https://github.com/sponsors/SuperchupuDev"
5721
  }
5722
  },
5723
- "node_modules/toidentifier": {
5724
- "version": "1.0.1",
5725
- "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
5726
- "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
5727
- "license": "MIT",
5728
- "engines": {
5729
- "node": ">=0.6"
5730
- }
5731
- },
5732
  "node_modules/trim-lines": {
5733
  "version": "3.0.1",
5734
  "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
@@ -5766,19 +4882,6 @@
5766
  "node": ">= 0.8.0"
5767
  }
5768
  },
5769
- "node_modules/type-is": {
5770
- "version": "1.6.18",
5771
- "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
5772
- "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
5773
- "license": "MIT",
5774
- "dependencies": {
5775
- "media-typer": "0.3.0",
5776
- "mime-types": "~2.1.24"
5777
- },
5778
- "engines": {
5779
- "node": ">= 0.6"
5780
- }
5781
- },
5782
  "node_modules/unified": {
5783
  "version": "11.0.5",
5784
  "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
@@ -5873,15 +4976,6 @@
5873
  "url": "https://opencollective.com/unified"
5874
  }
5875
  },
5876
- "node_modules/unpipe": {
5877
- "version": "1.0.0",
5878
- "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
5879
- "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
5880
- "license": "MIT",
5881
- "engines": {
5882
- "node": ">= 0.8"
5883
- }
5884
- },
5885
  "node_modules/update-browserslist-db": {
5886
  "version": "1.2.2",
5887
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
@@ -5923,24 +5017,6 @@
5923
  "punycode": "^2.1.0"
5924
  }
5925
  },
5926
- "node_modules/utils-merge": {
5927
- "version": "1.0.1",
5928
- "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
5929
- "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
5930
- "license": "MIT",
5931
- "engines": {
5932
- "node": ">= 0.4.0"
5933
- }
5934
- },
5935
- "node_modules/vary": {
5936
- "version": "1.1.2",
5937
- "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
5938
- "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
5939
- "license": "MIT",
5940
- "engines": {
5941
- "node": ">= 0.8"
5942
- }
5943
- },
5944
  "node_modules/vfile": {
5945
  "version": "6.0.3",
5946
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
 
10
  "dependencies": {
11
  "@emotion/react": "^11.14.0",
12
  "@emotion/styled": "^11.14.1",
 
13
  "@mui/icons-material": "^7.3.6",
14
  "@mui/material": "^7.3.6",
15
  "@react-spring/web": "^10.0.3",
 
16
  "framer-motion": "^12.23.26",
17
  "highlight.js": "^11.11.1",
18
  "react": "^19.2.0",
 
1070
  "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
1071
  }
1072
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  "node_modules/@humanfs/core": {
1074
  "version": "0.19.1",
1075
  "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz",
 
1978
  "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
1979
  }
1980
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1981
  "node_modules/acorn": {
1982
  "version": "8.15.0",
1983
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
 
2019
  "url": "https://github.com/sponsors/epoberezkin"
2020
  }
2021
  },
 
 
 
 
 
 
 
 
 
 
2022
  "node_modules/ansi-styles": {
2023
  "version": "4.3.0",
2024
  "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
 
2042
  "dev": true,
2043
  "license": "Python-2.0"
2044
  },
 
 
 
 
 
 
2045
  "node_modules/babel-plugin-macros": {
2046
  "version": "3.1.0",
2047
  "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz",
 
2083
  "baseline-browser-mapping": "dist/cli.js"
2084
  }
2085
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2086
  "node_modules/brace-expansion": {
2087
  "version": "1.1.12",
2088
  "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz",
 
2129
  "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7"
2130
  }
2131
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2132
  "node_modules/callsites": {
2133
  "version": "3.1.0",
2134
  "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
 
2221
  "url": "https://github.com/sponsors/wooorm"
2222
  }
2223
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
2224
  "node_modules/clsx": {
2225
  "version": "2.1.1",
2226
  "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
 
2266
  "dev": true,
2267
  "license": "MIT"
2268
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2269
  "node_modules/convert-source-map": {
2270
  "version": "1.9.0",
2271
  "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz",
 
2285
  "url": "https://opencollective.com/express"
2286
  }
2287
  },
 
 
 
 
 
 
2288
  "node_modules/cosmiconfig": {
2289
  "version": "7.1.0",
2290
  "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz",
 
2367
  "dev": true,
2368
  "license": "MIT"
2369
  },
 
 
 
 
 
 
 
 
 
2370
  "node_modules/dequal": {
2371
  "version": "2.0.3",
2372
  "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
 
2375
  "node": ">=6"
2376
  }
2377
  },
 
 
 
 
 
 
 
 
 
 
2378
  "node_modules/devlop": {
2379
  "version": "1.1.0",
2380
  "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
 
2397
  "csstype": "^3.0.2"
2398
  }
2399
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2400
  "node_modules/electron-to-chromium": {
2401
  "version": "1.5.267",
2402
  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz",
 
2404
  "dev": true,
2405
  "license": "ISC"
2406
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2407
  "node_modules/error-ex": {
2408
  "version": "1.3.4",
2409
  "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz",
 
2413
  "is-arrayish": "^0.2.1"
2414
  }
2415
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2416
  "node_modules/esbuild": {
2417
  "version": "0.25.12",
2418
  "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz",
 
2465
  "node": ">=6"
2466
  }
2467
  },
 
 
 
 
 
 
2468
  "node_modules/escape-string-regexp": {
2469
  "version": "4.0.0",
2470
  "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
 
2671
  "node": ">=0.10.0"
2672
  }
2673
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2674
  "node_modules/extend": {
2675
  "version": "3.0.2",
2676
  "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
 
2728
  "node": ">=16.0.0"
2729
  }
2730
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2731
  "node_modules/find-root": {
2732
  "version": "1.1.0",
2733
  "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz",
 
2772
  "dev": true,
2773
  "license": "ISC"
2774
  },
 
 
 
 
 
 
 
 
 
2775
  "node_modules/framer-motion": {
2776
  "version": "12.23.26",
2777
  "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.23.26.tgz",
 
2799
  }
2800
  }
2801
  },
 
 
 
 
 
 
 
 
 
2802
  "node_modules/fsevents": {
2803
  "version": "2.3.3",
2804
  "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
 
2832
  "node": ">=6.9.0"
2833
  }
2834
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2835
  "node_modules/glob-parent": {
2836
  "version": "6.0.2",
2837
  "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
 
2858
  "url": "https://github.com/sponsors/sindresorhus"
2859
  }
2860
  },
 
 
 
 
 
 
 
 
 
 
 
 
2861
  "node_modules/has-flag": {
2862
  "version": "4.0.0",
2863
  "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
 
2868
  "node": ">=8"
2869
  }
2870
  },
 
 
 
 
 
 
 
 
 
 
 
 
2871
  "node_modules/hasown": {
2872
  "version": "2.0.2",
2873
  "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
 
2989
  "url": "https://opencollective.com/unified"
2990
  }
2991
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2992
  "node_modules/ignore": {
2993
  "version": "5.3.2",
2994
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
 
3025
  "node": ">=0.8.19"
3026
  }
3027
  },
 
 
 
 
 
 
3028
  "node_modules/inline-style-parser": {
3029
  "version": "0.2.7",
3030
  "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.7.tgz",
3031
  "integrity": "sha512-Nb2ctOyNR8DqQoR0OwRG95uNWIC0C1lCgf5Naz5H6Ji72KZ8OcFZLz2P5sNgwlyoJ8Yif11oMuYs5pBQa86csA=="
3032
  },
 
 
 
 
 
 
 
 
 
3033
  "node_modules/is-alphabetical": {
3034
  "version": "2.0.1",
3035
  "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
 
3092
  "node": ">=0.10.0"
3093
  }
3094
  },
 
 
 
 
 
 
 
 
 
 
3095
  "node_modules/is-glob": {
3096
  "version": "4.0.3",
3097
  "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
 
3310
  "url": "https://github.com/sponsors/wooorm"
3311
  }
3312
  },
 
 
 
 
 
 
 
 
 
3313
  "node_modules/mdast-util-find-and-replace": {
3314
  "version": "3.0.2",
3315
  "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
 
3576
  "url": "https://opencollective.com/unified"
3577
  }
3578
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3579
  "node_modules/micromark": {
3580
  "version": "4.0.2",
3581
  "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
 
4111
  }
4112
  ]
4113
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4114
  "node_modules/minimatch": {
4115
  "version": "3.1.2",
4116
  "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
 
4171
  "dev": true,
4172
  "license": "MIT"
4173
  },
 
 
 
 
 
 
 
 
 
4174
  "node_modules/node-releases": {
4175
  "version": "2.0.27",
4176
  "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz",
 
4187
  "node": ">=0.10.0"
4188
  }
4189
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4190
  "node_modules/optionator": {
4191
  "version": "0.9.4",
4192
  "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
 
4290
  "url": "https://github.com/sponsors/sindresorhus"
4291
  }
4292
  },
 
 
 
 
 
 
 
 
 
4293
  "node_modules/path-exists": {
4294
  "version": "4.0.0",
4295
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
 
4316
  "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==",
4317
  "license": "MIT"
4318
  },
 
 
 
 
 
 
4319
  "node_modules/path-type": {
4320
  "version": "4.0.0",
4321
  "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
 
4404
  "url": "https://github.com/sponsors/wooorm"
4405
  }
4406
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
4407
  "node_modules/punycode": {
4408
  "version": "2.3.1",
4409
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 
4414
  "node": ">=6"
4415
  }
4416
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4417
  "node_modules/react": {
4418
  "version": "19.2.3",
4419
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz",
 
4682
  "fsevents": "~2.3.2"
4683
  }
4684
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4685
  "node_modules/scheduler": {
4686
  "version": "0.27.0",
4687
  "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz",
 
4698
  "semver": "bin/semver.js"
4699
  }
4700
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4701
  "node_modules/set-cookie-parser": {
4702
  "version": "2.7.2",
4703
  "resolved": "https://registry.npmjs.org/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz",
4704
  "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==",
4705
  "license": "MIT"
4706
  },
 
 
 
 
 
 
4707
  "node_modules/shebang-command": {
4708
  "version": "2.0.0",
4709
  "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
 
4727
  "node": ">=8"
4728
  }
4729
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4730
  "node_modules/source-map": {
4731
  "version": "0.5.7",
4732
  "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz",
 
4755
  "url": "https://github.com/sponsors/wooorm"
4756
  }
4757
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4758
  "node_modules/stringify-entities": {
4759
  "version": "4.0.4",
4760
  "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
 
4768
  "url": "https://github.com/sponsors/wooorm"
4769
  }
4770
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
4771
  "node_modules/strip-json-comments": {
4772
  "version": "3.1.1",
4773
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 
4845
  "url": "https://github.com/sponsors/SuperchupuDev"
4846
  }
4847
  },
 
 
 
 
 
 
 
 
 
4848
  "node_modules/trim-lines": {
4849
  "version": "3.0.1",
4850
  "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
 
4882
  "node": ">= 0.8.0"
4883
  }
4884
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
4885
  "node_modules/unified": {
4886
  "version": "11.0.5",
4887
  "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
 
4976
  "url": "https://opencollective.com/unified"
4977
  }
4978
  },
 
 
 
 
 
 
 
 
 
4979
  "node_modules/update-browserslist-db": {
4980
  "version": "1.2.2",
4981
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.2.tgz",
 
5017
  "punycode": "^2.1.0"
5018
  }
5019
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5020
  "node_modules/vfile": {
5021
  "version": "6.0.3",
5022
  "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
package.json CHANGED
@@ -12,15 +12,13 @@
12
  "start:prod": "NODE_ENV=production node server/index.js"
13
  },
14
  "dependencies": {
 
15
  "@emotion/react": "^11.14.0",
16
  "@emotion/styled": "^11.14.1",
17
- "@huggingface/hub": "^2.8.1",
18
  "@mui/icons-material": "^7.3.6",
19
  "@mui/material": "^7.3.6",
20
  "@react-spring/web": "^10.0.3",
21
- "express": "^4.21.2",
22
  "framer-motion": "^12.23.26",
23
- "fuse.js": "^7.1.0",
24
  "highlight.js": "^11.11.1",
25
  "react": "^19.2.0",
26
  "react-dom": "^19.2.0",
 
12
  "start:prod": "NODE_ENV=production node server/index.js"
13
  },
14
  "dependencies": {
15
+ "express": "^4.21.2",
16
  "@emotion/react": "^11.14.0",
17
  "@emotion/styled": "^11.14.1",
 
18
  "@mui/icons-material": "^7.3.6",
19
  "@mui/material": "^7.3.6",
20
  "@react-spring/web": "^10.0.3",
 
21
  "framer-motion": "^12.23.26",
 
22
  "highlight.js": "^11.11.1",
23
  "react": "^19.2.0",
24
  "react-dom": "^19.2.0",
server/index.js CHANGED
@@ -8,7 +8,7 @@ const app = express();
8
  const PORT = process.env.PORT || 7860;
9
 
10
  // Cache configuration
11
- const CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes
12
  const OFFICIAL_APP_LIST_URL = 'https://huggingface.co/datasets/pollen-robotics/reachy-mini-official-app-store/raw/main/app-list.json';
13
  const HF_SPACES_API = 'https://huggingface.co/api/spaces';
14
  // Note: HF API doesn't support pagination with filter=, so we use a high limit
@@ -22,7 +22,6 @@ let appsCache = {
22
  };
23
 
24
  // Fetch apps from HuggingFace API
25
- // Returns format compatible with desktop app (with url, source_kind, extra)
26
  async function fetchAppsFromHF() {
27
  console.log('[Cache] Fetching apps from HuggingFace API...');
28
 
@@ -44,86 +43,36 @@ async function fetchAppsFromHF() {
44
  const allSpaces = await spacesResponse.json();
45
  console.log(`[Cache] Fetched ${allSpaces.length} spaces from HuggingFace`);
46
 
47
- // 3. Build apps list in desktop-compatible format
48
  const allApps = allSpaces.map(space => {
49
  const spaceId = space.id || '';
50
  const tags = space.tags || [];
51
  const isOfficial = officialSet.has(spaceId.toLowerCase());
52
  const isPythonApp = tags.includes('reachy_mini_python_app');
53
- const author = spaceId.split('/')[0];
54
- const name = spaceId.split('/').pop();
55
 
56
  return {
57
- // Core fields (used by both website and desktop)
58
  id: spaceId,
59
- name,
60
  description: space.cardData?.short_description || '',
61
- url: `https://huggingface.co/spaces/${spaceId}`,
62
- source_kind: 'hf_space',
 
 
63
  isOfficial,
64
-
65
- // Extra metadata (desktop-compatible structure)
66
- extra: {
67
- id: spaceId,
68
- author,
69
- likes: space.likes || 0,
70
- downloads: space.downloads || 0,
71
- createdAt: space.createdAt || null,
72
- lastModified: space.lastModified,
73
- runtime: space.runtime || null,
74
- tags,
75
- isPythonApp,
76
- cardData: {
77
- emoji: space.cardData?.emoji || (isPythonApp ? '📦' : '🌐'),
78
- short_description: space.cardData?.short_description || '',
79
- sdk: space.cardData?.sdk || null,
80
- tags: space.cardData?.tags || [],
81
- // Preserve other cardData fields
82
- ...space.cardData,
83
- },
84
- },
85
  };
86
  });
87
 
88
- // Deduplicate by name: forks keep the same repo name (e.g. 4 spaces
89
- // named "reachy_mini_conversation_app" from different authors).
90
- // Priority: 1) official, 2) oldest (original), 3) most likes as tiebreaker.
91
- const deduped = new Map();
92
- for (const app of allApps) {
93
- const key = app.name.toLowerCase();
94
- const existing = deduped.get(key);
95
- if (!existing) {
96
- deduped.set(key, app);
97
- continue;
98
- }
99
- // Official always wins
100
- if (app.isOfficial && !existing.isOfficial) {
101
- deduped.set(key, app);
102
- continue;
103
- }
104
- if (existing.isOfficial) continue;
105
- // Oldest wins (the original is created before its forks)
106
- const appDate = app.extra.createdAt ? new Date(app.extra.createdAt).getTime() : Infinity;
107
- const existingDate = existing.extra.createdAt ? new Date(existing.extra.createdAt).getTime() : Infinity;
108
- if (appDate < existingDate) {
109
- deduped.set(key, app);
110
- } else if (appDate === existingDate && (app.extra.likes || 0) > (existing.extra.likes || 0)) {
111
- deduped.set(key, app);
112
- }
113
- }
114
- const uniqueApps = [...deduped.values()];
115
-
116
- console.log(`[Cache] Deduplicated ${allApps.length} → ${uniqueApps.length} apps (removed ${allApps.length - uniqueApps.length} forks with duplicate names)`);
117
-
118
  // Sort: official first, then by likes
119
- uniqueApps.sort((a, b) => {
120
  if (a.isOfficial !== b.isOfficial) {
121
  return a.isOfficial ? -1 : 1;
122
  }
123
- return (b.extra.likes || 0) - (a.extra.likes || 0);
124
  });
125
 
126
- return uniqueApps;
127
  } catch (err) {
128
  console.error('[Cache] Error fetching apps:', err);
129
  throw err;
@@ -183,22 +132,6 @@ app.get('/api/apps', async (req, res) => {
183
  }
184
  });
185
 
186
- // OAuth config endpoint - expose public OAuth variables to the frontend
187
- // (Docker Spaces don't auto-inject window.huggingface.variables like static Spaces)
188
- app.get('/api/oauth-config', (req, res) => {
189
- const clientId = process.env.OAUTH_CLIENT_ID;
190
- const scopes = process.env.OAUTH_SCOPES || 'openid profile';
191
-
192
- if (!clientId) {
193
- return res.status(503).json({
194
- error: 'OAuth not configured',
195
- hint: 'Make sure hf_oauth: true is set in README.md and the Space has been rebuilt',
196
- });
197
- }
198
-
199
- res.json({ clientId, scopes });
200
- });
201
-
202
  // Health check
203
  app.get('/api/health', (req, res) => {
204
  res.json({
 
8
  const PORT = process.env.PORT || 7860;
9
 
10
  // Cache configuration
11
+ const CACHE_TTL_MS = 24 * 60 * 60 * 1000; // 24 hours
12
  const OFFICIAL_APP_LIST_URL = 'https://huggingface.co/datasets/pollen-robotics/reachy-mini-official-app-store/raw/main/app-list.json';
13
  const HF_SPACES_API = 'https://huggingface.co/api/spaces';
14
  // Note: HF API doesn't support pagination with filter=, so we use a high limit
 
22
  };
23
 
24
  // Fetch apps from HuggingFace API
 
25
  async function fetchAppsFromHF() {
26
  console.log('[Cache] Fetching apps from HuggingFace API...');
27
 
 
43
  const allSpaces = await spacesResponse.json();
44
  console.log(`[Cache] Fetched ${allSpaces.length} spaces from HuggingFace`);
45
 
46
+ // 3. Build apps list with isOfficial and isPythonApp flags
47
  const allApps = allSpaces.map(space => {
48
  const spaceId = space.id || '';
49
  const tags = space.tags || [];
50
  const isOfficial = officialSet.has(spaceId.toLowerCase());
51
  const isPythonApp = tags.includes('reachy_mini_python_app');
 
 
52
 
53
  return {
 
54
  id: spaceId,
55
+ name: spaceId.split('/').pop(),
56
  description: space.cardData?.short_description || '',
57
+ cardData: space.cardData || {},
58
+ likes: space.likes || 0,
59
+ lastModified: space.lastModified,
60
+ author: spaceId.split('/')[0],
61
  isOfficial,
62
+ isPythonApp,
63
+ tags,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
64
  };
65
  });
66
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
  // Sort: official first, then by likes
68
+ allApps.sort((a, b) => {
69
  if (a.isOfficial !== b.isOfficial) {
70
  return a.isOfficial ? -1 : 1;
71
  }
72
+ return (b.likes || 0) - (a.likes || 0);
73
  });
74
 
75
+ return allApps;
76
  } catch (err) {
77
  console.error('[Cache] Error fetching apps:', err);
78
  throw err;
 
132
  }
133
  });
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  // Health check
136
  app.get('/api/health', (req, res) => {
137
  res.json({
src/App.jsx CHANGED
@@ -1,9 +1,8 @@
1
  import { useEffect } from 'react';
2
- import { BrowserRouter, Routes, Route, Navigate, useLocation, useNavigate } from 'react-router-dom';
3
  import { ThemeProvider, CssBaseline } from '@mui/material';
4
  import theme from './theme/theme';
5
  import { AppsProvider } from './context/AppsContext';
6
- import { AuthProvider } from './context/AuthContext';
7
 
8
  import Home from './pages/Home';
9
  import Download from './pages/Download';
@@ -13,59 +12,14 @@ import Buy from './pages/Buy';
13
  import GettingStarted from './pages/GettingStarted';
14
  import Build from './pages/Build';
15
 
16
- /**
17
- * Handle hash-to-path redirect for HuggingFace Spaces iframe embedding.
18
- *
19
- * HF propagates the parent page's hash to the iframe on initial load.
20
- * For example, visiting huggingface.co/reachy-mini#/apps will load the
21
- * iframe at *.hf.space/#/apps. This component reads that hash and
22
- * converts it to a BrowserRouter path (e.g. /apps).
23
- */
24
- function HashRedirect() {
25
- const navigate = useNavigate();
26
-
27
- useEffect(() => {
28
- const hash = window.location.hash;
29
- // Match hash routes like #/apps, #/download, #apps, #download, etc.
30
- if (hash && hash.length > 1) {
31
- // Support both #/apps and #apps formats
32
- const path = hash.startsWith('#/') ? hash.slice(1) : `/${hash.slice(1)}`;
33
- // Use replaceState to cleanly remove hash without triggering navigation
34
- window.history.replaceState(null, '', window.location.pathname);
35
- navigate(path, { replace: true });
36
- }
37
- }, [navigate]);
38
-
39
- return null;
40
- }
41
-
42
- /**
43
- * Sync the current route back to the HF parent page via postMessage.
44
- * This updates the URL in the browser address bar so users can
45
- * copy/share deep links (e.g. huggingface.co/reachy-mini#/apps).
46
- *
47
- * Also handles scrollTo query parameter for anchor-like behavior.
48
- */
49
- function RouteSync() {
50
  const location = useLocation();
51
 
52
  useEffect(() => {
53
- // Sync current path to parent frame hash (for HF Spaces embedding)
54
- const isInIframe = window.parent !== window;
55
- if (isInIframe && location.pathname !== '/') {
56
- window.parent.postMessage(
57
- { hash: `#${location.pathname}` },
58
- 'https://huggingface.co'
59
- );
60
- } else if (isInIframe && location.pathname === '/') {
61
- // Clear hash when on home page
62
- window.parent.postMessage({ hash: '' }, 'https://huggingface.co');
63
- }
64
-
65
- // Handle scrollTo query parameter
66
  const params = new URLSearchParams(location.search);
67
  const scrollTo = params.get('scrollTo');
68
-
69
  if (scrollTo) {
70
  // Retry mechanism to wait for element to be rendered
71
  const scrollToElement = (retries = 0) => {
@@ -73,11 +27,14 @@ function RouteSync() {
73
  if (element) {
74
  element.scrollIntoView({ behavior: 'smooth', block: 'center' });
75
  } else if (retries < 10) {
 
76
  setTimeout(() => scrollToElement(retries + 1), 100);
77
  }
78
  };
 
79
  setTimeout(() => scrollToElement(), 300);
80
  } else {
 
81
  window.scrollTo({ top: 0, behavior: 'smooth' });
82
  }
83
  }, [location.pathname, location.search]);
@@ -89,25 +46,20 @@ export default function App() {
89
  return (
90
  <ThemeProvider theme={theme}>
91
  <CssBaseline />
92
- <AuthProvider>
93
- <AppsProvider>
94
- <BrowserRouter>
95
- <HashRedirect />
96
- <RouteSync />
97
- <Routes>
98
- <Route path="/" element={<Home />} />
99
- <Route path="/getting-started" element={<GettingStarted />} />
100
- <Route path="https://huggingface.co/docs/reachy_mini/" element={<Build />} />
101
- <Route path="/download" element={<Download />} />
102
- <Route path="https://huggingface.co/docs/reachy_mini/troubleshooting" element={<FAQ />} />
103
- <Route path="/apps" element={<Apps />} />
104
- <Route path="/buy" element={<Buy />} />
105
- {/* Catch-all: redirect unknown routes to home */}
106
- <Route path="*" element={<Navigate to="/" replace />} />
107
- </Routes>
108
- </BrowserRouter>
109
- </AppsProvider>
110
- </AuthProvider>
111
  </ThemeProvider>
112
  );
113
  }
 
1
  import { useEffect } from 'react';
2
+ import { HashRouter, Routes, Route, useLocation } from 'react-router-dom';
3
  import { ThemeProvider, CssBaseline } from '@mui/material';
4
  import theme from './theme/theme';
5
  import { AppsProvider } from './context/AppsContext';
 
6
 
7
  import Home from './pages/Home';
8
  import Download from './pages/Download';
 
12
  import GettingStarted from './pages/GettingStarted';
13
  import Build from './pages/Build';
14
 
15
+ function ScrollToTop() {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16
  const location = useLocation();
17
 
18
  useEffect(() => {
19
+ // Check for scrollTo query parameter (used for anchor-like behavior with HashRouter)
 
 
 
 
 
 
 
 
 
 
 
 
20
  const params = new URLSearchParams(location.search);
21
  const scrollTo = params.get('scrollTo');
22
+
23
  if (scrollTo) {
24
  // Retry mechanism to wait for element to be rendered
25
  const scrollToElement = (retries = 0) => {
 
27
  if (element) {
28
  element.scrollIntoView({ behavior: 'smooth', block: 'center' });
29
  } else if (retries < 10) {
30
+ // Retry up to 10 times with 100ms interval
31
  setTimeout(() => scrollToElement(retries + 1), 100);
32
  }
33
  };
34
+ // Initial delay for page render
35
  setTimeout(() => scrollToElement(), 300);
36
  } else {
37
+ // Otherwise scroll to top
38
  window.scrollTo({ top: 0, behavior: 'smooth' });
39
  }
40
  }, [location.pathname, location.search]);
 
46
  return (
47
  <ThemeProvider theme={theme}>
48
  <CssBaseline />
49
+ <AppsProvider>
50
+ <HashRouter>
51
+ <ScrollToTop />
52
+ <Routes>
53
+ <Route path="/" element={<Home />} />
54
+ <Route path="/getting-started" element={<GettingStarted />} />
55
+ <Route path="https://huggingface.co/docs/reachy_mini/" element={<Build />} />
56
+ <Route path="/download" element={<Download />} />
57
+ <Route path="https://huggingface.co/docs/reachy_mini/troubleshooting" element={<FAQ />} />
58
+ <Route path="/apps" element={<Apps />} />
59
+ <Route path="/buy" element={<Buy />} />
60
+ </Routes>
61
+ </HashRouter>
62
+ </AppsProvider>
 
 
 
 
 
63
  </ThemeProvider>
64
  );
65
  }
src/components/InstallModal.jsx CHANGED
@@ -8,20 +8,16 @@ import {
8
  DialogContent,
9
  IconButton,
10
  Link,
11
- Tooltip,
12
  Typography,
13
  } from '@mui/material';
14
  import CloseIcon from '@mui/icons-material/Close';
15
  import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
16
- import FavoriteIcon from '@mui/icons-material/Favorite';
17
  import VerifiedIcon from '@mui/icons-material/Verified';
18
  import DownloadIcon from '@mui/icons-material/Download';
19
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
20
  import ComputerIcon from '@mui/icons-material/Computer';
21
- import { useAuth } from '../context/AuthContext';
22
 
23
  function InstallModal({ open, onClose, app }) {
24
- const { isLoggedIn, isSpaceLiked, toggleLike } = useAuth();
25
  // Detect Linux users
26
  const isLinux = useMemo(() => {
27
  if (typeof navigator === 'undefined') return false;
@@ -33,18 +29,16 @@ function InstallModal({ open, onClose, app }) {
33
  if (!app) return null;
34
 
35
  const appName = app.name || app.id?.split('/').pop();
36
- const cardData = app.extra?.cardData || {};
37
  const emoji = cardData.emoji || '📦';
38
  const description = cardData.short_description || app.description || 'No description';
39
  const deepLinkUrl = `reachymini://install/${appName}`;
40
- const spaceUrl = app.url || `https://huggingface.co/spaces/${app.id}`;
41
 
42
- const author = app.extra?.author || app.id?.split('/')?.[0] || null;
43
  const isOfficial = app.isOfficial;
44
- const baseLikes = app.extra?.likes || 0;
45
- const isLiked = isSpaceLiked(app.id);
46
- const displayedLikes = baseLikes + (isLiked ? 1 : 0);
47
- const lastModified = app.extra?.lastModified || null;
48
  const formattedDate = lastModified
49
  ? new Date(lastModified).toLocaleDateString('en-US', {
50
  month: 'short',
@@ -170,47 +164,10 @@ function InstallModal({ open, onClose, app }) {
170
 
171
  {/* Stats row */}
172
  <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mt: 1 }}>
173
- <Tooltip
174
- title={isLoggedIn ? '' : 'Sign in to like this app'}
175
- arrow
176
- placement="top"
177
- disableHoverListener={isLoggedIn}
178
- >
179
- <Box
180
- component="button"
181
- onClick={() => toggleLike(app.id)}
182
- sx={{
183
- display: 'flex',
184
- alignItems: 'center',
185
- gap: 0.5,
186
- border: 'none',
187
- bgcolor: 'transparent',
188
- cursor: 'pointer',
189
- p: 0.5,
190
- m: -0.5,
191
- borderRadius: '6px',
192
- transition: 'all 0.15s ease',
193
- '&:hover': {
194
- bgcolor: 'rgba(236, 72, 153, 0.08)',
195
- },
196
- }}
197
- >
198
- {isLiked ? (
199
- <FavoriteIcon sx={{ fontSize: 14, color: '#ec4899' }} />
200
- ) : (
201
- <FavoriteBorderIcon sx={{ fontSize: 14, color: '#999' }} />
202
- )}
203
- <Typography
204
- sx={{
205
- fontSize: 12,
206
- color: isLiked ? '#ec4899' : '#999',
207
- fontWeight: isLiked ? 600 : 400,
208
- }}
209
- >
210
- {displayedLikes}
211
- </Typography>
212
- </Box>
213
- </Tooltip>
214
  {formattedDate && (
215
  <Typography sx={{ fontSize: 12, color: '#aaa' }}>
216
  Updated {formattedDate}
 
8
  DialogContent,
9
  IconButton,
10
  Link,
 
11
  Typography,
12
  } from '@mui/material';
13
  import CloseIcon from '@mui/icons-material/Close';
14
  import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
 
15
  import VerifiedIcon from '@mui/icons-material/Verified';
16
  import DownloadIcon from '@mui/icons-material/Download';
17
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
18
  import ComputerIcon from '@mui/icons-material/Computer';
 
19
 
20
  function InstallModal({ open, onClose, app }) {
 
21
  // Detect Linux users
22
  const isLinux = useMemo(() => {
23
  if (typeof navigator === 'undefined') return false;
 
29
  if (!app) return null;
30
 
31
  const appName = app.name || app.id?.split('/').pop();
32
+ const cardData = app.cardData || {};
33
  const emoji = cardData.emoji || '📦';
34
  const description = cardData.short_description || app.description || 'No description';
35
  const deepLinkUrl = `reachymini://install/${appName}`;
36
+ const spaceUrl = `https://huggingface.co/spaces/${app.id}`;
37
 
38
+ const author = app.id?.split('/')?.[0] || app.author || null;
39
  const isOfficial = app.isOfficial;
40
+ const likes = app.likes || 0;
41
+ const lastModified = app.lastModified || app.createdAt || null;
 
 
42
  const formattedDate = lastModified
43
  ? new Date(lastModified).toLocaleDateString('en-US', {
44
  month: 'short',
 
164
 
165
  {/* Stats row */}
166
  <Box sx={{ display: 'flex', alignItems: 'center', gap: 2, mt: 1 }}>
167
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
168
+ <FavoriteBorderIcon sx={{ fontSize: 14, color: '#999' }} />
169
+ <Typography sx={{ fontSize: 12, color: '#999' }}>{likes}</Typography>
170
+ </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
171
  {formattedDate && (
172
  <Typography sx={{ fontSize: 12, color: '#aaa' }}>
173
  Updated {formattedDate}
src/context/AppsContext.jsx CHANGED
@@ -11,7 +11,6 @@ const FALLBACK_OFFICIAL_URL = 'https://huggingface.co/datasets/pollen-robotics/r
11
  const FALLBACK_LIMIT = 1000;
12
 
13
  // Fallback: fetch directly from HuggingFace API (for dev mode)
14
- // Returns same format as server (desktop-compatible)
15
  async function fetchAppsDirectFromHF() {
16
  console.log('[AppsContext] Fallback: fetching directly from HuggingFace API');
17
 
@@ -31,42 +30,24 @@ async function fetchAppsDirectFromHF() {
31
  }
32
  const allSpaces = await spacesResponse.json();
33
 
34
- // Build apps list in desktop-compatible format
35
  const allApps = allSpaces.map(space => {
36
  const spaceId = space.id || '';
37
  const tags = space.tags || [];
38
  const isOfficial = officialSet.has(spaceId.toLowerCase());
39
  const isPythonApp = tags.includes('reachy_mini_python_app');
40
- const author = spaceId.split('/')[0];
41
- const name = spaceId.split('/').pop();
42
 
43
  return {
44
- // Core fields
45
  id: spaceId,
46
- name,
47
  description: space.cardData?.short_description || '',
48
- url: `https://huggingface.co/spaces/${spaceId}`,
49
- source_kind: 'hf_space',
 
 
50
  isOfficial,
51
-
52
- // Extra metadata (desktop-compatible structure)
53
- extra: {
54
- id: spaceId,
55
- author,
56
- likes: space.likes || 0,
57
- downloads: space.downloads || 0,
58
- lastModified: space.lastModified,
59
- runtime: space.runtime || null,
60
- tags,
61
- isPythonApp,
62
- cardData: {
63
- emoji: space.cardData?.emoji || (isPythonApp ? '📦' : '🌐'),
64
- short_description: space.cardData?.short_description || '',
65
- sdk: space.cardData?.sdk || null,
66
- tags: space.cardData?.tags || [],
67
- ...space.cardData,
68
- },
69
- },
70
  };
71
  });
72
 
@@ -75,7 +56,7 @@ async function fetchAppsDirectFromHF() {
75
  if (a.isOfficial !== b.isOfficial) {
76
  return a.isOfficial ? -1 : 1;
77
  }
78
- return (b.extra.likes || 0) - (a.extra.likes || 0);
79
  });
80
 
81
  return allApps;
 
11
  const FALLBACK_LIMIT = 1000;
12
 
13
  // Fallback: fetch directly from HuggingFace API (for dev mode)
 
14
  async function fetchAppsDirectFromHF() {
15
  console.log('[AppsContext] Fallback: fetching directly from HuggingFace API');
16
 
 
30
  }
31
  const allSpaces = await spacesResponse.json();
32
 
33
+ // Build apps list
34
  const allApps = allSpaces.map(space => {
35
  const spaceId = space.id || '';
36
  const tags = space.tags || [];
37
  const isOfficial = officialSet.has(spaceId.toLowerCase());
38
  const isPythonApp = tags.includes('reachy_mini_python_app');
 
 
39
 
40
  return {
 
41
  id: spaceId,
42
+ name: spaceId.split('/').pop(),
43
  description: space.cardData?.short_description || '',
44
+ cardData: space.cardData || {},
45
+ likes: space.likes || 0,
46
+ lastModified: space.lastModified,
47
+ author: spaceId.split('/')[0],
48
  isOfficial,
49
+ isPythonApp,
50
+ tags,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
51
  };
52
  });
53
 
 
56
  if (a.isOfficial !== b.isOfficial) {
57
  return a.isOfficial ? -1 : 1;
58
  }
59
+ return (b.likes || 0) - (a.likes || 0);
60
  });
61
 
62
  return allApps;
src/context/AuthContext.jsx DELETED
@@ -1,248 +0,0 @@
1
- import { createContext, useContext, useState, useEffect, useCallback, useRef } from 'react';
2
- import { oauthLoginUrl, oauthHandleRedirectIfPresent } from '@huggingface/hub';
3
-
4
- const AuthContext = createContext(null);
5
-
6
- const HF_API = 'https://huggingface.co';
7
-
8
- // Whether we're running inside an iframe (i.e. embedded on huggingface.co)
9
- const isInIframe = typeof window !== 'undefined' && window.parent !== window;
10
-
11
- /**
12
- * Fetch OAuth config (clientId, scopes) from our Express server.
13
- * Docker Spaces don't auto-inject window.huggingface.variables like static Spaces,
14
- * so we expose OAUTH_CLIENT_ID via a server endpoint.
15
- */
16
- async function fetchOAuthConfig() {
17
- try {
18
- const response = await fetch('/api/oauth-config');
19
- if (!response.ok) return null;
20
- return await response.json();
21
- } catch {
22
- return null;
23
- }
24
- }
25
-
26
- /**
27
- * Fetch all space IDs liked by a user.
28
- * Returns a Set of lowercase space IDs (e.g. "pollen-robotics/reachy-mini-teleop").
29
- */
30
- async function fetchUserLikedSpaces(username) {
31
- try {
32
- const response = await fetch(`${HF_API}/api/users/${username}/likes`);
33
- if (!response.ok) return new Set();
34
- const likes = await response.json();
35
- // Filter only spaces and return their repo names as a Set
36
- return new Set(
37
- likes
38
- .filter((item) => item.repo?.type === 'space')
39
- .map((item) => item.repo.name.toLowerCase())
40
- );
41
- } catch (err) {
42
- console.error('[Auth] Failed to fetch user likes:', err);
43
- return new Set();
44
- }
45
- }
46
-
47
- /**
48
- * Send a like request to the HF parent frame via postMessage.
49
- * Returns a Promise that resolves with the response data.
50
- * The parent frame (huggingface.co) handles auth via session cookies.
51
- */
52
- function likeViaPostMessage(spaceId) {
53
- return new Promise((resolve, reject) => {
54
- const timeout = setTimeout(() => {
55
- window.removeEventListener('message', handler);
56
- reject(new Error('Like request timed out'));
57
- }, 5000);
58
-
59
- function handler(event) {
60
- if (event.data?.type !== 'LIKE_REPO_RESPONSE') return;
61
- clearTimeout(timeout);
62
- window.removeEventListener('message', handler);
63
- resolve(event.data);
64
- }
65
-
66
- window.addEventListener('message', handler);
67
- window.parent.postMessage(
68
- { type: 'LIKE_REPO_REQUEST', repo: { type: 'space', name: spaceId } },
69
- '*'
70
- );
71
- });
72
- }
73
-
74
- // Provider component
75
- export function AuthProvider({ children }) {
76
- const [user, setUser] = useState(null); // { name, avatarUrl }
77
- const [likedSpaceIds, setLikedSpaceIds] = useState(new Set());
78
- const [isLoading, setIsLoading] = useState(true);
79
- const [oauthConfig, setOauthConfig] = useState(null); // { clientId, scopes }
80
- const pendingLikes = useRef(new Set()); // Track in-flight like requests
81
-
82
- // On mount: fetch OAuth config + check if user just completed OAuth redirect
83
- useEffect(() => {
84
- async function init() {
85
- try {
86
- // Fetch OAuth config from server (needed for Docker Spaces)
87
- const config = await fetchOAuthConfig();
88
- if (config?.clientId) {
89
- setOauthConfig(config);
90
- console.log('[Auth] OAuth config loaded (clientId available)');
91
- } else {
92
- console.log('[Auth] OAuth not available (no clientId from server)');
93
- }
94
-
95
- // Check if user just completed OAuth redirect
96
- const oauthResult = await oauthHandleRedirectIfPresent();
97
- if (oauthResult) {
98
- const { userInfo } = oauthResult;
99
- const userData = {
100
- name: userInfo.name,
101
- preferredUsername: userInfo.preferred_username || userInfo.name,
102
- avatarUrl: userInfo.picture,
103
- };
104
- setUser(userData);
105
-
106
- // Fetch user's liked spaces
107
- const likes = await fetchUserLikedSpaces(userData.preferredUsername);
108
- setLikedSpaceIds(likes);
109
-
110
- console.log(
111
- `[Auth] Logged in as ${userData.preferredUsername}, ${likes.size} liked spaces`
112
- );
113
- }
114
- } catch (err) {
115
- console.error('[Auth] Init error:', err);
116
- } finally {
117
- setIsLoading(false);
118
- }
119
- }
120
-
121
- init();
122
- }, []);
123
-
124
- // Login: redirect to HF OAuth (passing clientId from server config)
125
- const login = useCallback(async () => {
126
- if (!oauthConfig?.clientId) {
127
- console.warn('[Auth] Cannot login: OAuth not configured');
128
- return;
129
- }
130
-
131
- try {
132
- const loginUrl = await oauthLoginUrl({
133
- clientId: oauthConfig.clientId,
134
- scopes: oauthConfig.scopes || 'openid profile',
135
- });
136
- window.location.href = loginUrl;
137
- } catch (err) {
138
- console.error('[Auth] Failed to get OAuth URL:', err);
139
- }
140
- }, [oauthConfig]);
141
-
142
- // Logout: clear state
143
- const logout = useCallback(() => {
144
- setUser(null);
145
- setLikedSpaceIds(new Set());
146
- }, []);
147
-
148
- // Check if a space is liked
149
- const isSpaceLiked = useCallback(
150
- (spaceId) => {
151
- return likedSpaceIds.has(spaceId?.toLowerCase());
152
- },
153
- [likedSpaceIds]
154
- );
155
-
156
- /**
157
- * Like a space via the HF parent frame postMessage protocol.
158
- * The parent (huggingface.co) handles auth via session cookies.
159
- * Like-only (no unlike) — if already liked, it's a no-op.
160
- */
161
- const toggleLike = useCallback(
162
- async (spaceId) => {
163
- const spaceIdLower = spaceId?.toLowerCase();
164
- if (!spaceIdLower) return;
165
-
166
- // Already liked → no-op (postMessage only supports like, not unlike)
167
- if (likedSpaceIds.has(spaceIdLower)) return;
168
-
169
- // Not in iframe → can't use postMessage, prompt OAuth login as fallback
170
- if (!isInIframe) {
171
- console.warn('[Auth] Not in iframe, postMessage unavailable');
172
- return;
173
- }
174
-
175
- // Prevent duplicate requests
176
- if (pendingLikes.current.has(spaceIdLower)) return;
177
- pendingLikes.current.add(spaceIdLower);
178
-
179
- // Optimistic update
180
- setLikedSpaceIds((prev) => {
181
- const next = new Set(prev);
182
- next.add(spaceIdLower);
183
- return next;
184
- });
185
-
186
- try {
187
- const result = await likeViaPostMessage(spaceId);
188
-
189
- if (result.error) {
190
- throw new Error(`${result.error.code}: ${result.error.message}`);
191
- }
192
-
193
- if (result.status === 'not_logged_in') {
194
- // User not logged in to HF → revert and prompt login
195
- throw new Error('not_logged_in');
196
- }
197
-
198
- // "done" or "already_liked" → success
199
- console.log(`[Auth] Liked ${spaceId}: ${result.status}`, result.likes != null ? `(${result.likes} likes)` : '');
200
- } catch (err) {
201
- console.error(`[Auth] Failed to like ${spaceId}:`, err.message);
202
-
203
- // Revert optimistic update
204
- setLikedSpaceIds((prev) => {
205
- const reverted = new Set(prev);
206
- reverted.delete(spaceIdLower);
207
- return reverted;
208
- });
209
-
210
- // If not logged in, prompt OAuth login
211
- if (err.message === 'not_logged_in') {
212
- login();
213
- }
214
- } finally {
215
- pendingLikes.current.delete(spaceIdLower);
216
- }
217
- },
218
- [likedSpaceIds, login]
219
- );
220
-
221
- return (
222
- <AuthContext.Provider
223
- value={{
224
- user,
225
- isLoggedIn: !!user,
226
- isLoading,
227
- isOAuthAvailable: !!oauthConfig?.clientId,
228
- isInIframe,
229
- likedSpaceIds,
230
- login,
231
- logout,
232
- isSpaceLiked,
233
- toggleLike,
234
- }}
235
- >
236
- {children}
237
- </AuthContext.Provider>
238
- );
239
- }
240
-
241
- // Hook to use auth context
242
- export function useAuth() {
243
- const context = useContext(AuthContext);
244
- if (!context) {
245
- throw new Error('useAuth must be used within an AuthProvider');
246
- }
247
- return context;
248
- }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/pages/Apps.jsx CHANGED
@@ -1,4 +1,4 @@
1
- import { useState, useMemo, useCallback, useEffect, useRef, memo } from 'react';
2
  import {
3
  Box,
4
  Container,
@@ -12,87 +12,29 @@ import {
12
  Link,
13
  IconButton,
14
  Button,
15
- Tooltip,
16
  } from '@mui/material';
17
  import SearchIcon from '@mui/icons-material/Search';
18
  import CloseIcon from '@mui/icons-material/Close';
19
  import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
20
- import FavoriteIcon from '@mui/icons-material/Favorite';
21
  import AccessTimeIcon from '@mui/icons-material/AccessTime';
22
  import VerifiedIcon from '@mui/icons-material/Verified';
23
  import DownloadIcon from '@mui/icons-material/Download';
24
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
25
- import LogoutIcon from '@mui/icons-material/Logout';
26
  import Layout from '../components/Layout';
27
  import ReachiesCarousel from '../components/ReachiesCarousel';
28
  import { useApps } from '../context/AppsContext';
29
- import { useAuth } from '../context/AuthContext';
30
  import InstallModal from '../components/InstallModal';
31
 
32
- /**
33
- * Render text with highlighted match ranges from Fuse.js.
34
- * indices is an array of [start, end] pairs.
35
- */
36
- function HighlightText({ text, indices }) {
37
- if (!text) return null;
38
- if (!indices || indices.length === 0) return text;
39
-
40
- // Only keep matches that span at least 2 characters
41
- const significant = indices.filter(([start, end]) => end - start >= 1);
42
- if (significant.length === 0) return text;
43
-
44
- // Merge overlapping / adjacent ranges and sort
45
- const sorted = [...significant].sort((a, b) => a[0] - b[0]);
46
- const merged = [sorted[0]];
47
- for (let i = 1; i < sorted.length; i++) {
48
- const prev = merged[merged.length - 1];
49
- if (sorted[i][0] <= prev[1] + 1) {
50
- prev[1] = Math.max(prev[1], sorted[i][1]);
51
- } else {
52
- merged.push(sorted[i]);
53
- }
54
- }
55
-
56
- const parts = [];
57
- let cursor = 0;
58
- for (const [start, end] of merged) {
59
- if (cursor < start) {
60
- parts.push(<span key={`t${cursor}`}>{text.slice(cursor, start)}</span>);
61
- }
62
- parts.push(
63
- <span
64
- key={`h${start}`}
65
- style={{
66
- backgroundColor: 'rgba(255, 149, 0, 0.18)',
67
- color: '#b36b00',
68
- borderRadius: 2,
69
- padding: '0 1px',
70
- }}
71
- >
72
- {text.slice(start, end + 1)}
73
- </span>
74
- );
75
- cursor = end + 1;
76
- }
77
- if (cursor < text.length) {
78
- parts.push(<span key={`t${cursor}`}>{text.slice(cursor)}</span>);
79
- }
80
- return <>{parts}</>;
81
- }
82
-
83
- // App Card Component (memoized to avoid re-renders when only search changes)
84
- const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLike, isLoggedIn, matchData }) {
85
  const isOfficial = app.isOfficial;
86
- const isPythonApp = app.extra?.isPythonApp !== false; // Default to true for backwards compatibility
87
- const cardData = app.extra?.cardData || {};
88
- const author = app.extra?.author || app.id?.split('/')?.[0] || null;
89
- const baseLikes = app.extra?.likes || 0;
90
- const lastModified = app.extra?.lastModified || null;
91
  const emoji = cardData.emoji || (isPythonApp ? '📦' : '🌐');
92
- const spaceUrl = app.url || `https://huggingface.co/spaces/${app.id}`;
93
-
94
- // Compute displayed likes: adjust based on like state vs original data
95
- const displayedLikes = baseLikes + (isLiked ? 1 : 0);
96
 
97
  const formattedDate = lastModified
98
  ? new Date(lastModified).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
@@ -177,7 +119,7 @@ const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLi
177
  whiteSpace: 'nowrap',
178
  }}
179
  >
180
- <HighlightText text={author} indices={matchData?._searchAuthor} />
181
  </Typography>
182
  </Box>
183
  )}
@@ -226,52 +168,19 @@ const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLi
226
  )}
227
  </Box>
228
 
229
- {/* Likes - Interactive */}
230
- <Tooltip
231
- title={isLoggedIn ? '' : 'Sign in to like this app'}
232
- arrow
233
- placement="top"
234
- disableHoverListener={isLoggedIn}
235
- >
236
- <Box
237
- component="button"
238
- onClick={(e) => {
239
- e.preventDefault();
240
- e.stopPropagation();
241
- onToggleLike?.(app.id);
242
- }}
243
  sx={{
244
- display: 'flex',
245
- alignItems: 'center',
246
- gap: 0.5,
247
- flexShrink: 0,
248
- border: 'none',
249
- bgcolor: 'transparent',
250
- cursor: 'pointer',
251
- p: 0.5,
252
- borderRadius: '6px',
253
- transition: 'all 0.15s ease',
254
- '&:hover': {
255
- bgcolor: 'rgba(236, 72, 153, 0.08)',
256
- },
257
  }}
258
  >
259
- {isLiked ? (
260
- <FavoriteIcon sx={{ fontSize: 16, color: '#ec4899' }} />
261
- ) : (
262
- <FavoriteBorderIcon sx={{ fontSize: 16, color: '#666666' }} />
263
- )}
264
- <Typography
265
- sx={{
266
- fontSize: 12,
267
- fontWeight: 600,
268
- color: isLiked ? '#ec4899' : '#666666',
269
- }}
270
- >
271
- {displayedLikes}
272
- </Typography>
273
- </Box>
274
- </Tooltip>
275
  </Box>
276
 
277
  {/* Content */}
@@ -298,10 +207,7 @@ const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLi
298
  flex: 1,
299
  }}
300
  >
301
- <HighlightText
302
- text={app.name || app.id?.split('/').pop()}
303
- indices={matchData?.name}
304
- />
305
  </Typography>
306
 
307
  <Typography
@@ -331,10 +237,7 @@ const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLi
331
  flex: 1,
332
  }}
333
  >
334
- <HighlightText
335
- text={cardData.short_description || app.description || 'No description'}
336
- indices={matchData?._searchDescription}
337
- />
338
  </Typography>
339
 
340
  {/* Date + Install Button */}
@@ -385,115 +288,14 @@ const AppCard = memo(function AppCard({ app, onInstallClick, isLiked, onToggleLi
385
  </Box>
386
  </Box>
387
  );
388
- });
389
-
390
- // Isolated search input — typing only re-renders this component, not the whole page
391
- const SearchInput = memo(function SearchInput({ onSearch }) {
392
- const [value, setValue] = useState('');
393
- const debounceRef = useRef(null);
394
-
395
- useEffect(() => {
396
- clearTimeout(debounceRef.current);
397
- if (!value.trim()) {
398
- onSearch('');
399
- return;
400
- }
401
- debounceRef.current = setTimeout(() => onSearch(value.trim()), 200);
402
- return () => clearTimeout(debounceRef.current);
403
- }, [value, onSearch]);
404
-
405
- return (
406
- <>
407
- <SearchIcon sx={{ fontSize: 22, color: '#999' }} />
408
- <InputBase
409
- placeholder="Search apps by name, description, tags..."
410
- value={value}
411
- onChange={(e) => setValue(e.target.value)}
412
- sx={{
413
- flex: 1,
414
- fontSize: 15,
415
- fontWeight: 500,
416
- color: '#333',
417
- '& input::placeholder': {
418
- color: '#999',
419
- opacity: 1,
420
- },
421
- }}
422
- />
423
- {value && (
424
- <IconButton
425
- onClick={() => { setValue(''); onSearch(''); }}
426
- size="small"
427
- sx={{ color: '#999' }}
428
- >
429
- <CloseIcon sx={{ fontSize: 20 }} />
430
- </IconButton>
431
- )}
432
- </>
433
- );
434
- });
435
-
436
- // Tags to exclude from category filters
437
- const EXCLUDED_TAGS = new Set([
438
- 'reachy_mini', 'reachy-mini', 'reachy_mini_python_app',
439
- 'static', 'docker', 'region:us', 'region:eu',
440
- ]);
441
-
442
- // Format a tag name for display (e.g. "reachy_mini_game" → "Reachy Mini Game")
443
- function formatTagName(tag) {
444
- if (tag.startsWith('sdk:')) {
445
- const sdk = tag.replace('sdk:', '');
446
- return sdk.charAt(0).toUpperCase() + sdk.slice(1).toLowerCase();
447
- }
448
- return tag
449
- .replace(/_/g, ' ')
450
- .replace(/\b\w/g, (l) => l.toUpperCase());
451
  }
452
 
453
  // Main Apps Page
454
  export default function Apps() {
455
  // Get apps from context (cached globally)
456
  const { apps, loading, error } = useApps();
457
- const { user, isLoggedIn, isOAuthAvailable, login, logout, isSpaceLiked, toggleLike } = useAuth();
458
  const [officialOnly, setOfficialOnly] = useState(false);
459
- const [selectedCategory, setSelectedCategory] = useState(null);
460
- const [searchResults, setSearchResults] = useState(null); // null = no search, [] = no matches
461
- const [isSearching, setIsSearching] = useState(false);
462
- const workerRef = useRef(null);
463
-
464
- // Initialize search Web Worker
465
- useEffect(() => {
466
- workerRef.current = new Worker(
467
- new URL('../workers/searchWorker.js', import.meta.url),
468
- { type: 'module' }
469
- );
470
-
471
- workerRef.current.onmessage = (e) => {
472
- if (e.data.type === 'RESULTS') {
473
- setSearchResults(e.data.results);
474
- }
475
- };
476
-
477
- return () => workerRef.current?.terminate();
478
- }, []);
479
-
480
- // Send apps to worker to build index whenever apps change
481
- useEffect(() => {
482
- if (workerRef.current && apps.length > 0) {
483
- workerRef.current.postMessage({ type: 'INDEX', apps });
484
- }
485
- }, [apps]);
486
-
487
- // Callback from SearchInput component (already debounced)
488
- const handleSearch = useCallback((query) => {
489
- if (!query) {
490
- setSearchResults(null);
491
- setIsSearching(false);
492
- return;
493
- }
494
- setIsSearching(true);
495
- workerRef.current?.postMessage({ type: 'SEARCH', query });
496
- }, []);
497
 
498
  // Install modal state
499
  const [installModalOpen, setInstallModalOpen] = useState(false);
@@ -509,110 +311,30 @@ export default function Apps() {
509
  setSelectedApp(null);
510
  };
511
 
512
- const handleToggleLike = useCallback(
513
- (spaceId) => {
514
- toggleLike(spaceId);
515
- },
516
- [toggleLike]
517
- );
518
-
519
- // Extract available categories from app tags, sorted by count (top 8)
520
- const categories = useMemo(() => {
521
- const categoryMap = new Map();
522
-
523
- // Use only apps matching current mode (official toggle)
524
- const baseApps = officialOnly ? apps.filter((a) => a.isOfficial) : apps;
525
-
526
- baseApps.forEach((app) => {
527
- const rootTags = app.extra?.tags || [];
528
- const cardDataTags = app.extra?.cardData?.tags || [];
529
- const allTags = [...new Set([...rootTags, ...cardDataTags])];
530
- const sdk = app.extra?.sdk || app.extra?.cardData?.sdk;
531
-
532
- allTags.forEach((tag) => {
533
- if (
534
- tag &&
535
- typeof tag === 'string' &&
536
- !tag.startsWith('region:') &&
537
- !EXCLUDED_TAGS.has(tag.toLowerCase())
538
- ) {
539
- categoryMap.set(tag, (categoryMap.get(tag) || 0) + 1);
540
- }
541
- });
542
-
543
- // Add SDK as category if not already covered by a tag
544
- if (sdk && typeof sdk === 'string') {
545
- const hasMatchingTag = allTags.some(
546
- (t) => t && typeof t === 'string' && t.toLowerCase() === sdk.toLowerCase()
547
- );
548
- if (!hasMatchingTag) {
549
- const sdkKey = `sdk:${sdk}`;
550
- categoryMap.set(sdkKey, (categoryMap.get(sdkKey) || 0) + 1);
551
- }
552
- }
553
- });
554
-
555
- return Array.from(categoryMap.entries())
556
- .map(([name, count]) => ({ name, count }))
557
- .sort((a, b) => (b.count !== a.count ? b.count - a.count : a.name.localeCompare(b.name)))
558
- .slice(0, 8);
559
- }, [apps, officialOnly]);
560
-
561
- // Filter apps based on worker search results, official toggle, and category
562
  const filteredApps = useMemo(() => {
563
  let result = apps;
564
 
565
  // Filter by official
566
  if (officialOnly) {
567
- result = result.filter((app) => app.isOfficial === true);
568
- }
569
-
570
- // Filter by category
571
- if (selectedCategory) {
572
- result = result.filter((app) => {
573
- const rootTags = app.extra?.tags || [];
574
- const cardDataTags = app.extra?.cardData?.tags || [];
575
- const allTags = [...new Set([...rootTags, ...cardDataTags])];
576
- const sdk = app.extra?.sdk || app.extra?.cardData?.sdk;
577
-
578
- if (selectedCategory.startsWith('sdk:')) {
579
- return sdk === selectedCategory.replace('sdk:', '');
580
- }
581
- const tagMatch = allTags.some(
582
- (t) => t && typeof t === 'string' && t.toLowerCase() === selectedCategory.toLowerCase()
583
- );
584
- const sdkMatch =
585
- sdk && typeof sdk === 'string' && sdk.toLowerCase() === selectedCategory.toLowerCase();
586
- return tagMatch || sdkMatch;
587
- });
588
  }
589
 
590
- // Apply fuzzy search results from worker
591
- if (searchResults !== null) {
592
- const scoreMap = new Map(searchResults.map((r) => [r.id, r.score]));
593
- const matchedIds = new Set(searchResults.map((r) => r.id));
594
- result = result.filter((app) => matchedIds.has(app.id));
595
- result.sort((a, b) => (scoreMap.get(a.id) || 1) - (scoreMap.get(b.id) || 1));
596
- return result;
 
 
597
  }
598
 
599
- // Default sort: by likes (descending)
600
- result.sort((a, b) => (b.extra?.likes || 0) - (a.extra?.likes || 0));
601
-
602
  return result;
603
- }, [apps, officialOnly, selectedCategory, searchResults]);
604
-
605
- // Build a map of app ID → match highlight data
606
- const matchDataMap = useMemo(() => {
607
- if (!searchResults) return null;
608
- const map = new Map();
609
- for (const r of searchResults) {
610
- map.set(r.id, r.matches);
611
- }
612
- return map;
613
- }, [searchResults]);
614
 
615
- const isFiltered = searchResults !== null || officialOnly || selectedCategory;
616
 
617
  return (
618
  <Layout transparentHeader>
@@ -735,7 +457,7 @@ export default function Apps() {
735
  </Box>
736
 
737
  {/* Search Section */}
738
- <Container maxWidth="lg" sx={{ mt: -4, mb: 1, position: 'relative', zIndex: 10 }}>
739
  <Box
740
  sx={{
741
  display: 'flex',
@@ -743,14 +465,39 @@ export default function Apps() {
743
  gap: 2,
744
  px: 3,
745
  py: 2,
746
- mb: 2,
747
  borderRadius: '16px',
748
  bgcolor: 'white',
749
  boxShadow: '0 4px 24px rgba(0, 0, 0, 0.1)',
750
  border: '1px solid rgba(0, 0, 0, 0.06)',
751
  }}
752
  >
753
- <SearchInput onSearch={handleSearch} />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
754
 
755
  {/* Separator */}
756
  <Box sx={{ width: '1px', height: '24px', bgcolor: 'rgba(0, 0, 0, 0.1)' }} />
@@ -795,192 +542,9 @@ export default function Apps() {
795
  }
796
  sx={{ m: 0 }}
797
  />
798
-
799
- {/* Auth: Login / User (only show when OAuth is available) */}
800
- {isOAuthAvailable && (
801
- <Box sx={{ width: '1px', height: '24px', bgcolor: 'rgba(0, 0, 0, 0.1)' }} />
802
- )}
803
-
804
- {isLoggedIn ? (
805
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 1 }}>
806
- <Avatar
807
- src={user?.avatarUrl}
808
- sx={{
809
- width: 28,
810
- height: 28,
811
- fontSize: 12,
812
- fontWeight: 600,
813
- bgcolor: '#FF9500',
814
- }}
815
- >
816
- {user?.name?.charAt(0)?.toUpperCase()}
817
- </Avatar>
818
- <Typography
819
- sx={{
820
- fontSize: 13,
821
- fontWeight: 600,
822
- color: '#333',
823
- maxWidth: 100,
824
- overflow: 'hidden',
825
- textOverflow: 'ellipsis',
826
- whiteSpace: 'nowrap',
827
- }}
828
- >
829
- {user?.preferredUsername || user?.name}
830
- </Typography>
831
- <Tooltip title="Sign out">
832
- <IconButton onClick={logout} size="small" sx={{ color: '#999' }}>
833
- <LogoutIcon sx={{ fontSize: 18 }} />
834
- </IconButton>
835
- </Tooltip>
836
- </Box>
837
- ) : isOAuthAvailable ? (
838
- <Button
839
- onClick={login}
840
- variant="text"
841
- size="small"
842
- sx={{
843
- textTransform: 'none',
844
- fontWeight: 600,
845
- fontSize: 13,
846
- color: '#333',
847
- gap: 0.75,
848
- px: 1.5,
849
- borderRadius: '8px',
850
- whiteSpace: 'nowrap',
851
- '&:hover': {
852
- bgcolor: 'rgba(0, 0, 0, 0.04)',
853
- },
854
- }}
855
- >
856
- <Box
857
- component="img"
858
- src="/assets/hf-logo.svg"
859
- alt=""
860
- sx={{ height: 16 }}
861
- />
862
- Sign in
863
- </Button>
864
- ) : null}
865
  </Box>
866
  </Container>
867
 
868
- {/* Category Tags */}
869
- {!loading && categories.length > 0 && (
870
- <Container maxWidth="lg" sx={{ mt: 1, mb: 4 }}>
871
- <Box
872
- sx={{
873
- display: 'flex',
874
- alignItems: 'center',
875
- gap: 1.5,
876
- flexWrap: 'wrap',
877
- }}
878
- >
879
- <Typography
880
- sx={{
881
- fontSize: 12,
882
- fontWeight: 600,
883
- color: '#999',
884
- textTransform: 'uppercase',
885
- letterSpacing: '0.05em',
886
- }}
887
- >
888
- Tags
889
- </Typography>
890
-
891
- {/* "All" chip */}
892
- <Chip
893
- label={
894
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
895
- <span>All</span>
896
- <Typography
897
- component="span"
898
- sx={{
899
- fontSize: 11,
900
- fontWeight: 600,
901
- color: !selectedCategory ? '#FF9500' : '#999',
902
- opacity: 0.8,
903
- }}
904
- >
905
- ({officialOnly ? apps.filter((a) => a.isOfficial).length : apps.length})
906
- </Typography>
907
- </Box>
908
- }
909
- onClick={() => setSelectedCategory(null)}
910
- size="small"
911
- sx={{
912
- height: 28,
913
- fontSize: 12,
914
- fontWeight: !selectedCategory ? 700 : 500,
915
- bgcolor: !selectedCategory
916
- ? 'rgba(255, 149, 0, 0.12)'
917
- : 'rgba(0, 0, 0, 0.04)',
918
- color: !selectedCategory ? '#FF9500' : '#666',
919
- border: !selectedCategory
920
- ? '1px solid rgba(255, 149, 0, 0.4)'
921
- : '1px solid rgba(0, 0, 0, 0.1)',
922
- cursor: 'pointer',
923
- transition: 'all 0.15s ease',
924
- '&:hover': {
925
- bgcolor: !selectedCategory
926
- ? 'rgba(255, 149, 0, 0.18)'
927
- : 'rgba(0, 0, 0, 0.08)',
928
- },
929
- '& .MuiChip-label': { px: 1.5 },
930
- }}
931
- />
932
-
933
- {/* Category chips */}
934
- {categories.map((cat) => {
935
- const isSelected = selectedCategory === cat.name;
936
- return (
937
- <Chip
938
- key={cat.name}
939
- label={
940
- <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
941
- <span>{formatTagName(cat.name)}</span>
942
- <Typography
943
- component="span"
944
- sx={{
945
- fontSize: 11,
946
- fontWeight: 600,
947
- color: isSelected ? '#FF9500' : '#999',
948
- opacity: 0.8,
949
- }}
950
- >
951
- ({cat.count})
952
- </Typography>
953
- </Box>
954
- }
955
- onClick={() => setSelectedCategory(isSelected ? null : cat.name)}
956
- size="small"
957
- sx={{
958
- height: 28,
959
- fontSize: 12,
960
- fontWeight: isSelected ? 700 : 500,
961
- bgcolor: isSelected
962
- ? 'rgba(255, 149, 0, 0.12)'
963
- : 'rgba(0, 0, 0, 0.04)',
964
- color: isSelected ? '#FF9500' : '#666',
965
- border: isSelected
966
- ? '1px solid rgba(255, 149, 0, 0.4)'
967
- : '1px solid rgba(0, 0, 0, 0.1)',
968
- cursor: 'pointer',
969
- transition: 'all 0.15s ease',
970
- '&:hover': {
971
- bgcolor: isSelected
972
- ? 'rgba(255, 149, 0, 0.18)'
973
- : 'rgba(0, 0, 0, 0.08)',
974
- },
975
- '& .MuiChip-label': { px: 1.5 },
976
- }}
977
- />
978
- );
979
- })}
980
- </Box>
981
- </Container>
982
- )}
983
-
984
  {/* Apps Grid */}
985
  <Container maxWidth="lg" sx={{ pb: 10 }}>
986
  {loading ? (
@@ -1018,10 +582,6 @@ export default function Apps() {
1018
  key={app.id || index}
1019
  app={app}
1020
  onInstallClick={handleInstallClick}
1021
- isLiked={isSpaceLiked(app.id)}
1022
- onToggleLike={handleToggleLike}
1023
- isLoggedIn={isLoggedIn}
1024
- matchData={matchDataMap?.get(app.id) || null}
1025
  />
1026
  ))}
1027
  </Box>
 
1
+ import { useState, useMemo } from 'react';
2
  import {
3
  Box,
4
  Container,
 
12
  Link,
13
  IconButton,
14
  Button,
 
15
  } from '@mui/material';
16
  import SearchIcon from '@mui/icons-material/Search';
17
  import CloseIcon from '@mui/icons-material/Close';
18
  import FavoriteBorderIcon from '@mui/icons-material/FavoriteBorder';
 
19
  import AccessTimeIcon from '@mui/icons-material/AccessTime';
20
  import VerifiedIcon from '@mui/icons-material/Verified';
21
  import DownloadIcon from '@mui/icons-material/Download';
22
  import OpenInNewIcon from '@mui/icons-material/OpenInNew';
 
23
  import Layout from '../components/Layout';
24
  import ReachiesCarousel from '../components/ReachiesCarousel';
25
  import { useApps } from '../context/AppsContext';
 
26
  import InstallModal from '../components/InstallModal';
27
 
28
+ // App Card Component
29
+ function AppCard({ app, onInstallClick }) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  const isOfficial = app.isOfficial;
31
+ const isPythonApp = app.isPythonApp !== false; // Default to true for backwards compatibility
32
+ const cardData = app.cardData || {};
33
+ const author = app.id?.split('/')?.[0] || app.author || null;
34
+ const likes = app.likes || 0;
35
+ const lastModified = app.lastModified || app.createdAt || null;
36
  const emoji = cardData.emoji || (isPythonApp ? '📦' : '🌐');
37
+ const spaceUrl = `https://huggingface.co/spaces/${app.id}`;
 
 
 
38
 
39
  const formattedDate = lastModified
40
  ? new Date(lastModified).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })
 
119
  whiteSpace: 'nowrap',
120
  }}
121
  >
122
+ {author}
123
  </Typography>
124
  </Box>
125
  )}
 
168
  )}
169
  </Box>
170
 
171
+ {/* Likes */}
172
+ <Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5, flexShrink: 0 }}>
173
+ <FavoriteBorderIcon sx={{ fontSize: 16, color: '#666666' }} />
174
+ <Typography
 
 
 
 
 
 
 
 
 
 
175
  sx={{
176
+ fontSize: 12,
177
+ fontWeight: 600,
178
+ color: '#666666',
 
 
 
 
 
 
 
 
 
 
179
  }}
180
  >
181
+ {likes}
182
+ </Typography>
183
+ </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
184
  </Box>
185
 
186
  {/* Content */}
 
207
  flex: 1,
208
  }}
209
  >
210
+ {app.name || app.id?.split('/').pop()}
 
 
 
211
  </Typography>
212
 
213
  <Typography
 
237
  flex: 1,
238
  }}
239
  >
240
+ {cardData.short_description || app.description || 'No description'}
 
 
 
241
  </Typography>
242
 
243
  {/* Date + Install Button */}
 
288
  </Box>
289
  </Box>
290
  );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
291
  }
292
 
293
  // Main Apps Page
294
  export default function Apps() {
295
  // Get apps from context (cached globally)
296
  const { apps, loading, error } = useApps();
297
+ const [searchQuery, setSearchQuery] = useState('');
298
  const [officialOnly, setOfficialOnly] = useState(false);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
299
 
300
  // Install modal state
301
  const [installModalOpen, setInstallModalOpen] = useState(false);
 
311
  setSelectedApp(null);
312
  };
313
 
314
+ // Filter apps based on search and official toggle
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
315
  const filteredApps = useMemo(() => {
316
  let result = apps;
317
 
318
  // Filter by official
319
  if (officialOnly) {
320
+ result = result.filter(app => app.isOfficial === true);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
321
  }
322
 
323
+ // Filter by search
324
+ if (searchQuery.trim()) {
325
+ const query = searchQuery.toLowerCase();
326
+ result = result.filter(app =>
327
+ app.name?.toLowerCase().includes(query) ||
328
+ app.id?.toLowerCase().includes(query) ||
329
+ app.description?.toLowerCase().includes(query) ||
330
+ app.cardData?.short_description?.toLowerCase().includes(query)
331
+ );
332
  }
333
 
 
 
 
334
  return result;
335
+ }, [apps, searchQuery, officialOnly]);
 
 
 
 
 
 
 
 
 
 
336
 
337
+ const isFiltered = searchQuery.trim() || officialOnly;
338
 
339
  return (
340
  <Layout transparentHeader>
 
457
  </Box>
458
 
459
  {/* Search Section */}
460
+ <Container maxWidth="lg" sx={{ mt: -4, mb: 4, position: 'relative', zIndex: 10 }}>
461
  <Box
462
  sx={{
463
  display: 'flex',
 
465
  gap: 2,
466
  px: 3,
467
  py: 2,
 
468
  borderRadius: '16px',
469
  bgcolor: 'white',
470
  boxShadow: '0 4px 24px rgba(0, 0, 0, 0.1)',
471
  border: '1px solid rgba(0, 0, 0, 0.06)',
472
  }}
473
  >
474
+ <SearchIcon sx={{ fontSize: 22, color: '#999' }} />
475
+ <InputBase
476
+ placeholder="Search apps by name or description..."
477
+ value={searchQuery}
478
+ onChange={(e) => setSearchQuery(e.target.value)}
479
+ sx={{
480
+ flex: 1,
481
+ fontSize: 15,
482
+ fontWeight: 500,
483
+ color: '#333',
484
+ '& input::placeholder': {
485
+ color: '#999',
486
+ opacity: 1,
487
+ },
488
+ }}
489
+ />
490
+
491
+ {/* Clear search */}
492
+ {searchQuery && (
493
+ <IconButton
494
+ onClick={() => setSearchQuery('')}
495
+ size="small"
496
+ sx={{ color: '#999' }}
497
+ >
498
+ <CloseIcon sx={{ fontSize: 20 }} />
499
+ </IconButton>
500
+ )}
501
 
502
  {/* Separator */}
503
  <Box sx={{ width: '1px', height: '24px', bgcolor: 'rgba(0, 0, 0, 0.1)' }} />
 
542
  }
543
  sx={{ m: 0 }}
544
  />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  </Box>
546
  </Container>
547
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
548
  {/* Apps Grid */}
549
  <Container maxWidth="lg" sx={{ pb: 10 }}>
550
  {loading ? (
 
582
  key={app.id || index}
583
  app={app}
584
  onInstallClick={handleInstallClick}
 
 
 
 
585
  />
586
  ))}
587
  </Box>
src/pages/Buy.jsx CHANGED
@@ -98,7 +98,7 @@ const faqItems = [
98
  },
99
  {
100
  question: 'What about customs and import taxes?',
101
- answer: 'EU/UK and US/Canada orders ship duty-paid (DDP) no surprise fees on delivery. Other destinations ship DAP, meaning local import duties and taxes may apply upon delivery.',
102
  },
103
  {
104
  question: 'Can I upgrade from Lite to Wireless later?',
@@ -372,25 +372,6 @@ function ProductCardsSection() {
372
  </Grid>
373
  ))}
374
  </Grid>
375
-
376
- {/* Lead time & shipping notice */}
377
- <Box sx={{ textAlign: 'center', mt: 5, mb: 6 }}>
378
- <Typography
379
- variant="body1"
380
- sx={{ fontWeight: 600, color: 'text.primary' }}
381
- >
382
- Current Lead time: 90 days after purchase
383
- </Typography>
384
- <Typography
385
- variant="body2"
386
- color="text.secondary"
387
- sx={{ maxWidth: 600, mx: 'auto', lineHeight: 1.7, mt: 1 }}
388
- >
389
- <strong>Import duties:</strong> EU/UK + US/Canada ship duty-paid (DDP).
390
- <br />
391
- Other destinations may incur local import duties/taxes on delivery (DAP).
392
- </Typography>
393
- </Box>
394
  </Container>
395
  );
396
  }
 
98
  },
99
  {
100
  question: 'What about customs and import taxes?',
101
+ answer: 'The displayed price does not include customs duties or import taxes, which vary by country. You may be responsible for these fees upon delivery.',
102
  },
103
  {
104
  question: 'Can I upgrade from Lite to Wireless later?',
 
372
  </Grid>
373
  ))}
374
  </Grid>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
375
  </Container>
376
  );
377
  }
src/pages/Download.jsx CHANGED
@@ -29,6 +29,12 @@ const PLATFORMS = {
29
  arch: 'M1, M2, M3, M4',
30
  format: '.dmg',
31
  },
 
 
 
 
 
 
32
  'windows-x86_64': {
33
  name: 'Windows',
34
  subtitle: '64-bit',
@@ -181,6 +187,13 @@ function parseReleasePlatforms(assets) {
181
  platforms['darwin-aarch64'] = { url };
182
  }
183
 
 
 
 
 
 
 
 
184
  // Windows - .msi (exclude .sig signature files)
185
  if (name.endsWith('.msi')) {
186
  platforms['windows-x86_64'] = { url };
@@ -206,6 +219,7 @@ function getDownloadUrlForPlatform(release, platform) {
206
  // Platform Card component
207
  function PlatformCard({ platformKey, url, isActive, onClick }) {
208
  const platform = PLATFORMS[platformKey];
 
209
  const isBeta = platformKey.includes('windows') || platformKey.includes('linux');
210
 
211
  return (
@@ -221,17 +235,37 @@ function PlatformCard({ platformKey, url, isActive, onClick }) {
221
  borderColor: isActive ? 'rgba(59, 130, 246, 0.4)' : 'rgba(255, 255, 255, 0.08)',
222
  backdropFilter: 'blur(10px)',
223
  transition: 'all 0.25s ease',
 
224
  '&:hover': {
225
  borderColor: isActive ? 'rgba(59, 130, 246, 0.6)' : 'rgba(255, 255, 255, 0.2)',
226
  transform: 'translateY(-4px)',
227
  boxShadow: isActive
228
  ? '0 12px 40px rgba(59, 130, 246, 0.2)'
229
  : '0 12px 40px rgba(0, 0, 0, 0.3)',
 
230
  },
231
  }}
232
  >
233
- {/* Beta tag for Windows and Linux */}
234
- {isBeta && (
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
235
  <Chip
236
  label="Beta"
237
  size="small"
@@ -251,7 +285,7 @@ function PlatformCard({ platformKey, url, isActive, onClick }) {
251
 
252
  <CardContent
253
  component="a"
254
- href={url}
255
  sx={{
256
  display: 'flex',
257
  flexDirection: 'column',
@@ -261,6 +295,7 @@ function PlatformCard({ platformKey, url, isActive, onClick }) {
261
  color: 'inherit',
262
  p: 3,
263
  '&:last-child': { pb: 3 },
 
264
  }}
265
  >
266
  <Box sx={{
@@ -521,67 +556,169 @@ export default function Download() {
521
  </Typography>
522
  </Stack>
523
 
524
- {/* Primary download button */}
525
- <Button
526
- variant="contained"
527
- size="large"
528
- href={currentUrl}
529
- startIcon={<DownloadIcon />}
530
- sx={{
531
- px: 6,
532
- py: 2,
533
- fontSize: 17,
534
- fontWeight: 600,
535
- borderRadius: 3,
536
- background: 'linear-gradient(135deg, #FF9500 0%, #764ba2 100%)',
537
- boxShadow: '0 8px 32px rgba(255, 149, 0, 0.35)',
538
- transition: 'all 0.3s ease',
539
- '&:hover': {
540
- boxShadow: '0 12px 48px rgba(59, 130, 246, 0.5)',
541
- transform: 'translateY(-2px)',
542
- },
543
- }}
544
- >
545
- Download for {currentPlatform?.name}
546
- </Button>
 
 
547
 
548
- <Typography
549
- variant="body2"
550
- sx={{
551
- color: 'rgba(255,255,255,0.4)',
552
- mt: 2,
553
- fontSize: 13,
554
- }}
555
- >
556
- {currentPlatform?.subtitle} • {currentPlatform?.format?.replace('.', '').toUpperCase()} package
557
- </Typography>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
558
 
559
- {/* Beta Warning for Windows and Linux */}
560
- {(detectedPlatform?.startsWith('windows') || detectedPlatform?.includes('linux')) && (
561
- <Box
562
- sx={{
563
- mt: 3,
564
- p: 2.5,
565
- background: 'linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)',
566
- border: '1px solid rgba(59, 130, 246, 0.3)',
567
- borderRadius: 2,
568
- maxWidth: 500,
569
- mx: 'auto',
570
- }}
571
- >
572
  <Typography
573
  variant="body2"
574
  sx={{
575
- color: 'rgba(255,255,255,0.8)',
576
- fontWeight: 500,
 
 
 
577
  }}
578
  >
579
- {detectedPlatform?.startsWith('windows')
580
- ? <>⚠️ Windows version is currently in Beta installation requires <strong style={{ color: 'rgba(255,255,255,0.9)' }}>administrator privileges</strong>.</>
581
- : <>⚠️ Linux version is currently in Beta — please report any issues on <a href="https://github.com/pollen-robotics/reachy-mini-desktop-app/issues" target="_blank" rel="noopener noreferrer" style={{ color: '#3b82f6', textDecoration: 'underline' }}>GitHub</a> or <a href="https://discord.gg/HDrGY9eJHt" target="_blank" rel="noopener noreferrer" style={{ color: '#3b82f6', textDecoration: 'underline' }}>Discord</a>.</>
582
- }
583
  </Typography>
584
- </Box>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
585
  )}
586
 
587
  {/* App screenshot */}
@@ -616,8 +753,8 @@ export default function Download() {
616
  </Typography>
617
 
618
  <Grid container spacing={2}>
619
- {['darwin-aarch64', 'windows-x86_64', 'linux-x86_64'].map((key) => (
620
- <Grid size={{ xs: 12, sm: 4 }} key={key}>
621
  <PlatformCard
622
  platformKey={key}
623
  url={releaseData?.platforms[key]?.url}
@@ -628,6 +765,26 @@ export default function Download() {
628
  ))}
629
  </Grid>
630
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
631
  </Box>
632
 
633
  {/* Features / What's included */}
 
29
  arch: 'M1, M2, M3, M4',
30
  format: '.dmg',
31
  },
32
+ 'darwin-x86_64': {
33
+ name: 'macOS',
34
+ subtitle: 'Intel',
35
+ arch: 'x86_64',
36
+ format: '.dmg',
37
+ },
38
  'windows-x86_64': {
39
  name: 'Windows',
40
  subtitle: '64-bit',
 
187
  platforms['darwin-aarch64'] = { url };
188
  }
189
 
190
+ // macOS Intel - prefer .dmg
191
+ if (name.includes('x64.dmg') && !name.includes('arm64')) {
192
+ platforms['darwin-x86_64'] = { url };
193
+ } else if (name.includes('darwin-x86_64') && !platforms['darwin-x86_64']) {
194
+ platforms['darwin-x86_64'] = { url };
195
+ }
196
+
197
  // Windows - .msi (exclude .sig signature files)
198
  if (name.endsWith('.msi')) {
199
  platforms['windows-x86_64'] = { url };
 
219
  // Platform Card component
220
  function PlatformCard({ platformKey, url, isActive, onClick }) {
221
  const platform = PLATFORMS[platformKey];
222
+ const isComingSoon = platformKey.includes('darwin-x86_64');
223
  const isBeta = platformKey.includes('windows') || platformKey.includes('linux');
224
 
225
  return (
 
235
  borderColor: isActive ? 'rgba(59, 130, 246, 0.4)' : 'rgba(255, 255, 255, 0.08)',
236
  backdropFilter: 'blur(10px)',
237
  transition: 'all 0.25s ease',
238
+ opacity: isComingSoon ? 0.7 : 1,
239
  '&:hover': {
240
  borderColor: isActive ? 'rgba(59, 130, 246, 0.6)' : 'rgba(255, 255, 255, 0.2)',
241
  transform: 'translateY(-4px)',
242
  boxShadow: isActive
243
  ? '0 12px 40px rgba(59, 130, 246, 0.2)'
244
  : '0 12px 40px rgba(0, 0, 0, 0.3)',
245
+ opacity: 1,
246
  },
247
  }}
248
  >
249
+ {/* Coming soon tag */}
250
+ {isComingSoon && (
251
+ <Chip
252
+ label="Coming soon"
253
+ size="small"
254
+ sx={{
255
+ position: 'absolute',
256
+ top: 8,
257
+ right: 8,
258
+ backgroundColor: 'rgba(255, 149, 0, 0.2)',
259
+ color: '#FF9500',
260
+ fontSize: 10,
261
+ fontWeight: 700,
262
+ height: 20,
263
+ '& .MuiChip-label': { px: 1 },
264
+ }}
265
+ />
266
+ )}
267
+ {/* Beta tag for Windows */}
268
+ {isBeta && !isComingSoon && (
269
  <Chip
270
  label="Beta"
271
  size="small"
 
285
 
286
  <CardContent
287
  component="a"
288
+ href={isComingSoon ? undefined : url}
289
  sx={{
290
  display: 'flex',
291
  flexDirection: 'column',
 
295
  color: 'inherit',
296
  p: 3,
297
  '&:last-child': { pb: 3 },
298
+ pointerEvents: isComingSoon ? 'none' : 'auto',
299
  }}
300
  >
301
  <Box sx={{
 
556
  </Typography>
557
  </Stack>
558
 
559
+ {/* Primary download button - different for Windows/macOS Apple Silicon/Linux vs macOS Intel */}
560
+ {detectedPlatform?.startsWith('windows') || detectedPlatform === 'darwin-aarch64' || detectedPlatform?.includes('linux') ? (
561
+ <>
562
+ <Button
563
+ variant="contained"
564
+ size="large"
565
+ href={currentUrl}
566
+ startIcon={<DownloadIcon />}
567
+ sx={{
568
+ px: 6,
569
+ py: 2,
570
+ fontSize: 17,
571
+ fontWeight: 600,
572
+ borderRadius: 3,
573
+ background: 'linear-gradient(135deg, #FF9500 0%, #764ba2 100%)',
574
+ boxShadow: '0 8px 32px rgba(255, 149, 0, 0.35)',
575
+ transition: 'all 0.3s ease',
576
+ '&:hover': {
577
+ boxShadow: '0 12px 48px rgba(59, 130, 246, 0.5)',
578
+ transform: 'translateY(-2px)',
579
+ },
580
+ }}
581
+ >
582
+ Download for {currentPlatform?.name}
583
+ </Button>
584
 
585
+ <Typography
586
+ variant="body2"
587
+ sx={{
588
+ color: 'rgba(255,255,255,0.4)',
589
+ mt: 2,
590
+ fontSize: 13,
591
+ }}
592
+ >
593
+ {currentPlatform?.subtitle} • {currentPlatform?.format?.replace('.', '').toUpperCase()} package
594
+ </Typography>
595
+
596
+ {/* Beta Warning for Windows and Linux */}
597
+ {(detectedPlatform?.startsWith('windows') || detectedPlatform?.includes('linux')) && (
598
+ <Box
599
+ sx={{
600
+ mt: 3,
601
+ p: 2.5,
602
+ background: 'linear-gradient(135deg, rgba(59, 130, 246, 0.1) 0%, rgba(139, 92, 246, 0.08) 100%)',
603
+ border: '1px solid rgba(59, 130, 246, 0.3)',
604
+ borderRadius: 2,
605
+ maxWidth: 500,
606
+ mx: 'auto',
607
+ }}
608
+ >
609
+ <Typography
610
+ variant="body2"
611
+ sx={{
612
+ color: 'rgba(255,255,255,0.8)',
613
+ fontWeight: 500,
614
+ }}
615
+ >
616
+ {detectedPlatform?.startsWith('windows')
617
+ ? <>⚠️ Windows version is currently in Beta — installation requires <strong style={{ color: 'rgba(255,255,255,0.9)' }}>administrator privileges</strong>.</>
618
+ : <>⚠️ Linux version is currently in Beta — please report any issues on <a href="https://github.com/pollen-robotics/reachy-mini-desktop-app/issues" target="_blank" rel="noopener noreferrer" style={{ color: '#3b82f6', textDecoration: 'underline' }}>GitHub</a> or <a href="https://discord.gg/HDrGY9eJHt" target="_blank" rel="noopener noreferrer" style={{ color: '#3b82f6', textDecoration: 'underline' }}>Discord</a>.</>
619
+ }
620
+ </Typography>
621
+ </Box>
622
+ )}
623
+ </>
624
+ ) : (
625
+ <>
626
+ {/* macOS Intel - Coming soon message */}
627
+ <Box
628
+ sx={{
629
+ px: 5,
630
+ py: 2.5,
631
+ borderRadius: 3,
632
+ background: 'linear-gradient(135deg, rgba(255, 149, 0, 0.15) 0%, rgba(139, 92, 246, 0.1) 100%)',
633
+ border: '1px solid rgba(255, 149, 0, 0.3)',
634
+ display: 'inline-block',
635
+ }}
636
+ >
637
+ <Typography
638
+ variant="h6"
639
+ sx={{
640
+ color: '#FF9500',
641
+ fontWeight: 600,
642
+ fontSize: 17,
643
+ }}
644
+ >
645
+ 🚧 {currentPlatform?.name} desktop app coming soon!
646
+ </Typography>
647
+ </Box>
648
 
 
 
 
 
 
 
 
 
 
 
 
 
 
649
  <Typography
650
  variant="body2"
651
  sx={{
652
+ color: 'rgba(255,255,255,0.5)',
653
+ mt: 2,
654
+ fontSize: 14,
655
+ maxWidth: 500,
656
+ mx: 'auto',
657
  }}
658
  >
659
+ We're working hard to bring Reachy Mini Control to {currentPlatform?.name}.
660
+ In the meantime, macOS (Apple Silicon) is fully supported, and Windows & Linux are in beta!
 
 
661
  </Typography>
662
+
663
+ {/* Alternative for Linux/advanced users - Python SDK */}
664
+ <Box
665
+ sx={{
666
+ mt: 3,
667
+ p: 3,
668
+ borderRadius: 2,
669
+ background: 'rgba(16, 185, 129, 0.08)',
670
+ border: '1px solid rgba(16, 185, 129, 0.25)',
671
+ maxWidth: 520,
672
+ mx: 'auto',
673
+ textAlign: 'left',
674
+ }}
675
+ >
676
+ <Typography
677
+ variant="body2"
678
+ sx={{
679
+ color: 'rgba(255,255,255,0.9)',
680
+ fontWeight: 600,
681
+ mb: 1,
682
+ fontSize: 14,
683
+ }}
684
+ >
685
+ 🐍 Looking to use the Python SDK directly?
686
+ </Typography>
687
+ <Typography
688
+ variant="body2"
689
+ sx={{
690
+ color: 'rgba(255,255,255,0.6)',
691
+ fontSize: 13,
692
+ lineHeight: 1.6,
693
+ mb: 1.5,
694
+ }}
695
+ >
696
+ {detectedPlatform?.includes('linux')
697
+ ? "Linux users can interact directly with their Reachy Mini using the Python SDK. Run the daemon locally and use the full API for custom applications."
698
+ : "Advanced users can interact directly with their Reachy Mini using the Python SDK and daemon."}
699
+ </Typography>
700
+ <Button
701
+ variant="outlined"
702
+ size="small"
703
+ href="https://huggingface.co/docs/reachy_mini/"
704
+ target="_blank"
705
+ endIcon={<OpenInNewIcon sx={{ fontSize: 14 }} />}
706
+ sx={{
707
+ color: '#10b981',
708
+ borderColor: 'rgba(16, 185, 129, 0.4)',
709
+ fontWeight: 600,
710
+ fontSize: 12,
711
+ textTransform: 'none',
712
+ '&:hover': {
713
+ borderColor: '#10b981',
714
+ bgcolor: 'rgba(16, 185, 129, 0.1)',
715
+ },
716
+ }}
717
+ >
718
+ View reachy_mini on GitHub
719
+ </Button>
720
+ </Box>
721
+ </>
722
  )}
723
 
724
  {/* App screenshot */}
 
753
  </Typography>
754
 
755
  <Grid container spacing={2}>
756
+ {['darwin-aarch64', 'darwin-x86_64', 'windows-x86_64', 'linux-x86_64'].map((key) => (
757
+ <Grid size={{ xs: 6, sm: 3 }} key={key}>
758
  <PlatformCard
759
  platformKey={key}
760
  url={releaseData?.platforms[key]?.url}
 
765
  ))}
766
  </Grid>
767
 
768
+ {/* Platform support notice - show on Windows, macOS Apple Silicon, and Linux */}
769
+ {(detectedPlatform?.startsWith('windows') || detectedPlatform === 'darwin-aarch64' || detectedPlatform?.includes('linux')) && (
770
+ <Box
771
+ sx={{
772
+ mt: 3,
773
+ p: 2,
774
+ background: 'rgba(255, 255, 255, 0.03)',
775
+ border: '1px solid rgba(255, 255, 255, 0.08)',
776
+ borderRadius: 2,
777
+ textAlign: 'center',
778
+ }}
779
+ >
780
+ <Typography
781
+ variant="body2"
782
+ sx={{ color: 'rgba(255,255,255,0.5)' }}
783
+ >
784
+ 🚧 macOS Intel support coming soon
785
+ </Typography>
786
+ </Box>
787
+ )}
788
  </Box>
789
 
790
  {/* Features / What's included */}
src/pages/GettingStarted.jsx CHANGED
@@ -326,7 +326,7 @@ export default function GettingStarted() {
326
  The desktop app includes everything you need to control your Lite version.
327
  </Typography>
328
  <Typography variant="caption" sx={{ display: 'block', mb: 2, color: 'warning.main' }}>
329
- Desktop App available for macOS (Apple Silicon), Windows & Linux (beta).
330
  </Typography>
331
  <Button
332
  variant="contained"
@@ -438,7 +438,7 @@ export default function GettingStarted() {
438
  </Typography>
439
  </Box>
440
 
441
- {/* Step 2: Connect & Install */}
442
  <Box sx={{ mb: 10 }}>
443
  <Box sx={{ mb: 3 }}>
444
  <Typography
@@ -452,85 +452,95 @@ export default function GettingStarted() {
452
  >
453
  Step 2
454
  </Typography>
455
- <Typography variant="h3" sx={{ mt: 0.5 }}>Connect & install the app</Typography>
456
  </Box>
457
 
458
- <Grid container spacing={4} alignItems="center">
459
- <Grid size={{ xs: 12, md: 6 }}>
460
- <Stepper orientation="vertical">
461
- <Step active completed={false}>
462
- <StepLabel>
463
- <Typography fontWeight={600}>Power on your Reachy Mini</Typography>
464
- </StepLabel>
465
- <StepContent>
466
- <Typography variant="body2" color="text.secondary">
467
- Wait about 30 seconds for the robot to boot up.
468
- </Typography>
469
- </StepContent>
470
- </Step>
471
- <Step active completed={false}>
472
- <StepLabel>
473
- <Typography fontWeight={600}>Download the desktop app</Typography>
474
- </StepLabel>
475
- <StepContent>
476
- <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
477
- The desktop app includes everything you need to control your Wireless version.
478
- </Typography>
479
- <Typography variant="caption" sx={{ display: 'block', mb: 2, color: 'warning.main' }}>
480
- Desktop App available for macOS (Apple Silicon), Windows & Linux (beta).
481
- </Typography>
482
- <Button
483
- variant="contained"
484
- component={RouterLink}
485
- to="/download"
486
- startIcon={<DownloadIcon/>}
487
- >
488
- Download Desktop App
489
- </Button>
490
-
491
- <Button
492
- variant="outlined"
493
- href="https://huggingface.co/docs/reachy_mini/SDK/installation"
494
- target="_blank"
495
- startIcon={<OpenInNewIcon/>}
496
- >
497
- Alternative: Python SDK
498
- </Button>
499
- </StepContent>
500
- </Step>
501
- <Step active completed={false}>
502
- <StepLabel>
503
- <Typography fontWeight={600}>Connect the robot to your Wi-Fi</Typography>
504
- </StepLabel>
505
- <StepContent>
506
- <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
507
- Use the desktop app to connect your robot to your Wi-Fi network.
508
- </Typography>
509
- </StepContent>
510
- </Step>
511
- <Step active completed={false}>
512
- <StepLabel>
513
- <Typography fontWeight={600}>Open the app & play!</Typography>
514
- </StepLabel>
515
- <StepContent>
516
- <Typography variant="body2" color="text.secondary">
517
- The app will automatically detect your robot. Click "Wake Up" to start,
518
- then explore 30+ apps created by the community, from hand tracking to AI conversations! If you experience any issues, feel free to check our <a href="https://huggingface.co/docs/reachy_mini/troubleshooting" target="_blank">Troubleshooting & FAQ page</a> or contact us on <a href="https://discord.gg/HDrGY9eJHt" target="_blank">Discord</a>.
519
- </Typography>
520
- </StepContent>
521
- </Step>
522
- </Stepper>
523
- </Grid>
524
-
525
- <Grid size={{ xs: 12, md: 6 }}>
526
- <Box
527
- component="img"
528
- src="/assets/desktop-app-screenshot--dark.png"
529
- alt="Reachy Mini Control App"
530
- sx={{ width: '100%', display: 'block', borderRadius: '12px' }}
531
- />
532
- </Grid>
533
- </Grid>
 
 
 
 
 
 
 
 
 
 
534
  </Box>
535
  </>
536
  )}
 
326
  The desktop app includes everything you need to control your Lite version.
327
  </Typography>
328
  <Typography variant="caption" sx={{ display: 'block', mb: 2, color: 'warning.main' }}>
329
+ Desktop App available for macOS (Apple Silicon), Windows & Linux (beta). macOS Intel coming soon!
330
  </Typography>
331
  <Button
332
  variant="contained"
 
438
  </Typography>
439
  </Box>
440
 
441
+ {/* Step 2: Connect to Wi-Fi */}
442
  <Box sx={{ mb: 10 }}>
443
  <Box sx={{ mb: 3 }}>
444
  <Typography
 
452
  >
453
  Step 2
454
  </Typography>
455
+ <Typography variant="h3" sx={{ mt: 0.5 }}>Connect to your Wi-Fi</Typography>
456
  </Box>
457
 
458
+ <Stepper orientation="vertical">
459
+ <Step active completed={false}>
460
+ <StepLabel>
461
+ <Typography fontWeight={600}>Power on your Reachy Mini</Typography>
462
+ </StepLabel>
463
+ <StepContent>
464
+ <Typography variant="body2" color="text.secondary">
465
+ Wait about 30 seconds for the robot to boot up.
466
+ </Typography>
467
+ </StepContent>
468
+ </Step>
469
+ <Step active completed={false}>
470
+ <StepLabel>
471
+ <Typography fontWeight={600}>Connect to the robot's Wi-Fi</Typography>
472
+ </StepLabel>
473
+ <StepContent>
474
+ <Grid container columnSpacing={2.5} rowSpacing={2} alignItems="center">
475
+ <Grid
476
+ size={{ xs: 12, md: 6 }}
477
+ sx={{
478
+ flexBasis: { md: '110px', xs: '100%' },
479
+ maxWidth: { md: '110px', xs: '100%' },
480
+ display: 'flex',
481
+ alignItems: 'center',
482
+ }}
483
+ >
484
+ <Box
485
+ component="img"
486
+ src="/assets/reachy-mini-access-point-QR-code.png"
487
+ alt="Reachy Mini access point QR code"
488
+ sx={{ height: 100, width: 'auto', display: 'block' }}
489
+ />
490
+ </Grid>
491
+ <Grid size={{ xs: 12, md: 6 }} sx={{ display: 'flex', alignItems: 'center' }}>
492
+ <Box sx={{ bgcolor: 'background.alt', p: 2, borderRadius: 2, mb: 1 }}>
493
+ <Typography variant="body2">
494
+ <strong>Network:</strong> <code>reachy-mini-ap</code>
495
+ </Typography>
496
+ <Typography variant="body2">
497
+ <strong>Password:</strong> <code>reachy-mini</code>
498
+ </Typography>
499
+ </Box>
500
+ </Grid>
501
+ </Grid>
502
+ </StepContent>
503
+ </Step>
504
+ <Step active completed={false}>
505
+ <StepLabel>
506
+ <Typography fontWeight={600}>Open settings in your browser</Typography>
507
+ </StepLabel>
508
+ <StepContent>
509
+ <Button
510
+ variant="outlined"
511
+ size="small"
512
+ href="http://reachy-mini.local:8000/settings"
513
+ target="_blank"
514
+ endIcon={<OpenInNewIcon />}
515
+ >
516
+ http://reachy-mini.local:8000/settings
517
+ </Button>
518
+ </StepContent>
519
+ </Step>
520
+ <Step active completed={false}>
521
+ <StepLabel>
522
+ <Typography fontWeight={600}>Enter your Wi-Fi & update</Typography>
523
+ </StepLabel>
524
+ <StepContent>
525
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 1 }}>
526
+ The robot will restart and connect to your network. Then access the dashboard
527
+ to explore 30+ apps, from hand tracking to AI conversations!
528
+ </Typography>
529
+ <Typography variant="body2" color="text.secondary" sx={{ mb: 2, fontStyle: 'italic' }}>
530
+ If the dashboard doesn't appear correctly, please use Chrome or Firefox.
531
+ </Typography>
532
+ <Button
533
+ variant="contained"
534
+ size="small"
535
+ href="http://reachy-mini.local:8000"
536
+ target="_blank"
537
+ endIcon={<OpenInNewIcon />}
538
+ >
539
+ Open Dashboard
540
+ </Button>
541
+ </StepContent>
542
+ </Step>
543
+ </Stepper>
544
  </Box>
545
  </>
546
  )}
src/pages/Home.jsx CHANGED
@@ -765,20 +765,6 @@ function ProductsSection() {
765
  >
766
  Current Lead time: 90 days after purchase
767
  </Typography>
768
- <Typography
769
- variant="body2"
770
- sx={{
771
- color: "text.secondary",
772
- mt: 1,
773
- maxWidth: 520,
774
- mx: "auto",
775
- lineHeight: 1.7,
776
- }}
777
- >
778
- <strong>Import duties:</strong> EU/UK + US/Canada ship duty-paid (DDP).
779
- <br />
780
- Other destinations may incur local import duties/taxes on delivery (DAP).
781
- </Typography>
782
  </Box>
783
 
784
  <Typography
 
765
  >
766
  Current Lead time: 90 days after purchase
767
  </Typography>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
768
  </Box>
769
 
770
  <Typography
src/workers/searchWorker.js DELETED
@@ -1,71 +0,0 @@
1
- import Fuse from 'fuse.js';
2
-
3
- let fuse = null;
4
-
5
- const FUSE_OPTIONS = {
6
- keys: [
7
- { name: 'name', weight: 3 },
8
- { name: 'id', weight: 2 },
9
- { name: '_searchAuthor', weight: 2 },
10
- { name: '_searchDescription', weight: 1.5 },
11
- { name: '_searchTags', weight: 1 },
12
- ],
13
- threshold: 0.35,
14
- ignoreLocation: true,
15
- includeScore: true,
16
- includeMatches: true,
17
- };
18
-
19
- /**
20
- * Build a Fuse.js index from app data.
21
- * Flattens searchable fields for better matching.
22
- */
23
- function buildIndex(apps) {
24
- const enriched = apps.map((app) => ({
25
- id: app.id,
26
- name: app.name,
27
- _searchAuthor: app.extra?.author || app.id?.split('/')?.[0] || '',
28
- _searchDescription:
29
- app.extra?.cardData?.short_description || app.description || '',
30
- _searchTags: [...(app.extra?.tags || []), ...(app.extra?.cardData?.tags || [])]
31
- .filter(Boolean)
32
- .join(' '),
33
- }));
34
-
35
- fuse = new Fuse(enriched, FUSE_OPTIONS);
36
- }
37
-
38
- /**
39
- * Search and return ordered list of matching app IDs with scores.
40
- */
41
- /**
42
- * Search and return ordered list of matching app IDs with scores and match indices.
43
- * Matches are keyed by field name for easy highlight rendering.
44
- */
45
- function search(query) {
46
- if (!fuse || !query.trim()) return [];
47
- const results = fuse.search(query.trim());
48
- return results.map((r) => {
49
- // Convert Fuse matches array into a map: fieldName → [[start, end], ...]
50
- const matches = {};
51
- if (r.matches) {
52
- for (const m of r.matches) {
53
- matches[m.key] = m.indices;
54
- }
55
- }
56
- return { id: r.item.id, score: r.score, matches };
57
- });
58
- }
59
-
60
- // Handle messages from the main thread
61
- self.onmessage = (e) => {
62
- const { type, apps, query } = e.data;
63
-
64
- if (type === 'INDEX') {
65
- buildIndex(apps);
66
- self.postMessage({ type: 'INDEXED' });
67
- } else if (type === 'SEARCH') {
68
- const results = search(query);
69
- self.postMessage({ type: 'RESULTS', results, query });
70
- }
71
- };
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
yarn.lock CHANGED
@@ -470,20 +470,6 @@
470
  "@eslint/core" "^0.17.0"
471
  levn "^0.4.1"
472
 
473
- "@huggingface/hub@^2.8.1":
474
- version "2.8.1"
475
- resolved "https://registry.npmjs.org/@huggingface/hub/-/hub-2.8.1.tgz"
476
- integrity sha512-VAsXdMiIHPteXQJhrwaBEiePTWiJ0zBSymHdnX4J+AijjNN0h3RzGfkKemXcu75gu/TmRLFY9l8+2Tkdmpis0w==
477
- dependencies:
478
- "@huggingface/tasks" "^0.19.82"
479
- optionalDependencies:
480
- cli-progress "^3.12.0"
481
-
482
- "@huggingface/tasks@^0.19.82":
483
- version "0.19.83"
484
- resolved "https://registry.npmjs.org/@huggingface/tasks/-/tasks-0.19.83.tgz"
485
- integrity sha512-nBt3S6x+MWUTmfey1drQZRMuEopEbz2aEMUsoddfpCuzIYAMCsJDX7xeNuJnzvbVGis3gXXCRcLHVhFtHaaiyA==
486
-
487
  "@humanfs/core@^0.19.1":
488
  version "0.19.1"
489
  resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz"
@@ -922,7 +908,7 @@
922
 
923
  accepts@~1.3.8:
924
  version "1.3.8"
925
- resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
926
  integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
927
  dependencies:
928
  mime-types "~2.1.34"
@@ -948,11 +934,6 @@ ajv@^6.12.4:
948
  json-schema-traverse "^0.4.1"
949
  uri-js "^4.2.2"
950
 
951
- ansi-regex@^5.0.1:
952
- version "5.0.1"
953
- resolved "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz"
954
- integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
955
-
956
  ansi-styles@^4.1.0:
957
  version "4.3.0"
958
  resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
@@ -967,7 +948,7 @@ argparse@^2.0.1:
967
 
968
  array-flatten@1.1.1:
969
  version "1.1.1"
970
- resolved "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz"
971
  integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
972
 
973
  babel-plugin-macros@^3.1.0:
@@ -996,7 +977,7 @@ baseline-browser-mapping@^2.9.0:
996
 
997
  body-parser@~1.20.3:
998
  version "1.20.4"
999
- resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.4.tgz"
1000
  integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==
1001
  dependencies:
1002
  bytes "~3.1.2"
@@ -1033,12 +1014,12 @@ browserslist@^4.24.0:
1033
 
1034
  bytes@~3.1.2:
1035
  version "3.1.2"
1036
- resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
1037
  integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
1038
 
1039
  call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
1040
  version "1.0.2"
1041
- resolved "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz"
1042
  integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
1043
  dependencies:
1044
  es-errors "^1.3.0"
@@ -1046,7 +1027,7 @@ call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
1046
 
1047
  call-bound@^1.0.2:
1048
  version "1.0.4"
1049
- resolved "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz"
1050
  integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
1051
  dependencies:
1052
  call-bind-apply-helpers "^1.0.2"
@@ -1095,13 +1076,6 @@ character-reference-invalid@^2.0.0:
1095
  resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz"
1096
  integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
1097
 
1098
- cli-progress@^3.12.0:
1099
- version "3.12.0"
1100
- resolved "https://registry.npmjs.org/cli-progress/-/cli-progress-3.12.0.tgz"
1101
- integrity sha512-tRkV3HJ1ASwm19THiiLIXLO7Im7wlTuKnvkYaTkyoAPefqjNg7W7DHKUlGRxy9vxDvbyCYQkQozvptuMkGCg8A==
1102
- dependencies:
1103
- string-width "^4.2.3"
1104
-
1105
  clsx@^2.1.1:
1106
  version "2.1.1"
1107
  resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
@@ -1131,14 +1105,14 @@ concat-map@0.0.1:
1131
 
1132
  content-disposition@~0.5.4:
1133
  version "0.5.4"
1134
- resolved "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz"
1135
  integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
1136
  dependencies:
1137
  safe-buffer "5.2.1"
1138
 
1139
  content-type@~1.0.4, content-type@~1.0.5:
1140
  version "1.0.5"
1141
- resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz"
1142
  integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
1143
 
1144
  convert-source-map@^1.5.0:
@@ -1153,7 +1127,7 @@ convert-source-map@^2.0.0:
1153
 
1154
  cookie-signature@~1.0.6:
1155
  version "1.0.7"
1156
- resolved "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz"
1157
  integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==
1158
 
1159
  cookie@^1.0.1:
@@ -1163,7 +1137,7 @@ cookie@^1.0.1:
1163
 
1164
  cookie@~0.7.1:
1165
  version "0.7.2"
1166
- resolved "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz"
1167
  integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
1168
 
1169
  cosmiconfig@^7.0.0:
@@ -1193,7 +1167,7 @@ csstype@^3.0.2, csstype@^3.1.3, csstype@^3.2.2:
1193
 
1194
  debug@2.6.9:
1195
  version "2.6.9"
1196
- resolved "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
1197
  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
1198
  dependencies:
1199
  ms "2.0.0"
@@ -1219,7 +1193,7 @@ deep-is@^0.1.3:
1219
 
1220
  depd@2.0.0, depd@~2.0.0:
1221
  version "2.0.0"
1222
- resolved "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz"
1223
  integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
1224
 
1225
  dequal@^2.0.0:
@@ -1229,7 +1203,7 @@ dequal@^2.0.0:
1229
 
1230
  destroy@1.2.0, destroy@~1.2.0:
1231
  version "1.2.0"
1232
- resolved "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz"
1233
  integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
1234
 
1235
  devlop@^1.0.0, devlop@^1.1.0:
@@ -1249,7 +1223,7 @@ dom-helpers@^5.0.1:
1249
 
1250
  dunder-proto@^1.0.1:
1251
  version "1.0.1"
1252
- resolved "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz"
1253
  integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
1254
  dependencies:
1255
  call-bind-apply-helpers "^1.0.1"
@@ -1258,7 +1232,7 @@ dunder-proto@^1.0.1:
1258
 
1259
  ee-first@1.1.1:
1260
  version "1.1.1"
1261
- resolved "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz"
1262
  integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
1263
 
1264
  electron-to-chromium@^1.5.263:
@@ -1266,14 +1240,9 @@ electron-to-chromium@^1.5.263:
1266
  resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz"
1267
  integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==
1268
 
1269
- emoji-regex@^8.0.0:
1270
- version "8.0.0"
1271
- resolved "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz"
1272
- integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
1273
-
1274
  encodeurl@~2.0.0:
1275
  version "2.0.0"
1276
- resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz"
1277
  integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
1278
 
1279
  error-ex@^1.3.1:
@@ -1285,17 +1254,17 @@ error-ex@^1.3.1:
1285
 
1286
  es-define-property@^1.0.1:
1287
  version "1.0.1"
1288
- resolved "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz"
1289
  integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
1290
 
1291
  es-errors@^1.3.0:
1292
  version "1.3.0"
1293
- resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz"
1294
  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
1295
 
1296
  es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
1297
  version "1.1.1"
1298
- resolved "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz"
1299
  integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
1300
  dependencies:
1301
  es-errors "^1.3.0"
@@ -1339,7 +1308,7 @@ escalade@^3.2.0:
1339
 
1340
  escape-html@~1.0.3:
1341
  version "1.0.3"
1342
- resolved "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz"
1343
  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
1344
 
1345
  escape-string-regexp@^4.0.0:
@@ -1466,12 +1435,12 @@ esutils@^2.0.2:
1466
 
1467
  etag@~1.8.1:
1468
  version "1.8.1"
1469
- resolved "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz"
1470
  integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
1471
 
1472
  express@^4.21.2:
1473
  version "4.22.1"
1474
- resolved "https://registry.npmjs.org/express/-/express-4.22.1.tgz"
1475
  integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==
1476
  dependencies:
1477
  accepts "~1.3.8"
@@ -1540,7 +1509,7 @@ file-entry-cache@^8.0.0:
1540
 
1541
  finalhandler@~1.3.1:
1542
  version "1.3.2"
1543
- resolved "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz"
1544
  integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==
1545
  dependencies:
1546
  debug "2.6.9"
@@ -1579,7 +1548,7 @@ flatted@^3.2.9:
1579
 
1580
  forwarded@0.2.0:
1581
  version "0.2.0"
1582
- resolved "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz"
1583
  integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
1584
 
1585
  framer-motion@^12.23.26:
@@ -1593,7 +1562,7 @@ framer-motion@^12.23.26:
1593
 
1594
  fresh@~0.5.2:
1595
  version "0.5.2"
1596
- resolved "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz"
1597
  integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
1598
 
1599
  fsevents@~2.3.2, fsevents@~2.3.3:
@@ -1606,11 +1575,6 @@ function-bind@^1.1.2:
1606
  resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
1607
  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
1608
 
1609
- fuse.js@^7.1.0:
1610
- version "7.1.0"
1611
- resolved "https://registry.yarnpkg.com/fuse.js/-/fuse.js-7.1.0.tgz#306228b4befeee11e05b027087c2744158527d09"
1612
- integrity sha512-trLf4SzuuUxfusZADLINj+dE8clK1frKdmqiJNb1Es75fmI5oY6X2mxLVUciLLjxqw/xr72Dhy+lER6dGd02FQ==
1613
-
1614
  gensync@^1.0.0-beta.2:
1615
  version "1.0.0-beta.2"
1616
  resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz"
@@ -1618,7 +1582,7 @@ gensync@^1.0.0-beta.2:
1618
 
1619
  get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
1620
  version "1.3.0"
1621
- resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz"
1622
  integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
1623
  dependencies:
1624
  call-bind-apply-helpers "^1.0.2"
@@ -1634,7 +1598,7 @@ get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
1634
 
1635
  get-proto@^1.0.1:
1636
  version "1.0.1"
1637
- resolved "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz"
1638
  integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
1639
  dependencies:
1640
  dunder-proto "^1.0.1"
@@ -1659,7 +1623,7 @@ globals@^16.5.0:
1659
 
1660
  gopd@^1.2.0:
1661
  version "1.2.0"
1662
- resolved "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz"
1663
  integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
1664
 
1665
  has-flag@^4.0.0:
@@ -1669,7 +1633,7 @@ has-flag@^4.0.0:
1669
 
1670
  has-symbols@^1.1.0:
1671
  version "1.1.0"
1672
- resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz"
1673
  integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
1674
 
1675
  hasown@^2.0.2:
@@ -1755,7 +1719,7 @@ html-url-attributes@^3.0.0:
1755
 
1756
  http-errors@~2.0.0, http-errors@~2.0.1:
1757
  version "2.0.1"
1758
- resolved "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz"
1759
  integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==
1760
  dependencies:
1761
  depd "~2.0.0"
@@ -1766,7 +1730,7 @@ http-errors@~2.0.0, http-errors@~2.0.1:
1766
 
1767
  iconv-lite@~0.4.24:
1768
  version "0.4.24"
1769
- resolved "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz"
1770
  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
1771
  dependencies:
1772
  safer-buffer ">= 2.1.2 < 3"
@@ -1791,7 +1755,7 @@ imurmurhash@^0.1.4:
1791
 
1792
  inherits@~2.0.4:
1793
  version "2.0.4"
1794
- resolved "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
1795
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
1796
 
1797
  inline-style-parser@0.2.7:
@@ -1801,7 +1765,7 @@ inline-style-parser@0.2.7:
1801
 
1802
  ipaddr.js@1.9.1:
1803
  version "1.9.1"
1804
- resolved "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz"
1805
  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
1806
 
1807
  is-alphabetical@^2.0.0:
@@ -1839,11 +1803,6 @@ is-extglob@^2.1.1:
1839
  resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
1840
  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
1841
 
1842
- is-fullwidth-code-point@^3.0.0:
1843
- version "3.0.0"
1844
- resolved "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz"
1845
- integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
1846
-
1847
  is-glob@^4.0.0, is-glob@^4.0.3:
1848
  version "4.0.3"
1849
  resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
@@ -1975,7 +1934,7 @@ markdown-table@^3.0.0:
1975
 
1976
  math-intrinsics@^1.1.0:
1977
  version "1.1.0"
1978
- resolved "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz"
1979
  integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
1980
 
1981
  mdast-util-find-and-replace@^3.0.0:
@@ -2160,17 +2119,17 @@ mdast-util-to-string@^4.0.0:
2160
 
2161
  media-typer@0.3.0:
2162
  version "0.3.0"
2163
- resolved "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz"
2164
  integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
2165
 
2166
  merge-descriptors@1.0.3:
2167
  version "1.0.3"
2168
- resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz"
2169
  integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
2170
 
2171
  methods@~1.1.2:
2172
  version "1.1.2"
2173
- resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
2174
  integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
2175
 
2176
  micromark-core-commonmark@^2.0.0:
@@ -2448,19 +2407,19 @@ micromark@^4.0.0:
2448
 
2449
  mime-db@1.52.0:
2450
  version "1.52.0"
2451
- resolved "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz"
2452
  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
2453
 
2454
  mime-types@~2.1.24, mime-types@~2.1.34:
2455
  version "2.1.35"
2456
- resolved "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz"
2457
  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
2458
  dependencies:
2459
  mime-db "1.52.0"
2460
 
2461
  mime@1.6.0:
2462
  version "1.6.0"
2463
- resolved "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz"
2464
  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
2465
 
2466
  minimatch@^3.1.2:
@@ -2484,12 +2443,12 @@ motion-utils@^12.23.6:
2484
 
2485
  ms@2.0.0:
2486
  version "2.0.0"
2487
- resolved "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
2488
  integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
2489
 
2490
  ms@2.1.3, ms@^2.1.3:
2491
  version "2.1.3"
2492
- resolved "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz"
2493
  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
2494
 
2495
  nanoid@^3.3.11:
@@ -2504,7 +2463,7 @@ natural-compare@^1.4.0:
2504
 
2505
  negotiator@0.6.3:
2506
  version "0.6.3"
2507
- resolved "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz"
2508
  integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
2509
 
2510
  node-releases@^2.0.27:
@@ -2519,12 +2478,12 @@ object-assign@^4.1.1:
2519
 
2520
  object-inspect@^1.13.3:
2521
  version "1.13.4"
2522
- resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz"
2523
  integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
2524
 
2525
  on-finished@~2.4.1:
2526
  version "2.4.1"
2527
- resolved "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz"
2528
  integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
2529
  dependencies:
2530
  ee-first "1.1.1"
@@ -2587,7 +2546,7 @@ parse-json@^5.0.0:
2587
 
2588
  parseurl@~1.3.3:
2589
  version "1.3.3"
2590
- resolved "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz"
2591
  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
2592
 
2593
  path-exists@^4.0.0:
@@ -2607,7 +2566,7 @@ path-parse@^1.0.7:
2607
 
2608
  path-to-regexp@~0.1.12:
2609
  version "0.1.12"
2610
- resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz"
2611
  integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
2612
 
2613
  path-type@^4.0.0:
@@ -2655,7 +2614,7 @@ property-information@^7.0.0:
2655
 
2656
  proxy-addr@~2.0.7:
2657
  version "2.0.7"
2658
- resolved "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz"
2659
  integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
2660
  dependencies:
2661
  forwarded "0.2.0"
@@ -2668,19 +2627,19 @@ punycode@^2.1.0:
2668
 
2669
  qs@~6.14.0:
2670
  version "6.14.1"
2671
- resolved "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz"
2672
  integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==
2673
  dependencies:
2674
  side-channel "^1.1.0"
2675
 
2676
  range-parser@~1.2.1:
2677
  version "1.2.1"
2678
- resolved "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz"
2679
  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
2680
 
2681
  raw-body@~2.5.3:
2682
  version "2.5.3"
2683
- resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz"
2684
  integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==
2685
  dependencies:
2686
  bytes "~3.1.2"
@@ -2857,12 +2816,12 @@ rollup@^4.43.0:
2857
 
2858
  safe-buffer@5.2.1:
2859
  version "5.2.1"
2860
- resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz"
2861
  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
2862
 
2863
  "safer-buffer@>= 2.1.2 < 3":
2864
  version "2.1.2"
2865
- resolved "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz"
2866
  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
2867
 
2868
  scheduler@^0.27.0:
@@ -2877,7 +2836,7 @@ semver@^6.3.1:
2877
 
2878
  send@~0.19.0, send@~0.19.1:
2879
  version "0.19.2"
2880
- resolved "https://registry.npmjs.org/send/-/send-0.19.2.tgz"
2881
  integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==
2882
  dependencies:
2883
  debug "2.6.9"
@@ -2896,7 +2855,7 @@ send@~0.19.0, send@~0.19.1:
2896
 
2897
  serve-static@~1.16.2:
2898
  version "1.16.3"
2899
- resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz"
2900
  integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==
2901
  dependencies:
2902
  encodeurl "~2.0.0"
@@ -2911,7 +2870,7 @@ set-cookie-parser@^2.6.0:
2911
 
2912
  setprototypeof@1.2.0, setprototypeof@~1.2.0:
2913
  version "1.2.0"
2914
- resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz"
2915
  integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
2916
 
2917
  shebang-command@^2.0.0:
@@ -2928,7 +2887,7 @@ shebang-regex@^3.0.0:
2928
 
2929
  side-channel-list@^1.0.0:
2930
  version "1.0.0"
2931
- resolved "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz"
2932
  integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
2933
  dependencies:
2934
  es-errors "^1.3.0"
@@ -2936,7 +2895,7 @@ side-channel-list@^1.0.0:
2936
 
2937
  side-channel-map@^1.0.1:
2938
  version "1.0.1"
2939
- resolved "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz"
2940
  integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
2941
  dependencies:
2942
  call-bound "^1.0.2"
@@ -2946,7 +2905,7 @@ side-channel-map@^1.0.1:
2946
 
2947
  side-channel-weakmap@^1.0.2:
2948
  version "1.0.2"
2949
- resolved "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz"
2950
  integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
2951
  dependencies:
2952
  call-bound "^1.0.2"
@@ -2957,7 +2916,7 @@ side-channel-weakmap@^1.0.2:
2957
 
2958
  side-channel@^1.1.0:
2959
  version "1.1.0"
2960
- resolved "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz"
2961
  integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
2962
  dependencies:
2963
  es-errors "^1.3.0"
@@ -2983,18 +2942,9 @@ space-separated-tokens@^2.0.0:
2983
 
2984
  statuses@~2.0.1, statuses@~2.0.2:
2985
  version "2.0.2"
2986
- resolved "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz"
2987
  integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==
2988
 
2989
- string-width@^4.2.3:
2990
- version "4.2.3"
2991
- resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
2992
- integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
2993
- dependencies:
2994
- emoji-regex "^8.0.0"
2995
- is-fullwidth-code-point "^3.0.0"
2996
- strip-ansi "^6.0.1"
2997
-
2998
  stringify-entities@^4.0.0:
2999
  version "4.0.4"
3000
  resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
@@ -3003,13 +2953,6 @@ stringify-entities@^4.0.0:
3003
  character-entities-html4 "^2.0.0"
3004
  character-entities-legacy "^3.0.0"
3005
 
3006
- strip-ansi@^6.0.1:
3007
- version "6.0.1"
3008
- resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
3009
- integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
3010
- dependencies:
3011
- ansi-regex "^5.0.1"
3012
-
3013
  strip-json-comments@^3.1.1:
3014
  version "3.1.1"
3015
  resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
@@ -3056,7 +2999,7 @@ tinyglobby@^0.2.15:
3056
 
3057
  toidentifier@~1.0.1:
3058
  version "1.0.1"
3059
- resolved "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz"
3060
  integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
3061
 
3062
  trim-lines@^3.0.0:
@@ -3083,7 +3026,7 @@ type-check@^0.4.0, type-check@~0.4.0:
3083
 
3084
  type-is@~1.6.18:
3085
  version "1.6.18"
3086
- resolved "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz"
3087
  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
3088
  dependencies:
3089
  media-typer "0.3.0"
@@ -3150,7 +3093,7 @@ unist-util-visit@^5.0.0:
3150
 
3151
  unpipe@~1.0.0:
3152
  version "1.0.0"
3153
- resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
3154
  integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
3155
 
3156
  update-browserslist-db@^1.2.0:
@@ -3170,12 +3113,12 @@ uri-js@^4.2.2:
3170
 
3171
  utils-merge@1.0.1:
3172
  version "1.0.1"
3173
- resolved "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz"
3174
  integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
3175
 
3176
  vary@~1.1.2:
3177
  version "1.1.2"
3178
- resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
3179
  integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
3180
 
3181
  vfile-message@^4.0.0:
 
470
  "@eslint/core" "^0.17.0"
471
  levn "^0.4.1"
472
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
473
  "@humanfs/core@^0.19.1":
474
  version "0.19.1"
475
  resolved "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz"
 
908
 
909
  accepts@~1.3.8:
910
  version "1.3.8"
911
+ resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e"
912
  integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
913
  dependencies:
914
  mime-types "~2.1.34"
 
934
  json-schema-traverse "^0.4.1"
935
  uri-js "^4.2.2"
936
 
 
 
 
 
 
937
  ansi-styles@^4.1.0:
938
  version "4.3.0"
939
  resolved "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz"
 
948
 
949
  array-flatten@1.1.1:
950
  version "1.1.1"
951
+ resolved "https://registry.yarnpkg.com/array-flatten/-/array-flatten-1.1.1.tgz#9a5f699051b1e7073328f2a008968b64ea2955d2"
952
  integrity sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==
953
 
954
  babel-plugin-macros@^3.1.0:
 
977
 
978
  body-parser@~1.20.3:
979
  version "1.20.4"
980
+ resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.4.tgz#f8e20f4d06ca8a50a71ed329c15dccad1cdc547f"
981
  integrity sha512-ZTgYYLMOXY9qKU/57FAo8F+HA2dGX7bqGc71txDRC1rS4frdFI5R7NhluHxH6M0YItAP0sHB4uqAOcYKxO6uGA==
982
  dependencies:
983
  bytes "~3.1.2"
 
1014
 
1015
  bytes@~3.1.2:
1016
  version "3.1.2"
1017
+ resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
1018
  integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==
1019
 
1020
  call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2:
1021
  version "1.0.2"
1022
+ resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6"
1023
  integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==
1024
  dependencies:
1025
  es-errors "^1.3.0"
 
1027
 
1028
  call-bound@^1.0.2:
1029
  version "1.0.4"
1030
+ resolved "https://registry.yarnpkg.com/call-bound/-/call-bound-1.0.4.tgz#238de935d2a2a692928c538c7ccfa91067fd062a"
1031
  integrity sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==
1032
  dependencies:
1033
  call-bind-apply-helpers "^1.0.2"
 
1076
  resolved "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz"
1077
  integrity sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==
1078
 
 
 
 
 
 
 
 
1079
  clsx@^2.1.1:
1080
  version "2.1.1"
1081
  resolved "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz"
 
1105
 
1106
  content-disposition@~0.5.4:
1107
  version "0.5.4"
1108
+ resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe"
1109
  integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==
1110
  dependencies:
1111
  safe-buffer "5.2.1"
1112
 
1113
  content-type@~1.0.4, content-type@~1.0.5:
1114
  version "1.0.5"
1115
+ resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
1116
  integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
1117
 
1118
  convert-source-map@^1.5.0:
 
1127
 
1128
  cookie-signature@~1.0.6:
1129
  version "1.0.7"
1130
+ resolved "https://registry.yarnpkg.com/cookie-signature/-/cookie-signature-1.0.7.tgz#ab5dd7ab757c54e60f37ef6550f481c426d10454"
1131
  integrity sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==
1132
 
1133
  cookie@^1.0.1:
 
1137
 
1138
  cookie@~0.7.1:
1139
  version "0.7.2"
1140
+ resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.7.2.tgz#556369c472a2ba910f2979891b526b3436237ed7"
1141
  integrity sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==
1142
 
1143
  cosmiconfig@^7.0.0:
 
1167
 
1168
  debug@2.6.9:
1169
  version "2.6.9"
1170
+ resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f"
1171
  integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==
1172
  dependencies:
1173
  ms "2.0.0"
 
1193
 
1194
  depd@2.0.0, depd@~2.0.0:
1195
  version "2.0.0"
1196
+ resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df"
1197
  integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==
1198
 
1199
  dequal@^2.0.0:
 
1203
 
1204
  destroy@1.2.0, destroy@~1.2.0:
1205
  version "1.2.0"
1206
+ resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015"
1207
  integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==
1208
 
1209
  devlop@^1.0.0, devlop@^1.1.0:
 
1223
 
1224
  dunder-proto@^1.0.1:
1225
  version "1.0.1"
1226
+ resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a"
1227
  integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==
1228
  dependencies:
1229
  call-bind-apply-helpers "^1.0.1"
 
1232
 
1233
  ee-first@1.1.1:
1234
  version "1.1.1"
1235
+ resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
1236
  integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==
1237
 
1238
  electron-to-chromium@^1.5.263:
 
1240
  resolved "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.267.tgz"
1241
  integrity sha512-0Drusm6MVRXSOJpGbaSVgcQsuB4hEkMpHXaVstcPmhu5LIedxs1xNK/nIxmQIU/RPC0+1/o0AVZfBTkTNJOdUw==
1242
 
 
 
 
 
 
1243
  encodeurl@~2.0.0:
1244
  version "2.0.0"
1245
+ resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
1246
  integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
1247
 
1248
  error-ex@^1.3.1:
 
1254
 
1255
  es-define-property@^1.0.1:
1256
  version "1.0.1"
1257
+ resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa"
1258
  integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==
1259
 
1260
  es-errors@^1.3.0:
1261
  version "1.3.0"
1262
+ resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
1263
  integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
1264
 
1265
  es-object-atoms@^1.0.0, es-object-atoms@^1.1.1:
1266
  version "1.1.1"
1267
+ resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1"
1268
  integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==
1269
  dependencies:
1270
  es-errors "^1.3.0"
 
1308
 
1309
  escape-html@~1.0.3:
1310
  version "1.0.3"
1311
+ resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
1312
  integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==
1313
 
1314
  escape-string-regexp@^4.0.0:
 
1435
 
1436
  etag@~1.8.1:
1437
  version "1.8.1"
1438
+ resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887"
1439
  integrity sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==
1440
 
1441
  express@^4.21.2:
1442
  version "4.22.1"
1443
+ resolved "https://registry.yarnpkg.com/express/-/express-4.22.1.tgz#1de23a09745a4fffdb39247b344bb5eaff382069"
1444
  integrity sha512-F2X8g9P1X7uCPZMA3MVf9wcTqlyNp7IhH5qPCI0izhaOIYXaW9L535tGA3qmjRzpH+bZczqq7hVKxTR4NWnu+g==
1445
  dependencies:
1446
  accepts "~1.3.8"
 
1509
 
1510
  finalhandler@~1.3.1:
1511
  version "1.3.2"
1512
+ resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.3.2.tgz#1ebc2228fc7673aac4a472c310cc05b77d852b88"
1513
  integrity sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==
1514
  dependencies:
1515
  debug "2.6.9"
 
1548
 
1549
  forwarded@0.2.0:
1550
  version "0.2.0"
1551
+ resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811"
1552
  integrity sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==
1553
 
1554
  framer-motion@^12.23.26:
 
1562
 
1563
  fresh@~0.5.2:
1564
  version "0.5.2"
1565
+ resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
1566
  integrity sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==
1567
 
1568
  fsevents@~2.3.2, fsevents@~2.3.3:
 
1575
  resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz"
1576
  integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
1577
 
 
 
 
 
 
1578
  gensync@^1.0.0-beta.2:
1579
  version "1.0.0-beta.2"
1580
  resolved "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz"
 
1582
 
1583
  get-intrinsic@^1.2.5, get-intrinsic@^1.3.0:
1584
  version "1.3.0"
1585
+ resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01"
1586
  integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==
1587
  dependencies:
1588
  call-bind-apply-helpers "^1.0.2"
 
1598
 
1599
  get-proto@^1.0.1:
1600
  version "1.0.1"
1601
+ resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1"
1602
  integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==
1603
  dependencies:
1604
  dunder-proto "^1.0.1"
 
1623
 
1624
  gopd@^1.2.0:
1625
  version "1.2.0"
1626
+ resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1"
1627
  integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==
1628
 
1629
  has-flag@^4.0.0:
 
1633
 
1634
  has-symbols@^1.1.0:
1635
  version "1.1.0"
1636
+ resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338"
1637
  integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==
1638
 
1639
  hasown@^2.0.2:
 
1719
 
1720
  http-errors@~2.0.0, http-errors@~2.0.1:
1721
  version "2.0.1"
1722
+ resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.1.tgz#36d2f65bc909c8790018dd36fb4d93da6caae06b"
1723
  integrity sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==
1724
  dependencies:
1725
  depd "~2.0.0"
 
1730
 
1731
  iconv-lite@~0.4.24:
1732
  version "0.4.24"
1733
+ resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
1734
  integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==
1735
  dependencies:
1736
  safer-buffer ">= 2.1.2 < 3"
 
1755
 
1756
  inherits@~2.0.4:
1757
  version "2.0.4"
1758
+ resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
1759
  integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
1760
 
1761
  inline-style-parser@0.2.7:
 
1765
 
1766
  ipaddr.js@1.9.1:
1767
  version "1.9.1"
1768
+ resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
1769
  integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
1770
 
1771
  is-alphabetical@^2.0.0:
 
1803
  resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
1804
  integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==
1805
 
 
 
 
 
 
1806
  is-glob@^4.0.0, is-glob@^4.0.3:
1807
  version "4.0.3"
1808
  resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz"
 
1934
 
1935
  math-intrinsics@^1.1.0:
1936
  version "1.1.0"
1937
+ resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9"
1938
  integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==
1939
 
1940
  mdast-util-find-and-replace@^3.0.0:
 
2119
 
2120
  media-typer@0.3.0:
2121
  version "0.3.0"
2122
+ resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748"
2123
  integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==
2124
 
2125
  merge-descriptors@1.0.3:
2126
  version "1.0.3"
2127
+ resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
2128
  integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
2129
 
2130
  methods@~1.1.2:
2131
  version "1.1.2"
2132
+ resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee"
2133
  integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
2134
 
2135
  micromark-core-commonmark@^2.0.0:
 
2407
 
2408
  mime-db@1.52.0:
2409
  version "1.52.0"
2410
+ resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
2411
  integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==
2412
 
2413
  mime-types@~2.1.24, mime-types@~2.1.34:
2414
  version "2.1.35"
2415
+ resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a"
2416
  integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==
2417
  dependencies:
2418
  mime-db "1.52.0"
2419
 
2420
  mime@1.6.0:
2421
  version "1.6.0"
2422
+ resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
2423
  integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==
2424
 
2425
  minimatch@^3.1.2:
 
2443
 
2444
  ms@2.0.0:
2445
  version "2.0.0"
2446
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8"
2447
  integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==
2448
 
2449
  ms@2.1.3, ms@^2.1.3:
2450
  version "2.1.3"
2451
+ resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
2452
  integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
2453
 
2454
  nanoid@^3.3.11:
 
2463
 
2464
  negotiator@0.6.3:
2465
  version "0.6.3"
2466
+ resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd"
2467
  integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==
2468
 
2469
  node-releases@^2.0.27:
 
2478
 
2479
  object-inspect@^1.13.3:
2480
  version "1.13.4"
2481
+ resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.4.tgz#8375265e21bc20d0fa582c22e1b13485d6e00213"
2482
  integrity sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==
2483
 
2484
  on-finished@~2.4.1:
2485
  version "2.4.1"
2486
+ resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f"
2487
  integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==
2488
  dependencies:
2489
  ee-first "1.1.1"
 
2546
 
2547
  parseurl@~1.3.3:
2548
  version "1.3.3"
2549
+ resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4"
2550
  integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==
2551
 
2552
  path-exists@^4.0.0:
 
2566
 
2567
  path-to-regexp@~0.1.12:
2568
  version "0.1.12"
2569
+ resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.12.tgz#d5e1a12e478a976d432ef3c58d534b9923164bb7"
2570
  integrity sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==
2571
 
2572
  path-type@^4.0.0:
 
2614
 
2615
  proxy-addr@~2.0.7:
2616
  version "2.0.7"
2617
+ resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.7.tgz#f19fe69ceab311eeb94b42e70e8c2070f9ba1025"
2618
  integrity sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==
2619
  dependencies:
2620
  forwarded "0.2.0"
 
2627
 
2628
  qs@~6.14.0:
2629
  version "6.14.1"
2630
+ resolved "https://registry.yarnpkg.com/qs/-/qs-6.14.1.tgz#a41d85b9d3902f31d27861790506294881871159"
2631
  integrity sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==
2632
  dependencies:
2633
  side-channel "^1.1.0"
2634
 
2635
  range-parser@~1.2.1:
2636
  version "1.2.1"
2637
+ resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031"
2638
  integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==
2639
 
2640
  raw-body@~2.5.3:
2641
  version "2.5.3"
2642
+ resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.3.tgz#11c6650ee770a7de1b494f197927de0c923822e2"
2643
  integrity sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==
2644
  dependencies:
2645
  bytes "~3.1.2"
 
2816
 
2817
  safe-buffer@5.2.1:
2818
  version "5.2.1"
2819
+ resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6"
2820
  integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==
2821
 
2822
  "safer-buffer@>= 2.1.2 < 3":
2823
  version "2.1.2"
2824
+ resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
2825
  integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==
2826
 
2827
  scheduler@^0.27.0:
 
2836
 
2837
  send@~0.19.0, send@~0.19.1:
2838
  version "0.19.2"
2839
+ resolved "https://registry.yarnpkg.com/send/-/send-0.19.2.tgz#59bc0da1b4ea7ad42736fd642b1c4294e114ff29"
2840
  integrity sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==
2841
  dependencies:
2842
  debug "2.6.9"
 
2855
 
2856
  serve-static@~1.16.2:
2857
  version "1.16.3"
2858
+ resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.3.tgz#a97b74d955778583f3862a4f0b841eb4d5d78cf9"
2859
  integrity sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==
2860
  dependencies:
2861
  encodeurl "~2.0.0"
 
2870
 
2871
  setprototypeof@1.2.0, setprototypeof@~1.2.0:
2872
  version "1.2.0"
2873
+ resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424"
2874
  integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==
2875
 
2876
  shebang-command@^2.0.0:
 
2887
 
2888
  side-channel-list@^1.0.0:
2889
  version "1.0.0"
2890
+ resolved "https://registry.yarnpkg.com/side-channel-list/-/side-channel-list-1.0.0.tgz#10cb5984263115d3b7a0e336591e290a830af8ad"
2891
  integrity sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==
2892
  dependencies:
2893
  es-errors "^1.3.0"
 
2895
 
2896
  side-channel-map@^1.0.1:
2897
  version "1.0.1"
2898
+ resolved "https://registry.yarnpkg.com/side-channel-map/-/side-channel-map-1.0.1.tgz#d6bb6b37902c6fef5174e5f533fab4c732a26f42"
2899
  integrity sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==
2900
  dependencies:
2901
  call-bound "^1.0.2"
 
2905
 
2906
  side-channel-weakmap@^1.0.2:
2907
  version "1.0.2"
2908
+ resolved "https://registry.yarnpkg.com/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz#11dda19d5368e40ce9ec2bdc1fb0ecbc0790ecea"
2909
  integrity sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==
2910
  dependencies:
2911
  call-bound "^1.0.2"
 
2916
 
2917
  side-channel@^1.1.0:
2918
  version "1.1.0"
2919
+ resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.1.0.tgz#c3fcff9c4da932784873335ec9765fa94ff66bc9"
2920
  integrity sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==
2921
  dependencies:
2922
  es-errors "^1.3.0"
 
2942
 
2943
  statuses@~2.0.1, statuses@~2.0.2:
2944
  version "2.0.2"
2945
+ resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.2.tgz#8f75eecef765b5e1cfcdc080da59409ed424e382"
2946
  integrity sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==
2947
 
 
 
 
 
 
 
 
 
 
2948
  stringify-entities@^4.0.0:
2949
  version "4.0.4"
2950
  resolved "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz"
 
2953
  character-entities-html4 "^2.0.0"
2954
  character-entities-legacy "^3.0.0"
2955
 
 
 
 
 
 
 
 
2956
  strip-json-comments@^3.1.1:
2957
  version "3.1.1"
2958
  resolved "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz"
 
2999
 
3000
  toidentifier@~1.0.1:
3001
  version "1.0.1"
3002
+ resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
3003
  integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
3004
 
3005
  trim-lines@^3.0.0:
 
3026
 
3027
  type-is@~1.6.18:
3028
  version "1.6.18"
3029
+ resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131"
3030
  integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==
3031
  dependencies:
3032
  media-typer "0.3.0"
 
3093
 
3094
  unpipe@~1.0.0:
3095
  version "1.0.0"
3096
+ resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec"
3097
  integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==
3098
 
3099
  update-browserslist-db@^1.2.0:
 
3113
 
3114
  utils-merge@1.0.1:
3115
  version "1.0.1"
3116
+ resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
3117
  integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
3118
 
3119
  vary@~1.1.2:
3120
  version "1.1.2"
3121
+ resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc"
3122
  integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==
3123
 
3124
  vfile-message@^4.0.0: