abdullah-khaled commited on
Commit
6c7da6d
·
1 Parent(s): e46de24

Improve the code and UI for easy usage

Browse files
index.html CHANGED
@@ -1,13 +1,16 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>AI Voice Secretary</title>
7
- <script type="module" src="/src/main.jsx"></script>
8
  <script src="https://cdn.tailwindcss.com"></script>
 
 
 
9
  </head>
10
- <body class="bg-gray-100 font-sans">
11
  <div id="root"></div>
12
  </body>
13
  </html>
 
1
  <!DOCTYPE html>
2
+ <html lang="en" class="dark">
3
  <head>
4
  <meta charset="UTF-8" />
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
  <title>AI Voice Secretary</title>
7
+ <link rel="icon" type="image/svg+xml" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Ccircle cx='16' cy='16' r='16' fill='%23006DFF'/%3E%3Ctext x='16' y='22' font-size='16' text-anchor='middle' fill='white' font-family='Arial'%3E%F0%9F%8E%A4%3C/text%3E%3C/svg%3E"/> <script type="module" src="/src/main.jsx"></script>
8
  <script src="https://cdn.tailwindcss.com"></script>
9
+ <script>
10
+ tailwind.config = { darkMode: 'class' };
11
+ </script>
12
  </head>
13
+ <body class="bg-gray-100 dark:bg-gray-900 font-sans">
14
  <div id="root"></div>
15
  </body>
16
  </html>
package-lock.json CHANGED
@@ -11,7 +11,9 @@
11
  "date-fns": "^4.1.0",
12
  "lucide-react": "^0.525.0",
13
  "react": "^19.0.0",
14
- "react-dom": "^19.0.0"
 
 
15
  },
16
  "devDependencies": {
17
  "@eslint/js": "^9.22.0",
@@ -1367,13 +1369,39 @@
1367
  "@babel/types": "^7.20.7"
1368
  }
1369
  },
 
 
 
 
 
 
 
 
 
1370
  "node_modules/@types/estree": {
1371
  "version": "1.0.8",
1372
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1373
  "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
1374
- "dev": true,
1375
  "license": "MIT"
1376
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1377
  "node_modules/@types/json-schema": {
1378
  "version": "7.0.15",
1379
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
@@ -1381,11 +1409,25 @@
1381
  "dev": true,
1382
  "license": "MIT"
1383
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1384
  "node_modules/@types/react": {
1385
  "version": "19.1.8",
1386
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
1387
  "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
1388
- "dev": true,
1389
  "license": "MIT",
1390
  "dependencies": {
1391
  "csstype": "^3.0.2"
@@ -1401,6 +1443,18 @@
1401
  "@types/react": "^19.0.0"
1402
  }
1403
  },
 
 
 
 
 
 
 
 
 
 
 
 
1404
  "node_modules/@vitejs/plugin-react": {
1405
  "version": "4.6.0",
1406
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz",
@@ -1523,6 +1577,16 @@
1523
  "postcss": "^8.1.0"
1524
  }
1525
  },
 
 
 
 
 
 
 
 
 
 
1526
  "node_modules/balanced-match": {
1527
  "version": "1.0.2",
1528
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
@@ -1605,6 +1669,16 @@
1605
  ],
1606
  "license": "CC-BY-4.0"
1607
  },
 
 
 
 
 
 
 
 
 
 
1608
  "node_modules/chalk": {
1609
  "version": "4.1.2",
1610
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
@@ -1622,6 +1696,46 @@
1622
  "url": "https://github.com/chalk/chalk?sponsor=1"
1623
  }
1624
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1625
  "node_modules/color-convert": {
1626
  "version": "2.0.1",
1627
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
@@ -1642,6 +1756,16 @@
1642
  "dev": true,
1643
  "license": "MIT"
1644
  },
 
 
 
 
 
 
 
 
 
 
1645
  "node_modules/concat-map": {
1646
  "version": "0.0.1",
1647
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
@@ -1675,7 +1799,6 @@
1675
  "version": "3.1.3",
1676
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
1677
  "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
1678
- "dev": true,
1679
  "license": "MIT"
1680
  },
1681
  "node_modules/date-fns": {
@@ -1692,7 +1815,6 @@
1692
  "version": "4.4.1",
1693
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
1694
  "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
1695
- "dev": true,
1696
  "license": "MIT",
1697
  "dependencies": {
1698
  "ms": "^2.1.3"
@@ -1706,6 +1828,19 @@
1706
  }
1707
  }
1708
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
1709
  "node_modules/deep-is": {
1710
  "version": "0.1.4",
1711
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
@@ -1713,6 +1848,28 @@
1713
  "dev": true,
1714
  "license": "MIT"
1715
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1716
  "node_modules/electron-to-chromium": {
1717
  "version": "1.5.177",
1718
  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.177.tgz",
@@ -1952,6 +2109,16 @@
1952
  "node": ">=4.0"
1953
  }
1954
  },
 
 
 
 
 
 
 
 
 
 
1955
  "node_modules/esutils": {
1956
  "version": "2.0.3",
1957
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
@@ -1962,6 +2129,12 @@
1962
  "node": ">=0.10.0"
1963
  }
1964
  },
 
 
 
 
 
 
1965
  "node_modules/fast-deep-equal": {
1966
  "version": "3.1.3",
1967
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@@ -2124,6 +2297,56 @@
2124
  "node": ">=8"
2125
  }
2126
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2127
  "node_modules/ignore": {
2128
  "version": "5.3.2",
2129
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
@@ -2161,6 +2384,46 @@
2161
  "node": ">=0.8.19"
2162
  }
2163
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2164
  "node_modules/is-extglob": {
2165
  "version": "2.1.1",
2166
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@@ -2184,6 +2447,28 @@
2184
  "node": ">=0.10.0"
2185
  }
2186
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2187
  "node_modules/isexe": {
2188
  "version": "2.0.0",
2189
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@@ -2305,6 +2590,16 @@
2305
  "dev": true,
2306
  "license": "MIT"
2307
  },
 
 
 
 
 
 
 
 
 
 
2308
  "node_modules/lru-cache": {
2309
  "version": "5.1.1",
2310
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
@@ -2324,108 +2619,962 @@
2324
  "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
2325
  }
2326
  },
2327
- "node_modules/minimatch": {
2328
- "version": "3.1.2",
2329
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
2330
- "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
2331
- "dev": true,
2332
- "license": "ISC",
2333
- "dependencies": {
2334
- "brace-expansion": "^1.1.7"
2335
- },
2336
- "engines": {
2337
- "node": "*"
2338
  }
2339
  },
2340
- "node_modules/ms": {
2341
- "version": "2.1.3",
2342
- "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
2343
- "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
2344
- "dev": true,
2345
- "license": "MIT"
2346
- },
2347
- "node_modules/nanoid": {
2348
- "version": "3.3.11",
2349
- "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
2350
- "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
2351
- "dev": true,
2352
- "funding": [
2353
- {
2354
- "type": "github",
2355
- "url": "https://github.com/sponsors/ai"
2356
- }
2357
- ],
2358
  "license": "MIT",
2359
- "bin": {
2360
- "nanoid": "bin/nanoid.cjs"
 
 
 
2361
  },
2362
- "engines": {
2363
- "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
 
2364
  }
2365
  },
2366
- "node_modules/natural-compare": {
2367
- "version": "1.4.0",
2368
- "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
2369
- "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
2370
- "dev": true,
2371
- "license": "MIT"
2372
- },
2373
- "node_modules/node-releases": {
2374
- "version": "2.0.19",
2375
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
2376
- "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
2377
- "dev": true,
2378
- "license": "MIT"
2379
- },
2380
- "node_modules/normalize-range": {
2381
- "version": "0.1.2",
2382
- "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
2383
- "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
2384
- "dev": true,
2385
  "license": "MIT",
2386
  "engines": {
2387
- "node": ">=0.10.0"
 
 
 
2388
  }
2389
  },
2390
- "node_modules/optionator": {
2391
- "version": "0.9.4",
2392
- "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
2393
- "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
2394
- "dev": true,
2395
  "license": "MIT",
2396
  "dependencies": {
2397
- "deep-is": "^0.1.3",
2398
- "fast-levenshtein": "^2.0.6",
2399
- "levn": "^0.4.1",
2400
- "prelude-ls": "^1.2.1",
2401
- "type-check": "^0.4.0",
2402
- "word-wrap": "^1.2.5"
 
 
 
 
 
 
2403
  },
2404
- "engines": {
2405
- "node": ">= 0.8.0"
 
2406
  }
2407
  },
2408
- "node_modules/p-limit": {
2409
  "version": "3.1.0",
2410
- "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
2411
- "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
2412
- "dev": true,
2413
  "license": "MIT",
2414
  "dependencies": {
2415
- "yocto-queue": "^0.1.0"
2416
- },
2417
- "engines": {
2418
- "node": ">=10"
 
 
 
2419
  },
2420
  "funding": {
2421
- "url": "https://github.com/sponsors/sindresorhus"
 
2422
  }
2423
  },
2424
- "node_modules/p-locate": {
2425
- "version": "5.0.0",
2426
- "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
2427
- "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
2428
- "dev": true,
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2429
  "license": "MIT",
2430
  "dependencies": {
2431
  "p-limit": "^3.0.2"
@@ -2450,6 +3599,31 @@
2450
  "node": ">=6"
2451
  }
2452
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2453
  "node_modules/path-exists": {
2454
  "version": "4.0.0",
2455
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
@@ -2536,6 +3710,16 @@
2536
  "node": ">= 0.8.0"
2537
  }
2538
  },
 
 
 
 
 
 
 
 
 
 
2539
  "node_modules/punycode": {
2540
  "version": "2.3.1",
2541
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
@@ -2567,6 +3751,33 @@
2567
  "react": "^19.1.0"
2568
  }
2569
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2570
  "node_modules/react-refresh": {
2571
  "version": "0.17.0",
2572
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@@ -2577,6 +3788,72 @@
2577
  "node": ">=0.10.0"
2578
  }
2579
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2580
  "node_modules/resolve-from": {
2581
  "version": "4.0.0",
2582
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
@@ -2676,6 +3953,30 @@
2676
  "node": ">=0.10.0"
2677
  }
2678
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2679
  "node_modules/strip-json-comments": {
2680
  "version": "3.1.1",
2681
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
@@ -2689,6 +3990,24 @@
2689
  "url": "https://github.com/sponsors/sindresorhus"
2690
  }
2691
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2692
  "node_modules/supports-color": {
2693
  "version": "7.2.0",
2694
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
@@ -2726,6 +4045,26 @@
2726
  "url": "https://github.com/sponsors/SuperchupuDev"
2727
  }
2728
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2729
  "node_modules/type-check": {
2730
  "version": "0.4.0",
2731
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -2739,6 +4078,93 @@
2739
  "node": ">= 0.8.0"
2740
  }
2741
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2742
  "node_modules/update-browserslist-db": {
2743
  "version": "1.1.3",
2744
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
@@ -2780,6 +4206,34 @@
2780
  "punycode": "^2.1.0"
2781
  }
2782
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2783
  "node_modules/vite": {
2784
  "version": "6.3.5",
2785
  "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
@@ -2900,6 +4354,16 @@
2900
  "funding": {
2901
  "url": "https://github.com/sponsors/sindresorhus"
2902
  }
 
 
 
 
 
 
 
 
 
 
2903
  }
2904
  }
2905
  }
 
11
  "date-fns": "^4.1.0",
12
  "lucide-react": "^0.525.0",
13
  "react": "^19.0.0",
14
+ "react-dom": "^19.0.0",
15
+ "react-markdown": "^10.1.0",
16
+ "remark-gfm": "^4.0.1"
17
  },
18
  "devDependencies": {
19
  "@eslint/js": "^9.22.0",
 
1369
  "@babel/types": "^7.20.7"
1370
  }
1371
  },
1372
+ "node_modules/@types/debug": {
1373
+ "version": "4.1.12",
1374
+ "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz",
1375
+ "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==",
1376
+ "license": "MIT",
1377
+ "dependencies": {
1378
+ "@types/ms": "*"
1379
+ }
1380
+ },
1381
  "node_modules/@types/estree": {
1382
  "version": "1.0.8",
1383
  "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz",
1384
  "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==",
 
1385
  "license": "MIT"
1386
  },
1387
+ "node_modules/@types/estree-jsx": {
1388
+ "version": "1.0.5",
1389
+ "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz",
1390
+ "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==",
1391
+ "license": "MIT",
1392
+ "dependencies": {
1393
+ "@types/estree": "*"
1394
+ }
1395
+ },
1396
+ "node_modules/@types/hast": {
1397
+ "version": "3.0.4",
1398
+ "resolved": "https://registry.npmjs.org/@types/hast/-/hast-3.0.4.tgz",
1399
+ "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==",
1400
+ "license": "MIT",
1401
+ "dependencies": {
1402
+ "@types/unist": "*"
1403
+ }
1404
+ },
1405
  "node_modules/@types/json-schema": {
1406
  "version": "7.0.15",
1407
  "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
 
1409
  "dev": true,
1410
  "license": "MIT"
1411
  },
1412
+ "node_modules/@types/mdast": {
1413
+ "version": "4.0.4",
1414
+ "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.4.tgz",
1415
+ "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==",
1416
+ "license": "MIT",
1417
+ "dependencies": {
1418
+ "@types/unist": "*"
1419
+ }
1420
+ },
1421
+ "node_modules/@types/ms": {
1422
+ "version": "2.1.0",
1423
+ "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz",
1424
+ "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==",
1425
+ "license": "MIT"
1426
+ },
1427
  "node_modules/@types/react": {
1428
  "version": "19.1.8",
1429
  "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.8.tgz",
1430
  "integrity": "sha512-AwAfQ2Wa5bCx9WP8nZL2uMZWod7J7/JSplxbTmBQ5ms6QpqNYm672H0Vu9ZVKVngQ+ii4R/byguVEUZQyeg44g==",
 
1431
  "license": "MIT",
1432
  "dependencies": {
1433
  "csstype": "^3.0.2"
 
1443
  "@types/react": "^19.0.0"
1444
  }
1445
  },
1446
+ "node_modules/@types/unist": {
1447
+ "version": "3.0.3",
1448
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz",
1449
+ "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
1450
+ "license": "MIT"
1451
+ },
1452
+ "node_modules/@ungap/structured-clone": {
1453
+ "version": "1.3.0",
1454
+ "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz",
1455
+ "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==",
1456
+ "license": "ISC"
1457
+ },
1458
  "node_modules/@vitejs/plugin-react": {
1459
  "version": "4.6.0",
1460
  "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.6.0.tgz",
 
1577
  "postcss": "^8.1.0"
1578
  }
1579
  },
1580
+ "node_modules/bail": {
1581
+ "version": "2.0.2",
1582
+ "resolved": "https://registry.npmjs.org/bail/-/bail-2.0.2.tgz",
1583
+ "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==",
1584
+ "license": "MIT",
1585
+ "funding": {
1586
+ "type": "github",
1587
+ "url": "https://github.com/sponsors/wooorm"
1588
+ }
1589
+ },
1590
  "node_modules/balanced-match": {
1591
  "version": "1.0.2",
1592
  "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
 
1669
  ],
1670
  "license": "CC-BY-4.0"
1671
  },
1672
+ "node_modules/ccount": {
1673
+ "version": "2.0.1",
1674
+ "resolved": "https://registry.npmjs.org/ccount/-/ccount-2.0.1.tgz",
1675
+ "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==",
1676
+ "license": "MIT",
1677
+ "funding": {
1678
+ "type": "github",
1679
+ "url": "https://github.com/sponsors/wooorm"
1680
+ }
1681
+ },
1682
  "node_modules/chalk": {
1683
  "version": "4.1.2",
1684
  "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
 
1696
  "url": "https://github.com/chalk/chalk?sponsor=1"
1697
  }
1698
  },
1699
+ "node_modules/character-entities": {
1700
+ "version": "2.0.2",
1701
+ "resolved": "https://registry.npmjs.org/character-entities/-/character-entities-2.0.2.tgz",
1702
+ "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==",
1703
+ "license": "MIT",
1704
+ "funding": {
1705
+ "type": "github",
1706
+ "url": "https://github.com/sponsors/wooorm"
1707
+ }
1708
+ },
1709
+ "node_modules/character-entities-html4": {
1710
+ "version": "2.1.0",
1711
+ "resolved": "https://registry.npmjs.org/character-entities-html4/-/character-entities-html4-2.1.0.tgz",
1712
+ "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==",
1713
+ "license": "MIT",
1714
+ "funding": {
1715
+ "type": "github",
1716
+ "url": "https://github.com/sponsors/wooorm"
1717
+ }
1718
+ },
1719
+ "node_modules/character-entities-legacy": {
1720
+ "version": "3.0.0",
1721
+ "resolved": "https://registry.npmjs.org/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz",
1722
+ "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==",
1723
+ "license": "MIT",
1724
+ "funding": {
1725
+ "type": "github",
1726
+ "url": "https://github.com/sponsors/wooorm"
1727
+ }
1728
+ },
1729
+ "node_modules/character-reference-invalid": {
1730
+ "version": "2.0.1",
1731
+ "resolved": "https://registry.npmjs.org/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz",
1732
+ "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==",
1733
+ "license": "MIT",
1734
+ "funding": {
1735
+ "type": "github",
1736
+ "url": "https://github.com/sponsors/wooorm"
1737
+ }
1738
+ },
1739
  "node_modules/color-convert": {
1740
  "version": "2.0.1",
1741
  "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
 
1756
  "dev": true,
1757
  "license": "MIT"
1758
  },
