@nextclaw/ui 0.19.5 → 0.20.0

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 (265) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-DrbfiNNF.js → abnfDiagram-VRR7QNED-BcFJp_1X.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-DdLb2IVc.js → aiden0z-pptx-renderer.es-Dp2ezlQn.js} +1 -1
  4. package/dist/assets/api-C2iYhdTe.js +13 -0
  5. package/dist/assets/{appearance-settings-page-cTuZdROP.js → appearance-settings-page-BwQDQr0n.js} +1 -1
  6. package/dist/assets/{arc-BXlMc9B0.js → arc-CkgyPMyW.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-DO2x53FK.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-DXAJxxO0.js → architectureDiagram-ZJ3FMSHR-CMd4cIjI.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-i4Kz7zU0.js → blockDiagram-677ZJIJ3-Bz3tbhet.js} +1 -1
  10. package/dist/assets/book-open-C0IlT9zs.js +1 -0
  11. package/dist/assets/{c4Diagram-LMCZKHZV-BDc0TkPd.js → c4Diagram-LMCZKHZV-D9hFNAGv.js} +1 -1
  12. package/dist/assets/channel-nBvMaVyP.js +1 -0
  13. package/dist/assets/channels-list-page-B0z4bh8e.js +8 -0
  14. package/dist/assets/chunk-2Q5K7J3B-CJgqFDS4.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-DuReei0Z.js → chunk-32BRIVSS-7NrjEITq.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-D8APq0tm.js → chunk-52WLFC77-viIcqj9q.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-BsPrNXNS.js → chunk-5FCAYU7R-Vsu4J-VM.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-BRtkOPoJ.js → chunk-5HE753X5-C-w6CQZK.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-ChPc8e8U.js → chunk-5JV3BV7I-CuKgJQ4W.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-CzTjlJGb.js → chunk-5TONJI2A-CYeiWZKb.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-xsv_22jR.js → chunk-5VM5RSS4-DRCbfwIn.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-CwECujme.js → chunk-7BUUIJ7U-BvydOcm3.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-DQAm173p.js → chunk-BIQX33UG-RlppOXKo.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-BkpajeQb.js → chunk-C7G6YPKG-DAizzYwr.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-ND65pWRA.js → chunk-CQNSW5MT-5eChJHca.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-vMu6ijxG.js → chunk-CYSBUYHQ-YHPr53fv.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-CEaeW3gH.js → chunk-EMLP6XTP-CK4D7Hz5.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-a4ER-HDr.js → chunk-EX3LRPZG-BmlDFIRW.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-C_8fQssn.js → chunk-FWX5IMBZ-D0ksHZZ9.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-CEvw-PEd.js → chunk-HOUHSVGY-DPkq8FCm.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-BEtF0fGY.js → chunk-ICXQ74PX-FAatv5fC.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-DGRrVp8-.js → chunk-JG7HCLWE-DLkIi4F7.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-DRqpp_nM.js → chunk-JWPE2WC7-DawOjNE3.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-BDEriOXs.js → chunk-MOJQB5TN-Di52TorW.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-JWRNzdnw.js → chunk-MOZMSUNE-PfyK9w1-.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-DS2VTgCx.js → chunk-OGEWGWER-C3jdH3Ty.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-3LiYTBUB.js → chunk-OSBZ3O6U-CecHo-f0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-o9M2s_SO.js → chunk-PUDLZKDR-f3IJh3sN.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-DTB7Ma2N.js → chunk-Q4XR5HBZ-DQFeUPxZ.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-DpkxZJL5.js → chunk-QBLGF6JB-BhUeG0y2.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-CRejkHJU.js → chunk-R7FJI6CG-C3XpZ-2u.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-B1boBjLt.js → chunk-RYQCIY6F-CAqa5klQ.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-DgKa97bj.js → chunk-U6XO7XAA-D_WXezeD.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-DE56eh0N.js → chunk-V7JOEXUC-CZ70oOtp.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-BGvh3yIx.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-BzomPZq2.js → chunk-VR4S4FIN-B7AHEYYk.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-C6gy8rSd.js → chunk-WYO6CB5R-Dn9To_OI.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-DaBxG5gs.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-BK98yfl9.js → chunk-YOTPTUD7-BYEgRBk3.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-DFXAhwUT.js → chunk-ZGVPDNZ5-C6YtZDU0.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-Bbu6BiNC.js → chunk-ZIRB5QZD-DMuYtnHF.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-P6N03G2R.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-B1_uLvMv.js +1 -0
  54. package/dist/assets/{config-split-page-DO1Oyb-r.js → config-split-page-VDTf3zZb.js} +1 -1
  55. package/dist/assets/confirm-dialog-BI_Vt2s_.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-BV8KR4uF.js → cose-bilkent-JH36ORCC-Bc2Ex5Y3.js} +1 -1
  57. package/dist/assets/createLucideIcon-Bydi9Gc5.js +1 -0
  58. package/dist/assets/cynefin-VYW2F7L2-B-H8Qp-n.js +1 -0
  59. package/dist/assets/{cynefinDiagram-TSTJHNR4-Ckd6nU9t.js → cynefinDiagram-TSTJHNR4-DVysQoGz.js} +1 -1
  60. package/dist/assets/{dagre-D_T-FW5i.js → dagre-CMhQ1cAG.js} +1 -1
  61. package/dist/assets/{dagre-VKFMJZFB-CyDaQpD4.js → dagre-VKFMJZFB-RYyWpPJZ.js} +1 -1
  62. package/dist/assets/{desktop-update-config-XSV-DjJW.js → desktop-update-config-DVoD6NxG.js} +1 -1
  63. package/dist/assets/{diagram-FQU43EPY-CghnwS0H.js → diagram-FQU43EPY-BD6KONdF.js} +1 -1
  64. package/dist/assets/{diagram-G47NLZAW-DK4DJ3Kx.js → diagram-G47NLZAW-CGB2ptRR.js} +1 -1
  65. package/dist/assets/{diagram-NH7WQ7WH-CUgDkZCJ.js → diagram-NH7WQ7WH-DnkOI6cU.js} +1 -1
  66. package/dist/assets/{diagram-OA4YK3LP-vO2gyH6n.js → diagram-OA4YK3LP-DF82RZ7P.js} +1 -1
  67. package/dist/assets/{diagram-WEI45ONY-DbSBRDSr.js → diagram-WEI45ONY-Be2v7Ck9.js} +1 -1
  68. package/dist/assets/{dist-C-2H76Cv.js → dist-A5btQciA.js} +1 -1
  69. package/dist/assets/dist-CmHtkFbs.js +1 -0
  70. package/dist/assets/doc-browser-Cm_tMZ_C.js +1 -0
  71. package/dist/assets/doc-browser-Dto54tCf.js +1 -0
  72. package/dist/assets/{doc-browser-context-C0Bns1J6.js → doc-browser-context-Bxw0ora-.js} +1 -1
  73. package/dist/assets/{docx-preview-DfxIm9IB.js → docx-preview-BqwAjrj3.js} +1 -1
  74. package/dist/assets/{ebnfDiagram-CCIWWBDH-W7CAcK7r.js → ebnfDiagram-CCIWWBDH-CP98OYk8.js} +1 -1
  75. package/dist/assets/{erDiagram-Q63AITRT-LA0JWL8-.js → erDiagram-Q63AITRT-ZLlx8xfs.js} +1 -1
  76. package/dist/assets/eventmodeling-45OFAUF4-YVtShyFA.js +1 -0
  77. package/dist/assets/extensions-CG4voDCh.js +1 -0
  78. package/dist/assets/flowDiagram-23GEKE2U-BeKXabVO.js +1 -0
  79. package/dist/assets/{form-actions-CMxhPact.js → form-actions-lv47uGIB.js} +1 -1
  80. package/dist/assets/{ganttDiagram-NO4QXBWP-_PiTPavJ.js → ganttDiagram-NO4QXBWP-DXCDjHR2.js} +1 -1
  81. package/dist/assets/gitGraph-TEB2WS4Q-jTSiBj-p.js +1 -0
  82. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DsRB1ZMU.js → gitGraphDiagram-IHSO6WYX-l2HWrTJp.js} +1 -1
  83. package/dist/assets/index-BTSwNDVi.js +109 -0
  84. package/dist/assets/index-QrdUWHfm.css +1 -0
  85. package/dist/assets/info-DKCQHKI2-BIporNWb.js +1 -0
  86. package/dist/assets/{infoDiagram-FWYZ7A6U-wIRraYx5.js → infoDiagram-FWYZ7A6U-D-DjX-bn.js} +1 -1
  87. package/dist/assets/{ishikawaDiagram-FXEZZL3T-CNzsrcrh.js → ishikawaDiagram-FXEZZL3T-y_Xy128F.js} +1 -1
  88. package/dist/assets/{journeyDiagram-5HDEW3XC-Dy9huHxy.js → journeyDiagram-5HDEW3XC-DJn5xYcQ.js} +1 -1
  89. package/dist/assets/{kanban-definition-HUTT4EX6-BOCIN365.js → kanban-definition-HUTT4EX6-DJVIOR21.js} +1 -1
  90. package/dist/assets/{line-BxsizOTs.js → line-Cos7PIOj.js} +1 -1
  91. package/dist/assets/{linear-C_YHf92I.js → linear-BtDLqtzF.js} +1 -1
  92. package/dist/assets/loader-circle-CkaqeEne.js +1 -0
  93. package/dist/assets/{map-CobcjgZ8.js → map-BKnQyvd0.js} +1 -1
  94. package/dist/assets/mcp-marketplace-page-BSk0qUOX.js +9 -0
  95. package/dist/assets/mcp-marketplace-page-BgZGc-S_.js +1 -0
  96. package/dist/assets/{mermaid-parser.core-D7lu2RbA.js → mermaid-parser.core-XUNZhr2W.js} +2 -2
  97. package/dist/assets/{mermaid.core-CbfW2Rk8.js → mermaid.core-ySrvolIe.js} +3 -3
  98. package/dist/assets/{mindmap-definition-LN4V7U3C-DUjGXlIC.js → mindmap-definition-LN4V7U3C-BypL9tei.js} +1 -1
  99. package/dist/assets/model-config-page-c8RnUGoH.js +1 -0
  100. package/dist/assets/{navigation-link-v3FaWcIr.js → navigation-link-D0agTj5t.js} +1 -1
  101. package/dist/assets/{ordinal-CjoUCHto.js → ordinal-CxxVWxYU.js} +1 -1
  102. package/dist/assets/packet-7NZHBO7P-CuFMADV0.js +1 -0
  103. package/dist/assets/{pegDiagram-2B236MQR-BQY2DgiP.js → pegDiagram-2B236MQR-D-U2KBGv.js} +1 -1
  104. package/dist/assets/pie-RZYD4A2V-XUeIXhM9.js +1 -0
  105. package/dist/assets/{pieDiagram-ENE6RG2P-D18owe4D.js → pieDiagram-ENE6RG2P-CBi3GusJ.js} +1 -1
  106. package/dist/assets/plus-BiMjkv3R.js +1 -0
  107. package/dist/assets/{privacy-settings-page-CYM1Dxwo.js → privacy-settings-page-CPkyTvEE.js} +1 -1
  108. package/dist/assets/providers-config-page-CGUEb5Dx.js +1 -0
  109. package/dist/assets/{quadrantDiagram-ABIIQ3AL-B8F7YoTA.js → quadrantDiagram-ABIIQ3AL-H0ZuvnZL.js} +1 -1
  110. package/dist/assets/radar-I7S5WNFK-D-NNGh1k.js +1 -0
  111. package/dist/assets/railroad-3IZDKUUU-C67_uDaM.js +1 -0
  112. package/dist/assets/railroad-abnf-AHOZXSZD-BZ8Yflnd.js +1 -0
  113. package/dist/assets/railroad-ebnf-EBAXGLYW-B7zO7RoK.js +1 -0
  114. package/dist/assets/railroad-peg-LSFZ7HO6-R63PTgLz.js +1 -0
  115. package/dist/assets/{railroadDiagram-RFXS5EU6-RU8GpxN5.js → railroadDiagram-RFXS5EU6-D7Y2EqSd.js} +1 -1
  116. package/dist/assets/react-BMYFkaGi.js +8 -0
  117. package/dist/assets/refresh-cw-DjgY2Eju.js +1 -0
  118. package/dist/assets/remote-1LxkpRED.js +1 -0
  119. package/dist/assets/{requirementDiagram-TGXJPOKE-ubPOmmNi.js → requirementDiagram-TGXJPOKE-BiXNX1NN.js} +1 -1
  120. package/dist/assets/rotate-cw-Dot4PVh0.js +1 -0
  121. package/dist/assets/{runtime-config-page-C7vksFVD.js → runtime-config-page-C6nzqTHg.js} +1 -1
  122. package/dist/assets/{sankeyDiagram-HTMAVEWB-8L_hXJWW.js → sankeyDiagram-HTMAVEWB-qzDeLztW.js} +1 -1
  123. package/dist/assets/save-B-jdTe_c.js +1 -0
  124. package/dist/assets/search-CEp2AIiV.js +1 -0
  125. package/dist/assets/{search-config-page-DKdEf7m9.js → search-config-page-Dp5guS5O.js} +1 -1
  126. package/dist/assets/{secrets-config-page-COlMqSfL.js → secrets-config-page-C7nRMjiv.js} +2 -2
  127. package/dist/assets/security-config-DvHUDxSg.js +1 -0
  128. package/dist/assets/{select-BACzapV3.js → select-BNA3cma8.js} +2 -2
  129. package/dist/assets/{sequenceDiagram-DBY2YBRQ-Dom6Qor5.js → sequenceDiagram-DBY2YBRQ-BtRbPQtL.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-cZcFSpLJ.js → sizeCapture-X5ZJPWSS-CMYvM3SL.js} +1 -1
  131. package/dist/assets/skeleton-CbroEO8Q.js +1 -0
  132. package/dist/assets/{src-BE9E1xcR.js → src-Bw-mzbQb.js} +1 -1
  133. package/dist/assets/{stateDiagram-2N3HPSRC-CHrpxmGO.js → stateDiagram-2N3HPSRC-BJ-mFeOz.js} +1 -1
  134. package/dist/assets/stateDiagram-v2-6OUMAXLB-BE-HqxOK.js +1 -0
  135. package/dist/assets/{swimlanes-5IMT3BWC-D6nJS2xQ.js → swimlanes-5IMT3BWC-BsucVOO2.js} +2 -2
  136. package/dist/assets/swimlanesDiagram-G3AALYLV-Dr1H9LJH.js +8 -0
  137. package/dist/assets/{tag-chip-BnamTQVX.js → tag-chip-DmCNi4-9.js} +1 -1
  138. package/dist/assets/{timeline-definition-FHXFAJF6-DJNn-HQO.js → timeline-definition-FHXFAJF6-Bicewlzo.js} +1 -1
  139. package/dist/assets/treeView-QDETBFTQ-npJ5dbqS.js +1 -0
  140. package/dist/assets/treemap-6X3UGDF4-pYrBx6oa.js +1 -0
  141. package/dist/assets/{vennDiagram-L72KCM5P-CCEzHoXt.js → vennDiagram-L72KCM5P-BxOOnO9x.js} +1 -1
  142. package/dist/assets/wardley-OPB4EBWU-CmSRKLR8.js +1 -0
  143. package/dist/assets/{wardleyDiagram-EHGQE667-BhyVxVXt.js → wardleyDiagram-EHGQE667-Cl7NltMv.js} +1 -1
  144. package/dist/assets/x-BTo5-A8h.js +1 -0
  145. package/dist/assets/{xychartDiagram-FW5EYKEG-CacW_246.js → xychartDiagram-FW5EYKEG-DiMIxQ7N.js} +1 -1
  146. package/dist/index.html +23 -22
  147. package/package.json +8 -8
  148. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +4 -0
  149. package/src/app/configs/app-navigation.config.ts +6 -0
  150. package/src/app/index.tsx +7 -0
  151. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +33 -1
  152. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +1 -0
  153. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +18 -1
  154. package/src/features/chat/features/conversation/hooks/__tests__/use-session-run-queue.test.tsx +26 -0
  155. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +4 -1
  156. package/src/features/chat/features/conversation/hooks/use-session-run-queue.ts +8 -2
  157. package/src/features/chat/features/message/components/__tests__/chat-message-observation-event.test.tsx +46 -0
  158. package/src/features/chat/features/message/components/chat-message-list.container.tsx +11 -0
  159. package/src/features/chat/features/message/components/chat-message-observation-event.tsx +92 -0
  160. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +38 -0
  161. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +52 -0
  162. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +34 -0
  163. package/src/features/chat/features/message/utils/chat-message-observation-event.utils.ts +81 -0
  164. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +15 -1
  165. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +62 -1
  166. package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +36 -2
  167. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +28 -0
  168. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +10 -2
  169. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +35 -0
  170. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +26 -47
  171. package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +73 -0
  172. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +28 -0
  173. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +3 -1
  174. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +3 -4
  175. package/src/features/chat/features/workspace/components/__tests__/chat-session-child-sessions.test.tsx +27 -1
  176. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +13 -0
  177. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +29 -0
  178. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +19 -0
  179. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +36 -2
  180. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +4 -0
  181. package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +28 -17
  182. package/src/features/chat/features/workspace/components/overview/__tests__/chat-session-continuous-attention.test.tsx +67 -0
  183. package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +234 -0
  184. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +9 -7
  185. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +9 -4
  186. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +25 -0
  187. package/src/features/chat/managers/chat-thread.manager.ts +5 -7
  188. package/src/features/chat/stores/chat-thread.store.ts +7 -4
  189. package/src/features/extensions/hooks/use-extensions.ts +11 -0
  190. package/src/features/extensions/index.ts +1 -0
  191. package/src/features/extensions/pages/__tests__/extensions-config-page.test.tsx +34 -0
  192. package/src/features/extensions/pages/extensions-config-page.tsx +136 -0
  193. package/src/shared/lib/api/ncp-session.types.ts +38 -0
  194. package/src/shared/lib/api/runtime-control.types.ts +17 -0
  195. package/src/shared/lib/api/types.ts +6 -0
  196. package/src/shared/lib/api/utils/ncp-session.utils.ts +35 -1
  197. package/src/shared/lib/api/utils/runtime-control.utils.ts +8 -0
  198. package/src/shared/lib/i18n/locales/en-US/chat.json +40 -0
  199. package/src/shared/lib/i18n/locales/en-US/core.json +27 -0
  200. package/src/shared/lib/i18n/locales/zh-CN/chat.json +40 -0
  201. package/src/shared/lib/i18n/locales/zh-CN/core.json +27 -0
  202. package/dist/assets/api-CUSlBpt_.js +0 -13
  203. package/dist/assets/architecture-TIHT7OUA-Bq0i9WiE.js +0 -1
  204. package/dist/assets/book-open-BJ-xAIp9.js +0 -1
  205. package/dist/assets/channel-iCaZQ87u.js +0 -1
  206. package/dist/assets/channels-list-page-nRHOasWA.js +0 -8
  207. package/dist/assets/chunk-2Q5K7J3B-DFQtlnJS.js +0 -1
  208. package/dist/assets/chunk-VAUOI2AC-CF9A-TeE.js +0 -1
  209. package/dist/assets/chunk-XXDRQBXY-BRxdfVCU.js +0 -1
  210. package/dist/assets/classDiagram-OUVF2IWQ-Bo28fPi-.js +0 -1
  211. package/dist/assets/classDiagram-v2-EOCWNBFH-BOVN9wUz.js +0 -1
  212. package/dist/assets/confirm-dialog-CCae9Fr4.js +0 -5
  213. package/dist/assets/cynefin-VYW2F7L2-B7rF3JRp.js +0 -1
  214. package/dist/assets/dist-1N9UpL3c.js +0 -1
  215. package/dist/assets/doc-browser-BHFCZIoL.js +0 -1
  216. package/dist/assets/doc-browser-CRlpqMwH.js +0 -1
  217. package/dist/assets/eventmodeling-45OFAUF4-DzQLXa3s.js +0 -1
  218. package/dist/assets/flowDiagram-23GEKE2U-C81eexJZ.js +0 -1
  219. package/dist/assets/gitGraph-TEB2WS4Q-Dt3ZqpFD.js +0 -1
  220. package/dist/assets/index-DdYOaKm7.js +0 -109
  221. package/dist/assets/index-DxCGKhAw.css +0 -1
  222. package/dist/assets/info-DKCQHKI2-CCCE7dXC.js +0 -1
  223. package/dist/assets/loader-circle-B-UdXjl4.js +0 -1
  224. package/dist/assets/mcp-marketplace-page-BUIGtSda.js +0 -1
  225. package/dist/assets/mcp-marketplace-page-CStb8ml2.js +0 -9
  226. package/dist/assets/model-config-page-BkfUYm95.js +0 -1
  227. package/dist/assets/packet-7NZHBO7P-D19yoxFV.js +0 -1
  228. package/dist/assets/pie-RZYD4A2V-hMaJbLrn.js +0 -1
  229. package/dist/assets/plus-Dw9QFnKr.js +0 -1
  230. package/dist/assets/providers-config-page-fxL4I4bz.js +0 -1
  231. package/dist/assets/radar-I7S5WNFK-C8BkvScA.js +0 -1
  232. package/dist/assets/railroad-3IZDKUUU-CozZOitC.js +0 -1
  233. package/dist/assets/railroad-abnf-AHOZXSZD-BqIoFXFi.js +0 -1
  234. package/dist/assets/railroad-ebnf-EBAXGLYW-CD2e3dng.js +0 -1
  235. package/dist/assets/railroad-peg-LSFZ7HO6-DoKlRUOF.js +0 -1
  236. package/dist/assets/react-Dlz_IxoP.js +0 -8
  237. package/dist/assets/refresh-cw-CK4I5UFv.js +0 -1
  238. package/dist/assets/remote-B0pPgtC6.js +0 -1
  239. package/dist/assets/rotate-cw-Z48dmh13.js +0 -1
  240. package/dist/assets/save-Bo37cIFb.js +0 -1
  241. package/dist/assets/search-CZiEm7YD.js +0 -1
  242. package/dist/assets/security-config-CitLihXA.js +0 -1
  243. package/dist/assets/skeleton-B2PCAZJG.js +0 -1
  244. package/dist/assets/stateDiagram-v2-6OUMAXLB-b1pZQlDD.js +0 -1
  245. package/dist/assets/swimlanesDiagram-G3AALYLV-CowxgWVn.js +0 -8
  246. package/dist/assets/treeView-QDETBFTQ-BQlYkJbz.js +0 -1
  247. package/dist/assets/treemap-6X3UGDF4-DzTOd1bd.js +0 -1
  248. package/dist/assets/wardley-OPB4EBWU-D80jQc0y.js +0 -1
  249. package/dist/assets/x-BzYEzkLA.js +0 -1
  250. /package/dist/assets/{array-COEOjKUv.js → array-BQVzeGRt.js} +0 -0
  251. /package/dist/assets/{chunk-KEIR6QF5-D7oHAcry.js → chunk-KEIR6QF5-f9psyCXK.js} +0 -0
  252. /package/dist/assets/{chunk-Y2CYZVJY-08IUpgTP.js → chunk-Y2CYZVJY-D1mAU_Po.js} +0 -0
  253. /package/dist/assets/{config-hints-BoE41XDI.js → config-hints-nV5HPOvq.js} +0 -0
  254. /package/dist/assets/{cytoscape.esm-HifWufkp.js → cytoscape.esm-0uDtdD0l.js} +0 -0
  255. /package/dist/assets/{defaultLocale-CA-_SsC1.js → defaultLocale-DSWXGgt0.js} +0 -0
  256. /package/dist/assets/{dist-C60rvfy8.js → dist--ta5rX12.js} +0 -0
  257. /package/dist/assets/{graphlib-CGYCNsoJ.js → graphlib-CQ6QV4yP.js} +0 -0
  258. /package/dist/assets/{host-capabilities-DPNRAjIj.js → host-capabilities-Tk038i04.js} +0 -0
  259. /package/dist/assets/{init-C3plFVsG.js → init-Y6h9jGtZ.js} +0 -0
  260. /package/dist/assets/{jszip.min-CeYs17Gn.js → jszip.min-8hyJpgpx.js} +0 -0
  261. /package/dist/assets/{katex-DRThweSG.js → katex-ARhY9PS1.js} +0 -0
  262. /package/dist/assets/{middleware-D05fK5Sb.js → middleware-DXsbf9xI.js} +0 -0
  263. /package/dist/assets/{path-BKtKxQ6Y.js → path-10BjdyLh.js} +0 -0
  264. /package/dist/assets/{rough.esm-DurL7-u7.js → rough.esm-DxgC_14r.js} +0 -0
  265. /package/dist/assets/{xlsx-Sg8fZ6nH.js → xlsx-DrDuDNAa.js} +0 -0
