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.
- package/dist/agent/index.d.ts +3 -3
- package/dist/agent/index.js +1361 -215
- package/dist/agent/index.js.map +1 -1
- package/dist/cli.js +2179 -349
- package/dist/cli.js.map +1 -1
- package/dist/db/index.d.ts +20 -2
- package/dist/db/index.js +97 -0
- package/dist/db/index.js.map +1 -1
- package/dist/{index-BzedNBK-.d.ts → index-BblbmG_0.d.ts} +42 -6
- package/dist/index.d.ts +6 -6
- package/dist/index.js +2165 -335
- package/dist/index.js.map +1 -1
- package/dist/{schema-CkrIadxa.d.ts → schema-D_8A4k01.d.ts} +270 -3
- package/dist/search-ybREg7F_.d.ts +254 -0
- package/dist/server/index.js +2163 -333
- package/dist/server/index.js.map +1 -1
- package/dist/tools/index.d.ts +7 -56
- package/dist/tools/index.js +894 -27
- package/dist/tools/index.js.map +1 -1
- package/package.json +5 -1
- package/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/BUILD_ID +1 -1
- package/web/.next/standalone/web/.next/app-path-routes-manifest.json +4 -0
- package/web/.next/standalone/web/.next/build-manifest.json +7 -6
- package/web/.next/standalone/web/.next/prerender-manifest.json +99 -3
- package/web/.next/standalone/web/.next/required-server-files.json +28 -4
- package/web/.next/standalone/web/.next/routes-manifest.json +24 -0
- package/web/.next/standalone/web/.next/server/app/(main)/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/(main)/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/(main)/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/(main)/session/[id]/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/_global-error/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.html +2 -2
- package/web/.next/standalone/web/.next/server/app/_global-error.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page/build-manifest.json +5 -4
- package/web/.next/standalone/web/.next/server/app/_not-found/page.js +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.html +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/api/config/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/api/health/route.js.nft.json +1 -1
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.html +86 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.rsc +36 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_full.segment.rsc +36 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation/__PAGE__.segment.rsc +22 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs/installation.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/installation.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.html +268 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.rsc +82 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_full.segment.rsc +82 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills/__PAGE__.segment.rsc +66 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs/skills.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/skills.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/app-paths-manifest.json +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/build-manifest.json +18 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/next-font-manifest.json +11 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/react-loadable-manifest.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page/server-reference-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js +21 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.map +5 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page.js.nft.json +1 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools/page_client-reference-manifest.js +2 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.html +242 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.meta +16 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.rsc +87 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_full.segment.rsc +87 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools/__PAGE__.segment.rsc +72 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs/tools.segment.rsc +4 -0
- package/web/.next/standalone/web/.next/server/app/docs/tools.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/docs.html +74 -0
- package/web/.next/standalone/web/.next/server/app/docs.meta +15 -0
- package/web/.next/standalone/web/.next/server/app/docs.rsc +34 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_full.segment.rsc +34 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_head.segment.rsc +6 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_index.segment.rsc +7 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/_tree.segment.rsc +3 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs/__PAGE__.segment.rsc +20 -0
- package/web/.next/standalone/web/.next/server/app/docs.segments/docs.segment.rsc +5 -0
- package/web/.next/standalone/web/.next/server/app/index.html +1 -1
- package/web/.next/standalone/web/.next/server/app/index.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p/__PAGE__.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/!KG1haW4p.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_full.segment.rsc +4 -4
- package/web/.next/standalone/web/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/web/.next/standalone/web/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/web/.next/standalone/web/.next/server/app-paths-manifest.json +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_378282b1._.js → 2374f_244589df._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_5de336d2._.js → 2374f_41a27541._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_30f9df13._.js → 2374f_47c9e2d5._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_d94c2b70._.js → 2374f_4bf2df9d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_663d1038._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_954e49c0._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_1d78db71._.js → 2374f_c33b095a._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_bbc99511._.js → 2374f_fa61fbb2._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{2374f_8825dcc9._.js → 2374f_fb82ac0d._.js} +1 -1
- package/web/.next/standalone/web/.next/server/chunks/ssr/{web_96bca05b._.js → 2374f_next_dist_bbe64674._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__7f04455b._.js → [root-of-the-server]__1e06ddf7._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2b151e1c._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__2dbf511a._.js +9 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__397fadd4._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__44bd8bd1._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__70cecda8._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__9fdf9974._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__f18f92f4._.js → [root-of-the-server]__b050bb8f._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__d3034cd2._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/{[root-of-the-server]__c3a1e22c._.js → [root-of-the-server]__ef2713cf._.js} +2 -2
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__f764bebe._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_046bf7db._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_656c1e45._.js +7 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_76ccf09f._.js +8 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_installation_page_actions_52cc0648.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_page_actions_4fe77da8.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_skills_page_actions_251df2e1.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web__next-internal_server_app_docs_tools_page_actions_3e6382b0.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_a565dc94._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_b1cce0b7._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_b42ed1be._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_c0c2bee4._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_eea9c122._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_ff00a5c3._.js +4 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_layout_tsx_453f6492._.js +3 -0
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_src_app_(main)_page_tsx_5ac4794b._.js +3 -0
- package/web/.next/standalone/web/.next/server/middleware-build-manifest.js +5 -4
- package/web/.next/standalone/web/.next/server/next-font-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/next-font-manifest.json +16 -0
- package/web/.next/standalone/web/.next/server/pages/404.html +1 -1
- package/web/.next/standalone/web/.next/server/pages/500.html +2 -2
- package/web/.next/standalone/web/.next/server/server-reference-manifest.js +1 -1
- package/web/.next/standalone/web/.next/server/server-reference-manifest.json +1 -1
- package/web/.next/standalone/web/.next/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/standalone/web/.next/static/{static/chunks/5ec82ce8f3aabaf0.js → chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/standalone/web/.next/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/standalone/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/standalone/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/af22745850132107.css +1 -0
- package/web/.next/standalone/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/standalone/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/standalone/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/.next/standalone/web/.next/static/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/{static/chunks/5ec82ce8f3aabaf0.js → standalone/web/.next/static/static/chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/standalone/web/.next/static/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/standalone/web/.next/static/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/standalone/web/.next/static/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/af22745850132107.css +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/standalone/web/.next/static/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/standalone/web/.next/static/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/.next/standalone/web/mdx-components.tsx +119 -0
- package/web/.next/standalone/web/next.config.ts +15 -1
- package/web/.next/standalone/web/package-lock.json +559 -4
- package/web/.next/standalone/web/package.json +4 -0
- package/web/.next/standalone/web/runtime-config.json +1 -1
- package/web/.next/standalone/web/server.js +1 -1
- package/web/.next/standalone/web/src/app/(main)/page.tsx +127 -5
- package/web/.next/standalone/web/src/app/docs/installation/page.mdx +128 -0
- package/web/.next/standalone/web/src/app/docs/layout.tsx +74 -0
- package/web/.next/standalone/web/src/app/docs/page.mdx +90 -0
- package/web/.next/standalone/web/src/app/docs/skills/page.mdx +334 -0
- package/web/.next/standalone/web/src/app/docs/tools/page.mdx +300 -0
- package/web/.next/standalone/web/src/components/ai-elements/mention-input.tsx +809 -0
- package/web/.next/standalone/web/src/components/ai-elements/search-tool.tsx +400 -0
- package/web/.next/standalone/web/src/components/ai-elements/speech-input.tsx +89 -36
- package/web/.next/standalone/web/src/components/ai-elements/subagent-modal.tsx +275 -0
- package/web/.next/standalone/web/src/components/ai-elements/write-file-tool.tsx +19 -5
- package/web/.next/standalone/web/src/components/chat-interface.tsx +820 -50
- package/web/.next/standalone/web/src/hooks/use-workspace-files.ts +108 -0
- package/web/.next/standalone/web/src/lib/api.ts +223 -6
- package/web/.next/static/chunks/0cc382a66266188e.js +7 -0
- package/web/.next/static/chunks/0fda34e553582102.js +1 -0
- package/web/.next/{standalone/web/.next/static/chunks/5ec82ce8f3aabaf0.js → static/chunks/6407c045dfc908fe.js} +3 -3
- package/web/.next/static/chunks/651e187cc15d66de.js +1 -0
- package/web/.next/static/chunks/862ced58ce21a270.js +4 -0
- package/web/.next/static/chunks/89bc21c0443670f4.js +1 -0
- package/web/.next/static/chunks/8f4edf22ededc29b.js +7 -0
- package/web/.next/static/chunks/ad6b9dbb257d62cc.js +1 -0
- package/web/.next/static/chunks/af22745850132107.css +1 -0
- package/web/.next/static/chunks/b9ad1584d4e11d12.js +1 -0
- package/web/.next/static/chunks/db9b22c844a35e20.js +5 -0
- package/web/.next/static/chunks/turbopack-597558bb7b6982f6.js +4 -0
- package/web/package.json +4 -0
- package/dist/bash-CGAqW7HR.d.ts +0 -80
- package/web/.next/standalone/web/.next/server/chunks/ssr/2374f_9bf3c7f3._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__0f6b5fa7._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__513c6b45._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__a984d933._.js +0 -9
- package/web/.next/standalone/web/.next/server/chunks/ssr/[root-of-the-server]__de58a952._.js +0 -3
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_19b6934c._.js +0 -8
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_d7d3e40d._.js +0 -7
- package/web/.next/standalone/web/.next/server/chunks/ssr/web_e6034803._.js +0 -3
- package/web/.next/standalone/web/.next/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/standalone/web/.next/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/a86053f0894587f2.js +0 -7
- package/web/.next/standalone/web/.next/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/standalone/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/standalone/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
- package/web/.next/standalone/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- package/web/.next/standalone/web/.next/static/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/standalone/web/.next/static/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/a86053f0894587f2.js +0 -7
- package/web/.next/standalone/web/.next/static/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/standalone/web/.next/static/static/chunks/d0a69c59b1c0d99c.css +0 -1
- package/web/.next/standalone/web/.next/static/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- package/web/.next/static/chunks/03d4169891280e04.js +0 -7
- package/web/.next/static/chunks/2d5da0cfc011b8d9.js +0 -1
- package/web/.next/static/chunks/634fd97fab9ed4e4.js +0 -4
- package/web/.next/static/chunks/77e4bf0421481629.js +0 -1
- package/web/.next/static/chunks/a86053f0894587f2.js +0 -7
- package/web/.next/static/chunks/beb9625c4a470042.js +0 -1
- package/web/.next/static/chunks/c2244168e74b8c78.js +0 -1
- package/web/.next/static/chunks/c81c1aec4369c77f.js +0 -5
- package/web/.next/static/chunks/d0a69c59b1c0d99c.css +0 -1
- package/web/.next/static/chunks/turbopack-54bc7d566cd2d105.js +0 -4
- /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → static/uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /package/web/.next/standalone/web/.next/static/{static/kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_ssgManifest.js +0 -0
- /package/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_buildManifest.js +0 -0
- /package/web/.next/static/{kABnAk0Y1tlcrUKDlM8UT → uXbuwS0U7fRElucaXbKUe}/_clientMiddlewareManifest.json +0 -0
- /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,
|
|
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
|
|
131
|
+
const SpeechRecognitionAPI =
|
|
127
132
|
window.SpeechRecognition || window.webkitSpeechRecognition;
|
|
128
|
-
const speechRecognition = new
|
|
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
|
|
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
|
-
|
|
159
|
+
newFinalTranscript += transcript;
|
|
160
|
+
} else {
|
|
161
|
+
interimTranscript += transcript;
|
|
149
162
|
}
|
|
150
163
|
}
|
|
151
164
|
|
|
152
|
-
|
|
153
|
-
|
|
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.
|
|
189
|
+
recognitionRef.current.abort();
|
|
168
190
|
}
|
|
169
191
|
};
|
|
170
|
-
}, [mode, onTranscriptionChange,
|
|
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" &&
|
|
266
|
+
if (mode === "speech-recognition" && recognitionRef.current) {
|
|
245
267
|
if (isListening) {
|
|
246
|
-
|
|
268
|
+
recognitionRef.current.stop();
|
|
247
269
|
} else {
|
|
248
|
-
|
|
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,
|
|
284
|
+
}, [mode, isListening, startMediaRecorder, stopMediaRecorder]);
|
|
258
285
|
|
|
259
286
|
// Determine if button should be disabled
|
|
260
|
-
const
|
|
287
|
+
const isButtonDisabled =
|
|
288
|
+
disabled ||
|
|
261
289
|
mode === "none" ||
|
|
262
|
-
(mode === "speech-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
|
-
|
|
271
|
-
<
|
|
272
|
-
|
|
273
|
-
|
|
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-
|
|
329
|
+
"relative z-10 rounded-full transition-all duration-200",
|
|
285
330
|
isListening
|
|
286
|
-
? "bg-
|
|
287
|
-
: "
|
|
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={
|
|
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 &&
|
|
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>
|