S01Nour commited on
Commit
a94de35
·
1 Parent(s): 78b3b2b

feat/ui: chat adapted tools

Browse files
package-lock.json CHANGED
@@ -22,6 +22,7 @@
22
  "web-vitals": "^2.1.4"
23
  },
24
  "devDependencies": {
 
25
  "@types/react": "^19.2.7",
26
  "@types/react-dom": "^19.2.3",
27
  "autoprefixer": "^10.4.22",
@@ -75,6 +76,7 @@
75
  "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
76
  "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
77
  "license": "MIT",
 
78
  "dependencies": {
79
  "@babel/code-frame": "^7.27.1",
80
  "@babel/generator": "^7.28.5",
@@ -724,6 +726,7 @@
724
  "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz",
725
  "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==",
726
  "license": "MIT",
 
727
  "dependencies": {
728
  "@babel/helper-plugin-utils": "^7.27.1"
729
  },
@@ -1607,6 +1610,7 @@
1607
  "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz",
1608
  "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==",
1609
  "license": "MIT",
 
1610
  "dependencies": {
1611
  "@babel/helper-annotate-as-pure": "^7.27.1",
1612
  "@babel/helper-module-imports": "^7.27.1",
@@ -2097,7 +2101,6 @@
2097
  "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
2098
  "license": "MIT",
2099
  "optional": true,
2100
- "peer": true,
2101
  "dependencies": {
2102
  "@jridgewell/trace-mapping": "0.3.9"
2103
  },
@@ -2111,7 +2114,6 @@
2111
  "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
2112
  "license": "MIT",
2113
  "optional": true,
2114
- "peer": true,
2115
  "dependencies": {
2116
  "@jridgewell/resolve-uri": "^3.0.3",
2117
  "@jridgewell/sourcemap-codec": "^1.4.10"
@@ -3410,6 +3412,7 @@
3410
  "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
3411
  "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
3412
  "license": "MIT",
 
3413
  "dependencies": {
3414
  "@babel/code-frame": "^7.10.4",
3415
  "@babel/runtime": "^7.12.5",
@@ -3515,32 +3518,28 @@
3515
  "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
3516
  "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
3517
  "license": "MIT",
3518
- "optional": true,
3519
- "peer": true
3520
  },
3521
  "node_modules/@tsconfig/node12": {
3522
  "version": "1.0.11",
3523
  "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
3524
  "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
3525
  "license": "MIT",
3526
- "optional": true,
3527
- "peer": true
3528
  },
3529
  "node_modules/@tsconfig/node14": {
3530
  "version": "1.0.3",
3531
  "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
3532
  "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
3533
  "license": "MIT",
3534
- "optional": true,
3535
- "peer": true
3536
  },
3537
  "node_modules/@tsconfig/node16": {
3538
  "version": "1.0.4",
3539
  "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
3540
  "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
3541
  "license": "MIT",
3542
- "optional": true,
3543
- "peer": true
3544
  },
3545
  "node_modules/@types/aria-query": {
3546
  "version": "5.0.4",
@@ -3825,10 +3824,11 @@
3825
  "license": "MIT"
3826
  },
3827
  "node_modules/@types/node": {
3828
- "version": "24.10.2",
3829
- "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.2.tgz",
3830
- "integrity": "sha512-WOhQTZ4G8xZ1tjJTvKOpyEVSGgOTvJAfDK3FNFgELyaTpzhdgHVHeqW8V+UJvzF5BT+/B54T/1S2K6gd9c7bbA==",
3831
  "license": "MIT",
 
3832
  "dependencies": {
3833
  "undici-types": "~7.16.0"
3834
  }
@@ -3878,6 +3878,7 @@
3878
  "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
3879
  "devOptional": true,
3880
  "license": "MIT",
 
3881
  "dependencies": {
3882
  "csstype": "^3.2.2"
3883
  }
@@ -3888,6 +3889,7 @@
3888
  "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
3889
  "devOptional": true,
3890
  "license": "MIT",
 
3891
  "peerDependencies": {
3892
  "@types/react": "^19.2.0"
3893
  }
@@ -4008,6 +4010,7 @@
4008
  "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
4009
  "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
4010
  "license": "MIT",
 
4011
  "dependencies": {
4012
  "@eslint-community/regexpp": "^4.4.0",
4013
  "@typescript-eslint/scope-manager": "5.62.0",
@@ -4061,6 +4064,7 @@
4061
  "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
4062
  "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
4063
  "license": "BSD-2-Clause",
 
4064
  "dependencies": {
4065
  "@typescript-eslint/scope-manager": "5.62.0",
4066
  "@typescript-eslint/types": "5.62.0",
@@ -4430,6 +4434,7 @@
4430
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
4431
  "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
4432
  "license": "MIT",
 
4433
  "bin": {
4434
  "acorn": "bin/acorn"
4435
  },
@@ -4528,6 +4533,7 @@
4528
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
4529
  "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
4530
  "license": "MIT",
 
4531
  "dependencies": {
4532
  "fast-deep-equal": "^3.1.1",
4533
  "fast-json-stable-stringify": "^2.0.0",
@@ -5435,6 +5441,7 @@
5435
  }
5436
  ],
5437
  "license": "MIT",
 
5438
  "dependencies": {
5439
  "baseline-browser-mapping": "^2.9.0",
5440
  "caniuse-lite": "^1.0.30001759",
@@ -6103,8 +6110,7 @@
6103
  "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
6104
  "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
6105
  "license": "MIT",
6106
- "optional": true,
6107
- "peer": true
6108
  },
6109
  "node_modules/cross-spawn": {
6110
  "version": "7.0.6",
@@ -6904,7 +6910,6 @@
6904
  "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
6905
  "license": "BSD-3-Clause",
6906
  "optional": true,
6907
- "peer": true,
6908
  "engines": {
6909
  "node": ">=0.3.1"
6910
  }
@@ -7455,6 +7460,7 @@
7455
  "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
7456
  "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
7457
  "license": "MIT",
 
7458
  "dependencies": {
7459
  "@eslint-community/eslint-utils": "^4.2.0",
7460
  "@eslint-community/regexpp": "^4.6.1",
@@ -10210,6 +10216,7 @@
10210
  "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
10211
  "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
10212
  "license": "MIT",
 
10213
  "dependencies": {
10214
  "@jest/core": "^27.5.1",
10215
  "import-local": "^3.0.2",
@@ -11095,6 +11102,7 @@
11095
  "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
11096
  "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
11097
  "license": "MIT",
 
11098
  "bin": {
11099
  "jiti": "bin/jiti.js"
11100
  }
@@ -11539,8 +11547,7 @@
11539
  "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
11540
  "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
11541
  "license": "ISC",
11542
- "optional": true,
11543
- "peer": true
11544
  },
11545
  "node_modules/makeerror": {
11546
  "version": "1.0.12",
@@ -12458,6 +12465,7 @@
12458
  }
12459
  ],
12460
  "license": "MIT",
 
12461
  "dependencies": {
12462
  "nanoid": "^3.3.11",
12463
  "picocolors": "^1.1.1",
@@ -13592,6 +13600,7 @@
13592
  "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
13593
  "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
13594
  "license": "MIT",
 
13595
  "dependencies": {
13596
  "cssesc": "^3.0.0",
13597
  "util-deprecate": "^1.0.2"
@@ -13952,6 +13961,7 @@
13952
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
13953
  "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
13954
  "license": "MIT",
 
13955
  "engines": {
13956
  "node": ">=0.10.0"
13957
  }
@@ -14083,6 +14093,7 @@
14083
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
14084
  "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
14085
  "license": "MIT",
 
14086
  "dependencies": {
14087
  "scheduler": "^0.27.0"
14088
  },
@@ -14100,13 +14111,15 @@
14100
  "version": "17.0.2",
14101
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
14102
  "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
14103
- "license": "MIT"
 
14104
  },
14105
  "node_modules/react-redux": {
14106
  "version": "9.2.0",
14107
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
14108
  "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
14109
  "license": "MIT",
 
14110
  "dependencies": {
14111
  "@types/use-sync-external-store": "^0.0.6",
14112
  "use-sync-external-store": "^1.4.0"
@@ -14130,6 +14143,7 @@
14130
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
14131
  "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
14132
  "license": "MIT",
 
14133
  "engines": {
14134
  "node": ">=0.10.0"
14135
  }
@@ -14368,7 +14382,8 @@
14368
  "version": "5.0.1",
14369
  "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
14370
  "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
14371
- "license": "MIT"
 
14372
  },
14373
  "node_modules/redux-thunk": {
14374
  "version": "3.1.0",
@@ -14694,6 +14709,7 @@
14694
  "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
14695
  "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
14696
  "license": "MIT",
 
14697
  "bin": {
14698
  "rollup": "dist/bin/rollup"
14699
  },
@@ -14936,6 +14952,7 @@
14936
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
14937
  "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
14938
  "license": "MIT",
 
14939
  "dependencies": {
14940
  "fast-deep-equal": "^3.1.3",
14941
  "fast-uri": "^3.0.1",
@@ -16286,23 +16303,6 @@
16286
  }
16287
  }
16288
  },
16289
- "node_modules/tailwindcss/node_modules/yaml": {
16290
- "version": "2.8.2",
16291
- "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz",
16292
- "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==",
16293
- "license": "ISC",
16294
- "optional": true,
16295
- "peer": true,
16296
- "bin": {
16297
- "yaml": "bin.mjs"
16298
- },
16299
- "engines": {
16300
- "node": ">= 14.6"
16301
- },
16302
- "funding": {
16303
- "url": "https://github.com/sponsors/eemeli"
16304
- }
16305
- },
16306
  "node_modules/tapable": {
16307
  "version": "2.3.0",
16308
  "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
@@ -16526,6 +16526,7 @@
16526
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
16527
  "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
16528
  "license": "MIT",
 
16529
  "engines": {
16530
  "node": ">=12"
16531
  },
@@ -16614,7 +16615,6 @@
16614
  "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
16615
  "license": "MIT",
16616
  "optional": true,
16617
- "peer": true,
16618
  "dependencies": {
16619
  "@cspotcode/source-map-support": "^0.8.0",
16620
  "@tsconfig/node10": "^1.0.7",
@@ -16659,7 +16659,6 @@
16659
  "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
16660
  "license": "MIT",
16661
  "optional": true,
16662
- "peer": true,
16663
  "dependencies": {
16664
  "acorn": "^8.11.0"
16665
  },
@@ -16672,8 +16671,7 @@
16672
  "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
16673
  "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
16674
  "license": "MIT",
16675
- "optional": true,
16676
- "peer": true
16677
  },
16678
  "node_modules/tsconfig-paths": {
16679
  "version": "3.15.0",
@@ -16761,6 +16759,7 @@
16761
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
16762
  "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
16763
  "license": "(MIT OR CC0-1.0)",
 
16764
  "engines": {
16765
  "node": ">=10"
16766
  },
@@ -17102,8 +17101,7 @@
17102
  "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
17103
  "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
17104
  "license": "MIT",
17105
- "optional": true,
17106
- "peer": true
17107
  },
17108
  "node_modules/v8-to-istanbul": {
17109
  "version": "8.1.1",
@@ -17229,6 +17227,7 @@
17229
  "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz",
17230
  "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
17231
  "license": "MIT",
 
17232
  "dependencies": {
17233
  "@types/eslint-scope": "^3.7.7",
17234
  "@types/estree": "^1.0.8",
@@ -17300,6 +17299,7 @@
17300
  "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
17301
  "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
17302
  "license": "MIT",
 
17303
  "dependencies": {
17304
  "@types/bonjour": "^3.5.9",
17305
  "@types/connect-history-api-fallback": "^1.3.5",
@@ -17712,6 +17712,7 @@
17712
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
17713
  "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
17714
  "license": "MIT",
 
17715
  "dependencies": {
17716
  "fast-deep-equal": "^3.1.3",
17717
  "fast-uri": "^3.0.1",
@@ -18087,7 +18088,6 @@
18087
  "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
18088
  "license": "MIT",
18089
  "optional": true,
18090
- "peer": true,
18091
  "engines": {
18092
  "node": ">=6"
18093
  }
 
22
  "web-vitals": "^2.1.4"
23
  },
24
  "devDependencies": {
25
+ "@types/node": "^25.0.3",
26
  "@types/react": "^19.2.7",
27
  "@types/react-dom": "^19.2.3",
28
  "autoprefixer": "^10.4.22",
 
76
  "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.5.tgz",
77
  "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==",
78
  "license": "MIT",
79
+ "peer": true,
80
  "dependencies": {
81
  "@babel/code-frame": "^7.27.1",
82
  "@babel/generator": "^7.28.5",
 
726
  "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.27.1.tgz",
727
  "integrity": "sha512-p9OkPbZ5G7UT1MofwYFigGebnrzGJacoBSQM0/6bi/PUMVE+qlWDD/OalvQKbwgQzU6dl0xAv6r4X7Jme0RYxA==",
728
  "license": "MIT",
729
+ "peer": true,
730
  "dependencies": {
731
  "@babel/helper-plugin-utils": "^7.27.1"
732
  },
 
1610
  "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.27.1.tgz",
1611
  "integrity": "sha512-2KH4LWGSrJIkVf5tSiBFYuXDAoWRq2MMwgivCf+93dd0GQi8RXLjKA/0EvRnVV5G0hrHczsquXuD01L8s6dmBw==",
1612
  "license": "MIT",
1613
+ "peer": true,
1614
  "dependencies": {
1615
  "@babel/helper-annotate-as-pure": "^7.27.1",
1616
  "@babel/helper-module-imports": "^7.27.1",
 
2101
  "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==",
2102
  "license": "MIT",
2103
  "optional": true,
 
2104
  "dependencies": {
2105
  "@jridgewell/trace-mapping": "0.3.9"
2106
  },
 
2114
  "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==",
2115
  "license": "MIT",
2116
  "optional": true,
 
2117
  "dependencies": {
2118
  "@jridgewell/resolve-uri": "^3.0.3",
2119
  "@jridgewell/sourcemap-codec": "^1.4.10"
 
3412
  "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz",
3413
  "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
3414
  "license": "MIT",
3415
+ "peer": true,
3416
  "dependencies": {
3417
  "@babel/code-frame": "^7.10.4",
3418
  "@babel/runtime": "^7.12.5",
 
3518
  "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.12.tgz",
3519
  "integrity": "sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==",
3520
  "license": "MIT",
3521
+ "optional": true
 
3522
  },
3523
  "node_modules/@tsconfig/node12": {
3524
  "version": "1.0.11",
3525
  "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz",
3526
  "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==",
3527
  "license": "MIT",
3528
+ "optional": true
 
3529
  },
3530
  "node_modules/@tsconfig/node14": {
3531
  "version": "1.0.3",
3532
  "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz",
3533
  "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==",
3534
  "license": "MIT",
3535
+ "optional": true
 
3536
  },
3537
  "node_modules/@tsconfig/node16": {
3538
  "version": "1.0.4",
3539
  "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz",
3540
  "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==",
3541
  "license": "MIT",
3542
+ "optional": true
 
3543
  },
3544
  "node_modules/@types/aria-query": {
3545
  "version": "5.0.4",
 
3824
  "license": "MIT"
3825
  },
3826
  "node_modules/@types/node": {
3827
+ "version": "25.0.3",
3828
+ "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.3.tgz",
3829
+ "integrity": "sha512-W609buLVRVmeW693xKfzHeIV6nJGGz98uCPfeXI1ELMLXVeKYZ9m15fAMSaUPBHYLGFsVRcMmSCksQOrZV9BYA==",
3830
  "license": "MIT",
3831
+ "peer": true,
3832
  "dependencies": {
3833
  "undici-types": "~7.16.0"
3834
  }
 
3878
  "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==",
3879
  "devOptional": true,
3880
  "license": "MIT",
3881
+ "peer": true,
3882
  "dependencies": {
3883
  "csstype": "^3.2.2"
3884
  }
 
3889
  "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==",
3890
  "devOptional": true,
3891
  "license": "MIT",
3892
+ "peer": true,
3893
  "peerDependencies": {
3894
  "@types/react": "^19.2.0"
3895
  }
 
4010
  "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz",
4011
  "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==",
4012
  "license": "MIT",
4013
+ "peer": true,
4014
  "dependencies": {
4015
  "@eslint-community/regexpp": "^4.4.0",
4016
  "@typescript-eslint/scope-manager": "5.62.0",
 
4064
  "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz",
4065
  "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==",
4066
  "license": "BSD-2-Clause",
4067
+ "peer": true,
4068
  "dependencies": {
4069
  "@typescript-eslint/scope-manager": "5.62.0",
4070
  "@typescript-eslint/types": "5.62.0",
 
4434
  "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz",
4435
  "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==",
4436
  "license": "MIT",
4437
+ "peer": true,
4438
  "bin": {
4439
  "acorn": "bin/acorn"
4440
  },
 
4533
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
4534
  "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
4535
  "license": "MIT",
4536
+ "peer": true,
4537
  "dependencies": {
4538
  "fast-deep-equal": "^3.1.1",
4539
  "fast-json-stable-stringify": "^2.0.0",
 
5441
  }
5442
  ],
5443
  "license": "MIT",
5444
+ "peer": true,
5445
  "dependencies": {
5446
  "baseline-browser-mapping": "^2.9.0",
5447
  "caniuse-lite": "^1.0.30001759",
 
6110
  "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz",
6111
  "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==",
6112
  "license": "MIT",
6113
+ "optional": true
 
6114
  },
6115
  "node_modules/cross-spawn": {
6116
  "version": "7.0.6",
 
6910
  "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==",
6911
  "license": "BSD-3-Clause",
6912
  "optional": true,
 
6913
  "engines": {
6914
  "node": ">=0.3.1"
6915
  }
 
7460
  "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==",
7461
  "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.",
7462
  "license": "MIT",
7463
+ "peer": true,
7464
  "dependencies": {
7465
  "@eslint-community/eslint-utils": "^4.2.0",
7466
  "@eslint-community/regexpp": "^4.6.1",
 
10216
  "resolved": "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz",
10217
  "integrity": "sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ==",
10218
  "license": "MIT",
10219
+ "peer": true,
10220
  "dependencies": {
10221
  "@jest/core": "^27.5.1",
10222
  "import-local": "^3.0.2",
 
11102
  "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz",
11103
  "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==",
11104
  "license": "MIT",
11105
+ "peer": true,
11106
  "bin": {
11107
  "jiti": "bin/jiti.js"
11108
  }
 
11547
  "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz",
11548
  "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==",
11549
  "license": "ISC",
11550
+ "optional": true
 
11551
  },
11552
  "node_modules/makeerror": {
11553
  "version": "1.0.12",
 
12465
  }
12466
  ],
12467
  "license": "MIT",
12468
+ "peer": true,
12469
  "dependencies": {
12470
  "nanoid": "^3.3.11",
12471
  "picocolors": "^1.1.1",
 
13600
  "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
13601
  "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
13602
  "license": "MIT",
13603
+ "peer": true,
13604
  "dependencies": {
13605
  "cssesc": "^3.0.0",
13606
  "util-deprecate": "^1.0.2"
 
13961
  "resolved": "https://registry.npmjs.org/react/-/react-19.2.1.tgz",
13962
  "integrity": "sha512-DGrYcCWK7tvYMnWh79yrPHt+vdx9tY+1gPZa7nJQtO/p8bLTDaHp4dzwEhQB7pZ4Xe3ok4XKuEPrVuc+wlpkmw==",
13963
  "license": "MIT",
13964
+ "peer": true,
13965
  "engines": {
13966
  "node": ">=0.10.0"
13967
  }
 
14093
  "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.1.tgz",
14094
  "integrity": "sha512-ibrK8llX2a4eOskq1mXKu/TGZj9qzomO+sNfO98M6d9zIPOEhlBkMkBUBLd1vgS0gQsLDBzA+8jJBVXDnfHmJg==",
14095
  "license": "MIT",
14096
+ "peer": true,
14097
  "dependencies": {
14098
  "scheduler": "^0.27.0"
14099
  },
 
14111
  "version": "17.0.2",
14112
  "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
14113
  "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
14114
+ "license": "MIT",
14115
+ "peer": true
14116
  },
14117
  "node_modules/react-redux": {
14118
  "version": "9.2.0",
14119
  "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.2.0.tgz",
14120
  "integrity": "sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==",
14121
  "license": "MIT",
14122
+ "peer": true,
14123
  "dependencies": {
14124
  "@types/use-sync-external-store": "^0.0.6",
14125
  "use-sync-external-store": "^1.4.0"
 
14143
  "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz",
14144
  "integrity": "sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A==",
14145
  "license": "MIT",
14146
+ "peer": true,
14147
  "engines": {
14148
  "node": ">=0.10.0"
14149
  }
 
14382
  "version": "5.0.1",
14383
  "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
14384
  "integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
14385
+ "license": "MIT",
14386
+ "peer": true
14387
  },
14388
  "node_modules/redux-thunk": {
14389
  "version": "3.1.0",
 
14709
  "resolved": "https://registry.npmjs.org/rollup/-/rollup-2.79.2.tgz",
14710
  "integrity": "sha512-fS6iqSPZDs3dr/y7Od6y5nha8dW1YnbgtsyotCVvoFGKbERG++CVRFv1meyGDE1SNItQA8BrnCw7ScdAhRJ3XQ==",
14711
  "license": "MIT",
14712
+ "peer": true,
14713
  "bin": {
14714
  "rollup": "dist/bin/rollup"
14715
  },
 
14952
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
14953
  "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
14954
  "license": "MIT",
14955
+ "peer": true,
14956
  "dependencies": {
14957
  "fast-deep-equal": "^3.1.3",
14958
  "fast-uri": "^3.0.1",
 
16303
  }
16304
  }
16305
  },
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
16306
  "node_modules/tapable": {
16307
  "version": "2.3.0",
16308
  "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz",
 
16526
  "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz",
16527
  "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==",
16528
  "license": "MIT",
16529
+ "peer": true,
16530
  "engines": {
16531
  "node": ">=12"
16532
  },
 
16615
  "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==",
16616
  "license": "MIT",
16617
  "optional": true,
 
16618
  "dependencies": {
16619
  "@cspotcode/source-map-support": "^0.8.0",
16620
  "@tsconfig/node10": "^1.0.7",
 
16659
  "integrity": "sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==",
16660
  "license": "MIT",
16661
  "optional": true,
 
16662
  "dependencies": {
16663
  "acorn": "^8.11.0"
16664
  },
 
16671
  "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz",
16672
  "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==",
16673
  "license": "MIT",
16674
+ "optional": true
 
16675
  },
16676
  "node_modules/tsconfig-paths": {
16677
  "version": "3.15.0",
 
16759
  "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
16760
  "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
16761
  "license": "(MIT OR CC0-1.0)",
16762
+ "peer": true,
16763
  "engines": {
16764
  "node": ">=10"
16765
  },
 
17101
  "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz",
17102
  "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==",
17103
  "license": "MIT",
17104
+ "optional": true
 
17105
  },
17106
  "node_modules/v8-to-istanbul": {
17107
  "version": "8.1.1",
 
17227
  "resolved": "https://registry.npmjs.org/webpack/-/webpack-5.103.0.tgz",
17228
  "integrity": "sha512-HU1JOuV1OavsZ+mfigY0j8d1TgQgbZ6M+J75zDkpEAwYeXjWSqrGJtgnPblJjd/mAyTNQ7ygw0MiKOn6etz8yw==",
17229
  "license": "MIT",
17230
+ "peer": true,
17231
  "dependencies": {
17232
  "@types/eslint-scope": "^3.7.7",
17233
  "@types/estree": "^1.0.8",
 
17299
  "resolved": "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.15.2.tgz",
17300
  "integrity": "sha512-0XavAZbNJ5sDrCbkpWL8mia0o5WPOd2YGtxrEiZkBK9FjLppIUK2TgxK6qGD2P3hUXTJNNPVibrerKcx5WkR1g==",
17301
  "license": "MIT",
17302
+ "peer": true,
17303
  "dependencies": {
17304
  "@types/bonjour": "^3.5.9",
17305
  "@types/connect-history-api-fallback": "^1.3.5",
 
17712
  "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
17713
  "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
17714
  "license": "MIT",
17715
+ "peer": true,
17716
  "dependencies": {
17717
  "fast-deep-equal": "^3.1.3",
17718
  "fast-uri": "^3.0.1",
 
18088
  "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==",
18089
  "license": "MIT",
18090
  "optional": true,
 
18091
  "engines": {
18092
  "node": ">=6"
18093
  }
package.json CHANGED
@@ -17,6 +17,7 @@
17
  "web-vitals": "^2.1.4"
18
  },
19
  "devDependencies": {
 
20
  "@types/react": "^19.2.7",
21
  "@types/react-dom": "^19.2.3",
22
  "autoprefixer": "^10.4.22",
 
17
  "web-vitals": "^2.1.4"
18
  },
19
  "devDependencies": {
20
+ "@types/node": "^25.0.3",
21
  "@types/react": "^19.2.7",
22
  "@types/react-dom": "^19.2.3",
23
  "autoprefixer": "^10.4.22",
src/app/components/chat/ChatInput.tsx CHANGED
@@ -4,8 +4,8 @@ import { useMCPTools } from '../../hooks/useMCPTools.ts';
4
  import type { MCPTool } from '../../types/index.ts';
5
 
6
  type ChatInputProps = {
7
- onSubmit?: (message: string, selectedTool?: string | null) => void;
8
- onAudioSubmit?: (audioBlob: Blob, selectedTool?: string | null) => void;
9
  placeholder?: string;
10
  };
11
 
@@ -14,11 +14,39 @@ const isMCPTool = (value: any): value is MCPTool => {
14
  return value && typeof value === 'object' && typeof value.name === 'string';
15
  };
16
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
17
  const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...' }: ChatInputProps) => {
18
  const [input, setInput] = useState('');
19
  const [isRecording, setIsRecording] = useState(false);
20
  const [showToolsDropdown, setShowToolsDropdown] = useState(false);
21
  const [selectedTool, setSelectedTool] = useState<string | null>(null);
 
22
  const [searchQuery, setSearchQuery] = useState('');
23
  const [focusedIndex, setFocusedIndex] = useState(-1);
24
  const [dropdownPosition, setDropdownPosition] = useState<'below' | 'above'>('below');
@@ -36,15 +64,21 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
36
  const recordingTimerRef = useRef<NodeJS.Timeout | null>(null);
37
  const { tools, loading, error, refetch } = useMCPTools();
38
 
39
- const handleSubmit = (e: any) => {
40
  e.preventDefault();
 
41
  if (input.trim()) {
42
  if (onSubmit) {
43
- onSubmit(input, selectedTool);
44
  }
45
  console.log('Submitted:', input);
46
  setInput('');
 
 
 
 
47
  }
 
48
  };
49
 
50
  const handleMicClick = async () => {
@@ -207,7 +241,11 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
207
 
208
  // Submit the recorded audio if available
209
  if (audioBlob && onAudioSubmit) {
210
- onAudioSubmit(audioBlob, selectedTool);
 
 
 
 
211
  }
212
 
213
  // Clean up and return to normal chat mode
@@ -518,9 +556,13 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
518
  e.preventDefault();
519
  if (input.trim()) {
520
  if (onSubmit) {
521
- onSubmit(input, selectedTool);
522
  }
523
  setInput('');
 
 
 
 
524
  }
525
  }
526
  }}
@@ -534,6 +576,35 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
534
  }}
535
  />
536
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
537
  <div className="flex items-center justify-between mt-8">
538
  <div className="flex items-center gap-2">
539
  <button
@@ -619,7 +690,7 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
619
  </div>
620
  </div>
621
  </div>
622
- ) : tools.filter(isMCPTool).length === 0 ? (
623
  <div className="px-4 py-6">
624
  <div className="flex flex-col items-center gap-3 text-center">
625
  <div className="h-12 w-12 rounded-full bg-zinc-100 dark:bg-zinc-800 flex items-center justify-center">
@@ -635,6 +706,7 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
635
  <div className="p-2">
636
  {tools
637
  .filter(isMCPTool)
 
638
  .filter(tool =>
639
  tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
640
  (tool.description && tool.description.toLowerCase().includes(searchQuery.toLowerCase()))
@@ -651,7 +723,12 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
651
  : 'hover:bg-zinc-50 dark:hover:bg-zinc-800/50 border border-transparent'
652
  }`}
653
  onClick={() => {
654
- setSelectedTool((current) => (current === tool.name ? null : tool.name));
 
 
 
 
 
655
  setShowToolsDropdown(false);
656
  setFocusedIndex(-1);
657
  }}
@@ -690,6 +767,7 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
690
  type="button"
691
  onClick={() => {
692
  setSelectedTool(null);
 
693
  setShowToolsDropdown(false);
694
  setFocusedIndex(-1);
695
  }}
@@ -721,14 +799,6 @@ const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...'
721
 
722
  <button
723
  type="submit"
724
- onClick={() => {
725
- if (input.trim()) {
726
- if (onSubmit) {
727
- onSubmit(input, selectedTool);
728
- }
729
- setInput('');
730
- }
731
- }}
732
  disabled={!input.trim()}
733
  className="h-8 w-8 p-0 bg-zinc-300 dark:bg-zinc-700 hover:bg-zinc-400 dark:hover:bg-zinc-600 disabled:bg-zinc-200 dark:disabled:bg-zinc-800 disabled:text-zinc-400 dark:disabled:text-zinc-500 text-zinc-800 dark:text-white rounded-lg transition-all duration-200 hover:scale-110 disabled:hover:scale-100 flex items-center justify-center disabled:cursor-not-allowed"
734
  >
 
4
  import type { MCPTool } from '../../types/index.ts';
5
 
6
  type ChatInputProps = {
7
+ onSubmit?: (message: string, selectedTool?: string | null, stance?: 'positive' | 'negative') => void;
8
+ onAudioSubmit?: (audioBlob: Blob, selectedTool?: string | null, stance?: 'positive' | 'negative') => void;
9
  placeholder?: string;
10
  };
11
 
 
14
  return value && typeof value === 'object' && typeof value.name === 'string';
15
  };
16
 
17
+ // Allowed tools - only these 3 will be shown
18
+ const ALLOWED_TOOLS = ['detect stance', 'generate argument', 'extract topic'];
19
+
20
+ // Helper function to check if a tool name matches one of the allowed tools
21
+ const isAllowedTool = (toolName: string): boolean => {
22
+ const toolLower = toolName.toLowerCase();
23
+ return ALLOWED_TOOLS.some(allowed =>
24
+ toolLower.includes(allowed.split(' ')[0]) &&
25
+ toolLower.includes(allowed.split(' ')[1])
26
+ );
27
+ };
28
+
29
+ // Helper function to normalize tool name to standard format
30
+ const normalizeToolName = (toolName: string): string => {
31
+ const toolLower = toolName.toLowerCase();
32
+ if (toolLower.includes('detect') && toolLower.includes('stance')) {
33
+ return 'detect stance';
34
+ }
35
+ if (toolLower.includes('generate') && toolLower.includes('argument')) {
36
+ return 'generate argument';
37
+ }
38
+ if (toolLower.includes('extract') && toolLower.includes('topic')) {
39
+ return 'extract topic';
40
+ }
41
+ return toolName;
42
+ };
43
+
44
  const ChatInput = ({ onSubmit, onAudioSubmit, placeholder = 'Ask a follow-up...' }: ChatInputProps) => {
45
  const [input, setInput] = useState('');
46
  const [isRecording, setIsRecording] = useState(false);
47
  const [showToolsDropdown, setShowToolsDropdown] = useState(false);
48
  const [selectedTool, setSelectedTool] = useState<string | null>(null);
49
+ const [selectedStance, setSelectedStance] = useState<'positive' | 'negative' | null>(null);
50
  const [searchQuery, setSearchQuery] = useState('');
51
  const [focusedIndex, setFocusedIndex] = useState(-1);
52
  const [dropdownPosition, setDropdownPosition] = useState<'below' | 'above'>('below');
 
64
  const recordingTimerRef = useRef<NodeJS.Timeout | null>(null);
65
  const { tools, loading, error, refetch } = useMCPTools();
66
 
67
+ const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
68
  e.preventDefault();
69
+ e.stopPropagation();
70
  if (input.trim()) {
71
  if (onSubmit) {
72
+ onSubmit(input, selectedTool, selectedStance || undefined);
73
  }
74
  console.log('Submitted:', input);
75
  setInput('');
76
+ // Reset stance after submit if generate argument tool
77
+ if (selectedTool && normalizeToolName(selectedTool) === 'generate argument') {
78
+ setSelectedStance(null);
79
+ }
80
  }
81
+ return false;
82
  };
83
 
84
  const handleMicClick = async () => {
 
241
 
242
  // Submit the recorded audio if available
243
  if (audioBlob && onAudioSubmit) {
244
+ onAudioSubmit(audioBlob, selectedTool, selectedStance || undefined);
245
+ // Reset stance after submit if generate argument tool
246
+ if (selectedTool && normalizeToolName(selectedTool) === 'generate argument') {
247
+ setSelectedStance(null);
248
+ }
249
  }
250
 
251
  // Clean up and return to normal chat mode
 
556
  e.preventDefault();
557
  if (input.trim()) {
558
  if (onSubmit) {
559
+ onSubmit(input, selectedTool, selectedStance || undefined);
560
  }
561
  setInput('');
562
+ // Reset stance after submit if generate argument tool
563
+ if (selectedTool && normalizeToolName(selectedTool) === 'generate argument') {
564
+ setSelectedStance(null);
565
+ }
566
  }
567
  }
568
  }}
 
576
  }}
577
  />
578
 
579
+ {/* Stance selection buttons for generate argument tool */}
580
+ {selectedTool && normalizeToolName(selectedTool) === 'generate argument' && (
581
+ <div className="mt-3 flex items-center gap-2">
582
+ <span className="text-xs font-medium text-zinc-600 dark:text-zinc-400">Select stance:</span>
583
+ <button
584
+ type="button"
585
+ onClick={() => setSelectedStance(selectedStance === 'positive' ? null : 'positive')}
586
+ className={`px-3 py-1.5 text-xs rounded-full font-medium transition-all ${
587
+ selectedStance === 'positive'
588
+ ? 'bg-emerald-500 text-white shadow-md'
589
+ : 'bg-emerald-100 text-emerald-800 hover:bg-emerald-200 dark:bg-emerald-900/40 dark:text-emerald-200 dark:hover:bg-emerald-900/60'
590
+ }`}
591
+ >
592
+ Positive
593
+ </button>
594
+ <button
595
+ type="button"
596
+ onClick={() => setSelectedStance(selectedStance === 'negative' ? null : 'negative')}
597
+ className={`px-3 py-1.5 text-xs rounded-full font-medium transition-all ${
598
+ selectedStance === 'negative'
599
+ ? 'bg-rose-500 text-white shadow-md'
600
+ : 'bg-rose-100 text-rose-800 hover:bg-rose-200 dark:bg-rose-900/40 dark:text-rose-200 dark:hover:bg-rose-900/60'
601
+ }`}
602
+ >
603
+ Negative
604
+ </button>
605
+ </div>
606
+ )}
607
+
608
  <div className="flex items-center justify-between mt-8">
609
  <div className="flex items-center gap-2">
610
  <button
 
690
  </div>
691
  </div>
692
  </div>
693
+ ) : tools.filter(isMCPTool).filter(tool => isAllowedTool(tool.name)).length === 0 ? (
694
  <div className="px-4 py-6">
695
  <div className="flex flex-col items-center gap-3 text-center">
696
  <div className="h-12 w-12 rounded-full bg-zinc-100 dark:bg-zinc-800 flex items-center justify-center">
 
706
  <div className="p-2">
707
  {tools
708
  .filter(isMCPTool)
709
+ .filter(tool => isAllowedTool(tool.name))
710
  .filter(tool =>
711
  tool.name.toLowerCase().includes(searchQuery.toLowerCase()) ||
712
  (tool.description && tool.description.toLowerCase().includes(searchQuery.toLowerCase()))
 
723
  : 'hover:bg-zinc-50 dark:hover:bg-zinc-800/50 border border-transparent'
724
  }`}
725
  onClick={() => {
726
+ const newTool = tool.name === selectedTool ? null : tool.name;
727
+ setSelectedTool(newTool);
728
+ // Reset stance when tool changes
729
+ if (newTool && normalizeToolName(newTool) !== 'generate argument') {
730
+ setSelectedStance(null);
731
+ }
732
  setShowToolsDropdown(false);
733
  setFocusedIndex(-1);
734
  }}
 
767
  type="button"
768
  onClick={() => {
769
  setSelectedTool(null);
770
+ setSelectedStance(null);
771
  setShowToolsDropdown(false);
772
  setFocusedIndex(-1);
773
  }}
 
799
 
800
  <button
801
  type="submit"
 
 
 
 
 
 
 
 
802
  disabled={!input.trim()}
803
  className="h-8 w-8 p-0 bg-zinc-300 dark:bg-zinc-700 hover:bg-zinc-400 dark:hover:bg-zinc-600 disabled:bg-zinc-200 dark:disabled:bg-zinc-800 disabled:text-zinc-400 dark:disabled:text-zinc-500 text-zinc-800 dark:text-white rounded-lg transition-all duration-200 hover:scale-110 disabled:hover:scale-100 flex items-center justify-center disabled:cursor-not-allowed"
804
  >
src/app/components/chat/MessageList.tsx CHANGED
@@ -28,7 +28,50 @@ const MessageList = ({ messages, isLoading = false, error = null, onRetry }: Mes
28
  });
29
  };
30
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
31
  return (
 
 
32
  <div className="flex-1 overflow-y-auto px-4 py-6 space-y-4">
33
  {messages.length === 0 && !isLoading && (
34
  <div className="flex flex-col items-center justify-center h-full text-center">
@@ -48,7 +91,8 @@ const MessageList = ({ messages, isLoading = false, error = null, onRetry }: Mes
48
  </div>
49
  )}
50
 
51
- {messages.map((message) => (
 
52
  <div
53
  key={message.id}
54
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
@@ -58,22 +102,47 @@ const MessageList = ({ messages, isLoading = false, error = null, onRetry }: Mes
58
  message.role === 'user'
59
  ? 'bg-teal-500 text-white ml-12'
60
  : 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 mr-12'
61
- }`}
62
  >
63
  {message.audioUrl ? (
64
- // Audio message display - only the player itself
65
  <div>
66
- <AudioPlayer
67
- src={message.audioUrl}
68
- />
69
  </div>
70
  ) : (
71
  // Regular text message
72
  <div>
73
- <div className="whitespace-pre-wrap break-words">{message.content}</div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
74
  </div>
75
  )}
76
- <div
77
  className={`text-xs mt-1 ${
78
  message.role === 'user'
79
  ? 'text-teal-100'
@@ -84,7 +153,7 @@ const MessageList = ({ messages, isLoading = false, error = null, onRetry }: Mes
84
  </div>
85
  </div>
86
  </div>
87
- ))}
88
 
89
  {isLoading && (
90
  <div className="flex justify-start">
 
28
  });
29
  };
30
 
31
+ const getToolClasses = (message: ChatMessage) => {
32
+ if (message.role !== 'assistant' || !message.tool) return '';
33
+
34
+ const toolLower = message.tool.toLowerCase();
35
+
36
+ // Match tool names flexibly (case-insensitive, handles variations)
37
+ if (toolLower.includes('detect') && toolLower.includes('stance')) {
38
+ return 'border-l-4 border-blue-400 pl-3 bg-blue-50/30 dark:bg-blue-900/10';
39
+ }
40
+ if (toolLower.includes('generate') && toolLower.includes('argument')) {
41
+ return 'border-l-4 border-purple-400 pl-3 bg-purple-50/30 dark:bg-purple-900/10';
42
+ }
43
+ if (toolLower.includes('extract') && toolLower.includes('topic')) {
44
+ return 'border-l-4 border-emerald-400 pl-3 bg-emerald-50/30 dark:bg-emerald-900/10';
45
+ }
46
+
47
+ // Default styling for other tools
48
+ return 'border-l-4 border-teal-400 pl-3 bg-teal-50/30 dark:bg-teal-900/10';
49
+ };
50
+
51
+ const getToolLabel = (tool: string | null | undefined): string => {
52
+ if (!tool) return '';
53
+
54
+ const toolLower = tool.toLowerCase();
55
+
56
+ if (toolLower.includes('detect') && toolLower.includes('stance')) {
57
+ return 'Detect Stance';
58
+ }
59
+ if (toolLower.includes('generate') && toolLower.includes('argument')) {
60
+ return 'Generate Argument';
61
+ }
62
+ if (toolLower.includes('extract') && toolLower.includes('topic')) {
63
+ return 'Extract Topic';
64
+ }
65
+
66
+ // Return formatted tool name (capitalize first letter of each word)
67
+ return tool.split(/[\s_-]+/).map(word =>
68
+ word.charAt(0).toUpperCase() + word.slice(1).toLowerCase()
69
+ ).join(' ');
70
+ };
71
+
72
  return (
73
+
74
+
75
  <div className="flex-1 overflow-y-auto px-4 py-6 space-y-4">
76
  {messages.length === 0 && !isLoading && (
77
  <div className="flex flex-col items-center justify-center h-full text-center">
 
91
  </div>
92
  )}
93
 
94
+ {messages.map((message) => {
95
+ return(
96
  <div
97
  key={message.id}
98
  className={`flex ${message.role === 'user' ? 'justify-end' : 'justify-start'}`}
 
102
  message.role === 'user'
103
  ? 'bg-teal-500 text-white ml-12'
104
  : 'bg-gray-100 dark:bg-gray-800 text-gray-900 dark:text-gray-100 mr-12'
105
+ }${getToolClasses(message)}`} // Apply tool-specific classes
106
  >
107
  {message.audioUrl ? (
108
+ // Audio message display
109
  <div>
110
+ <AudioPlayer src={message.audioUrl} />
 
 
111
  </div>
112
  ) : (
113
  // Regular text message
114
  <div>
115
+ {message.tool && message.role === 'assistant' && (
116
+ <div className="mb-2 text-xs font-semibold uppercase tracking-wide text-gray-500 dark:text-gray-400">
117
+ {getToolLabel(message.tool)}
118
+ </div>
119
+ )}
120
+
121
+ <div className="whitespace-pre-wrap break-words">
122
+ {message.content}
123
+ </div>
124
+
125
+ {message.role === 'assistant' && message.tool &&
126
+ message.tool.toLowerCase().includes('generate') &&
127
+ message.tool.toLowerCase().includes('argument') && (
128
+ <div className="mt-3 flex gap-2">
129
+ <button
130
+ type="button"
131
+ className="px-3 py-1 text-xs rounded-full bg-emerald-100 text-emerald-800 hover:bg-emerald-200 dark:bg-emerald-900/40 dark:text-emerald-200 transition-colors"
132
+ >
133
+ Positive stance
134
+ </button>
135
+ <button
136
+ type="button"
137
+ className="px-3 py-1 text-xs rounded-full bg-rose-100 text-rose-800 hover:bg-rose-200 dark:bg-rose-900/40 dark:text-rose-200 transition-colors"
138
+ >
139
+ Negative stance
140
+ </button>
141
+ </div>
142
+ )}
143
  </div>
144
  )}
145
+ <div
146
  className={`text-xs mt-1 ${
147
  message.role === 'user'
148
  ? 'text-teal-100'
 
153
  </div>
154
  </div>
155
  </div>
156
+ )})}
157
 
158
  {isLoading && (
159
  <div className="flex justify-start">
src/app/hooks/useChat.ts CHANGED
@@ -28,6 +28,16 @@ export const useChat = () => {
28
 
29
 
30
  const extractTopicAndPosition = useCallback(async (userInput: string): Promise<{ topic: string; position: string }> => {
 
 
 
 
 
 
 
 
 
 
31
  const extractionPrompt = `Extract the debate topic and position from the following text. Return ONLY a JSON object with "topic" and "position" keys. The position should be either "positive" (in favor) or "negative" (against).
32
 
33
  Text: "${userInput}"
@@ -38,16 +48,33 @@ Example output:
38
  try {
39
  const response = await sendChatMessageStream([
40
  { id: generateId(), role: 'user', content: extractionPrompt, timestamp: new Date() }
41
- ], () => {});
42
-
43
- // Parse the JSON response
44
- const jsonMatch = response.match(/\{[^}]+\}/);
 
 
 
 
 
45
  if (jsonMatch) {
46
- return JSON.parse(jsonMatch[0]);
 
 
 
 
 
 
 
 
 
 
47
  }
48
- throw new Error('Failed to extract topic and position');
 
49
  } catch (error) {
50
- throw new Error('Failed to extract topic and position from input');
 
51
  }
52
  }, []);
53
 
@@ -57,9 +84,11 @@ Example output:
57
  // Add user audio message
58
  const userMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
59
  role: 'user',
60
- content: 'Audio message sent', // Placeholder text for audio message
61
- audioUrl: URL.createObjectURL(audioBlob), // Store the audio URL for playback in UI
 
62
  };
 
63
  addMessage(userMessage);
64
 
65
  // Set loading state
@@ -121,12 +150,14 @@ Example output:
121
  }
122
  }, [addMessage]);
123
 
124
- const sendMessage = useCallback(async (content: string, selectedTool?: string | null) => {
125
  // Add user message
126
  const userMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
127
  role: 'user',
128
  content,
 
129
  };
 
130
  addMessage(userMessage);
131
 
132
  // Set loading state
@@ -141,8 +172,20 @@ Example output:
141
 
142
  if (isGenerateArgumentTool) {
143
  console.log('Generate argument tool detected:', selectedTool);
144
- // Extract topic and position using LLM
145
- const { topic, position } = await extractTopicAndPosition(content);
 
 
 
 
 
 
 
 
 
 
 
 
146
 
147
  // Call the external API
148
  const argumentResponse = await generateEnhancedArgument({ topic, position });
@@ -151,7 +194,9 @@ Example output:
151
  const assistantMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
152
  role: 'assistant',
153
  content: argumentResponse.enhanced_argument,
 
154
  };
 
155
  addMessage(assistantMessage);
156
  } else {
157
  // Regular chat - use n8n webhook /debate-assistant endpoint
@@ -191,6 +236,7 @@ Example output:
191
 
192
  // Extract final_argument from response
193
  const finalArgument = data?.final_argument || data?.result?.result?.[0]?.text || 'No response received';
 
194
 
195
  // Parse if final_argument is a JSON string
196
  let finalContent = finalArgument;
@@ -207,6 +253,7 @@ Example output:
207
  const assistantMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
208
  role: 'assistant',
209
  content: finalContent,
 
210
  };
211
  addMessage(assistantMessage);
212
  }
@@ -227,7 +274,7 @@ Example output:
227
  const lastUserMessage = [...state.messages]
228
  .reverse()
229
  .find(msg => msg.role === 'user');
230
-
231
  if (lastUserMessage) {
232
  // Remove the last failed assistant message if exists
233
  setState(prev => {
@@ -241,7 +288,7 @@ Example output:
241
  }
242
  return { ...prev, error: null };
243
  });
244
-
245
  // Resend the user message
246
  sendMessage(lastUserMessage.content);
247
  }
 
28
 
29
 
30
  const extractTopicAndPosition = useCallback(async (userInput: string): Promise<{ topic: string; position: string }> => {
31
+ // Default fallback values
32
+ const fallback = {
33
+ topic: userInput.trim().slice(0, 100) || 'General Discussion',
34
+ position: 'positive'
35
+ };
36
+
37
+ if (!userInput || !userInput.trim()) {
38
+ return fallback;
39
+ }
40
+
41
  const extractionPrompt = `Extract the debate topic and position from the following text. Return ONLY a JSON object with "topic" and "position" keys. The position should be either "positive" (in favor) or "negative" (against).
42
 
43
  Text: "${userInput}"
 
48
  try {
49
  const response = await sendChatMessageStream([
50
  { id: generateId(), role: 'user', content: extractionPrompt, timestamp: new Date() }
51
+ ], () => { });
52
+
53
+ if (!response) {
54
+ console.warn('Empty response from extraction API');
55
+ return fallback;
56
+ }
57
+
58
+ // Try to extract JSON from the response (handles markdown blocks and extra text)
59
+ const jsonMatch = response.match(/\{[\s\S]*?\}/);
60
  if (jsonMatch) {
61
+ try {
62
+ const parsed = JSON.parse(jsonMatch[0]);
63
+ return {
64
+ topic: parsed.topic || fallback.topic,
65
+ position: (parsed.position?.toLowerCase().includes('neg')) ? 'negative' : 'positive'
66
+ };
67
+ } catch (parseError) {
68
+ console.error('Failed to parse extracted JSON:', parseError, 'Raw match:', jsonMatch[0]);
69
+ }
70
+ } else {
71
+ console.warn('No JSON structure found in extraction response:', response);
72
  }
73
+
74
+ return fallback;
75
  } catch (error) {
76
+ console.error('Error in extractTopicAndPosition process:', error);
77
+ return fallback;
78
  }
79
  }, []);
80
 
 
84
  // Add user audio message
85
  const userMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
86
  role: 'user',
87
+ content: 'Audio message sent',
88
+ audioUrl: URL.createObjectURL(audioBlob),
89
+ tool: selectedTool ?? null,
90
  };
91
+
92
  addMessage(userMessage);
93
 
94
  // Set loading state
 
150
  }
151
  }, [addMessage]);
152
 
153
+ const sendMessage = useCallback(async (content: string, selectedTool?: string | null, stance?: 'positive' | 'negative') => {
154
  // Add user message
155
  const userMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
156
  role: 'user',
157
  content,
158
+ tool: selectedTool ?? null,
159
  };
160
+
161
  addMessage(userMessage);
162
 
163
  // Set loading state
 
172
 
173
  if (isGenerateArgumentTool) {
174
  console.log('Generate argument tool detected:', selectedTool);
175
+ // Extract topic and position from content
176
+ let topic: string;
177
+ let position: string;
178
+
179
+ try {
180
+ const extracted = await extractTopicAndPosition(content);
181
+ topic = extracted.topic;
182
+ // Use provided stance if available, otherwise use extracted position
183
+ position = stance || extracted.position;
184
+ } catch (error) {
185
+ console.error('Unexpected error in topic extraction:', error);
186
+ topic = content.slice(0, 100) || 'General Discussion';
187
+ position = stance || 'positive';
188
+ }
189
 
190
  // Call the external API
191
  const argumentResponse = await generateEnhancedArgument({ topic, position });
 
194
  const assistantMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
195
  role: 'assistant',
196
  content: argumentResponse.enhanced_argument,
197
+ tool: selectedTool ?? 'generate_argument',
198
  };
199
+
200
  addMessage(assistantMessage);
201
  } else {
202
  // Regular chat - use n8n webhook /debate-assistant endpoint
 
236
 
237
  // Extract final_argument from response
238
  const finalArgument = data?.final_argument || data?.result?.result?.[0]?.text || 'No response received';
239
+ const assistantTool = data?.tool_name ?? selectedTool ?? 'debate-assistant';
240
 
241
  // Parse if final_argument is a JSON string
242
  let finalContent = finalArgument;
 
253
  const assistantMessage: Omit<ChatMessage, 'id' | 'timestamp'> = {
254
  role: 'assistant',
255
  content: finalContent,
256
+ tool: assistantTool,
257
  };
258
  addMessage(assistantMessage);
259
  }
 
274
  const lastUserMessage = [...state.messages]
275
  .reverse()
276
  .find(msg => msg.role === 'user');
277
+
278
  if (lastUserMessage) {
279
  // Remove the last failed assistant message if exists
280
  setState(prev => {
 
288
  }
289
  return { ...prev, error: null };
290
  });
291
+
292
  // Resend the user message
293
  sendMessage(lastUserMessage.content);
294
  }
src/app/pages/ChatPage.tsx CHANGED
@@ -6,11 +6,11 @@ import { useChat } from '../hooks/useChat.ts';
6
  const ChatPage = () => {
7
  const { messages, isLoading, error, sendMessage, sendAudioMessage, retryLastMessage } = useChat();
8
 
9
- const handleMessageSubmit = (message: string, selectedTool?: string | null) => {
10
- sendMessage(message, selectedTool);
11
  };
12
 
13
- const handleAudioSubmit = (audioBlob: Blob, selectedTool?: string | null) => {
14
  sendAudioMessage(audioBlob, selectedTool);
15
  };
16
 
 
6
  const ChatPage = () => {
7
  const { messages, isLoading, error, sendMessage, sendAudioMessage, retryLastMessage } = useChat();
8
 
9
+ const handleMessageSubmit = (message: string, selectedTool?: string | null, stance?: 'positive' | 'negative') => {
10
+ sendMessage(message, selectedTool, stance);
11
  };
12
 
13
+ const handleAudioSubmit = (audioBlob: Blob, selectedTool?: string | null, stance?: 'positive' | 'negative') => {
14
  sendAudioMessage(audioBlob, selectedTool);
15
  };
16
 
src/app/types/chat.types.ts CHANGED
@@ -6,6 +6,7 @@ export type ChatMessage = {
6
  content: string;
7
  audioUrl?: string; // URL for audio messages
8
  timestamp: Date;
 
9
  };
10
 
11
  export type ChatState = {
 
6
  content: string;
7
  audioUrl?: string; // URL for audio messages
8
  timestamp: Date;
9
+ tool?: string | null;
10
  };
11
 
12
  export type ChatState = {