@leanspec/ui 0.2.7 → 0.2.9
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/standalone/packages/ui/.next/BUILD_ID +1 -1
- package/.next/standalone/packages/ui/.next/app-path-routes-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/build-manifest.json +4 -4
- package/.next/standalone/packages/ui/.next/prerender-manifest.json +3 -3
- package/.next/standalone/packages/ui/.next/routes-manifest.json +24 -0
- package/.next/standalone/packages/ui/.next/server/app/_global-error/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/packages/ui/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_not-found/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/_not-found.html +2 -2
- package/.next/standalone/packages/ui/.next/server/app/_not-found.rsc +19 -19
- package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_full.segment.rsc +19 -19
- package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
- package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
- package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
- package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_tree.segment.rsc +6 -6
- package/.next/standalone/packages/ui/.next/server/app/api/context/route/app-paths-manifest.json +3 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route/build-manifest.json +11 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route/server-reference-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route.js +8 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route.js.map +5 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route.js.nft.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/api/context/route_client-reference-manifest.js +2 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/app-paths-manifest.json +3 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/build-manifest.json +11 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/server-reference-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js +8 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js.map +5 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js.nft.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route_client-reference-manifest.js +2 -0
- package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/revalidate/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/dependency-graph/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/status/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/subspecs/[file]/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/api/stats/route.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/context/page/app-paths-manifest.json +3 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page/build-manifest.json +18 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page/next-font-manifest.json +6 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page/server-reference-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page.js +19 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page.js.map +5 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page.js.nft.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/context/page_client-reference-manifest.js +2 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page/app-paths-manifest.json +3 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page/build-manifest.json +18 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page/next-font-manifest.json +6 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page/react-loadable-manifest.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page/server-reference-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js +19 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js.map +5 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js.nft.json +1 -0
- package/.next/standalone/packages/ui/.next/server/app/dependencies/page_client-reference-manifest.js +2 -0
- package/.next/standalone/packages/ui/.next/server/app/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/projects.html +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects.rsc +24 -23
- package/.next/standalone/packages/ui/.next/server/app/projects.segments/_full.segment.rsc +24 -23
- package/.next/standalone/packages/ui/.next/server/app/projects.segments/_index.segment.rsc +9 -9
- package/.next/standalone/packages/ui/.next/server/app/projects.segments/_tree.segment.rsc +3 -3
- package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects.segment.rsc +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/specs/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/specs/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/stats/page/build-manifest.json +2 -2
- package/.next/standalone/packages/ui/.next/server/app/stats/page.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app/stats/page.js.nft.json +1 -1
- package/.next/standalone/packages/ui/.next/server/app/stats/page_client-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/app-paths-manifest.json +4 -0
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__3559376c._.js +2 -2
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__65667b70._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__803d07f0._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__84cdc14a._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__8a9ab1a3._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__b0969111._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__bdc3963a._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__f5c6d6b8._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_context_route_actions_dead6daa.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_dependencies_route_actions_cf6b14c3.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__12b4eb41._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__3c77d95b._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__44b603f9._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__69a0d63a._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__daee3355._.js → [root-of-the-server]__8608a6fa._.js} +2 -2
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__fd80e4dd._.js → [root-of-the-server]__a965a67b._.js} +2 -2
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__daedd80e._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__dc176c61._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__5382b397._.js → [root-of-the-server]__ead1539c._.js} +2 -2
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_000dd317._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_03aa8d19._.js +7 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_28fe1532._.js +5 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_8cec504f._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_959ad3d8._.js +4 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/{_22274047._.js → _adb9d7cb._.js} +2 -2
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_da18b655._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_ea899b87._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_f38e75b7._.js +4 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_fe120f8a._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_015f83ca._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_1120b57c._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_151891de._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_1c916fe3._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_80605a06._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_bb80de48._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_e8075f9b._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_f919ef4a._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_context_page_actions_1a062d48.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_dependencies_page_actions_57387d47.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_context_context-client_tsx_4ba99a62._.js +12 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_dependencies_dependencies-client_tsx_0e82443a._.js +4 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_specs_specs-client_tsx_0bb8f8f8._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_components_spec-detail-wrapper_tsx_fd35401c._.js +3 -0
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_components_specs-nav-sidebar_tsx_8237ed13._.js +1 -1
- package/.next/standalone/packages/ui/.next/server/middleware-build-manifest.js +2 -2
- package/.next/standalone/packages/ui/.next/server/pages/404.html +2 -2
- package/.next/standalone/packages/ui/.next/server/pages/500.html +2 -2
- package/.next/standalone/packages/ui/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/packages/ui/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/packages/ui/.next/static/chunks/094cf9f4e3553261.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/1c015eb9eaaf9f9c.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/1d36660b2877d213.js +3 -0
- package/.next/standalone/packages/ui/.next/static/chunks/46275d9d67603bf5.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/4d29ca0fa6843070.js +2 -0
- package/.next/standalone/packages/ui/.next/static/chunks/59854b15bf046467.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/{cca4441cde342ae3.js → 5fd8101e32b076b1.js} +1 -1
- package/.next/standalone/packages/ui/.next/static/chunks/6d938a49daa10208.js +2 -0
- package/.next/standalone/packages/ui/.next/static/chunks/979625373d5474b6.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/a33f10af6abef4df.js +10 -0
- package/.next/{static/chunks/794f3931f1ca12d2.js → standalone/packages/ui/.next/static/chunks/a3d7e1be47de010b.js} +1 -1
- package/.next/standalone/packages/ui/.next/static/chunks/a5e25b9fa6b88eee.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/b53250480fde6816.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/b7f19087afe1d2c9.css +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/c658e22a605e0ed1.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/c92660d8d0c4763d.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/ddd87cd0d26bc2f5.js +1 -0
- package/.next/standalone/packages/ui/.next/static/chunks/eeec245955b3b600.js +5 -0
- package/.next/standalone/packages/ui/.next/static/chunks/ff11efb770d5a0bc.js +1 -0
- package/.next/{static/chunks/turbopack-5fa55215af0efb15.js → standalone/packages/ui/.next/static/chunks/turbopack-261c5dcdd873f310.js} +1 -1
- package/.next/standalone/packages/ui/package.json +5 -1
- package/.next/standalone/packages/ui/src/app/api/context/route.ts +22 -0
- package/.next/standalone/packages/ui/src/app/api/dependencies/route.ts +81 -0
- package/.next/standalone/packages/ui/src/app/context/context-client.tsx +393 -0
- package/.next/standalone/packages/ui/src/app/context/page.tsx +17 -0
- package/.next/standalone/packages/ui/src/app/dependencies/constants.ts +24 -0
- package/.next/standalone/packages/ui/src/app/dependencies/dependencies-client.tsx +625 -0
- package/.next/standalone/packages/ui/src/app/dependencies/index.ts +19 -0
- package/.next/standalone/packages/ui/src/app/dependencies/page.tsx +38 -0
- package/.next/standalone/packages/ui/src/app/dependencies/spec-node.tsx +80 -0
- package/.next/standalone/packages/ui/src/app/dependencies/spec-sidebar.tsx +198 -0
- package/.next/standalone/packages/ui/src/app/dependencies/types.ts +37 -0
- package/.next/standalone/packages/ui/src/app/dependencies/utils.ts +194 -0
- package/.next/standalone/packages/ui/src/app/globals.css +16 -16
- package/.next/standalone/packages/ui/src/app/layout.tsx +4 -7
- package/.next/standalone/packages/ui/src/components/context-file-detail.tsx +308 -0
- package/.next/standalone/packages/ui/src/components/context-file-viewer.tsx +385 -0
- package/.next/standalone/packages/ui/src/components/main-sidebar.tsx +19 -1
- package/.next/standalone/packages/ui/src/components/spec-detail-client.tsx +181 -134
- package/.next/standalone/packages/ui/src/components/spec-detail-wrapper.tsx +20 -0
- package/.next/standalone/packages/ui/src/components/specs-nav-sidebar.tsx +3 -2
- package/.next/standalone/packages/ui/src/components/ui/accordion.tsx +58 -0
- package/.next/standalone/packages/ui/src/lib/db/service-queries.ts +172 -3
- package/.next/standalone/packages/ui/src/lib/specs/types.ts +44 -0
- package/.next/standalone/packages/ui/tsconfig.tsbuildinfo +1 -1
- package/.next/static/chunks/094cf9f4e3553261.js +1 -0
- package/.next/static/chunks/1c015eb9eaaf9f9c.js +1 -0
- package/.next/static/chunks/1d36660b2877d213.js +3 -0
- package/.next/static/chunks/46275d9d67603bf5.js +1 -0
- package/.next/static/chunks/4d29ca0fa6843070.js +2 -0
- package/.next/static/chunks/59854b15bf046467.js +1 -0
- package/.next/static/chunks/{cca4441cde342ae3.js → 5fd8101e32b076b1.js} +1 -1
- package/.next/static/chunks/6d938a49daa10208.js +2 -0
- package/.next/static/chunks/979625373d5474b6.js +1 -0
- package/.next/static/chunks/a33f10af6abef4df.js +10 -0
- package/.next/{standalone/packages/ui/.next/static/chunks/794f3931f1ca12d2.js → static/chunks/a3d7e1be47de010b.js} +1 -1
- package/.next/static/chunks/a5e25b9fa6b88eee.js +1 -0
- package/.next/static/chunks/b53250480fde6816.js +1 -0
- package/.next/static/chunks/b7f19087afe1d2c9.css +1 -0
- package/.next/static/chunks/c658e22a605e0ed1.js +1 -0
- package/.next/static/chunks/c92660d8d0c4763d.js +1 -0
- package/.next/static/chunks/ddd87cd0d26bc2f5.js +1 -0
- package/.next/static/chunks/eeec245955b3b600.js +5 -0
- package/.next/static/chunks/ff11efb770d5a0bc.js +1 -0
- package/.next/{standalone/packages/ui/.next/static/chunks/turbopack-5fa55215af0efb15.js → static/chunks/turbopack-261c5dcdd873f310.js} +1 -1
- package/package.json +6 -2
- package/.next/standalone/node_modules/.pnpm/source-map@0.8.0-beta.0/node_modules/source-map/package.json +0 -95
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__1d0c2012._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__73f60f12._.js +0 -7
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__a7ae8552._.js +0 -7
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_0f9ffe32._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_14118969._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_4129cc0f._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_497c8b73._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_ac867463._.js +0 -3
- package/.next/standalone/packages/ui/.next/server/chunks/ssr/_c2f54661._.js +0 -5
- package/.next/standalone/packages/ui/.next/static/chunks/16ff9833ae1bb3ae.js +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/2204c0f16b23ec4c.js +0 -3
- package/.next/standalone/packages/ui/.next/static/chunks/294dea6dbec43ca6.js +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/7590e65bcaa41e8b.js +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/b6976cf6c48996e5.js +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/b8353eb8c6fb895e.js +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/b845813463167db0.js +0 -5
- package/.next/standalone/packages/ui/.next/static/chunks/bd9893e28f8f6a9a.css +0 -1
- package/.next/standalone/packages/ui/.next/static/chunks/d784d84d5b880e48.js +0 -1
- package/.next/static/chunks/16ff9833ae1bb3ae.js +0 -1
- package/.next/static/chunks/2204c0f16b23ec4c.js +0 -3
- package/.next/static/chunks/294dea6dbec43ca6.js +0 -1
- package/.next/static/chunks/7590e65bcaa41e8b.js +0 -1
- package/.next/static/chunks/b6976cf6c48996e5.js +0 -1
- package/.next/static/chunks/b8353eb8c6fb895e.js +0 -1
- package/.next/static/chunks/b845813463167db0.js +0 -5
- package/.next/static/chunks/bd9893e28f8f6a9a.css +0 -1
- package/.next/static/chunks/d784d84d5b880e48.js +0 -1
- /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_buildManifest.js +0 -0
- /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_ssgManifest.js +0 -0
- /package/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_buildManifest.js +0 -0
- /package/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context File Detail Component
|
|
3
|
+
* Full-page markdown view with table of contents sidebar, similar to spec detail page
|
|
4
|
+
* Supports Mermaid diagrams and rich markdown rendering
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
'use client';
|
|
8
|
+
|
|
9
|
+
import * as React from 'react';
|
|
10
|
+
import ReactMarkdown from 'react-markdown';
|
|
11
|
+
import remarkGfm from 'remark-gfm';
|
|
12
|
+
import rehypeHighlight from 'rehype-highlight';
|
|
13
|
+
import rehypeSlug from 'rehype-slug';
|
|
14
|
+
import {
|
|
15
|
+
Copy,
|
|
16
|
+
Check,
|
|
17
|
+
Clock,
|
|
18
|
+
Coins,
|
|
19
|
+
ExternalLink,
|
|
20
|
+
ArrowLeft,
|
|
21
|
+
Bot,
|
|
22
|
+
BookOpen,
|
|
23
|
+
ScrollText,
|
|
24
|
+
FileCode,
|
|
25
|
+
Settings,
|
|
26
|
+
History,
|
|
27
|
+
Users,
|
|
28
|
+
Shield,
|
|
29
|
+
Scale,
|
|
30
|
+
Sparkles,
|
|
31
|
+
} from 'lucide-react';
|
|
32
|
+
import { Button } from '@/components/ui/button';
|
|
33
|
+
import { Badge } from '@/components/ui/badge';
|
|
34
|
+
import { TableOfContentsSidebar, TableOfContents } from '@/components/table-of-contents';
|
|
35
|
+
import { BackToTop } from '@/components/back-to-top';
|
|
36
|
+
import { MermaidDiagram } from '@/components/mermaid-diagram';
|
|
37
|
+
import { cn } from '@/lib/utils';
|
|
38
|
+
import type { ContextFile } from '@/lib/specs/types';
|
|
39
|
+
|
|
40
|
+
interface ContextFileDetailProps {
|
|
41
|
+
file: ContextFile;
|
|
42
|
+
projectRoot?: string;
|
|
43
|
+
onBack: () => void;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Get icon for file based on name
|
|
48
|
+
*/
|
|
49
|
+
function getFileIcon(fileName: string): React.ComponentType<{ className?: string }> {
|
|
50
|
+
const name = fileName.toLowerCase();
|
|
51
|
+
|
|
52
|
+
// Agent instruction files
|
|
53
|
+
if (name === 'agents.md' || name.includes('agent')) return Bot;
|
|
54
|
+
if (name === 'gemini.md') return Sparkles;
|
|
55
|
+
if (name === 'claude.md') return Bot;
|
|
56
|
+
if (name === 'copilot.md' || name === 'copilot-instructions.md') return Bot;
|
|
57
|
+
|
|
58
|
+
// Project documentation
|
|
59
|
+
if (name === 'readme.md') return BookOpen;
|
|
60
|
+
if (name === 'contributing.md') return Users;
|
|
61
|
+
if (name === 'changelog.md') return History;
|
|
62
|
+
if (name === 'license.md' || name === 'license') return Scale;
|
|
63
|
+
if (name === 'security.md') return Shield;
|
|
64
|
+
|
|
65
|
+
// Config files
|
|
66
|
+
if (name.endsWith('.json')) return Settings;
|
|
67
|
+
if (name === 'config.json') return Settings;
|
|
68
|
+
|
|
69
|
+
// Code-related
|
|
70
|
+
if (name.includes('api') || name.includes('spec')) return FileCode;
|
|
71
|
+
|
|
72
|
+
// Default
|
|
73
|
+
return ScrollText;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Get icon color class for file based on name
|
|
78
|
+
*/
|
|
79
|
+
function getFileIconColor(fileName: string): string {
|
|
80
|
+
const name = fileName.toLowerCase();
|
|
81
|
+
|
|
82
|
+
if (name === 'agents.md' || name.includes('agent')) return 'text-purple-500';
|
|
83
|
+
if (name === 'gemini.md') return 'text-blue-500';
|
|
84
|
+
if (name === 'claude.md') return 'text-orange-500';
|
|
85
|
+
if (name === 'copilot.md' || name === 'copilot-instructions.md') return 'text-sky-500';
|
|
86
|
+
if (name === 'readme.md') return 'text-green-500';
|
|
87
|
+
if (name === 'contributing.md') return 'text-pink-500';
|
|
88
|
+
if (name === 'changelog.md') return 'text-amber-500';
|
|
89
|
+
if (name === 'license.md' || name === 'license') return 'text-slate-500';
|
|
90
|
+
if (name === 'security.md') return 'text-red-500';
|
|
91
|
+
if (name.endsWith('.json')) return 'text-yellow-500';
|
|
92
|
+
|
|
93
|
+
return 'text-muted-foreground';
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get token count color based on thresholds
|
|
98
|
+
*/
|
|
99
|
+
function getTokenColor(tokens: number): string {
|
|
100
|
+
if (tokens < 2000) return 'text-green-600 dark:text-green-400';
|
|
101
|
+
if (tokens < 3500) return 'text-blue-600 dark:text-blue-400';
|
|
102
|
+
if (tokens < 5000) return 'text-yellow-600 dark:text-yellow-400';
|
|
103
|
+
return 'text-red-600 dark:text-red-400';
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Get token status label
|
|
108
|
+
*/
|
|
109
|
+
function getTokenStatus(tokens: number): string {
|
|
110
|
+
if (tokens < 2000) return 'Optimal';
|
|
111
|
+
if (tokens < 3500) return 'Good';
|
|
112
|
+
if (tokens < 5000) return 'Large';
|
|
113
|
+
return 'Very Large';
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Format date for display
|
|
118
|
+
*/
|
|
119
|
+
function formatDate(date: Date | string): string {
|
|
120
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
121
|
+
return d.toLocaleDateString('en-US', {
|
|
122
|
+
year: 'numeric',
|
|
123
|
+
month: 'short',
|
|
124
|
+
day: 'numeric',
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Generate VS Code URI to open file
|
|
130
|
+
*/
|
|
131
|
+
function getVSCodeUri(projectRoot: string, filePath: string): string {
|
|
132
|
+
const fullPath = filePath.startsWith('/') ? filePath : `${projectRoot}/${filePath}`;
|
|
133
|
+
return `vscode://file${fullPath}`;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function ContextFileDetail({ file, projectRoot, onBack }: ContextFileDetailProps) {
|
|
137
|
+
const [copied, setCopied] = React.useState(false);
|
|
138
|
+
|
|
139
|
+
const FileIcon = getFileIcon(file.name);
|
|
140
|
+
const iconColor = getFileIconColor(file.name);
|
|
141
|
+
|
|
142
|
+
const handleCopy = async () => {
|
|
143
|
+
try {
|
|
144
|
+
await navigator.clipboard.writeText(file.content);
|
|
145
|
+
setCopied(true);
|
|
146
|
+
setTimeout(() => setCopied(false), 2000);
|
|
147
|
+
} catch (error) {
|
|
148
|
+
console.error('Failed to copy content:', error);
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
|
|
152
|
+
const handleOpenInEditor = () => {
|
|
153
|
+
if (projectRoot) {
|
|
154
|
+
window.open(getVSCodeUri(projectRoot, file.path), '_blank');
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
const isMarkdown = file.name.endsWith('.md');
|
|
159
|
+
const isJson = file.name.endsWith('.json');
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<>
|
|
163
|
+
{/* Header */}
|
|
164
|
+
<header className="lg:sticky lg:top-14 lg:z-20 border-b bg-card">
|
|
165
|
+
<div className="px-3 sm:px-6 py-2 sm:py-3">
|
|
166
|
+
{/* Line 1: Back button + File name */}
|
|
167
|
+
<div className="flex items-center gap-3 mb-1.5 sm:mb-2">
|
|
168
|
+
<Button
|
|
169
|
+
variant="ghost"
|
|
170
|
+
size="sm"
|
|
171
|
+
onClick={onBack}
|
|
172
|
+
className="h-8 px-2 -ml-2"
|
|
173
|
+
>
|
|
174
|
+
<ArrowLeft className="h-4 w-4 mr-1" />
|
|
175
|
+
Back
|
|
176
|
+
</Button>
|
|
177
|
+
<FileIcon className={cn('h-5 w-5 shrink-0', iconColor)} />
|
|
178
|
+
<h1 className="text-lg sm:text-xl font-bold tracking-tight truncate">
|
|
179
|
+
{file.name}
|
|
180
|
+
</h1>
|
|
181
|
+
</div>
|
|
182
|
+
|
|
183
|
+
{/* Line 2: Badges and actions */}
|
|
184
|
+
<div className="flex flex-wrap items-center gap-2">
|
|
185
|
+
<Badge variant="outline" className={cn('text-xs', getTokenColor(file.tokenCount))}>
|
|
186
|
+
<Coins className="h-3 w-3 mr-1" />
|
|
187
|
+
{file.tokenCount.toLocaleString()} tokens
|
|
188
|
+
</Badge>
|
|
189
|
+
<span className="text-xs text-muted-foreground">
|
|
190
|
+
{getTokenStatus(file.tokenCount)}
|
|
191
|
+
</span>
|
|
192
|
+
<div className="h-4 w-px bg-border mx-1 hidden sm:block" />
|
|
193
|
+
<span className="text-xs text-muted-foreground flex items-center gap-1">
|
|
194
|
+
<Clock className="h-3 w-3" />
|
|
195
|
+
Modified {formatDate(file.lastModified)}
|
|
196
|
+
</span>
|
|
197
|
+
<span className="text-xs text-muted-foreground">•</span>
|
|
198
|
+
<span className="text-xs text-muted-foreground">
|
|
199
|
+
{file.content.split('\n').length} lines
|
|
200
|
+
</span>
|
|
201
|
+
</div>
|
|
202
|
+
|
|
203
|
+
{/* Line 3: Path and actions */}
|
|
204
|
+
<div className="flex items-center justify-between gap-2 mt-1.5 sm:mt-2">
|
|
205
|
+
<span className="text-xs text-muted-foreground truncate">{file.path}</span>
|
|
206
|
+
<div className="flex items-center gap-1 shrink-0">
|
|
207
|
+
{projectRoot && (
|
|
208
|
+
<Button
|
|
209
|
+
variant="ghost"
|
|
210
|
+
size="sm"
|
|
211
|
+
className="h-7 px-2 text-xs"
|
|
212
|
+
onClick={handleOpenInEditor}
|
|
213
|
+
title="Open in VS Code"
|
|
214
|
+
>
|
|
215
|
+
<ExternalLink className="h-3.5 w-3.5 mr-1" />
|
|
216
|
+
Open in Editor
|
|
217
|
+
</Button>
|
|
218
|
+
)}
|
|
219
|
+
<Button
|
|
220
|
+
variant="ghost"
|
|
221
|
+
size="sm"
|
|
222
|
+
className="h-7 px-2 text-xs"
|
|
223
|
+
onClick={handleCopy}
|
|
224
|
+
title="Copy content"
|
|
225
|
+
>
|
|
226
|
+
{copied ? (
|
|
227
|
+
<>
|
|
228
|
+
<Check className="h-3.5 w-3.5 mr-1 text-green-600" />
|
|
229
|
+
Copied!
|
|
230
|
+
</>
|
|
231
|
+
) : (
|
|
232
|
+
<>
|
|
233
|
+
<Copy className="h-3.5 w-3.5 mr-1" />
|
|
234
|
+
Copy
|
|
235
|
+
</>
|
|
236
|
+
)}
|
|
237
|
+
</Button>
|
|
238
|
+
</div>
|
|
239
|
+
</div>
|
|
240
|
+
</div>
|
|
241
|
+
</header>
|
|
242
|
+
|
|
243
|
+
{/* Main content with Sidebar */}
|
|
244
|
+
<div className="flex flex-col xl:flex-row xl:items-start">
|
|
245
|
+
<main className="flex-1 px-3 sm:px-6 py-3 sm:py-6 min-w-0">
|
|
246
|
+
<div className="space-y-4">
|
|
247
|
+
{isJson ? (
|
|
248
|
+
<pre className="p-4 text-sm overflow-x-auto bg-muted/20 rounded-lg border whitespace-pre-wrap">
|
|
249
|
+
{JSON.stringify(JSON.parse(file.content), null, 2)}
|
|
250
|
+
</pre>
|
|
251
|
+
) : isMarkdown ? (
|
|
252
|
+
<article className="prose prose-slate dark:prose-invert max-w-none prose-sm sm:prose-base">
|
|
253
|
+
<ReactMarkdown
|
|
254
|
+
remarkPlugins={[remarkGfm]}
|
|
255
|
+
rehypePlugins={[rehypeHighlight, rehypeSlug]}
|
|
256
|
+
components={{
|
|
257
|
+
pre: ({ children, ...props }) => {
|
|
258
|
+
// Safely get the first child element
|
|
259
|
+
const childArray = React.Children.toArray(children);
|
|
260
|
+
const firstChild = childArray[0];
|
|
261
|
+
|
|
262
|
+
// Check if this is a mermaid code block
|
|
263
|
+
if (
|
|
264
|
+
React.isValidElement(firstChild) &&
|
|
265
|
+
firstChild.type === 'code' &&
|
|
266
|
+
typeof (firstChild.props as { className?: string }).className === 'string' &&
|
|
267
|
+
(firstChild.props as { className?: string }).className?.includes('language-mermaid')
|
|
268
|
+
) {
|
|
269
|
+
const codeProps = firstChild.props as { children?: React.ReactNode };
|
|
270
|
+
const code = typeof codeProps.children === 'string'
|
|
271
|
+
? codeProps.children
|
|
272
|
+
: '';
|
|
273
|
+
return <MermaidDiagram code={code} />;
|
|
274
|
+
}
|
|
275
|
+
// Default pre rendering
|
|
276
|
+
return <pre {...props}>{children}</pre>;
|
|
277
|
+
},
|
|
278
|
+
}}
|
|
279
|
+
>
|
|
280
|
+
{file.content}
|
|
281
|
+
</ReactMarkdown>
|
|
282
|
+
</article>
|
|
283
|
+
) : (
|
|
284
|
+
<pre className="p-4 text-sm overflow-x-auto bg-muted/20 rounded-lg border whitespace-pre-wrap">
|
|
285
|
+
{file.content}
|
|
286
|
+
</pre>
|
|
287
|
+
)}
|
|
288
|
+
</div>
|
|
289
|
+
</main>
|
|
290
|
+
|
|
291
|
+
{/* Right Sidebar for TOC (Desktop only, markdown files only) */}
|
|
292
|
+
{isMarkdown && (
|
|
293
|
+
<aside className="hidden xl:block w-72 shrink-0 px-6 py-6 sticky top-32 h-[calc(100vh-8rem)] overflow-y-auto scrollbar-auto-hide">
|
|
294
|
+
<TableOfContentsSidebar content={file.content} />
|
|
295
|
+
</aside>
|
|
296
|
+
)}
|
|
297
|
+
</div>
|
|
298
|
+
|
|
299
|
+
{/* Floating action buttons (Mobile/Tablet only) */}
|
|
300
|
+
{isMarkdown && (
|
|
301
|
+
<div className="xl:hidden">
|
|
302
|
+
<TableOfContents content={file.content} />
|
|
303
|
+
</div>
|
|
304
|
+
)}
|
|
305
|
+
<BackToTop />
|
|
306
|
+
</>
|
|
307
|
+
);
|
|
308
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context File Viewer Component
|
|
3
|
+
* Displays a single context file with token count, copy button, and syntax highlighting
|
|
4
|
+
* Uses the same rich markdown rendering as spec detail page (including Mermaid diagrams)
|
|
5
|
+
* Phase 1 & 4: Spec 131 - UI Project Context Visibility
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
'use client';
|
|
9
|
+
|
|
10
|
+
import * as React from 'react';
|
|
11
|
+
import {
|
|
12
|
+
Copy,
|
|
13
|
+
Check,
|
|
14
|
+
FileText,
|
|
15
|
+
Clock,
|
|
16
|
+
Coins,
|
|
17
|
+
ExternalLink,
|
|
18
|
+
Maximize2,
|
|
19
|
+
Bot,
|
|
20
|
+
BookOpen,
|
|
21
|
+
ScrollText,
|
|
22
|
+
FileCode,
|
|
23
|
+
Settings,
|
|
24
|
+
History,
|
|
25
|
+
Users,
|
|
26
|
+
Shield,
|
|
27
|
+
Scale,
|
|
28
|
+
Sparkles,
|
|
29
|
+
} from 'lucide-react';
|
|
30
|
+
import { Card, CardHeader, CardTitle, CardContent } from '@/components/ui/card';
|
|
31
|
+
import { Button } from '@/components/ui/button';
|
|
32
|
+
import { Badge } from '@/components/ui/badge';
|
|
33
|
+
import { cn } from '@/lib/utils';
|
|
34
|
+
import ReactMarkdown from 'react-markdown';
|
|
35
|
+
import remarkGfm from 'remark-gfm';
|
|
36
|
+
import rehypeHighlight from 'rehype-highlight';
|
|
37
|
+
import rehypeSlug from 'rehype-slug';
|
|
38
|
+
import { MermaidDiagram } from '@/components/mermaid-diagram';
|
|
39
|
+
|
|
40
|
+
interface ContextFileViewerProps {
|
|
41
|
+
name: string;
|
|
42
|
+
path: string;
|
|
43
|
+
content: string;
|
|
44
|
+
tokenCount: number;
|
|
45
|
+
lastModified: Date | string;
|
|
46
|
+
isExpanded?: boolean;
|
|
47
|
+
onToggle?: () => void;
|
|
48
|
+
className?: string;
|
|
49
|
+
searchQuery?: string;
|
|
50
|
+
projectRoot?: string;
|
|
51
|
+
onViewDetail?: () => void;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Get icon for file based on name
|
|
56
|
+
*/
|
|
57
|
+
function getFileIcon(fileName: string): React.ComponentType<{ className?: string }> {
|
|
58
|
+
const name = fileName.toLowerCase();
|
|
59
|
+
|
|
60
|
+
// Agent instruction files
|
|
61
|
+
if (name === 'agents.md' || name.includes('agent')) return Bot;
|
|
62
|
+
if (name === 'gemini.md') return Sparkles;
|
|
63
|
+
if (name === 'claude.md') return Bot;
|
|
64
|
+
if (name === 'copilot.md' || name === 'copilot-instructions.md') return Bot;
|
|
65
|
+
|
|
66
|
+
// Project documentation
|
|
67
|
+
if (name === 'readme.md') return BookOpen;
|
|
68
|
+
if (name === 'contributing.md') return Users;
|
|
69
|
+
if (name === 'changelog.md') return History;
|
|
70
|
+
if (name === 'license.md' || name === 'license') return Scale;
|
|
71
|
+
if (name === 'security.md') return Shield;
|
|
72
|
+
|
|
73
|
+
// Config files
|
|
74
|
+
if (name.endsWith('.json')) return Settings;
|
|
75
|
+
if (name === 'config.json') return Settings;
|
|
76
|
+
|
|
77
|
+
// Code-related
|
|
78
|
+
if (name.includes('api') || name.includes('spec')) return FileCode;
|
|
79
|
+
|
|
80
|
+
// Default
|
|
81
|
+
return ScrollText;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Get icon color class for file based on name
|
|
86
|
+
*/
|
|
87
|
+
function getFileIconColor(fileName: string): string {
|
|
88
|
+
const name = fileName.toLowerCase();
|
|
89
|
+
|
|
90
|
+
if (name === 'agents.md' || name.includes('agent')) return 'text-purple-500';
|
|
91
|
+
if (name === 'gemini.md') return 'text-blue-500';
|
|
92
|
+
if (name === 'claude.md') return 'text-orange-500';
|
|
93
|
+
if (name === 'copilot.md' || name === 'copilot-instructions.md') return 'text-sky-500';
|
|
94
|
+
if (name === 'readme.md') return 'text-green-500';
|
|
95
|
+
if (name === 'contributing.md') return 'text-pink-500';
|
|
96
|
+
if (name === 'changelog.md') return 'text-amber-500';
|
|
97
|
+
if (name === 'license.md' || name === 'license') return 'text-slate-500';
|
|
98
|
+
if (name === 'security.md') return 'text-red-500';
|
|
99
|
+
if (name.endsWith('.json')) return 'text-yellow-500';
|
|
100
|
+
|
|
101
|
+
return 'text-muted-foreground';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Get token count color based on thresholds
|
|
106
|
+
*/
|
|
107
|
+
function getTokenColor(tokens: number): string {
|
|
108
|
+
if (tokens < 2000) return 'text-green-600 dark:text-green-400';
|
|
109
|
+
if (tokens < 3500) return 'text-blue-600 dark:text-blue-400';
|
|
110
|
+
if (tokens < 5000) return 'text-yellow-600 dark:text-yellow-400';
|
|
111
|
+
return 'text-red-600 dark:text-red-400';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Get token status label
|
|
116
|
+
*/
|
|
117
|
+
function getTokenStatus(tokens: number): string {
|
|
118
|
+
if (tokens < 2000) return 'Optimal';
|
|
119
|
+
if (tokens < 3500) return 'Good';
|
|
120
|
+
if (tokens < 5000) return 'Large';
|
|
121
|
+
return 'Very Large';
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Format date for display
|
|
126
|
+
*/
|
|
127
|
+
function formatDate(date: Date | string): string {
|
|
128
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
129
|
+
return d.toLocaleDateString('en-US', {
|
|
130
|
+
year: 'numeric',
|
|
131
|
+
month: 'short',
|
|
132
|
+
day: 'numeric',
|
|
133
|
+
hour: '2-digit',
|
|
134
|
+
minute: '2-digit',
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Highlight search matches in text
|
|
140
|
+
*/
|
|
141
|
+
function highlightMatches(text: string, query: string): React.ReactNode {
|
|
142
|
+
if (!query || query.length < 2) return text;
|
|
143
|
+
|
|
144
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
145
|
+
const regex = new RegExp(`(${escapedQuery})`, 'gi');
|
|
146
|
+
const parts = text.split(regex);
|
|
147
|
+
|
|
148
|
+
return parts.map((part, i) =>
|
|
149
|
+
regex.test(part) ? (
|
|
150
|
+
<mark key={i} className="bg-yellow-200 dark:bg-yellow-800 rounded px-0.5">
|
|
151
|
+
{part}
|
|
152
|
+
</mark>
|
|
153
|
+
) : (
|
|
154
|
+
part
|
|
155
|
+
)
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Count search matches in content
|
|
161
|
+
*/
|
|
162
|
+
export function countMatches(content: string, query: string): number {
|
|
163
|
+
if (!query || query.length < 2) return 0;
|
|
164
|
+
const escapedQuery = query.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
165
|
+
const matches = content.match(new RegExp(escapedQuery, 'gi'));
|
|
166
|
+
return matches ? matches.length : 0;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Generate VS Code URI to open file
|
|
171
|
+
*/
|
|
172
|
+
function getVSCodeUri(projectRoot: string, filePath: string): string {
|
|
173
|
+
const fullPath = filePath.startsWith('/') ? filePath : `${projectRoot}/${filePath}`;
|
|
174
|
+
return `vscode://file${fullPath}`;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
export function ContextFileViewer({
|
|
178
|
+
name,
|
|
179
|
+
path,
|
|
180
|
+
content,
|
|
181
|
+
tokenCount,
|
|
182
|
+
lastModified,
|
|
183
|
+
isExpanded = false,
|
|
184
|
+
onToggle,
|
|
185
|
+
className,
|
|
186
|
+
searchQuery,
|
|
187
|
+
projectRoot,
|
|
188
|
+
onViewDetail,
|
|
189
|
+
}: ContextFileViewerProps) {
|
|
190
|
+
const [copied, setCopied] = React.useState(false);
|
|
191
|
+
const matchCount = searchQuery ? countMatches(content, searchQuery) : 0;
|
|
192
|
+
|
|
193
|
+
const handleCopy = async (e: React.MouseEvent) => {
|
|
194
|
+
e.stopPropagation();
|
|
195
|
+
try {
|
|
196
|
+
await navigator.clipboard.writeText(content);
|
|
197
|
+
setCopied(true);
|
|
198
|
+
setTimeout(() => setCopied(false), 2000);
|
|
199
|
+
} catch (error) {
|
|
200
|
+
console.error('Failed to copy content:', error);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
const handleOpenInEditor = (e: React.MouseEvent) => {
|
|
205
|
+
e.stopPropagation();
|
|
206
|
+
if (projectRoot) {
|
|
207
|
+
window.open(getVSCodeUri(projectRoot, path), '_blank');
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const handleViewDetail = (e: React.MouseEvent) => {
|
|
212
|
+
e.stopPropagation();
|
|
213
|
+
onViewDetail?.();
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const isJson = name.endsWith('.json');
|
|
217
|
+
const isMarkdown = name.endsWith('.md');
|
|
218
|
+
|
|
219
|
+
const FileIcon = getFileIcon(name);
|
|
220
|
+
const iconColor = getFileIconColor(name);
|
|
221
|
+
|
|
222
|
+
// For non-markdown content with search, highlight matches
|
|
223
|
+
const renderPlainContent = (text: string) => {
|
|
224
|
+
if (searchQuery && searchQuery.length >= 2) {
|
|
225
|
+
return highlightMatches(text, searchQuery);
|
|
226
|
+
}
|
|
227
|
+
return text;
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
return (
|
|
231
|
+
<Card className={cn('overflow-hidden', className)}>
|
|
232
|
+
<CardHeader
|
|
233
|
+
className={cn(
|
|
234
|
+
'cursor-pointer hover:bg-muted/50 transition-colors py-3 px-4',
|
|
235
|
+
isExpanded && 'border-b'
|
|
236
|
+
)}
|
|
237
|
+
onClick={onToggle}
|
|
238
|
+
>
|
|
239
|
+
<div className="flex items-center justify-between gap-2">
|
|
240
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
241
|
+
<FileIcon className={cn('h-4 w-4 shrink-0', iconColor)} />
|
|
242
|
+
<div className="min-w-0">
|
|
243
|
+
<CardTitle className="text-sm font-medium truncate">{name}</CardTitle>
|
|
244
|
+
<p className="text-xs text-muted-foreground truncate">{path}</p>
|
|
245
|
+
</div>
|
|
246
|
+
</div>
|
|
247
|
+
<div className="flex items-center gap-2 shrink-0">
|
|
248
|
+
{searchQuery && matchCount > 0 && (
|
|
249
|
+
<Badge variant="secondary" className="text-xs bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200">
|
|
250
|
+
{matchCount} match{matchCount !== 1 ? 'es' : ''}
|
|
251
|
+
</Badge>
|
|
252
|
+
)}
|
|
253
|
+
<Badge variant="outline" className={cn('text-xs', getTokenColor(tokenCount))}>
|
|
254
|
+
<Coins className="h-3 w-3 mr-1" />
|
|
255
|
+
{tokenCount.toLocaleString()} tokens
|
|
256
|
+
</Badge>
|
|
257
|
+
<span className="text-xs text-muted-foreground hidden sm:inline">
|
|
258
|
+
{getTokenStatus(tokenCount)}
|
|
259
|
+
</span>
|
|
260
|
+
{onViewDetail && (
|
|
261
|
+
<Button
|
|
262
|
+
variant="ghost"
|
|
263
|
+
size="sm"
|
|
264
|
+
className="h-7 w-7 p-0"
|
|
265
|
+
onClick={handleViewDetail}
|
|
266
|
+
title="View full page"
|
|
267
|
+
>
|
|
268
|
+
<Maximize2 className="h-3.5 w-3.5" />
|
|
269
|
+
</Button>
|
|
270
|
+
)}
|
|
271
|
+
{projectRoot && (
|
|
272
|
+
<Button
|
|
273
|
+
variant="ghost"
|
|
274
|
+
size="sm"
|
|
275
|
+
className="h-7 w-7 p-0"
|
|
276
|
+
onClick={handleOpenInEditor}
|
|
277
|
+
title="Open in VS Code"
|
|
278
|
+
>
|
|
279
|
+
<ExternalLink className="h-3.5 w-3.5" />
|
|
280
|
+
</Button>
|
|
281
|
+
)}
|
|
282
|
+
<Button
|
|
283
|
+
variant="ghost"
|
|
284
|
+
size="sm"
|
|
285
|
+
className="h-7 w-7 p-0"
|
|
286
|
+
onClick={handleCopy}
|
|
287
|
+
title="Copy content"
|
|
288
|
+
>
|
|
289
|
+
{copied ? (
|
|
290
|
+
<Check className="h-3.5 w-3.5 text-green-600" />
|
|
291
|
+
) : (
|
|
292
|
+
<Copy className="h-3.5 w-3.5" />
|
|
293
|
+
)}
|
|
294
|
+
</Button>
|
|
295
|
+
</div>
|
|
296
|
+
</div>
|
|
297
|
+
</CardHeader>
|
|
298
|
+
|
|
299
|
+
{isExpanded && (
|
|
300
|
+
<CardContent className="p-0">
|
|
301
|
+
{/* Metadata bar */}
|
|
302
|
+
<div className="flex items-center gap-4 px-4 py-2 bg-muted/30 text-xs text-muted-foreground border-b">
|
|
303
|
+
<span className="flex items-center gap-1">
|
|
304
|
+
<Clock className="h-3 w-3" />
|
|
305
|
+
Modified {formatDate(lastModified)}
|
|
306
|
+
</span>
|
|
307
|
+
<span>{content.split('\n').length} lines</span>
|
|
308
|
+
</div>
|
|
309
|
+
|
|
310
|
+
{/* Content area */}
|
|
311
|
+
<div className="max-h-[500px] overflow-auto">
|
|
312
|
+
{isJson ? (
|
|
313
|
+
<pre className="p-4 text-sm overflow-x-auto bg-muted/20 whitespace-pre-wrap">
|
|
314
|
+
{renderPlainContent(JSON.stringify(JSON.parse(content), null, 2))}
|
|
315
|
+
</pre>
|
|
316
|
+
) : isMarkdown ? (
|
|
317
|
+
searchQuery && searchQuery.length >= 2 ? (
|
|
318
|
+
// Render as plain text with highlighting when searching
|
|
319
|
+
<pre className="p-4 text-sm overflow-x-auto bg-muted/20 whitespace-pre-wrap font-mono">
|
|
320
|
+
{renderPlainContent(content)}
|
|
321
|
+
</pre>
|
|
322
|
+
) : (
|
|
323
|
+
<article className="prose prose-slate dark:prose-invert max-w-none prose-sm p-4">
|
|
324
|
+
<ReactMarkdown
|
|
325
|
+
remarkPlugins={[remarkGfm]}
|
|
326
|
+
rehypePlugins={[rehypeHighlight]}
|
|
327
|
+
>
|
|
328
|
+
{content}
|
|
329
|
+
</ReactMarkdown>
|
|
330
|
+
</article>
|
|
331
|
+
)
|
|
332
|
+
) : (
|
|
333
|
+
<pre className="p-4 text-sm overflow-x-auto bg-muted/20 whitespace-pre-wrap">
|
|
334
|
+
{renderPlainContent(content)}
|
|
335
|
+
</pre>
|
|
336
|
+
)}
|
|
337
|
+
</div>
|
|
338
|
+
</CardContent>
|
|
339
|
+
)}
|
|
340
|
+
</Card>
|
|
341
|
+
);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
/**
|
|
345
|
+
* Compact version for listing in accordion
|
|
346
|
+
*/
|
|
347
|
+
export function ContextFileCard({
|
|
348
|
+
name,
|
|
349
|
+
path,
|
|
350
|
+
content,
|
|
351
|
+
tokenCount,
|
|
352
|
+
lastModified,
|
|
353
|
+
className,
|
|
354
|
+
searchQuery,
|
|
355
|
+
projectRoot,
|
|
356
|
+
onViewDetail,
|
|
357
|
+
}: Omit<ContextFileViewerProps, 'isExpanded' | 'onToggle'>) {
|
|
358
|
+
const [expanded, setExpanded] = React.useState(false);
|
|
359
|
+
|
|
360
|
+
// Auto-expand when there are search matches
|
|
361
|
+
React.useEffect(() => {
|
|
362
|
+
if (searchQuery && searchQuery.length >= 2) {
|
|
363
|
+
const matches = countMatches(content, searchQuery);
|
|
364
|
+
if (matches > 0) {
|
|
365
|
+
setExpanded(true);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}, [searchQuery, content]);
|
|
369
|
+
|
|
370
|
+
return (
|
|
371
|
+
<ContextFileViewer
|
|
372
|
+
name={name}
|
|
373
|
+
path={path}
|
|
374
|
+
content={content}
|
|
375
|
+
tokenCount={tokenCount}
|
|
376
|
+
lastModified={lastModified}
|
|
377
|
+
isExpanded={expanded}
|
|
378
|
+
onToggle={() => setExpanded(!expanded)}
|
|
379
|
+
className={className}
|
|
380
|
+
searchQuery={searchQuery}
|
|
381
|
+
projectRoot={projectRoot}
|
|
382
|
+
onViewDetail={onViewDetail}
|
|
383
|
+
/>
|
|
384
|
+
);
|
|
385
|
+
}
|