@a5c-ai/babysitter-observer-dashboard 5.0.1-staging.7a8768ec
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 -0
- package/.next/app-path-routes-manifest.json +16 -0
- package/.next/build-manifest.json +23 -0
- package/.next/export-marker.json +6 -0
- package/.next/fallback-build-manifest.json +13 -0
- package/.next/images-manifest.json +68 -0
- package/.next/next-minimal-server.js.nft.json +1 -0
- package/.next/next-server.js.nft.json +1 -0
- package/.next/package.json +1 -0
- package/.next/prerender-manifest.json +114 -0
- package/.next/required-server-files.json +334 -0
- package/.next/routes-manifest.json +139 -0
- package/.next/server/app/_global-error/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_global-error/page/build-manifest.json +19 -0
- package/.next/server/app/_global-error/page/next-font-manifest.json +6 -0
- package/.next/server/app/_global-error/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_global-error/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_global-error/page.js +9 -0
- package/.next/server/app/_global-error/page.js.map +5 -0
- package/.next/server/app/_global-error/page.js.nft.json +1 -0
- package/.next/server/app/_global-error/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_global-error.html +1 -0
- package/.next/server/app/_global-error.meta +15 -0
- package/.next/server/app/_global-error.rsc +14 -0
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_full.segment.rsc +14 -0
- package/.next/server/app/_global-error.segments/_head.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_index.segment.rsc +5 -0
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -0
- package/.next/server/app/_not-found/page/app-paths-manifest.json +3 -0
- package/.next/server/app/_not-found/page/build-manifest.json +19 -0
- package/.next/server/app/_not-found/page/next-font-manifest.json +6 -0
- package/.next/server/app/_not-found/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/_not-found/page/server-reference-manifest.json +4 -0
- package/.next/server/app/_not-found/page.js +13 -0
- package/.next/server/app/_not-found/page.js.map +5 -0
- package/.next/server/app/_not-found/page.js.nft.json +1 -0
- package/.next/server/app/_not-found/page_client-reference-manifest.js +3 -0
- package/.next/server/app/_not-found.html +1 -0
- package/.next/server/app/_not-found.meta +16 -0
- package/.next/server/app/_not-found.rsc +19 -0
- package/.next/server/app/_not-found.segments/_full.segment.rsc +19 -0
- package/.next/server/app/_not-found.segments/_head.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_index.segment.rsc +8 -0
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +6 -0
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +5 -0
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/api/config/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/config/route/build-manifest.json +9 -0
- package/.next/server/app/api/config/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/config/route.js +8 -0
- package/.next/server/app/api/config/route.js.map +5 -0
- package/.next/server/app/api/config/route.js.nft.json +1 -0
- package/.next/server/app/api/config/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/digest/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/digest/route/build-manifest.json +9 -0
- package/.next/server/app/api/digest/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/digest/route.js +9 -0
- package/.next/server/app/api/digest/route.js.map +5 -0
- package/.next/server/app/api/digest/route.js.nft.json +1 -0
- package/.next/server/app/api/digest/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/events/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/events/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/events/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/events/route.js +8 -0
- package/.next/server/app/api/runs/[runId]/events/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/events/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/events/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/route.js +9 -0
- package/.next/server/app/api/runs/[runId]/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js +8 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.map +5 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/[runId]/tasks/[effectId]/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/runs/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/runs/route/build-manifest.json +9 -0
- package/.next/server/app/api/runs/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/runs/route.js +9 -0
- package/.next/server/app/api/runs/route.js.map +5 -0
- package/.next/server/app/api/runs/route.js.nft.json +1 -0
- package/.next/server/app/api/runs/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/stream/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/stream/route/build-manifest.json +9 -0
- package/.next/server/app/api/stream/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/stream/route.js +8 -0
- package/.next/server/app/api/stream/route.js.map +5 -0
- package/.next/server/app/api/stream/route.js.nft.json +1 -0
- package/.next/server/app/api/stream/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/test/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/test/route/build-manifest.json +9 -0
- package/.next/server/app/api/test/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/test/route.js +6 -0
- package/.next/server/app/api/test/route.js.map +5 -0
- package/.next/server/app/api/test/route.js.nft.json +1 -0
- package/.next/server/app/api/test/route_client-reference-manifest.js +3 -0
- package/.next/server/app/api/version/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/version/route/build-manifest.json +9 -0
- package/.next/server/app/api/version/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/version/route.js +7 -0
- package/.next/server/app/api/version/route.js.map +5 -0
- package/.next/server/app/api/version/route.js.nft.json +1 -0
- package/.next/server/app/api/version/route_client-reference-manifest.js +3 -0
- package/.next/server/app/icon.svg/route/app-paths-manifest.json +3 -0
- package/.next/server/app/icon.svg/route/build-manifest.json +9 -0
- package/.next/server/app/icon.svg/route.js +7 -0
- package/.next/server/app/icon.svg/route.js.map +5 -0
- package/.next/server/app/icon.svg/route.js.nft.json +1 -0
- package/.next/server/app/icon.svg.body +20 -0
- package/.next/server/app/icon.svg.meta +1 -0
- package/.next/server/app/index.html +1 -0
- package/.next/server/app/index.meta +14 -0
- package/.next/server/app/index.rsc +21 -0
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +9 -0
- package/.next/server/app/index.segments/_full.segment.rsc +21 -0
- package/.next/server/app/index.segments/_head.segment.rsc +6 -0
- package/.next/server/app/index.segments/_index.segment.rsc +8 -0
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -0
- package/.next/server/app/page/app-paths-manifest.json +3 -0
- package/.next/server/app/page/build-manifest.json +19 -0
- package/.next/server/app/page/next-font-manifest.json +6 -0
- package/.next/server/app/page/react-loadable-manifest.json +1 -0
- package/.next/server/app/page/server-reference-manifest.json +17 -0
- package/.next/server/app/page.js +15 -0
- package/.next/server/app/page.js.map +5 -0
- package/.next/server/app/page.js.nft.json +1 -0
- package/.next/server/app/page_client-reference-manifest.js +3 -0
- package/.next/server/app/runs/[runId]/page/app-paths-manifest.json +3 -0
- package/.next/server/app/runs/[runId]/page/build-manifest.json +19 -0
- package/.next/server/app/runs/[runId]/page/next-font-manifest.json +6 -0
- package/.next/server/app/runs/[runId]/page/react-loadable-manifest.json +22 -0
- package/.next/server/app/runs/[runId]/page/server-reference-manifest.json +17 -0
- package/.next/server/app/runs/[runId]/page.js +15 -0
- package/.next/server/app/runs/[runId]/page.js.map +5 -0
- package/.next/server/app/runs/[runId]/page.js.nft.json +1 -0
- package/.next/server/app/runs/[runId]/page_client-reference-manifest.js +3 -0
- package/.next/server/app-paths-manifest.json +16 -0
- package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js +3 -0
- package/.next/server/chunks/01oi_server_app_api_runs_[runId]_tasks_[effectId]_route_actions_0r72yai.js.map +1 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js +3 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_events_route_actions_0~msldk.js.map +1 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js +3 -0
- package/.next/server/chunks/0h.v__next-internal_server_app_api_runs_[runId]_route_actions_09iz0n6.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_config_route_actions_0~eypoa.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_digest_route_actions_04jj5zs.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_runs_route_actions_0~-t-o4.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_stream_route_actions_0fkmv2_.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_test_route_actions_00ugava.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_api_version_route_actions_0~v3ojm.js.map +1 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js +3 -0
- package/.next/server/chunks/0juq_observer-dashboard__next-internal_server_app_icon_svg_route_actions_0yypxkm.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0.6bt.6._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__08kwev1._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__08kwev1._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__096el.d._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__096el.d._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0_bmt4z._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0_ln2d2._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0al3v65._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0al3v65._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0gf516b._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0gf516b._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0kdfw4x._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0pxt00h._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0rubnza._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0rubnza._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0tn9iud._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__0ws5o6i._.js.map +1 -0
- package/.next/server/chunks/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/[turbopack]_runtime.js.map +11 -0
- package/.next/server/chunks/node_modules_next_04~_e52._.js +13 -0
- package/.next/server/chunks/node_modules_next_04~_e52._.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js +9 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_0nyyph-.js.map +1 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js +3 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_02.vvb.._.js.map +1 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js +3 -0
- package/.next/server/chunks/packages_observer-dashboard_src_lib_0rqgpk0._.js.map +1 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js +3 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__global-error_page_actions_0ekoxmy.js.map +1 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js +3 -0
- package/.next/server/chunks/ssr/0juq_observer-dashboard__next-internal_server_app__not-found_page_actions_09b3ti3.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js +4 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__006f7~t._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__02jgvbi._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__04j8t~1._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__09c~s.0._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cpy61n._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0cwa32j._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js +19 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0fk_g0j._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pddpic._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0pvtneq._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js +3 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__0xc-2vm._.js.map +1 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js +33 -0
- package/.next/server/chunks/ssr/[root-of-the-server]__10xgshr._.js.map +1 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js +903 -0
- package/.next/server/chunks/ssr/[turbopack]_runtime.js.map +11 -0
- package/.next/server/chunks/ssr/_00yo1im._.js +3 -0
- package/.next/server/chunks/ssr/_00yo1im._.js.map +1 -0
- package/.next/server/chunks/ssr/_03sbc.o._.js +3 -0
- package/.next/server/chunks/ssr/_03sbc.o._.js.map +1 -0
- package/.next/server/chunks/ssr/_04z5ea0._.js +3 -0
- package/.next/server/chunks/ssr/_04z5ea0._.js.map +1 -0
- package/.next/server/chunks/ssr/_0gmb3g_._.js +3 -0
- package/.next/server/chunks/ssr/_0gmb3g_._.js.map +1 -0
- package/.next/server/chunks/ssr/_0okz9j8._.js +6 -0
- package/.next/server/chunks/ssr/_0okz9j8._.js.map +1 -0
- package/.next/server/chunks/ssr/_0wrbwro._.js +6 -0
- package/.next/server/chunks/ssr/_0wrbwro._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js +33 -0
- package/.next/server/chunks/ssr/node_modules_09w7yel._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0avqw4q._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js +6 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0h9llsw._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_0i_._k3._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_0ee1czk._.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_global-error_0lgvd_..js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js +3 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_client_components_builtin_unauthorized_0cjv-23.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0amzg6z.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0dw1x0d.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_0rc3ul_.js.map +1 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js +4 -0
- package/.next/server/chunks/ssr/node_modules_next_dist_esm_build_templates_app-page_10iomok.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js +3 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_0~a2tmu._.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js +3 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_0f.ozc-._.js.map +1 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js +7 -0
- package/.next/server/chunks/ssr/packages_observer-dashboard_src_components_providers_tsx_03_vrrn._.js.map +1 -0
- package/.next/server/functions-config-manifest.json +4 -0
- package/.next/server/interception-route-rewrite-manifest.js +1 -0
- package/.next/server/middleware-build-manifest.js +23 -0
- package/.next/server/middleware-manifest.json +6 -0
- package/.next/server/next-font-manifest.js +1 -0
- package/.next/server/next-font-manifest.json +6 -0
- package/.next/server/pages/404.html +1 -0
- package/.next/server/pages/500.html +1 -0
- package/.next/server/pages-manifest.json +4 -0
- package/.next/server/prefetch-hints.json +1 -0
- package/.next/server/server-reference-manifest.js +1 -0
- package/.next/server/server-reference-manifest.json +24 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_buildManifest.js +11 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_clientMiddlewareManifest.js +1 -0
- package/.next/static/MvfPly4ZXHqaveDVOG1qq/_ssgManifest.js +1 -0
- package/.next/static/chunks/00c87uqn~kk2z.js +1 -0
- package/.next/static/chunks/01xlw8hd842-c.js +1 -0
- package/.next/static/chunks/02p9dpa.0oxiz.js +1 -0
- package/.next/static/chunks/034i63v_muq~d.js +1 -0
- package/.next/static/chunks/03mo-6~4pxdio.js +1 -0
- package/.next/static/chunks/03~yq9q893hmn.js +1 -0
- package/.next/static/chunks/04dm5qn77.fc2.js +1 -0
- package/.next/static/chunks/07uz2g0_38qia.js +4 -0
- package/.next/static/chunks/08nqnwvixoxnk.js +2 -0
- package/.next/static/chunks/0d3shmwh5_nmn.js +1 -0
- package/.next/static/chunks/0e_wyjw3nx.a..js +1 -0
- package/.next/static/chunks/0mstyq17cbf8-.js +1 -0
- package/.next/static/chunks/0ntuxw9.how-q.js +1 -0
- package/.next/static/chunks/0t3uzajv1qzmo.js +1 -0
- package/.next/static/chunks/0u~_nwr5-v.xp.js +1 -0
- package/.next/static/chunks/0wxcxh6eyzams.js +1 -0
- package/.next/static/chunks/0x6y7yt4kiddp.css +1 -0
- package/.next/static/chunks/0z6-vonyxr0dn.js +4 -0
- package/.next/static/chunks/0ze4gu236oq96.js +31 -0
- package/.next/static/chunks/0zwozael9msy1.js +1 -0
- package/.next/static/chunks/10qd__0r7a~.l.js +1 -0
- package/.next/static/chunks/11jh_u1ynmatg.js +1 -0
- package/.next/static/chunks/13xer3cb9shu-.js +5 -0
- package/.next/static/chunks/142zlnch_xmgd.js +1 -0
- package/.next/static/chunks/144kcri75qczu.js +1 -0
- package/.next/static/chunks/turbopack-0-ww6fe7b37qt.js +1 -0
- package/.next/static/media/icon.08ljfy7xai2x_.svg +20 -0
- package/LICENSE +21 -0
- package/README.md +490 -0
- package/next.config.mjs +25 -0
- package/package.json +104 -0
- package/postcss.config.mjs +8 -0
- package/src/app/actions/__tests__/approve-breakpoint.test.ts +246 -0
- package/src/app/actions/approve-breakpoint.ts +145 -0
- package/src/app/api/config/route.ts +137 -0
- package/src/app/api/digest/route.ts +45 -0
- package/src/app/api/runs/[runId]/events/route.ts +56 -0
- package/src/app/api/runs/[runId]/route.ts +84 -0
- package/src/app/api/runs/[runId]/tasks/[effectId]/route.ts +44 -0
- package/src/app/api/runs/route.ts +48 -0
- package/src/app/api/stream/route.ts +136 -0
- package/src/app/api/test/route.ts +1 -0
- package/src/app/api/version/route.ts +57 -0
- package/src/app/globals.css +555 -0
- package/src/app/icon.svg +20 -0
- package/src/app/layout.tsx +39 -0
- package/src/app/not-found.tsx +16 -0
- package/src/app/page.tsx +120 -0
- package/src/app/runs/[runId]/page.tsx +279 -0
- package/src/cli.ts +271 -0
- package/src/components/breakpoint/__tests__/breakpoint-approval.test.tsx +212 -0
- package/src/components/breakpoint/__tests__/breakpoint-panel.test.tsx +130 -0
- package/src/components/breakpoint/__tests__/file-preview.test.tsx +313 -0
- package/src/components/breakpoint/breakpoint-approval.tsx +138 -0
- package/src/components/breakpoint/breakpoint-panel.tsx +95 -0
- package/src/components/breakpoint/file-preview.tsx +215 -0
- package/src/components/dashboard/.gitkeep +0 -0
- package/src/components/dashboard/__tests__/breakpoint-banner.test.tsx +177 -0
- package/src/components/dashboard/__tests__/catch-up-banner.test.tsx +141 -0
- package/src/components/dashboard/__tests__/executive-summary-banner.test.tsx +164 -0
- package/src/components/dashboard/__tests__/kpi-grid.test.tsx +101 -0
- package/src/components/dashboard/__tests__/pagination-controls.test.tsx +125 -0
- package/src/components/dashboard/__tests__/project-accordion.test.tsx +97 -0
- package/src/components/dashboard/__tests__/project-list-view.test.tsx +174 -0
- package/src/components/dashboard/__tests__/project-search-input.test.tsx +110 -0
- package/src/components/dashboard/__tests__/project-section-header.test.tsx +91 -0
- package/src/components/dashboard/__tests__/project-section.test.tsx +151 -0
- package/src/components/dashboard/__tests__/run-card.test.tsx +164 -0
- package/src/components/dashboard/__tests__/run-filter-bar.test.tsx +109 -0
- package/src/components/dashboard/__tests__/run-list.test.tsx +123 -0
- package/src/components/dashboard/__tests__/search-filter.test.tsx +150 -0
- package/src/components/dashboard/__tests__/virtualized-run-list.test.tsx +179 -0
- package/src/components/dashboard/breakpoint-banner.tsx +301 -0
- package/src/components/dashboard/catch-up-banner.tsx +88 -0
- package/src/components/dashboard/executive-summary-banner.tsx +174 -0
- package/src/components/dashboard/global-search.tsx +323 -0
- package/src/components/dashboard/kpi-grid.tsx +140 -0
- package/src/components/dashboard/pagination-controls.tsx +100 -0
- package/src/components/dashboard/project-accordion.tsx +72 -0
- package/src/components/dashboard/project-health-card.tsx +536 -0
- package/src/components/dashboard/project-list-view.tsx +246 -0
- package/src/components/dashboard/project-search-input.tsx +41 -0
- package/src/components/dashboard/project-section-header.tsx +73 -0
- package/src/components/dashboard/project-section.tsx +89 -0
- package/src/components/dashboard/run-card.tsx +218 -0
- package/src/components/dashboard/run-filter-bar.tsx +100 -0
- package/src/components/dashboard/run-list.tsx +77 -0
- package/src/components/dashboard/search-filter.tsx +69 -0
- package/src/components/dashboard/virtualized-run-list.tsx +130 -0
- package/src/components/details/.gitkeep +0 -0
- package/src/components/details/__tests__/agent-panel.test.tsx +236 -0
- package/src/components/details/__tests__/json-tree.test.tsx +347 -0
- package/src/components/details/__tests__/log-viewer.test.tsx +168 -0
- package/src/components/details/__tests__/task-detail.test.tsx +212 -0
- package/src/components/details/__tests__/timing-panel.test.tsx +271 -0
- package/src/components/details/agent-panel.tsx +234 -0
- package/src/components/details/json-tree/categorize.ts +131 -0
- package/src/components/details/json-tree/index.tsx +120 -0
- package/src/components/details/json-tree/json-node.tsx +223 -0
- package/src/components/details/json-tree/smart-summary.tsx +596 -0
- package/src/components/details/json-tree/tree-controls.tsx +47 -0
- package/src/components/details/json-tree.tsx +9 -0
- package/src/components/details/log-viewer.tsx +140 -0
- package/src/components/details/task-detail.tsx +114 -0
- package/src/components/details/timing-panel.tsx +247 -0
- package/src/components/events/.gitkeep +0 -0
- package/src/components/events/__tests__/event-item.test.tsx +211 -0
- package/src/components/events/__tests__/event-stream.test.tsx +225 -0
- package/src/components/events/event-item.tsx +121 -0
- package/src/components/events/event-stream.tsx +260 -0
- package/src/components/notifications/.gitkeep +0 -0
- package/src/components/notifications/__tests__/notification-panel.test.tsx +287 -0
- package/src/components/notifications/__tests__/notification-provider.test.tsx +585 -0
- package/src/components/notifications/__tests__/toast-stack.test.tsx +217 -0
- package/src/components/notifications/notification-panel.tsx +124 -0
- package/src/components/notifications/notification-provider.tsx +175 -0
- package/src/components/notifications/toast-stack.tsx +75 -0
- package/src/components/pipeline/.gitkeep +0 -0
- package/src/components/pipeline/__tests__/parallel-group.test.tsx +88 -0
- package/src/components/pipeline/__tests__/pipeline-view.test.tsx +345 -0
- package/src/components/pipeline/__tests__/step-card.test.tsx +330 -0
- package/src/components/pipeline/parallel-group.tsx +39 -0
- package/src/components/pipeline/pipeline-view.tsx +197 -0
- package/src/components/pipeline/step-card.tsx +166 -0
- package/src/components/providers/event-stream-provider.tsx +29 -0
- package/src/components/providers.tsx +24 -0
- package/src/components/shared/.gitkeep +0 -0
- package/src/components/shared/__tests__/empty-state.test.tsx +49 -0
- package/src/components/shared/__tests__/friendly-id.test.tsx +47 -0
- package/src/components/shared/__tests__/kbd.test.tsx +45 -0
- package/src/components/shared/__tests__/kind-badge.test.tsx +71 -0
- package/src/components/shared/__tests__/metrics-row.test.tsx +74 -0
- package/src/components/shared/__tests__/outcome-banner.test.tsx +71 -0
- package/src/components/shared/__tests__/progress-bar.test.tsx +89 -0
- package/src/components/shared/__tests__/session-pill.test.tsx +62 -0
- package/src/components/shared/__tests__/settings-modal.test.tsx +201 -0
- package/src/components/shared/__tests__/shortcuts-help.test.tsx +103 -0
- package/src/components/shared/__tests__/status-badge.test.tsx +98 -0
- package/src/components/shared/__tests__/theme-provider.test.tsx +100 -0
- package/src/components/shared/__tests__/truncated-id.test.tsx +53 -0
- package/src/components/shared/app-footer.tsx +80 -0
- package/src/components/shared/app-header.tsx +160 -0
- package/src/components/shared/empty-state.tsx +18 -0
- package/src/components/shared/error-boundary.tsx +81 -0
- package/src/components/shared/friendly-id.tsx +48 -0
- package/src/components/shared/kbd.tsx +15 -0
- package/src/components/shared/kind-badge.tsx +51 -0
- package/src/components/shared/metrics-row.tsx +106 -0
- package/src/components/shared/outcome-banner.tsx +56 -0
- package/src/components/shared/progress-bar.tsx +42 -0
- package/src/components/shared/session-pill.tsx +69 -0
- package/src/components/shared/settings-modal.tsx +509 -0
- package/src/components/shared/shortcuts-help.tsx +113 -0
- package/src/components/shared/status-badge.tsx +110 -0
- package/src/components/shared/theme-provider.tsx +46 -0
- package/src/components/shared/truncated-id.tsx +51 -0
- package/src/components/ui/.gitkeep +0 -0
- package/src/components/ui/__tests__/accordion.test.tsx +96 -0
- package/src/components/ui/__tests__/badge.test.tsx +69 -0
- package/src/components/ui/__tests__/button.test.tsx +113 -0
- package/src/components/ui/__tests__/tabs.test.tsx +75 -0
- package/src/components/ui/__tests__/tooltip.test.tsx +90 -0
- package/src/components/ui/accordion.tsx +61 -0
- package/src/components/ui/badge.tsx +25 -0
- package/src/components/ui/button.tsx +40 -0
- package/src/components/ui/card.tsx +21 -0
- package/src/components/ui/scroll-area.tsx +35 -0
- package/src/components/ui/separator.tsx +24 -0
- package/src/components/ui/tabs.tsx +64 -0
- package/src/components/ui/tooltip.tsx +37 -0
- package/src/hooks/.gitkeep +0 -0
- package/src/hooks/__tests__/use-animated-number.test.ts +184 -0
- package/src/hooks/__tests__/use-batched-updates.test.ts +315 -0
- package/src/hooks/__tests__/use-event-stream.test.ts +243 -0
- package/src/hooks/__tests__/use-keyboard.test.ts +217 -0
- package/src/hooks/__tests__/use-notifications.test.ts +230 -0
- package/src/hooks/__tests__/use-polling.test.ts +274 -0
- package/src/hooks/__tests__/use-project-runs.test.ts +163 -0
- package/src/hooks/__tests__/use-projects.test.ts +248 -0
- package/src/hooks/__tests__/use-run-dashboard.test.ts +168 -0
- package/src/hooks/__tests__/use-run-detail.test.ts +273 -0
- package/src/hooks/__tests__/use-smart-polling.test.ts +305 -0
- package/src/hooks/use-animated-number.ts +87 -0
- package/src/hooks/use-batched-updates.ts +150 -0
- package/src/hooks/use-event-stream.ts +150 -0
- package/src/hooks/use-keyboard.ts +45 -0
- package/src/hooks/use-notifications.ts +82 -0
- package/src/hooks/use-persisted-state.ts +60 -0
- package/src/hooks/use-polling.ts +60 -0
- package/src/hooks/use-project-runs.ts +51 -0
- package/src/hooks/use-projects.ts +26 -0
- package/src/hooks/use-run-dashboard.ts +207 -0
- package/src/hooks/use-run-detail.ts +77 -0
- package/src/hooks/use-smart-polling.ts +144 -0
- package/src/lib/.gitkeep +0 -0
- package/src/lib/__tests__/cn.test.ts +69 -0
- package/src/lib/__tests__/config-loader.test.ts +210 -0
- package/src/lib/__tests__/config.test.ts +561 -0
- package/src/lib/__tests__/error-handler.test.ts +143 -0
- package/src/lib/__tests__/fetcher.test.ts +517 -0
- package/src/lib/__tests__/global-registry.test.ts +214 -0
- package/src/lib/__tests__/parser.test.ts +1532 -0
- package/src/lib/__tests__/path-resolver.test.ts +112 -0
- package/src/lib/__tests__/run-cache.test.ts +591 -0
- package/src/lib/__tests__/server-init.test.ts +512 -0
- package/src/lib/__tests__/source-discovery.test.ts +246 -0
- package/src/lib/__tests__/utils.test.ts +160 -0
- package/src/lib/__tests__/watcher.test.ts +227 -0
- package/src/lib/cn.ts +6 -0
- package/src/lib/config-loader.ts +195 -0
- package/src/lib/config.ts +20 -0
- package/src/lib/error-handler.ts +76 -0
- package/src/lib/fetcher.ts +394 -0
- package/src/lib/global-registry.ts +117 -0
- package/src/lib/parser.ts +794 -0
- package/src/lib/path-resolver.ts +16 -0
- package/src/lib/run-cache.ts +404 -0
- package/src/lib/server-init.ts +226 -0
- package/src/lib/services/__tests__/run-query-service.test.ts +819 -0
- package/src/lib/services/run-query-service.ts +286 -0
- package/src/lib/source-discovery.ts +216 -0
- package/src/lib/utils.ts +103 -0
- package/src/lib/watcher.ts +265 -0
- package/src/test/fixtures.ts +269 -0
- package/src/test/mocks/handlers.ts +110 -0
- package/src/test/mocks/server.ts +17 -0
- package/src/test/setup.ts +200 -0
- package/src/test/test-utils.tsx +36 -0
- package/src/types/.gitkeep +0 -0
- package/src/types/breakpoint.ts +17 -0
- package/src/types/index.ts +214 -0
- package/tsconfig.json +50 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { memo } from "react";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
import { GitBranch } from "lucide-react";
|
|
5
|
+
import type { ReactNode } from "react";
|
|
6
|
+
|
|
7
|
+
interface ParallelGroupProps {
|
|
8
|
+
children: ReactNode;
|
|
9
|
+
count: number;
|
|
10
|
+
className?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Visual wrapper that groups tasks detected as running in parallel.
|
|
15
|
+
* Shows a dashed border container with a "parallel" label and renders
|
|
16
|
+
* the grouped StepCards inside.
|
|
17
|
+
*/
|
|
18
|
+
export const ParallelGroup = memo(function ParallelGroup({ children, count, className }: ParallelGroupProps) {
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
className={cn(
|
|
22
|
+
"relative rounded-lg border border-dashed border-secondary/20 bg-secondary/[0.03] p-3 pt-7",
|
|
23
|
+
className
|
|
24
|
+
)}
|
|
25
|
+
>
|
|
26
|
+
{/* Label */}
|
|
27
|
+
<div className="absolute top-1.5 left-2.5 flex items-center gap-1 text-xs leading-tight font-medium text-info uppercase tracking-wider select-none">
|
|
28
|
+
<GitBranch className="h-3 w-3 text-info" />
|
|
29
|
+
<span>parallel</span>
|
|
30
|
+
<span className="text-foreground-muted">· {count} tasks</span>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
{/* Grouped task cards */}
|
|
34
|
+
<div className="flex flex-col gap-2">
|
|
35
|
+
{children}
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
);
|
|
39
|
+
});
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useMemo, useState, memo } from "react";
|
|
3
|
+
import { StepCard } from "./step-card";
|
|
4
|
+
import { ParallelGroup } from "./parallel-group";
|
|
5
|
+
import { ProgressBar } from "@/components/shared/progress-bar";
|
|
6
|
+
import { StatusBadge } from "@/components/shared/status-badge";
|
|
7
|
+
import { SessionPill } from "@/components/shared/session-pill";
|
|
8
|
+
import { TruncatedId } from "@/components/shared/truncated-id";
|
|
9
|
+
import { formatDuration, friendlyProcessName } from "@/lib/utils";
|
|
10
|
+
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
11
|
+
import type { Run, TaskEffect } from "@/types";
|
|
12
|
+
import { ChevronRight, Clock, Layers, ChevronDown } from "lucide-react";
|
|
13
|
+
import Link from "next/link";
|
|
14
|
+
|
|
15
|
+
/** Threshold in ms -- tasks requested within this window are considered parallel */
|
|
16
|
+
const PARALLEL_THRESHOLD_MS = 100;
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* A pipeline entry is either a single task or a group of tasks
|
|
20
|
+
* that were requested in parallel.
|
|
21
|
+
*/
|
|
22
|
+
type PipelineEntry =
|
|
23
|
+
| { type: "single"; task: TaskEffect }
|
|
24
|
+
| { type: "parallel"; tasks: TaskEffect[] };
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Detect parallel task groups.
|
|
28
|
+
*
|
|
29
|
+
* Two tasks are considered parallel when they share the same stepId prefix
|
|
30
|
+
* (the portion before the first `.`) OR their requestedAt timestamps are
|
|
31
|
+
* within PARALLEL_THRESHOLD_MS of each other.
|
|
32
|
+
*
|
|
33
|
+
* The algorithm walks the task list (sorted by requestedAt) and greedily
|
|
34
|
+
* merges adjacent tasks that satisfy either condition into a group.
|
|
35
|
+
*/
|
|
36
|
+
function groupParallelTasks(tasks: TaskEffect[]): PipelineEntry[] {
|
|
37
|
+
if (tasks.length === 0) return [];
|
|
38
|
+
|
|
39
|
+
const sorted = [...tasks].sort(
|
|
40
|
+
(a, b) => new Date(a.requestedAt).getTime() - new Date(b.requestedAt).getTime()
|
|
41
|
+
);
|
|
42
|
+
|
|
43
|
+
const entries: PipelineEntry[] = [];
|
|
44
|
+
let currentGroup: TaskEffect[] = [sorted[0]];
|
|
45
|
+
|
|
46
|
+
for (let i = 1; i < sorted.length; i++) {
|
|
47
|
+
const prev = currentGroup[currentGroup.length - 1];
|
|
48
|
+
const curr = sorted[i];
|
|
49
|
+
|
|
50
|
+
const prevTime = new Date(prev.requestedAt).getTime();
|
|
51
|
+
const currTime = new Date(curr.requestedAt).getTime();
|
|
52
|
+
const timeDelta = Math.abs(currTime - prevTime);
|
|
53
|
+
|
|
54
|
+
const prevStepPrefix = prev.stepId.split(".")[0];
|
|
55
|
+
const currStepPrefix = curr.stepId.split(".")[0];
|
|
56
|
+
const sameStepPrefix = prevStepPrefix === currStepPrefix;
|
|
57
|
+
|
|
58
|
+
if (sameStepPrefix || timeDelta <= PARALLEL_THRESHOLD_MS) {
|
|
59
|
+
currentGroup.push(curr);
|
|
60
|
+
} else {
|
|
61
|
+
// Flush the accumulated group
|
|
62
|
+
if (currentGroup.length === 1) {
|
|
63
|
+
entries.push({ type: "single", task: currentGroup[0] });
|
|
64
|
+
} else {
|
|
65
|
+
entries.push({ type: "parallel", tasks: currentGroup });
|
|
66
|
+
}
|
|
67
|
+
currentGroup = [curr];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Flush remaining group
|
|
72
|
+
if (currentGroup.length === 1) {
|
|
73
|
+
entries.push({ type: "single", task: currentGroup[0] });
|
|
74
|
+
} else {
|
|
75
|
+
entries.push({ type: "parallel", tasks: currentGroup });
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return entries;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
interface PipelineViewProps {
|
|
82
|
+
run: Run;
|
|
83
|
+
selectedEffectId: string | null;
|
|
84
|
+
onSelectEffect: (effectId: string) => void;
|
|
85
|
+
runStatus?: string;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const INITIAL_TASK_LIMIT = 20;
|
|
89
|
+
|
|
90
|
+
export const PipelineView = memo(function PipelineView({ run, selectedEffectId, onSelectEffect, runStatus }: PipelineViewProps) {
|
|
91
|
+
const effectiveStatus = runStatus ?? run.status;
|
|
92
|
+
const isReviewMode = effectiveStatus === "completed" || effectiveStatus === "failed";
|
|
93
|
+
const isRunning = effectiveStatus === "requested" || effectiveStatus === "waiting";
|
|
94
|
+
const progress = run.totalTasks > 0 ? Math.round((run.completedTasks / run.totalTasks) * 100) : 0;
|
|
95
|
+
const [showAllTasks, setShowAllTasks] = useState(false);
|
|
96
|
+
|
|
97
|
+
const pipelineEntries = useMemo(() => groupParallelTasks(run.tasks), [run.tasks]);
|
|
98
|
+
const hasMore = pipelineEntries.length > INITIAL_TASK_LIMIT;
|
|
99
|
+
const visibleEntries = showAllTasks || !hasMore ? pipelineEntries : pipelineEntries.slice(0, INITIAL_TASK_LIMIT);
|
|
100
|
+
|
|
101
|
+
return (
|
|
102
|
+
<div data-testid="pipeline-view" className="flex flex-col h-full">
|
|
103
|
+
{/* Header with breadcrumb — sticky so it's always visible */}
|
|
104
|
+
<div className="shrink-0 sticky top-0 z-20 border-b border-border p-4 pb-3 bg-card/95 backdrop-blur-sm">
|
|
105
|
+
{/* Breadcrumb navigation — prominent for easy back-navigation */}
|
|
106
|
+
<nav data-testid="pipeline-breadcrumb" className="flex items-center gap-1.5 mb-2 text-sm">
|
|
107
|
+
<Link href="/" className="text-foreground-muted hover:text-primary transition-colors font-semibold">
|
|
108
|
+
Projects
|
|
109
|
+
</Link>
|
|
110
|
+
<ChevronRight className="h-3.5 w-3.5 text-foreground-muted/50" />
|
|
111
|
+
<span className="text-foreground-secondary font-semibold">
|
|
112
|
+
{run.projectName || friendlyProcessName(run.processId)}
|
|
113
|
+
</span>
|
|
114
|
+
<ChevronRight className="h-3.5 w-3.5 text-foreground-muted/50" />
|
|
115
|
+
<TruncatedId id={run.runId} chars={4} className="text-foreground font-medium" />
|
|
116
|
+
<StatusBadge status={run.status} className="ml-1" />
|
|
117
|
+
</nav>
|
|
118
|
+
<div className="flex items-center gap-4 text-xs text-foreground-muted mb-3">
|
|
119
|
+
<SessionPill sessionId={run.sessionId} active={run.status === "waiting"} />
|
|
120
|
+
<span className="inline-flex items-center gap-1">
|
|
121
|
+
<Layers className="h-3 w-3" />
|
|
122
|
+
{run.completedTasks}/{run.totalTasks} tasks
|
|
123
|
+
</span>
|
|
124
|
+
<span className="inline-flex items-center gap-1">
|
|
125
|
+
<Clock className="h-3 w-3" />
|
|
126
|
+
{formatDuration(run.duration)}
|
|
127
|
+
</span>
|
|
128
|
+
</div>
|
|
129
|
+
<div className="flex items-center gap-3">
|
|
130
|
+
<div className="flex-1">
|
|
131
|
+
<ProgressBar value={progress} glow={isRunning} />
|
|
132
|
+
</div>
|
|
133
|
+
<span className="text-xs leading-tight text-foreground-muted tabular-nums shrink-0">{progress}%</span>
|
|
134
|
+
</div>
|
|
135
|
+
</div>
|
|
136
|
+
|
|
137
|
+
{/* Step list */}
|
|
138
|
+
<ScrollArea className="flex-1 relative">
|
|
139
|
+
{/* Subtle top gradient for depth — brand magenta/cyan */}
|
|
140
|
+
<div className="sticky top-0 z-10 h-3 bg-gradient-to-b from-primary/[0.06] via-secondary/[0.03] to-transparent pointer-events-none" />
|
|
141
|
+
<div className="flex flex-col gap-2 px-4 pb-4">
|
|
142
|
+
{(() => {
|
|
143
|
+
let stepCounter = 0;
|
|
144
|
+
return visibleEntries.map((entry) => {
|
|
145
|
+
if (entry.type === "single") {
|
|
146
|
+
stepCounter++;
|
|
147
|
+
return (
|
|
148
|
+
<StepCard
|
|
149
|
+
key={entry.task.effectId}
|
|
150
|
+
task={entry.task}
|
|
151
|
+
runId={run.runId}
|
|
152
|
+
onSelect={onSelectEffect}
|
|
153
|
+
isSelected={selectedEffectId === entry.task.effectId}
|
|
154
|
+
defaultExpanded={isReviewMode}
|
|
155
|
+
stepNumber={stepCounter}
|
|
156
|
+
/>
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Parallel group — all share the same step number
|
|
161
|
+
stepCounter++;
|
|
162
|
+
const groupKey = entry.tasks.map((t) => t.effectId).join("|");
|
|
163
|
+
return (
|
|
164
|
+
<ParallelGroup key={groupKey} count={entry.tasks.length}>
|
|
165
|
+
{entry.tasks.map((task) => (
|
|
166
|
+
<StepCard
|
|
167
|
+
key={task.effectId}
|
|
168
|
+
task={task}
|
|
169
|
+
runId={run.runId}
|
|
170
|
+
onSelect={onSelectEffect}
|
|
171
|
+
isSelected={selectedEffectId === task.effectId}
|
|
172
|
+
defaultExpanded={isReviewMode}
|
|
173
|
+
stepNumber={stepCounter}
|
|
174
|
+
/>
|
|
175
|
+
))}
|
|
176
|
+
</ParallelGroup>
|
|
177
|
+
);
|
|
178
|
+
});
|
|
179
|
+
})()}
|
|
180
|
+
{hasMore && !showAllTasks && (
|
|
181
|
+
<button
|
|
182
|
+
data-testid="show-all-tasks-btn"
|
|
183
|
+
onClick={() => setShowAllTasks(true)}
|
|
184
|
+
className="w-full flex items-center justify-center gap-1.5 rounded-md border border-border bg-background-secondary/50 py-2 text-xs text-foreground-muted hover:text-foreground hover:bg-background-secondary transition-colors"
|
|
185
|
+
>
|
|
186
|
+
<ChevronDown className="h-3.5 w-3.5" />
|
|
187
|
+
Show all {pipelineEntries.length} tasks
|
|
188
|
+
</button>
|
|
189
|
+
)}
|
|
190
|
+
{run.tasks.length === 0 && (
|
|
191
|
+
<div className="text-sm text-foreground-muted text-center py-8">No tasks yet</div>
|
|
192
|
+
)}
|
|
193
|
+
</div>
|
|
194
|
+
</ScrollArea>
|
|
195
|
+
</div>
|
|
196
|
+
);
|
|
197
|
+
});
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { useState, useEffect, memo } from "react";
|
|
3
|
+
import { cn } from "@/lib/cn";
|
|
4
|
+
import { StatusBadge } from "@/components/shared/status-badge";
|
|
5
|
+
import { KindBadge } from "@/components/shared/kind-badge";
|
|
6
|
+
import { formatDuration } from "@/lib/utils";
|
|
7
|
+
import { TruncatedId } from "@/components/shared/truncated-id";
|
|
8
|
+
import type { TaskEffect } from "@/types";
|
|
9
|
+
import { ChevronDown, Clock, Hand } from "lucide-react";
|
|
10
|
+
|
|
11
|
+
interface StepCardProps {
|
|
12
|
+
task: TaskEffect;
|
|
13
|
+
runId: string;
|
|
14
|
+
onSelect: (effectId: string) => void;
|
|
15
|
+
isSelected: boolean;
|
|
16
|
+
defaultExpanded?: boolean;
|
|
17
|
+
/** 1-based step number for display */
|
|
18
|
+
stepNumber?: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const StepCard = memo(function StepCard({ task, runId: _runId, onSelect, isSelected, defaultExpanded = false, stepNumber }: StepCardProps) {
|
|
22
|
+
const [expanded, setExpanded] = useState(defaultExpanded);
|
|
23
|
+
const isRunning = task.status === "requested";
|
|
24
|
+
const isBreakpointWaiting = task.kind === "breakpoint" && task.status === "requested";
|
|
25
|
+
const borderColor = isBreakpointWaiting ? "border-l-warning shadow-step-inset-warning" :
|
|
26
|
+
task.status === "resolved" ? "border-l-success" :
|
|
27
|
+
task.status === "error" ? "border-l-error" :
|
|
28
|
+
isRunning ? "border-l-info shadow-step-inset-cyan" : "border-l-pending/30";
|
|
29
|
+
|
|
30
|
+
// Live elapsed time counter for running tasks
|
|
31
|
+
const [elapsedMs, setElapsedMs] = useState<number>(0);
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!isRunning || !task.requestedAt) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Calculate initial elapsed time
|
|
39
|
+
const calculateElapsed = () => {
|
|
40
|
+
const requestedTime = new Date(task.requestedAt).getTime();
|
|
41
|
+
const now = Date.now();
|
|
42
|
+
return now - requestedTime;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// Set initial value
|
|
46
|
+
setElapsedMs(calculateElapsed());
|
|
47
|
+
|
|
48
|
+
// Update every second
|
|
49
|
+
const interval = setInterval(() => {
|
|
50
|
+
setElapsedMs(calculateElapsed());
|
|
51
|
+
}, 1000);
|
|
52
|
+
|
|
53
|
+
return () => clearInterval(interval);
|
|
54
|
+
}, [isRunning, task.requestedAt]);
|
|
55
|
+
|
|
56
|
+
return (
|
|
57
|
+
<div
|
|
58
|
+
data-testid={`step-card-${task.effectId}`}
|
|
59
|
+
data-status={task.status}
|
|
60
|
+
data-selected={String(isSelected)}
|
|
61
|
+
className={cn(
|
|
62
|
+
"w-full text-left rounded-lg border border-card-border bg-card transition-all duration-150",
|
|
63
|
+
"hover:bg-[var(--card-hover)] border-l-[3px]",
|
|
64
|
+
borderColor,
|
|
65
|
+
isSelected && "border-l-primary bg-primary-muted shadow-step-selected",
|
|
66
|
+
isBreakpointWaiting && "animate-breakpoint-glow"
|
|
67
|
+
)}
|
|
68
|
+
>
|
|
69
|
+
<button
|
|
70
|
+
onClick={() => onSelect(task.effectId)}
|
|
71
|
+
className="w-full text-left p-3"
|
|
72
|
+
>
|
|
73
|
+
<div className="flex items-center justify-between gap-2">
|
|
74
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
75
|
+
{isBreakpointWaiting ? (
|
|
76
|
+
<Hand className="h-3.5 w-3.5 text-warning animate-pulse-dot shrink-0 drop-shadow-[var(--drop-glow-warning)]" />
|
|
77
|
+
) : isRunning ? (
|
|
78
|
+
<div className="h-2 w-2 rounded-full bg-info animate-pulse-dot shrink-0 shadow-step-running-dot" />
|
|
79
|
+
) : null}
|
|
80
|
+
{stepNumber && (
|
|
81
|
+
<span className="flex-shrink-0 w-5 h-5 rounded-full bg-background-tertiary text-xs font-mono text-foreground-muted flex items-center justify-center">
|
|
82
|
+
{stepNumber}
|
|
83
|
+
</span>
|
|
84
|
+
)}
|
|
85
|
+
<span className="text-sm font-medium text-foreground truncate">{task.title}</span>
|
|
86
|
+
</div>
|
|
87
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
88
|
+
<KindBadge kind={task.kind} />
|
|
89
|
+
<StatusBadge status={task.status} />
|
|
90
|
+
</div>
|
|
91
|
+
</div>
|
|
92
|
+
{isBreakpointWaiting ? (
|
|
93
|
+
<div className="flex items-center gap-1 mt-1.5 text-xs text-warning">
|
|
94
|
+
<Hand className="h-3 w-3 shrink-0 drop-shadow-[var(--drop-glow-warning-sm)]" />
|
|
95
|
+
<span className="font-medium">Your approval is needed</span>
|
|
96
|
+
<span className="font-mono text-warning animate-pulse">{formatDuration(elapsedMs)}</span>
|
|
97
|
+
</div>
|
|
98
|
+
) : (task.duration || isRunning) ? (
|
|
99
|
+
<div className="flex items-center gap-1 mt-1.5 text-xs text-foreground-muted">
|
|
100
|
+
<Clock className="h-3 w-3 shrink-0" />
|
|
101
|
+
<span className="whitespace-nowrap">
|
|
102
|
+
{isRunning ? (
|
|
103
|
+
<span className="animate-pulse font-mono text-info/80">running {formatDuration(elapsedMs)}...</span>
|
|
104
|
+
) : (
|
|
105
|
+
<span className="font-mono">{formatDuration(task.duration)}</span>
|
|
106
|
+
)}
|
|
107
|
+
</span>
|
|
108
|
+
</div>
|
|
109
|
+
) : null}
|
|
110
|
+
</button>
|
|
111
|
+
|
|
112
|
+
{/* Expand/collapse toggle */}
|
|
113
|
+
<div className="flex items-center border-t border-card-border">
|
|
114
|
+
<button
|
|
115
|
+
onClick={(e) => { e.stopPropagation(); setExpanded((v) => !v); }}
|
|
116
|
+
className="flex items-center justify-center px-3 py-2 text-foreground-muted hover:text-primary hover:bg-primary-muted rounded-b-lg transition-colors w-full"
|
|
117
|
+
aria-label={expanded ? "Collapse details" : "Expand details"}
|
|
118
|
+
>
|
|
119
|
+
<ChevronDown className={cn("h-4 w-4 transition-transform duration-200", !expanded && "-rotate-90")} />
|
|
120
|
+
</button>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
{/* Expanded detail */}
|
|
124
|
+
<div
|
|
125
|
+
className={cn(
|
|
126
|
+
"grid transition-[grid-template-rows,opacity] duration-200 ease-out",
|
|
127
|
+
expanded ? "grid-rows-[1fr] opacity-100" : "grid-rows-[0fr] opacity-0"
|
|
128
|
+
)}
|
|
129
|
+
>
|
|
130
|
+
<div className="overflow-hidden">
|
|
131
|
+
<div className="px-3 pb-3 text-xs text-foreground-muted space-y-1.5 border-t border-card-border">
|
|
132
|
+
<div className="flex items-center gap-2 pt-2">
|
|
133
|
+
<span className="text-foreground-secondary font-medium">Step:</span>
|
|
134
|
+
<TruncatedId id={task.stepId} chars={4} />
|
|
135
|
+
</div>
|
|
136
|
+
{task.duration != null && (
|
|
137
|
+
<div className="flex items-center gap-2">
|
|
138
|
+
<span className="text-foreground-secondary font-medium">
|
|
139
|
+
{task.kind === "breakpoint" ? "Approval time:" : "Duration:"}
|
|
140
|
+
</span>
|
|
141
|
+
<span>{formatDuration(task.duration)}</span>
|
|
142
|
+
</div>
|
|
143
|
+
)}
|
|
144
|
+
{task.requestedAt && (
|
|
145
|
+
<div className="flex items-center gap-2">
|
|
146
|
+
<span className="text-foreground-secondary font-medium">Requested:</span>
|
|
147
|
+
<span>{new Date(task.requestedAt).toLocaleTimeString()}</span>
|
|
148
|
+
</div>
|
|
149
|
+
)}
|
|
150
|
+
{task.resolvedAt && (
|
|
151
|
+
<div className="flex items-center gap-2">
|
|
152
|
+
<span className="text-foreground-secondary font-medium">Resolved:</span>
|
|
153
|
+
<span>{new Date(task.resolvedAt).toLocaleTimeString()}</span>
|
|
154
|
+
</div>
|
|
155
|
+
)}
|
|
156
|
+
{task.error && (
|
|
157
|
+
<div className="mt-2 rounded bg-error-muted p-2 text-error">
|
|
158
|
+
<span className="font-medium">{task.error.name}:</span> {task.error.message}
|
|
159
|
+
</div>
|
|
160
|
+
)}
|
|
161
|
+
</div>
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
);
|
|
166
|
+
});
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { createContext, useContext } from "react";
|
|
3
|
+
import { useEventStream, StreamEvent } from "@/hooks/use-event-stream";
|
|
4
|
+
|
|
5
|
+
interface EventStreamContextValue {
|
|
6
|
+
connected: boolean;
|
|
7
|
+
lastEvent: StreamEvent | null;
|
|
8
|
+
error: string | null;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const EventStreamContext = createContext<EventStreamContextValue | null>(null);
|
|
12
|
+
|
|
13
|
+
export function EventStreamProvider({ children }: { children: React.ReactNode }) {
|
|
14
|
+
const { connected, lastEvent, error } = useEventStream();
|
|
15
|
+
|
|
16
|
+
return (
|
|
17
|
+
<EventStreamContext.Provider value={{ connected, lastEvent, error }}>
|
|
18
|
+
{children}
|
|
19
|
+
</EventStreamContext.Provider>
|
|
20
|
+
);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useEventStreamContext() {
|
|
24
|
+
const context = useContext(EventStreamContext);
|
|
25
|
+
if (!context) {
|
|
26
|
+
throw new Error("useEventStreamContext must be used within EventStreamProvider");
|
|
27
|
+
}
|
|
28
|
+
return context;
|
|
29
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { ThemeProvider } from "@/components/shared/theme-provider";
|
|
3
|
+
import { NotificationProvider } from "@/components/notifications/notification-provider";
|
|
4
|
+
import { EventStreamProvider } from "@/components/providers/event-stream-provider";
|
|
5
|
+
import { ShortcutsHelp } from "@/components/shared/shortcuts-help";
|
|
6
|
+
import { AppHeader } from "@/components/shared/app-header";
|
|
7
|
+
import { AppFooter } from "@/components/shared/app-footer";
|
|
8
|
+
|
|
9
|
+
export function Providers({ children }: { children: React.ReactNode }) {
|
|
10
|
+
return (
|
|
11
|
+
<ThemeProvider>
|
|
12
|
+
<NotificationProvider>
|
|
13
|
+
<EventStreamProvider>
|
|
14
|
+
<div className="flex flex-col min-h-screen">
|
|
15
|
+
<AppHeader />
|
|
16
|
+
<main id="main-content" className="flex-1 flex flex-col">{children}</main>
|
|
17
|
+
<AppFooter />
|
|
18
|
+
</div>
|
|
19
|
+
<ShortcutsHelp />
|
|
20
|
+
</EventStreamProvider>
|
|
21
|
+
</NotificationProvider>
|
|
22
|
+
</ThemeProvider>
|
|
23
|
+
);
|
|
24
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { render, screen } from '@/test/test-utils';
|
|
4
|
+
import { EmptyState } from '../empty-state';
|
|
5
|
+
|
|
6
|
+
describe('EmptyState', () => {
|
|
7
|
+
it('renders without crashing', () => {
|
|
8
|
+
render(<EmptyState />);
|
|
9
|
+
expect(screen.getByText('No runs found')).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders default title', () => {
|
|
13
|
+
render(<EmptyState />);
|
|
14
|
+
expect(screen.getByText('No runs found')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('renders default description', () => {
|
|
18
|
+
render(<EmptyState />);
|
|
19
|
+
expect(screen.getByText('Start a babysitter run to see it here.')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('renders custom title', () => {
|
|
23
|
+
render(<EmptyState title="Nothing here" />);
|
|
24
|
+
expect(screen.getByText('Nothing here')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('renders custom description', () => {
|
|
28
|
+
render(<EmptyState description="Try adding some data." />);
|
|
29
|
+
expect(screen.getByText('Try adding some data.')).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders the Inbox icon', () => {
|
|
33
|
+
const { container } = render(<EmptyState />);
|
|
34
|
+
const svg = container.querySelector('svg');
|
|
35
|
+
expect(svg).toBeInTheDocument();
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('applies custom className', () => {
|
|
39
|
+
const { container } = render(<EmptyState className="my-custom" />);
|
|
40
|
+
const wrapper = container.firstChild as HTMLElement;
|
|
41
|
+
expect(wrapper.className).toContain('my-custom');
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('renders custom title and description together', () => {
|
|
45
|
+
render(<EmptyState title="Empty" description="Custom desc" />);
|
|
46
|
+
expect(screen.getByText('Empty')).toBeInTheDocument();
|
|
47
|
+
expect(screen.getByText('Custom desc')).toBeInTheDocument();
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect, vi } from 'vitest';
|
|
3
|
+
import { render, screen } from '@/test/test-utils';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { FriendlyId } from '../friendly-id';
|
|
6
|
+
|
|
7
|
+
describe('FriendlyId', () => {
|
|
8
|
+
it('renders without crashing', () => {
|
|
9
|
+
render(<FriendlyId id="abc123def456" />);
|
|
10
|
+
// formatShortId("abc123def456") defaults to 4 chars => "...f456"
|
|
11
|
+
expect(screen.getByText('...f456')).toBeInTheDocument();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('renders truncated ID using formatShortId', () => {
|
|
15
|
+
render(<FriendlyId id="my-long-test-id" />);
|
|
16
|
+
expect(screen.getByText('...t-id')).toBeInTheDocument();
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('renders as a button element', () => {
|
|
20
|
+
render(<FriendlyId id="test-id" />);
|
|
21
|
+
expect(screen.getByRole('button')).toBeInTheDocument();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('copies full ID to clipboard on click', async () => {
|
|
25
|
+
const user = userEvent.setup();
|
|
26
|
+
const writeTextSpy = vi.spyOn(navigator.clipboard, 'writeText');
|
|
27
|
+
render(<FriendlyId id="full-id-for-copy" />);
|
|
28
|
+
const btn = screen.getByRole('button');
|
|
29
|
+
await user.click(btn);
|
|
30
|
+
expect(writeTextSpy).toHaveBeenCalledWith('full-id-for-copy');
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
it('applies copied class after clicking', async () => {
|
|
34
|
+
const user = userEvent.setup();
|
|
35
|
+
render(<FriendlyId id="some-id" />);
|
|
36
|
+
const btn = screen.getByRole('button');
|
|
37
|
+
await user.click(btn);
|
|
38
|
+
// After copying, button gets 'text-primary' class to indicate copied state
|
|
39
|
+
expect(btn.className).toContain('text-primary');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('applies custom className', () => {
|
|
43
|
+
const { container } = render(<FriendlyId id="test" className="my-class" />);
|
|
44
|
+
const btn = container.querySelector('button');
|
|
45
|
+
expect(btn?.className).toContain('my-class');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { render, screen } from '@/test/test-utils';
|
|
4
|
+
import { Kbd } from '../kbd';
|
|
5
|
+
|
|
6
|
+
describe('Kbd', () => {
|
|
7
|
+
it('renders without crashing', () => {
|
|
8
|
+
render(<Kbd>K</Kbd>);
|
|
9
|
+
expect(screen.getByText('K')).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders the children text', () => {
|
|
13
|
+
render(<Kbd>Enter</Kbd>);
|
|
14
|
+
expect(screen.getByText('Enter')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('renders as a <kbd> element', () => {
|
|
18
|
+
const { container } = render(<Kbd>A</Kbd>);
|
|
19
|
+
const kbd = container.querySelector('kbd');
|
|
20
|
+
expect(kbd).toBeInTheDocument();
|
|
21
|
+
expect(kbd?.textContent).toBe('A');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('applies default styling classes', () => {
|
|
25
|
+
const { container } = render(<Kbd>X</Kbd>);
|
|
26
|
+
const kbd = container.querySelector('kbd') as HTMLElement;
|
|
27
|
+
expect(kbd.className).toContain('inline-flex');
|
|
28
|
+
expect(kbd.className).toContain('font-mono');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('applies custom className', () => {
|
|
32
|
+
const { container } = render(<Kbd className="extra-class">M</Kbd>);
|
|
33
|
+
const kbd = container.querySelector('kbd') as HTMLElement;
|
|
34
|
+
expect(kbd.className).toContain('extra-class');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders complex children', () => {
|
|
38
|
+
render(
|
|
39
|
+
<Kbd>
|
|
40
|
+
<span>Ctrl</span>
|
|
41
|
+
</Kbd>,
|
|
42
|
+
);
|
|
43
|
+
expect(screen.getByText('Ctrl')).toBeInTheDocument();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
import { render, screen } from '@/test/test-utils';
|
|
4
|
+
import { KindBadge } from '../kind-badge';
|
|
5
|
+
|
|
6
|
+
describe('KindBadge', () => {
|
|
7
|
+
it('renders without crashing', () => {
|
|
8
|
+
render(<KindBadge kind="node" />);
|
|
9
|
+
expect(screen.getByText('node')).toBeInTheDocument();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('renders "agent" kind with correct text', () => {
|
|
13
|
+
render(<KindBadge kind="agent" />);
|
|
14
|
+
expect(screen.getByText('agent')).toBeInTheDocument();
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
it('renders "node" kind with correct text', () => {
|
|
18
|
+
render(<KindBadge kind="node" />);
|
|
19
|
+
expect(screen.getByText('node')).toBeInTheDocument();
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('renders "shell" kind with correct text', () => {
|
|
23
|
+
render(<KindBadge kind="shell" />);
|
|
24
|
+
expect(screen.getByText('shell')).toBeInTheDocument();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('renders "skill" kind with correct text', () => {
|
|
28
|
+
render(<KindBadge kind="skill" />);
|
|
29
|
+
expect(screen.getByText('skill')).toBeInTheDocument();
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('renders "breakpoint" kind with "approval" display text', () => {
|
|
33
|
+
render(<KindBadge kind="breakpoint" />);
|
|
34
|
+
expect(screen.getByText('approval')).toBeInTheDocument();
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it('renders "sleep" kind with correct text', () => {
|
|
38
|
+
render(<KindBadge kind="sleep" />);
|
|
39
|
+
expect(screen.getByText('sleep')).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders an SVG icon alongside the kind text', () => {
|
|
43
|
+
const { container } = render(<KindBadge kind="agent" />);
|
|
44
|
+
const svg = container.querySelector('svg');
|
|
45
|
+
expect(svg).toBeInTheDocument();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('applies agent color class for agent kind', () => {
|
|
49
|
+
const { container } = render(<KindBadge kind="agent" />);
|
|
50
|
+
const badge = container.firstChild as HTMLElement;
|
|
51
|
+
expect(badge.className).toContain('text-primary');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it('applies warning color class for node kind', () => {
|
|
55
|
+
const { container } = render(<KindBadge kind="node" />);
|
|
56
|
+
const badge = container.firstChild as HTMLElement;
|
|
57
|
+
expect(badge.className).toContain('text-warning');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it('applies info color class for skill kind', () => {
|
|
61
|
+
const { container } = render(<KindBadge kind="skill" />);
|
|
62
|
+
const badge = container.firstChild as HTMLElement;
|
|
63
|
+
expect(badge.className).toContain('text-info');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('applies custom className', () => {
|
|
67
|
+
const { container } = render(<KindBadge kind="agent" className="extra-cls" />);
|
|
68
|
+
const badge = container.firstChild as HTMLElement;
|
|
69
|
+
expect(badge.className).toContain('extra-cls');
|
|
70
|
+
});
|
|
71
|
+
});
|