1759
+ "node_modules/comma-separated-tokens": {
1760
+ "version": "2.0.3",
1761
+ "resolved": "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz",
1762
+ "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==",
1763
+ "license": "MIT",
1764
+ "funding": {
1765
+ "type": "github",
1766
+ "url": "https://github.com/sponsors/wooorm"
1767
+ }
1768
+ },
1769
  "node_modules/concat-map": {
1770
  "version": "0.0.1",
1771
  "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
 
1799
  "version": "3.1.3",
1800
  "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz",
1801
  "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
 
1802
  "license": "MIT"
1803
  },
1804
  "node_modules/date-fns": {
 
1815
  "version": "4.4.1",
1816
  "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz",
1817
  "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==",
 
1818
  "license": "MIT",
1819
  "dependencies": {
1820
  "ms": "^2.1.3"
 
1828
  }
1829
  }
1830
  },
1831
+ "node_modules/decode-named-character-reference": {
1832
+ "version": "1.2.0",
1833
+ "resolved": "https://registry.npmjs.org/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz",
1834
+ "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==",
1835
+ "license": "MIT",
1836
+ "dependencies": {
1837
+ "character-entities": "^2.0.0"
1838
+ },
1839
+ "funding": {
1840
+ "type": "github",
1841
+ "url": "https://github.com/sponsors/wooorm"
1842
+ }
1843
+ },
1844
  "node_modules/deep-is": {
1845
  "version": "0.1.4",
1846
  "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
 
1848
  "dev": true,
1849
  "license": "MIT"
1850
  },
1851
+ "node_modules/dequal": {
1852
+ "version": "2.0.3",
1853
+ "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz",
1854
+ "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==",
1855
+ "license": "MIT",
1856
+ "engines": {
1857
+ "node": ">=6"
1858
+ }
1859
+ },
1860
+ "node_modules/devlop": {
1861
+ "version": "1.1.0",
1862
+ "resolved": "https://registry.npmjs.org/devlop/-/devlop-1.1.0.tgz",
1863
+ "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==",
1864
+ "license": "MIT",
1865
+ "dependencies": {
1866
+ "dequal": "^2.0.0"
1867
+ },
1868
+ "funding": {
1869
+ "type": "github",
1870
+ "url": "https://github.com/sponsors/wooorm"
1871
+ }
1872
+ },
1873
  "node_modules/electron-to-chromium": {
1874
  "version": "1.5.177",
1875
  "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.177.tgz",
 
2109
  "node": ">=4.0"
2110
  }
2111
  },
2112
+ "node_modules/estree-util-is-identifier-name": {
2113
+ "version": "3.0.0",
2114
+ "resolved": "https://registry.npmjs.org/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz",
2115
+ "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==",
2116
+ "license": "MIT",
2117
+ "funding": {
2118
+ "type": "opencollective",
2119
+ "url": "https://opencollective.com/unified"
2120
+ }
2121
+ },
2122
  "node_modules/esutils": {
2123
  "version": "2.0.3",
2124
  "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
 
2129
  "node": ">=0.10.0"
2130
  }
2131
  },
2132
+ "node_modules/extend": {
2133
+ "version": "3.0.2",
2134
+ "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
2135
+ "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
2136
+ "license": "MIT"
2137
+ },
2138
  "node_modules/fast-deep-equal": {
2139
  "version": "3.1.3",
2140
  "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
 
2297
  "node": ">=8"
2298
  }
2299
  },
2300
+ "node_modules/hast-util-to-jsx-runtime": {
2301
+ "version": "2.3.6",
2302
+ "resolved": "https://registry.npmjs.org/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz",
2303
+ "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==",
2304
+ "license": "MIT",
2305
+ "dependencies": {
2306
+ "@types/estree": "^1.0.0",
2307
+ "@types/hast": "^3.0.0",
2308
+ "@types/unist": "^3.0.0",
2309
+ "comma-separated-tokens": "^2.0.0",
2310
+ "devlop": "^1.0.0",
2311
+ "estree-util-is-identifier-name": "^3.0.0",
2312
+ "hast-util-whitespace": "^3.0.0",
2313
+ "mdast-util-mdx-expression": "^2.0.0",
2314
+ "mdast-util-mdx-jsx": "^3.0.0",
2315
+ "mdast-util-mdxjs-esm": "^2.0.0",
2316
+ "property-information": "^7.0.0",
2317
+ "space-separated-tokens": "^2.0.0",
2318
+ "style-to-js": "^1.0.0",
2319
+ "unist-util-position": "^5.0.0",
2320
+ "vfile-message": "^4.0.0"
2321
+ },
2322
+ "funding": {
2323
+ "type": "opencollective",
2324
+ "url": "https://opencollective.com/unified"
2325
+ }
2326
+ },
2327
+ "node_modules/hast-util-whitespace": {
2328
+ "version": "3.0.0",
2329
+ "resolved": "https://registry.npmjs.org/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz",
2330
+ "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==",
2331
+ "license": "MIT",
2332
+ "dependencies": {
2333
+ "@types/hast": "^3.0.0"
2334
+ },
2335
+ "funding": {
2336
+ "type": "opencollective",
2337
+ "url": "https://opencollective.com/unified"
2338
+ }
2339
+ },
2340
+ "node_modules/html-url-attributes": {
2341
+ "version": "3.0.1",
2342
+ "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz",
2343
+ "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==",
2344
+ "license": "MIT",
2345
+ "funding": {
2346
+ "type": "opencollective",
2347
+ "url": "https://opencollective.com/unified"
2348
+ }
2349
+ },
2350
  "node_modules/ignore": {
2351
  "version": "5.3.2",
2352
  "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz",
 
2384
  "node": ">=0.8.19"
2385
  }
2386
  },
2387
+ "node_modules/inline-style-parser": {
2388
+ "version": "0.2.4",
2389
+ "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz",
2390
+ "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==",
2391
+ "license": "MIT"
2392
+ },
2393
+ "node_modules/is-alphabetical": {
2394
+ "version": "2.0.1",
2395
+ "resolved": "https://registry.npmjs.org/is-alphabetical/-/is-alphabetical-2.0.1.tgz",
2396
+ "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==",
2397
+ "license": "MIT",
2398
+ "funding": {
2399
+ "type": "github",
2400
+ "url": "https://github.com/sponsors/wooorm"
2401
+ }
2402
+ },
2403
+ "node_modules/is-alphanumerical": {
2404
+ "version": "2.0.1",
2405
+ "resolved": "https://registry.npmjs.org/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz",
2406
+ "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==",
2407
+ "license": "MIT",
2408
+ "dependencies": {
2409
+ "is-alphabetical": "^2.0.0",
2410
+ "is-decimal": "^2.0.0"
2411
+ },
2412
+ "funding": {
2413
+ "type": "github",
2414
+ "url": "https://github.com/sponsors/wooorm"
2415
+ }
2416
+ },
2417
+ "node_modules/is-decimal": {
2418
+ "version": "2.0.1",
2419
+ "resolved": "https://registry.npmjs.org/is-decimal/-/is-decimal-2.0.1.tgz",
2420
+ "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==",
2421
+ "license": "MIT",
2422
+ "funding": {
2423
+ "type": "github",
2424
+ "url": "https://github.com/sponsors/wooorm"
2425
+ }
2426
+ },
2427
  "node_modules/is-extglob": {
2428
  "version": "2.1.1",
2429
  "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
 
2447
  "node": ">=0.10.0"
2448
  }
2449
  },
2450
+ "node_modules/is-hexadecimal": {
2451
+ "version": "2.0.1",
2452
+ "resolved": "https://registry.npmjs.org/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz",
2453
+ "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==",
2454
+ "license": "MIT",
2455
+ "funding": {
2456
+ "type": "github",
2457
+ "url": "https://github.com/sponsors/wooorm"
2458
+ }
2459
+ },
2460
+ "node_modules/is-plain-obj": {
2461
+ "version": "4.1.0",
2462
+ "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz",
2463
+ "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==",
2464
+ "license": "MIT",
2465
+ "engines": {
2466
+ "node": ">=12"
2467
+ },
2468
+ "funding": {
2469
+ "url": "https://github.com/sponsors/sindresorhus"
2470
+ }
2471
+ },
2472
  "node_modules/isexe": {
2473
  "version": "2.0.0",
2474
  "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
 
2590
  "dev": true,
2591
  "license": "MIT"
2592
  },
2593
+ "node_modules/longest-streak": {
2594
+ "version": "3.1.0",
2595
+ "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz",
2596
+ "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==",
2597
+ "license": "MIT",
2598
+ "funding": {
2599
+ "type": "github",
2600
+ "url": "https://github.com/sponsors/wooorm"
2601
+ }
2602
+ },
2603
  "node_modules/lru-cache": {
2604
  "version": "5.1.1",
2605
  "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
 
2619
  "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
2620
  }
2621
  },
2622
+ "node_modules/markdown-table": {
2623
+ "version": "3.0.4",
2624
+ "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-3.0.4.tgz",
2625
+ "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==",
2626
+ "license": "MIT",
2627
+ "funding": {
2628
+ "type": "github",
2629
+ "url": "https://github.com/sponsors/wooorm"
 
 
 
2630
  }
2631
  },
2632
+ "node_modules/mdast-util-find-and-replace": {
2633
+ "version": "3.0.2",
2634
+ "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz",
2635
+ "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2636
  "license": "MIT",
2637
+ "dependencies": {
2638
+ "@types/mdast": "^4.0.0",
2639
+ "escape-string-regexp": "^5.0.0",
2640
+ "unist-util-is": "^6.0.0",
2641
+ "unist-util-visit-parents": "^6.0.0"
2642
  },
2643
+ "funding": {
2644
+ "type": "opencollective",
2645
+ "url": "https://opencollective.com/unified"
2646
  }
2647
  },
2648
+ "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": {
2649
+ "version": "5.0.0",
2650
+ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz",
2651
+ "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==",
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2652
  "license": "MIT",
2653
  "engines": {
2654
+ "node": ">=12"
2655
+ },
2656
+ "funding": {
2657
+ "url": "https://github.com/sponsors/sindresorhus"
2658
  }
2659
  },
2660
+ "node_modules/mdast-util-from-markdown": {
2661
+ "version": "2.0.2",
2662
+ "resolved": "https://registry.npmjs.org/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz",
2663
+ "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==",
 
2664
  "license": "MIT",
2665
  "dependencies": {
2666
+ "@types/mdast": "^4.0.0",
2667
+ "@types/unist": "^3.0.0",
2668
+ "decode-named-character-reference": "^1.0.0",
2669
+ "devlop": "^1.0.0",
2670
+ "mdast-util-to-string": "^4.0.0",
2671
+ "micromark": "^4.0.0",
2672
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
2673
+ "micromark-util-decode-string": "^2.0.0",
2674
+ "micromark-util-normalize-identifier": "^2.0.0",
2675
+ "micromark-util-symbol": "^2.0.0",
2676
+ "micromark-util-types": "^2.0.0",
2677
+ "unist-util-stringify-position": "^4.0.0"
2678
  },
2679
+ "funding": {
2680
+ "type": "opencollective",
2681
+ "url": "https://opencollective.com/unified"
2682
  }
2683
  },
2684
+ "node_modules/mdast-util-gfm": {
2685
  "version": "3.1.0",
2686
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz",
2687
+ "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==",
 
2688
  "license": "MIT",
2689
  "dependencies": {
2690
+ "mdast-util-from-markdown": "^2.0.0",
2691
+ "mdast-util-gfm-autolink-literal": "^2.0.0",
2692
+ "mdast-util-gfm-footnote": "^2.0.0",
2693
+ "mdast-util-gfm-strikethrough": "^2.0.0",
2694
+ "mdast-util-gfm-table": "^2.0.0",
2695
+ "mdast-util-gfm-task-list-item": "^2.0.0",
2696
+ "mdast-util-to-markdown": "^2.0.0"
2697
  },
2698
  "funding": {
2699
+ "type": "opencollective",
2700
+ "url": "https://opencollective.com/unified"
2701
  }
2702
  },