@@ -2,6 +2,7 @@ import { ChevronRight, GitBranch, MessageSquarePlus } from 'lucide-react';
2
2
 
3
3
  import { usePresenter } from '@/features/chat/components/providers/chat-presenter.provider';
4
4
  import type { ResolvedChildSessionTab } from '@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view';
5
+ import { ChatSessionMoreActionsMenu } from '@/features/chat/features/session/components/session-header/chat-session-more-actions-menu';
5
6
  import { t } from '@/shared/lib/i18n';
6
7
 
7
8
  export function ChatSessionChildSessions({
@@ -38,25 +39,35 @@ export function ChatSessionChildSessions({
38
39
  ) : (
39
40
  <div className="mt-4 space-y-2">
40
41
  {childSessionTabs.map((tab) => (
41
- <button
42
+ <div
42
43
  key={tab.sessionKey}
43
- type="button"
44
- className="group flex w-full items-center gap-3 rounded-lg border border-gray-200/80 bg-white px-3 py-3 text-left transition-colors hover:border-gray-300 hover:bg-gray-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/25"
45
- onClick={() => presenter.chatThreadManager.selectChildSessionDetail(tab.sessionKey)}
44
+ className="group relative flex w-full items-center rounded-lg border border-gray-200/80 bg-white transition-colors hover:border-gray-300 hover:bg-gray-50"
46
45
  >
47
- <span className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-100 text-gray-600">
48
- <GitBranch className="h-4 w-4" />
49
- </span>
50
- <span className="min-w-0 flex-1">
51
- <span className="block truncate text-sm font-medium text-gray-900">{tab.title}</span>
52
- {tab.projectName || tab.sessionTypeLabel ? (
53
- <span className="mt-0.5 block truncate text-xs text-gray-500">
54
- {[tab.sessionTypeLabel, tab.projectName].filter(Boolean).join(' · ')}
55
- </span>
56
- ) : null}
57
- </span>
58
- <ChevronRight className="h-4 w-4 shrink-0 text-gray-300 transition-transform group-hover:translate-x-0.5 group-hover:text-gray-500" />
59
- </button>
46
+ <button
47
+ type="button"
48
+ className="flex min-w-0 flex-1 items-center gap-3 px-3 py-3 pr-12 text-left focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-primary/25"
49
+ onClick={() => presenter.chatThreadManager.selectChildSessionDetail(tab.sessionKey)}
50
+ >
51
+ <span className="inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-100 text-gray-600">
52
+ <GitBranch className="h-4 w-4" />
53
+ </span>
54
+ <span className="min-w-0 flex-1">
55
+ <span className="block truncate text-sm font-medium text-gray-900">{tab.title}</span>
56
+ {tab.projectName || tab.sessionTypeLabel ? (
57
+ <span className="mt-0.5 block truncate text-xs text-gray-500">
58
+ {[tab.sessionTypeLabel, tab.projectName].filter(Boolean).join(' · ')}
59
+ </span>
60
+ ) : null}
61
+ </span>
62
+ <ChevronRight className="h-4 w-4 shrink-0 text-gray-300 transition-transform group-hover:translate-x-0.5 group-hover:text-gray-500" />
63
+ </button>
64
+ <ChatSessionMoreActionsMenu
65
+ sessionKey={tab.sessionKey}
66
+ triggerSize="sm"
67
+ triggerTone="strong"
68
+ className="absolute right-2 top-1/2 -translate-y-1/2 bg-white opacity-100 md:opacity-0 md:group-hover:opacity-100 md:group-focus-within:opacity-100"
69
+ />
70
+ </div>
60
71
  ))}
61
72
  </div>
62
73
  )}
@@ -0,0 +1,67 @@
1
+ import { render, screen } from '@testing-library/react';
2
+ import userEvent from '@testing-library/user-event';
3
+ import { beforeEach, expect, it, vi } from 'vitest';
4
+ import { ChatSessionContinuousAttention } from '@/features/chat/features/workspace/components/overview/chat-session-continuous-attention';
5
+
6
+ const mocks = vi.hoisted(() => ({
7
+ mutateAsync: vi.fn(),
8
+ }));
9
+
10
+ vi.mock('@/features/chat/features/ncp/hooks/use-ncp-session-queries', () => ({
11
+ useNcpSessionObservations: () => ({
12
+ data: {
13
+ sessionId: 'session-1',
14
+ bindings: [{
15
+ id: 'binding-1',
16
+ kind: 'context',
17
+ extensionId: 'world-extension',
18
+ title: 'World',
19
+ description: 'Latest state',
20
+ status: 'active',
21
+ createdAt: '2026-08-23T08:00:00.000Z',
22
+ lastReadAt: '2026-08-23T08:05:00.000Z',
23
+ safeConfigPreview: 'project: nextclaw',
24
+ }],
25
+ subscriptions: [{
26
+ id: 'subscription-1',
27
+ kind: 'events',
28
+ extensionId: 'world-extension',
29
+ title: 'World events',
30
+ status: 'paused',
31
+ createdAt: '2026-08-23T08:01:00.000Z',
32
+ pendingCount: 2,
33
+ }],
34
+ counts: { total: 2, context: 1, events: 1, needsAttention: 1 },
35
+ },
36
+ isPending: false,
37
+ isError: false,
38
+ refetch: vi.fn(),
39
+ }),
40
+ useNcpObservationAction: () => ({
41
+ isPending: false,
42
+ variables: undefined,
43
+ mutateAsync: mocks.mutateAsync,
44
+ }),
45
+ }));
46
+
47
+ beforeEach(() => {
48
+ vi.clearAllMocks();
49
+ mocks.mutateAsync.mockResolvedValue(undefined);
50
+ });
51
+
52
+ it('separates state and event relationships and manages the active relationship', async () => {
53
+ const user = userEvent.setup();
54
+ render(<ChatSessionContinuousAttention sessionKey="session-1" />);
55
+
56
+ expect(screen.getByText('World')).toBeTruthy();
57
+ expect(screen.getByText('Needs attention')).toBeTruthy();
58
+ await user.click(screen.getByRole('tab', { name: /Events/ }));
59
+ expect(screen.getByText('World events')).toBeTruthy();
60
+ await user.click(screen.getByRole('button', { name: 'Resume' }));
61
+
62
+ expect(mocks.mutateAsync).toHaveBeenCalledWith({
63
+ kind: 'events',
64
+ id: 'subscription-1',
65
+ action: 'resume',
66
+ });
67
+ });
@@ -0,0 +1,234 @@
1
+ import { AlertTriangle, Clock3, Eye, Inbox, Pause, Play, RefreshCw, Trash2 } from 'lucide-react';
2
+ import { useState } from 'react';
3
+ import { formatDateTime, t } from '@/shared/lib/i18n';
4
+ import { useConfirmDialog } from '@/shared/hooks/use-confirm-dialog';
5
+ import {
6
+ useNcpObservationAction,
7
+ useNcpSessionObservations,
8
+ } from '@/features/chat/features/ncp/hooks/use-ncp-session-queries';
9
+ import type {
10
+ NcpSessionObservationAction,
11
+ NcpSessionObservationKind,
12
+ NcpSessionObservationView,
13
+ } from '@/shared/lib/api';
14
+ import { cn } from '@/shared/lib/utils';
15
+
16
+ type AttentionGroup = NcpSessionObservationKind;
17
+
18
+ const statusTone: Record<NcpSessionObservationView['status'], string> = {
19
+ active: 'border-emerald-200 bg-emerald-50 text-emerald-700',
20
+ paused: 'border-amber-200 bg-amber-50 text-amber-700',
21
+ degraded: 'border-orange-200 bg-orange-50 text-orange-700',
22
+ expired: 'border-gray-200 bg-gray-100 text-gray-600',
23
+ broken: 'border-rose-200 bg-rose-50 text-rose-700',
24
+ };
25
+
26
+ function replaceCount(template: string, count: number): string {
27
+ return template.replace('{count}', String(count));
28
+ }
29
+
30
+ function getStatusLabel(status: NcpSessionObservationView['status']): string {
31
+ return t(`chatWorkspaceContinuousAttentionStatus${status[0].toUpperCase()}${status.slice(1)}`);
32
+ }
33
+
34
+ function getGroupLabel(kind: AttentionGroup): string {
35
+ return t(kind === 'context' ? 'chatWorkspaceContinuousAttentionState' : 'chatWorkspaceContinuousAttentionEvents');
36
+ }
37
+
38
+ function ObservationRow({
39
+ item,
40
+ isPending,
41
+ onAction,
42
+ }: {
43
+ item: NcpSessionObservationView;
44
+ isPending: boolean;
45
+ onAction: (action: NcpSessionObservationAction) => void;
46
+ }) {
47
+ const canToggle = item.status === 'active' || item.status === 'paused';
48
+ const toggleAction = item.status === 'active' ? 'pause' : 'resume';
49
+ const toggleLabel = toggleAction === 'pause'
50
+ ? t('chatWorkspaceContinuousAttentionPause')
51
+ : t('chatWorkspaceContinuousAttentionResume');
52
+
53
+ return (
54
+ <article className="rounded-xl border border-gray-200/80 bg-white p-3 shadow-sm">
55
+ <div className="flex min-w-0 items-start gap-3">
56
+ <span className="mt-0.5 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-lg bg-gray-100 text-gray-500">
57
+ {item.kind === 'context' ? <Eye className="h-4 w-4" /> : <Inbox className="h-4 w-4" />}
58
+ </span>
59
+ <div className="min-w-0 flex-1">
60
+ <div className="flex flex-wrap items-center gap-2">
61
+ <h3 className="truncate text-sm font-semibold text-gray-900">{item.title}</h3>
62
+ <span className={cn('rounded-full border px-1.5 py-0.5 text-[10px] font-medium', statusTone[item.status])}>
63
+ {getStatusLabel(item.status)}
64
+ </span>
65
+ </div>
66
+ <p className="mt-0.5 truncate text-[11px] text-gray-400">{item.extensionId} · {getGroupLabel(item.kind)}</p>
67
+ {item.description ? <p className="mt-2 text-xs leading-5 text-gray-600">{item.description}</p> : null}
68
+ {item.statusReason ? (
69
+ <p className="mt-2 flex gap-1.5 text-xs leading-5 text-orange-700">
70
+ <AlertTriangle className="mt-0.5 h-3.5 w-3.5 shrink-0" />
71
+ <span>{item.statusReason}</span>
72
+ </p>
73
+ ) : null}
74
+ {item.safeConfigPreview ? (
75
+ <p className="mt-2 rounded-md bg-gray-50 px-2 py-1.5 text-[11px] leading-5 text-gray-500">
76
+ {item.safeConfigPreview}
77
+ </p>
78
+ ) : null}
79
+ <div className="mt-2 flex flex-wrap gap-x-3 gap-y-1 text-[11px] text-gray-400">
80
+ <span>{t('chatWorkspaceContinuousAttentionCreated')}: {formatDateTime(item.createdAt)}</span>
81
+ {item.expiresAt ? <span>{t('chatWorkspaceContinuousAttentionExpires')}: {formatDateTime(item.expiresAt)}</span> : null}
82
+ {item.lastReadAt ? <span>{t('chatWorkspaceContinuousAttentionLastRead')}: {formatDateTime(item.lastReadAt)}</span> : null}
83
+ {item.kind === 'events' ? (
84
+ <>
85
+ <span>{replaceCount(t('chatWorkspaceContinuousAttentionPending'), item.pendingCount ?? 0)}</span>
86
+ {item.suppressedCount ? <span>{replaceCount(t('chatWorkspaceContinuousAttentionSuppressed'), item.suppressedCount)}</span> : null}
87
+ {item.deliveryFailureCount ? <span className="text-rose-600">{replaceCount(t('chatWorkspaceContinuousAttentionFailures'), item.deliveryFailureCount)}</span> : null}
88
+ </>
89
+ ) : null}
90
+ </div>
91
+ </div>
92
+ </div>
93
+ <div className="mt-3 flex items-center justify-end gap-2 border-t border-gray-100 pt-2">
94
+ {canToggle ? (
95
+ <button
96
+ type="button"
97
+ data-testid={`observation-${item.id}-${toggleAction}`}
98
+ disabled={isPending}
99
+ className="inline-flex items-center gap-1.5 rounded-md border border-gray-200 px-2.5 py-1.5 text-xs font-medium text-gray-700 transition-colors hover:bg-gray-50 disabled:cursor-not-allowed disabled:opacity-50"
100
+ onClick={() => onAction(toggleAction)}
101
+ >
102
+ {toggleAction === 'pause' ? <Pause className="h-3.5 w-3.5" /> : <Play className="h-3.5 w-3.5" />}
103
+ {isPending ? t('chatWorkspaceContinuousAttentionUpdating') : toggleLabel}
104
+ </button>
105
+ ) : null}
106
+ <button
107
+ type="button"
108
+ data-testid={`observation-${item.id}-remove`}
109
+ disabled={isPending}
110
+ className="inline-flex items-center gap-1.5 rounded-md border border-rose-200 px-2.5 py-1.5 text-xs font-medium text-rose-700 transition-colors hover:bg-rose-50 disabled:cursor-not-allowed disabled:opacity-50"
111
+ onClick={() => onAction('remove')}
112
+ >
113
+ <Trash2 className="h-3.5 w-3.5" />
114
+ {t('chatWorkspaceContinuousAttentionRemove')}
115
+ </button>
116
+ </div>
117
+ </article>
118
+ );
119
+ }
120
+
121
+ export function ChatSessionContinuousAttention({ sessionKey }: { sessionKey: string | null }) {
122
+ const [activeGroup, setActiveGroup] = useState<AttentionGroup>('context');
123
+ const [actionError, setActionError] = useState(false);
124
+ const query = useNcpSessionObservations(sessionKey);
125
+ const action = useNcpObservationAction(sessionKey);
126
+ const { confirm, ConfirmDialog } = useConfirmDialog();
127
+
128
+ if (!sessionKey) return null;
129
+
130
+ const view = query.data;
131
+ const items = activeGroup === 'context' ? (view?.bindings ?? []) : (view?.subscriptions ?? []);
132
+ const runAction = async (item: NcpSessionObservationView, nextAction: NcpSessionObservationAction) => {
133
+ if (nextAction === 'remove') {
134
+ const confirmed = await confirm({
135
+ title: t('chatWorkspaceContinuousAttentionRemoveTitle'),
136
+ description: t('chatWorkspaceContinuousAttentionRemoveDescription'),
137
+ confirmLabel: t('chatWorkspaceContinuousAttentionRemove'),
138
+ variant: 'destructive',
139
+ });
140
+ if (!confirmed) return;
141
+ }
142
+ setActionError(false);
143
+ try {
144
+ await action.mutateAsync({ kind: item.kind, id: item.id, action: nextAction });
145
+ } catch {
146
+ setActionError(true);
147
+ }
148
+ };
149
+
150
+ return (
151
+ <div className="h-full overflow-auto bg-gray-50/45 px-4 py-5 custom-scrollbar">
152
+ <div className="mx-auto max-w-xl">
153
+ <ConfirmDialog />
154
+ <div className="flex items-start gap-3">
155
+ <span className="inline-flex h-9 w-9 shrink-0 items-center justify-center rounded-lg bg-primary/10 text-primary">
156
+ <Eye className="h-4 w-4" />
157
+ </span>
158
+ <div className="min-w-0">
159
+ <h2 className="text-base font-semibold text-gray-900">{t('chatWorkspaceContinuousAttention')}</h2>
160
+ <p className="mt-1 break-all text-xs leading-5 text-gray-500">{t('chatWorkspaceContinuousAttentionDescription')} · {sessionKey}</p>
161
+ </div>
162
+ </div>
163
+
164
+ {view ? (
165
+ <div className="mt-4 grid grid-cols-2 gap-2 sm:grid-cols-4">
166
+ <SummaryCard label={t('chatWorkspaceContinuousAttentionTotal')} value={view.counts.total} />
167
+ <SummaryCard label={t('chatWorkspaceContinuousAttentionState')} value={view.counts.context} />
168
+ <SummaryCard label={t('chatWorkspaceContinuousAttentionEvents')} value={view.counts.events} />
169
+ <SummaryCard label={t('chatWorkspaceContinuousAttentionNeedsAttention')} value={view.counts.needsAttention} />
170
+ </div>
171
+ ) : null}
172
+
173
+ <div className="mt-4 flex rounded-lg border border-gray-200 bg-white p-1" role="tablist">
174
+ {(['context', 'events'] as const).map((group) => {
175
+ const count = group === 'context' ? (view?.counts.context ?? 0) : (view?.counts.events ?? 0);
176
+ return (
177
+ <button
178
+ key={group}
179
+ type="button"
180
+ role="tab"
181
+ aria-selected={activeGroup === group}
182
+ className={cn('flex-1 rounded-md px-3 py-2 text-xs font-medium transition-colors', activeGroup === group ? 'bg-gray-100 text-gray-900' : 'text-gray-500 hover:text-gray-700')}
183
+ onClick={() => setActiveGroup(group)}
184
+ >
185
+ {getGroupLabel(group)} <span className="ml-1 tabular-nums text-gray-400">{count}</span>
186
+ </button>
187
+ );
188
+ })}
189
+ </div>
190
+ {actionError ? <p className="mt-2 text-xs text-rose-600">{t('chatWorkspaceContinuousAttentionActionFailed')}</p> : null}
191
+
192
+ {query.isPending ? (
193
+ <div className="mt-3 space-y-2" aria-label={t('chatWorkspaceContinuousAttentionLoading')}>
194
+ {[0, 1].map((item) => <div key={item} className="h-28 animate-pulse rounded-xl border border-gray-200 bg-white" />)}
195
+ </div>
196
+ ) : query.isError ? (
197
+ <div className="mt-4 rounded-xl border border-rose-200 bg-rose-50 px-4 py-5 text-center">
198
+ <p className="text-sm font-medium text-rose-800">{t('chatWorkspaceContinuousAttentionLoadFailed')}</p>
199
+ <button type="button" className="mt-3 inline-flex items-center gap-1.5 rounded-md border border-rose-200 bg-white px-3 py-1.5 text-xs font-medium text-rose-700" onClick={() => void query.refetch()}>
200
+ <RefreshCw className="h-3.5 w-3.5" />
201
+ {t('chatWorkspaceContinuousAttentionRetry')}
202
+ </button>
203
+ </div>
204
+ ) : items.length === 0 ? (
205
+ <div className="mt-4 rounded-xl border border-dashed border-gray-300 bg-white px-4 py-8 text-center">
206
+ <Clock3 className="mx-auto h-5 w-5 text-gray-300" />
207
+ <p className="mt-2 text-sm font-medium text-gray-700">{t('chatWorkspaceContinuousAttentionEmpty')}</p>
208
+ <p className="mt-1 text-xs leading-5 text-gray-500">{t('chatWorkspaceContinuousAttentionEmptyDescription')}</p>
209
+ </div>
210
+ ) : (
211
+ <div className="mt-3 space-y-2">
212
+ {items.map((item) => (
213
+ <ObservationRow
214
+ key={`${item.kind}:${item.id}`}
215
+ item={item}
216
+ isPending={action.isPending && action.variables?.id === item.id}
217
+ onAction={(nextAction) => void runAction(item, nextAction)}
218
+ />
219
+ ))}
220
+ </div>
221
+ )}
222
+ </div>
223
+ </div>
224
+ );
225
+ }
226
+
227
+ function SummaryCard({ label, value }: { label: string; value: number }) {
228
+ return (
229
+ <div className="rounded-lg border border-gray-200/80 bg-white px-3 py-2">
230
+ <div className="text-[10px] uppercase tracking-wide text-gray-400">{label}</div>
231
+ <div className="mt-1 text-lg font-semibold tabular-nums text-gray-900">{value}</div>
232
+ </div>
233
+ );
234
+ }
@@ -221,6 +221,7 @@ describe("fixed workspace tabs", () => {
221
221
  "overview",
222
222
  "child-sessions",
223
223
  "cron:session",
224
+ "continuous-attention",
224
225
  "project-files",
225
226
  ]);
226
227
  });
