sparkecoder 0.1.22 → 0.1.23

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (290) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +1308 -212
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +1933 -454
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +20 -3
  7. package/dist/db/index.js +97 -0
  8. package/dist/db/index.js.map +1 -1
  9. package/dist/{index-CNwLFGiZ.d.ts → index-BblbmG_0.d.ts} +19 -4
  10. package/dist/index.d.ts +6 -6
  11. package/dist/index.js +1913 -434
  12. package/dist/index.js.map +1 -1
  13. package/dist/{schema-Df7MU3nM.d.ts → schema-D_8A4k01.d.ts} +246 -2
  14. package/dist/search-ybREg7F_.d.ts +254 -0
  15. package/dist/server/index.js +1918 -439
  16. package/dist/server/index.js.map +1 -1
  17. package/dist/tools/index.d.ts +7 -56
  18. package/dist/tools/index.js +894 -27
  19. package/dist/tools/index.js.map +1 -1
  20. package/package.json +5 -1
  21. package/web/.next/BUILD_ID +1 -1
  22. package/web/.next/standalone/web/.next/BUILD_ID +1 -1
  23. package/web/.next/standalone/web/.next/app-path-routes-manifest.json +4 -0
  24. package/web/.next/standalone/web/.next/build-manifest.json +7 -6
  25. package/web/.next/standalone/web/.next/prerender-manifest.json +99 -3
  26. package/web/.next/standalone/web/.next/required-server-files.json +28 -4
  27. package/web/.next/standalone/web/.next/routes-manifest.json +24 -0
  28. package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +5 -4
  29. package/web/.next/standalone/web/.next/server/app/(main)/page.js +2 -2
  30. package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
  31. package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
  32. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +5 -4
  33. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js +2 -2
  34. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
  35. package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
  36. package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +5 -4
  37. package/web/.next/standalone/web/.next/server/app/_global-error/page.js +2 -2
  38. package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -1
  39. package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  40. package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
  41. package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
  42. package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  43. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  44. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  45. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  46. package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  47. package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +5 -4
  48. package/web/.next/standalone/web/.next/server/app/_not-found/page.js +2 -2
  49. package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -1
  50. package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  51. package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
  52. package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
  53. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
  54. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  55. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
  56. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  57. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  58. package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
  59. package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
  60. package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
  61. package/web/.next/standalone/web/.next/server/app/docs/installation/page/app-paths-manifest.json +3 -0
  62. package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +18 -0
  63. package/web/.next/standalone/web/.next/server/app/docs/installation/page/next-font-manifest.json +11 -0
  64. package/web/.next/standalone/web/.next/server/app/docs/installation/page/react-loadable-manifest.json +1 -0
  65. package/web/.next/standalone/web/.next/server/app/docs/installation/page/server-reference-manifest.json +4 -0
  66. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js +21 -0
  67. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.map +5 -0
  68. package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.nft.json +1 -0
  69. package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +2 -0
  70. package/web/.next/standalone/web/.next/server/app/docs/installation.html +86 -0
  71. package/web/.next/standalone/web/.next/server/app/docs/installation.meta +16 -0
  72. package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +36 -0
  73. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +36 -0
  74. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +6 -0
  75. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +7 -0
  76. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +3 -0
  77. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +22 -0
  78. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +4 -0
  79. package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +5 -0
  80. package/web/.next/standalone/web/.next/server/app/docs/page/app-paths-manifest.json +3 -0
  81. package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +18 -0
  82. package/web/.next/standalone/web/.next/server/app/docs/page/next-font-manifest.json +11 -0
  83. package/web/.next/standalone/web/.next/server/app/docs/page/react-loadable-manifest.json +1 -0
  84. package/web/.next/standalone/web/.next/server/app/docs/page/server-reference-manifest.json +4 -0
  85. package/web/.next/standalone/web/.next/server/app/docs/page.js +21 -0
  86. package/web/.next/standalone/web/.next/server/app/docs/page.js.map +5 -0
  87. package/web/.next/standalone/web/.next/server/app/docs/page.js.nft.json +1 -0
  88. package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +2 -0
  89. package/web/.next/standalone/web/.next/server/app/docs/skills/page/app-paths-manifest.json +3 -0
  90. package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +18 -0
  91. package/web/.next/standalone/web/.next/server/app/docs/skills/page/next-font-manifest.json +11 -0
  92. package/web/.next/standalone/web/.next/server/app/docs/skills/page/react-loadable-manifest.json +1 -0
  93. package/web/.next/standalone/web/.next/server/app/docs/skills/page/server-reference-manifest.json +4 -0
  94. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js +21 -0
  95. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.map +5 -0
  96. package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.nft.json +1 -0
  97. package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +2 -0
  98. package/web/.next/standalone/web/.next/server/app/docs/skills.html +268 -0
  99. package/web/.next/standalone/web/.next/server/app/docs/skills.meta +16 -0
  100. package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +82 -0
  101. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +82 -0
  102. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +6 -0
  103. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +7 -0
  104. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +3 -0
  105. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +66 -0
  106. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +4 -0
  107. package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +5 -0
  108. package/web/.next/standalone/web/.next/server/app/docs/tools/page/app-paths-manifest.json +3 -0
  109. package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +18 -0
  110. package/web/.next/standalone/web/.next/server/app/docs/tools/page/next-font-manifest.json +11 -0
  111. package/web/.next/standalone/web/.next/server/app/docs/tools/page/react-loadable-manifest.json +1 -0
  112. package/web/.next/standalone/web/.next/server/app/docs/tools/page/server-reference-manifest.json +4 -0
  113. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js +21 -0
  114. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.map +5 -0
  115. package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.nft.json +1 -0
  116. package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +2 -0
  117. package/web/.next/standalone/web/.next/server/app/docs/tools.html +242 -0
  118. package/web/.next/standalone/web/.next/server/app/docs/tools.meta +16 -0
  119. package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +87 -0
  120. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +87 -0
  121. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +6 -0
  122. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +7 -0
  123. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +3 -0
  124. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +72 -0
  125. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +4 -0
  126. package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +5 -0
  127. package/web/.next/standalone/web/.next/server/app/docs.html +74 -0
  128. package/web/.next/standalone/web/.next/server/app/docs.meta +15 -0
  129. package/web/.next/standalone/web/.next/server/app/docs.rsc +34 -0
  130. package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +34 -0
  131. package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +6 -0
  132. package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +7 -0
  133. package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +3 -0
  134. package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +20 -0
  135. package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +5 -0
  136. package/web/.next/standalone/web/.next/server/app/index.html +1 -1
  137. package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
  138. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
  139. package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
  140. package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
  141. package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
  142. package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
  143. package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
  144. package/web/.next/standalone/web/.next/server/app-paths-manifest.json +4 -0
  145. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5f58fd73._.js → 2374f_244589df._.js} +1 -1
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_84859a94._.js → 2374f_41a27541._.js} +1 -1
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_65fcfd95._.js → 2374f_47c9e2d5._.js} +1 -1
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_f1038f7c._.js → 2374f_4bf2df9d._.js} +1 -1
  149. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_663d1038._.js +3 -0
  150. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_954e49c0._.js +3 -0
  151. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_387a1437._.js → 2374f_c33b095a._.js} +1 -1
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_cfd0137a._.js → 2374f_fa61fbb2._.js} +1 -1
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_741f6b67._.js → 2374f_fb82ac0d._.js} +1 -1
  154. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_96bca05b._.js → 2374f_next_dist_bbe64674._.js} +2 -2
  155. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7f04455b._.js → [root-of-the-server]__1e06ddf7._.js} +2 -2
  156. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2b151e1c._.js +3 -0
  157. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2dbf511a._.js +9 -0
  158. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__397fadd4._.js +3 -0
  159. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44bd8bd1._.js +3 -0
  160. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__70cecda8._.js +3 -0
  161. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9fdf9974._.js +3 -0
  162. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f18f92f4._.js → [root-of-the-server]__b050bb8f._.js} +2 -2
  163. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d3034cd2._.js +3 -0
  164. package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c3a1e22c._.js → [root-of-the-server]__ef2713cf._.js} +2 -2
  165. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f764bebe._.js +3 -0
  166. package/web/.next/standalone/web/.next/server/chunks/ssr/web_046bf7db._.js +3 -0
  167. package/web/.next/standalone/web/.next/server/chunks/ssr/web_656c1e45._.js +7 -0
  168. package/web/.next/standalone/web/.next/server/chunks/ssr/{web_c7618534._.js → web_76ccf09f._.js} +4 -4
  169. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_installation_page_actions_52cc0648.js +3 -0
  170. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_page_actions_4fe77da8.js +3 -0
  171. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_skills_page_actions_251df2e1.js +3 -0
  172. package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_tools_page_actions_3e6382b0.js +3 -0
  173. package/web/.next/standalone/web/.next/server/chunks/ssr/web_a565dc94._.js +4 -0
  174. package/web/.next/standalone/web/.next/server/chunks/ssr/web_b1cce0b7._.js +4 -0
  175. package/web/.next/standalone/web/.next/server/chunks/ssr/web_b42ed1be._.js +3 -0
  176. package/web/.next/standalone/web/.next/server/chunks/ssr/web_c0c2bee4._.js +4 -0
  177. package/web/.next/standalone/web/.next/server/chunks/ssr/web_eea9c122._.js +3 -0
  178. package/web/.next/standalone/web/.next/server/chunks/ssr/web_ff00a5c3._.js +4 -0
  179. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
  180. package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +3 -0
  181. package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +5 -4
  182. package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
  183. package/web/.next/standalone/web/.next/server/next-font-manifest.json +16 -0
  184. package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
  185. package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
  186. package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
  187. package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
  188. package/web/.next/standalone/web/.next/static/chunks/0cc382a66266188e.js +7 -0
  189. package/web/.next/standalone/web/.next/static/chunks/0fda34e553582102.js +1 -0
  190. package/web/.next/standalone/web/.next/static/{static/chunks/5e5b485d77ac0d8f.js → chunks/6407c045dfc908fe.js} +3 -3
  191. package/web/.next/standalone/web/.next/static/chunks/651e187cc15d66de.js +1 -0
  192. package/web/.next/standalone/web/.next/static/chunks/862ced58ce21a270.js +4 -0
  193. package/web/.next/standalone/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
  194. package/web/.next/standalone/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
  195. package/web/.next/standalone/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
  196. package/web/.next/standalone/web/.next/static/chunks/af22745850132107.css +1 -0
  197. package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
  198. package/web/.next/standalone/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
  199. package/web/.next/standalone/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  200. package/web/.next/standalone/web/.next/static/static/chunks/0cc382a66266188e.js +7 -0
  201. package/web/.next/standalone/web/.next/static/static/chunks/0fda34e553582102.js +1 -0
  202. package/web/.next/{static/chunks/5e5b485d77ac0d8f.js → standalone/web/.next/static/static/chunks/6407c045dfc908fe.js} +3 -3
  203. package/web/.next/standalone/web/.next/static/static/chunks/651e187cc15d66de.js +1 -0
  204. package/web/.next/standalone/web/.next/static/static/chunks/862ced58ce21a270.js +4 -0
  205. package/web/.next/standalone/web/.next/static/static/chunks/89bc21c0443670f4.js +1 -0
  206. package/web/.next/standalone/web/.next/static/static/chunks/8f4edf22ededc29b.js +7 -0
  207. package/web/.next/standalone/web/.next/static/static/chunks/ad6b9dbb257d62cc.js +1 -0
  208. package/web/.next/standalone/web/.next/static/static/chunks/af22745850132107.css +1 -0
  209. package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +1 -0
  210. package/web/.next/standalone/web/.next/static/static/chunks/db9b22c844a35e20.js +5 -0
  211. package/web/.next/standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  212. package/web/.next/standalone/web/mdx-components.tsx +119 -0
  213. package/web/.next/standalone/web/next.config.ts +15 -1
  214. package/web/.next/standalone/web/package-lock.json +559 -4
  215. package/web/.next/standalone/web/package.json +4 -0
  216. package/web/.next/standalone/web/runtime-config.json +1 -1
  217. package/web/.next/standalone/web/server.js +1 -1
  218. package/web/.next/standalone/web/src/app/(main)/page.tsx +127 -5
  219. package/web/.next/standalone/web/src/app/docs/installation/page.mdx +128 -0
  220. package/web/.next/standalone/web/src/app/docs/layout.tsx +74 -0
  221. package/web/.next/standalone/web/src/app/docs/page.mdx +90 -0
  222. package/web/.next/standalone/web/src/app/docs/skills/page.mdx +334 -0
  223. package/web/.next/standalone/web/src/app/docs/tools/page.mdx +300 -0
  224. package/web/.next/standalone/web/src/components/ai-elements/mention-input.tsx +809 -0
  225. package/web/.next/standalone/web/src/components/ai-elements/search-tool.tsx +400 -0
  226. package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +275 -0
  227. package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +19 -5
  228. package/web/.next/standalone/web/src/components/chat-interface.tsx +525 -71
  229. package/web/.next/standalone/web/src/hooks/use-workspace-files.ts +108 -0
  230. package/web/.next/standalone/web/src/lib/api.ts +90 -4
  231. package/web/.next/static/chunks/0cc382a66266188e.js +7 -0
  232. package/web/.next/static/chunks/0fda34e553582102.js +1 -0
  233. package/web/.next/{standalone/web/.next/static/chunks/5e5b485d77ac0d8f.js → static/chunks/6407c045dfc908fe.js} +3 -3
  234. package/web/.next/static/chunks/651e187cc15d66de.js +1 -0
  235. package/web/.next/static/chunks/862ced58ce21a270.js +4 -0
  236. package/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
  237. package/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
  238. package/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
  239. package/web/.next/static/chunks/af22745850132107.css +1 -0
  240. package/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
  241. package/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
  242. package/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  243. package/web/package.json +4 -0
  244. package/dist/bash-CGAqW7HR.d.ts +0 -80
  245. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_814be2c9._.js +0 -3
  246. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +0 -3
  247. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__3ec22171._.js +0 -9
  248. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__513c6b45._.js +0 -3
  249. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +0 -3
  250. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +0 -7
  251. package/web/.next/standalone/web/.next/server/chunks/ssr/web_e6034803._.js +0 -3
  252. package/web/.next/standalone/web/.next/static/chunks/03d4169891280e04.js +0 -7
  253. package/web/.next/standalone/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  254. package/web/.next/standalone/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
  255. package/web/.next/standalone/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  256. package/web/.next/standalone/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  257. package/web/.next/standalone/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  258. package/web/.next/standalone/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  259. package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  260. package/web/.next/standalone/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
  261. package/web/.next/standalone/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  262. package/web/.next/standalone/web/.next/static/static/chunks/03d4169891280e04.js +0 -7
  263. package/web/.next/standalone/web/.next/static/static/chunks/2d5da0cfc011b8d9.js +0 -1
  264. package/web/.next/standalone/web/.next/static/static/chunks/3bb454ca848ec78e.js +0 -7
  265. package/web/.next/standalone/web/.next/static/static/chunks/634fd97fab9ed4e4.js +0 -4
  266. package/web/.next/standalone/web/.next/static/static/chunks/77e4bf0421481629.js +0 -1
  267. package/web/.next/standalone/web/.next/static/static/chunks/beb9625c4a470042.js +0 -1
  268. package/web/.next/standalone/web/.next/static/static/chunks/c2244168e74b8c78.js +0 -1
  269. package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +0 -5
  270. package/web/.next/standalone/web/.next/static/static/chunks/cb355fac10c6ad11.css +0 -1
  271. package/web/.next/standalone/web/.next/static/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  272. package/web/.next/static/chunks/03d4169891280e04.js +0 -7
  273. package/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  274. package/web/.next/static/chunks/3bb454ca848ec78e.js +0 -7
  275. package/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  276. package/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  277. package/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  278. package/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  279. package/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  280. package/web/.next/static/chunks/cb355fac10c6ad11.css +0 -1
  281. package/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  282. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  283. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  284. /package/web/.next/standalone/web/.next/static/{n86r6x1RoUipFp6nLIk-R → static/uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  285. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  286. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  287. /package/web/.next/standalone/web/.next/static/{static/n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  288. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  289. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  290. /package/web/.next/static/{n86r6x1RoUipFp6nLIk-R → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
@@ -0,0 +1,809 @@
1
+ "use client";
2
+
3
+ import {
4
+ useState,
5
+ useRef,
6
+ useCallback,
7
+ useEffect,
8
+ type KeyboardEvent,
9
+ type ChangeEvent,
10
+ createContext,
11
+ useContext,
12
+ useMemo,
13
+ type ReactNode,
14
+ type ComponentProps,
15
+ type ClipboardEventHandler,
16
+ } from "react";
17
+ import { cn } from "@/lib/utils";
18
+ import {
19
+ Command,
20
+ CommandEmpty,
21
+ CommandGroup,
22
+ CommandItem,
23
+ CommandList,
24
+ } from "@/components/ui/command";
25
+ import {
26
+ Popover,
27
+ PopoverContent,
28
+ PopoverAnchor,
29
+ } from "@/components/ui/popover";
30
+ import { useWorkspaceFiles } from "@/hooks/use-workspace-files";
31
+ import { usePromptInputAttachments } from "@/components/ai-elements/prompt-input";
32
+ import {
33
+ FileIcon,
34
+ FolderIcon,
35
+ HashIcon,
36
+ HelpCircleIcon,
37
+ TerminalIcon,
38
+ } from "lucide-react";
39
+
40
+ // ============================================================================
41
+ // Types
42
+ // ============================================================================
43
+
44
+ export interface FileMention {
45
+ id: string;
46
+ type: "file" | "folder";
47
+ path: string;
48
+ name: string;
49
+ }
50
+
51
+ export interface SlashCommand {
52
+ id: string;
53
+ name: string;
54
+ description: string;
55
+ icon?: ReactNode;
56
+ /** Text to insert when this command is selected */
57
+ prompt?: string;
58
+ /** Optional action to run (for special commands like clear) */
59
+ action?: () => void;
60
+ }
61
+
62
+ interface TriggerState {
63
+ type: "file" | "command";
64
+ query: string;
65
+ startIndex: number; // Where the trigger character is
66
+ endIndex: number; // Current cursor position
67
+ }
68
+
69
+ // ============================================================================
70
+ // Context - tracks mentions found in the text (for extraction on submit)
71
+ // ============================================================================
72
+
73
+ interface MentionInputContextValue {
74
+ /** Extract mentions from text (parses @path patterns) */
75
+ extractMentions: (text: string) => FileMention[];
76
+ /** Clear any internal state */
77
+ clear: () => void;
78
+ }
79
+
80
+ const MentionInputContext = createContext<MentionInputContextValue | null>(null);
81
+
82
+ export function useMentionInput() {
83
+ const ctx = useContext(MentionInputContext);
84
+ if (!ctx) {
85
+ throw new Error("useMentionInput must be used within MentionInputProvider");
86
+ }
87
+ return ctx;
88
+ }
89
+
90
+ // Regex to match @mentions in text (matches @path/to/file or @folder/)
91
+ const MENTION_REGEX = /@([\w./-]+\/?)/g;
92
+
93
+ // ============================================================================
94
+ // Default Slash Commands - Each inserts a prompt template
95
+ // ============================================================================
96
+
97
+ export const DEFAULT_SLASH_COMMANDS: SlashCommand[] = [
98
+ {
99
+ id: "help",
100
+ name: "help",
101
+ description: "Ask about available features",
102
+ icon: <HelpCircleIcon className="size-4" />,
103
+ prompt: "What commands and features are available? How can you help me?",
104
+ },
105
+ {
106
+ id: "terminal",
107
+ name: "terminal",
108
+ description: "Open a new terminal",
109
+ icon: <TerminalIcon className="size-4" />,
110
+ prompt: "Please open a new terminal session for me.",
111
+ },
112
+ {
113
+ id: "explain",
114
+ name: "explain",
115
+ description: "Explain a file or code",
116
+ icon: <FileIcon className="size-4" />,
117
+ prompt: "Please explain this code: ",
118
+ },
119
+ {
120
+ id: "fix",
121
+ name: "fix",
122
+ description: "Fix a bug or issue",
123
+ icon: <HashIcon className="size-4" />,
124
+ prompt: "Please help me fix this issue: ",
125
+ },
126
+ {
127
+ id: "refactor",
128
+ name: "refactor",
129
+ description: "Refactor code",
130
+ icon: <HashIcon className="size-4" />,
131
+ prompt: "Please refactor this code to be cleaner and more maintainable: ",
132
+ },
133
+ {
134
+ id: "test",
135
+ name: "test",
136
+ description: "Write tests",
137
+ icon: <HashIcon className="size-4" />,
138
+ prompt: "Please write tests for: ",
139
+ },
140
+ ];
141
+
142
+ // ============================================================================
143
+ // Provider - Simplified to extract mentions from text on demand
144
+ // ============================================================================
145
+
146
+ interface MentionInputProviderProps {
147
+ children: ReactNode;
148
+ onChange?: (mentions: FileMention[]) => void;
149
+ }
150
+
151
+ export function MentionInputProvider({
152
+ children,
153
+ }: MentionInputProviderProps) {
154
+ // Extract mentions from text by parsing @path patterns
155
+ const extractMentions = useCallback((text: string): FileMention[] => {
156
+ const mentions: FileMention[] = [];
157
+ const regex = new RegExp(MENTION_REGEX.source, 'g');
158
+ let match;
159
+
160
+ while ((match = regex.exec(text)) !== null) {
161
+ const path = match[1];
162
+ const isFolder = path.endsWith('/');
163
+ mentions.push({
164
+ id: `mention-${match.index}`,
165
+ type: isFolder ? 'folder' : 'file',
166
+ path: isFolder ? path.slice(0, -1) : path,
167
+ name: path.split('/').pop()?.replace(/\/$/, '') || path,
168
+ });
169
+ }
170
+
171
+ return mentions;
172
+ }, []);
173
+
174
+ const clear = useCallback(() => {
175
+ // No-op for now, mentions are extracted from text
176
+ }, []);
177
+
178
+ const value = useMemo(
179
+ () => ({
180
+ extractMentions,
181
+ clear,
182
+ }),
183
+ [extractMentions, clear]
184
+ );
185
+
186
+ return (
187
+ <MentionInputContext.Provider value={value}>
188
+ {children}
189
+ </MentionInputContext.Provider>
190
+ );
191
+ }
192
+
193
+ // ============================================================================
194
+ // Highlight Backdrop - renders behind textarea to show styled mentions
195
+ // ============================================================================
196
+
197
+ interface HighlightBackdropProps {
198
+ value: string;
199
+ className?: string;
200
+ }
201
+
202
+ export function HighlightBackdrop({ value, className }: HighlightBackdropProps) {
203
+ // Parse text and highlight @mentions
204
+ const renderHighlightedText = () => {
205
+ const parts: ReactNode[] = [];
206
+ const regex = new RegExp(MENTION_REGEX.source, 'g');
207
+ let lastIndex = 0;
208
+ let match;
209
+
210
+ while ((match = regex.exec(value)) !== null) {
211
+ // Add text before the match
212
+ if (match.index > lastIndex) {
213
+ parts.push(
214
+ <span key={`text-${lastIndex}`}>
215
+ {value.slice(lastIndex, match.index)}
216
+ </span>
217
+ );
218
+ }
219
+
220
+ // Add the highlighted mention
221
+ parts.push(
222
+ <span
223
+ key={`mention-${match.index}`}
224
+ className="bg-primary/20 text-primary rounded px-0.5 -mx-0.5"
225
+ >
226
+ {match[0]}
227
+ </span>
228
+ );
229
+
230
+ lastIndex = match.index + match[0].length;
231
+ }
232
+
233
+ // Add remaining text
234
+ if (lastIndex < value.length) {
235
+ parts.push(
236
+ <span key={`text-${lastIndex}`}>
237
+ {value.slice(lastIndex)}
238
+ </span>
239
+ );
240
+ }
241
+
242
+ // Add a trailing space to match textarea behavior
243
+ parts.push(<span key="trailing">&nbsp;</span>);
244
+
245
+ return parts;
246
+ };
247
+
248
+ return (
249
+ <div
250
+ className={cn(
251
+ "absolute inset-0 pointer-events-none whitespace-pre-wrap break-words overflow-hidden",
252
+ "px-3 py-2 text-sm text-transparent",
253
+ className
254
+ )}
255
+ aria-hidden="true"
256
+ >
257
+ {renderHighlightedText()}
258
+ </div>
259
+ );
260
+ }
261
+
262
+ // Legacy export for backwards compatibility - no longer shows chips
263
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
264
+ export function MentionsDisplay(_props: { className?: string }) {
265
+ // Mentions are now shown inline, this component is no longer needed
266
+ return null;
267
+ }
268
+
269
+ // ============================================================================
270
+ // Mention Textarea with Popover
271
+ // ============================================================================
272
+
273
+ interface MentionTextareaProps
274
+ extends Omit<ComponentProps<"textarea">, "onChange"> {
275
+ sessionId: string;
276
+ value: string;
277
+ onChange: (value: string) => void;
278
+ onSlashCommand?: (command: SlashCommand) => void;
279
+ slashCommands?: SlashCommand[];
280
+ /** Show inline highlighting for @mentions (default: true) */
281
+ showHighlights?: boolean;
282
+ }
283
+
284
+ export function MentionTextarea({
285
+ sessionId,
286
+ value,
287
+ onChange,
288
+ onSlashCommand,
289
+ slashCommands = DEFAULT_SLASH_COMMANDS,
290
+ showHighlights = true,
291
+ className,
292
+ onKeyDown,
293
+ disabled,
294
+ ...props
295
+ }: MentionTextareaProps) {
296
+ const textareaRef = useRef<HTMLTextAreaElement>(null);
297
+ const backdropRef = useRef<HTMLDivElement>(null);
298
+ const [trigger, setTrigger] = useState<TriggerState | null>(null);
299
+ const [selectedIndex, setSelectedIndex] = useState(0);
300
+ const [popoverPosition, setPopoverPosition] = useState({ top: 0, left: 0 });
301
+ const [isComposing, setIsComposing] = useState(false); // For IME input
302
+
303
+ // Get attachments context for paste handling
304
+ const attachments = usePromptInputAttachments();
305
+
306
+ // Fetch workspace files when @ is triggered
307
+ const { files, isLoading } = useWorkspaceFiles({
308
+ sessionId,
309
+ query: trigger?.type === "file" ? trigger.query : "",
310
+ enabled: trigger?.type === "file",
311
+ limit: 15,
312
+ });
313
+
314
+ // Filter slash commands based on query
315
+ const filteredCommands = useMemo(() => {
316
+ if (trigger?.type !== "command") return [];
317
+ const query = trigger.query.toLowerCase();
318
+ return slashCommands.filter(
319
+ (cmd) =>
320
+ cmd.name.toLowerCase().includes(query) ||
321
+ cmd.description.toLowerCase().includes(query)
322
+ );
323
+ }, [trigger, slashCommands]);
324
+
325
+ // Current items to display
326
+ const items = trigger?.type === "file" ? files : filteredCommands;
327
+ const showPopover = trigger !== null && (items.length > 0 || isLoading);
328
+
329
+ // Calculate popover position based on textarea caret
330
+ const updatePopoverPosition = useCallback(() => {
331
+ const textarea = textareaRef.current;
332
+ if (!textarea || !trigger) return;
333
+
334
+ // Create a hidden div to measure text up to caret
335
+ const mirror = document.createElement("div");
336
+ const style = window.getComputedStyle(textarea);
337
+
338
+ // Copy relevant styles
339
+ const stylesToCopy = [
340
+ "fontFamily",
341
+ "fontSize",
342
+ "fontWeight",
343
+ "lineHeight",
344
+ "padding",
345
+ "paddingLeft",
346
+ "paddingTop",
347
+ "border",
348
+ "borderWidth",
349
+ "boxSizing",
350
+ "whiteSpace",
351
+ "wordWrap",
352
+ "wordBreak",
353
+ ];
354
+
355
+ stylesToCopy.forEach((prop) => {
356
+ // @ts-expect-error - style props
357
+ mirror.style[prop] = style[prop];
358
+ });
359
+
360
+ mirror.style.position = "absolute";
361
+ mirror.style.visibility = "hidden";
362
+ mirror.style.height = "auto";
363
+ mirror.style.width = `${textarea.clientWidth}px`;
364
+ mirror.style.overflow = "hidden";
365
+
366
+ // Get text up to trigger position
367
+ const textBeforeTrigger = value.substring(0, trigger.startIndex);
368
+ mirror.textContent = textBeforeTrigger;
369
+
370
+ // Add a span for the trigger character to measure position
371
+ const marker = document.createElement("span");
372
+ marker.textContent = "|";
373
+ mirror.appendChild(marker);
374
+
375
+ document.body.appendChild(mirror);
376
+
377
+ // Get position relative to textarea
378
+ const textareaRect = textarea.getBoundingClientRect();
379
+ const markerRect = marker.getBoundingClientRect();
380
+ const mirrorRect = mirror.getBoundingClientRect();
381
+
382
+ // Calculate offset from textarea
383
+ const left = markerRect.left - mirrorRect.left;
384
+ const top = markerRect.top - mirrorRect.top + parseInt(style.lineHeight);
385
+
386
+ // Adjust for scroll
387
+ const scrollTop = textarea.scrollTop;
388
+
389
+ document.body.removeChild(mirror);
390
+
391
+ setPopoverPosition({
392
+ left: Math.min(left, textareaRect.width - 250), // Keep popover in view
393
+ top: top - scrollTop + 4,
394
+ });
395
+ }, [trigger, value]);
396
+
397
+ useEffect(() => {
398
+ updatePopoverPosition();
399
+ }, [updatePopoverPosition]);
400
+
401
+ // Detect trigger characters
402
+ const detectTrigger = useCallback(
403
+ (text: string, cursorPos: number) => {
404
+ if (cursorPos === 0) {
405
+ setTrigger(null);
406
+ return;
407
+ }
408
+
409
+ const beforeCursor = text.slice(0, cursorPos);
410
+
411
+ // Check for @ trigger (file mention)
412
+ const atMatch = beforeCursor.match(/@([^\s@]*)$/);
413
+ if (atMatch) {
414
+ setTrigger({
415
+ type: "file",
416
+ query: atMatch[1],
417
+ startIndex: cursorPos - atMatch[0].length,
418
+ endIndex: cursorPos,
419
+ });
420
+ setSelectedIndex(0);
421
+ return;
422
+ }
423
+
424
+ // Check for / trigger at start of line or after whitespace (slash command)
425
+ const slashMatch = beforeCursor.match(/(?:^|\s)\/([^\s]*)$/);
426
+ if (slashMatch) {
427
+ setTrigger({
428
+ type: "command",
429
+ query: slashMatch[1],
430
+ startIndex: cursorPos - slashMatch[1].length - 1,
431
+ endIndex: cursorPos,
432
+ });
433
+ setSelectedIndex(0);
434
+ return;
435
+ }
436
+
437
+ setTrigger(null);
438
+ },
439
+ []
440
+ );
441
+
442
+ // Handle input change
443
+ const handleChange = useCallback(
444
+ (e: ChangeEvent<HTMLTextAreaElement>) => {
445
+ const newValue = e.target.value;
446
+ const cursorPos = e.target.selectionStart ?? 0;
447
+
448
+ onChange(newValue);
449
+ detectTrigger(newValue, cursorPos);
450
+ },
451
+ [onChange, detectTrigger]
452
+ );
453
+
454
+ // Handle selection from popover
455
+ const handleSelect = useCallback(
456
+ (item: (typeof files)[0] | SlashCommand) => {
457
+ if (!trigger || !textareaRef.current) return;
458
+
459
+ if (trigger.type === "file" && "path" in item) {
460
+ // Insert @path inline (replacing @query with @fullpath)
461
+ const before = value.slice(0, trigger.startIndex);
462
+ const after = value.slice(trigger.endIndex);
463
+ // Add trailing slash for folders, space after for usability
464
+ const mentionText = `@${item.path}${item.type === 'folder' ? '/' : ''} `;
465
+ const newValue = before + mentionText + after;
466
+
467
+ onChange(newValue);
468
+
469
+ // Set cursor after the inserted mention
470
+ setTimeout(() => {
471
+ if (textareaRef.current) {
472
+ const newPos = trigger.startIndex + mentionText.length;
473
+ textareaRef.current.selectionStart = newPos;
474
+ textareaRef.current.selectionEnd = newPos;
475
+ textareaRef.current.focus();
476
+ }
477
+ }, 0);
478
+ } else if (trigger.type === "command" && "description" in item) {
479
+ const cmd = item as SlashCommand;
480
+
481
+ // If command has an action, call it (for special commands)
482
+ if (cmd.action) {
483
+ cmd.action();
484
+ onSlashCommand?.(cmd);
485
+
486
+ // Remove the /command from input
487
+ const before = value.slice(0, trigger.startIndex);
488
+ const after = value.slice(trigger.endIndex);
489
+ onChange((before + after).trim());
490
+ } else if (cmd.prompt) {
491
+ // Insert the prompt text (replacing /command with the prompt)
492
+ const before = value.slice(0, trigger.startIndex);
493
+ const after = value.slice(trigger.endIndex);
494
+ const newValue = before + cmd.prompt + after;
495
+
496
+ onChange(newValue);
497
+
498
+ // Set cursor at end of inserted prompt
499
+ setTimeout(() => {
500
+ if (textareaRef.current) {
501
+ const newPos = trigger.startIndex + cmd.prompt!.length;
502
+ textareaRef.current.selectionStart = newPos;
503
+ textareaRef.current.selectionEnd = newPos;
504
+ textareaRef.current.focus();
505
+ }
506
+ }, 0);
507
+ } else {
508
+ // Fallback: just notify via callback
509
+ onSlashCommand?.(cmd);
510
+
511
+ // Remove the /command from input
512
+ const before = value.slice(0, trigger.startIndex);
513
+ const after = value.slice(trigger.endIndex);
514
+ onChange((before + after).trim());
515
+ }
516
+
517
+ setTimeout(() => {
518
+ textareaRef.current?.focus();
519
+ }, 0);
520
+ }
521
+
522
+ setTrigger(null);
523
+ },
524
+ [trigger, value, onChange, onSlashCommand]
525
+ );
526
+
527
+ // Handle keyboard navigation and form submission
528
+ const handleKeyDown = useCallback(
529
+ (e: KeyboardEvent<HTMLTextAreaElement>) => {
530
+ // Call external handler first
531
+ onKeyDown?.(e);
532
+ if (e.defaultPrevented) return;
533
+
534
+ // When popover is showing, handle navigation
535
+ if (showPopover) {
536
+ switch (e.key) {
537
+ case "ArrowDown":
538
+ e.preventDefault();
539
+ setSelectedIndex((prev) => (prev + 1) % items.length);
540
+ return;
541
+ case "ArrowUp":
542
+ e.preventDefault();
543
+ setSelectedIndex((prev) => (prev - 1 + items.length) % items.length);
544
+ return;
545
+ case "Tab":
546
+ case "Enter":
547
+ if (items.length > 0) {
548
+ e.preventDefault();
549
+ handleSelect(items[selectedIndex]);
550
+ }
551
+ return;
552
+ case "Escape":
553
+ e.preventDefault();
554
+ setTrigger(null);
555
+ return;
556
+ }
557
+ }
558
+
559
+ // When popover is NOT showing, handle Enter to submit form
560
+ if (e.key === "Enter") {
561
+ // Don't submit during IME composition
562
+ if (isComposing || e.nativeEvent.isComposing) {
563
+ return;
564
+ }
565
+ // Shift+Enter for newline
566
+ if (e.shiftKey) {
567
+ return;
568
+ }
569
+
570
+ e.preventDefault();
571
+
572
+ // Find the form - use closest() as fallback if .form is null
573
+ const textarea = e.currentTarget;
574
+ const form = textarea.form || textarea.closest('form') as HTMLFormElement | null;
575
+
576
+ if (form) {
577
+ // Find and check submit button
578
+ const submitButton = form.querySelector(
579
+ 'button[type="submit"]'
580
+ ) as HTMLButtonElement | null;
581
+
582
+ if (submitButton?.disabled) {
583
+ return;
584
+ }
585
+
586
+ // Click the submit button directly (more reliable than requestSubmit)
587
+ if (submitButton) {
588
+ submitButton.click();
589
+ } else {
590
+ // Fallback to requestSubmit
591
+ form.requestSubmit();
592
+ }
593
+ }
594
+ }
595
+ // Backspace to remove last attachment when textarea is empty
596
+ if (e.key === "Backspace" && value === "" && attachments.files.length > 0) {
597
+ e.preventDefault();
598
+ const lastAttachment = attachments.files.at(-1);
599
+ if (lastAttachment) {
600
+ attachments.remove(lastAttachment.id);
601
+ }
602
+ }
603
+ },
604
+ [showPopover, items, selectedIndex, handleSelect, onKeyDown, isComposing, value, attachments]
605
+ );
606
+
607
+ // Handle paste for file attachments
608
+ const handlePaste: ClipboardEventHandler<HTMLTextAreaElement> = useCallback((event) => {
609
+ const items = event.clipboardData?.items;
610
+ if (!items) return;
611
+
612
+ const files: File[] = [];
613
+ for (const item of items) {
614
+ if (item.kind === "file") {
615
+ const file = item.getAsFile();
616
+ if (file) files.push(file);
617
+ }
618
+ }
619
+
620
+ if (files.length > 0) {
621
+ event.preventDefault();
622
+ attachments.add(files);
623
+ }
624
+ }, [attachments]);
625
+
626
+ // Handle click to update cursor position
627
+ const handleClick = useCallback(() => {
628
+ const textarea = textareaRef.current;
629
+ if (!textarea) return;
630
+ detectTrigger(value, textarea.selectionStart ?? 0);
631
+ }, [value, detectTrigger]);
632
+
633
+ // Sync backdrop scroll with textarea
634
+ const handleScroll = useCallback(() => {
635
+ if (textareaRef.current && backdropRef.current) {
636
+ backdropRef.current.scrollTop = textareaRef.current.scrollTop;
637
+ }
638
+ }, []);
639
+
640
+ // Parse text and render with highlighted mentions
641
+ const renderHighlightedText = () => {
642
+ const parts: ReactNode[] = [];
643
+ const regex = new RegExp(MENTION_REGEX.source, 'g');
644
+ let lastIndex = 0;
645
+ let match;
646
+
647
+ while ((match = regex.exec(value)) !== null) {
648
+ // Add text before the match
649
+ if (match.index > lastIndex) {
650
+ parts.push(
651
+ <span key={`text-${lastIndex}`}>
652
+ {value.slice(lastIndex, match.index)}
653
+ </span>
654
+ );
655
+ }
656
+
657
+ // Add the highlighted mention
658
+ parts.push(
659
+ <mark
660
+ key={`mention-${match.index}`}
661
+ className="bg-primary/20 text-transparent rounded-sm"
662
+ >
663
+ {match[0]}
664
+ </mark>
665
+ );
666
+
667
+ lastIndex = match.index + match[0].length;
668
+ }
669
+
670
+ // Add remaining text
671
+ if (lastIndex < value.length) {
672
+ parts.push(
673
+ <span key={`text-${lastIndex}`}>
674
+ {value.slice(lastIndex)}
675
+ </span>
676
+ );
677
+ }
678
+
679
+ // Add trailing character to match textarea sizing
680
+ parts.push(<span key="trailing">{'\n'}</span>);
681
+
682
+ return parts;
683
+ };
684
+
685
+ return (
686
+ <div className="relative w-full">
687
+ <Popover open={showPopover} onOpenChange={() => setTrigger(null)}>
688
+ <PopoverAnchor
689
+ style={{
690
+ position: "absolute",
691
+ left: popoverPosition.left,
692
+ top: popoverPosition.top,
693
+ width: 0,
694
+ height: 0,
695
+ }}
696
+ />
697
+ <PopoverContent
698
+ align="start"
699
+ side="top"
700
+ className="w-64 p-0"
701
+ onOpenAutoFocus={(e) => e.preventDefault()}
702
+ >
703
+ <Command>
704
+ <CommandList>
705
+ {isLoading && trigger?.type === "file" && (
706
+ <div className="p-2 text-sm text-muted-foreground text-center">
707
+ Loading...
708
+ </div>
709
+ )}
710
+ {!isLoading && items.length === 0 && (
711
+ <CommandEmpty>
712
+ {trigger?.type === "file"
713
+ ? "No files found"
714
+ : "No commands found"}
715
+ </CommandEmpty>
716
+ )}
717
+ {trigger?.type === "file" && files.length > 0 && (
718
+ <CommandGroup heading="Files & Folders">
719
+ {files.map((file, index) => (
720
+ <CommandItem
721
+ key={file.path}
722
+ onSelect={() => handleSelect(file)}
723
+ className={cn(
724
+ "cursor-pointer",
725
+ index === selectedIndex && "bg-accent"
726
+ )}
727
+ >
728
+ {file.type === "folder" ? (
729
+ <FolderIcon className="mr-2 size-4 text-blue-500" />
730
+ ) : (
731
+ <FileIcon className="mr-2 size-4 text-muted-foreground" />
732
+ )}
733
+ <span className="truncate">{file.path}</span>
734
+ </CommandItem>
735
+ ))}
736
+ </CommandGroup>
737
+ )}
738
+ {trigger?.type === "command" && filteredCommands.length > 0 && (
739
+ <CommandGroup heading="Commands">
740
+ {filteredCommands.map((cmd, index) => (
741
+ <CommandItem
742
+ key={cmd.id}
743
+ onSelect={() => handleSelect(cmd)}
744
+ className={cn(
745
+ "cursor-pointer",
746
+ index === selectedIndex && "bg-accent"
747
+ )}
748
+ >
749
+ {cmd.icon || <HashIcon className="mr-2 size-4" />}
750
+ <div className="flex flex-col ml-2">
751
+ <span className="font-medium">/{cmd.name}</span>
752
+ <span className="text-xs text-muted-foreground">
753
+ {cmd.description}
754
+ </span>
755
+ </div>
756
+ </CommandItem>
757
+ ))}
758
+ </CommandGroup>
759
+ )}
760
+ </CommandList>
761
+ </Command>
762
+ </PopoverContent>
763
+ </Popover>
764
+
765
+ {/* Highlight backdrop - renders behind textarea to show colored mentions */}
766
+ {showHighlights && (
767
+ <div
768
+ ref={backdropRef}
769
+ aria-hidden="true"
770
+ className="absolute inset-0 pointer-events-none overflow-hidden whitespace-pre-wrap break-words px-3 py-3 text-sm"
771
+ style={{ color: 'transparent' }}
772
+ >
773
+ {renderHighlightedText()}
774
+ </div>
775
+ )}
776
+
777
+ <textarea
778
+ ref={textareaRef}
779
+ name="message"
780
+ data-slot="input-group-control"
781
+ value={value}
782
+ onChange={handleChange}
783
+ onKeyDown={handleKeyDown}
784
+ onClick={handleClick}
785
+ onScroll={handleScroll}
786
+ onPaste={handlePaste}
787
+ onCompositionStart={() => setIsComposing(true)}
788
+ onCompositionEnd={() => setIsComposing(false)}
789
+ disabled={disabled}
790
+ className={cn(
791
+ // Match InputGroupTextarea styling - no border since InputGroup provides it
792
+ "flex-1 w-full resize-none rounded-none border-0 py-3 px-3 text-sm shadow-none",
793
+ "placeholder:text-muted-foreground focus-visible:ring-0 focus-visible:outline-none",
794
+ "disabled:cursor-not-allowed disabled:opacity-50",
795
+ // Make background transparent so highlights show through
796
+ "bg-transparent dark:bg-transparent",
797
+ className
798
+ )}
799
+ {...props}
800
+ />
801
+ </div>
802
+ );
803
+ }
804
+
805
+ // ============================================================================
806
+ // Exports
807
+ // ============================================================================
808
+
809
+ export type { TriggerState };