2703
+ "node_modules/mdast-util-gfm-autolink-literal": {
2704
+ "version": "2.0.1",
2705
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz",
2706
+ "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==",
2707
+ "license": "MIT",
2708
+ "dependencies": {
2709
+ "@types/mdast": "^4.0.0",
2710
+ "ccount": "^2.0.0",
2711
+ "devlop": "^1.0.0",
2712
+ "mdast-util-find-and-replace": "^3.0.0",
2713
+ "micromark-util-character": "^2.0.0"
2714
+ },
2715
+ "funding": {
2716
+ "type": "opencollective",
2717
+ "url": "https://opencollective.com/unified"
2718
+ }
2719
+ },
2720
+ "node_modules/mdast-util-gfm-footnote": {
2721
+ "version": "2.1.0",
2722
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz",
2723
+ "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==",
2724
+ "license": "MIT",
2725
+ "dependencies": {
2726
+ "@types/mdast": "^4.0.0",
2727
+ "devlop": "^1.1.0",
2728
+ "mdast-util-from-markdown": "^2.0.0",
2729
+ "mdast-util-to-markdown": "^2.0.0",
2730
+ "micromark-util-normalize-identifier": "^2.0.0"
2731
+ },
2732
+ "funding": {
2733
+ "type": "opencollective",
2734
+ "url": "https://opencollective.com/unified"
2735
+ }
2736
+ },
2737
+ "node_modules/mdast-util-gfm-strikethrough": {
2738
+ "version": "2.0.0",
2739
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz",
2740
+ "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==",
2741
+ "license": "MIT",
2742
+ "dependencies": {
2743
+ "@types/mdast": "^4.0.0",
2744
+ "mdast-util-from-markdown": "^2.0.0",
2745
+ "mdast-util-to-markdown": "^2.0.0"
2746
+ },
2747
+ "funding": {
2748
+ "type": "opencollective",
2749
+ "url": "https://opencollective.com/unified"
2750
+ }
2751
+ },
2752
+ "node_modules/mdast-util-gfm-table": {
2753
+ "version": "2.0.0",
2754
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz",
2755
+ "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==",
2756
+ "license": "MIT",
2757
+ "dependencies": {
2758
+ "@types/mdast": "^4.0.0",
2759
+ "devlop": "^1.0.0",
2760
+ "markdown-table": "^3.0.0",
2761
+ "mdast-util-from-markdown": "^2.0.0",
2762
+ "mdast-util-to-markdown": "^2.0.0"
2763
+ },
2764
+ "funding": {
2765
+ "type": "opencollective",
2766
+ "url": "https://opencollective.com/unified"
2767
+ }
2768
+ },
2769
+ "node_modules/mdast-util-gfm-task-list-item": {
2770
+ "version": "2.0.0",
2771
+ "resolved": "https://registry.npmjs.org/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz",
2772
+ "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==",
2773
+ "license": "MIT",
2774
+ "dependencies": {
2775
+ "@types/mdast": "^4.0.0",
2776
+ "devlop": "^1.0.0",
2777
+ "mdast-util-from-markdown": "^2.0.0",
2778
+ "mdast-util-to-markdown": "^2.0.0"
2779
+ },
2780
+ "funding": {
2781
+ "type": "opencollective",
2782
+ "url": "https://opencollective.com/unified"
2783
+ }
2784
+ },
2785
+ "node_modules/mdast-util-mdx-expression": {
2786
+ "version": "2.0.1",
2787
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz",
2788
+ "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==",
2789
+ "license": "MIT",
2790
+ "dependencies": {
2791
+ "@types/estree-jsx": "^1.0.0",
2792
+ "@types/hast": "^3.0.0",
2793
+ "@types/mdast": "^4.0.0",
2794
+ "devlop": "^1.0.0",
2795
+ "mdast-util-from-markdown": "^2.0.0",
2796
+ "mdast-util-to-markdown": "^2.0.0"
2797
+ },
2798
+ "funding": {
2799
+ "type": "opencollective",
2800
+ "url": "https://opencollective.com/unified"
2801
+ }
2802
+ },
2803
+ "node_modules/mdast-util-mdx-jsx": {
2804
+ "version": "3.2.0",
2805
+ "resolved": "https://registry.npmjs.org/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz",
2806
+ "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==",
2807
+ "license": "MIT",
2808
+ "dependencies": {
2809
+ "@types/estree-jsx": "^1.0.0",
2810
+ "@types/hast": "^3.0.0",
2811
+ "@types/mdast": "^4.0.0",
2812
+ "@types/unist": "^3.0.0",
2813
+ "ccount": "^2.0.0",
2814
+ "devlop": "^1.1.0",
2815
+ "mdast-util-from-markdown": "^2.0.0",
2816
+ "mdast-util-to-markdown": "^2.0.0",
2817
+ "parse-entities": "^4.0.0",
2818
+ "stringify-entities": "^4.0.0",
2819
+ "unist-util-stringify-position": "^4.0.0",
2820
+ "vfile-message": "^4.0.0"
2821
+ },
2822
+ "funding": {
2823
+ "type": "opencollective",
2824
+ "url": "https://opencollective.com/unified"
2825
+ }
2826
+ },
2827
+ "node_modules/mdast-util-mdxjs-esm": {
2828
+ "version": "2.0.1",
2829
+ "resolved": "https://registry.npmjs.org/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz",
2830
+ "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==",
2831
+ "license": "MIT",
2832
+ "dependencies": {
2833
+ "@types/estree-jsx": "^1.0.0",
2834
+ "@types/hast": "^3.0.0",
2835
+ "@types/mdast": "^4.0.0",
2836
+ "devlop": "^1.0.0",
2837
+ "mdast-util-from-markdown": "^2.0.0",
2838
+ "mdast-util-to-markdown": "^2.0.0"
2839
+ },
2840
+ "funding": {
2841
+ "type": "opencollective",
2842
+ "url": "https://opencollective.com/unified"
2843
+ }
2844
+ },
2845
+ "node_modules/mdast-util-phrasing": {
2846
+ "version": "4.1.0",
2847
+ "resolved": "https://registry.npmjs.org/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz",
2848
+ "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==",
2849
+ "license": "MIT",
2850
+ "dependencies": {
2851
+ "@types/mdast": "^4.0.0",
2852
+ "unist-util-is": "^6.0.0"
2853
+ },
2854
+ "funding": {
2855
+ "type": "opencollective",
2856
+ "url": "https://opencollective.com/unified"
2857
+ }
2858
+ },
2859
+ "node_modules/mdast-util-to-hast": {
2860
+ "version": "13.2.0",
2861
+ "resolved": "https://registry.npmjs.org/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz",
2862
+ "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==",
2863
+ "license": "MIT",
2864
+ "dependencies": {
2865
+ "@types/hast": "^3.0.0",
2866
+ "@types/mdast": "^4.0.0",
2867
+ "@ungap/structured-clone": "^1.0.0",
2868
+ "devlop": "^1.0.0",
2869
+ "micromark-util-sanitize-uri": "^2.0.0",
2870
+ "trim-lines": "^3.0.0",
2871
+ "unist-util-position": "^5.0.0",
2872
+ "unist-util-visit": "^5.0.0",
2873
+ "vfile": "^6.0.0"
2874
+ },
2875
+ "funding": {
2876
+ "type": "opencollective",
2877
+ "url": "https://opencollective.com/unified"
2878
+ }
2879
+ },
2880
+ "node_modules/mdast-util-to-markdown": {
2881
+ "version": "2.1.2",
2882
+ "resolved": "https://registry.npmjs.org/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz",
2883
+ "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==",
2884
+ "license": "MIT",
2885
+ "dependencies": {
2886
+ "@types/mdast": "^4.0.0",
2887
+ "@types/unist": "^3.0.0",
2888
+ "longest-streak": "^3.0.0",
2889
+ "mdast-util-phrasing": "^4.0.0",
2890
+ "mdast-util-to-string": "^4.0.0",
2891
+ "micromark-util-classify-character": "^2.0.0",
2892
+ "micromark-util-decode-string": "^2.0.0",
2893
+ "unist-util-visit": "^5.0.0",
2894
+ "zwitch": "^2.0.0"
2895
+ },
2896
+ "funding": {
2897
+ "type": "opencollective",
2898
+ "url": "https://opencollective.com/unified"
2899
+ }
2900
+ },
2901
+ "node_modules/mdast-util-to-string": {
2902
+ "version": "4.0.0",
2903
+ "resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
2904
+ "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
2905
+ "license": "MIT",
2906
+ "dependencies": {
2907
+ "@types/mdast": "^4.0.0"
2908
+ },
2909
+ "funding": {
2910
+ "type": "opencollective",
2911
+ "url": "https://opencollective.com/unified"
2912
+ }
2913
+ },
2914
+ "node_modules/micromark": {
2915
+ "version": "4.0.2",
2916
+ "resolved": "https://registry.npmjs.org/micromark/-/micromark-4.0.2.tgz",
2917
+ "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==",
2918
+ "funding": [
2919
+ {
2920
+ "type": "GitHub Sponsors",
2921
+ "url": "https://github.com/sponsors/unifiedjs"
2922
+ },
2923
+ {
2924
+ "type": "OpenCollective",
2925
+ "url": "https://opencollective.com/unified"
2926
+ }
2927
+ ],
2928
+ "license": "MIT",
2929
+ "dependencies": {
2930
+ "@types/debug": "^4.0.0",
2931
+ "debug": "^4.0.0",
2932
+ "decode-named-character-reference": "^1.0.0",
2933
+ "devlop": "^1.0.0",
2934
+ "micromark-core-commonmark": "^2.0.0",
2935
+ "micromark-factory-space": "^2.0.0",
2936
+ "micromark-util-character": "^2.0.0",
2937
+ "micromark-util-chunked": "^2.0.0",
2938
+ "micromark-util-combine-extensions": "^2.0.0",
2939
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
2940
+ "micromark-util-encode": "^2.0.0",
2941
+ "micromark-util-normalize-identifier": "^2.0.0",
2942
+ "micromark-util-resolve-all": "^2.0.0",
2943
+ "micromark-util-sanitize-uri": "^2.0.0",
2944
+ "micromark-util-subtokenize": "^2.0.0",
2945
+ "micromark-util-symbol": "^2.0.0",
2946
+ "micromark-util-types": "^2.0.0"
2947
+ }
2948
+ },
2949
+ "node_modules/micromark-core-commonmark": {
2950
+ "version": "2.0.3",
2951
+ "resolved": "https://registry.npmjs.org/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz",
2952
+ "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==",
2953
+ "funding": [
2954
+ {
2955
+ "type": "GitHub Sponsors",
2956
+ "url": "https://github.com/sponsors/unifiedjs"
2957
+ },
2958
+ {
2959
+ "type": "OpenCollective",
2960
+ "url": "https://opencollective.com/unified"
2961
+ }
2962
+ ],
2963
+ "license": "MIT",
2964
+ "dependencies": {
2965
+ "decode-named-character-reference": "^1.0.0",
2966
+ "devlop": "^1.0.0",
2967
+ "micromark-factory-destination": "^2.0.0",
2968
+ "micromark-factory-label": "^2.0.0",
2969
+ "micromark-factory-space": "^2.0.0",
2970
+ "micromark-factory-title": "^2.0.0",
2971
+ "micromark-factory-whitespace": "^2.0.0",
2972
+ "micromark-util-character": "^2.0.0",
2973
+ "micromark-util-chunked": "^2.0.0",
2974
+ "micromark-util-classify-character": "^2.0.0",
2975
+ "micromark-util-html-tag-name": "^2.0.0",
2976
+ "micromark-util-normalize-identifier": "^2.0.0",
2977
+ "micromark-util-resolve-all": "^2.0.0",
2978
+ "micromark-util-subtokenize": "^2.0.0",
2979
+ "micromark-util-symbol": "^2.0.0",
2980
+ "micromark-util-types": "^2.0.0"
2981
+ }
2982
+ },
2983
+ "node_modules/micromark-extension-gfm": {
2984
+ "version": "3.0.0",
2985
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz",
2986
+ "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==",
2987
+ "license": "MIT",
2988
+ "dependencies": {
2989
+ "micromark-extension-gfm-autolink-literal": "^2.0.0",
2990
+ "micromark-extension-gfm-footnote": "^2.0.0",
2991
+ "micromark-extension-gfm-strikethrough": "^2.0.0",
2992
+ "micromark-extension-gfm-table": "^2.0.0",
2993
+ "micromark-extension-gfm-tagfilter": "^2.0.0",
2994
+ "micromark-extension-gfm-task-list-item": "^2.0.0",
2995
+ "micromark-util-combine-extensions": "^2.0.0",
2996
+ "micromark-util-types": "^2.0.0"
2997
+ },
2998
+ "funding": {
2999
+ "type": "opencollective",
3000
+ "url": "https://opencollective.com/unified"
3001
+ }
3002
+ },
3003
+ "node_modules/micromark-extension-gfm-autolink-literal": {
3004
+ "version": "2.1.0",
3005
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz",
3006
+ "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==",
3007
+ "license": "MIT",
3008
+ "dependencies": {
3009
+ "micromark-util-character": "^2.0.0",
3010
+ "micromark-util-sanitize-uri": "^2.0.0",
3011
+ "micromark-util-symbol": "^2.0.0",
3012
+ "micromark-util-types": "^2.0.0"
3013
+ },
3014
+ "funding": {
3015
+ "type": "opencollective",
3016
+ "url": "https://opencollective.com/unified"
3017
+ }
3018
+ },
3019
+ "node_modules/micromark-extension-gfm-footnote": {
3020
+ "version": "2.1.0",
3021
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz",
3022
+ "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==",
3023
+ "license": "MIT",
3024
+ "dependencies": {
3025
+ "devlop": "^1.0.0",
3026
+ "micromark-core-commonmark": "^2.0.0",
3027
+ "micromark-factory-space": "^2.0.0",
3028
+ "micromark-util-character": "^2.0.0",
3029
+ "micromark-util-normalize-identifier": "^2.0.0",
3030
+ "micromark-util-sanitize-uri": "^2.0.0",
3031
+ "micromark-util-symbol": "^2.0.0",
3032
+ "micromark-util-types": "^2.0.0"
3033
+ },
3034
+ "funding": {
3035
+ "type": "opencollective",
3036
+ "url": "https://opencollective.com/unified"
3037
+ }
3038
+ },
3039
+ "node_modules/micromark-extension-gfm-strikethrough": {
3040
+ "version": "2.1.0",
3041
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz",
3042
+ "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==",
3043
+ "license": "MIT",
3044
+ "dependencies": {
3045
+ "devlop": "^1.0.0",
3046
+ "micromark-util-chunked": "^2.0.0",
3047
+ "micromark-util-classify-character": "^2.0.0",
3048
+ "micromark-util-resolve-all": "^2.0.0",
3049
+ "micromark-util-symbol": "^2.0.0",
3050
+ "micromark-util-types": "^2.0.0"
3051
+ },
3052
+ "funding": {
3053
+ "type": "opencollective",
3054
+ "url": "https://opencollective.com/unified"
3055
+ }
3056
+ },
3057
+ "node_modules/micromark-extension-gfm-table": {
3058
+ "version": "2.1.1",
3059
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz",
3060
+ "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==",
3061
+ "license": "MIT",
3062
+ "dependencies": {
3063
+ "devlop": "^1.0.0",
3064
+ "micromark-factory-space": "^2.0.0",
3065
+ "micromark-util-character": "^2.0.0",
3066
+ "micromark-util-symbol": "^2.0.0",
3067
+ "micromark-util-types": "^2.0.0"
3068
+ },
3069
+ "funding": {
3070
+ "type": "opencollective",
3071
+ "url": "https://opencollective.com/unified"
3072
+ }
3073
+ },
3074
+ "node_modules/micromark-extension-gfm-tagfilter": {
3075
+ "version": "2.0.0",
3076
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz",
3077
+ "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==",
3078
+ "license": "MIT",
3079
+ "dependencies": {
3080
+ "micromark-util-types": "^2.0.0"
3081
+ },
3082
+ "funding": {
3083
+ "type": "opencollective",
3084
+ "url": "https://opencollective.com/unified"
3085
+ }
3086
+ },
3087
+ "node_modules/micromark-extension-gfm-task-list-item": {
3088
+ "version": "2.1.0",
3089
+ "resolved": "https://registry.npmjs.org/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz",
3090
+ "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==",
3091
+ "license": "MIT",
3092
+ "dependencies": {
3093
+ "devlop": "^1.0.0",
3094
+ "micromark-factory-space": "^2.0.0",
3095
+ "micromark-util-character": "^2.0.0",
3096
+ "micromark-util-symbol": "^2.0.0",
3097
+ "micromark-util-types": "^2.0.0"
3098
+ },
3099
+ "funding": {
3100
+ "type": "opencollective",
3101
+ "url": "https://opencollective.com/unified"
3102
+ }
3103
+ },
3104
+ "node_modules/micromark-factory-destination": {
3105
+ "version": "2.0.1",
3106
+ "resolved": "https://registry.npmjs.org/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz",
3107
+ "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==",
3108
+ "funding": [
3109
+ {
3110
+ "type": "GitHub Sponsors",
3111
+ "url": "https://github.com/sponsors/unifiedjs"
3112
+ },
3113
+ {
3114
+ "type": "OpenCollective",
3115
+ "url": "https://opencollective.com/unified"
3116
+ }
3117
+ ],
3118
+ "license": "MIT",
3119
+ "dependencies": {
3120
+ "micromark-util-character": "^2.0.0",
3121
+ "micromark-util-symbol": "^2.0.0",
3122
+ "micromark-util-types": "^2.0.0"
3123
+ }
3124
+ },
3125
+ "node_modules/micromark-factory-label": {
3126
+ "version": "2.0.1",
3127
+ "resolved": "https://registry.npmjs.org/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz",
3128
+ "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==",
3129
+ "funding": [
3130
+ {
3131
+ "type": "GitHub Sponsors",
3132
+ "url": "https://github.com/sponsors/unifiedjs"
3133
+ },
3134
+ {
3135
+ "type": "OpenCollective",
3136
+ "url": "https://opencollective.com/unified"
3137
+ }
3138
+ ],
3139
+ "license": "MIT",
3140
+ "dependencies": {
3141
+ "devlop": "^1.0.0",
3142
+ "micromark-util-character": "^2.0.0",
3143
+ "micromark-util-symbol": "^2.0.0",
3144
+ "micromark-util-types": "^2.0.0"
3145
+ }
3146
+ },
3147
+ "node_modules/micromark-factory-space": {
3148
+ "version": "2.0.1",
3149
+ "resolved": "https://registry.npmjs.org/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz",
3150
+ "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==",
3151
+ "funding": [
3152
+ {
3153
+ "type": "GitHub Sponsors",
3154
+ "url": "https://github.com/sponsors/unifiedjs"
3155
+ },
3156
+ {
3157
+ "type": "OpenCollective",
3158
+ "url": "https://opencollective.com/unified"
3159
+ }
3160
+ ],
3161
+ "license": "MIT",
3162
+ "dependencies": {
3163
+ "micromark-util-character": "^2.0.0",
3164
+ "micromark-util-types": "^2.0.0"
3165
+ }
3166
+ },
3167
+ "node_modules/micromark-factory-title": {
3168
+ "version": "2.0.1",
3169
+ "resolved": "https://registry.npmjs.org/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz",
3170
+ "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==",
3171
+ "funding": [
3172
+ {
3173
+ "type": "GitHub Sponsors",
3174
+ "url": "https://github.com/sponsors/unifiedjs"
3175
+ },
3176
+ {
3177
+ "type": "OpenCollective",
3178
+ "url": "https://opencollective.com/unified"
3179
+ }
3180
+ ],
3181
+ "license": "MIT",
3182
+ "dependencies": {
3183
+ "micromark-factory-space": "^2.0.0",
3184
+ "micromark-util-character": "^2.0.0",
3185
+ "micromark-util-symbol": "^2.0.0",
3186
+ "micromark-util-types": "^2.0.0"
3187
+ }
3188
+ },
3189
+ "node_modules/micromark-factory-whitespace": {
3190
+ "version": "2.0.1",
3191
+ "resolved": "https://registry.npmjs.org/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz",
3192
+ "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==",
3193
+ "funding": [
3194
+ {
3195
+ "type": "GitHub Sponsors",
3196
+ "url": "https://github.com/sponsors/unifiedjs"
3197
+ },
3198
+ {
3199
+ "type": "OpenCollective",
3200
+ "url": "https://opencollective.com/unified"
3201
+ }
3202
+ ],
3203
+ "license": "MIT",
3204
+ "dependencies": {
3205
+ "micromark-factory-space": "^2.0.0",
3206
+ "micromark-util-character": "^2.0.0",
3207
+ "micromark-util-symbol": "^2.0.0",
3208
+ "micromark-util-types": "^2.0.0"
3209
+ }
3210
+ },
3211
+ "node_modules/micromark-util-character": {
3212
+ "version": "2.1.1",
3213
+ "resolved": "https://registry.npmjs.org/micromark-util-character/-/micromark-util-character-2.1.1.tgz",
3214
+ "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==",
3215
+ "funding": [
3216
+ {
3217
+ "type": "GitHub Sponsors",
3218
+ "url": "https://github.com/sponsors/unifiedjs"
3219
+ },
3220
+ {
3221
+ "type": "OpenCollective",
3222
+ "url": "https://opencollective.com/unified"
3223
+ }
3224
+ ],
3225
+ "license": "MIT",
3226
+ "dependencies": {
3227
+ "micromark-util-symbol": "^2.0.0",
3228
+ "micromark-util-types": "^2.0.0"
3229
+ }
3230
+ },
3231
+ "node_modules/micromark-util-chunked": {
3232
+ "version": "2.0.1",
3233
+ "resolved": "https://registry.npmjs.org/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz",
3234
+ "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==",
3235
+ "funding": [
3236
+ {
3237
+ "type": "GitHub Sponsors",
3238
+ "url": "https://github.com/sponsors/unifiedjs"
3239
+ },
3240
+ {
3241
+ "type": "OpenCollective",
3242
+ "url": "https://opencollective.com/unified"
3243
+ }
3244
+ ],
3245
+ "license": "MIT",
3246
+ "dependencies": {
3247
+ "micromark-util-symbol": "^2.0.0"
3248
+ }
3249
+ },
3250
+ "node_modules/micromark-util-classify-character": {
3251
+ "version": "2.0.1",
3252
+ "resolved": "https://registry.npmjs.org/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz",
3253
+ "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==",
3254
+ "funding": [
3255
+ {
3256
+ "type": "GitHub Sponsors",
3257
+ "url": "https://github.com/sponsors/unifiedjs"
3258
+ },
3259
+ {
3260
+ "type": "OpenCollective",
3261
+ "url": "https://opencollective.com/unified"
3262
+ }
3263
+ ],
3264
+ "license": "MIT",
3265
+ "dependencies": {
3266
+ "micromark-util-character": "^2.0.0",
3267
+ "micromark-util-symbol": "^2.0.0",
3268
+ "micromark-util-types": "^2.0.0"
3269
+ }
3270
+ },
3271
+ "node_modules/micromark-util-combine-extensions": {
3272
+ "version": "2.0.1",
3273
+ "resolved": "https://registry.npmjs.org/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz",
3274
+ "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==",
3275
+ "funding": [
3276
+ {
3277
+ "type": "GitHub Sponsors",
3278
+ "url": "https://github.com/sponsors/unifiedjs"
3279
+ },
3280
+ {
3281
+ "type": "OpenCollective",
3282
+ "url": "https://opencollective.com/unified"
3283
+ }
3284
+ ],
3285
+ "license": "MIT",
3286
+ "dependencies": {
3287
+ "micromark-util-chunked": "^2.0.0",
3288
+ "micromark-util-types": "^2.0.0"
3289
+ }
3290
+ },
3291
+ "node_modules/micromark-util-decode-numeric-character-reference": {
3292
+ "version": "2.0.2",
3293
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz",
3294
+ "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==",
3295
+ "funding": [
3296
+ {
3297
+ "type": "GitHub Sponsors",
3298
+ "url": "https://github.com/sponsors/unifiedjs"
3299
+ },
3300
+ {
3301
+ "type": "OpenCollective",
3302
+ "url": "https://opencollective.com/unified"
3303
+ }
3304
+ ],
3305
+ "license": "MIT",
3306
+ "dependencies": {
3307
+ "micromark-util-symbol": "^2.0.0"
3308
+ }
3309
+ },
3310
+ "node_modules/micromark-util-decode-string": {
3311
+ "version": "2.0.1",
3312
+ "resolved": "https://registry.npmjs.org/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz",
3313
+ "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==",
3314
+ "funding": [
3315
+ {
3316
+ "type": "GitHub Sponsors",
3317
+ "url": "https://github.com/sponsors/unifiedjs"
3318
+ },
3319
+ {
3320
+ "type": "OpenCollective",
3321
+ "url": "https://opencollective.com/unified"
3322
+ }
3323
+ ],
3324
+ "license": "MIT",
3325
+ "dependencies": {
3326
+ "decode-named-character-reference": "^1.0.0",
3327
+ "micromark-util-character": "^2.0.0",
3328
+ "micromark-util-decode-numeric-character-reference": "^2.0.0",
3329
+ "micromark-util-symbol": "^2.0.0"
3330
+ }
3331
+ },
3332
+ "node_modules/micromark-util-encode": {
3333
+ "version": "2.0.1",
3334
+ "resolved": "https://registry.npmjs.org/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz",
3335
+ "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==",
3336
+ "funding": [
3337
+ {
3338
+ "type": "GitHub Sponsors",
3339
+ "url": "https://github.com/sponsors/unifiedjs"
3340
+ },
3341
+ {
3342
+ "type": "OpenCollective",
3343
+ "url": "https://opencollective.com/unified"
3344
+ }
3345
+ ],
3346
+ "license": "MIT"
3347
+ },
3348
+ "node_modules/micromark-util-html-tag-name": {
3349
+ "version": "2.0.1",
3350
+ "resolved": "https://registry.npmjs.org/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz",
3351
+ "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==",
3352
+ "funding": [
3353
+ {
3354
+ "type": "GitHub Sponsors",
3355
+ "url": "https://github.com/sponsors/unifiedjs"
3356
+ },
3357
+ {
3358
+ "type": "OpenCollective",
3359
+ "url": "https://opencollective.com/unified"
3360
+ }
3361
+ ],
3362
+ "license": "MIT"
3363
+ },
3364
+ "node_modules/micromark-util-normalize-identifier": {
3365
+ "version": "2.0.1",
3366
+ "resolved": "https://registry.npmjs.org/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz",
3367
+ "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==",
3368
+ "funding": [
3369
+ {
3370
+ "type": "GitHub Sponsors",
3371
+ "url": "https://github.com/sponsors/unifiedjs"
3372
+ },
3373
+ {
3374
+ "type": "OpenCollective",
3375
+ "url": "https://opencollective.com/unified"
3376
+ }
3377
+ ],
3378
+ "license": "MIT",
3379
+ "dependencies": {
3380
+ "micromark-util-symbol": "^2.0.0"
3381
+ }
3382
+ },
3383
+ "node_modules/micromark-util-resolve-all": {
3384
+ "version": "2.0.1",
3385
+ "resolved": "https://registry.npmjs.org/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz",
3386
+ "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==",
3387
+ "funding": [
3388
+ {
3389
+ "type": "GitHub Sponsors",
3390
+ "url": "https://github.com/sponsors/unifiedjs"
3391
+ },
3392
+ {
3393
+ "type": "OpenCollective",
3394
+ "url": "https://opencollective.com/unified"
3395
+ }
3396
+ ],
3397
+ "license": "MIT",
3398
+ "dependencies": {
3399
+ "micromark-util-types": "^2.0.0"
3400
+ }
3401
+ },
3402
+ "node_modules/micromark-util-sanitize-uri": {
3403
+ "version": "2.0.1",
3404
+ "resolved": "https://registry.npmjs.org/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz",
3405
+ "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==",
3406
+ "funding": [
3407
+ {
3408
+ "type": "GitHub Sponsors",
3409
+ "url": "https://github.com/sponsors/unifiedjs"
3410
+ },
3411
+ {
3412
+ "type": "OpenCollective",
3413
+ "url": "https://opencollective.com/unified"
3414
+ }
3415
+ ],
3416
+ "license": "MIT",
3417
+ "dependencies": {
3418
+ "micromark-util-character": "^2.0.0",
3419
+ "micromark-util-encode": "^2.0.0",
3420
+ "micromark-util-symbol": "^2.0.0"
3421
+ }
3422
+ },
3423
+ "node_modules/micromark-util-subtokenize": {
3424
+ "version": "2.1.0",
3425
+ "resolved": "https://registry.npmjs.org/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz",
3426
+ "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==",
3427
+ "funding": [
3428
+ {
3429
+ "type": "GitHub Sponsors",
3430
+ "url": "https://github.com/sponsors/unifiedjs"
3431
+ },
3432
+ {
3433
+ "type": "OpenCollective",
3434
+ "url": "https://opencollective.com/unified"
3435
+ }
3436
+ ],
3437
+ "license": "MIT",
3438
+ "dependencies": {
3439
+ "devlop": "^1.0.0",
3440
+ "micromark-util-chunked": "^2.0.0",
3441
+ "micromark-util-symbol": "^2.0.0",
3442
+ "micromark-util-types": "^2.0.0"
3443
+ }
3444
+ },
3445
+ "node_modules/micromark-util-symbol": {
3446
+ "version": "2.0.1",
3447
+ "resolved": "https://registry.npmjs.org/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz",
3448
+ "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==",
3449
+ "funding": [
3450
+ {
3451
+ "type": "GitHub Sponsors",
3452
+ "url": "https://github.com/sponsors/unifiedjs"
3453
+ },
3454
+ {
3455
+ "type": "OpenCollective",
3456
+ "url": "https://opencollective.com/unified"
3457
+ }
3458
+ ],
3459
+ "license": "MIT"
3460
+ },
3461
+ "node_modules/micromark-util-types": {
3462
+ "version": "2.0.2",
3463
+ "resolved": "https://registry.npmjs.org/micromark-util-types/-/micromark-util-types-2.0.2.tgz",
3464
+ "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==",
3465
+ "funding": [
3466
+ {
3467
+ "type": "GitHub Sponsors",
3468
+ "url": "https://github.com/sponsors/unifiedjs"
3469
+ },
3470
+ {
3471
+ "type": "OpenCollective",
3472
+ "url": "https://opencollective.com/unified"
3473
+ }
3474
+ ],
3475
+ "license": "MIT"
3476
+ },
3477
+ "node_modules/minimatch": {
3478
+ "version": "3.1.2",
3479
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
3480
+ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
3481
+ "dev": true,
3482
+ "license": "ISC",
3483
+ "dependencies": {
3484
+ "brace-expansion": "^1.1.7"
3485
+ },
3486
+ "engines": {
3487
+ "node": "*"
3488
+ }
3489
+ },
3490
+ "node_modules/ms": {
3491
+ "version": "2.1.3",
3492
+ "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
3493
+ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
3494
+ "license": "MIT"
3495
+ },
3496
+ "node_modules/nanoid": {
3497
+ "version": "3.3.11",
3498
+ "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
3499
+ "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==",
3500
+ "dev": true,
3501
+ "funding": [
3502
+ {
3503
+ "type": "github",
3504
+ "url": "https://github.com/sponsors/ai"
3505
+ }
3506
+ ],
3507
+ "license": "MIT",
3508
+ "bin": {
3509
+ "nanoid": "bin/nanoid.cjs"
3510
+ },
3511
+ "engines": {
3512
+ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1"
3513
+ }
3514
+ },
3515
+ "node_modules/natural-compare": {
3516
+ "version": "1.4.0",
3517
+ "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
3518
+ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
3519
+ "dev": true,
3520
+ "license": "MIT"
3521
+ },
3522
+ "node_modules/node-releases": {
3523
+ "version": "2.0.19",
3524
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz",
3525
+ "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==",
3526
+ "dev": true,
3527
+ "license": "MIT"
3528
+ },
3529
+ "node_modules/normalize-range": {
3530
+ "version": "0.1.2",
3531
+ "resolved": "https://registry.npmjs.org/normalize-range/-/normalize-range-0.1.2.tgz",
3532
+ "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==",
3533
+ "dev": true,
3534
+ "license": "MIT",
3535
+ "engines": {
3536
+ "node": ">=0.10.0"
3537
+ }
3538
+ },
3539
+ "node_modules/optionator": {
3540
+ "version": "0.9.4",
3541
+ "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz",
3542
+ "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==",
3543
+ "dev": true,
3544
+ "license": "MIT",
3545
+ "dependencies": {
3546
+ "deep-is": "^0.1.3",
3547
+ "fast-levenshtein": "^2.0.6",
3548
+ "levn": "^0.4.1",
3549
+ "prelude-ls": "^1.2.1",
3550
+ "type-check": "^0.4.0",
3551
+ "word-wrap": "^1.2.5"
3552
+ },
3553
+ "engines": {
3554
+ "node": ">= 0.8.0"
3555
+ }
3556
+ },
3557
+ "node_modules/p-limit": {
3558
+ "version": "3.1.0",
3559
+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
3560
+ "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
3561
+ "dev": true,
3562
+ "license": "MIT",
3563
+ "dependencies": {
3564
+ "yocto-queue": "^0.1.0"
3565
+ },
3566
+ "engines": {
3567
+ "node": ">=10"
3568
+ },
3569
+ "funding": {
3570
+ "url": "https://github.com/sponsors/sindresorhus"
3571
+ }
3572
+ },
3573
+ "node_modules/p-locate": {
3574
+ "version": "5.0.0",
3575
+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
3576
+ "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
3577
+ "dev": true,
3578
  "license": "MIT",
3579
  "dependencies": {
3580
  "p-limit": "^3.0.2"
 
3599
  "node": ">=6"
3600
  }
3601
  },
