@shepai/cli 1.147.0 → 1.148.0-pr462.42531fe

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 (228) hide show
  1. package/apis/json-schema/Attachment.yaml +3 -0
  2. package/dist/packages/core/src/domain/generated/output.d.ts +4 -0
  3. package/dist/packages/core/src/domain/generated/output.d.ts.map +1 -1
  4. package/dist/src/presentation/web/app/actions/compose-user-input.d.ts +1 -0
  5. package/dist/src/presentation/web/app/actions/compose-user-input.d.ts.map +1 -1
  6. package/dist/src/presentation/web/app/actions/compose-user-input.js +3 -1
  7. package/dist/src/presentation/web/app/actions/create-feature.d.ts +1 -0
  8. package/dist/src/presentation/web/app/actions/create-feature.d.ts.map +1 -1
  9. package/dist/src/presentation/web/app/api/agent-events/route.js +1 -1
  10. package/dist/src/presentation/web/app/api/sessions/route.d.ts.map +1 -1
  11. package/dist/src/presentation/web/app/api/sessions/route.js +2 -268
  12. package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts +17 -0
  13. package/dist/src/presentation/web/app/api/sessions-batch/route.d.ts.map +1 -0
  14. package/dist/src/presentation/web/app/api/sessions-batch/route.js +61 -0
  15. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.d.ts +5 -1
  16. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.d.ts.map +1 -1
  17. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.js +6 -3
  18. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.stories.d.ts +2 -0
  19. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.stories.d.ts.map +1 -1
  20. package/dist/src/presentation/web/components/common/attachment-chip/attachment-chip.stories.js +20 -0
  21. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.d.ts.map +1 -1
  22. package/dist/src/presentation/web/components/common/control-center-drawer/feature-drawer-client.js +5 -1
  23. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts +2 -0
  24. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.d.ts.map +1 -1
  25. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.d.ts.map +1 -1
  26. package/dist/src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.js +4 -1
  27. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts +2 -0
  28. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.d.ts.map +1 -1
  29. package/dist/src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.js +4 -1
  30. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts +1 -1
  31. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.d.ts.map +1 -1
  32. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.js +15 -73
  33. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.d.ts.map +1 -1
  34. package/dist/src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.js +18 -17
  35. package/dist/src/presentation/web/components/features/control-center/control-center.d.ts.map +1 -1
  36. package/dist/src/presentation/web/components/features/control-center/control-center.js +2 -1
  37. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.d.ts.map +1 -1
  38. package/dist/src/presentation/web/components/features/control-center/use-control-center-state.js +4 -1
  39. package/dist/src/presentation/web/hooks/sessions-provider.d.ts +12 -0
  40. package/dist/src/presentation/web/hooks/sessions-provider.d.ts.map +1 -0
  41. package/dist/src/presentation/web/hooks/sessions-provider.js +57 -0
  42. package/dist/src/presentation/web/hooks/use-deploy-action.d.ts.map +1 -1
  43. package/dist/src/presentation/web/hooks/use-deploy-action.js +8 -54
  44. package/dist/src/presentation/web/lib/session-scanner.d.ts +27 -0
  45. package/dist/src/presentation/web/lib/session-scanner.d.ts.map +1 -0
  46. package/dist/src/presentation/web/lib/session-scanner.js +255 -0
  47. package/dist/tsconfig.build.tsbuildinfo +1 -1
  48. package/package.json +1 -1
  49. package/web/.next/BUILD_ID +1 -1
  50. package/web/.next/app-path-routes-manifest.json +1 -0
  51. package/web/.next/build-manifest.json +2 -2
  52. package/web/.next/fallback-build-manifest.json +2 -2
  53. package/web/.next/prerender-manifest.json +3 -3
  54. package/web/.next/required-server-files.js +3 -3
  55. package/web/.next/required-server-files.json +3 -3
  56. package/web/.next/routes-manifest.json +6 -0
  57. package/web/.next/server/app/(dashboard)/@drawer/adopt/page/server-reference-manifest.json +28 -28
  58. package/web/.next/server/app/(dashboard)/@drawer/adopt/page.js.nft.json +1 -1
  59. package/web/.next/server/app/(dashboard)/@drawer/adopt/page_client-reference-manifest.js +1 -1
  60. package/web/.next/server/app/(dashboard)/@drawer/create/page/server-reference-manifest.json +28 -28
  61. package/web/.next/server/app/(dashboard)/@drawer/create/page.js.nft.json +1 -1
  62. package/web/.next/server/app/(dashboard)/@drawer/create/page_client-reference-manifest.js +1 -1
  63. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  64. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  65. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  66. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page/server-reference-manifest.json +36 -36
  67. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page.js.nft.json +1 -1
  68. package/web/.next/server/app/(dashboard)/@drawer/feature/[featureId]/page_client-reference-manifest.js +1 -1
  69. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  70. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page.js.nft.json +1 -1
  71. package/web/.next/server/app/(dashboard)/@drawer/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  72. package/web/.next/server/app/(dashboard)/create/page/server-reference-manifest.json +28 -28
  73. package/web/.next/server/app/(dashboard)/create/page.js.nft.json +1 -1
  74. package/web/.next/server/app/(dashboard)/create/page_client-reference-manifest.js +1 -1
  75. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page/server-reference-manifest.json +36 -36
  76. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page.js.nft.json +1 -1
  77. package/web/.next/server/app/(dashboard)/feature/[featureId]/[tab]/page_client-reference-manifest.js +1 -1
  78. package/web/.next/server/app/(dashboard)/feature/[featureId]/page/server-reference-manifest.json +36 -36
  79. package/web/.next/server/app/(dashboard)/feature/[featureId]/page.js.nft.json +1 -1
  80. package/web/.next/server/app/(dashboard)/feature/[featureId]/page_client-reference-manifest.js +1 -1
  81. package/web/.next/server/app/(dashboard)/page/server-reference-manifest.json +26 -26
  82. package/web/.next/server/app/(dashboard)/page.js.nft.json +1 -1
  83. package/web/.next/server/app/(dashboard)/page_client-reference-manifest.js +1 -1
  84. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page/server-reference-manifest.json +26 -26
  85. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page.js.nft.json +1 -1
  86. package/web/.next/server/app/(dashboard)/repository/[repositoryId]/page_client-reference-manifest.js +1 -1
  87. package/web/.next/server/app/_global-error.html +2 -2
  88. package/web/.next/server/app/_global-error.rsc +1 -1
  89. package/web/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
  90. package/web/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  91. package/web/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  92. package/web/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  93. package/web/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  94. package/web/.next/server/app/_not-found/page/server-reference-manifest.json +3 -3
  95. package/web/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  96. package/web/.next/server/app/api/attachments/preview/route.js.nft.json +1 -1
  97. package/web/.next/server/app/api/evidence/route.js.nft.json +1 -1
  98. package/web/.next/server/app/api/graph-data/route.js.nft.json +1 -1
  99. package/web/.next/server/app/api/sessions/route.js +2 -3
  100. package/web/.next/server/app/api/sessions/route.js.nft.json +1 -1
  101. package/web/.next/server/app/api/sessions-batch/route/app-paths-manifest.json +3 -0
  102. package/web/.next/server/app/api/sessions-batch/route/build-manifest.json +11 -0
  103. package/web/.next/server/app/api/sessions-batch/route/server-reference-manifest.json +4 -0
  104. package/web/.next/server/app/api/sessions-batch/route.js +7 -0
  105. package/web/.next/server/app/api/sessions-batch/route.js.map +5 -0
  106. package/web/.next/server/app/api/sessions-batch/route.js.nft.json +1 -0
  107. package/web/.next/server/app/api/sessions-batch/route_client-reference-manifest.js +2 -0
  108. package/web/.next/server/app/settings/page/server-reference-manifest.json +8 -8
  109. package/web/.next/server/app/settings/page.js.nft.json +1 -1
  110. package/web/.next/server/app/settings/page_client-reference-manifest.js +1 -1
  111. package/web/.next/server/app/skills/page/server-reference-manifest.json +8 -8
  112. package/web/.next/server/app/skills/page.js.nft.json +1 -1
  113. package/web/.next/server/app/skills/page_client-reference-manifest.js +1 -1
  114. package/web/.next/server/app/tools/page/server-reference-manifest.json +8 -8
  115. package/web/.next/server/app/tools/page.js.nft.json +1 -1
  116. package/web/.next/server/app/tools/page_client-reference-manifest.js +1 -1
  117. package/web/.next/server/app/version/page/server-reference-manifest.json +3 -3
  118. package/web/.next/server/app/version/page_client-reference-manifest.js +1 -1
  119. package/web/.next/server/app-paths-manifest.json +1 -0
  120. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js +1 -1
  121. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_4d623b8e.js.map +1 -1
  122. package/web/.next/server/chunks/744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js +3 -0
  123. package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js +3 -0
  124. package/web/.next/server/chunks/[root-of-the-server]__0d33c29e._.js.map +1 -0
  125. package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js +3 -0
  126. package/web/.next/server/chunks/[root-of-the-server]__2f61738a._.js.map +1 -0
  127. package/web/.next/server/chunks/[root-of-the-server]__a402b567._.js +1 -1
  128. package/web/.next/server/chunks/[root-of-the-server]__c6e32a23._.js.map +1 -1
  129. package/web/.next/server/chunks/[root-of-the-server]__cd67a84c._.js.map +1 -1
  130. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js +1 -1
  131. package/web/.next/server/chunks/ssr/744ca_web_components_common_control-center-drawer_create-drawer-client_tsx_5e26fc0a._.js.map +1 -1
  132. package/web/.next/server/chunks/ssr/[root-of-the-server]__0b150ddf._.js.map +1 -1
  133. package/web/.next/server/chunks/ssr/[root-of-the-server]__2138fa7e._.js +2 -2
  134. package/web/.next/server/chunks/ssr/[root-of-the-server]__2138fa7e._.js.map +1 -1
  135. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js +1 -1
  136. package/web/.next/server/chunks/ssr/[root-of-the-server]__29580090._.js.map +1 -1
  137. package/web/.next/server/chunks/ssr/[root-of-the-server]__357d99f9._.js +1 -1
  138. package/web/.next/server/chunks/ssr/[root-of-the-server]__3ef34e4c._.js +1 -1
  139. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js +1 -1
  140. package/web/.next/server/chunks/ssr/[root-of-the-server]__43f51aa6._.js.map +1 -1
  141. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js +1 -1
  142. package/web/.next/server/chunks/ssr/[root-of-the-server]__815546bd._.js.map +1 -1
  143. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js +5 -2
  144. package/web/.next/server/chunks/ssr/[root-of-the-server]__aad040c0._.js.map +1 -1
  145. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js +1 -1
  146. package/web/.next/server/chunks/ssr/[root-of-the-server]__c094882b._.js.map +1 -1
  147. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js +1 -1
  148. package/web/.next/server/chunks/ssr/[root-of-the-server]__d48c5b11._.js.map +1 -1
  149. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js +1 -1
  150. package/web/.next/server/chunks/ssr/[root-of-the-server]__dac5dbf1._.js.map +1 -1
  151. package/web/.next/server/chunks/ssr/[root-of-the-server]__df7c1cd3._.js +1 -1
  152. package/web/.next/server/chunks/ssr/[root-of-the-server]__df7c1cd3._.js.map +1 -1
  153. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js +1 -1
  154. package/web/.next/server/chunks/ssr/[root-of-the-server]__fae8b355._.js.map +1 -1
  155. package/web/.next/server/chunks/ssr/_05c23ad9._.js +1 -1
  156. package/web/.next/server/chunks/ssr/_05c23ad9._.js.map +1 -1
  157. package/web/.next/server/chunks/ssr/_0c5f56e3._.js +2 -2
  158. package/web/.next/server/chunks/ssr/_0c5f56e3._.js.map +1 -1
  159. package/web/.next/server/chunks/ssr/_16eb4fec._.js +1 -1
  160. package/web/.next/server/chunks/ssr/_16eb4fec._.js.map +1 -1
  161. package/web/.next/server/chunks/ssr/_1b719e7f._.js +1 -1
  162. package/web/.next/server/chunks/ssr/_1b719e7f._.js.map +1 -1
  163. package/web/.next/server/chunks/ssr/_24c98f2c._.js +3 -0
  164. package/web/.next/server/chunks/ssr/{_b839d4f1._.js.map → _24c98f2c._.js.map} +1 -1
  165. package/web/.next/server/chunks/ssr/_37e8548b._.js +1 -1
  166. package/web/.next/server/chunks/ssr/_37e8548b._.js.map +1 -1
  167. package/web/.next/server/chunks/ssr/_41d9cd95._.js +4 -0
  168. package/web/.next/server/chunks/ssr/_41d9cd95._.js.map +1 -0
  169. package/web/.next/server/chunks/ssr/_55d763e2._.js +1 -1
  170. package/web/.next/server/chunks/ssr/_55d763e2._.js.map +1 -1
  171. package/web/.next/server/chunks/ssr/_6256a985._.js +1 -1
  172. package/web/.next/server/chunks/ssr/_6256a985._.js.map +1 -1
  173. package/web/.next/server/chunks/ssr/_64bdfc6f._.js +2 -2
  174. package/web/.next/server/chunks/ssr/_64bdfc6f._.js.map +1 -1
  175. package/web/.next/server/chunks/ssr/_8fcc39d4._.js +1 -1
  176. package/web/.next/server/chunks/ssr/_8fcc39d4._.js.map +1 -1
  177. package/web/.next/server/chunks/ssr/_b71645b4._.js +1 -1
  178. package/web/.next/server/chunks/ssr/_b71645b4._.js.map +1 -1
  179. package/web/.next/server/chunks/ssr/_d4b20e29._.js.map +1 -1
  180. package/web/.next/server/chunks/ssr/_d8575088._.js +1 -1
  181. package/web/.next/server/chunks/ssr/_d8575088._.js.map +1 -1
  182. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js +1 -1
  183. package/web/.next/server/chunks/ssr/b1a17_presentation_web_components_features_settings_settings-page-client_tsx_6ed9d5f8._.js.map +1 -1
  184. package/web/.next/server/chunks/ssr/{src_presentation_web_523768ca._.js → src_presentation_web_4128a266._.js} +2 -2
  185. package/web/.next/server/chunks/ssr/{src_presentation_web_523768ca._.js.map → src_presentation_web_4128a266._.js.map} +1 -1
  186. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js +1 -1
  187. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_skills_page_actions_1b176e3c.js.map +1 -1
  188. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js +1 -1
  189. package/web/.next/server/chunks/ssr/src_presentation_web__next-internal_server_app_tools_page_actions_bd9f0dda.js.map +1 -1
  190. package/web/.next/server/chunks/ssr/src_presentation_web_app_actions_open-ide_ts_baaca5d5._.js +1 -1
  191. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js +1 -1
  192. package/web/.next/server/chunks/ssr/src_presentation_web_components_e599bb8c._.js.map +1 -1
  193. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js +1 -1
  194. package/web/.next/server/chunks/ssr/src_presentation_web_components_features_control-center_7ac3562e._.js.map +1 -1
  195. package/web/.next/server/pages/500.html +2 -2
  196. package/web/.next/server/server-reference-manifest.js +1 -1
  197. package/web/.next/server/server-reference-manifest.json +44 -44
  198. package/web/.next/static/chunks/{63f412a89bde2484.js → 08a345de60f7a916.js} +1 -1
  199. package/web/.next/static/chunks/155eeac110efde49.js +5 -0
  200. package/web/.next/static/chunks/{8923825cdb21cf1c.js → 2a89bc5c539a887c.js} +1 -1
  201. package/web/.next/static/chunks/49057cf8cd37e262.js +1 -0
  202. package/web/.next/static/chunks/57489f571eaef682.js +1 -0
  203. package/web/.next/static/chunks/{a63c16171b149d2d.js → 76ae0ee5b19cb24d.js} +1 -1
  204. package/web/.next/static/chunks/{9fd5c3ecc8c5eb07.js → 79050ec2da3b1545.js} +1 -1
  205. package/web/.next/static/chunks/{79984461b85f1e4e.js → 82b4ce7dd487497c.js} +1 -1
  206. package/web/.next/static/chunks/9b8678597fa1db84.css +1 -0
  207. package/web/.next/static/chunks/a48d1fbd73f27a65.js +1 -0
  208. package/web/.next/static/chunks/{0ddea46ed4ba1621.js → b650639ad2c0225a.js} +1 -1
  209. package/web/.next/static/chunks/da1da0780b5c0772.js +2 -0
  210. package/web/.next/static/chunks/f5c613066998f4d3.js +1 -0
  211. package/web/.next/static/chunks/{28f5e2d65510526a.js → fbaff86b010c0bff.js} +1 -1
  212. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js +0 -3
  213. package/web/.next/server/chunks/403f9_next_dist_esm_build_templates_app-route_ff60e4a5.js.map +0 -1
  214. package/web/.next/server/chunks/[externals]__448264a3._.js +0 -3
  215. package/web/.next/server/chunks/ssr/_5a50254c._.js +0 -4
  216. package/web/.next/server/chunks/ssr/_5a50254c._.js.map +0 -1
  217. package/web/.next/server/chunks/ssr/_b839d4f1._.js +0 -3
  218. package/web/.next/static/chunks/0539e93ac218f442.js +0 -2
  219. package/web/.next/static/chunks/7ab5c32984198a7e.js +0 -2
  220. package/web/.next/static/chunks/869ae48ca73fa328.js +0 -1
  221. package/web/.next/static/chunks/a0ab3aee9a5e3d02.js +0 -1
  222. package/web/.next/static/chunks/d61c239df26c61fd.css +0 -1
  223. package/web/.next/static/chunks/ebbc5dd20e227e3d.js +0 -1
  224. package/web/.next/static/chunks/fa556c575c788679.js +0 -1
  225. /package/web/.next/server/chunks/{[externals]__448264a3._.js.map → 744ca_web__next-internal_server_app_api_sessions-batch_route_actions_4859f283.js.map} +0 -0
  226. /package/web/.next/static/{41QsWn4e2sGnQCNBIar6W → kW82VHngwCjHeZ0IeW0ZK}/_buildManifest.js +0 -0
  227. /package/web/.next/static/{41QsWn4e2sGnQCNBIar6W → kW82VHngwCjHeZ0IeW0ZK}/_clientMiddlewareManifest.json +0 -0
  228. /package/web/.next/static/{41QsWn4e2sGnQCNBIar6W → kW82VHngwCjHeZ0IeW0ZK}/_ssgManifest.js +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"attachment-chip.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/attachment-chip/attachment-chip.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAOrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7C,eAAO,MAAM,SAAS,EAAE,KAOvB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAOrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAC"}
