vibepulse 0.2.0 → 0.2.1
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/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +4 -0
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/fallback-build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +32 -0
- package/.next/server/app/_global-error/page.js +1 -1
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/node/events/route.js +1 -1
- package/.next/server/app/api/node/events/route.js.nft.json +1 -1
- package/.next/server/app/api/node/health/route.js +1 -1
- package/.next/server/app/api/node/health/route.js.nft.json +1 -1
- package/.next/server/app/api/node/sessions/[id]/archive/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route/build-manifest.json +11 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route.js +6 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route.js.map +5 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route.js.nft.json +1 -0
- package/.next/server/app/api/node/sessions/[id]/archive/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route/build-manifest.json +11 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route.js +7 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route.js.map +5 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -0
- package/.next/server/app/api/node/sessions/[id]/delete/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route/build-manifest.json +11 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js +7 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.map +5 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -0
- package/.next/server/app/api/node/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/node/sessions/route.js +1 -1
- package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-events/route.js +1 -1
- package/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/archive/route.js +2 -1
- package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
- package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route/build-manifest.json +11 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route.js +7 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route.js.map +5 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +4 -0
- package/.next/server/chunks/[root-of-the-server]__1211da38._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__1211da38._.js.map +1 -0
- package/.next/{standalone/.next/server/chunks/[root-of-the-server]__b698889b._.js → server/chunks/[root-of-the-server]__1b87ec42._.js} +2 -2
- package/.next/server/chunks/[root-of-the-server]__1b87ec42._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__2b526e7a._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__db285678._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +1 -0
- package/.next/{standalone/.next/server/chunks/[root-of-the-server]__16a9eb0a._.js → server/chunks/[root-of-the-server]__e00a9200._.js} +2 -2
- package/.next/server/chunks/{[root-of-the-server]__16a9eb0a._.js.map → [root-of-the-server]__e00a9200._.js.map} +1 -1
- package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js.map +1 -0
- package/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js +3 -0
- package/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js.map +1 -1
- package/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js → [root-of-the-server]__631e12d0._.js} +2 -2
- package/.next/server/chunks/ssr/[root-of-the-server]__a8cd3911._.js +3 -0
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +4 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/routes-manifest.json +32 -0
- package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/node/events/route.js +1 -1
- package/.next/standalone/.next/server/app/api/node/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/health/route.js +1 -1
- package/.next/standalone/.next/server/app/api/node/health/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js +6 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/archive/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js +7 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/delete/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js +7 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/node/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-events/route.js +1 -1
- package/.next/standalone/.next/server/app/api/opencode-events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +2 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +7 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/sessions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page.js +1 -1
- package/.next/standalone/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +4 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1211da38._.js +3 -0
- package/.next/{server/chunks/[root-of-the-server]__b698889b._.js → standalone/.next/server/chunks/[root-of-the-server]__1b87ec42._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2b526e7a._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +3 -0
- package/.next/{server/chunks/[root-of-the-server]__16a9eb0a._.js → standalone/.next/server/chunks/[root-of-the-server]__e00a9200._.js} +2 -2
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__e5df5e5f._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_archive_route_actions_237255a5.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_node_sessions_[id]_delete_route_actions_e5d426f6.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_open-editor_route_actions_eaebf476.js +3 -0
- package/.next/standalone/.next/server/chunks/ce889_server_app_api_node_sessions_[id]_open-editor_route_actions_791cdf5b.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7e181e75.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_fa835ac3.js +2 -2
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js → [root-of-the-server]__631e12d0._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__a8cd3911._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +3 -3
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/7ac19aaef01f4a03.js +13 -0
- package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +3 -0
- package/.next/standalone/AGENTS.md +85 -0
- package/.next/standalone/README.md +76 -0
- package/.next/standalone/__mocks__/child_process.ts +4 -0
- package/.next/standalone/bin/dev-runtime.js +58 -0
- package/.next/standalone/bin/vibepulse.js +87 -0
- package/.next/standalone/check-hsql.mjs +71 -0
- package/.next/standalone/docs/session-status-detection.md +258 -0
- package/.next/standalone/eslint.config.mjs +31 -0
- package/.next/standalone/next.config.ts +8 -0
- package/.next/standalone/package-lock.json +10312 -0
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/postcss.config.mjs +7 -0
- package/.next/standalone/src/AGENTS.md +41 -0
- package/.next/standalone/src/app/api/AGENTS.md +40 -0
- package/.next/standalone/src/app/api/node/events/route.test.ts +196 -0
- package/.next/standalone/src/app/api/node/events/route.ts +259 -0
- package/.next/standalone/src/app/api/node/health/route.test.ts +190 -0
- package/.next/standalone/src/app/api/node/health/route.ts +48 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +128 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +97 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/delete/route.test.ts +113 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/delete/route.ts +81 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/open-editor/route.test.ts +206 -0
- package/.next/standalone/src/app/api/node/sessions/[id]/open-editor/route.ts +123 -0
- package/.next/standalone/src/app/api/node/sessions/route.test.ts +408 -0
- package/.next/standalone/src/app/api/node/sessions/route.ts +1094 -0
- package/.next/standalone/src/app/api/nodes/route.test.ts +237 -0
- package/.next/standalone/src/app/api/nodes/route.ts +176 -0
- package/.next/standalone/src/app/api/opencode-config/route.test.ts +86 -0
- package/.next/standalone/src/app/api/opencode-config/route.ts +376 -0
- package/.next/standalone/src/app/api/opencode-config/status/route.ts +31 -0
- package/.next/standalone/src/app/api/opencode-events/route.test.ts +624 -0
- package/.next/standalone/src/app/api/opencode-events/route.ts +508 -0
- package/.next/standalone/src/app/api/opencode-models/route.test.ts +167 -0
- package/.next/standalone/src/app/api/opencode-models/route.ts +76 -0
- package/.next/standalone/src/app/api/profiles/[id]/apply/route.ts +49 -0
- package/.next/standalone/src/app/api/profiles/[id]/export/route.ts +31 -0
- package/.next/standalone/src/app/api/profiles/[id]/route.ts +160 -0
- package/.next/standalone/src/app/api/profiles/import/route.test.js +107 -0
- package/.next/standalone/src/app/api/profiles/import/route.ts +65 -0
- package/.next/standalone/src/app/api/profiles/route.ts +107 -0
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +136 -0
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +170 -0
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +113 -0
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +137 -0
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +218 -0
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +85 -0
- package/.next/standalone/src/app/api/sessions/[id]/route.test.ts +531 -0
- package/.next/standalone/src/app/api/sessions/[id]/route.ts +75 -0
- package/.next/standalone/src/app/api/sessions/route.test.ts +1298 -0
- package/.next/standalone/src/app/api/sessions/route.ts +1695 -0
- package/.next/standalone/src/app/favicon.ico +0 -0
- package/.next/standalone/src/app/globals.css +66 -0
- package/.next/standalone/src/app/layout.tsx +37 -0
- package/.next/standalone/src/app/page.test.tsx +134 -0
- package/.next/standalone/src/app/page.tsx +358 -0
- package/.next/standalone/src/components/AGENTS.md +42 -0
- package/.next/standalone/src/components/ErrorBoundary.tsx +72 -0
- package/.next/standalone/src/components/KanbanBoard.test.tsx +704 -0
- package/.next/standalone/src/components/KanbanBoard.tsx +852 -0
- package/.next/standalone/src/components/LoadingState.tsx +37 -0
- package/.next/standalone/src/components/ProjectCard.test.tsx +773 -0
- package/.next/standalone/src/components/ProjectCard.tsx +595 -0
- package/.next/standalone/src/components/QueryProvider.tsx +25 -0
- package/.next/standalone/src/components/SessionCard.test.tsx +566 -0
- package/.next/standalone/src/components/SessionCard.tsx +434 -0
- package/.next/standalone/src/components/SessionList.tsx +60 -0
- package/.next/standalone/src/components/host-config/HostManagerDialog.test.tsx +252 -0
- package/.next/standalone/src/components/host-config/HostManagerDialog.tsx +476 -0
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.test.tsx +72 -0
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.tsx +483 -0
- package/.next/standalone/src/components/opencode-config/AgentModelSelector.tsx +284 -0
- package/.next/standalone/src/components/opencode-config/AgentsConfigPanel.tsx +162 -0
- package/.next/standalone/src/components/opencode-config/ConfigButton.tsx +43 -0
- package/.next/standalone/src/components/opencode-config/ConfigPanel.tsx +91 -0
- package/.next/standalone/src/components/opencode-config/FullscreenConfigPanel.tsx +435 -0
- package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.test.tsx +91 -0
- package/.next/standalone/src/components/opencode-config/GeneralSettingsForm.tsx +288 -0
- package/.next/standalone/src/components/opencode-config/categories/CategoriesList.tsx +382 -0
- package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.test.tsx +111 -0
- package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.tsx +174 -0
- package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.tsx +453 -0
- package/.next/standalone/src/components/opencode-config/profiles/ProfileCard.tsx +140 -0
- package/.next/standalone/src/components/opencode-config/profiles/ProfileEditor.tsx +446 -0
- package/.next/standalone/src/components/opencode-config/profiles/ProfileList.tsx +446 -0
- package/.next/standalone/src/components/opencode-config/profiles/ProfileManager.test.tsx +225 -0
- package/.next/standalone/src/components/opencode-config/profiles/ProfileManager.tsx +405 -0
- package/.next/standalone/src/components/ui/Tabs.tsx +59 -0
- package/.next/standalone/src/hooks/useHostSources.test.ts +509 -0
- package/.next/standalone/src/hooks/useHostSources.ts +299 -0
- package/.next/standalone/src/hooks/useOpencodeSync.test.ts +387 -0
- package/.next/standalone/src/hooks/useOpencodeSync.ts +571 -0
- package/.next/standalone/src/index.ts +2 -0
- package/.next/standalone/src/lib/editorLauncher.server.ts +36 -0
- package/.next/standalone/src/lib/editorLauncher.test.ts +35 -0
- package/.next/standalone/src/lib/editorLauncher.ts +25 -0
- package/.next/standalone/src/lib/hostAccent.test.ts +58 -0
- package/.next/standalone/src/lib/hostAccent.ts +46 -0
- package/.next/standalone/src/lib/hostIdentity.test.ts +187 -0
- package/.next/standalone/src/lib/hostIdentity.ts +122 -0
- package/.next/standalone/src/lib/hostSourcesStorage.test.ts +141 -0
- package/.next/standalone/src/lib/hostSourcesStorage.ts +72 -0
- package/.next/standalone/src/lib/nodeProtocol.test.ts +159 -0
- package/.next/standalone/src/lib/nodeProtocol.ts +142 -0
- package/.next/standalone/src/lib/nodeRegistry.test.ts +173 -0
- package/.next/standalone/src/lib/nodeRegistry.ts +398 -0
- package/.next/standalone/src/lib/notificationSound.ts +292 -0
- package/.next/standalone/src/lib/opencodeConfig.test.ts +100 -0
- package/.next/standalone/src/lib/opencodeConfig.ts +76 -0
- package/.next/standalone/src/lib/opencodeDiscovery.ts +275 -0
- package/.next/standalone/src/lib/profiles/share.test.ts +91 -0
- package/.next/standalone/src/lib/profiles/share.ts +93 -0
- package/.next/standalone/src/lib/profiles/storage.test.ts +108 -0
- package/.next/standalone/src/lib/profiles/storage.ts +370 -0
- package/.next/standalone/src/lib/runtimeMode.test.ts +29 -0
- package/.next/standalone/src/lib/runtimeMode.ts +29 -0
- package/.next/standalone/src/lib/sessionActionErrors.ts +37 -0
- package/.next/standalone/src/lib/sessionArchiveOverrides.test.ts +43 -0
- package/.next/standalone/src/lib/sessionArchiveOverrides.ts +116 -0
- package/.next/standalone/src/lib/transform.test.ts +121 -0
- package/.next/standalone/src/lib/transform.ts +193 -0
- package/.next/standalone/src/test/setup.ts +8 -0
- package/.next/standalone/src/types/index.ts +152 -0
- package/.next/standalone/src/types/opencodeConfig.ts +149 -0
- package/.next/standalone/tsconfig.json +34 -0
- package/.next/standalone/tsconfig.lib.json +17 -0
- package/.next/standalone/vitest.config.ts +16 -0
- package/.next/static/chunks/7ac19aaef01f4a03.js +13 -0
- package/.next/static/chunks/f42202943f6742e5.css +3 -0
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +5 -1
- package/.next/types/validator.ts +36 -0
- package/package.json +1 -1
- package/.next/server/chunks/[root-of-the-server]__0b017945._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__0b017945._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__1e118bd3._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__1e118bd3._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__6979e732._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__6979e732._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__b698889b._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__ddc251b7._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__ddc251b7._.js.map +0 -1
- package/.next/server/chunks/ssr/[root-of-the-server]__b0788643._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__0b017945._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__1e118bd3._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6979e732._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__a7b4d79d._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__ddc251b7._.js +0 -3
- package/.next/standalone/.next/server/chunks/ssr/[root-of-the-server]__b0788643._.js +0 -3
- package/.next/standalone/.next/static/chunks/9f8c22002e7395e8.js +0 -13
- package/.next/standalone/.next/static/chunks/f1b55db60e7ed6f3.css +0 -3
- package/.next/static/chunks/9f8c22002e7395e8.js +0 -13
- package/.next/static/chunks/f1b55db60e7ed6f3.css +0 -3
- /package/.next/server/chunks/ssr/{[root-of-the-server]__efc52f08._.js.map → [root-of-the-server]__631e12d0._.js.map} +0 -0
- /package/.next/server/chunks/ssr/{[root-of-the-server]__b0788643._.js.map → [root-of-the-server]__a8cd3911._.js.map} +0 -0
- /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_ssgManifest.js +0 -0
- /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_buildManifest.js +0 -0
- /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{L_tmqf71LaeMzApO4SiU- → Fw2R3y-fHX4B2SWxNy_4X}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
|
|
5
|
+
import { useForm, Controller } from 'react-hook-form';
|
|
6
|
+
import { Settings as SettingsIcon, Save, Loader2, Check, AlertCircle } from 'lucide-react';
|
|
7
|
+
import type { OpenEditorTargetMode } from '@/types/opencodeConfig';
|
|
8
|
+
|
|
9
|
+
interface GeneralSettingsFormData {
|
|
10
|
+
stickyBusyDelaySeconds: number;
|
|
11
|
+
sessionsRefreshIntervalSeconds: number;
|
|
12
|
+
openEditorTargetMode: OpenEditorTargetMode;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface VibepulseConfig {
|
|
16
|
+
stickyBusyDelayMs?: number;
|
|
17
|
+
sessionsRefreshIntervalMs?: number;
|
|
18
|
+
openEditorTargetMode?: OpenEditorTargetMode;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface ConfigResponse {
|
|
22
|
+
vibepulse?: VibepulseConfig;
|
|
23
|
+
[key: string]: unknown;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function GeneralSettingsForm() {
|
|
27
|
+
const queryClient = useQueryClient();
|
|
28
|
+
const [toast, setToast] = React.useState<{
|
|
29
|
+
type: 'success' | 'error';
|
|
30
|
+
message: string;
|
|
31
|
+
} | null>(null);
|
|
32
|
+
|
|
33
|
+
const { data: config, isLoading } = useQuery<ConfigResponse>({
|
|
34
|
+
queryKey: ['opencode-config'],
|
|
35
|
+
queryFn: async () => {
|
|
36
|
+
const res = await fetch('/api/opencode-config');
|
|
37
|
+
if (!res.ok) throw new Error('Failed to fetch config');
|
|
38
|
+
return res.json();
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
control,
|
|
44
|
+
handleSubmit,
|
|
45
|
+
reset,
|
|
46
|
+
formState: { isDirty, isSubmitting }
|
|
47
|
+
} = useForm<GeneralSettingsFormData>({
|
|
48
|
+
defaultValues: {
|
|
49
|
+
stickyBusyDelaySeconds: 1,
|
|
50
|
+
sessionsRefreshIntervalSeconds: 5,
|
|
51
|
+
openEditorTargetMode: 'remote'
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
React.useEffect(() => {
|
|
56
|
+
if (config) {
|
|
57
|
+
reset({
|
|
58
|
+
stickyBusyDelaySeconds: typeof config.vibepulse?.stickyBusyDelayMs === 'number'
|
|
59
|
+
? Math.round(config.vibepulse.stickyBusyDelayMs / 1000)
|
|
60
|
+
: 1,
|
|
61
|
+
sessionsRefreshIntervalSeconds: typeof config.vibepulse?.sessionsRefreshIntervalMs === 'number'
|
|
62
|
+
? Math.round(config.vibepulse.sessionsRefreshIntervalMs / 1000)
|
|
63
|
+
: 5,
|
|
64
|
+
openEditorTargetMode: config.vibepulse?.openEditorTargetMode === 'hub' ? 'hub' : 'remote'
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}, [config, reset]);
|
|
68
|
+
|
|
69
|
+
React.useEffect(() => {
|
|
70
|
+
if (toast) {
|
|
71
|
+
const timer = setTimeout(() => setToast(null), 3000);
|
|
72
|
+
return () => clearTimeout(timer);
|
|
73
|
+
}
|
|
74
|
+
}, [toast]);
|
|
75
|
+
|
|
76
|
+
const mutation = useMutation({
|
|
77
|
+
mutationFn: async (data: GeneralSettingsFormData) => {
|
|
78
|
+
const res = await fetch('/api/opencode-config', {
|
|
79
|
+
method: 'POST',
|
|
80
|
+
headers: { 'Content-Type': 'application/json' },
|
|
81
|
+
body: JSON.stringify({
|
|
82
|
+
vibepulse: {
|
|
83
|
+
stickyBusyDelayMs: data.stickyBusyDelaySeconds * 1000,
|
|
84
|
+
sessionsRefreshIntervalMs: data.sessionsRefreshIntervalSeconds * 1000,
|
|
85
|
+
openEditorTargetMode: data.openEditorTargetMode
|
|
86
|
+
}
|
|
87
|
+
})
|
|
88
|
+
});
|
|
89
|
+
if (!res.ok) {
|
|
90
|
+
const error = await res.json();
|
|
91
|
+
throw new Error(error.error || 'Failed to save settings');
|
|
92
|
+
}
|
|
93
|
+
return res.json();
|
|
94
|
+
},
|
|
95
|
+
onSuccess: () => {
|
|
96
|
+
queryClient.invalidateQueries({ queryKey: ['opencode-config'] });
|
|
97
|
+
setToast({ type: 'success', message: 'Settings saved successfully' });
|
|
98
|
+
},
|
|
99
|
+
onError: (error: Error) => {
|
|
100
|
+
setToast({ type: 'error', message: error.message });
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const onSubmit = (data: GeneralSettingsFormData) => {
|
|
105
|
+
mutation.mutate(data);
|
|
106
|
+
};
|
|
107
|
+
|
|
108
|
+
if (isLoading) {
|
|
109
|
+
return (
|
|
110
|
+
<div className="flex h-40 items-center justify-center">
|
|
111
|
+
<Loader2 className="h-6 w-6 animate-spin text-zinc-400" />
|
|
112
|
+
</div>
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return (
|
|
117
|
+
<form onSubmit={handleSubmit(onSubmit)} className="space-y-6">
|
|
118
|
+
<div className="rounded-xl border border-zinc-200 bg-white p-6 shadow-sm dark:border-zinc-800 dark:bg-zinc-900">
|
|
119
|
+
<div className="mb-6 flex items-center gap-3 border-b border-zinc-100 pb-4 dark:border-zinc-800">
|
|
120
|
+
<div className="flex h-8 w-8 items-center justify-center rounded-lg bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">
|
|
121
|
+
<SettingsIcon className="h-4 w-4" />
|
|
122
|
+
</div>
|
|
123
|
+
<h3 className="text-lg font-medium text-zinc-900 dark:text-zinc-100">
|
|
124
|
+
Application Settings
|
|
125
|
+
</h3>
|
|
126
|
+
</div>
|
|
127
|
+
|
|
128
|
+
{toast && (
|
|
129
|
+
<div
|
|
130
|
+
role="alert"
|
|
131
|
+
className={`mb-6 flex items-center gap-2 rounded-lg px-4 py-3 text-sm ${
|
|
132
|
+
toast.type === 'success'
|
|
133
|
+
? 'bg-emerald-50 text-emerald-800 dark:bg-emerald-900/20 dark:text-emerald-300'
|
|
134
|
+
: 'bg-red-50 text-red-800 dark:bg-red-900/20 dark:text-red-300'
|
|
135
|
+
}`}
|
|
136
|
+
>
|
|
137
|
+
{toast.type === 'success' ? (
|
|
138
|
+
<Check className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
139
|
+
) : (
|
|
140
|
+
<AlertCircle className="h-4 w-4 shrink-0" aria-hidden="true" />
|
|
141
|
+
)}
|
|
142
|
+
<span>{toast.message}</span>
|
|
143
|
+
</div>
|
|
144
|
+
)}
|
|
145
|
+
|
|
146
|
+
<div className="space-y-8">
|
|
147
|
+
<div className="space-y-3">
|
|
148
|
+
<label htmlFor="open-editor-target-mode" className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
|
149
|
+
Remote Open Target
|
|
150
|
+
</label>
|
|
151
|
+
<Controller
|
|
152
|
+
name="openEditorTargetMode"
|
|
153
|
+
control={control}
|
|
154
|
+
render={({ field }) => (
|
|
155
|
+
<select
|
|
156
|
+
id="open-editor-target-mode"
|
|
157
|
+
value={field.value}
|
|
158
|
+
onChange={field.onChange}
|
|
159
|
+
className="w-full rounded-lg border border-zinc-200 bg-white px-3 py-2 text-sm text-zinc-900 dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-100"
|
|
160
|
+
aria-label="Remote open target"
|
|
161
|
+
>
|
|
162
|
+
<option value="remote">Remote node</option>
|
|
163
|
+
<option value="hub">Hub browser machine</option>
|
|
164
|
+
</select>
|
|
165
|
+
)}
|
|
166
|
+
/>
|
|
167
|
+
<p className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
168
|
+
Choose whether remote session open actions execute on the remote node itself or use the current hub/browser machine editor flow. Default is remote node.
|
|
169
|
+
</p>
|
|
170
|
+
</div>
|
|
171
|
+
|
|
172
|
+
<div className="space-y-3">
|
|
173
|
+
<div className="flex items-center justify-between">
|
|
174
|
+
<label htmlFor="sticky-busy-slider" className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
|
175
|
+
Sticky Busy Delay
|
|
176
|
+
</label>
|
|
177
|
+
<Controller
|
|
178
|
+
name="stickyBusyDelaySeconds"
|
|
179
|
+
control={control}
|
|
180
|
+
render={({ field }) => (
|
|
181
|
+
<span className="rounded-md bg-zinc-100 px-2 py-0.5 text-xs font-medium text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
|
182
|
+
{field.value}s
|
|
183
|
+
</span>
|
|
184
|
+
)}
|
|
185
|
+
/>
|
|
186
|
+
</div>
|
|
187
|
+
<Controller
|
|
188
|
+
name="stickyBusyDelaySeconds"
|
|
189
|
+
control={control}
|
|
190
|
+
render={({ field }) => (
|
|
191
|
+
<div className="flex items-center gap-3">
|
|
192
|
+
<input
|
|
193
|
+
id="sticky-busy-slider"
|
|
194
|
+
type="range"
|
|
195
|
+
min={0}
|
|
196
|
+
max={60}
|
|
197
|
+
step={1}
|
|
198
|
+
value={field.value}
|
|
199
|
+
onChange={(e) => field.onChange(parseInt(e.target.value, 10))}
|
|
200
|
+
className="flex-1 h-2 cursor-pointer appearance-none rounded-lg bg-zinc-200 accent-blue-600 dark:bg-zinc-700"
|
|
201
|
+
aria-label="Sticky busy delay slider"
|
|
202
|
+
/>
|
|
203
|
+
<input
|
|
204
|
+
type="number"
|
|
205
|
+
min={0}
|
|
206
|
+
max={300}
|
|
207
|
+
step={1}
|
|
208
|
+
value={field.value}
|
|
209
|
+
onChange={(e) => field.onChange(parseInt(e.target.value, 10) || 0)}
|
|
210
|
+
className="w-20 rounded-lg border border-zinc-200 bg-white px-2 py-1 text-center text-sm dark:border-zinc-800 dark:bg-zinc-950"
|
|
211
|
+
aria-label="Sticky busy delay value"
|
|
212
|
+
/>
|
|
213
|
+
</div>
|
|
214
|
+
)}
|
|
215
|
+
/>
|
|
216
|
+
<p className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
217
|
+
The duration to keep a session marked as "busy" after activity stops. This prevents the UI from flickering when agents pause briefly. Default is 1 second.
|
|
218
|
+
</p>
|
|
219
|
+
</div>
|
|
220
|
+
|
|
221
|
+
<div className="space-y-3">
|
|
222
|
+
<div className="flex items-center justify-between">
|
|
223
|
+
<label htmlFor="refresh-interval-slider" className="text-sm font-medium text-zinc-900 dark:text-zinc-100">
|
|
224
|
+
Sessions Refresh Interval
|
|
225
|
+
</label>
|
|
226
|
+
<Controller
|
|
227
|
+
name="sessionsRefreshIntervalSeconds"
|
|
228
|
+
control={control}
|
|
229
|
+
render={({ field }) => (
|
|
230
|
+
<span className="rounded-md bg-zinc-100 px-2 py-0.5 text-xs font-medium text-zinc-700 dark:bg-zinc-800 dark:text-zinc-300">
|
|
231
|
+
{field.value}s
|
|
232
|
+
</span>
|
|
233
|
+
)}
|
|
234
|
+
/>
|
|
235
|
+
</div>
|
|
236
|
+
<Controller
|
|
237
|
+
name="sessionsRefreshIntervalSeconds"
|
|
238
|
+
control={control}
|
|
239
|
+
render={({ field }) => (
|
|
240
|
+
<div className="flex items-center gap-3">
|
|
241
|
+
<input
|
|
242
|
+
id="refresh-interval-slider"
|
|
243
|
+
type="range"
|
|
244
|
+
min={1}
|
|
245
|
+
max={30}
|
|
246
|
+
step={1}
|
|
247
|
+
value={field.value}
|
|
248
|
+
onChange={(e) => field.onChange(parseInt(e.target.value, 10))}
|
|
249
|
+
className="flex-1 h-2 cursor-pointer appearance-none rounded-lg bg-zinc-200 accent-blue-600 dark:bg-zinc-700"
|
|
250
|
+
aria-label="Sessions refresh interval slider"
|
|
251
|
+
/>
|
|
252
|
+
<input
|
|
253
|
+
type="number"
|
|
254
|
+
min={1}
|
|
255
|
+
max={300}
|
|
256
|
+
step={1}
|
|
257
|
+
value={field.value}
|
|
258
|
+
onChange={(e) => field.onChange(parseInt(e.target.value, 10) || 1)}
|
|
259
|
+
className="w-20 rounded-lg border border-zinc-200 bg-white px-2 py-1 text-center text-sm dark:border-zinc-800 dark:bg-zinc-950"
|
|
260
|
+
aria-label="Sessions refresh interval value"
|
|
261
|
+
/>
|
|
262
|
+
</div>
|
|
263
|
+
)}
|
|
264
|
+
/>
|
|
265
|
+
<p className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
266
|
+
How often the board polls the server for session updates. Lower values provide more real-time feedback but increase server load. Default is 5 seconds.
|
|
267
|
+
</p>
|
|
268
|
+
</div>
|
|
269
|
+
</div>
|
|
270
|
+
|
|
271
|
+
<div className="mt-8 flex justify-end border-t border-zinc-100 pt-6 dark:border-zinc-800">
|
|
272
|
+
<button
|
|
273
|
+
type="submit"
|
|
274
|
+
disabled={!isDirty || isSubmitting || mutation.isPending}
|
|
275
|
+
className="flex items-center gap-2 rounded-lg bg-blue-600 px-4 py-2 text-sm font-medium text-white shadow-sm transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 dark:hover:bg-blue-500 dark:focus:ring-offset-zinc-900"
|
|
276
|
+
>
|
|
277
|
+
{(isSubmitting || mutation.isPending) ? (
|
|
278
|
+
<Loader2 className="h-4 w-4 animate-spin" />
|
|
279
|
+
) : (
|
|
280
|
+
<Save className="h-4 w-4" />
|
|
281
|
+
)}
|
|
282
|
+
Save Settings
|
|
283
|
+
</button>
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
</form>
|
|
287
|
+
);
|
|
288
|
+
}
|
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Pencil, Trash2, Layers, AlertTriangle, AlertCircle } from 'lucide-react';
|
|
5
|
+
import { useQuery } from '@tanstack/react-query';
|
|
6
|
+
import { CategoryConfig } from '../../../types/opencodeConfig';
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
interface ModelsResponse {
|
|
10
|
+
models: string[];
|
|
11
|
+
source: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isModelsResponse(value: unknown): value is ModelsResponse {
|
|
16
|
+
if (!value || typeof value !== 'object') return false;
|
|
17
|
+
const candidate = value as { models?: unknown };
|
|
18
|
+
return Array.isArray(candidate.models);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CategoriesListProps {
|
|
22
|
+
/** Record of category key to category configuration */
|
|
23
|
+
categories: Record<string, CategoryConfig>;
|
|
24
|
+
/** Callback when edit button is clicked */
|
|
25
|
+
onEdit: (categoryKey: string, config: CategoryConfig) => void;
|
|
26
|
+
/** Callback when delete button is clicked (only for custom categories) */
|
|
27
|
+
onDelete: (categoryKey: string) => void;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface CategoryDefinition {
|
|
31
|
+
key: string;
|
|
32
|
+
name: string;
|
|
33
|
+
description: string;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const CATEGORY_FALLBACK_CHAINS: Record<string, string[]> = {
|
|
37
|
+
'visual-engineering': ['google/gemini-3.1-pro', 'glm-5', 'anthropic/claude-opus-4-6'],
|
|
38
|
+
ultrabrain: ['openai/gpt-5.3-codex', 'google/gemini-3.1-pro', 'anthropic/claude-opus-4-6'],
|
|
39
|
+
deep: ['openai/gpt-5.3-codex', 'anthropic/claude-opus-4-6', 'google/gemini-3.1-pro'],
|
|
40
|
+
artistry: ['google/gemini-3.1-pro', 'anthropic/claude-opus-4-6', 'openai/gpt-5.4'],
|
|
41
|
+
quick: ['anthropic/claude-haiku-4-5', 'google/gemini-3-flash', 'opencode/gpt-5-nano'],
|
|
42
|
+
'unspecified-low': ['anthropic/claude-sonnet-4-6', 'openai/gpt-5.3-codex', 'google/gemini-3-flash'],
|
|
43
|
+
'unspecified-high': ['openai/gpt-5.4', 'anthropic/claude-opus-4-6', 'glm-5', 'kimi-for-coding/k2p5'],
|
|
44
|
+
writing: ['google/gemini-3-flash', 'anthropic/claude-sonnet-4-6'],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
const BUILT_IN_CATEGORIES: CategoryDefinition[] = [
|
|
48
|
+
{
|
|
49
|
+
key: 'visual-engineering',
|
|
50
|
+
name: 'Visual Engineering',
|
|
51
|
+
description: 'Visual and UI component engineering tasks',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
key: 'ultrabrain',
|
|
55
|
+
name: 'Ultrabrain',
|
|
56
|
+
description: 'Complex reasoning and deep analysis tasks',
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
key: 'deep',
|
|
60
|
+
name: 'Deep',
|
|
61
|
+
description: 'Deep research and comprehensive tasks',
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
key: 'artistry',
|
|
65
|
+
name: 'Artistry',
|
|
66
|
+
description: 'Creative and design-focused tasks',
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
key: 'quick',
|
|
70
|
+
name: 'Quick',
|
|
71
|
+
description: 'Fast, simple tasks requiring minimal processing',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
key: 'unspecified-low',
|
|
75
|
+
name: 'Unspecified Low',
|
|
76
|
+
description: 'Default low-complexity tasks',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'unspecified-high',
|
|
80
|
+
name: 'Unspecified High',
|
|
81
|
+
description: 'Default high-complexity tasks',
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
key: 'writing',
|
|
85
|
+
name: 'Writing',
|
|
86
|
+
description: 'Content creation and writing tasks',
|
|
87
|
+
},
|
|
88
|
+
];
|
|
89
|
+
|
|
90
|
+
/** Check if a category key is a built-in category */
|
|
91
|
+
function isBuiltInCategory(key: string): boolean {
|
|
92
|
+
return BUILT_IN_CATEGORIES.some((cat) => cat.key === key);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Get display info for a category */
|
|
96
|
+
function getCategoryInfo(key: string): CategoryDefinition {
|
|
97
|
+
const builtIn = BUILT_IN_CATEGORIES.find((cat) => cat.key === key);
|
|
98
|
+
if (builtIn) {
|
|
99
|
+
return builtIn;
|
|
100
|
+
}
|
|
101
|
+
// For custom categories, use the key as the name
|
|
102
|
+
return {
|
|
103
|
+
key,
|
|
104
|
+
name: key,
|
|
105
|
+
description: 'Custom category',
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Format variant display */
|
|
110
|
+
function formatVariant(variant?: string): string {
|
|
111
|
+
if (!variant) return '—';
|
|
112
|
+
return variant;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function formatProvider(model?: string): string {
|
|
116
|
+
if (!model) return 'Default provider';
|
|
117
|
+
|
|
118
|
+
const slashIndex = model.indexOf('/');
|
|
119
|
+
if (slashIndex === -1) return 'Unknown provider';
|
|
120
|
+
|
|
121
|
+
return model.substring(0, slashIndex);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/** Format model display */
|
|
125
|
+
function formatModel(model?: string): string {
|
|
126
|
+
if (!model) return 'Default model';
|
|
127
|
+
// Extract just the model name from a full path like "google/gemini-3.1-pro"
|
|
128
|
+
const parts = model.split('/');
|
|
129
|
+
return parts[parts.length - 1];
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
interface CategoryCardProps {
|
|
133
|
+
categoryKey: string;
|
|
134
|
+
config: CategoryConfig;
|
|
135
|
+
isBuiltIn: boolean;
|
|
136
|
+
availableModels: Set<string> | null;
|
|
137
|
+
onEdit: (key: string, config: CategoryConfig) => void;
|
|
138
|
+
onDelete: (key: string) => void;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function CategoryCard({
|
|
142
|
+
categoryKey,
|
|
143
|
+
config,
|
|
144
|
+
isBuiltIn,
|
|
145
|
+
availableModels,
|
|
146
|
+
onEdit,
|
|
147
|
+
onDelete,
|
|
148
|
+
}: CategoryCardProps) {
|
|
149
|
+
const info = getCategoryInfo(categoryKey);
|
|
150
|
+
const hasConfig = !!(config.model || config.variant);
|
|
151
|
+
const isModelInvalid = config.model && availableModels && availableModels.size > 0 && !availableModels.has(config.model);
|
|
152
|
+
|
|
153
|
+
// Dynamic border/bg classes based on status
|
|
154
|
+
let cardStateClasses = hasConfig
|
|
155
|
+
? 'bg-white border-zinc-200 dark:bg-zinc-900 dark:border-zinc-700 hover:border-zinc-300 dark:hover:border-zinc-600'
|
|
156
|
+
: 'bg-zinc-50/50 border-zinc-200/50 dark:bg-zinc-900/50 dark:border-zinc-800/50 hover:border-zinc-300 dark:hover:border-zinc-600';
|
|
157
|
+
|
|
158
|
+
if (isModelInvalid) {
|
|
159
|
+
cardStateClasses = 'bg-amber-50/50 border-amber-200 dark:bg-amber-900/10 dark:border-amber-800/50 hover:border-amber-300 dark:hover:border-amber-700';
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<div
|
|
164
|
+
className={`
|
|
165
|
+
group relative flex items-center justify-between p-4 rounded-lg border flex-col sm:flex-row items-start sm:items-center gap-4
|
|
166
|
+
transition-all duration-200
|
|
167
|
+
${cardStateClasses}
|
|
168
|
+
`}
|
|
169
|
+
>
|
|
170
|
+
<div className="flex-1 min-w-0">
|
|
171
|
+
<div className="flex items-center gap-2">
|
|
172
|
+
<h4 className="text-sm font-medium text-zinc-900 dark:text-zinc-100 truncate">
|
|
173
|
+
{info.name}
|
|
174
|
+
</h4>
|
|
175
|
+
{!isBuiltIn && (
|
|
176
|
+
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-blue-100 text-blue-700 dark:bg-blue-900/30 dark:text-blue-300">
|
|
177
|
+
Custom
|
|
178
|
+
</span>
|
|
179
|
+
)}
|
|
180
|
+
{isModelInvalid && (
|
|
181
|
+
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-amber-100 text-amber-700 dark:bg-amber-900/30 dark:text-amber-400">
|
|
182
|
+
<AlertTriangle className="h-3 w-3" />
|
|
183
|
+
Model Unavailable
|
|
184
|
+
</span>
|
|
185
|
+
)}
|
|
186
|
+
{!isModelInvalid && hasConfig && (
|
|
187
|
+
<span className="inline-flex items-center px-1.5 py-0.5 rounded text-[10px] font-medium bg-emerald-100 text-emerald-700 dark:bg-emerald-900/30 dark:text-emerald-300">
|
|
188
|
+
Configured
|
|
189
|
+
</span>
|
|
190
|
+
)}
|
|
191
|
+
{!hasConfig && (
|
|
192
|
+
<span className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium bg-zinc-100 text-zinc-500 dark:bg-zinc-800 dark:text-zinc-400">
|
|
193
|
+
<AlertCircle className="h-3 w-3" />
|
|
194
|
+
Built-in fallback
|
|
195
|
+
</span>
|
|
196
|
+
)}
|
|
197
|
+
</div>
|
|
198
|
+
<p className="mt-0.5 text-xs text-zinc-500 dark:text-zinc-400 truncate">
|
|
199
|
+
{info.description}
|
|
200
|
+
</p>
|
|
201
|
+
<div className="mt-2 flex items-center gap-3 text-xs">
|
|
202
|
+
<span className="text-zinc-600 dark:text-zinc-400">
|
|
203
|
+
<span className="text-zinc-400 dark:text-zinc-500">Provider:</span>{' '}
|
|
204
|
+
{formatProvider(config.model)}
|
|
205
|
+
</span>
|
|
206
|
+
<span className="text-zinc-600 dark:text-zinc-400">
|
|
207
|
+
<span className="text-zinc-400 dark:text-zinc-500">Model:</span>{' '}
|
|
208
|
+
{formatModel(config.model)}
|
|
209
|
+
</span>
|
|
210
|
+
{config.variant && (
|
|
211
|
+
<span className="text-zinc-600 dark:text-zinc-400">
|
|
212
|
+
<span className="text-zinc-400 dark:text-zinc-500">Variant:</span>{' '}
|
|
213
|
+
{formatVariant(config.variant)}
|
|
214
|
+
</span>
|
|
215
|
+
)}
|
|
216
|
+
</div>
|
|
217
|
+
</div>
|
|
218
|
+
|
|
219
|
+
<div className="flex items-center gap-1 ml-4 opacity-0 group-hover:opacity-100 transition-opacity">
|
|
220
|
+
<button
|
|
221
|
+
type="button"
|
|
222
|
+
onClick={() => onEdit(categoryKey, config)}
|
|
223
|
+
className="p-1.5 rounded-md text-zinc-500 hover:text-zinc-900 hover:bg-zinc-100 dark:text-zinc-400 dark:hover:text-zinc-100 dark:hover:bg-zinc-800 transition-colors"
|
|
224
|
+
aria-label={`Edit ${info.name} category`}
|
|
225
|
+
title={`Edit ${info.name}`}
|
|
226
|
+
>
|
|
227
|
+
<Pencil className="h-4 w-4" />
|
|
228
|
+
</button>
|
|
229
|
+
{!isBuiltIn && (
|
|
230
|
+
<button
|
|
231
|
+
type="button"
|
|
232
|
+
onClick={() => onDelete(categoryKey)}
|
|
233
|
+
className="p-1.5 rounded-md text-zinc-500 hover:text-red-600 hover:bg-red-50 dark:text-zinc-400 dark:hover:text-red-400 dark:hover:bg-red-900/20 transition-colors"
|
|
234
|
+
aria-label={`Delete ${info.name} category`}
|
|
235
|
+
title={`Delete ${info.name}`}
|
|
236
|
+
>
|
|
237
|
+
<Trash2 className="h-4 w-4" />
|
|
238
|
+
</button>
|
|
239
|
+
)}
|
|
240
|
+
</div>
|
|
241
|
+
</div>
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function CategoriesList({
|
|
246
|
+
categories,
|
|
247
|
+
onEdit,
|
|
248
|
+
onDelete,
|
|
249
|
+
}: CategoriesListProps) {
|
|
250
|
+
const { data: modelsData } = useQuery<ModelsResponse>({
|
|
251
|
+
queryKey: ['opencode-models'],
|
|
252
|
+
queryFn: async () => {
|
|
253
|
+
const res = await fetch('/api/opencode-models');
|
|
254
|
+
let parsed: unknown = null;
|
|
255
|
+
try {
|
|
256
|
+
parsed = await res.json();
|
|
257
|
+
} catch {
|
|
258
|
+
parsed = null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const errorMessage =
|
|
262
|
+
parsed &&
|
|
263
|
+
typeof parsed === 'object' &&
|
|
264
|
+
'error' in parsed &&
|
|
265
|
+
typeof parsed.error === 'string'
|
|
266
|
+
? parsed.error
|
|
267
|
+
: null;
|
|
268
|
+
|
|
269
|
+
if (!res.ok || errorMessage) {
|
|
270
|
+
throw new Error(errorMessage || `Failed to fetch models (${res.status})`);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
if (!isModelsResponse(parsed)) {
|
|
274
|
+
throw new Error('Invalid models response');
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const data = parsed;
|
|
278
|
+
return data;
|
|
279
|
+
},
|
|
280
|
+
retry: false,
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
const availableModels = React.useMemo(
|
|
284
|
+
() => (modelsData ? new Set(modelsData.models) : null),
|
|
285
|
+
[modelsData]
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
// Separate built-in and custom categories
|
|
289
|
+
const { builtIn, custom } = React.useMemo(() => {
|
|
290
|
+
const builtIn: { key: string; config: CategoryConfig }[] = [];
|
|
291
|
+
const custom: { key: string; config: CategoryConfig }[] = [];
|
|
292
|
+
|
|
293
|
+
// First, add all built-in categories (even if not in config)
|
|
294
|
+
BUILT_IN_CATEGORIES.forEach((cat) => {
|
|
295
|
+
builtIn.push({
|
|
296
|
+
key: cat.key,
|
|
297
|
+
config: categories[cat.key] || {},
|
|
298
|
+
});
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
// Then add custom categories
|
|
302
|
+
Object.entries(categories).forEach(([key, config]) => {
|
|
303
|
+
if (!isBuiltInCategory(key)) {
|
|
304
|
+
custom.push({ key, config });
|
|
305
|
+
}
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
return { builtIn, custom };
|
|
309
|
+
}, [categories]);
|
|
310
|
+
|
|
311
|
+
const hasCustomCategories = custom.length > 0;
|
|
312
|
+
|
|
313
|
+
return (
|
|
314
|
+
<div className="space-y-6">
|
|
315
|
+
{/* Built-in Categories */}
|
|
316
|
+
<section>
|
|
317
|
+
<div className="flex items-center gap-2 mb-3">
|
|
318
|
+
<Layers className="h-4 w-4 text-zinc-500 dark:text-zinc-400" />
|
|
319
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
|
320
|
+
Built-in Categories
|
|
321
|
+
</h3>
|
|
322
|
+
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
323
|
+
({builtIn.length})
|
|
324
|
+
</span>
|
|
325
|
+
</div>
|
|
326
|
+
<div className="space-y-2">
|
|
327
|
+
{builtIn.map(({ key, config }) => (
|
|
328
|
+
<CategoryCard
|
|
329
|
+
key={key}
|
|
330
|
+
categoryKey={key}
|
|
331
|
+
config={config}
|
|
332
|
+
isBuiltIn={true}
|
|
333
|
+
availableModels={availableModels}
|
|
334
|
+
onEdit={onEdit}
|
|
335
|
+
onDelete={onDelete}
|
|
336
|
+
/>
|
|
337
|
+
))}
|
|
338
|
+
</div>
|
|
339
|
+
</section>
|
|
340
|
+
|
|
341
|
+
{/* Custom Categories */}
|
|
342
|
+
{hasCustomCategories && (
|
|
343
|
+
<section>
|
|
344
|
+
<div className="flex items-center gap-2 mb-3">
|
|
345
|
+
<Layers className="h-4 w-4 text-blue-500" />
|
|
346
|
+
<h3 className="text-sm font-semibold text-zinc-900 dark:text-zinc-100">
|
|
347
|
+
Custom Categories
|
|
348
|
+
</h3>
|
|
349
|
+
<span className="text-xs text-zinc-500 dark:text-zinc-400">
|
|
350
|
+
({custom.length})
|
|
351
|
+
</span>
|
|
352
|
+
</div>
|
|
353
|
+
<div className="space-y-2">
|
|
354
|
+
{custom.map(({ key, config }) => (
|
|
355
|
+
<CategoryCard
|
|
356
|
+
key={key}
|
|
357
|
+
categoryKey={key}
|
|
358
|
+
config={config}
|
|
359
|
+
isBuiltIn={false}
|
|
360
|
+
availableModels={availableModels}
|
|
361
|
+
onEdit={onEdit}
|
|
362
|
+
onDelete={onDelete}
|
|
363
|
+
/>
|
|
364
|
+
))}
|
|
365
|
+
</div>
|
|
366
|
+
</section>
|
|
367
|
+
)}
|
|
368
|
+
|
|
369
|
+
{/* Empty state when no categories at all */}
|
|
370
|
+
{builtIn.length === 0 && !hasCustomCategories && (
|
|
371
|
+
<div className="text-center py-8">
|
|
372
|
+
<Layers className="h-8 w-8 mx-auto text-zinc-300 dark:text-zinc-600 mb-2" />
|
|
373
|
+
<p className="text-sm text-zinc-500 dark:text-zinc-400">
|
|
374
|
+
No categories configured
|
|
375
|
+
</p>
|
|
376
|
+
</div>
|
|
377
|
+
)}
|
|
378
|
+
</div>
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
export default CategoriesList;
|