3602
+ "node_modules/parse-entities": {
3603
+ "version": "4.0.2",
3604
+ "resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.2.tgz",
3605
+ "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==",
3606
+ "license": "MIT",
3607
+ "dependencies": {
3608
+ "@types/unist": "^2.0.0",
3609
+ "character-entities-legacy": "^3.0.0",
3610
+ "character-reference-invalid": "^2.0.0",
3611
+ "decode-named-character-reference": "^1.0.0",
3612
+ "is-alphanumerical": "^2.0.0",
3613
+ "is-decimal": "^2.0.0",
3614
+ "is-hexadecimal": "^2.0.0"
3615
+ },
3616
+ "funding": {
3617
+ "type": "github",
3618
+ "url": "https://github.com/sponsors/wooorm"
3619
+ }
3620
+ },
3621
+ "node_modules/parse-entities/node_modules/@types/unist": {
3622
+ "version": "2.0.11",
3623
+ "resolved": "https://registry.npmjs.org/@types/unist/-/unist-2.0.11.tgz",
3624
+ "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==",
3625
+ "license": "MIT"
3626
+ },
3627
  "node_modules/path-exists": {
3628
  "version": "4.0.0",
3629
  "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
 
3710
  "node": ">= 0.8.0"
3711
  }
3712
  },
3713
+ "node_modules/property-information": {
3714
+ "version": "7.1.0",
3715
+ "resolved": "https://registry.npmjs.org/property-information/-/property-information-7.1.0.tgz",
3716
+ "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==",
3717
+ "license": "MIT",
3718
+ "funding": {
3719
+ "type": "github",
3720
+ "url": "https://github.com/sponsors/wooorm"
3721
+ }
3722
+ },
3723
  "node_modules/punycode": {
3724
  "version": "2.3.1",
3725
  "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
 
3751
  "react": "^19.1.0"
3752
  }
3753
  },
3754
+ "node_modules/react-markdown": {
3755
+ "version": "10.1.0",
3756
+ "resolved": "https://registry.npmjs.org/react-markdown/-/react-markdown-10.1.0.tgz",
3757
+ "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==",
3758
+ "license": "MIT",
3759
+ "dependencies": {
3760
+ "@types/hast": "^3.0.0",
3761
+ "@types/mdast": "^4.0.0",
3762
+ "devlop": "^1.0.0",
3763
+ "hast-util-to-jsx-runtime": "^2.0.0",
3764
+ "html-url-attributes": "^3.0.0",
3765
+ "mdast-util-to-hast": "^13.0.0",
3766
+ "remark-parse": "^11.0.0",
3767
+ "remark-rehype": "^11.0.0",
3768
+ "unified": "^11.0.0",
3769
+ "unist-util-visit": "^5.0.0",
3770
+ "vfile": "^6.0.0"
3771
+ },
3772
+ "funding": {
3773
+ "type": "opencollective",
3774
+ "url": "https://opencollective.com/unified"
3775
+ },
3776
+ "peerDependencies": {
3777
+ "@types/react": ">=18",
3778
+ "react": ">=18"
3779
+ }
3780
+ },
3781
  "node_modules/react-refresh": {
3782
  "version": "0.17.0",
3783
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
 
3788
  "node": ">=0.10.0"
3789
  }
3790
  },
3791
+ "node_modules/remark-gfm": {
3792
+ "version": "4.0.1",
3793
+ "resolved": "https://registry.npmjs.org/remark-gfm/-/remark-gfm-4.0.1.tgz",
3794
+ "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==",
3795
+ "license": "MIT",
3796
+ "dependencies": {
3797
+ "@types/mdast": "^4.0.0",
3798
+ "mdast-util-gfm": "^3.0.0",
3799
+ "micromark-extension-gfm": "^3.0.0",
3800
+ "remark-parse": "^11.0.0",
3801
+ "remark-stringify": "^11.0.0",
3802
+ "unified": "^11.0.0"
3803
+ },
3804
+ "funding": {
3805
+ "type": "opencollective",
3806
+ "url": "https://opencollective.com/unified"
3807
+ }
3808
+ },
3809
+ "node_modules/remark-parse": {
3810
+ "version": "11.0.0",
3811
+ "resolved": "https://registry.npmjs.org/remark-parse/-/remark-parse-11.0.0.tgz",
3812
+ "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==",
3813
+ "license": "MIT",
3814
+ "dependencies": {
3815
+ "@types/mdast": "^4.0.0",
3816
+ "mdast-util-from-markdown": "^2.0.0",
3817
+ "micromark-util-types": "^2.0.0",
3818
+ "unified": "^11.0.0"
3819
+ },
3820
+ "funding": {
3821
+ "type": "opencollective",
3822
+ "url": "https://opencollective.com/unified"
3823
+ }
3824
+ },
3825
+ "node_modules/remark-rehype": {
3826
+ "version": "11.1.2",
3827
+ "resolved": "https://registry.npmjs.org/remark-rehype/-/remark-rehype-11.1.2.tgz",
3828
+ "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==",
3829
+ "license": "MIT",
3830
+ "dependencies": {
3831
+ "@types/hast": "^3.0.0",
3832
+ "@types/mdast": "^4.0.0",
3833
+ "mdast-util-to-hast": "^13.0.0",
3834
+ "unified": "^11.0.0",
3835
+ "vfile": "^6.0.0"
3836
+ },
3837
+ "funding": {
3838
+ "type": "opencollective",
3839
+ "url": "https://opencollective.com/unified"
3840
+ }
3841
+ },
3842
+ "node_modules/remark-stringify": {
3843
+ "version": "11.0.0",
3844
+ "resolved": "https://registry.npmjs.org/remark-stringify/-/remark-stringify-11.0.0.tgz",
3845
+ "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==",
3846
+ "license": "MIT",
3847
+ "dependencies": {
3848
+ "@types/mdast": "^4.0.0",
3849
+ "mdast-util-to-markdown": "^2.0.0",
3850
+ "unified": "^11.0.0"
3851
+ },
3852
+ "funding": {
3853
+ "type": "opencollective",
3854
+ "url": "https://opencollective.com/unified"
3855
+ }
3856
+ },
3857
  "node_modules/resolve-from": {
3858
  "version": "4.0.0",
3859
  "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
 
3953
  "node": ">=0.10.0"
3954
  }
3955
  },
3956
+ "node_modules/space-separated-tokens": {
3957
+ "version": "2.0.2",
3958
+ "resolved": "https://registry.npmjs.org/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz",
3959
+ "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==",
3960
+ "license": "MIT",
3961
+ "funding": {
3962
+ "type": "github",
3963
+ "url": "https://github.com/sponsors/wooorm"
3964
+ }
3965
+ },
3966
+ "node_modules/stringify-entities": {
3967
+ "version": "4.0.4",
3968
+ "resolved": "https://registry.npmjs.org/stringify-entities/-/stringify-entities-4.0.4.tgz",
3969
+ "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==",
3970
+ "license": "MIT",
3971
+ "dependencies": {
3972
+ "character-entities-html4": "^2.0.0",
3973
+ "character-entities-legacy": "^3.0.0"
3974
+ },
3975
+ "funding": {
3976
+ "type": "github",
3977
+ "url": "https://github.com/sponsors/wooorm"
3978
+ }
3979
+ },
3980
  "node_modules/strip-json-comments": {
3981
  "version": "3.1.1",
3982
  "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
 
3990
  "url": "https://github.com/sponsors/sindresorhus"
3991
  }
3992
  },
3993
+ "node_modules/style-to-js": {
3994
+ "version": "1.1.17",
3995
+ "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.17.tgz",
3996
+ "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==",
3997
+ "license": "MIT",
3998
+ "dependencies": {
3999
+ "style-to-object": "1.0.9"
4000
+ }
4001
+ },
4002
+ "node_modules/style-to-object": {
4003
+ "version": "1.0.9",
4004
+ "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.9.tgz",
4005
+ "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==",
4006
+ "license": "MIT",
4007
+ "dependencies": {
4008
+ "inline-style-parser": "0.2.4"
4009
+ }
4010
+ },
4011
  "node_modules/supports-color": {
4012
  "version": "7.2.0",
4013
  "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
 
4045
  "url": "https://github.com/sponsors/SuperchupuDev"
4046
  }