1
+ {"version":3,"file":"attachment-chip.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/attachment-chip/attachment-chip.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEnD,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,cAAc,CAQrC,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,cAAc,CAAC,CAAC;AAE7C,eAAO,MAAM,SAAS,EAAE,KAOvB,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAQhC,CAAC;AAEF,eAAO,MAAM,0BAA0B,EAAE,KASxC,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAOrB,CAAC;AAEF,eAAO,MAAM,QAAQ,EAAE,KAOtB,CAAC;AAEF,eAAO,MAAM,OAAO,EAAE,KAQrB,CAAC"}
@@ -6,6 +6,7 @@ const meta = {
6
6
  tags: ['autodocs'],
7
7
  args: {
8
8
  onRemove: fn(),
9
+ onNotesChange: fn(),
9
10
  },
10
11
  };
11
12
  export default meta;
@@ -17,6 +18,25 @@ export const ImageFile = {
17
18
  path: '/tmp/test/screenshot.png',
18
19
  },
19
20
  };
21
+ export const ImageFileWithNotes = {
22
+ args: {
23
+ name: 'dashboard.png',
24
+ size: 250000,
25
+ mimeType: 'image/png',
26
+ path: '/tmp/test/dashboard.png',
27
+ notes: 'This shows the main dashboard layout with the sidebar collapsed.',
28
+ },
29
+ };
30
+ export const ImageFileWithNotesReadOnly = {
31
+ args: {
32
+ name: 'wireframe.png',
33
+ size: 180000,
34
+ mimeType: 'image/png',
35
+ path: '/tmp/test/wireframe.png',
36
+ notes: 'Navigation should match this exact layout pattern.',
37
+ onNotesChange: undefined,
38
+ },
39
+ };
20
40
  export const PdfFile = {
21
41
  args: {
22
42
  name: 'requirements.pdf',
@@ -1 +1 @@
1
- {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAgoB1F"}
1
+ {"version":3,"file":"feature-drawer-client.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/control-center-drawer/feature-drawer-client.tsx"],"names":[],"mappings":"AAuCA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAK/D,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,UAAU,CAAC;IACjB,8FAA8F;IAC9F,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,CAAC,EAAE,IAAI,EAAE,WAAW,EAAE,MAAM,EAAE,EAAE,wBAAwB,2CAyoB1F"}
@@ -201,7 +201,11 @@ export function FeatureDrawerClient({ view: initialView, urlTab }) {
201
201
  setIsRejecting(true);
202
202
  try {
203
203
  const attachmentPaths = attachments.map((a) => a.path).filter(Boolean);
204
- const result = await rejectFeature(featureNode.featureId, feedback, attachmentPaths);
204
+ const notedAttachments = attachments.filter((a) => a.notes?.trim());
205
+ const feedbackWithNotes = notedAttachments.length > 0
206
+ ? `${feedback}\n\nImage notes:\n${notedAttachments.map((a) => `- @${a.path}: ${a.notes.trim()}`).join('\n')}`
207
+ : feedback;
208
+ const result = await rejectFeature(featureNode.featureId, feedbackWithNotes, attachmentPaths);
205
209
  if (!result.rejected) {
206
210
  toast.error(result.error ?? `Failed to reject ${label.toLowerCase()}`);
207
211
  return;
@@ -7,6 +7,8 @@ export interface RejectAttachment {
7
7
  mimeType: string;
8
8
  path: string;
9
9
  loading?: boolean;
10
+ /** Optional user notes or annotations for this image */
11
+ notes?: string;
10
12
  }
11
13
  export interface DrawerActionBarProps {
12
14
  /** Callback when user rejects with feedback (inline input or dialog) */
@@ -1 +1 @@
1
- {"version":3,"file":"drawer-action-bar-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,iGAAiG;AACjG,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACvE,kCAAkC;IAClC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,cAAc,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACvC,mDAAmD;IACnD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C"}
1
+ {"version":3,"file":"drawer-action-bar-config.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/drawer-action-bar/drawer-action-bar-config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,iGAAiG;AACjG,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,wDAAwD;IACxD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACnC,wEAAwE;IACxE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,gBAAgB,EAAE,KAAK,IAAI,CAAC;IACvE,kCAAkC;IAClC,SAAS,EAAE,MAAM,IAAI,CAAC;IACtB,gFAAgF;IAChF,YAAY,EAAE,MAAM,CAAC;IACrB,oFAAoF;IACpF,cAAc,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IACvC,mDAAmD;IACnD,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,4DAA4D;IAC5D,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,qFAAqF;IACrF,QAAQ,CAAC,EAAE,SAAS,CAAC;IACrB,iGAAiG;IACjG,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,iFAAiF;IACjF,iBAAiB,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;CAC7C"}
@@ -1 +1 @@
1
- {"version":3,"file":"drawer-action-bar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,oBAAoB,EAAoB,MAAM,4BAA4B,CAAC;AAmEzF,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,cAA0B,EAC1B,mBAAmB,EACnB,YAAoB,EACpB,WAAmB,EACnB,QAAQ,EACR,SAAS,EAAE,mBAAmB,EAC9B,iBAAiB,GAClB,EAAE,oBAAoB,2CAoYtB"}
1
+ {"version":3,"file":"drawer-action-bar.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/drawer-action-bar/drawer-action-bar.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAE,oBAAoB,EAAoB,MAAM,4BAA4B,CAAC;AAmEzF,wBAAgB,eAAe,CAAC,EAC9B,QAAQ,EACR,SAAS,EACT,YAAY,EACZ,cAA0B,EAC1B,mBAAmB,EACnB,YAAoB,EACpB,WAAmB,EACnB,QAAQ,EACR,SAAS,EAAE,mBAAmB,EAC9B,iBAAiB,GAClB,EAAE,oBAAoB,2CA0YtB"}
@@ -244,6 +244,9 @@ export function DrawerActionBar({ onReject, onApprove, approveLabel, approveVari
244
244
  const handleRemoveFile = useCallback((id) => {
245
245
  setAttachments((prev) => prev.filter((f) => f.id !== id));
246
246
  }, []);
247
+ const handleNotesChange = useCallback((id, notes) => {
248
+ setAttachments((prev) => prev.map((f) => (f.id === id ? { ...f, notes } : f)));
249
+ }, []);
247
250
  const clearForm = useCallback(() => {
248
251
  setChatInput('');
249
252
  setAttachments([]);
@@ -274,7 +277,7 @@ export function DrawerActionBar({ onReject, onApprove, approveLabel, approveVari
274
277
  }
275
278
  }, []);
276
279
  const modKey = typeof navigator !== 'undefined' && /Mac/i.test(navigator.userAgent) ? '⌘' : 'Ctrl';
277
- return (_jsxs("div", { className: "border-border shrink-0 border-t", children: [children, onReject ? (_jsx(TooltipProvider, { delayDuration: 400, children: _jsx("form", { ref: formRef, onSubmit: handleFormSubmit, className: "p-3", children: _jsx("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('rounded-md border-2 border-transparent transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: _jsxs("div", { className: "border-input focus-within:ring-ring/50 focus-within:border-ring flex flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]", children: [_jsx(Textarea, { placeholder: revisionPlaceholder ?? 'Ask AI to revise...', "aria-label": revisionPlaceholder ?? 'Ask AI to revise...', disabled: disabled, value: chatInput, onChange: (e) => setChatInput(e.target.value), onKeyDown: handleKeyDown, onPaste: handlePaste, rows: 1, className: "max-h-[35dvh] min-h-9 flex-1 resize-none overflow-y-auto rounded-none border-0 py-2 shadow-none focus-visible:ring-0", "data-testid": "drawer-chat-input" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: disabled, loading: file.loading }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-2 border-t px-3 py-1.5", children: [_jsxs("span", { className: "text-muted-foreground flex-1 truncate text-[11px]", children: [_jsxs("kbd", { className: "bg-muted rounded px-1 py-0.5 font-mono text-[10px]", children: [modKey, "+Enter"] }), ' ', hasText ? 'reject' : 'approve'] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleAddFiles, disabled: disabled, "aria-label": "Attach files", className: "text-muted-foreground hover:text-foreground cursor-pointer rounded p-1 transition-colors", children: _jsx(PaperclipIcon, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "top", children: "Attach files" })] }), _jsx("div", { onMouseLeave: () => setHoverExpanded(false), children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "submit", disabled: disabled, "data-testid": "drawer-action-submit", className: cn('relative flex h-9 min-w-[12rem] cursor-pointer items-center overflow-hidden rounded-md border pr-10 pl-4 text-sm font-medium whitespace-nowrap transition-colors', 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', approveExpanded
280
+ return (_jsxs("div", { className: "border-border shrink-0 border-t", children: [children, onReject ? (_jsx(TooltipProvider, { delayDuration: 400, children: _jsx("form", { ref: formRef, onSubmit: handleFormSubmit, className: "p-3", children: _jsx("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('rounded-md border-2 border-transparent transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: _jsxs("div", { className: "border-input focus-within:ring-ring/50 focus-within:border-ring flex flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow] focus-within:ring-[3px]", children: [_jsx(Textarea, { placeholder: revisionPlaceholder ?? 'Ask AI to revise...', "aria-label": revisionPlaceholder ?? 'Ask AI to revise...', disabled: disabled, value: chatInput, onChange: (e) => setChatInput(e.target.value), onKeyDown: handleKeyDown, onPaste: handlePaste, rows: 1, className: "max-h-[35dvh] min-h-9 flex-1 resize-none overflow-y-auto rounded-none border-0 py-2 shadow-none focus-visible:ring-0", "data-testid": "drawer-chat-input" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: disabled, loading: file.loading, notes: file.notes, onNotesChange: (notes) => handleNotesChange(file.id, notes) }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-2 border-t px-3 py-1.5", children: [_jsxs("span", { className: "text-muted-foreground flex-1 truncate text-[11px]", children: [_jsxs("kbd", { className: "bg-muted rounded px-1 py-0.5 font-mono text-[10px]", children: [modKey, "+Enter"] }), ' ', hasText ? 'reject' : 'approve'] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleAddFiles, disabled: disabled, "aria-label": "Attach files", className: "text-muted-foreground hover:text-foreground cursor-pointer rounded p-1 transition-colors", children: _jsx(PaperclipIcon, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "top", children: "Attach files" })] }), _jsx("div", { onMouseLeave: () => setHoverExpanded(false), children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("button", { type: "submit", disabled: disabled, "data-testid": "drawer-action-submit", className: cn('relative flex h-9 min-w-[12rem] cursor-pointer items-center overflow-hidden rounded-md border pr-10 pl-4 text-sm font-medium whitespace-nowrap transition-colors', 'disabled:pointer-events-none disabled:cursor-not-allowed disabled:opacity-50', approveExpanded
278
281
  ? `${accentBorder} text-white`
279
282
  : rejectHighlighted
280
283
  ? 'border-primary bg-muted ring-primary/30 shadow-sm ring-1'
@@ -8,6 +8,8 @@ export interface FormAttachment {
8
8
  mimeType: string;
9
9
  path: string;
10
10
  loading?: boolean;
11
+ /** Optional user notes or annotations for this image */
12
+ notes?: string;
11
13
  }
12
14
  /** Minimal feature descriptor for the parent selector. */
13
15
  export interface ParentFeatureOption {
@@ -1 +1 @@
1
- {"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"feature-create-drawer.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-create-drawer/feature-create-drawer.tsx"],"names":[],"mappings":"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"}
@@ -412,6 +412,9 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
412
412
  const handleRemoveFile = useCallback((id) => {
413
413
  setAttachments((prev) => prev.filter((f) => f.id !== id));
414
414
  }, []);
415
+ const handleNotesChange = useCallback((id, notes) => {
416
+ setAttachments((prev) => prev.map((f) => (f.id === id ? { ...f, notes } : f)));
417
+ }, []);
415
418
  const formRef = useRef(null);
416
419
  const handleKeyDown = useCallback((e) => {
417
420
  if ((e.ctrlKey || e.metaKey) && e.key === 'Enter') {
@@ -441,7 +444,7 @@ export function FeatureCreateDrawer({ open, onClose, onSubmit, repositoryPath, i
441
444
  setLocalRepos((prev) => [...prev, repo]);
442
445
  setSelectedRepoPath(repo.path);
443
446
  }, disabled: isSubmitting })] })) : validRepoPath ? (_jsxs("div", { className: "flex flex-col gap-1.5", "data-testid": "repo-readonly-section", children: [_jsx(Label, { className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "REPOSITORY" }), _jsx("p", { className: "text-sm", "data-testid": "repo-readonly-label", children: repositories?.find((r) => r.path === validRepoPath)?.name ??
444
- validRepoPath.split('/').pop() })] })) : null, _jsxs("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('flex flex-col gap-1.5 rounded-md border-2 border-transparent p-1 transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsxs("div", { ref: promptContainerRef, onFocus: handlePromptFocus, onBlur: handlePromptBlur, className: cn('border-input flex h-56 flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow]', isPromptFocused && 'ring-ring/50 border-ring ring-[3px]'), children: [_jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), onPaste: handlePaste, required: true, disabled: isSubmitting, className: "min-h-0 flex-1 resize-none rounded-none border-0 shadow-none focus-visible:ring-0" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: isSubmitting, loading: file.loading }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-3 border-t px-3 py-1.5", children: [_jsx(AgentModelPicker, { initialAgentType: overrideAgent ?? currentAgentType ?? 'claude-code', initialModel: overrideModel ?? currentModel ?? 'claude-sonnet-4-6', mode: "override", onAgentModelChange: (agent, model) => {
447
+ validRepoPath.split('/').pop() })] })) : null, _jsxs("div", { role: "region", "aria-label": "File drop zone", "data-drag-over": isDragOver ? 'true' : 'false', onDragEnter: handleDragEnter, onDragLeave: handleDragLeave, onDragOver: handleDragOver, onDrop: handleDrop, className: cn('flex flex-col gap-1.5 rounded-md border-2 border-transparent p-1 transition-colors', isDragOver && 'border-primary/50 bg-primary/5'), children: [_jsx(Label, { htmlFor: "feature-description", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "DESCRIBE YOUR FEATURE" }), _jsxs("div", { ref: promptContainerRef, onFocus: handlePromptFocus, onBlur: handlePromptBlur, className: cn('border-input flex h-56 flex-col overflow-hidden rounded-md border shadow-xs transition-[color,box-shadow]', isPromptFocused && 'ring-ring/50 border-ring ring-[3px]'), children: [_jsx(Textarea, { id: "feature-description", placeholder: "e.g. Add GitHub OAuth login with callback handling and token refresh...", value: description, onChange: (e) => setDescription(e.target.value), onPaste: handlePaste, required: true, disabled: isSubmitting, className: "min-h-0 flex-1 resize-none rounded-none border-0 shadow-none focus-visible:ring-0" }), attachments.length > 0 && (_jsx("div", { className: "flex flex-wrap items-center gap-1.5 px-3 py-2", children: attachments.map((file) => (_jsx(AttachmentChip, { name: file.name, size: file.size, mimeType: file.mimeType, path: file.path, onRemove: () => handleRemoveFile(file.id), disabled: isSubmitting, loading: file.loading, notes: file.notes, onNotesChange: (notes) => handleNotesChange(file.id, notes) }, file.id))) })), uploadError ? (_jsx("p", { className: "text-destructive px-3 pb-2 text-xs", children: uploadError })) : null, _jsxs("div", { className: "border-input flex items-center gap-3 border-t px-3 py-1.5", children: [_jsx(AgentModelPicker, { initialAgentType: overrideAgent ?? currentAgentType ?? 'claude-code', initialModel: overrideModel ?? currentModel ?? 'claude-sonnet-4-6', mode: "override", onAgentModelChange: (agent, model) => {
445
448
  setOverrideAgent(agent);
446
449
  setOverrideModel(model);
447
450
  }, disabled: isSubmitting, className: "w-55" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "ml-auto flex cursor-pointer items-center gap-2", children: [_jsx(Switch, { id: "pending-mode", checked: pending, onCheckedChange: setPending, disabled: isSubmitting }), _jsxs(Label, { htmlFor: "pending-mode", className: "flex cursor-pointer items-center gap-1 text-sm font-medium", children: [_jsx(Clock, { className: "h-3.5 w-3.5" }), "Pending"] })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Create without starting \u2014 start manually later." })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-2", children: [_jsx(Switch, { id: "fast-mode", checked: fast, onCheckedChange: setFast, disabled: isSubmitting }), _jsxs(Label, { htmlFor: "fast-mode", className: "flex cursor-pointer items-center gap-1 text-sm font-medium", children: [_jsx(Zap, { className: "h-3.5 w-3.5" }), "Fast Mode"] })] }) }), _jsx(TooltipContent, { side: "bottom", children: "Skip SDLC phases and implement directly from your prompt." })] }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("button", { type: "button", onClick: handleAddFiles, disabled: isSubmitting, "aria-label": "Attach files", className: "text-muted-foreground hover:text-foreground cursor-pointer rounded p-1 transition-colors", children: _jsx(PaperclipIcon, { className: "h-4 w-4" }) }) }), _jsx(TooltipContent, { side: "bottom", children: "Attach files" })] })] })] })] }), hasFeatures && initialParentId !== undefined ? (_jsxs("div", { className: "flex flex-col gap-1.5", children: [_jsx(Label, { htmlFor: "parent-feature", className: "text-muted-foreground text-xs font-semibold tracking-wider", children: "PARENT FEATURE" }), _jsx(ParentFeatureCombobox, { features: features, value: parentId, onChange: setParentId, disabled: isSubmitting })] })) : null, _jsxs("div", { className: "flex flex-col gap-2", children: [_jsxs("div", { className: "border-input flex items-center gap-4 rounded-md border px-3 py-2.5", children: [_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx("span", { className: "text-muted-foreground w-16 shrink-0 cursor-default text-xs font-semibold tracking-wider", children: "APPROVE" }) }), _jsx(TooltipContent, { side: "bottom", children: "Auto-approve phase transitions without manual review." })] }), _jsx("div", { className: "flex flex-1 items-center gap-4", children: AUTO_APPROVE_OPTIONS.map((opt) => (_jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsxs("div", { className: "flex cursor-pointer items-center gap-1.5", children: [_jsx(Switch, { id: `approve-${opt.id}`, size: "sm", checked: approvalGates[opt.id] ?? false, onCheckedChange: (v) => setApprovalGates((prev) => ({ ...prev, [opt.id]: v })), disabled: isSubmitting ||
@@ -18,6 +18,6 @@ interface FeatureSessionsDropdownProps {
18
18
  /** Callback to create a feature from a session. Only shown on repo nodes. */
19
19
  onCreateFromSession?: (session: SessionSummary, sessionFilePath: string) => void;
20
20
  }
21
- export declare function FeatureSessionsDropdown({ repositoryPath, className, includeWorktrees, onCreateFromSession, }: FeatureSessionsDropdownProps): import("react/jsx-runtime").JSX.Element;
21
+ export declare function FeatureSessionsDropdown({ repositoryPath, className, onCreateFromSession, }: FeatureSessionsDropdownProps): import("react/jsx-runtime").JSX.Element;
22
22
  export {};
23
23
  //# sourceMappingURL=feature-sessions-dropdown.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"feature-sessions-dropdown.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-sessions-dropdown.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,4BAA4B;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;CAClF;AAmDD,wBAAgB,uBAAuB,CAAC,EACtC,cAAc,EACd,SAAS,EACT,gBAAgB,EAChB,mBAAmB,GACpB,EAAE,4BAA4B,2CAiK9B"}
1
+ {"version":3,"file":"feature-sessions-dropdown.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-sessions-dropdown.tsx"],"names":[],"mappings":"AA8BA,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,WAAW,EAAE,MAAM,CAAC;IACpB,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,UAAU,4BAA4B;IACpC,cAAc,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,6EAA6E;IAC7E,mBAAmB,CAAC,EAAE,CAAC,OAAO,EAAE,cAAc,EAAE,eAAe,EAAE,MAAM,KAAK,IAAI,CAAC;CAClF;AAmDD,wBAAgB,uBAAuB,CAAC,EACtC,cAAc,EACd,SAAS,EACT,mBAAmB,GACpB,EAAE,4BAA4B,2CA6F9B"}
@@ -1,11 +1,12 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
3
- import { useState, useCallback, useEffect, useRef } from 'react';
4
- import { History, Copy, ExternalLink, Terminal, MessageSquare, Clock, Loader2, ChevronDown, Sparkles, } from 'lucide-react';
3
+ import { useState, useCallback } from 'react';
4
+ import { History, Copy, ExternalLink, Terminal, MessageSquare, Clock, ChevronDown, Sparkles, } from 'lucide-react';
5
5
  import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, DropdownMenuSub, DropdownMenuSubTrigger, DropdownMenuSubContent, DropdownMenuPortal, } from '../../ui/dropdown-menu.js';
6
6
  import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip.js';
7
7
  import { cn } from '../../../lib/utils.js';
8
8
  import { getAgentTypeIcon } from '../../common/feature-node/agent-type-icons.js';
9
+ import { useSessionsContext } from '../../../hooks/sessions-provider.js';
9
10
  const ACTIVE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
10
11
  const PREVIEW_COUNT = 3;
11
12
  function isSessionActive(session) {
@@ -56,88 +57,29 @@ async function copyToClipboard(text) {
56
57
  function stopNodeEvent(e) {
57
58
  e.stopPropagation();
58
59
  }
59
- export function FeatureSessionsDropdown({ repositoryPath, className, includeWorktrees, onCreateFromSession, }) {
60
- const [sessions, setSessions] = useState([]);
61
- const [loading, setLoading] = useState(false);
62
- const [fetched, setFetched] = useState(false);
63
- const [hasActiveSessions, setHasActiveSessions] = useState(false);
60
+ export function FeatureSessionsDropdown({ repositoryPath, className, onCreateFromSession, }) {
64
61
  const [expanded, setExpanded] = useState(false);
65
- const prevPathRef = useRef(repositoryPath);
66
- // Reset when path changes
67
- useEffect(() => {
68
- if (prevPathRef.current !== repositoryPath) {
69
- prevPathRef.current = repositoryPath;
70
- setSessions([]);
71
- setFetched(false);
72
- setHasActiveSessions(false);
73
- setExpanded(false);
74
- }
75
- }, [repositoryPath]);
76
- // Fetch sessions on mount. Fast because we only scan the matching project directory.
77
- // Populates count badge + active indicator, and pre-loads the dropdown.
78
- useEffect(() => {
79
- let cancelled = false;
80
- const params = new URLSearchParams({
81
- repositoryPath,
82
- limit: '10',
83
- ...(includeWorktrees && { includeWorktrees: 'true' }),
84
- });
85
- fetch(`/api/sessions?${params.toString()}`)
86
- .then((res) => (res.ok ? res.json() : null))
87
- .then((data) => {
88
- if (!cancelled && data?.sessions) {
89
- setSessions(data.sessions);
90
- setHasActiveSessions(data.sessions.some(isSessionActive));
91
- setFetched(true);
92
- }
93
- })
94
- .catch(() => undefined);
95
- return () => {
96
- cancelled = true;
97
- };
98
- }, [repositoryPath, includeWorktrees]);
99
- // Re-fetch on dropdown open if not already loaded (e.g. path changed)
100
- const doFetch = useCallback(async () => {
101
- if (fetched)
102
- return;
103
- setLoading(true);
104
- try {
105
- const params = new URLSearchParams({
106
- repositoryPath,
107
- limit: '10',
108
- ...(includeWorktrees && { includeWorktrees: 'true' }),
109
- });
110
- const res = await fetch(`/api/sessions?${params.toString()}`);
111
- if (res.ok) {
112
- const data = (await res.json());
113
- setSessions(data.sessions);
114
- setHasActiveSessions(data.sessions.some(isSessionActive));
115
- }
116
- }
117
- catch {
118
- // Silently fail
119
- }
120
- finally {
121
- setLoading(false);
122
- setFetched(true);
123
- }
124
- }, [repositoryPath, fetched, includeWorktrees]);
62
+ // Read sessions from the centralized SessionsProvider context.
63
+ // Sessions are batch-fetched every 30s — no per-instance HTTP calls.
64
+ const { getSessionsForPath, hasActiveSessions: hasActiveForPath } = useSessionsContext();
65
+ const sessions = getSessionsForPath(repositoryPath);
66
+ const active = hasActiveForPath(repositoryPath);
125
67
  const handleOpenChange = useCallback((open) => {
126
- if (open)
127
- void doFetch();
128
- }, [doFetch]);
68
+ if (!open)
69
+ setExpanded(false);
70
+ }, []);
129
71
  const visibleSessions = expanded ? sessions : sessions.slice(0, PREVIEW_COUNT);
130
72
  const hasMore = sessions.length > PREVIEW_COUNT;
131
- return (_jsxs(DropdownMenu, { modal: false, onOpenChange: handleOpenChange, children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": "View sessions", "data-testid": "feature-node-sessions-button", className: cn('nodrag relative flex h-5 cursor-pointer items-center gap-0.5 rounded px-0.5 text-[10px] transition-colors', 'text-muted-foreground hover:text-foreground hover:bg-muted', className), onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsx(History, { className: "h-3 w-3 shrink-0" }), sessions.length > 0 ? (_jsx("span", { "data-testid": "feature-node-sessions-count", children: sessions.length })) : null, hasActiveSessions ? (_jsx("span", { className: "absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-emerald-500" })) : null] }) }) }), _jsx(TooltipContent, { side: "top", children: hasActiveSessions ? 'Sessions (active)' : 'Sessions' })] }) }), _jsxs(DropdownMenuContent, { align: "start", side: "bottom", className: "w-80", onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsxs(DropdownMenuLabel, { className: "flex items-center gap-1.5 text-xs", children: [_jsx(History, { className: "h-3 w-3" }), "Agent Sessions"] }), _jsx(DropdownMenuSeparator, {}), loading ? (_jsxs("div", { className: "text-muted-foreground flex items-center justify-center gap-2 py-4 text-xs", children: [_jsx(Loader2, { className: "h-3.5 w-3.5 animate-spin" }), "Loading..."] })) : sessions.length === 0 ? (_jsx("div", { className: "text-muted-foreground py-4 text-center text-xs", children: "No sessions found" })) : (_jsxs(_Fragment, { children: [visibleSessions.map((session) => (_jsx(SessionRow, { session: session, repositoryPath: repositoryPath, onCreateFromSession: onCreateFromSession }, session.id))), hasMore ? (_jsxs(DropdownMenuItem, { className: "text-muted-foreground justify-center gap-1 py-1.5 text-[10px]", onClick: (e) => {
73
+ return (_jsxs(DropdownMenu, { modal: false, onOpenChange: handleOpenChange, children: [_jsx(TooltipProvider, { children: _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(DropdownMenuTrigger, { asChild: true, children: _jsxs("button", { type: "button", "aria-label": "View sessions", "data-testid": "feature-node-sessions-button", className: cn('nodrag relative flex h-5 cursor-pointer items-center gap-0.5 rounded px-0.5 text-[10px] transition-colors', 'text-muted-foreground hover:text-foreground hover:bg-muted', className), onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsx(History, { className: "h-3 w-3 shrink-0" }), sessions.length > 0 ? (_jsx("span", { "data-testid": "feature-node-sessions-count", children: sessions.length })) : null, active ? (_jsx("span", { className: "absolute -top-0.5 -right-0.5 h-1.5 w-1.5 rounded-full bg-emerald-500" })) : null] }) }) }), _jsx(TooltipContent, { side: "top", children: active ? 'Sessions (active)' : 'Sessions' })] }) }), _jsxs(DropdownMenuContent, { align: "start", side: "bottom", className: "w-80", onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsxs(DropdownMenuLabel, { className: "flex items-center gap-1.5 text-xs", children: [_jsx(History, { className: "h-3 w-3" }), "Agent Sessions"] }), _jsx(DropdownMenuSeparator, {}), sessions.length === 0 ? (_jsx("div", { className: "text-muted-foreground py-4 text-center text-xs", children: "No sessions found" })) : (_jsxs(_Fragment, { children: [visibleSessions.map((session) => (_jsx(SessionRow, { session: session, repositoryPath: repositoryPath, onCreateFromSession: onCreateFromSession }, session.id))), hasMore ? (_jsxs(DropdownMenuItem, { className: "text-muted-foreground justify-center gap-1 py-1.5 text-[10px]", onClick: (e) => {
132
74
  e.preventDefault();
133
75
  setExpanded((v) => !v);
134
76
  }, children: [_jsx(ChevronDown, { className: cn('h-3 w-3 transition-transform', expanded && 'rotate-180') }), expanded ? 'Show less' : `Show ${sessions.length - PREVIEW_COUNT} more`] })) : null] }))] })] }));
135
77
  }
136
78
  // ── Session row component ─────────────────────────────────────────────
137
79
  function SessionRow({ session, repositoryPath, onCreateFromSession, }) {
138
- const active = isSessionActive(session);
80
+ const sessionActive = isSessionActive(session);
139
81
  const AgentIcon = getAgentTypeIcon(session.agentType);
140
- return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { className: "flex items-start gap-2 py-2 pr-2", children: [_jsxs("div", { className: "relative mt-0.5 shrink-0", children: [_jsx(AgentIcon, { className: "h-4 w-4" }), active ? (_jsx("span", { className: "border-background absolute -right-0.5 -bottom-0.5 h-2 w-2 rounded-full border bg-emerald-500" })) : null] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsx("span", { className: "truncate text-xs leading-tight", children: truncatePreview(session.preview) }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-[10px] leading-tight", children: [_jsxs("span", { className: "flex items-center gap-0.5", children: [_jsx(MessageSquare, { className: "h-2.5 w-2.5" }), session.messageCount] }), session.firstMessageAt ? (_jsxs("span", { className: "flex items-center gap-0.5", children: [_jsx(Clock, { className: "h-2.5 w-2.5" }), new Date(session.firstMessageAt).toLocaleDateString()] })) : null, session.lastMessageAt ? (_jsx("span", { className: cn('ml-auto shrink-0', active ? 'font-medium text-emerald-600' : ''), children: formatRelativeTime(session.lastMessageAt) })) : null] })] })] }), _jsx(DropdownMenuPortal, { children: _jsxs(DropdownMenuSubContent, { onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => void copyToClipboard(`claude --resume ${session.id} --project ${repositoryPath}`), children: [_jsx(Terminal, { className: "h-3.5 w-3.5" }), "Copy resume command"] }), _jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => void copyToClipboard(session.id), children: [_jsx(Copy, { className: "h-3.5 w-3.5" }), "Copy session ID"] }), _jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => {
82
+ return (_jsxs(DropdownMenuSub, { children: [_jsxs(DropdownMenuSubTrigger, { className: "flex items-start gap-2 py-2 pr-2", children: [_jsxs("div", { className: "relative mt-0.5 shrink-0", children: [_jsx(AgentIcon, { className: "h-4 w-4" }), sessionActive ? (_jsx("span", { className: "border-background absolute -right-0.5 -bottom-0.5 h-2 w-2 rounded-full border bg-emerald-500" })) : null] }), _jsxs("div", { className: "flex min-w-0 flex-1 flex-col gap-0.5", children: [_jsx("span", { className: "truncate text-xs leading-tight", children: truncatePreview(session.preview) }), _jsxs("div", { className: "text-muted-foreground flex items-center gap-2 text-[10px] leading-tight", children: [_jsxs("span", { className: "flex items-center gap-0.5", children: [_jsx(MessageSquare, { className: "h-2.5 w-2.5" }), session.messageCount] }), session.firstMessageAt ? (_jsxs("span", { className: "flex items-center gap-0.5", children: [_jsx(Clock, { className: "h-2.5 w-2.5" }), new Date(session.firstMessageAt).toLocaleDateString()] })) : null, session.lastMessageAt ? (_jsx("span", { className: cn('ml-auto shrink-0', sessionActive ? 'font-medium text-emerald-600' : ''), children: formatRelativeTime(session.lastMessageAt) })) : null] })] })] }), _jsx(DropdownMenuPortal, { children: _jsxs(DropdownMenuSubContent, { onClick: stopNodeEvent, onPointerDown: stopNodeEvent, children: [_jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => void copyToClipboard(`claude --resume ${session.id} --project ${repositoryPath}`), children: [_jsx(Terminal, { className: "h-3.5 w-3.5" }), "Copy resume command"] }), _jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => void copyToClipboard(session.id), children: [_jsx(Copy, { className: "h-3.5 w-3.5" }), "Copy session ID"] }), _jsxs(DropdownMenuItem, { className: "gap-2 text-xs", onClick: () => {
141
83
  const vscodeUri = `vscode://file${repositoryPath}`;
142
84
  window.open(vscodeUri, '_blank');
143
85
  }, children: [_jsx(ExternalLink, { className: "h-3.5 w-3.5" }), "Open in IDE"] }), onCreateFromSession && session.filePath ? (_jsxs(_Fragment, { children: [_jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { className: "gap-2 text-xs font-medium text-violet-700 focus:bg-violet-50 focus:text-violet-800", onClick: () => onCreateFromSession(session, session.filePath), children: [_jsx(Sparkles, { className: "h-3.5 w-3.5 text-violet-500" }), "Create feature from session"] })] })) : null] }) })] }));
@@ -1 +1 @@
1
- {"version":3,"file":"feature-sessions-dropdown.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AA4DtE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,uBAAuB,CAU9C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAEtD,eAAO,MAAM,YAAY,EAAE,KAE1B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC"}
1
+ {"version":3,"file":"feature-sessions-dropdown.stories.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/common/feature-node/feature-sessions-dropdown.stories.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEvD,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AAsEtE,QAAA,MAAM,IAAI,EAAE,IAAI,CAAC,OAAO,uBAAuB,CAM9C,CAAC;AAEF,eAAe,IAAI,CAAC;AACpB,KAAK,KAAK,GAAG,QAAQ,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAEtD,eAAO,MAAM,YAAY,EAAE,KAE1B,CAAC;AAEF,eAAO,MAAM,kBAAkB,EAAE,KAEhC,CAAC;AAEF,eAAO,MAAM,KAAK,EAAE,KAEnB,CAAC"}
@@ -1,5 +1,7 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { FeatureSessionsDropdown } from './feature-sessions-dropdown.js';
3
+ import { SessionsProvider } from '../../../hooks/sessions-provider.js';
4
+ const REPO_PATH = '/home/user/workspaces/my-project';
3
5
  const mockSessions = [
4
6
  {
5
7
  id: 'a1b2c3d4-e5f6-7890-abcd-ef1234567890',
@@ -8,7 +10,7 @@ const mockSessions = [
8
10
  firstMessageAt: new Date(Date.now() - 3_600_000).toISOString(),
9
11
  lastMessageAt: new Date(Date.now() - 1_800_000).toISOString(),
10
12
  createdAt: new Date(Date.now() - 3_600_000).toISOString(),
11
- projectPath: '~/workspaces/my-project',
13
+ projectPath: REPO_PATH,
12
14
  },
13
15
  {
14
16
  id: 'b2c3d4e5-f6a7-8901-bcde-f12345678901',
@@ -17,7 +19,7 @@ const mockSessions = [
17
19
  firstMessageAt: new Date(Date.now() - 86_400_000).toISOString(),
18
20
  lastMessageAt: new Date(Date.now() - 82_800_000).toISOString(),
19
21
  createdAt: new Date(Date.now() - 86_400_000).toISOString(),
20
- projectPath: '~/workspaces/my-project',
22
+ projectPath: REPO_PATH,
21
23
  },
22
24
  {
23
25
  id: 'c3d4e5f6-a7b8-9012-cdef-123456789012',
@@ -26,7 +28,7 @@ const mockSessions = [
26
28
  firstMessageAt: new Date(Date.now() - 172_800_000).toISOString(),
27
29
  lastMessageAt: new Date(Date.now() - 169_200_000).toISOString(),
28
30
  createdAt: new Date(Date.now() - 172_800_000).toISOString(),
29
- projectPath: '~/workspaces/my-project',
31
+ projectPath: REPO_PATH,
30
32
  },
31
33
  ];
32
34
  const mockActiveSession = {
@@ -36,42 +38,41 @@ const mockActiveSession = {
36
38
  firstMessageAt: new Date(Date.now() - 180_000).toISOString(),
37
39
  lastMessageAt: new Date(Date.now() - 60_000).toISOString(), // 1 min ago — active
38
40
  createdAt: new Date(Date.now() - 180_000).toISOString(),
39
- projectPath: '~/workspaces/my-project',
41
+ projectPath: REPO_PATH,
40
42
  };
41
- function createFetchMock(sessions) {
43
+ /**
44
+ * Mock fetch for /api/sessions-batch, then wrap with SessionsProvider.
45
+ */
46
+ function createSessionsMock(sessions) {
42
47
  return (Story) => {
43
48
  const originalFetch = window.fetch;
44
49
  window.fetch = (async (input, init) => {
45
50
  const url = typeof input === 'string' ? input : input instanceof URL ? input.href : input.url;
46
- if (url.includes('/api/sessions')) {
47
- return new Response(JSON.stringify({ sessions }), {
51
+ if (url.includes('/api/sessions-batch')) {
52
+ return new Response(JSON.stringify({ sessionsByPath: sessions }), {
48
53
  status: 200,
49
54
  headers: { 'Content-Type': 'application/json' },
50
55
  });
51
56
  }
52
57
  return originalFetch(input, init);
53
58
  });
54
- return _jsx(Story, {});
59
+ return (_jsx(SessionsProvider, { children: _jsx(Story, {}) }));
55
60
  };
56
61
  }
57
62
  const meta = {
58
63
  title: 'Composed/FeatureSessionsDropdown',
59
64
  component: FeatureSessionsDropdown,
60
65
  tags: ['autodocs'],
61
- parameters: {
62
- layout: 'centered',
63
- },
64
- args: {
65
- repositoryPath: '/home/user/workspaces/my-project',
66
- },
66
+ parameters: { layout: 'centered' },
67
+ args: { repositoryPath: REPO_PATH },
67
68
  };
68
69
  export default meta;
69
70
  export const WithSessions = {
70
- decorators: [createFetchMock(mockSessions)],
71
+ decorators: [createSessionsMock({ [REPO_PATH]: mockSessions })],
71
72
  };
72
73
  export const WithActiveSessions = {
73
- decorators: [createFetchMock([mockActiveSession, ...mockSessions])],
74
+ decorators: [createSessionsMock({ [REPO_PATH]: [mockActiveSession, ...mockSessions] })],
74
75
  };
75
76
  export const Empty = {
76
- decorators: [createFetchMock([])],
77
+ decorators: [createSessionsMock({ [REPO_PATH]: [] })],
77
78
  };
@@ -1 +1 @@
1
- {"version":3,"file":"control-center.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAG5E,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CASvF"}
1
+ {"version":3,"file":"control-center.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/control-center.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAC1C,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAI5E,MAAM,WAAW,kBAAkB;IACjC,YAAY,EAAE,cAAc,EAAE,CAAC;IAC/B,YAAY,EAAE,IAAI,EAAE,CAAC;IACrB,kEAAkE;IAClE,MAAM,CAAC,EAAE,SAAS,CAAC;CACpB;AAED,wBAAgB,aAAa,CAAC,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,EAAE,EAAE,kBAAkB,2CAWvF"}
@@ -1,7 +1,8 @@
1
1
  'use client';
2
2
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
3
  import { ReactFlowProvider } from '@xyflow/react';
4
+ import { SessionsProvider } from '../../../hooks/sessions-provider.js';
4
5
  import { ControlCenterInner } from './control-center-inner.js';
5
6
  export function ControlCenter({ initialNodes, initialEdges, drawer }) {
6
- return (_jsxs("div", { "data-testid": "control-center", className: "h-full w-full", children: [_jsx(ReactFlowProvider, { children: _jsx(ControlCenterInner, { initialNodes: initialNodes, initialEdges: initialEdges }) }), _jsx("div", { children: drawer }, "drawer")] }));
7
+ return (_jsxs("div", { "data-testid": "control-center", className: "h-full w-full", children: [_jsx(SessionsProvider, { children: _jsx(ReactFlowProvider, { children: _jsx(ControlCenterInner, { initialNodes: initialNodes, initialEdges: initialEdges }) }) }), _jsx("div", { children: drawer }, "drawer")] }));
7
8
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CA+kBpB"}
1
+ {"version":3,"file":"use-control-center-state.d.ts","sourceRoot":"","sources":["../../../../../../../src/presentation/web/components/features/control-center/use-control-center-state.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAClE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kCAAkC,CAAC;AACxE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,qCAAqC,CAAC;AAC9E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uCAAuC,CAAC;AAC5E,OAAO,EAGL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAC;AAkBjC,OAAO,EAAiB,KAAK,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAM7E,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,KAAK,EAAE,IAAI,EAAE,CAAC;IACd,aAAa,EAAE,CAAC,OAAO,EAAE,UAAU,CAAC,cAAc,CAAC,EAAE,KAAK,IAAI,CAAC;IAC/D,aAAa,EAAE,CAAC,UAAU,EAAE,UAAU,KAAK,IAAI,CAAC;IAChD,mBAAmB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/E,YAAY,EAAE,CAAC,SAAS,EAAE,eAAe,KAAK,IAAI,CAAC;IACnD,oBAAoB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAClD,mBAAmB,EAAE,CACnB,SAAS,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,OAAO,EACjB,aAAa,CAAC,EAAE,OAAO,EACvB,OAAO,CAAC,EAAE,OAAO,KACd,IAAI,CAAC;IACV,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,kBAAkB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C,sBAAsB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACpD,sBAAsB,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAChE,iBAAiB,EAAE,CACjB,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,YAAY,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,EACvC,QAAQ,CAAC,EAAE,MAAM,KACd,MAAM,CAAC;IACZ,yDAAyD;IACzD,YAAY,EAAE,OAAO,CAAC;IACtB,0CAA0C;IAC1C,eAAe,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAC;IACzC,wDAAwD;IACxD,wBAAwB,EAAE,CAAC,aAAa,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACxE,gDAAgD;IAChD,iBAAiB,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,kBAAkB,GAAG,SAAS,CAAC;IACtE,0EAA0E;IAC1E,YAAY,EAAE,CAAC,SAAS,EAAE,cAAc,KAAK,IAAI,CAAC;CACnD;AAQD,wBAAgB,qBAAqB,CACnC,YAAY,EAAE,cAAc,EAAE,EAC9B,YAAY,EAAE,IAAI,EAAE,GACnB,kBAAkB,CAilBpB"}
@@ -18,7 +18,7 @@ import { createLogger } from '../../../lib/logger.js';
18
18
  import { mapEventTypeToState, resolveSseEventUpdates, } from '../../common/feature-node/derive-feature-state.js';
19
19
  import { useGraphState } from '../../../hooks/use-graph-state.js';
20
20
  const log = createLogger('[Polling]');
21
- const POLL_INTERVAL_MS = 3_000;
21
+ const POLL_INTERVAL_MS = 15_000;
22
22
  /** Must match the message string emitted by the SSE route in agent-events/route.ts */
23
23
  const METADATA_UPDATED_MESSAGE = 'Feature metadata updated';
24
24
  let nextFeatureId = 0;
@@ -175,6 +175,9 @@ export function useControlCenterState(initialNodes, initialEdges) {
175
175
  useEffect(() => {
176
176
  log.debug(`polling enabled (${POLL_INTERVAL_MS}ms interval)`);
177
177
  const timer = setInterval(async () => {
178
+ // Skip when tab is hidden — no point polling for a user who isn't looking.
179
+ if (document.hidden)
180
+ return;
178
181
  // Skip fetch entirely while a mutation is in-flight — the response
179
182
  // would contain pre-mutation data that reconcile would discard anyway.
180
183
  if (isMutating())
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from 'react';
2
+ import type { SessionSummary } from '../components/common/feature-node/feature-sessions-dropdown.js';
3
+ interface SessionsContextValue {
4
+ getSessionsForPath: (path: string) => SessionSummary[];
5
+ hasActiveSessions: (path: string) => boolean;
6
+ }
7
+ export declare function SessionsProvider({ children }: {
8
+ children: ReactNode;
9
+ }): import("react/jsx-runtime").JSX.Element;
10
+ export declare function useSessionsContext(): SessionsContextValue;
11
+ export {};
12
+ //# sourceMappingURL=sessions-provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sessions-provider.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/sessions-provider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAQL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4DAA4D,CAAC;AAOjG,UAAU,oBAAoB;IAC5B,kBAAkB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,cAAc,EAAE,CAAC;IACvD,iBAAiB,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,OAAO,CAAC;CAC9C;AAMD,wBAAgB,gBAAgB,CAAC,EAAE,QAAQ,EAAE,EAAE;IAAE,QAAQ,EAAE,SAAS,CAAA;CAAE,2CAoDrE;AAID,wBAAgB,kBAAkB,IAAI,oBAAoB,CAMzD"}
@@ -0,0 +1,57 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
4
+ const POLL_INTERVAL_MS = 30_000;
5
+ const ACTIVE_THRESHOLD_MS = 5 * 60 * 1000; // 5 minutes
6
+ const SessionsContext = createContext(null);
7
+ // ── Provider component ────────────────────────────────────────────────
8
+ export function SessionsProvider({ children }) {
9
+ const [sessionsByPath, setSessionsByPath] = useState(new Map());
10
+ const sessionsByPathRef = useRef(sessionsByPath);
11
+ sessionsByPathRef.current = sessionsByPath;
12
+ const fetchSessions = useCallback(async () => {
13
+ if (document.hidden)
14
+ return;
15
+ try {
16
+ const res = await fetch('/api/sessions-batch');
17
+ if (!res.ok)
18
+ return;
19
+ const data = (await res.json());
20
+ const next = new Map();
21
+ for (const [path, sessions] of Object.entries(data.sessionsByPath)) {
22
+ next.set(path, sessions);
23
+ }
24
+ setSessionsByPath(next);
25
+ }
26
+ catch {
27
+ // Silent — stale data is better than no data
28
+ }
29
+ }, []);
30
+ useEffect(() => {
31
+ void fetchSessions();
32
+ const timer = setInterval(() => void fetchSessions(), POLL_INTERVAL_MS);
33
+ return () => clearInterval(timer);
34
+ }, [fetchSessions]);
35
+ const getSessionsForPath = useCallback((path) => sessionsByPathRef.current.get(path) ?? [],
36
+ // eslint-disable-next-line react-hooks/exhaustive-deps
37
+ [sessionsByPath]);
38
+ const hasActiveSessions = useCallback((path) => {
39
+ const sessions = sessionsByPathRef.current.get(path);
40
+ if (!sessions)
41
+ return false;
42
+ const now = Date.now();
43
+ return sessions.some((s) => s.lastMessageAt && now - new Date(s.lastMessageAt).getTime() < ACTIVE_THRESHOLD_MS);
44
+ },
45
+ // eslint-disable-next-line react-hooks/exhaustive-deps
46
+ [sessionsByPath]);
47
+ const value = useMemo(() => ({ getSessionsForPath, hasActiveSessions }), [getSessionsForPath, hasActiveSessions]);
48
+ return _jsx(SessionsContext.Provider, { value: value, children: children });
49
+ }
50
+ // ── Consumer hook ─────────────────────────────────────────────────────
51
+ export function useSessionsContext() {
52
+ const ctx = useContext(SessionsContext);
53
+ if (!ctx) {
54
+ return { getSessionsForPath: () => [], hasActiveSessions: () => false };
55
+ }
56
+ return ctx;
57
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAmOlF"}
1
+ {"version":3,"file":"use-deploy-action.d.ts","sourceRoot":"","sources":["../../../../../src/presentation/web/hooks/use-deploy-action.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,sCAAsC,CAAC;AAM5E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,SAAS,GAAG,YAAY,CAAC;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5B,IAAI,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1B,aAAa,EAAE,OAAO,CAAC;IACvB,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,EAAE,eAAe,GAAG,IAAI,CAAC;IAC/B,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;CACpB;AAMD,wBAAgB,eAAe,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,GAAG,iBAAiB,CAwLlF"}