sparkecoder 0.1.21 → 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 (292) hide show
  1. package/dist/agent/index.d.ts +3 -3
  2. package/dist/agent/index.js +1361 -215
  3. package/dist/agent/index.js.map +1 -1
  4. package/dist/cli.js +2179 -349
  5. package/dist/cli.js.map +1 -1
  6. package/dist/db/index.d.ts +20 -2
  7. package/dist/db/index.js +97 -0
  8. package/dist/db/index.js.map +1 -1
  9. package/dist/{index-BzedNBK-.d.ts → index-BblbmG_0.d.ts} +42 -6
  10. package/dist/index.d.ts +6 -6
  11. package/dist/index.js +2165 -335
  12. package/dist/index.js.map +1 -1
  13. package/dist/{schema-CkrIadxa.d.ts → schema-D_8A4k01.d.ts} +270 -3
  14. package/dist/search-ybREg7F_.d.ts +254 -0
  15. package/dist/server/index.js +2163 -333
  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_378282b1._.js → 2374f_244589df._.js} +1 -1
  146. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5de336d2._.js → 2374f_41a27541._.js} +1 -1
  147. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_30f9df13._.js → 2374f_47c9e2d5._.js} +1 -1
  148. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d94c2b70._.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_1d78db71._.js → 2374f_c33b095a._.js} +1 -1
  152. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_bbc99511._.js → 2374f_fa61fbb2._.js} +1 -1
  153. package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8825dcc9._.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_76ccf09f._.js +8 -0
  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/5ec82ce8f3aabaf0.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/5ec82ce8f3aabaf0.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/speech-input.tsx +89 -36
  227. package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +275 -0
  228. package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +19 -5
  229. package/web/.next/standalone/web/src/components/chat-interface.tsx +820 -50
  230. package/web/.next/standalone/web/src/hooks/use-workspace-files.ts +108 -0
  231. package/web/.next/standalone/web/src/lib/api.ts +223 -6
  232. package/web/.next/static/chunks/0cc382a66266188e.js +7 -0
  233. package/web/.next/static/chunks/0fda34e553582102.js +1 -0
  234. package/web/.next/{standalone/web/.next/static/chunks/5ec82ce8f3aabaf0.js → static/chunks/6407c045dfc908fe.js} +3 -3
  235. package/web/.next/static/chunks/651e187cc15d66de.js +1 -0
  236. package/web/.next/static/chunks/862ced58ce21a270.js +4 -0
  237. package/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
  238. package/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
  239. package/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
  240. package/web/.next/static/chunks/af22745850132107.css +1 -0
  241. package/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
  242. package/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
  243. package/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
  244. package/web/package.json +4 -0
  245. package/dist/bash-CGAqW7HR.d.ts +0 -80
  246. package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_9bf3c7f3._.js +0 -3
  247. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +0 -3
  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]__a984d933._.js +0 -9
  250. package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +0 -3
  251. package/web/.next/standalone/web/.next/server/chunks/ssr/web_19b6934c._.js +0 -8
  252. package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +0 -7
  253. package/web/.next/standalone/web/.next/server/chunks/ssr/web_e6034803._.js +0 -3
  254. package/web/.next/standalone/web/.next/static/chunks/03d4169891280e04.js +0 -7
  255. package/web/.next/standalone/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  256. package/web/.next/standalone/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  257. package/web/.next/standalone/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  258. package/web/.next/standalone/web/.next/static/chunks/a86053f0894587f2.js +0 -7
  259. package/web/.next/standalone/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  260. package/web/.next/standalone/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  261. package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  262. package/web/.next/standalone/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
  263. package/web/.next/standalone/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  264. package/web/.next/standalone/web/.next/static/static/chunks/03d4169891280e04.js +0 -7
  265. package/web/.next/standalone/web/.next/static/static/chunks/2d5da0cfc011b8d9.js +0 -1
  266. package/web/.next/standalone/web/.next/static/static/chunks/634fd97fab9ed4e4.js +0 -4
  267. package/web/.next/standalone/web/.next/static/static/chunks/77e4bf0421481629.js +0 -1
  268. package/web/.next/standalone/web/.next/static/static/chunks/a86053f0894587f2.js +0 -7
  269. package/web/.next/standalone/web/.next/static/static/chunks/beb9625c4a470042.js +0 -1
  270. package/web/.next/standalone/web/.next/static/static/chunks/c2244168e74b8c78.js +0 -1
  271. package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +0 -5
  272. package/web/.next/standalone/web/.next/static/static/chunks/d0a69c59b1c0d99c.css +0 -1
  273. package/web/.next/standalone/web/.next/static/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  274. package/web/.next/static/chunks/03d4169891280e04.js +0 -7
  275. package/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
  276. package/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
  277. package/web/.next/static/chunks/77e4bf0421481629.js +0 -1
  278. package/web/.next/static/chunks/a86053f0894587f2.js +0 -7
  279. package/web/.next/static/chunks/beb9625c4a470042.js +0 -1
  280. package/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
  281. package/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
  282. package/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
  283. package/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
  284. /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  285. /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  286. /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  287. /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  288. /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  289. /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
  290. /package/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
  291. /package/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
  292. /package/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