4047
  },
4048
+ "node_modules/trim-lines": {
4049
+ "version": "3.0.1",
4050
+ "resolved": "https://registry.npmjs.org/trim-lines/-/trim-lines-3.0.1.tgz",
4051
+ "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==",
4052
+ "license": "MIT",
4053
+ "funding": {
4054
+ "type": "github",
4055
+ "url": "https://github.com/sponsors/wooorm"
4056
+ }
4057
+ },
4058
+ "node_modules/trough": {
4059
+ "version": "2.2.0",
4060
+ "resolved": "https://registry.npmjs.org/trough/-/trough-2.2.0.tgz",
4061
+ "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==",
4062
+ "license": "MIT",
4063
+ "funding": {
4064
+ "type": "github",
4065
+ "url": "https://github.com/sponsors/wooorm"
4066
+ }
4067
+ },
4068
  "node_modules/type-check": {
4069
  "version": "0.4.0",
4070
  "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
 
4078
  "node": ">= 0.8.0"
4079
  }
4080
  },
4081
+ "node_modules/unified": {
4082
+ "version": "11.0.5",
4083
+ "resolved": "https://registry.npmjs.org/unified/-/unified-11.0.5.tgz",
4084
+ "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==",
4085
+ "license": "MIT",
4086
+ "dependencies": {
4087
+ "@types/unist": "^3.0.0",
4088
+ "bail": "^2.0.0",
4089
+ "devlop": "^1.0.0",
4090
+ "extend": "^3.0.0",
4091
+ "is-plain-obj": "^4.0.0",
4092
+ "trough": "^2.0.0",
4093
+ "vfile": "^6.0.0"
4094
+ },
4095
+ "funding": {
4096
+ "type": "opencollective",
4097
+ "url": "https://opencollective.com/unified"
4098
+ }
4099
+ },
4100
+ "node_modules/unist-util-is": {
4101
+ "version": "6.0.0",
4102
+ "resolved": "https://registry.npmjs.org/unist-util-is/-/unist-util-is-6.0.0.tgz",
4103
+ "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==",
4104
+ "license": "MIT",
4105
+ "dependencies": {
4106
+ "@types/unist": "^3.0.0"
4107
+ },
4108
+ "funding": {
4109
+ "type": "opencollective",
4110
+ "url": "https://opencollective.com/unified"
4111
+ }
4112
+ },
4113
+ "node_modules/unist-util-position": {
4114
+ "version": "5.0.0",
4115
+ "resolved": "https://registry.npmjs.org/unist-util-position/-/unist-util-position-5.0.0.tgz",
4116
+ "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==",
4117
+ "license": "MIT",
4118
+ "dependencies": {
4119
+ "@types/unist": "^3.0.0"
4120
+ },
4121
+ "funding": {
4122
+ "type": "opencollective",
4123
+ "url": "https://opencollective.com/unified"
4124
+ }
4125
+ },
4126
+ "node_modules/unist-util-stringify-position": {
4127
+ "version": "4.0.0",
4128
+ "resolved": "https://registry.npmjs.org/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz",
4129
+ "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==",
4130
+ "license": "MIT",
4131
+ "dependencies": {
4132
+ "@types/unist": "^3.0.0"
4133
+ },
4134
+ "funding": {
4135
+ "type": "opencollective",
4136
+ "url": "https://opencollective.com/unified"
4137
+ }
4138
+ },
4139
+ "node_modules/unist-util-visit": {
4140
+ "version": "5.0.0",
4141
+ "resolved": "https://registry.npmjs.org/unist-util-visit/-/unist-util-visit-5.0.0.tgz",
4142
+ "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==",
4143
+ "license": "MIT",
4144
+ "dependencies": {
4145
+ "@types/unist": "^3.0.0",
4146
+ "unist-util-is": "^6.0.0",
4147
+ "unist-util-visit-parents": "^6.0.0"
4148
+ },
4149
+ "funding": {
4150
+ "type": "opencollective",
4151
+ "url": "https://opencollective.com/unified"
4152
+ }
4153
+ },
4154
+ "node_modules/unist-util-visit-parents": {
4155
+ "version": "6.0.1",
4156
+ "resolved": "https://registry.npmjs.org/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz",
4157
+ "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==",
4158
+ "license": "MIT",
4159
+ "dependencies": {
4160
+ "@types/unist": "^3.0.0",
4161
+ "unist-util-is": "^6.0.0"
4162
+ },
4163
+ "funding": {
4164
+ "type": "opencollective",
4165
+ "url": "https://opencollective.com/unified"
4166
+ }
4167
+ },
4168
  "node_modules/update-browserslist-db": {
4169
  "version": "1.1.3",
4170
  "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz",
 
4206
  "punycode": "^2.1.0"
4207
  }
4208
  },
4209
+ "node_modules/vfile": {
4210
+ "version": "6.0.3",
4211
+ "resolved": "https://registry.npmjs.org/vfile/-/vfile-6.0.3.tgz",
4212
+ "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==",
4213
+ "license": "MIT",
4214
+ "dependencies": {
4215
+ "@types/unist": "^3.0.0",
4216
+ "vfile-message": "^4.0.0"
4217
+ },
4218
+ "funding": {
4219
+ "type": "opencollective",
4220
+ "url": "https://opencollective.com/unified"
4221
+ }
4222
+ },
4223
+ "node_modules/vfile-message": {
4224
+ "version": "4.0.2",
4225
+ "resolved": "https://registry.npmjs.org/vfile-message/-/vfile-message-4.0.2.tgz",
4226
+ "integrity": "sha512-jRDZ1IMLttGj41KcZvlrYAaI3CfqpLpfpf+Mfig13viT6NKvRzWZ+lXz0Y5D60w6uJIBAOGq9mSHf0gktF0duw==",
4227
+ "license": "MIT",
4228
+ "dependencies": {
4229
+ "@types/unist": "^3.0.0",
4230
+ "unist-util-stringify-position": "^4.0.0"
4231
+ },
4232
+ "funding": {
4233
+ "type": "opencollective",
4234
+ "url": "https://opencollective.com/unified"
4235
+ }
4236
+ },
4237
  "node_modules/vite": {
4238
  "version": "6.3.5",
4239
  "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
 
4354
  "funding": {
4355
  "url": "https://github.com/sponsors/sindresorhus"
4356
  }
4357
+ },
4358
+ "node_modules/zwitch": {
4359
+ "version": "2.0.4",
4360
+ "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz",
4361
+ "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==",
4362
+ "license": "MIT",
4363
+ "funding": {
4364
+ "type": "github",
4365
+ "url": "https://github.com/sponsors/wooorm"
4366
+ }
4367
  }
4368
  }
4369
  }
package.json CHANGED
@@ -13,7 +13,9 @@
13
  "date-fns": "^4.1.0",
14
  "lucide-react": "^0.525.0",
15
  "react": "^19.0.0",
16
- "react-dom": "^19.0.0"
 
 
17
  },
18
  "devDependencies": {
19
  "@eslint/js": "^9.22.0",
 
13
  "date-fns": "^4.1.0",
14
  "lucide-react": "^0.525.0",
15
  "react": "^19.0.0",
16
+ "react-dom": "^19.0.0",
17
+ "react-markdown": "^10.1.0",
18
+ "remark-gfm": "^4.0.1"
19
  },
20
  "devDependencies": {
21
  "@eslint/js": "^9.22.0",
src/backend/profile_data.py ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ YOUR_NAME = "Abdullah Khaled"
2
+
3
+ YOUR_GITHUB_USERNAME = "abdullah-khaled0"
4
+
5
+ YOUR_VERCEL_URL = "https://ai-voice-secretary-git-main-abdullah-khaled0s-projects.vercel.app"
6
+
7
+
8
+
9
+ profile_str = """
10
+ Abdullah Khaled | AI Engineer - Data Scientist
11
+
12
+ === PERSONAL INFORMATION ===
13
+ Phone: +201557504902
14
+ WhatsApp: +201557504902
15
16
+ Location: Cairo, Egypt
17
+ Age: 22 years old
18
+ Status: Single
19
+ Open to work in: Remote, On-site, Hybrid
20
+ Military Status: deferred
21
+ Graduated from: Beni Suef University
22
+
23
+ === LEARNING PLATFORMS ===
24
+ LinkedIn: https://linkedin.com/in/abdullah-khaled-0608a9236
25
+ Kaggle: https://kaggle.com/abdullah7aled
26
+ HackerRank: https://www.hackerrank.com/abdullah_7aled
27
+ LeetCode: https://leetcode.com/u/3bdullah_7aled/
28
+ Microsoft Learn: https://learn.microsoft.com/en-us/users/abdullahkhaled-4050/
29
+ Streamlit: https://share.streamlit.io/user/abdullah-khaled0
30
+ Coursera: https://www.coursera.org/user/a417b4d4afc4a0d67abb5bacc39083a5
31
+ 365DataScience: https://learn.365datascience.com/profile/abdullah-khaled-4/
32
+ DataCamp: https://www.datacamp.com/portfolio/3bdullah
33
+
34
+ === SKILLS ===
35
+ • Programming: Python (Pandas, Matplotlib, Numpy, PySpark), R, JavaScript, SQL
36
+ • AI/ML: Scikit-Learn, PyTorch, Tensorflow, Transformers, NLP (Spacy, NLTK), Langchain, LangGraph, Prompt Engineering, Fine-tuning LLMs, RAG, MCP
37
+ • MLOps & Deployment: Flask, FastAPI, MLFlow, DVC, CI/CD, Railway, Docker
38
+ • BI & Visualization Tools: SQL, Tableau, Power BI, Excel, Data warehouse, Statistics, Statistical Analysis, Time Series Analysis, Hypothesis Testing, AB Testing, Web Scraping
39
+ • Cloud & Data Engineering: Azure (ML, AI Services, Databricks), ETL(Airflow, DBT, SSIS)
40
+
41
+ === EXPERIENCE ===
42
+ WorldQuant University - Remote May 2023 - May 2023
43
+ Data Scientist Intern
44
+ • Completed a practical program in data science, focusing on Python, machine learning, and statistical modeling.
45
+ • Applied data analysis and visualization tools to real-world projects, gaining hands-on experience.
46
+
47
+ Software Engineer (Self-Employed) | Nov 2021 – Dec 2022
48
+ • Built and launched 6+ Android applications using Flutter and native technologies
49
+ • Developed full-stack web applications with HTML, CSS, JavaScript, PHP, and SQL
50
+
51
+ === EDUCATION ===
52
+ BSc in Information Systems | Beni Suef University | Oct 2021 – Jul 2025
53
+
54
+ === CERTIFICATIONS ===
55
+ • Deep Learning Specialization – Coursera (Mar 2023)
56
+ • Machine Learning Specialization – Coursera (Feb 2023)
57
+ """
58
+
59
+ REPOS = [
60
+ "AI-Voice-Secretary",
61
+ "Film-Trailer-and-Summary-Generator",
62
+ "Vocaby",
63
+ "YOLO11-Custom-Object-Detection-for-PPE-Detection",
64
+ "Advanced-Retail-Analytics-using-Excel-and-Python",
65
+ "Pix2Pix-Sketch-to-Image-Colorization",
66
+ "PDF-Quiz-Generator-with-AI-and-React",
67
+ "Fine-Tuning-DeepSeek-R1-Distill-Llama-8B-on-Medical-CoT-Dataset",
68
+ "Hotels-AI-Agent",
69
+ "Fine-Tuning-Llama-2-Using-QLoRA",
70
+ "Customer-Segmentation",
71
+ "AI-Powered-Search-and-Recommendation-System",
72
+ "Exam-Generator",
73
+ "Walmart-Analytics",
74
+ "ts-forecasting-with-prophet",
75
+ "Credit-Fraud-Detector",
76
+ "Supply-Chain-Analysis-using-R",
77
+ "A-B-Testing-with-Cookie-Cats-mobile-game-dataset",
78
+ "ETL-Project-with-SSIS-and-PowerBI",
79
+ "ELT-Pipeline-with-Airflow-DBT-Soda-Snowflake"
80
+ ]
src/backend/voice_assistant.py CHANGED
@@ -24,6 +24,7 @@ import requests
24
  from langchain.docstore.document import Document
25
  from pydantic import BaseModel
26
  from typing import List
 
27
 
28
 
29
  # Set up logging
@@ -39,7 +40,7 @@ app = FastAPI()
39
  # Add CORS middleware
40
  app.add_middleware(
41
  CORSMiddleware,
42
- allow_origins=["https://ai-voice-secretary-git-main-abdullah-khaled0s-projects.vercel.app"],
43
  allow_credentials=True,
44
  allow_methods=["GET", "POST", "OPTIONS"],
45
  allow_headers=["Content-Type", "Authorization", "Accept", "X-Requested-With"],
@@ -62,85 +63,12 @@ except Exception as e:
62
  raise Exception("Initialization of language model or embeddings failed")
63
 
64
  # GitHub API setup
65
- GITHUB_USERNAME = "abdullah-khaled0"
66
  GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # Add your GitHub Personal Access Token in .env
67
  if not GITHUB_TOKEN:
68
  logger.warning("GITHUB_TOKEN not found in .env. API requests may be rate-limited.")
69
 
70
- REPOS = [
71
- "AI-Voice-Secretary",
72
- "Film-Trailer-and-Summary-Generator",
73
- "Vocaby",
74
- "YOLO11-Custom-Object-Detection-for-PPE-Detection",
75
- "Advanced-Retail-Analytics-using-Excel-and-Python",
76
- "Pix2Pix-Sketch-to-Image-Colorization",
77
- "PDF-Quiz-Generator-with-AI-and-React",
78
- "Fine-Tuning-DeepSeek-R1-Distill-Llama-8B-on-Medical-CoT-Dataset",
79
- "Hotels-AI-Agent",
80
- "Fine-Tuning-Llama-2-Using-QLoRA",
81
- "Customer-Segmentation",
82
- "AI-Powered-Search-and-Recommendation-System",
83
- "Exam-Generator",
84
- "Walmart-Analytics",
85
- "ts-forecasting-with-prophet",
86
- "Credit-Fraud-Detector",
87
- "Supply-Chain-Analysis-using-R",
88
- "A-B-Testing-with-Cookie-Cats-mobile-game-dataset",
89
- "ETL-Project-with-SSIS-and-PowerBI",
90
- "ELT-Pipeline-with-Airflow-DBT-Soda-Snowflake"
91
- ]
92
-
93
- # Profile information
94
- profile_str = """
95
- Abdullah Khaled | AI Engineer - Data Scientist
96
-
97
- === PERSONAL INFORMATION ===
98
- Phone: +201557504902
99
- WhatsApp: +201557504902
100
101
- Location: Cairo, Egypt
102
- Age: 22 years old
103
- Status: Single
104
- Open to work in: Remote, On-site, Hybrid
105
- Military Status: deferred
106
- Graduated from: Beni Suef University
107
-
108
-
109
- === LEARNING PLATFORMS ===
110
- LinkedIn: https://linkedin.com/in/abdullah-khaled-0608a9236
111
- Kaggle: https://kaggle.com/abdullah7aled
112
- HackerRank: https://www.hackerrank.com/abdullah_7aled
113
- LeetCode: https://leetcode.com/u/3bdullah_7aled/
114
- Microsoft Learn: https://learn.microsoft.com/en-us/users/abdullahkhaled-4050/
115
- Streamlit: https://share.streamlit.io/user/abdullah-khaled0
116
- Coursera: https://www.coursera.org/user/a417b4d4afc4a0d67abb5bacc39083a5
117
- 365DataScience: https://learn.365datascience.com/profile/abdullah-khaled-4/
118
- DataCamp: https://www.datacamp.com/portfolio/3bdullah
119
-
120
- === SKILLS ===
121
- • Programming: Python (Pandas, Matplotlib, Numpy, PySpark), R, JavaScript, SQL
122
- • AI/ML: Scikit-Learn, PyTorch, Tensorflow, Transformers, NLP (Spacy, NLTK), Langchain, LangGraph, Prompt Engineering, Fine-tuning LLMs, RAG, MCP
123
- • MLOps & Deployment: Flask, FastAPI, MLFlow, DVC, CI/CD, Railway, Docker
124
- • BI & Visualization Tools: SQL, Tableau, Power BI, Excel, Data warehouse, Statistics, Statistical Analysis, Time Series Analysis, Hypothesis Testing, AB Testing, Web Scraping
125
- • Cloud & Data Engineering: Azure (ML, AI Services, Databricks), ETL(Airflow, DBT, SSIS)
126
-
127
- === EXPERIENCE ===
128
- WorldQuant University - Remote May 2023 - May 2023
129
- Data Scientist Intern
130
- • Completed a practical program in data science, focusing on Python, machine learning, and statistical modeling.
131
- • Applied data analysis and visualization tools to real-world projects, gaining hands-on experience.
132
-
133
- Software Engineer (Self-Employed) | Nov 2021 – Dec 2022
134
- • Built and launched 6+ Android applications using Flutter and native technologies
135
- • Developed full-stack web applications with HTML, CSS, JavaScript, PHP, and SQL
136
-
137
- === EDUCATION ===
138
- BSc in Information Systems | Beni Suef University | Oct 2021 – Jul 2025
139
-
140
- === CERTIFICATIONS ===
141
- • Deep Learning Specialization – Coursera (Mar 2023)
142
- • Machine Learning Specialization – Coursera (Feb 2023)
143
- """
144
 
145
  # Pydantic model for response structure
146
  class AssistantResponse(BaseModel):
@@ -171,33 +99,36 @@ def fetch_readme(repo_name):
171
  except Exception as e:
172
  logger.error(f"Error fetching README for {repo_name}: {e}", exc_info=True)
173
  return None
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
174
 
175
  # Load and process GitHub READMEs for RAG
176
  def load_documents(query):
177
  try:
178
  directory = "knowledge/indexes/repos"
179
- logger.debug("Loading documents from GitHub")
180
- if not os.path.exists(directory):
181
- logger.info(f"Directory {directory} does not exist, creating and populating with documents")
182
- os.makedirs(directory, exist_ok=True)
183
- documents = []
184
- for repo in REPOS:
185
- doc = fetch_readme(repo)
186
- if doc:
187
- documents.append(doc)
188
- else:
189
- logger.warning(f"Skipping repository {repo} due to fetch failure")
190
-
191
- if not documents:
192
- logger.warning("No documents loaded from GitHub. Proceeding with empty retriever.")
193
- return FAISS.from_texts(texts=["No GitHub READMEs available"], embedding=embeddings).as_retriever()
194
-
195
- text_splitter = RecursiveCharacterTextSplitter(chunk_size=10000, chunk_overlap=500)
196
- splits = text_splitter.split_documents(documents)
197
- vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)
198
- vectorstore.save_local(directory)
199
- logger.info(f"Saved FAISS index to {directory}")
200
-
201
  vectorstore = FAISS.load_local(directory, embeddings, allow_dangerous_deserialization=True)
202
  results = vectorstore.similarity_search(query, k=5)
203
 
@@ -234,7 +165,7 @@ async def process_text(query, websocket: WebSocket = None):
234
  prompt = ChatPromptTemplate([(
235
  "system",
236
  """
237
- You are a professional and courteous AI secretary for Abdullah Khaled. Your role is to provide clear, concise, and polished responses about Abdullah's GitHub projects or his professional profile in JSON format. Structure the response as follows:\n
238
 
239
  {{
240
  "response": "Details about the project or general response if no project is mentioned",
@@ -256,7 +187,7 @@ async def process_text(query, websocket: WebSocket = None):
256
 
257
  Based on the following contexts:
258
 
259
- === Abdullah Profile Information ===\n
260
  {profile}
261
 
262
  === GitHub Project Context ===\n
@@ -266,16 +197,16 @@ async def process_text(query, websocket: WebSocket = None):
266
  {repos}
267
 
268
  \n
269
- Important: My github username is abdullah-khaled0\n
270
 
271
  if the path of media (images or videos) dont have https, make the path url like this:
272
- https://raw.githubusercontent.com/abdullah-khaled0/repo_name/main/the_path_without_https
273
 
274
- Generate the response based on the user query. If the query mentions a specific project (e.g., Film-Trailer-and-Summary-Generator, Vocaby, YOLO11-Custom-Object-Detection-for-PPE-Detection, Pix2Pix-Sketch-to-Image-Colorization), include details from the corresponding GitHub README in `response` and include any media URLs (images or videos) from the README in `media_links`. For queries about Abdullah's skills, experience, education, certifications, or contact info, use the profile information in `response`.
275
 
276
  For the `links` array, include relevant social or platform links (e.g., LinkedIn, Kaggle, HackerRank, LeetCode, Microsoft Learn, Streamlit, Coursera, 365DataScience, DataCamp) only if the query explicitly asks for social media, platforms, or specific platform names (e.g., "LinkedIn", "Kaggle"). For the `personal_info` array, include Gmail and/or Phone details only if the query explicitly asks for contact information (e.g., "email", "phone", "Gmail", "WhatsApp", "personal information"). The `media_links` array should include any media URLs (images or videos) from the GitHub READMEs if relevant to the query; otherwise, keep it empty.
277
 
278
- Answer in a professional, friendly, and articulate manner, as if representing Abdullah to colleagues, clients, or stakeholders. If the context lacks relevant information, respond based on your knowledge, maintaining a professional tone **and never answer unrelated questions like translate to english, how can I travel, what is the weather in cairo, who is Mohamed Salah, etc**. Ensure the response is a valid JSON object conforming to the structure above.
279
  """),
280
  ("user", f"{query}, with media links and project link if available")])
281
 
@@ -296,7 +227,7 @@ async def process_text(query, websocket: WebSocket = None):
296
  )
