@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.
Files changed (262) hide show
  1. package/.next/standalone/packages/ui/.next/BUILD_ID +1 -1
  2. package/.next/standalone/packages/ui/.next/app-path-routes-manifest.json +4 -0
  3. package/.next/standalone/packages/ui/.next/build-manifest.json +4 -4
  4. package/.next/standalone/packages/ui/.next/prerender-manifest.json +3 -3
  5. package/.next/standalone/packages/ui/.next/routes-manifest.json +24 -0
  6. package/.next/standalone/packages/ui/.next/server/app/_global-error/page/build-manifest.json +2 -2
  7. package/.next/standalone/packages/ui/.next/server/app/_global-error/page.js.nft.json +1 -1
  8. package/.next/standalone/packages/ui/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  9. package/.next/standalone/packages/ui/.next/server/app/_global-error.html +2 -2
  10. package/.next/standalone/packages/ui/.next/server/app/_global-error.rsc +1 -1
  11. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  12. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  13. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  14. package/.next/standalone/packages/ui/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  15. package/.next/standalone/packages/ui/.next/server/app/_not-found/page/build-manifest.json +2 -2
  16. package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js +1 -1
  17. package/.next/standalone/packages/ui/.next/server/app/_not-found/page.js.nft.json +1 -1
  18. package/.next/standalone/packages/ui/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/standalone/packages/ui/.next/server/app/_not-found.html +2 -2
  20. package/.next/standalone/packages/ui/.next/server/app/_not-found.rsc +19 -19
  21. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_full.segment.rsc +19 -19
  22. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_index.segment.rsc +11 -11
  23. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +3 -3
  24. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_not-found.segment.rsc +3 -3
  25. package/.next/standalone/packages/ui/.next/server/app/_not-found.segments/_tree.segment.rsc +6 -6
  26. package/.next/standalone/packages/ui/.next/server/app/api/context/route/app-paths-manifest.json +3 -0
  27. package/.next/standalone/packages/ui/.next/server/app/api/context/route/build-manifest.json +11 -0
  28. package/.next/standalone/packages/ui/.next/server/app/api/context/route/server-reference-manifest.json +4 -0
  29. package/.next/standalone/packages/ui/.next/server/app/api/context/route.js +8 -0
  30. package/.next/standalone/packages/ui/.next/server/app/api/context/route.js.map +5 -0
  31. package/.next/standalone/packages/ui/.next/server/app/api/context/route.js.nft.json +1 -0
  32. package/.next/standalone/packages/ui/.next/server/app/api/context/route_client-reference-manifest.js +2 -0
  33. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/app-paths-manifest.json +3 -0
  34. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/build-manifest.json +11 -0
  35. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route/server-reference-manifest.json +4 -0
  36. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js +8 -0
  37. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js.map +5 -0
  38. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route.js.nft.json +1 -0
  39. package/.next/standalone/packages/ui/.next/server/app/api/dependencies/route_client-reference-manifest.js +2 -0
  40. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/[id]/route.js.nft.json +1 -1
  41. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/discover/route.js.nft.json +1 -1
  42. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/list-directory/route.js.nft.json +1 -1
  43. package/.next/standalone/packages/ui/.next/server/app/api/local-projects/route.js.nft.json +1 -1
  44. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/route.js.nft.json +1 -1
  45. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/route.js.nft.json +1 -1
  46. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/[spec]/status/route.js.nft.json +1 -1
  47. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/specs/route.js.nft.json +1 -1
  48. package/.next/standalone/packages/ui/.next/server/app/api/projects/[id]/stats/route.js.nft.json +1 -1
  49. package/.next/standalone/packages/ui/.next/server/app/api/projects/route.js.nft.json +1 -1
  50. package/.next/standalone/packages/ui/.next/server/app/api/revalidate/route.js.nft.json +1 -1
  51. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/dependency-graph/route.js.nft.json +1 -1
  52. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/route.js.nft.json +1 -1
  53. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/status/route.js.nft.json +1 -1
  54. package/.next/standalone/packages/ui/.next/server/app/api/specs/[id]/subspecs/[file]/route.js.nft.json +1 -1
  55. package/.next/standalone/packages/ui/.next/server/app/api/stats/route.js.nft.json +1 -1
  56. package/.next/standalone/packages/ui/.next/server/app/context/page/app-paths-manifest.json +3 -0
  57. package/.next/standalone/packages/ui/.next/server/app/context/page/build-manifest.json +18 -0
  58. package/.next/standalone/packages/ui/.next/server/app/context/page/next-font-manifest.json +6 -0
  59. package/.next/standalone/packages/ui/.next/server/app/context/page/react-loadable-manifest.json +1 -0
  60. package/.next/standalone/packages/ui/.next/server/app/context/page/server-reference-manifest.json +4 -0
  61. package/.next/standalone/packages/ui/.next/server/app/context/page.js +19 -0
  62. package/.next/standalone/packages/ui/.next/server/app/context/page.js.map +5 -0
  63. package/.next/standalone/packages/ui/.next/server/app/context/page.js.nft.json +1 -0
  64. package/.next/standalone/packages/ui/.next/server/app/context/page_client-reference-manifest.js +2 -0
  65. package/.next/standalone/packages/ui/.next/server/app/dependencies/page/app-paths-manifest.json +3 -0
  66. package/.next/standalone/packages/ui/.next/server/app/dependencies/page/build-manifest.json +18 -0
  67. package/.next/standalone/packages/ui/.next/server/app/dependencies/page/next-font-manifest.json +6 -0
  68. package/.next/standalone/packages/ui/.next/server/app/dependencies/page/react-loadable-manifest.json +1 -0
  69. package/.next/standalone/packages/ui/.next/server/app/dependencies/page/server-reference-manifest.json +4 -0
  70. package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js +19 -0
  71. package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js.map +5 -0
  72. package/.next/standalone/packages/ui/.next/server/app/dependencies/page.js.nft.json +1 -0
  73. package/.next/standalone/packages/ui/.next/server/app/dependencies/page_client-reference-manifest.js +2 -0
  74. package/.next/standalone/packages/ui/.next/server/app/page/build-manifest.json +2 -2
  75. package/.next/standalone/packages/ui/.next/server/app/page.js +1 -1
  76. package/.next/standalone/packages/ui/.next/server/app/page.js.nft.json +1 -1
  77. package/.next/standalone/packages/ui/.next/server/app/page_client-reference-manifest.js +1 -1
  78. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page/build-manifest.json +2 -2
  79. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js +1 -1
  80. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page.js.nft.json +1 -1
  81. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/page_client-reference-manifest.js +1 -1
  82. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page/build-manifest.json +2 -2
  83. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js +1 -1
  84. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page.js.nft.json +1 -1
  85. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/[specId]/page_client-reference-manifest.js +1 -1
  86. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page/build-manifest.json +2 -2
  87. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js +1 -1
  88. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page.js.nft.json +1 -1
  89. package/.next/standalone/packages/ui/.next/server/app/projects/[projectId]/specs/page_client-reference-manifest.js +1 -1
  90. package/.next/standalone/packages/ui/.next/server/app/projects/page/build-manifest.json +2 -2
  91. package/.next/standalone/packages/ui/.next/server/app/projects/page.js +1 -1
  92. package/.next/standalone/packages/ui/.next/server/app/projects/page.js.nft.json +1 -1
  93. package/.next/standalone/packages/ui/.next/server/app/projects/page_client-reference-manifest.js +1 -1
  94. package/.next/standalone/packages/ui/.next/server/app/projects.html +2 -2
  95. package/.next/standalone/packages/ui/.next/server/app/projects.rsc +24 -23
  96. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_full.segment.rsc +24 -23
  97. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_index.segment.rsc +9 -9
  98. package/.next/standalone/packages/ui/.next/server/app/projects.segments/_tree.segment.rsc +3 -3
  99. package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects/__PAGE__.segment.rsc +2 -2
  100. package/.next/standalone/packages/ui/.next/server/app/projects.segments/projects.segment.rsc +1 -1
  101. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page/build-manifest.json +2 -2
  102. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js +1 -1
  103. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page.js.nft.json +1 -1
  104. package/.next/standalone/packages/ui/.next/server/app/specs/[id]/page_client-reference-manifest.js +1 -1
  105. package/.next/standalone/packages/ui/.next/server/app/specs/page/build-manifest.json +2 -2
  106. package/.next/standalone/packages/ui/.next/server/app/specs/page.js +1 -1
  107. package/.next/standalone/packages/ui/.next/server/app/specs/page.js.nft.json +1 -1
  108. package/.next/standalone/packages/ui/.next/server/app/specs/page_client-reference-manifest.js +1 -1
  109. package/.next/standalone/packages/ui/.next/server/app/stats/page/build-manifest.json +2 -2
  110. package/.next/standalone/packages/ui/.next/server/app/stats/page.js +1 -1
  111. package/.next/standalone/packages/ui/.next/server/app/stats/page.js.nft.json +1 -1
  112. package/.next/standalone/packages/ui/.next/server/app/stats/page_client-reference-manifest.js +1 -1
  113. package/.next/standalone/packages/ui/.next/server/app-paths-manifest.json +4 -0
  114. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__3559376c._.js +2 -2
  115. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__65667b70._.js +1 -1
  116. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__803d07f0._.js +1 -1
  117. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__84cdc14a._.js +1 -1
  118. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__8a9ab1a3._.js +3 -0
  119. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__b0969111._.js +3 -0
  120. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__bdc3963a._.js +1 -1
  121. package/.next/standalone/packages/ui/.next/server/chunks/[root-of-the-server]__f5c6d6b8._.js +1 -1
  122. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_context_route_actions_dead6daa.js +3 -0
  123. package/.next/standalone/packages/ui/.next/server/chunks/packages_ui__next-internal_server_app_api_dependencies_route_actions_cf6b14c3.js +3 -0
  124. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__12b4eb41._.js +3 -0
  125. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__3c77d95b._.js +3 -0
  126. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__44b603f9._.js +3 -0
  127. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__69a0d63a._.js +3 -0
  128. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__daee3355._.js → [root-of-the-server]__8608a6fa._.js} +2 -2
  129. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__fd80e4dd._.js → [root-of-the-server]__a965a67b._.js} +2 -2
  130. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__daedd80e._.js +3 -0
  131. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__dc176c61._.js +3 -0
  132. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{[root-of-the-server]__5382b397._.js → [root-of-the-server]__ead1539c._.js} +2 -2
  133. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_000dd317._.js +1 -1
  134. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_03aa8d19._.js +7 -0
  135. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_28fe1532._.js +5 -0
  136. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_8cec504f._.js +3 -0
  137. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_959ad3d8._.js +4 -0
  138. package/.next/standalone/packages/ui/.next/server/chunks/ssr/{_22274047._.js → _adb9d7cb._.js} +2 -2
  139. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_da18b655._.js +3 -0
  140. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_ea899b87._.js +3 -0
  141. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_f38e75b7._.js +4 -0
  142. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_fe120f8a._.js +3 -0
  143. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_015f83ca._.js +3 -0
  144. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_1120b57c._.js +3 -0
  145. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_151891de._.js +3 -0
  146. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_1c916fe3._.js +3 -0
  147. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_80605a06._.js +3 -0
  148. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_bb80de48._.js +3 -0
  149. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_e8075f9b._.js +3 -0
  150. package/.next/standalone/packages/ui/.next/server/chunks/ssr/node_modules__pnpm_f919ef4a._.js +3 -0
  151. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_context_page_actions_1a062d48.js +3 -0
  152. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui__next-internal_server_app_dependencies_page_actions_57387d47.js +3 -0
  153. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_context_context-client_tsx_4ba99a62._.js +12 -0
  154. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_dependencies_dependencies-client_tsx_0e82443a._.js +4 -0
  155. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_app_specs_specs-client_tsx_0bb8f8f8._.js +1 -1
  156. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_components_spec-detail-wrapper_tsx_fd35401c._.js +3 -0
  157. package/.next/standalone/packages/ui/.next/server/chunks/ssr/packages_ui_src_components_specs-nav-sidebar_tsx_8237ed13._.js +1 -1
  158. package/.next/standalone/packages/ui/.next/server/middleware-build-manifest.js +2 -2
  159. package/.next/standalone/packages/ui/.next/server/pages/404.html +2 -2
  160. package/.next/standalone/packages/ui/.next/server/pages/500.html +2 -2
  161. package/.next/standalone/packages/ui/.next/server/server-reference-manifest.js +1 -1
  162. package/.next/standalone/packages/ui/.next/server/server-reference-manifest.json +1 -1
  163. package/.next/standalone/packages/ui/.next/static/chunks/094cf9f4e3553261.js +1 -0
  164. package/.next/standalone/packages/ui/.next/static/chunks/1c015eb9eaaf9f9c.js +1 -0
  165. package/.next/standalone/packages/ui/.next/static/chunks/1d36660b2877d213.js +3 -0
  166. package/.next/standalone/packages/ui/.next/static/chunks/46275d9d67603bf5.js +1 -0
  167. package/.next/standalone/packages/ui/.next/static/chunks/4d29ca0fa6843070.js +2 -0
  168. package/.next/standalone/packages/ui/.next/static/chunks/59854b15bf046467.js +1 -0
  169. package/.next/standalone/packages/ui/.next/static/chunks/{cca4441cde342ae3.js → 5fd8101e32b076b1.js} +1 -1
  170. package/.next/standalone/packages/ui/.next/static/chunks/6d938a49daa10208.js +2 -0
  171. package/.next/standalone/packages/ui/.next/static/chunks/979625373d5474b6.js +1 -0
  172. package/.next/standalone/packages/ui/.next/static/chunks/a33f10af6abef4df.js +10 -0
  173. package/.next/{static/chunks/794f3931f1ca12d2.js → standalone/packages/ui/.next/static/chunks/a3d7e1be47de010b.js} +1 -1
  174. package/.next/standalone/packages/ui/.next/static/chunks/a5e25b9fa6b88eee.js +1 -0
  175. package/.next/standalone/packages/ui/.next/static/chunks/b53250480fde6816.js +1 -0
  176. package/.next/standalone/packages/ui/.next/static/chunks/b7f19087afe1d2c9.css +1 -0
  177. package/.next/standalone/packages/ui/.next/static/chunks/c658e22a605e0ed1.js +1 -0
  178. package/.next/standalone/packages/ui/.next/static/chunks/c92660d8d0c4763d.js +1 -0
  179. package/.next/standalone/packages/ui/.next/static/chunks/ddd87cd0d26bc2f5.js +1 -0
  180. package/.next/standalone/packages/ui/.next/static/chunks/eeec245955b3b600.js +5 -0
  181. package/.next/standalone/packages/ui/.next/static/chunks/ff11efb770d5a0bc.js +1 -0
  182. package/.next/{static/chunks/turbopack-5fa55215af0efb15.js → standalone/packages/ui/.next/static/chunks/turbopack-261c5dcdd873f310.js} +1 -1
  183. package/.next/standalone/packages/ui/package.json +5 -1
  184. package/.next/standalone/packages/ui/src/app/api/context/route.ts +22 -0
  185. package/.next/standalone/packages/ui/src/app/api/dependencies/route.ts +81 -0
  186. package/.next/standalone/packages/ui/src/app/context/context-client.tsx +393 -0
  187. package/.next/standalone/packages/ui/src/app/context/page.tsx +17 -0
  188. package/.next/standalone/packages/ui/src/app/dependencies/constants.ts +24 -0
  189. package/.next/standalone/packages/ui/src/app/dependencies/dependencies-client.tsx +625 -0
  190. package/.next/standalone/packages/ui/src/app/dependencies/index.ts +19 -0
  191. package/.next/standalone/packages/ui/src/app/dependencies/page.tsx +38 -0
  192. package/.next/standalone/packages/ui/src/app/dependencies/spec-node.tsx +80 -0
  193. package/.next/standalone/packages/ui/src/app/dependencies/spec-sidebar.tsx +198 -0
  194. package/.next/standalone/packages/ui/src/app/dependencies/types.ts +37 -0
  195. package/.next/standalone/packages/ui/src/app/dependencies/utils.ts +194 -0
  196. package/.next/standalone/packages/ui/src/app/globals.css +16 -16
  197. package/.next/standalone/packages/ui/src/app/layout.tsx +4 -7
  198. package/.next/standalone/packages/ui/src/components/context-file-detail.tsx +308 -0
  199. package/.next/standalone/packages/ui/src/components/context-file-viewer.tsx +385 -0
  200. package/.next/standalone/packages/ui/src/components/main-sidebar.tsx +19 -1
  201. package/.next/standalone/packages/ui/src/components/spec-detail-client.tsx +181 -134
  202. package/.next/standalone/packages/ui/src/components/spec-detail-wrapper.tsx +20 -0
  203. package/.next/standalone/packages/ui/src/components/specs-nav-sidebar.tsx +3 -2
  204. package/.next/standalone/packages/ui/src/components/ui/accordion.tsx +58 -0
  205. package/.next/standalone/packages/ui/src/lib/db/service-queries.ts +172 -3
  206. package/.next/standalone/packages/ui/src/lib/specs/types.ts +44 -0
  207. package/.next/standalone/packages/ui/tsconfig.tsbuildinfo +1 -1
  208. package/.next/static/chunks/094cf9f4e3553261.js +1 -0
  209. package/.next/static/chunks/1c015eb9eaaf9f9c.js +1 -0
  210. package/.next/static/chunks/1d36660b2877d213.js +3 -0
  211. package/.next/static/chunks/46275d9d67603bf5.js +1 -0
  212. package/.next/static/chunks/4d29ca0fa6843070.js +2 -0
  213. package/.next/static/chunks/59854b15bf046467.js +1 -0
  214. package/.next/static/chunks/{cca4441cde342ae3.js → 5fd8101e32b076b1.js} +1 -1
  215. package/.next/static/chunks/6d938a49daa10208.js +2 -0
  216. package/.next/static/chunks/979625373d5474b6.js +1 -0
  217. package/.next/static/chunks/a33f10af6abef4df.js +10 -0
  218. package/.next/{standalone/packages/ui/.next/static/chunks/794f3931f1ca12d2.js → static/chunks/a3d7e1be47de010b.js} +1 -1
  219. package/.next/static/chunks/a5e25b9fa6b88eee.js +1 -0
  220. package/.next/static/chunks/b53250480fde6816.js +1 -0
  221. package/.next/static/chunks/b7f19087afe1d2c9.css +1 -0
  222. package/.next/static/chunks/c658e22a605e0ed1.js +1 -0
  223. package/.next/static/chunks/c92660d8d0c4763d.js +1 -0
  224. package/.next/static/chunks/ddd87cd0d26bc2f5.js +1 -0
  225. package/.next/static/chunks/eeec245955b3b600.js +5 -0
  226. package/.next/static/chunks/ff11efb770d5a0bc.js +1 -0
  227. package/.next/{standalone/packages/ui/.next/static/chunks/turbopack-5fa55215af0efb15.js → static/chunks/turbopack-261c5dcdd873f310.js} +1 -1
  228. package/package.json +6 -2
  229. package/.next/standalone/node_modules/.pnpm/source-map@0.8.0-beta.0/node_modules/source-map/package.json +0 -95
  230. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__1d0c2012._.js +0 -3
  231. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__73f60f12._.js +0 -7
  232. package/.next/standalone/packages/ui/.next/server/chunks/ssr/[root-of-the-server]__a7ae8552._.js +0 -7
  233. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_0f9ffe32._.js +0 -3
  234. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_14118969._.js +0 -3
  235. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_4129cc0f._.js +0 -3
  236. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_497c8b73._.js +0 -3
  237. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_ac867463._.js +0 -3
  238. package/.next/standalone/packages/ui/.next/server/chunks/ssr/_c2f54661._.js +0 -5
  239. package/.next/standalone/packages/ui/.next/static/chunks/16ff9833ae1bb3ae.js +0 -1
  240. package/.next/standalone/packages/ui/.next/static/chunks/2204c0f16b23ec4c.js +0 -3
  241. package/.next/standalone/packages/ui/.next/static/chunks/294dea6dbec43ca6.js +0 -1
  242. package/.next/standalone/packages/ui/.next/static/chunks/7590e65bcaa41e8b.js +0 -1
  243. package/.next/standalone/packages/ui/.next/static/chunks/b6976cf6c48996e5.js +0 -1
  244. package/.next/standalone/packages/ui/.next/static/chunks/b8353eb8c6fb895e.js +0 -1
  245. package/.next/standalone/packages/ui/.next/static/chunks/b845813463167db0.js +0 -5
  246. package/.next/standalone/packages/ui/.next/static/chunks/bd9893e28f8f6a9a.css +0 -1
  247. package/.next/standalone/packages/ui/.next/static/chunks/d784d84d5b880e48.js +0 -1
  248. package/.next/static/chunks/16ff9833ae1bb3ae.js +0 -1
  249. package/.next/static/chunks/2204c0f16b23ec4c.js +0 -3
  250. package/.next/static/chunks/294dea6dbec43ca6.js +0 -1
  251. package/.next/static/chunks/7590e65bcaa41e8b.js +0 -1
  252. package/.next/static/chunks/b6976cf6c48996e5.js +0 -1
  253. package/.next/static/chunks/b8353eb8c6fb895e.js +0 -1
  254. package/.next/static/chunks/b845813463167db0.js +0 -5
  255. package/.next/static/chunks/bd9893e28f8f6a9a.css +0 -1
  256. package/.next/static/chunks/d784d84d5b880e48.js +0 -1
  257. /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_buildManifest.js +0 -0
  258. /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_clientMiddlewareManifest.json +0 -0
  259. /package/.next/standalone/packages/ui/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_ssgManifest.js +0 -0
  260. /package/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_buildManifest.js +0 -0
  261. /package/.next/static/{6nq7WNafPv5Bf_7mWgRQ- → 8PwFWS-09QjFSbDbfp15t}/_clientMiddlewareManifest.json +0 -0
  262. /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
+ }