@@ -0,0 +1,400 @@
1
+ "use client";
2
+
3
+ import { Badge } from "@/components/ui/badge";
4
+ import { Button } from "@/components/ui/button";
5
+ import { cn } from "@/lib/utils";
6
+ import {
7
+ CheckCircleIcon,
8
+ ChevronDownIcon,
9
+ ChevronRightIcon,
10
+ SearchIcon,
11
+ XCircleIcon,
12
+ Loader2Icon,
13
+ FileIcon,
14
+ CodeIcon,
15
+ FolderIcon,
16
+ TerminalIcon,
17
+ FileTextIcon,
18
+ BotIcon,
19
+ ExternalLinkIcon,
20
+ MaximizeIcon,
21
+ } from "lucide-react";
22
+ import { useState, useEffect, useRef } from "react";
23
+ import { SubagentModal } from "./subagent-modal";
24
+
25
+ export interface SearchInput {
26
+ query: string;
27
+ context?: string;
28
+ }
29
+
30
+ export interface SearchFinding {
31
+ type: "file" | "match" | "pattern";
32
+ path: string;
33
+ content?: string;
34
+ lineNumber?: number;
35
+ relevance: "high" | "medium" | "low";
36
+ context?: string;
37
+ }
38
+
39
+ export interface SearchOutput {
40
+ success?: boolean;
41
+ error?: string;
42
+ query?: string;
43
+ summary?: string;
44
+ findings?: SearchFinding[];
45
+ matchCount?: number;
46
+ filesSearched?: number;
47
+ formattedResult?: string;
48
+ executionId?: string;
49
+ stepsCount?: number;
50
+ }
51
+
52
+ export interface SubagentStep {
53
+ id: string;
54
+ type: "thought" | "tool_call" | "tool_result" | "text";
55
+ content: string;
56
+ toolName?: string;
57
+ toolInput?: unknown;
58
+ toolOutput?: unknown;
59
+ timestamp: number;
60
+ }
61
+
62
+ export interface SearchToolProps {
63
+ input: SearchInput;
64
+ output?: SearchOutput;
65
+ status: "pending" | "streaming" | "running" | "completed" | "rejected" | "error";
66
+ /** Live progress steps from the subagent */
67
+ steps?: SubagentStep[];
68
+ className?: string;
69
+ }
70
+
71
+ const getToolIcon = (toolName?: string) => {
72
+ switch (toolName) {
73
+ case "grep":
74
+ return SearchIcon;
75
+ case "glob":
76
+ return FolderIcon;
77
+ case "read_file":
78
+ return FileTextIcon;
79
+ case "list_dir":
80
+ return FolderIcon;
81
+ default:
82
+ return TerminalIcon;
83
+ }
84
+ };
85
+
86
+ export function SearchTool({
87
+ input,
88
+ output,
89
+ status,
90
+ steps = [],
91
+ className,
92
+ }: SearchToolProps) {
93
+ const [isExpanded, setIsExpanded] = useState(true);
94
+ const [isStepsExpanded, setIsStepsExpanded] = useState(true);
95
+ const [isModalOpen, setIsModalOpen] = useState(false);
96
+ const stepsContainerRef = useRef<HTMLDivElement>(null);
97
+
98
+ const isLoading = status === "pending" || status === "streaming" || status === "running";
99
+ const isCompleted = status === "completed";
100
+ const isError = status === "error" || output?.success === false;
101
+
102
+ // Auto-open modal when search starts (optional - can be removed if not desired)
103
+ useEffect(() => {
104
+ if (isLoading && steps.length === 1) {
105
+ setIsModalOpen(true);
106
+ }
107
+ }, [isLoading, steps.length]);
108
+
109
+ // Auto-scroll steps
110
+ useEffect(() => {
111
+ if (stepsContainerRef.current && isLoading) {
112
+ stepsContainerRef.current.scrollTop = stepsContainerRef.current.scrollHeight;
113
+ }
114
+ }, [steps, isLoading]);
115
+
116
+ const query = input?.query || "";
117
+ const displayQuery = query.length > 80 ? query.slice(0, 80) + "..." : query;
118
+
119
+ const getStatusDisplay = () => {
120
+ if (isLoading) {
121
+ return {
122
+ Icon: Loader2Icon,
123
+ color: "text-blue-600 dark:text-blue-500",
124
+ iconClass: "animate-spin",
125
+ label: steps.length > 0 ? `Searching... (${steps.length} steps)` : "Starting search...",
126
+ };
127
+ }
128
+ if (isCompleted && output?.success) {
129
+ return {
130
+ Icon: CheckCircleIcon,
131
+ color: "text-green-600 dark:text-green-500",
132
+ iconClass: "",
133
+ label: `Found ${output.matchCount || 0} matches`,
134
+ };
135
+ }
136
+ if (isError) {
137
+ return {
138
+ Icon: XCircleIcon,
139
+ color: "text-red-600 dark:text-red-500",
140
+ iconClass: "",
141
+ label: "Search failed",
142
+ };
143
+ }
144
+ return {
145
+ Icon: SearchIcon,
146
+ color: "text-muted-foreground",
147
+ iconClass: "",
148
+ label: "",
149
+ };
150
+ };
151
+
152
+ const { Icon, color, iconClass, label } = getStatusDisplay();
153
+
154
+ return (
155
+ <div className={cn("rounded-lg border overflow-hidden bg-background", className)}>
156
+ {/* Header */}
157
+ <div className="flex items-center justify-between w-full px-3 py-2.5 bg-gradient-to-r from-blue-500/5 to-purple-500/5">
158
+ <button
159
+ onClick={() => setIsExpanded(!isExpanded)}
160
+ className="flex items-center gap-2 min-w-0 flex-1 hover:opacity-80 transition-opacity"
161
+ >
162
+ <Icon className={cn("size-4 shrink-0", color, iconClass)} />
163
+ <BotIcon className="size-3.5 text-muted-foreground shrink-0" />
164
+ <span className="text-sm font-medium">Search Agent</span>
165
+ <code className="text-xs text-muted-foreground font-mono truncate ml-1" title={query}>
166
+ "{displayQuery}"
167
+ </code>
168
+ </button>
169
+ <div className="flex items-center gap-2 shrink-0">
170
+ {label && (
171
+ <Badge
172
+ variant="secondary"
173
+ className={cn(
174
+ "text-xs gap-1",
175
+ isLoading && "bg-blue-500/10 text-blue-600 dark:text-blue-400",
176
+ isCompleted && output?.success && "bg-green-500/10 text-green-600 dark:text-green-400",
177
+ isError && "bg-red-500/10 text-red-600 dark:text-red-400"
178
+ )}
179
+ >
180
+ {label}
181
+ </Badge>
182
+ )}
183
+ <Button
184
+ variant="ghost"
185
+ size="icon"
186
+ className="size-7"
187
+ onClick={(e) => {
188
+ e.stopPropagation();
189
+ setIsModalOpen(true);
190
+ }}
191
+ title="View in fullscreen"
192
+ >
193
+ <MaximizeIcon className="size-3.5" />
194
+ </Button>
195
+ <button onClick={() => setIsExpanded(!isExpanded)}>
196
+ {isExpanded ? (
197
+ <ChevronDownIcon className="size-4 text-muted-foreground" />
198
+ ) : (
199
+ <ChevronRightIcon className="size-4 text-muted-foreground" />
200
+ )}
201
+ </button>
202
+ </div>
203
+ </div>
204
+
205
+ {/* Content */}
206
+ {isExpanded && (
207
+ <div className="border-t">
208
+ {/* Query */}
209
+ <div className="px-3 py-2 bg-muted/30 border-b">
210
+ <div className="flex items-start gap-2">
211
+ <SearchIcon className="size-3.5 text-muted-foreground mt-0.5 shrink-0" />
212
+ <p className="text-xs text-muted-foreground">{query}</p>
213
+ </div>
214
+ {input.context && (
215
+ <div className="mt-1.5 pl-5 text-xs text-muted-foreground/70 italic">
216
+ Context: {input.context}
217
+ </div>
218
+ )}
219
+ </div>
220
+
221
+ {/* Steps - Show subagent activity */}
222
+ {steps.length > 0 && (
223
+ <div className="border-b">
224
+ <button
225
+ onClick={(e) => {
226
+ e.stopPropagation();
227
+ setIsStepsExpanded(!isStepsExpanded);
228
+ }}
229
+ className="flex items-center gap-2 w-full px-3 py-1.5 text-xs text-muted-foreground hover:bg-muted/50"
230
+ >
231
+ {isStepsExpanded ? (
232
+ <ChevronDownIcon className="size-3" />
233
+ ) : (
234
+ <ChevronRightIcon className="size-3" />
235
+ )}
236
+ <span>Agent Steps ({steps.length})</span>
237
+ {isLoading && (
238
+ <Loader2Icon className="size-3 animate-spin ml-auto" />
239
+ )}
240
+ </button>
241
+
242
+ {isStepsExpanded && (
243
+ <div
244
+ ref={stepsContainerRef}
245
+ className="max-h-[200px] overflow-auto bg-zinc-950"
246
+ >
247
+ {steps.map((step, index) => (
248
+ <StepItem key={step.id || index} step={step} />
249
+ ))}
250
+ </div>
251
+ )}
252
+ </div>
253
+ )}
254
+
255
+ {/* Results */}
256
+ {output && (
257
+ <div className="p-3">
258
+ {output.success ? (
259
+ <div className="space-y-3">
260
+ {/* Summary */}
261
+ {output.summary && (
262
+ <div className="text-sm text-foreground">
263
+ {output.summary}
264
+ </div>
265
+ )}
266
+
267
+ {/* Findings */}
268
+ {output.findings && output.findings.length > 0 && (
269
+ <div className="space-y-1">
270
+ <div className="text-xs font-medium text-muted-foreground mb-2">
271
+ Key Findings ({output.findings.length})
272
+ </div>
273
+ <div className="space-y-1 max-h-[200px] overflow-auto">
274
+ {output.findings.slice(0, 10).map((finding, index) => (
275
+ <FindingItem key={index} finding={finding} />
276
+ ))}
277
+ {output.findings.length > 10 && (
278
+ <div className="text-xs text-muted-foreground pl-5">
279
+ ... and {output.findings.length - 10} more
280
+ </div>
281
+ )}
282
+ </div>
283
+ </div>
284
+ )}
285
+
286
+ {/* Stats */}
287
+ <div className="flex items-center gap-4 text-xs text-muted-foreground pt-2 border-t">
288
+ <span>{output.matchCount || 0} matches</span>
289
+ <span>{output.filesSearched || 0} files searched</span>
290
+ <span>{output.stepsCount || 0} steps</span>
291
+ </div>
292
+ </div>
293
+ ) : (
294
+ <div className="flex items-start gap-2 text-red-600 dark:text-red-400 text-sm">
295
+ <XCircleIcon className="size-4 mt-0.5 shrink-0" />
296
+ <span>{output.error || "Search failed"}</span>
297
+ </div>
298
+ )}
299
+ </div>
300
+ )}
301
+
302
+ {/* Loading state */}
303
+ {isLoading && !output && steps.length === 0 && (
304
+ <div className="p-4 flex items-center justify-center gap-2 text-muted-foreground">
305
+ <Loader2Icon className="size-4 animate-spin" />
306
+ <span className="text-sm">Initializing search agent...</span>
307
+ </div>
308
+ )}
309
+ </div>
310
+ )}
311
+
312
+ {/* Fullscreen Modal */}
313
+ <SubagentModal
314
+ open={isModalOpen}
315
+ onOpenChange={setIsModalOpen}
316
+ title="Search Agent"
317
+ description="Exploring your codebase to find relevant information"
318
+ query={query}
319
+ steps={steps}
320
+ status={isLoading ? "running" : isError ? "error" : "completed"}
321
+ />
322
+ </div>
323
+ );
324
+ }
325
+
326
+ function StepItem({ step }: { step: SubagentStep }) {
327
+ const ToolIcon = getToolIcon(step.toolName);
328
+
329
+ return (
330
+ <div className="px-3 py-1.5 text-xs font-mono border-b border-zinc-800 last:border-0">
331
+ <div className="flex items-start gap-2">
332
+ {step.type === "tool_call" && (
333
+ <>
334
+ <ToolIcon className="size-3 text-blue-400 mt-0.5 shrink-0" />
335
+ <div className="min-w-0">
336
+ <span className="text-blue-400">{step.toolName}</span>
337
+ {step.toolInput !== undefined && step.toolInput !== null && (
338
+ <span className="text-zinc-500 ml-1 truncate">
339
+ {(() => {
340
+ try {
341
+ return JSON.stringify(step.toolInput).slice(0, 50) + "...";
342
+ } catch {
343
+ return "...";
344
+ }
345
+ })()}
346
+ </span>
347
+ )}
348
+ </div>
349
+ </>
350
+ )}
351
+ {step.type === "tool_result" && (
352
+ <>
353
+ <CheckCircleIcon className="size-3 text-green-400 mt-0.5 shrink-0" />
354
+ <span className="text-zinc-400 truncate">{step.content}</span>
355
+ </>
356
+ )}
357
+ {step.type === "text" && (
358
+ <>
359
+ <CodeIcon className="size-3 text-purple-400 mt-0.5 shrink-0" />
360
+ <span className="text-zinc-300 truncate">{step.content}</span>
361
+ </>
362
+ )}
363
+ {step.type === "thought" && (
364
+ <>
365
+ <BotIcon className="size-3 text-amber-400 mt-0.5 shrink-0" />
366
+ <span className="text-zinc-400 italic truncate">{step.content}</span>
367
+ </>
368
+ )}
369
+ </div>
370
+ </div>
371
+ );
372
+ }
373
+
374
+ function FindingItem({ finding }: { finding: SearchFinding }) {
375
+ const Icon = finding.type === "match" ? CodeIcon : FileIcon;
376
+ const relevanceColor = {
377
+ high: "text-green-500",
378
+ medium: "text-amber-500",
379
+ low: "text-muted-foreground",
380
+ }[finding.relevance];
381
+
382
+ return (
383
+ <div className="flex items-start gap-2 text-xs py-1 px-2 rounded hover:bg-muted/50">
384
+ <Icon className={cn("size-3 mt-0.5 shrink-0", relevanceColor)} />
385
+ <div className="min-w-0 flex-1">
386
+ <div className="flex items-center gap-2">
387
+ <span className="font-mono text-foreground truncate">{finding.path}</span>
388
+ {finding.lineNumber && (
389
+ <span className="text-muted-foreground">:{finding.lineNumber}</span>
390
+ )}
391
+ </div>
392
+ {finding.content && (
393
+ <code className="text-muted-foreground block truncate mt-0.5">
394
+ {finding.content}
395
+ </code>
396
+ )}
397
+ </div>
398
+ </div>
399
+ );
400
+ }
@@ -2,7 +2,7 @@
2
2
 