@@ -249,10 +250,10 @@ describe("fixed workspace tabs", () => {
249
250
  expect(
250
251
  tabs
251
252
  .filter((tab) =>
252
- ["child-sessions", "cron", "project-files"].includes(tab.kind),
253
+ ["child-sessions", "cron", "continuous-attention", "project-files"].includes(tab.kind),
253
254
  )
254
255
  .map((tab) => tab.title),
255
- ).toEqual(["子会话", "定时任务", "项目文件"]);
256
+ ).toEqual(["子会话", "定时任务", "持续关注", "项目文件"]);
256
257
  });
257
258
  });
258
259
 
@@ -284,7 +285,7 @@ describe("buildWorkspaceTabsViewModel", () => {
284
285
  onSelectCronJobs: vi.fn(),
285
286
  });
286
287
 
287
- expect(tabs[4]).toMatchObject({
288
+ expect(tabs[5]).toMatchObject({
288
289
  key: "child:child-1",
289
290
  kind: "child-session",
290
291
  title: "Child session",
@@ -292,7 +293,7 @@ describe("buildWorkspaceTabsViewModel", () => {
292
293
  showUnreadDot: true,
293
294
  });
294
295
 
295
- tabs[4]?.onSelect();
296
+ tabs[5]?.onSelect();
296
297
  expect(onSelectSession).toHaveBeenCalledWith("child-1");
297
298
  });
298
299
 
@@ -328,11 +329,12 @@ describe("buildWorkspaceTabsViewModel", () => {
328
329
  "overview",
329
330
  "child-sessions",
330
331
  "cron:session",
332
+ "continuous-attention",
331
333
  "project-files",
332
334
  "side-chat-draft:draft-1",
333
335
  "child:child-1",
334
336
  ]);
335
- expect(tabs[4]).toMatchObject({
337
+ expect(tabs[5]).toMatchObject({
336
338
  kind: "side-chat-draft",
337
339
  active: true,
338
340
  });
@@ -378,7 +380,7 @@ describe("buildWorkspaceTabsViewModel", () => {
378
380
  onSelectCronJobs: vi.fn(),
379
381
  });
380
382
 
381
- expect(tabs.slice(4)).toEqual([
383
+ expect(tabs.slice(5)).toEqual([
382
384
  expect.objectContaining({
383
385
  key: "file:parent::preview::demo.html",
384
386
  active: true,
@@ -401,7 +403,7 @@ describe("buildWorkspaceTabsViewModel", () => {
401
403
  }),
402
404
  }),
403
405
  ]);
404
- tabs[4]?.alternateViewerAction?.onSelect();
406
+ tabs[5]?.alternateViewerAction?.onSelect();
405
407
  expect(onOpenFileViewer).toHaveBeenCalledWith(
406
408
  "parent::preview::demo.html",
407
409
  "rendered",
@@ -21,7 +21,8 @@ export function areWorkspaceNavigationEntriesEqual(
21
21
  current.kind === 'overview' ||
22
22
  current.kind === 'child-sessions' ||
23
23
  current.kind === 'project-files' ||
24
- current.kind === 'cron'
24
+ current.kind === 'cron' ||
25
+ current.kind === 'continuous-attention'
25
26
  ) {
26
27
  return true;
27
28
  }
@@ -30,6 +31,7 @@ export function areWorkspaceNavigationEntriesEqual(
30
31
  next.kind !== 'child-sessions' &&
31
32
  next.kind !== 'project-files' &&
32
33
  next.kind !== 'cron' &&
34
+ next.kind !== 'continuous-attention' &&
33
35
  current.key === next.key
34
36
  );
35
37
  }
@@ -50,7 +52,8 @@ export function createWorkspaceNavigationEntryFromSnapshot(
50
52
  activeWorkspacePanelKind === 'overview' ||
51
53
  activeWorkspacePanelKind === 'child-sessions' ||
52
54
  activeWorkspacePanelKind === 'project-files' ||
53
- activeWorkspacePanelKind === 'cron'
55
+ activeWorkspacePanelKind === 'cron' ||
56
+ activeWorkspacePanelKind === 'continuous-attention'
54
57
  ) {
55
58
  return { kind: activeWorkspacePanelKind };
56
59
  }
@@ -72,7 +75,8 @@ export function createWorkspaceSelectionPatch(
72
75
  if (
73
76
  entry.kind === 'overview' ||
74
77
  entry.kind === 'child-sessions' ||
75
- entry.kind === 'project-files'
78
+ entry.kind === 'project-files' ||
79
+ entry.kind === 'continuous-attention'
76
80
  ) {
77
81
  return {
78
82
  activeWorkspacePanelKind: entry.kind,
@@ -128,7 +132,8 @@ export function closeWorkspaceTabSnapshot(
128
132
  entry.kind === 'overview' ||
129
133
  entry.kind === 'child-sessions' ||
130
134
  entry.kind === 'cron' ||
131
- entry.kind === 'project-files'
135
+ entry.kind === 'project-files' ||
136
+ entry.kind === 'continuous-attention'
132
137
  ) {
133
138
  return null;
134
139
  }
@@ -1,4 +1,5 @@
1
1
  import type { ResolvedChildSessionTab } from "@/features/chat/features/ncp/hooks/use-ncp-child-session-tabs-view";
2
+ import type { ContextMenuGroup } from "@/shared/components/ui/context-menu/context-menu";
2
3
  import { shouldShowUnreadSessionIndicator } from "@/features/chat/stores/chat-session-list.store";
3
4
  import type {
4
5
  ChatWorkspaceNavigationEntry,
@@ -39,6 +40,9 @@ export type WorkspaceSelection =
39
40
  }
40
41
  | {
41
42
  kind: "cron";
43
+ }
44
+ | {
45
+ kind: "continuous-attention";
42
46
  };
43
47
 
44
48
  export type WorkspaceTabViewModel = {
@@ -48,6 +52,7 @@ export type WorkspaceTabViewModel = {
48
52
  tooltip: string;
49
53
  active: boolean;
50
54
  agentId?: string | null;
55
+ sessionKey?: string | null;
51
56
  fileName?: string | null;
52
57
  showUnreadDot?: boolean;
53
58
  viewMode?: "preview" | "diff";
@@ -60,6 +65,8 @@ export type WorkspaceTabViewModel = {
60
65
  onAddToChat?: () => void;
61
66
  onSelect: () => void;
62
67
  onClose?: () => void;
68
+ menuLabel?: string;
69
+ menuGroups?: readonly ContextMenuGroup[];
63
70
  };
64
71
 
65
72
  export function readWorkspaceFileTitle(file: ChatWorkspaceFileTab): string {
@@ -100,6 +107,10 @@ export function resolveWorkspaceSelection(params: {
100
107
  return { kind: "cron" };
101
108
  }
102
109
 
110
+ if (activePanelKind === "continuous-attention") {
111
+ return { kind: "continuous-attention" };
112
+ }
113
+
103
114
  if (activePanelKind === "side-chat-draft" && activeSideChatDraft) {
104
115
  return {
105
116
  kind: "side-chat-draft",
@@ -173,6 +184,7 @@ type WorkspaceTabsViewModelParams = {
173
184
  onSelectChildSessions: () => void;
174
185
  onSelectProjectFiles: () => void;
175
186
  onSelectCronJobs: () => void;
187
+ onSelectContinuousAttention?: () => void;
176
188
  };
177
189
 
178
190
  function buildWorkspacePageTabs({
@@ -182,6 +194,7 @@ function buildWorkspacePageTabs({
182
194
  onSelectCronJobs,
183
195
  onSelectOverview,
184
196
  onSelectProjectFiles,
197
+ onSelectContinuousAttention,
185
198
  }: Pick<
186
199
  WorkspaceTabsViewModelParams,
187
200
  | "activeSelection"
@@ -190,6 +203,7 @@ function buildWorkspacePageTabs({
190
203
  | "onSelectCronJobs"
191
204
  | "onSelectOverview"
192
205
  | "onSelectProjectFiles"
206
+ | "onSelectContinuousAttention"
193
207
  >): WorkspaceTabViewModel[] {
194
208
  const pageTabs: WorkspaceTabViewModel[] = [
195
209
  {
@@ -216,6 +230,14 @@ function buildWorkspacePageTabs({
216
230
  active: activeSelection?.kind === "cron",
217
231
  onSelect: onSelectCronJobs,
218
232
  },
233
+ {
234
+ key: "continuous-attention",
235
+ kind: "continuous-attention",
236
+ title: t("chatWorkspaceContinuousAttention"),
237
+ tooltip: t("chatWorkspaceContinuousAttention"),
238
+ active: activeSelection?.kind === "continuous-attention",
239
+ onSelect: onSelectContinuousAttention ?? (() => undefined),
240
+ },
219
241
  {
220
242
  key: "project-files",
221
243
  kind: "project-files",
@@ -251,6 +273,7 @@ export function buildWorkspaceTabsViewModel(
251
273
  onSelectChildSessions,
252
274
  onSelectProjectFiles,
253
275
  onSelectCronJobs,
276
+ onSelectContinuousAttention = () => undefined,
254
277
  } = params;
255
278
 
256
279
  const workspacePages = buildWorkspacePageTabs({
@@ -260,6 +283,7 @@ export function buildWorkspaceTabsViewModel(
260
283
  onSelectCronJobs,
261
284
  onSelectOverview,
262
285
  onSelectProjectFiles,
286
+ onSelectContinuousAttention,
263
287
  });
264
288
 
265
289
  const sideChatDraftTabs = activeSideChatDraft
@@ -304,6 +328,7 @@ export function buildWorkspaceTabsViewModel(
304
328
  title: tab.title,
305
329
  tooltip: tab.title,
306
330
  agentId: tab.agentId,
331
+ sessionKey: tab.sessionKey,
307
332
  active:
308
333
  activeSelection?.kind === "child-session" &&
309
334
  activeSelection.tab.sessionKey === tab.sessionKey,
@@ -174,7 +174,7 @@ export class ChatThreadManager {
174
174
 
175
175
  private openWorkspacePage = (
176
176
  rawParentSessionKey: string | null,
177
- kind: 'overview' | 'child-sessions' | 'project-files' | 'cron',
177
+ kind: 'overview' | 'child-sessions' | 'project-files' | 'cron' | 'continuous-attention',
178
178
  ) => {
179
179
  const parentSessionKey = rawParentSessionKey?.trim() || null;
180
180
  if (!parentSessionKey && kind !== 'project-files') {
@@ -234,13 +234,11 @@ export class ChatThreadManager {
234
234
  if (snapshot) useChatThreadStore.getState().setSnapshot(snapshot);
235
235
  };
236
236
 
237
- openChildSessions = (sessionKey: string) => {
238
- this.openWorkspacePage(sessionKey, 'child-sessions');
239
- };
237
+ openChildSessions = (sessionKey: string) => this.openWorkspacePage(sessionKey, 'child-sessions');
240
238
 
241
- openProjectFiles = (sessionKey: string | null) => {
242
- this.openWorkspacePage(sessionKey, 'project-files');
243
- };
239
+ openContinuousAttention = (sessionKey: string) => this.openWorkspacePage(sessionKey, 'continuous-attention');
240
+
241
+ openProjectFiles = (sessionKey: string | null) => this.openWorkspacePage(sessionKey, 'project-files');
244
242
 
245
243
  materializeRootDraftSession = (rawSessionKey: string) => {
246
244
  const sessionKey = rawSessionKey.trim();
@@ -57,7 +57,8 @@ export type ChatWorkspacePanelKind =
57
57
  | 'side-chat-draft'
58
58
  | 'project-files'
59
59
  | 'file'
60
- | 'cron';
60
+ | 'cron'
61
+ | 'continuous-attention';
61
62
 
62
63
  export type ChatWorkspaceNavigationEntry =
63
64
  | { kind: 'overview' }
@@ -66,7 +67,8 @@ export type ChatWorkspaceNavigationEntry =
66
67
  | { kind: 'side-chat-draft'; key: string }
67
68
  | { kind: 'project-files' }
68
69
  | { kind: 'file'; key: string }
69
- | { kind: 'cron' };
70
+ | { kind: 'cron' }
71
+ | { kind: 'continuous-attention' };
70
72
 
71
73
  export type ChatThreadSnapshot = {
72
74
  sessionTypeLabel?: string | null;
@@ -102,7 +104,7 @@ export type ChatThreadSnapshot = {
102
104
  };
103
105
 
104
106
  const CHAT_THREAD_WORKSPACE_STORAGE_KEY = 'nextclaw.chat.workspace-panel.state';
105
- const CHAT_THREAD_WORKSPACE_STORAGE_VERSION = 2;
107
+ const CHAT_THREAD_WORKSPACE_STORAGE_VERSION = 3;
106
108
  const CHAT_THREAD_MAX_PERSISTED_WORKSPACE_FILE_TABS = 8;
107
109
 
108
110
  type ChatThreadStore = {
@@ -207,7 +209,8 @@ function normalizePersistedWorkspaceNavigationEntry(value: unknown): ChatWorkspa
207
209
  value.kind === 'overview' ||
208
210
  value.kind === 'child-sessions' ||
209
211
  value.kind === 'project-files' ||
210
- value.kind === 'cron'
212
+ value.kind === 'cron' ||
213
+ value.kind === 'continuous-attention'
211
214
  ) {
212
215
  return { kind: value.kind };
213
216
  }
@@ -0,0 +1,11 @@
1
+ import { useQuery } from '@tanstack/react-query';
2
+ import { fetchExtensionCatalog } from '@/shared/lib/api';
3
+
4
+ export function useExtensions() {
5
+ return useQuery({
6
+ queryKey: ['extension-catalog'],
7
+ queryFn: fetchExtensionCatalog,
8
+ staleTime: 5_000,
9
+ refetchOnWindowFocus: true,
10
+ });
11
+ }
@@ -0,0 +1 @@
1
+ export { ExtensionsConfigPage } from "./pages/extensions-config-page";