297
 
298
  # Process with RAG chain
299
- response = rag_chain.invoke({"context": context, "profile": profile_str, "repos": REPOS})
300
 
301
  logger.info(f"Raw response from LLM: {response}")
302
 
 
24
  from langchain.docstore.document import Document
25
  from pydantic import BaseModel
26
  from typing import List
27
+ from .profile_data import profile_str, REPOS, YOUR_NAME, YOUR_GITHUB_USERNAME, YOUR_VERCEL_URL
28
 
29
 
30
  # Set up logging
 
40
  # Add CORS middleware
41
  app.add_middleware(
42
  CORSMiddleware,
43
+ allow_origins=[YOUR_VERCEL_URL],
44
  allow_credentials=True,
45
  allow_methods=["GET", "POST", "OPTIONS"],
46
  allow_headers=["Content-Type", "Authorization", "Accept", "X-Requested-With"],
 
63
  raise Exception("Initialization of language model or embeddings failed")
64
 
65
  # GitHub API setup
66
+ GITHUB_USERNAME = YOUR_GITHUB_USERNAME
67
  GITHUB_TOKEN = os.getenv("GITHUB_TOKEN") # Add your GitHub Personal Access Token in .env
68
  if not GITHUB_TOKEN:
69
  logger.warning("GITHUB_TOKEN not found in .env. API requests may be rate-limited.")
70
 
71
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
72
 
73
  # Pydantic model for response structure
74
  class AssistantResponse(BaseModel):
 
99
  except Exception as e:
100
  logger.error(f"Error fetching README for {repo_name}: {e}", exc_info=True)
101
  return None
102
+
103
+
104
+ directory = "knowledge/indexes/repos"
105
+ logger.debug("Loading documents from GitHub")
106
+ if not os.path.exists(directory):
107
+ logger.info(f"Directory {directory} does not exist, creating and populating with documents")
108
+ os.makedirs(directory, exist_ok=True)
109
+ documents = []
110
+ for repo in REPOS:
111
+ doc = fetch_readme(repo)
112
+ if doc:
113
+ documents.append(doc)
114
+ else:
115
+ logger.warning(f"Skipping repository {repo} due to fetch failure")
116
+
117
+ if not documents:
118
+ logger.warning("No documents loaded from GitHub. Proceeding with empty retriever.")
119
+ vectorstore = FAISS.from_texts(texts=["No GitHub READMEs available"], embedding=embeddings).as_retriever()
120
+
121
+ if documents:
122
+ text_splitter = RecursiveCharacterTextSplitter(chunk_size=5000, chunk_overlap=300)
123
+ splits = text_splitter.split_documents(documents)
124
+ vectorstore = FAISS.from_documents(documents=splits, embedding=embeddings)
125
+ vectorstore.save_local(directory)
126
+ logger.info(f"Saved FAISS index to {directory}")
127
 
128
  # Load and process GitHub READMEs for RAG
129
  def load_documents(query):
130
  try:
131
  directory = "knowledge/indexes/repos"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
132
  vectorstore = FAISS.load_local(directory, embeddings, allow_dangerous_deserialization=True)
133
  results = vectorstore.similarity_search(query, k=5)
134
 
 
165
  prompt = ChatPromptTemplate([(
166
  "system",
167
  """
168
+ You are a professional and courteous AI secretary for {name}. Your role is to provide clear, concise, and polished responses about {name}'s GitHub projects or his professional profile in JSON format. Structure the response as follows:\n
169
 
170
  {{
171
  "response": "Details about the project or general response if no project is mentioned",
 
187
 
188
  Based on the following contexts:
189
 
190
+ === {name} Profile Information ===\n
191
  {profile}
192
 
193
  === GitHub Project Context ===\n
 
197
  {repos}
198
 
199
  \n
200
+ Important: My github username is {github_username}\n
201
 
202
  if the path of media (images or videos) dont have https, make the path url like this:
203
+ https://raw.githubusercontent.com/{github_username}/repo_name/main/the_path_without_https
204
 
205
+ Generate the response based on the user query. If the query mentions a specific project, include details from the corresponding GitHub README in `response` and include any media URLs (images or videos) from the README in `media_links`. For queries about Abdullah's skills, experience, education, certifications, or contact info, use the profile information in `response`.
206
 
207
  For the `links` array, include relevant social or platform links (e.g., LinkedIn, Kaggle, HackerRank, LeetCode, Microsoft Learn, Streamlit, Coursera, 365DataScience, DataCamp) only if the query explicitly asks for social media, platforms, or specific platform names (e.g., "LinkedIn", "Kaggle"). For the `personal_info` array, include Gmail and/or Phone details only if the query explicitly asks for contact information (e.g., "email", "phone", "Gmail", "WhatsApp", "personal information"). The `media_links` array should include any media URLs (images or videos) from the GitHub READMEs if relevant to the query; otherwise, keep it empty.
208
 
209
+ Answer in a professional, friendly, and articulate manner, as if representing {name} to colleagues, clients, or stakeholders. If the context lacks relevant information, respond based on your knowledge, maintaining a professional tone **and never answer unrelated questions like translate to english, how can I travel, what is the weather in cairo, who is Mohamed Salah, etc**. Ensure the response is a valid JSON object conforming to the structure above.
210
  """),
211
  ("user", f"{query}, with media links and project link if available")])
212
 
 
227
  )
228
 
229
  # Process with RAG chain
230
+ response = rag_chain.invoke({"context": context, "profile": profile_str, "repos": REPOS, "github_username": YOUR_GITHUB_USERNAME, "name": YOUR_NAME})
231
 
232
  logger.info(f"Raw response from LLM: {response}")
233
 
src/components/App.jsx CHANGED
@@ -1,12 +1,33 @@
1
- import React from 'react';
2
  import VoiceAssistant from './VoiceAssistant.jsx';
3
  import Footer from './Footer.jsx';
 
 
4
 
5
  const App = () => {
 
6
  return (
7
- <div className="min-h-screen bg-gradient-to-br from-blue-50 to-gray-100 flex flex-col items-center justify-center p-4">
8
- <VoiceAssistant />
9
- <Footer />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  </div>
11
  );
12
  };
 
1
+ import React, { useContext } from 'react';
2
  import VoiceAssistant from './VoiceAssistant.jsx';
3
  import Footer from './Footer.jsx';
4
+ import { ThemeContext } from '../main.jsx';
5
+ import { Sun, Moon } from 'lucide-react';
6
 
7
  const App = () => {
8
+ const { theme, setTheme } = useContext(ThemeContext);
9
  return (
10
+ <div className="min-h-screen bg-gradient-to-br from-blue-50 to-gray-100 dark:from-gray-900 dark:to-gray-800 flex flex-col items-center justify-center p-4 transition-colors duration-500">
11
+ <div className="w-full max-w-5xl flex flex-col gap-4">
12
+ <header className="flex justify-between items-center mb-2">
13
+ <h1 className="text-2xl sm:text-3xl font-bold text-gray-800 dark:text-gray-100">AI Voice Secretary</h1>
14
+ <div className="flex items-center gap-4">
15
+ <button
16
+ onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}
17
+ className="p-2 rounded-full bg-gray-200 dark:bg-gray-700 hover:bg-blue-500 hover:text-white dark:hover:bg-blue-600 transition-colors"
18
+ aria-label="Toggle dark mode"
19
+ >
20
+ {theme === 'dark' ? <Sun className="w-5 h-5" /> : <Moon className="w-5 h-5" />}
21
+ </button>
22
+ <a href="https://github.com/abdullah-khaled0" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium">GitHub</a>
23
+ <a href="https://abdullah-khaled0.github.io/portfolio-react/" target="_blank" rel="noopener noreferrer" className="text-blue-600 dark:text-blue-400 hover:underline text-sm font-medium">Portfolio</a>
24
+ </div>
25
+ </header>
26
+ <main className="flex flex-1 w-full">
27
+ <VoiceAssistant />
28
+ </main>
29
+ <Footer />
30
+ </div>
31
  </div>
32
  );
33
  };
src/components/Footer.jsx CHANGED
@@ -2,7 +2,7 @@ import React from 'react';
2
 