3
3
  import { Button } from "@/components/ui/button";
4
4
  import { cn } from "@/lib/utils";
5
- import { LoaderIcon, MicIcon, SquareIcon } from "lucide-react";
5
+ import { LoaderIcon, MicIcon, MicOffIcon } from "lucide-react";
6
6
  import {
7
7
  type ComponentProps,
8
8
  useCallback,
@@ -17,6 +17,7 @@ interface SpeechRecognition extends EventTarget {
17
17
  lang: string;
18
18
  start(): void;
19
19
  stop(): void;
20
+ abort(): void;
20
21
  onstart: ((this: SpeechRecognition, ev: Event) => void) | null;
21
22
  onend: ((this: SpeechRecognition, ev: Event) => void) | null;
22
23
  onresult:
@@ -67,8 +68,11 @@ declare global {
67
68
 
68
69
  type SpeechInputMode = "speech-recognition" | "media-recorder" | "none";
69
70
 
70
- export type SpeechInputProps = ComponentProps<typeof Button> & {
71
+ export type SpeechInputProps = Omit<ComponentProps<typeof Button>, 'onClick'> & {
72
+ /** Called with finalized transcript text (confirmed words) */
71
73
  onTranscriptionChange?: (text: string) => void;
74
+ /** Called with interim transcript text as user speaks (live preview) */
75
+ onInterimTranscription?: (text: string) => void;
72
76
  /**
73
77
  * Callback for when audio is recorded using MediaRecorder fallback.
74
78
  * This is called in browsers that don't support the Web Speech API (Firefox, Safari).
@@ -98,19 +102,20 @@ const detectSpeechInputMode = (): SpeechInputMode => {
98
102
  export const SpeechInput = ({
99
103
  className,
100
104
  onTranscriptionChange,
105
+ onInterimTranscription,
101
106
  onAudioRecorded,
102
107
  lang = "en-US",
108
+ disabled,
103
109
  ...props
104
110
  }: SpeechInputProps) => {
105
111
  const [isListening, setIsListening] = useState(false);
106
112
  const [isProcessing, setIsProcessing] = useState(false);
107
113
  const [mode, setMode] = useState<SpeechInputMode>("none");
108
- const [recognition, setRecognition] = useState<SpeechRecognition | null>(
109
- null
110
- );
111
114
  const recognitionRef = useRef<SpeechRecognition | null>(null);
112
115
  const mediaRecorderRef = useRef<MediaRecorder | null>(null);
113
116
  const audioChunksRef = useRef<Blob[]>([]);
117
+ // Track the accumulated final transcript for the current session
118
+ const sessionTranscriptRef = useRef<string>("");
114
119
 
115
120
  // Detect mode on mount
116
121
  useEffect(() => {
@@ -123,9 +128,9 @@ export const SpeechInput = ({
123
128
  return;
124
129
  }
125
130
 
126
- const SpeechRecognition =
131
+ const SpeechRecognitionAPI =
127
132
  window.SpeechRecognition || window.webkitSpeechRecognition;
128
- const speechRecognition = new SpeechRecognition();
133
+ const speechRecognition = new SpeechRecognitionAPI();
129
134
 
130
135
  speechRecognition.continuous = true;
131
136
  speechRecognition.interimResults = true;
@@ -133,41 +138,58 @@ export const SpeechInput = ({
133
138
 
134
139
  speechRecognition.onstart = () => {
135
140
  setIsListening(true);
141
+ sessionTranscriptRef.current = "";
136
142
  };
137
143
 
138
144
  speechRecognition.onend = () => {
139
145
  setIsListening(false);
146
+ // Clear any interim display when stopped
147
+ onInterimTranscription?.("");
140
148
  };
141
149
 
142
150
  speechRecognition.onresult = (event) => {
143
- let finalTranscript = "";
151
+ let interimTranscript = "";
152
+ let newFinalTranscript = "";
144
153
 
145
154
  for (let i = event.resultIndex; i < event.results.length; i++) {
146
155
  const result = event.results[i];
156
+ const transcript = result[0]?.transcript ?? "";
157
+
147
158
  if (result.isFinal) {
148
- finalTranscript += result[0]?.transcript ?? "";
159
+ newFinalTranscript += transcript;
160
+ } else {
161
+ interimTranscript += transcript;
149
162
  }
150
163
  }
151
164
 
152
- if (finalTranscript) {
153
- onTranscriptionChange?.(finalTranscript);
165
+ // If we have new finalized text, add it to the input
166
+ if (newFinalTranscript) {
167
+ sessionTranscriptRef.current += newFinalTranscript;
168
+ onTranscriptionChange?.(newFinalTranscript);
169
+ // Clear interim since it's now final
170
+ onInterimTranscription?.("");
171
+ }
172
+
173
+ // Show interim results as live preview
174
+ if (interimTranscript) {
175
+ onInterimTranscription?.(interimTranscript);
154
176
  }
155
177
  };
156
178
 
157
179
  speechRecognition.onerror = (event) => {
158
180
  console.error("Speech recognition error:", event.error);
159
181
  setIsListening(false);
182
+ onInterimTranscription?.("");
160
183
  };
161
184
 
162
185
  recognitionRef.current = speechRecognition;
163
- setRecognition(speechRecognition);
164
186
 
165
187
  return () => {
166
188
  if (recognitionRef.current) {
167
- recognitionRef.current.stop();
189
+ recognitionRef.current.abort();
168
190
  }
169
191
  };
170
- }, [mode, onTranscriptionChange, lang]);
192
+ }, [mode, lang, onTranscriptionChange, onInterimTranscription]);
171
193
 
172
194
  // Start MediaRecorder recording
173
195
  const startMediaRecorder = useCallback(async () => {
@@ -241,11 +263,16 @@ export const SpeechInput = ({
241
263
  }, []);
242
264
 
243
265
  const toggleListening = useCallback(() => {
244
- if (mode === "speech-recognition" && recognition) {
266
+ if (mode === "speech-recognition" && recognitionRef.current) {
245
267
  if (isListening) {
246
- recognition.stop();
268
+ recognitionRef.current.stop();
247
269
  } else {
248
- recognition.start();
270
+ try {
271
+ recognitionRef.current.start();
272
+ } catch (e) {
273
+ // Recognition might already be started
274
+ console.warn("Speech recognition start error:", e);
275
+ }
249
276
  }
250
277
  } else if (mode === "media-recorder") {
251
278
  if (isListening) {
@@ -254,45 +281,71 @@ export const SpeechInput = ({
254
281
  startMediaRecorder();
255
282
  }
256
283
  }
257
- }, [mode, recognition, isListening, startMediaRecorder, stopMediaRecorder]);
284
+ }, [mode, isListening, startMediaRecorder, stopMediaRecorder]);
258
285
 
259
286
  // Determine if button should be disabled
260
- const isDisabled =
287
+ const isButtonDisabled =
288
+ disabled ||
261
289
  mode === "none" ||
262
- (mode === "speech-recognition" && !recognition) ||
290
+ (mode === "speech-recognition" && !recognitionRef.current) ||
263
291
  (mode === "media-recorder" && !onAudioRecorded) ||
264
292
  isProcessing;
265
293
 
294
+ // Not supported - show disabled mic with tooltip hint
295
+ if (mode === "none") {
296
+ return (
297
+ <Button
298
+ type="button"
299
+ className={cn(
300
+ "relative rounded-full opacity-50 cursor-not-allowed",
301
+ className
302
+ )}
303
+ disabled
304
+ title="Speech recognition not supported in this browser"
305
+ {...props}
306
+ >
307
+ <MicOffIcon className="size-4" />
308
+ </Button>
309
+ );
310
+ }
311
+
266
312
  return (
267
313
  <div className="relative inline-flex items-center justify-center">
268
- {/* Animated pulse rings */}
269
- {isListening &&
270
- [0, 1, 2].map((index) => (
271
- <div
272
- className="absolute inset-0 animate-ping rounded-full border-2 border-red-400/30"
273
- key={index}
274
- style={{
275
- animationDelay: `${index * 0.3}s`,
276
- animationDuration: "2s",
277
- }}
314
+ {/* Animated pulse rings when listening */}
315
+ {isListening && (
316
+ <>
317
+ <span className="absolute inset-0 rounded-full bg-red-500/20 animate-ping" />
318
+ <span
319
+ className="absolute inset-0 rounded-full bg-red-500/10 animate-ping"
320
+ style={{ animationDelay: "0.2s" }}
278
321
  />
279
- ))}
322
+ </>
323
+ )}
280
324
 
281
325
  {/* Main record button */}
282
326
  <Button
327
+ type="button"
283
328
  className={cn(
284
- "relative z-10 rounded-full transition-all duration-300",
329
+ "relative z-10 rounded-full transition-all duration-200",
285
330
  isListening
286
- ? "bg-destructive text-white hover:bg-destructive/80 hover:text-white"
287
- : "bg-primary text-primary-foreground hover:bg-primary/80 hover:text-primary-foreground",
331
+ ? "bg-red-500 text-white hover:bg-red-600 shadow-lg shadow-red-500/25"
332
+ : "hover:bg-muted",
288
333
  className
289
334
  )}
290
- disabled={isDisabled}
335
+ disabled={isButtonDisabled}
291
336
  onClick={toggleListening}
337
+ title={isListening ? "Stop recording" : "Start voice input"}
338
+ variant={isListening ? "default" : "ghost"}
292
339
  {...props}
293
340
  >
294
341
  {isProcessing && <LoaderIcon className="size-4 animate-spin" />}
295
- {!isProcessing && isListening && <SquareIcon className="size-4" />}
342
+ {!isProcessing && isListening && (
343
+ <div className="relative">
344
+ <MicIcon className="size-4" />
345
+ {/* Recording indicator dot */}
346
+ <span className="absolute -top-0.5 -right-0.5 size-2 bg-white rounded-full animate-pulse" />
347
+ </div>
348
+ )}
296
349
  {!(isProcessing || isListening) && <MicIcon className="size-4" />}
297
350
  </Button>
298
351
  </div>