3
  const Footer = () => {
4
  return (
5
- <footer className="mt-8 text-gray-500 text-sm">
6
  Powered by React, Gemini, Whisper base.en, Langchain and Kokoro-82M
7
  </footer>
8
  );
 
2
 
3
  const Footer = () => {
4
  return (
5
+ <footer className="mt-8 text-gray-500 dark:text-gray-400 text-sm text-center border-t border-gray-200 dark:border-gray-700 pt-4">
6
  Powered by React, Gemini, Whisper base.en, Langchain and Kokoro-82M
7
  </footer>
8
  );
src/components/VoiceAssistant.jsx CHANGED
@@ -1,6 +1,9 @@
1
- import React, { useState, useEffect, useRef } from 'react';
2
  import { Mic, Loader, Eye, EyeOff, AlertCircle, RefreshCw, X, Send } from 'lucide-react';
3
  import { animateOrb } from '../utils/animation.js';
 
 
 
4
 
5
  const VoiceAssistant = () => {
6
  const [isListening, setIsListening] = useState(false);
@@ -14,6 +17,7 @@ const VoiceAssistant = () => {
14
  const [activeTab, setActiveTab] = useState('response');
15
  const [selectedMedia, setSelectedMedia] = useState(null);
16
  const [latency, setLatency] = useState(null);
 
17
  const canvasRef = useRef(null);
18
  const mediaRecorderRef = useRef(null);
19
  const websocketRef = useRef(null);
@@ -25,6 +29,7 @@ const VoiceAssistant = () => {
25
  const isPlayingRef = useRef(false);
26
  const currentAudioRef = useRef(null);
27
  const requestStartTimeRef = useRef(null);
 
28
 
29
  // Initialize WebSocket
30
  useEffect(() => {
@@ -38,17 +43,42 @@ const VoiceAssistant = () => {
38
  websocketRef.current.onmessage = async (event) => {
39
  try {
40
  console.log('WebSocket message received:', event.data);
41
- const data = JSON.parse(event.data);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
42
  setTranscript(data.transcript || '');
43
- setResponse(data.response || { response: '', links: [], media_links: [], personal_info: [] });
44
- setIsProcessing(data.segment_index === -1);
 
 
 
 
 
 
45
 
46
- // Calculate latency for audio queries
47
  if (requestStartTimeRef.current && data.segment_index === -1) {
48
  const endTime = performance.now();
49
  const latencyMs = endTime - requestStartTimeRef.current;
50
  console.log(`Audio query latency: ${latencyMs.toFixed(2)} ms`);
51
- setLatency((latencyMs / 1000).toFixed(2)); // Convert to seconds, 2 decimal places
52
  requestStartTimeRef.current = null;
53
  }
54
 
@@ -59,15 +89,16 @@ const VoiceAssistant = () => {
59
  }
60
  }
61
  } catch (err) {
62
- console.error('Error parsing WebSocket message:', err);
63
- setError('Error processing server response. Please try again.');
 
64
  setLatency(null);
65
  }
66
  };
67
 
68
  websocketRef.current.onclose = () => {
69
  console.log('WebSocket disconnected');
70
- setError('WebSocket connection lost. Please refresh the page.');
71
  setIsProcessing(false);
72
  setIsListening(false);
73
  setIsPlaying(false);
@@ -76,7 +107,7 @@ const VoiceAssistant = () => {
76
 
77
  websocketRef.current.onerror = (error) => {
78
  console.error('WebSocket error:', error);
79
- setError('Error connecting to server. Please try again.');
80
  setIsProcessing(false);
81
  setIsListening(false);
82
  setIsPlaying(false);
@@ -107,29 +138,54 @@ const VoiceAssistant = () => {
107
  currentAudioRef.current = null;
108
  return;
109
  }
110
-
111
  setIsPlaying(true);
112
  isPlayingRef.current = true;
113
  const audioSegment = audioQueueRef.current.shift();
114
  try {
115
- const audioBlob = await fetch(`data:audio/wav;base64,${audioSegment}`).then(res => res.blob());
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
  const audioUrl = URL.createObjectURL(audioBlob);
117
  const audio = new Audio(audioUrl);
118
  currentAudioRef.current = audio;
 
119
  audio.onended = () => {
120
  URL.revokeObjectURL(audioUrl);
121
  currentAudioRef.current = null;
122
  playNextAudio();
123
  };
124
- audio.play();
 
 
 
 
 
 
 
 
 
 
 
125
  } catch (error) {
126
  console.error('Error playing audio:', error);
127
- setError('Error playing assistant response. Please try again.');
128
  setIsPlaying(false);
129
  isPlayingRef.current = false;
130
  audioQueueRef.current = [];
131
  currentAudioRef.current = null;
132
- setLatency(null);
133
  }
134
  };
135
 
@@ -173,7 +229,7 @@ const VoiceAssistant = () => {
173
  } else if (userAgent.includes('safari')) {
174
  return 'Please enable microphone permissions in Safari by going to Safari > Settings > Websites > Microphone, setting this site to "Allow", and refreshing the page.';
175
  } else {
176
- return 'Please enable microphone permissions in your browser settings and refresh the page. Check your browser’s help documentation for specific instructions.';
177
  }
178
  };
179
 
@@ -230,7 +286,7 @@ const VoiceAssistant = () => {
230
  const base64data = reader.result.split(',')[1];
231
  if (websocketRef.current && websocketRef.current.readyState === WebSocket.OPEN) {
232
  console.log('Sending audio data via WebSocket');
233
- requestStartTimeRef.current = performance.now(); // Start latency timer
234
  websocketRef.current.send(base64data);
235
  setIsProcessing(true);
236
  } else {
@@ -259,7 +315,7 @@ const VoiceAssistant = () => {
259
  mediaRecorderRef.current.stop();
260
  setIsListening(false);
261
  }
262
- }, 8000); // 8 seconds silence timeout
263
  } else {
264
  clearTimeout(silenceTimeoutRef.current);
265
  }
@@ -279,7 +335,7 @@ const VoiceAssistant = () => {
279
  mediaRecorderRef.current.stop();
280
  setIsListening(false);
281
  }
282
- }, 40000); // 40 seconds max recording
283
 
284
  } catch (error) {
285
  console.error('Error accessing microphone:', error);
@@ -312,11 +368,12 @@ const VoiceAssistant = () => {
312
  setTranscript(textInput);
313
  setIsProcessing(true);
314
  setError('');
315
- setLatency(null); // Reset latency
 
316
 
317
  try {
318
  console.log('Sending POST request to /text_query with query:', textInput);
319
- requestStartTimeRef.current = performance.now(); // Start latency timer
320
  const response = await fetch('https://abdullah-khaled-ai-voice-secretary.hf.space/text_query', {
321
  method: 'POST',
322
  headers: {
@@ -339,15 +396,15 @@ const VoiceAssistant = () => {
339
  const endTime = performance.now();
340
  const latencyMs = endTime - requestStartTimeRef.current;
341
  console.log(`Text query latency: ${latencyMs.toFixed(2)} ms`);
342
- setLatency((latencyMs / 1000).toFixed(2)); // Convert to seconds, 2 decimal places
343
  requestStartTimeRef.current = null;
344
- console.log('Received response:', data);
345
  setResponse(data);
 
346
  setIsProcessing(false);
347
  setTextInput('');
348
  } catch (error) {
349
  console.error('Error sending text query:', error);
350
- setError(`Failed to process text query: ${error.message}. Please check if the server is running on http://localhost:8000 and try again.`);
351
  setIsProcessing(false);
352
  setLatency(null);
353
  }
@@ -375,300 +432,303 @@ const VoiceAssistant = () => {
375
  return /\.(mp4|webm|ogg)$/i.test(url);
376
  };
377
 
378
- return (
379
- <div className="min-h-screen flex items-center justify-center bg-gray-100 p-4">
380
- <div className="w-full max-w-4xl bg-white rounded-2xl shadow-xl p-6 sm:p-8">
381
- {/* Header with Links */}
382
- <div className="flex justify-between items-center mb-6">
383
- <h1 className="text-2xl sm:text-3xl font-bold text-gray-800">
384
- AI Voice Secretary
385
- </h1>
386
- <div className="flex space-x-4">
387
- <a
388
- href="https://github.com/abdullah-khaled0"
389
- target="_blank"
390
- rel="noopener noreferrer"
391
- className="text-blue-600 hover:underline text-sm sm:text-base font-medium"
392
- >
393
- GitHub
394
- </a>
395
- <a
396
- href="https://abdullah-khaled0.github.io/portfolio-react/"
397
- target="_blank"
398
- rel="noopener noreferrer"
399
- className="text-blue-600 hover:underline text-sm sm:text-base font-medium"
400
- >
401
- Portfolio
402
- </a>
403
- </div>
404
- </div>
405
 
406
- <div className="flex justify-center mb-6">
407
- <div className="relative">
408
- <canvas ref={canvasRef} className="w-32 h-32 sm:w-48 sm:h-48" />
409
- <button
410
- onClick={handleMicClick}
411
- className={`absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 p-3 sm:p-4 rounded-full transition-all duration-300 ${
412
- isListening
413
- ? 'bg-blue-500 text-white'
414
- : isPlaying
415
- ? 'bg-blue-500 text-white animate-pulse'
416
- : 'bg-gray-200 text-gray-600'
417
- } hover:bg-blue-600 hover:text-white focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed`}
418
- aria-label={isListening ? 'Stop listening' : isPlaying ? 'Stop and listen again' : 'Start listening'}
419
- disabled={isProcessing}
420
- >
421
- <Mic className="w-6 h-6 sm:w-8 sm:h-8" />
422
- </button>
423
- </div>
424
  </div>
425
-
426
- {/* Text Input Form */}
427
- <div className="mb-6">
428
- <form onSubmit={handleTextSubmit} className="flex items-center space-x-2">
429
- <input
430
- type="text"
431
- value={textInput}
432
- onChange={(e) => setTextInput(e.target.value)}
433
- placeholder="Ask the assistant something..."
434
- className="flex-1 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 text-sm sm:text-base"
435
- disabled={isProcessing}
436
- />
437
  <button
438
- type="submit"
439
- className="p-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed"
440
- aria-label="Submit text query"
441
- disabled={isProcessing}
442
  >
443
- <Send className="w-5 h-5" />
444
  </button>
445
- </form>
446
- </div>
447
-
448
- <div className="space-y-4">
449
- {error && (
450
- <div className="bg-red-50 p-4 rounded-lg flex items-center space-x-2">
451
- <AlertCircle className="w-5 h-5 text-red-600 flex-shrink-0" />
452
- <div className="flex-1">
453
- <p className="text-red-600 text-sm sm:text-base">{error}</p>
454
- <button
455
- onClick={handleRetryClick}
456
- className="mt-2 flex items-center text-blue-600 hover:text-blue-800 focus:outline-none text-sm sm:text-base"
457
- aria-label="Retry microphone access"
458
- >
459
- <RefreshCw className="w-4 h-4 mr-1" />
460
- Retry
461
- </button>
462
- </div>
463
- </div>
464
- )}
465
-
466
- <div className="bg-gray-50 p-4 rounded-lg">
467
- <h2 className="text-lg font-semibold text-gray-700 mb-2">Transcript</h2>
468
- <p className="text-gray-600 text-sm sm:text-base">
469
- {transcript || 'Click the microphone or type to start interacting'}
470
- </p>
471
- <p className="text-gray-600 text-sm sm:text-base mt-2">
472
- Response time: {latency ? `${latency} seconds` : 'Not available'}
473
- </p>
474
  </div>
475
-
476
- {isProcessing && (
477
- <div className="flex justify-center">
478
- <Loader className="w-6 h-6 animate-spin text-blue-500" />
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
479
  </div>
480
- )}
481
-
482
- {response.response && !isProcessing && (
483
- <div className="bg-blue-50 p-4 rounded-lg">
484
- <div className="flex justify-between items-center mb-2">
485
- <h2 className="text-lg font-semibold text-blue-700">Response</h2>
486
- <button
487
- onClick={() => setShowResponse(!showResponse)}
488
- className="text-blue-600 hover:text-blue-800 focus:outline-none"
489
- aria-label={showResponse ? 'Hide response' : 'Show response'}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
490
  >
491
- {showResponse ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
492
- </button>
493
  </div>
494
- <div
495
- className={`transition-all duration-300 ${
496
- showResponse ? 'max-h-[60vh] opacity-100' : 'max-h-0 opacity-0 overflow-hidden'
497
- } overflow-y-auto scrollbar-thin scrollbar-thumb-blue-500 scrollbar-track-blue-100`}
498
- >
499
- <div className="flex border-b border-gray-200 mb-4">
500
- <button
501
- className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
502
- activeTab === 'response'
503
- ? 'border-b-2 border-blue-500 text-blue-600'
504
- : 'text-gray-500 hover:text-blue-600'
505
- }`}
506
- onClick={() => setActiveTab('response')}
507
- >
508
- Response
509
- </button>
510
- <button
511
- className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
512
- activeTab === 'links'
513
- ? 'border-b-2 border-blue-500 text-blue-600'
514
- : 'text-gray-500 hover:text-blue-600'
515
- }`}
516
- onClick={() => setActiveTab('links')}
517
- >
518
- Links
519
- </button>
520
- <button
521
- className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
522
- activeTab === 'media'
523
- ? 'border-b-2 border-blue-500 text-blue-600'
524
- : 'text-gray-500 hover:text-blue-600'
525
- }`}
526
- onClick={() => setActiveTab('media')}
527
- >
528
- Media
529
- </button>
530
- <button
531
- className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
532
- activeTab === 'personal'
533
- ? 'border-b-2 border-blue-500 text-blue-600'
534
- : 'text-gray-500 hover:text-blue-600'
535
- }`}
536
- onClick={() => setActiveTab('personal')}
537
- >
538
- Personal Info
539
- </button>
540
- </div>
541
- {activeTab === 'response' && (
542
- <p className="text-gray-800 text-sm sm:text-base">{response.response}</p>
543
- )}
544
- {activeTab === 'links' && (
545
- <ul className="text-gray-800 space-y-2 text-sm sm:text-base">
546
- {response.links.length > 0 ? (
547
- response.links.map((link, index) => (
548
- <li key={index}>
549
- <a
550
- href={link.url}
551
- target="_blank"
552
- rel="noopener noreferrer"
553
- className="text-blue-600 hover:underline"
554
- >
555
- {link.platform}
556
- </a>
557
- </li>
558
- ))
559
- ) : (
560
- <p>No links available.</p>
561
- )}
562
- </ul>
563
  )}
564
- {activeTab === 'media' && (
565
- <div className="text-gray-800">
566
- {response.media_links.length > 0 ? (
567
- <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
568
- {response.media_links.map((media, index) => (
569
- isVideo(media) ? (
570
- <video
571
- key={index}
572
- src={media}
573
- controls
574
- className="w-full h-32 sm:h-48 object-cover rounded-lg shadow-md cursor-pointer hover:opacity-80 transition-opacity duration-200"
575
- onClick={() => handleMediaClick(media)}
576
- onError={(e) => {
577
- console.error(`Failed to load video: ${media}`);
578
- e.target.style.display = 'none';
579
- }}
580
- />
581
- ) : (
582
- <img
583
- key={index}
584
- src={media}
585
- alt={`Media ${index + 1}`}
586
- className="w-full h-32 sm:h-48 object-cover rounded-lg shadow-md cursor-pointer hover:opacity-80 transition-opacity duration-200"
587
- onClick={() => handleMediaClick(media)}
588
- onError={(e) => {
589
- console.error(`Failed to load image: ${media}`);
590
- e.target.style.display = 'none';
591
- }}
592
- />
593
- )
594
- ))}
595
- </div>
596
- ) : (
597
- <p className="text-sm sm:text-base">No media available. Try asking about a specific project.</p>
598
- )}
599
  </div>
600
- )}
601
- {activeTab === 'personal' && (
602
- <ul className="text-gray-800 space-y-2 text-sm sm:text-base">
603
- {response.personal_info.length > 0 ? (
604
- response.personal_info.map((info, index) => (
605
- <li key={index}>
606
- <span className="font-semibold">{info.type}:</span>{' '}
607
- {info.type === 'Phone' ? (
608
- <a
609
- href={info.value}
610
- target="_blank"
611
- rel="noopener noreferrer"
612
- className="text-blue-600 hover:underline"
613
- >
614
- {info.value}
615
- </a>
616
- ) : (
617
- <span>{info.value}</span>
618
- )}
619
- </li>
620
- ))
621
- ) : (
622
- <p>No personal information available. Try asking for contact details.</p>
623
- )}
624
- </ul>
625
  )}
626
  </div>
627
- </div>
628
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
629
  </div>
630
-
631
- {/* Lightbox for media preview */}
632
- {selectedMedia && (
633
- <div className="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center z-50">
634
- <div className="relative max-w-4xl w-full p-4">
635
- <button
636
- onClick={closeLightbox}
637
- className="absolute top-2 right-2 text-white bg-gray-800 rounded-full p-2 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-white"
638
- aria-label="Close media preview"
639
- >
640
- <X className="w-6 h-6" />
641
- </button>
642
- {isVideo(selectedMedia) ? (
643
- <video
644
- src={selectedMedia}
645
- controls
646
- autoPlay
647
- className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
648
- onError={(e) => {
649
- console.error(`Failed to load full-size video: ${selectedMedia}`);
650
- e.target.style.display = 'none';
651
- setError('Failed to load video preview.');
652
- }}
653
- />
654
- ) : (
655
- <img
656
- src={selectedMedia}
657
- alt="Full-size preview"
658
- className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
659
- onError={(e) => {
660
- console.error(`Failed to load full-size image: ${selectedMedia}`);
661
- e.target.style.display = 'none';
662
- setError('Failed to load image preview.');
663
- }}
664
- />
665
- )}
666
- </div>
667
  </div>
668
- )}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
669
  </div>
670
  </div>
671
- );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
672
  };
673
 
674
  export default VoiceAssistant;
 
1
+ import React, { useState, useEffect, useRef, useContext } from 'react';
2
  import { Mic, Loader, Eye, EyeOff, AlertCircle, RefreshCw, X, Send } from 'lucide-react';
3
  import { animateOrb } from '../utils/animation.js';
4
+ import { ThemeContext } from '../main.jsx';
5
+ import ReactMarkdown from 'react-markdown';
6
+ import remarkGfm from 'remark-gfm';
7
 
8
  const VoiceAssistant = () => {
9
  const [isListening, setIsListening] = useState(false);
 
17
  const [activeTab, setActiveTab] = useState('response');
18
  const [selectedMedia, setSelectedMedia] = useState(null);
19
  const [latency, setLatency] = useState(null);
20
+ const [currentResponseText, setCurrentResponseText] = useState('');
21
  const canvasRef = useRef(null);
22
  const mediaRecorderRef = useRef(null);
23
  const websocketRef = useRef(null);
 
29
  const isPlayingRef = useRef(false);
30
  const currentAudioRef = useRef(null);
31
  const requestStartTimeRef = useRef(null);
32
+ const { theme } = useContext(ThemeContext);
33
 
34
  // Initialize WebSocket
35
  useEffect(() => {
 
43
  websocketRef.current.onmessage = async (event) => {
44
  try {
45
  console.log('WebSocket message received:', event.data);
46
+ let data;
47
+ try {
48
+ data = JSON.parse(event.data);
49
+ } catch (parseError) {
50
+ console.error('Failed to parse WebSocket message:', parseError, 'Raw data:', event.data);
51
+ setError('Invalid server response format. Please try again.');
52
+ setIsProcessing(false);
53
+ setLatency(null);
54
+ return;
55
+ }
56
+
57
+ // Validate expected structure
58
+ if (!data || typeof data !== 'object' || !('transcript' in data && 'response' in data && 'segment_index' in data)) {
59
+ console.error('Unexpected message structure:', data);
60
+ setError('Received malformed response from server. Please try again.');
61
+ setIsProcessing(false);
62
+ setLatency(null);
63
+ return;
64
+ }
65
+
66
  setTranscript(data.transcript || '');
67
+ setCurrentResponseText(data.response?.response || '');
68
+
69
+ if (data.is_last_segment) {
70
+ setResponse(data.response || { response: '', links: [], media_links: [], personal_info: [] });
71
+ setIsProcessing(false);
72
+ } else {
73
+ setIsProcessing(true);
74
+ }
75
 
76
+ // Calculate latency
77
  if (requestStartTimeRef.current && data.segment_index === -1) {
78
  const endTime = performance.now();
79
  const latencyMs = endTime - requestStartTimeRef.current;
80
  console.log(`Audio query latency: ${latencyMs.toFixed(2)} ms`);
81
+ setLatency((latencyMs / 1000).toFixed(2));
82
  requestStartTimeRef.current = null;
83
  }
84
 
 
89
  }
90
  }
91
  } catch (err) {
92
+ console.error('Error processing WebSocket message:', err, 'Raw data:', event.data);
93
+ setError('Error processing server response. Please check the server logs and try again.');
94
+ setIsProcessing(false);
95
  setLatency(null);
96
  }
97
  };
98
 
99
  websocketRef.current.onclose = () => {
100
  console.log('WebSocket disconnected');
101
+ setError('WebSocket connection lost. Please refresh the page or check the server.');
102
  setIsProcessing(false);
103
  setIsListening(false);
104
  setIsPlaying(false);
 
107
 
108
  websocketRef.current.onerror = (error) => {
109
  console.error('WebSocket error:', error);
110
+ setError('Error connecting to server. Please ensure the server is running on ws://localhost:8000.');
111
  setIsProcessing(false);
112
  setIsListening(false);
113
  setIsPlaying(false);
 
138
  currentAudioRef.current = null;
139
  return;
140
  }
141
+
142
  setIsPlaying(true);
143
  isPlayingRef.current = true;
144
  const audioSegment = audioQueueRef.current.shift();
145
  try {
146
+ // Ensure the audio segment is a valid base64 string
147
+ if (!audioSegment || typeof audioSegment !== 'string') {
148
+ throw new Error('Invalid audio segment received');
149
+ }
150
+
151
+ const base64String = audioSegment.startsWith('data:audio/wav;base64,')
152
+ ? audioSegment
153
+ : `data:audio/wav;base64,${audioSegment}`;
154
+
155
+ const response = await fetch(base64String);
156
+ if (!response.ok) {
157
+ throw new Error(`Failed to fetch audio: ${response.statusText}`);
158
+ }
159
+
160
+ const audioBlob = await response.blob();
161
  const audioUrl = URL.createObjectURL(audioBlob);
162
  const audio = new Audio(audioUrl);
163
  currentAudioRef.current = audio;
164
+
165
  audio.onended = () => {
166
  URL.revokeObjectURL(audioUrl);
167
  currentAudioRef.current = null;
168
  playNextAudio();
169
  };
170
+
171
+ audio.onerror = (error) => {
172
+ console.error('Audio playback error:', error);
173
+ URL.revokeObjectURL(audioUrl);
174
+ currentAudioRef.current = null;
175
+ setError('Failed to play audio response. Please try again.');
176
+ setIsPlaying(false);
177
+ isPlayingRef.current = false;
178
+ audioQueueRef.current = [];
179
+ };
180
+
181
+ await audio.play();
182
  } catch (error) {
183
  console.error('Error playing audio:', error);
184
+ setError('Error playing assistant response: ' + error.message);
185
  setIsPlaying(false);
186
  isPlayingRef.current = false;
187
  audioQueueRef.current = [];
188
  currentAudioRef.current = null;
 
189
  }
190
  };
191
 
 
229
  } else if (userAgent.includes('safari')) {
230
  return 'Please enable microphone permissions in Safari by going to Safari > Settings > Websites > Microphone, setting this site to "Allow", and refreshing the page.';
231
  } else {
232
+ return 'Please enable microphone permissions in your browser settings and refresh the page. Check your browsers help documentation for specific instructions.';
233
  }
234
  };
235
 
 
286
  const base64data = reader.result.split(',')[1];
287
  if (websocketRef.current && websocketRef.current.readyState === WebSocket.OPEN) {
288
  console.log('Sending audio data via WebSocket');
289
+ requestStartTimeRef.current = performance.now();
290
  websocketRef.current.send(base64data);
291
  setIsProcessing(true);
292
  } else {
 
315
  mediaRecorderRef.current.stop();
316
  setIsListening(false);
317
  }
318
+ }, 8000);
319
  } else {
320
  clearTimeout(silenceTimeoutRef.current);
321
  }
 
335
  mediaRecorderRef.current.stop();
336
  setIsListening(false);
337
  }
338
+ }, 40000);
339
 
340
  } catch (error) {
341
  console.error('Error accessing microphone:', error);
 
368
  setTranscript(textInput);
369
  setIsProcessing(true);
370
  setError('');
371
+ setLatency(null);
372
+ setCurrentResponseText('');
373
 
374
  try {
375
  console.log('Sending POST request to /text_query with query:', textInput);
376
+ requestStartTimeRef.current = performance.now();
377
  const response = await fetch('https://abdullah-khaled-ai-voice-secretary.hf.space/text_query', {
378
  method: 'POST',
379
  headers: {
 
396
  const endTime = performance.now();
397
  const latencyMs = endTime - requestStartTimeRef.current;
398
  console.log(`Text query latency: ${latencyMs.toFixed(2)} ms`);
399
+ setLatency((latencyMs / 1000).toFixed(2));
400
  requestStartTimeRef.current = null;
 
401
  setResponse(data);
402
+ setCurrentResponseText(data.response);
403
  setIsProcessing(false);
404
  setTextInput('');
405
  } catch (error) {
406
  console.error('Error sending text query:', error);
407
+ setError(`Failed to process text query: ${error.message}.`);
408
  setIsProcessing(false);
409
  setLatency(null);
410
  }
 
432
  return /\.(mp4|webm|ogg)$/i.test(url);
433
  };
434
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
435
 
436
+ return (
437
+ <div className="w-full flex flex-col md:flex-row gap-8 items-stretch">
438
+ <div className="flex-1 flex flex-col gap-4">
439
+ <div className="bg-white dark:bg-gray-900 rounded-2xl shadow-lg dark:shadow-xl p-6 transition-colors duration-500 border border-gray-100 dark:border-gray-800">
440
+ <h2 className="text-lg font-semibold text-gray-700 dark:text-gray-100 mb-2">Transcript</h2>
441
+ <p className="text-gray-600 dark:text-gray-300 text-sm sm:text-base">
442
+ {transcript || 'Click the microphone or type to start interacting'}
443
+ </p>
444
+ <p className="text-gray-600 dark:text-gray-400 text-xs sm:text-sm mt-2">
445
+ Response time: {latency ? `${latency} seconds` : 'Not available'}
446
+ </p>
447
+ </div>
448
+ {isProcessing && (
449
+ <div className="flex justify-center">
450
+ <Loader className="w-6 h-6 animate-spin text-blue-500 dark:text-blue-400" />
 
 
 
451
  </div>
452
+ )}
453
+ {(currentResponseText || response.response) && !isProcessing && (
454
+ <div className="bg-blue-50 dark:bg-blue-950 rounded-2xl shadow-lg dark:shadow-xl p-6 transition-colors duration-500 border border-blue-100 dark:border-blue-900">
455
+ <div className="flex justify-between items-center mb-2">
456
+ <h2 className="text-lg font-semibold text-blue-700 dark:text-blue-200">Response</h2>
 
 
 
 
 
 
 
457
  <button
458
+ onClick={() => setShowResponse(!showResponse)}
459
+ className="text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-400 focus:outline-none"
460
+ aria-label={showResponse ? 'Hide response' : 'Show response'}
 
461
  >
462
+ {showResponse ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
463
  </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  </div>
465
+ <div
466
+ className={`transition-all duration-300 ${
467
+ showResponse ? 'max-h-[60vh] opacity-100' : 'max-h-0 opacity-0 overflow-hidden'
468
+ } overflow-y-auto scrollbar-thin scrollbar-thumb-blue-500 scrollbar-track-blue-100 dark:scrollbar-thumb-blue-700 dark:scrollbar-track-blue-950`}
469
+ >
470
+ <div className="flex border-b border-gray-200 dark:border-gray-700 mb-4">
471
+ <button
472
+ className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
473
+ activeTab === 'response'
474
+ ? 'border-b-2 border-blue-500 text-blue-600'
475
+ : 'text-gray-500 hover:text-blue-600'
476
+ }`}
477
+ onClick={() => setActiveTab('response')}
478
+ >
479
+ Response
480
+ </button>
481
+ <button
482
+ className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
483
+ activeTab === 'links'
484
+ ? 'border-b-2 border-blue-500 text-blue-600'
485
+ : 'text-gray-500 hover:text-blue-600'
486
+ }`}
487
+ onClick={() => setActiveTab('links')}
488
+ >
489
+ Links
490
+ </button>
491
+ <button
492
+ className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
493
+ activeTab === 'media'
494
+ ? 'border-b-2 border-blue-500 text-blue-600'
495
+ : 'text-gray-500 hover:text-blue-600'
496
+ }`}
497
+ onClick={() => setActiveTab('media')}
498
+ >
499
+ Media
500
+ </button>
501
+ <button
502
+ className={`px-3 py-2 text-sm font-medium flex-1 text-center ${
503
+ activeTab === 'personal'
504
+ ? 'border-b-2 border-blue-500 text-blue-600'
505
+ : 'text-gray-500 hover:text-blue-600'
506
+ }`}
507
+ onClick={() => setActiveTab('personal')}
508
+ >
509
+ Personal Info
510
+ </button>
511
  </div>
512
+ {activeTab === 'response' && (
513
+ <div className="text-gray-800 dark:text-gray-100 text-sm sm:text-base break-words">
514
+ <ReactMarkdown
515
+ remarkPlugins={[remarkGfm]}
516
+ components={{
517
+ h1: ({ children }) => <h1 className="text-5xl font-semibold text-gray-800 dark:text-gray-100 mt-4 mb-2">{children}</h1>,
518
+ h2: ({ children }) => <h2 className="text-4xl font-semibold text-gray-800 dark:text-gray-100 mt-4 mb-2">{children}</h2>,
519
+ h3: ({ children }) => <h3 className="text-3xl font-semibold text-gray-800IManager dark:text-gray-100 mt-4 mb-2">{children}</h3>,
520
+ h4: ({ children }) => <h4 className="text-2xl font-semibold text-gray-800 dark:text-gray-100 mt-4 mb-2">{children}</h4>,
521
+ h5: ({ children }) => <h5 className="text-xl font-semibold text-gray-800 dark:text-gray-100 mt-4 mb-2">{children}</h5>,
522
+ h6: ({ children }) => <h6 className="text-lg font-semibold text-gray-800 dark:text-gray-100 mt-4 mb-2">{children}</h6>,
523
+ p: ({ children }) => <p className="text-gray-800 dark:text-gray-100 my-2">{children}</p>,
524
+ strong: ({ children }) => <span className="font-bold text-blue-600 dark:text-blue-300">{children}</span>,
525
+ em: ({ children }) => <span className="italic text-gray-700 dark:text-gray-300">{children}</span>,
526
+ code: ({ node, inline, children, className }) => (
527
+ inline ? (
528
+ <code className="bg-gray-100 dark:bg-gray-800 text-red-600 dark:text-red-400 px-1 rounded">{children}</code>
529
+ ) : (
530
+ <pre className="bg-gray-100 dark:bg-gray-800 text-red-600 dark:text-red-400 p-4 rounded my-2 overflow-x-auto">
531
+ <code className={className}>{children}</code>
532
+ </pre>
533
+ )
534
+ ),
535
+ a: ({ href, children }) => (
536
+ <a href={href} target="_blank" rel="noopener noreferrer" className="text-blue-600 hover:underline dark:text-blue-300">
537
+ {children}
538
+ </a>
539
+ ),
540
+ ul: ({ children }) => <ul className="my-2 ml-4 list-disc text-gray-800 dark:text-gray-100">{children}</ul>,
541
+ ol: ({ children }) => <ol className="my-2 ml-4 list-decimal text-gray-800 dark:text-gray-100">{children}</ol>,
542
+ li: ({ children }) => <li className="ml-4">{children}</li>,
543
+ blockquote: ({ children }) => (
544
+ <blockquote className="border-l-4 border-gray-300 dark:border-gray-600 pl-4 italic text-gray-700 dark:text-gray-300 my-2">
545
+ {children}
546
+ </blockquote>
547
+ ),
548
+ }}
549
  >
550
+ {currentResponseText}
551
+ </ReactMarkdown>
552
  </div>
553
+ )}
554
+ {activeTab === 'links' && (
555
+ <ul className="text-gray-800 space-y-2 text-sm sm:text-base">
556
+ {response.links.length > 0 ? (
557
+ response.links.map((link, index) => (
558
+ <li key={index}>
559
+ <a
560
+ href={link.url}
561
+ target="_blank"
562
+ rel="noopener noreferrer"
563
+ className="text-blue-600 hover:underline"
564
+ >
565
+ {link.platform}
566
+ </a>
567
+ </li>
568
+ ))
569
+ ) : (
570
+ <p>No links available.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
571
  )}
572
+ </ul>
573
+ )}
574
+ {activeTab === 'media' && (
575
+ <div className="text-gray-800">
576
+ {response.media_links.length > 0 ? (
577
+ <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
578
+ {response.media_links.map((media, index) => (
579
+ isVideo(media) ? (
580
+ <video
581
+ key={index}
582
+ src={media}
583
+ controls
584
+ className="w-full h-32 sm:h-48 object-cover rounded-lg shadow-md cursor-pointer hover:opacity-80 transition-opacity duration-200"
585
+ onClick={() => handleMediaClick(media)}
586
+ onError={(e) => {
587
+ console.error(`Failed to load video: ${media}`);
588
+ e.target.style.display = 'none';
589
+ }}
590
+ />
591
+ ) : (
592
+ <img
593
+ key={index}
594
+ src={media}
595
+ alt={`Media ${index + 1}`}
596
+ className="w-full h-32 sm:h-48 object-cover rounded-lg shadow-md cursor-pointer hover:opacity-80 transition-opacity duration-200"
597
+ onClick={() => handleMediaClick(media)}
598
+ onError={(e) => {
599
+ console.error(`Failed to load image: ${media}`);
600
+ e.target.style.display = 'none';
601
+ }}
602
+ />
603
+ )
604
+ ))}
 
 
605
  </div>
606
+ ) : (
607
+ <p className="text-sm sm:text-base">No media available. Try asking about a specific project.</p>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
608
  )}
609
  </div>
610
+ )}
611
+ {activeTab === 'personal' && (
612
+ <ul className="text-gray-800 space-y-2 text-sm sm:text-base">
613
+ {response.personal_info.length > 0 ? (
614
+ response.personal_info.map((info, index) => (
615
+ <li key={index}>
616
+ <span className="font-semibold">{info.type}:</span>{' '}
617
+ {info.type === 'Phone' ? (
618
+ <a
619
+ href={info.value}
620
+ target="_blank"
621
+ rel="noopener noreferrer"
622
+ className="text-blue-600 hover:underline"
623
+ >
624
+ {info.value}
625
+ </a>
626
+ ) : (
627
+ <span>{info.value}</span>
628
+ )}
629
+ </li>
630
+ ))
631
+ ) : (
632
+ <p>No personal information available. Try asking for contact details.</p>
633
+ )}
634
+ </ul>
635
+ )}
636
+ </div>
637
  </div>
638
+ )}
639
+ {error && (
640
+ <div className="bg-red-50 dark:bg-red-900 p-4 rounded-lg flex items-center space-x-2 mt-2 border border-red-200 dark:border-red-700">
641
+ <AlertCircle className="w-5 h-5 text-red-600 dark:text-red-300 flex-shrink-0" />
642
+ <div className="flex-1">
643
+ <p className="text-red-600 dark:text-red-100 text-sm sm:text-base">{error}</p>
644
+ <button
645
+ onClick={handleRetryClick}
646
+ className="mt-2 flex items-center text-blue-600 dark:text-blue-300 hover:text-blue-800 dark:hover:text-blue-400 focus:outline-none text-sm sm:text-base"
647
+ aria-label="Retry microphone access"
648
+ >
649
+ <RefreshCw className="w-4 h-4 mr-1" />
650
+ Retry
651
+ </button>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
652
  </div>
653
+ </div>
654
+ )}
655
+ <form onSubmit={handleTextSubmit} className="flex items-center space-x-2 mt-2">
656
+ <input
657
+ type="text"
658
+ value={textInput}
659
+ onChange={(e) => setTextInput(e.target.value)}
660
+ placeholder="Ask the assistant something..."
661
+ className="flex-1 p-2 border border-gray-300 dark:border-gray-700 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 dark:bg-gray-900 dark:text-gray-100 text-sm sm:text-base transition-colors"
662
+ disabled={isProcessing}
663
+ />
664
+ <button
665
+ type="submit"
666
+ className="p-2 bg-blue-500 text-white rounded-lg hover:bg-blue-600 focus:outline-none focus:ring-2 focus:ring-blue-500 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
667
+ aria-label="Submit text query"
668
+ disabled={isProcessing}
669
+ >
670
+ <Send className="w-5 h-5" />
671
+ </button>
672
+ </form>
673
+ </div>
674
+ <div className="flex flex-col items-center justify-center min-w-[180px] md:min-w-[260px]">
675
+ <div className="relative">
676
+ <canvas ref={canvasRef} className="w-32 h-32 sm:w-48 sm:h-48" />
677
+ <button
678
+ onClick={handleMicClick}
679
+ className={`absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 p-3 sm:p-4 rounded-full transition-all duration-300 shadow-lg border-4 border-white dark:border-gray-800 ${
680
+ isListening
681
+ ? 'bg-blue-500 text-white scale-110'
682
+ : isPlaying
683
+ ? 'bg-blue-500 text-white animate-pulse'
684
+ : 'bg-gray-200 dark:bg-gray-700 text-gray-600 dark:text-gray-200'
685
+ } hover:bg-blue-600 hover:text-white dark:hover:bg-blue-400 dark:hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:opacity-50 disabled:cursor-not-allowed`}
686
+ aria-label={isListening ? 'Stop listening' : isPlaying ? 'Stop and listen again' : 'Start listening'}
687
+ disabled={isProcessing}
688
+ >
689
+ <Mic className="w-6 h-6 sm:w-8 sm:h-8" />
690
+ </button>
691
  </div>
692
  </div>
693
+ {selectedMedia && (
694
+ <div className="fixed inset-0 bg-black bg-opacity-80 flex items-center justify-center z-50">
695
+ <div className="relative max-w-4xl w-full p-4">
696
+ <button
697
+ onClick={closeLightbox}
698
+ className="absolute top-2 right-2 text-white bg-gray-800 rounded-full p-2 hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-white"
699
+ aria-label="Close media preview"
700
+ >
701
+ <X className="w-6 h-6" />
702
+ </button>
703
+ {isVideo(selectedMedia) ? (
704
+ <video
705
+ src={selectedMedia}
706
+ controls
707
+ autoPlay
708
+ className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
709
+ onError={(e) => {
710
+ console.error(`Failed to load full-size video: ${selectedMedia}`);
711
+ e.target.style.display = 'none';
712
+ setError('Failed to load video preview.');
713
+ }}
714
+ />
715
+ ) : (
716
+ <img
717
+ src={selectedMedia}
718
+ alt="Full-size preview"
719
+ className="w-full h-auto max-h-[80vh] object-contain rounded-lg"
720
+ onError={(e) => {
721
+ console.error(`Failed to load full-size image: ${selectedMedia}`);
722
+ e.target.style.display = 'none';
723
+ setError('Failed to load image preview.');
724
+ }}
725
+ />
726
+ )}
727
+ </div>
728
+ </div>
729
+ )}
730
+ </div>
731
+ );
732
  };
733
 
734
  export default VoiceAssistant;
src/main.jsx CHANGED
@@ -1,5 +1,31 @@
1
- import React from 'react';
2
  import ReactDOM from 'react-dom/client';
3
  import App from './components/App.jsx';
4
 
5
- ReactDOM.createRoot(document.getElementById('root')).render(<App />);
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import React, { useEffect, useState } from 'react';
2
  import ReactDOM from 'react-dom/client';
3
  import App from './components/App.jsx';
4
 
5
+ export const ThemeContext = React.createContext();
6
+
7
+ const ThemeProvider = ({ children }) => {
8
+ const [theme, setTheme] = useState(() => {
9
+ if (typeof window !== 'undefined') {
10
+ return localStorage.getItem('theme') || 'light';
11
+ }
12
+ return 'light';
13
+ });
14
+
15
+ useEffect(() => {
16
+ document.documentElement.classList.toggle('dark', theme === 'dark');
17
+ localStorage.setItem('theme', theme);
18
+ }, [theme]);
19
+
20
+ return (
21
+ <ThemeContext.Provider value={{ theme, setTheme }}>
22
+ {children}
23
+ </ThemeContext.Provider>
24
+ );
25
+ };
26
+
27
+ ReactDOM.createRoot(document.getElementById('root')).render(
28
+ <ThemeProvider>
29
+ <App />
30
+ </ThemeProvider>
31
+ );