@nextclaw/ui 0.15.10 → 0.15.12

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 (360) hide show
  1. package/CHANGELOG.md +53 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-BYpK0F5F.js → abnfDiagram-VRR7QNED-M4g_AyR8.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-D87ywFdC.js → aiden0z-pptx-renderer.es-Bd99vut2.js} +1 -1
  4. package/dist/assets/api-Bw1oLhwf.js +13 -0
  5. package/dist/assets/app-presenter-provider-DSN90VJw.js +106 -0
  6. package/dist/assets/appearance-settings-page-BDpels6a.js +1 -0
  7. package/dist/assets/{arc-DFELHWM2.js → arc-IO7WNWOB.js} +1 -1
  8. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +1 -0
  9. package/dist/assets/{architectureDiagram-ZJ3FMSHR-CvRhdovL.js → architectureDiagram-ZJ3FMSHR-Dc-lvdUh.js} +1 -1
  10. package/dist/assets/{blockDiagram-677ZJIJ3-DVYNkbR_.js → blockDiagram-677ZJIJ3-CcsoWpIY.js} +1 -1
  11. package/dist/assets/{book-open-w3160PZf.js → book-open-D-oLp0QJ.js} +1 -1
  12. package/dist/assets/{c4Diagram-LMCZKHZV-B1w-iwRe.js → c4Diagram-LMCZKHZV-Bsrtg6b2.js} +1 -1
  13. package/dist/assets/card-CA3ozZwW.js +1 -0
  14. package/dist/assets/channel-BSKkTPe8.js +1 -0
  15. package/dist/assets/channels-list-page-B44EWctT.js +8 -0
  16. package/dist/assets/chat-page-CH8ypOKO.js +1 -0
  17. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +1 -0
  18. package/dist/assets/{chunk-32BRIVSS-kG3lg0bC.js → chunk-32BRIVSS-Bt08t8oG.js} +1 -1
  19. package/dist/assets/{chunk-52WLFC77-BLeB7iGk.js → chunk-52WLFC77-Ddx68Xzx.js} +1 -1
  20. package/dist/assets/{chunk-5FCAYU7R-BQga1Rr7.js → chunk-5FCAYU7R-B7p9_q55.js} +1 -1
  21. package/dist/assets/{chunk-5HE753X5-DU81hkcF.js → chunk-5HE753X5-D52KvdTm.js} +1 -1
  22. package/dist/assets/{chunk-5JV3BV7I-C2bAx0m7.js → chunk-5JV3BV7I-C329h8nI.js} +1 -1
  23. package/dist/assets/{chunk-5TONJI2A-BExIIgWY.js → chunk-5TONJI2A-BZyUBxWt.js} +1 -1
  24. package/dist/assets/{chunk-5VM5RSS4-C3XB05yh.js → chunk-5VM5RSS4-BUiYXZmu.js} +1 -1
  25. package/dist/assets/{chunk-7BUUIJ7U-JiyRtrGu.js → chunk-7BUUIJ7U-B7o-mn4r.js} +1 -1
  26. package/dist/assets/{chunk-BIQX33UG-BZRV20pI.js → chunk-BIQX33UG-Bey9GoRP.js} +1 -1
  27. package/dist/assets/{chunk-C7G6YPKG-AsyDSQ9j.js → chunk-C7G6YPKG-C6tosszM.js} +1 -1
  28. package/dist/assets/{chunk-CQNSW5MT-iN-fiXVA.js → chunk-CQNSW5MT-BNrdkgGQ.js} +1 -1
  29. package/dist/assets/{chunk-CYSBUYHQ-DhC_TRzI.js → chunk-CYSBUYHQ-DHODFeyB.js} +1 -1
  30. package/dist/assets/{chunk-EMLP6XTP-QFLK8R6d.js → chunk-EMLP6XTP-BcmLjrcq.js} +1 -1
  31. package/dist/assets/{chunk-EX3LRPZG-DzUwhkCz.js → chunk-EX3LRPZG-r-_eEtJI.js} +1 -1
  32. package/dist/assets/{chunk-FWX5IMBZ-DFelpZBy.js → chunk-FWX5IMBZ-DOObDWbf.js} +2 -2
  33. package/dist/assets/{chunk-HOUHSVGY-BMPwFEwN.js → chunk-HOUHSVGY-Bd0FnbWZ.js} +1 -1
  34. package/dist/assets/{chunk-ICXQ74PX-DH8NJMok.js → chunk-ICXQ74PX-ntNwAm4_.js} +1 -1
  35. package/dist/assets/{chunk-JG7HCLWE-D-95AsO4.js → chunk-JG7HCLWE-YtRpblZN.js} +1 -1
  36. package/dist/assets/{chunk-JWPE2WC7-DIaTv8kx.js → chunk-JWPE2WC7-De5-D9ta.js} +1 -1
  37. package/dist/assets/{chunk-MOJQB5TN-BPFLzWgj.js → chunk-MOJQB5TN-CdvKhyp2.js} +1 -1
  38. package/dist/assets/{chunk-MOZMSUNE-DR_fzxG0.js → chunk-MOZMSUNE-DJMMN_lu.js} +1 -1
  39. package/dist/assets/{chunk-OGEWGWER-TIcEoPPH.js → chunk-OGEWGWER-DOFWVw3O.js} +1 -1
  40. package/dist/assets/{chunk-OSBZ3O6U-BALNvSfG.js → chunk-OSBZ3O6U-DZkrIN9r.js} +1 -1
  41. package/dist/assets/{chunk-PUDLZKDR-I-EXhf_k.js → chunk-PUDLZKDR-D1wma2Yf.js} +1 -1
  42. package/dist/assets/{chunk-Q4XR5HBZ-uvIGstBS.js → chunk-Q4XR5HBZ-CjPy94Lz.js} +1 -1
  43. package/dist/assets/{chunk-QBLGF6JB-r-TjLdxa.js → chunk-QBLGF6JB-Hy1bEgS-.js} +1 -1
  44. package/dist/assets/{chunk-R7FJI6CG-N-1LDgvy.js → chunk-R7FJI6CG-CUPIHyh-.js} +1 -1
  45. package/dist/assets/{chunk-RYQCIY6F-jxNASx65.js → chunk-RYQCIY6F-D5Ua4pcv.js} +1 -1
  46. package/dist/assets/{chunk-U6XO7XAA-Cphbuc7q.js → chunk-U6XO7XAA-DaUQcqb7.js} +1 -1
  47. package/dist/assets/{chunk-V7JOEXUC-BLEuBf1s.js → chunk-V7JOEXUC-DX2zLfQe.js} +1 -1
  48. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +1 -0
  49. package/dist/assets/{chunk-VR4S4FIN-BAUbR0ke.js → chunk-VR4S4FIN-CFA-WQik.js} +1 -1
  50. package/dist/assets/{chunk-WYO6CB5R-Do6SJrnY.js → chunk-WYO6CB5R-Bk2HeDvJ.js} +2 -2
  51. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +1 -0
  52. package/dist/assets/{chunk-YOTPTUD7-CQqvMTCr.js → chunk-YOTPTUD7-DUG4FpBN.js} +1 -1
  53. package/dist/assets/{chunk-ZGVPDNZ5-B7kWWaXv.js → chunk-ZGVPDNZ5-70JdIGo0.js} +1 -1
  54. package/dist/assets/{chunk-ZIRB5QZD-DTU0cDNh.js → chunk-ZIRB5QZD-XZdgBt5L.js} +1 -1
  55. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +1 -0
  56. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +1 -0
  57. package/dist/assets/config-split-page-DskB5yAV.js +1 -0
  58. package/dist/assets/confirm-dialog-BRQpFnvD.js +5 -0
  59. package/dist/assets/{cose-bilkent-JH36ORCC-B9LrtV-q.js → cose-bilkent-JH36ORCC-B-PQoGX-.js} +1 -1
  60. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +1 -0
  61. package/dist/assets/{cynefinDiagram-TSTJHNR4-BfjvW4su.js → cynefinDiagram-TSTJHNR4-BoLCPEM0.js} +1 -1
  62. package/dist/assets/{dagre-DkiXZKkL.js → dagre-D7Ie4eXr.js} +1 -1
  63. package/dist/assets/{dagre-VKFMJZFB-B1pQKQSY.js → dagre-VKFMJZFB-CKRrwZA-.js} +1 -1
  64. package/dist/assets/desktop-update-config-se0OEu06.js +1 -0
  65. package/dist/assets/{diagram-FQU43EPY-cB4EZdaP.js → diagram-FQU43EPY-CASL1J3_.js} +1 -1
  66. package/dist/assets/{diagram-G47NLZAW-CtW8im1B.js → diagram-G47NLZAW-CvLPBIzf.js} +1 -1
  67. package/dist/assets/{diagram-NH7WQ7WH-BDCtrCCm.js → diagram-NH7WQ7WH-Ba6fGdYP.js} +1 -1
  68. package/dist/assets/{diagram-OA4YK3LP-DEMJ42Gt.js → diagram-OA4YK3LP-DkpfgEjM.js} +1 -1
  69. package/dist/assets/{diagram-WEI45ONY-CdCcxSYU.js → diagram-WEI45ONY-BP5UMVsN.js} +1 -1
  70. package/dist/assets/{dist-DEJlYi0E.js → dist-f6aZEsu8.js} +1 -1
  71. package/dist/assets/doc-browser-BRiAfy_N.js +1 -0
  72. package/dist/assets/doc-browser-DW7afLXs.js +1 -0
  73. package/dist/assets/{doc-browser-context-CpvHiuQG.js → doc-browser-context-DhTa-zP_.js} +1 -1
  74. package/dist/assets/{docx-preview-zis3U0Fa.js → docx-preview-D0tp3a9T.js} +1 -1
  75. package/dist/assets/{ebnfDiagram-CCIWWBDH-DBZETBwE.js → ebnfDiagram-CCIWWBDH-TJKbUcmc.js} +1 -1
  76. package/dist/assets/{erDiagram-Q63AITRT-y2zDXprP.js → erDiagram-Q63AITRT-DzGPKkKH.js} +1 -1
  77. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +1 -0
  78. package/dist/assets/eye-DMGpISEo.js +1 -0
  79. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +1 -0
  80. package/dist/assets/{form-actions-5OsDRt0g.js → form-actions-SgP78YnZ.js} +1 -1
  81. package/dist/assets/{ganttDiagram-NO4QXBWP-D2DP344e.js → ganttDiagram-NO4QXBWP-JhJzsGp2.js} +1 -1
  82. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +1 -0
  83. package/dist/assets/{gitGraphDiagram-IHSO6WYX-D37uiwA2.js → gitGraphDiagram-IHSO6WYX-DZ1CKMj8.js} +1 -1
  84. package/dist/assets/index-B2YF-7Yw.css +1 -0
  85. package/dist/assets/index-CBD4zQ_A.js +2 -0
  86. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +1 -0
  87. package/dist/assets/{infoDiagram-FWYZ7A6U-CG-WAIRi.js → infoDiagram-FWYZ7A6U-BNXNKfRQ.js} +1 -1
  88. package/dist/assets/{ishikawaDiagram-FXEZZL3T-WeCTnW0f.js → ishikawaDiagram-FXEZZL3T-DoiEfjNw.js} +1 -1
  89. package/dist/assets/{journeyDiagram-5HDEW3XC-DUFBW9Td.js → journeyDiagram-5HDEW3XC-D6oK2HSy.js} +1 -1
  90. package/dist/assets/{kanban-definition-HUTT4EX6-B0lxgMpg.js → kanban-definition-HUTT4EX6-B2UnLOH-.js} +1 -1
  91. package/dist/assets/{line-B78frP_c.js → line-BM7PoxY5.js} +1 -1
  92. package/dist/assets/{linear-C6lVEcb2.js → linear-B3DiqMjk.js} +1 -1
  93. package/dist/assets/loader-circle-DEQ2CflP.js +1 -0
  94. package/dist/assets/{logo-badge-TdPa95AC.js → logo-badge-BZnxuJYW.js} +1 -1
  95. package/dist/assets/{map-Bdri5UL5.js → map-BAAdHTqR.js} +1 -1
  96. package/dist/assets/mcp-marketplace-page-B8eWiT9H.js +1 -0
  97. package/dist/assets/{mcp-marketplace-page-cm3ifaKu.js → mcp-marketplace-page-Dm08nf11.js} +2 -2
  98. package/dist/assets/{mermaid-parser.core-Cue18vLo.js → mermaid-parser.core-BeMviefY.js} +2 -2
  99. package/dist/assets/{mermaid.core-CXwWRh5i.js → mermaid.core-D0Q77r9I.js} +3 -3
  100. package/dist/assets/{mindmap-definition-LN4V7U3C-CFxz0P0-.js → mindmap-definition-LN4V7U3C-BQ7r7bR8.js} +1 -1
  101. package/dist/assets/model-config-page-k_tCjGlK.js +1 -0
  102. package/dist/assets/navigation-link-CJ3fpUBN.js +1 -0
  103. package/dist/assets/{ordinal-Cbd8t4LB.js → ordinal-Bt2N4IkA.js} +1 -1
  104. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +1 -0
  105. package/dist/assets/{pegDiagram-2B236MQR-B_Qt9zyT.js → pegDiagram-2B236MQR-B-C9-LGz.js} +1 -1
  106. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +1 -0
  107. package/dist/assets/{pieDiagram-ENE6RG2P-CRQdSuwS.js → pieDiagram-ENE6RG2P-CtirZLtn.js} +1 -1
  108. package/dist/assets/plus-9_9Q74qc.js +1 -0
  109. package/dist/assets/{popover-EulxhA0q.js → popover-Cq_oB7HI.js} +1 -1
  110. package/dist/assets/provider-scoped-model-input-Cwv5zw0Z.js +1 -0
  111. package/dist/assets/providers-config-page-D82jX13I.js +1 -0
  112. package/dist/assets/{quadrantDiagram-ABIIQ3AL-D5kPpttq.js → quadrantDiagram-ABIIQ3AL-DiBWAzDU.js} +1 -1
  113. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +1 -0
  114. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +1 -0
  115. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +1 -0
  116. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +1 -0
  117. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +1 -0
  118. package/dist/assets/{railroadDiagram-RFXS5EU6-DGh_TGlQ.js → railroadDiagram-RFXS5EU6-CP6b3PBI.js} +1 -1
  119. package/dist/assets/{react-Bb0EYsLS.js → react-BjpVlGAu.js} +1 -1
  120. package/dist/assets/{refresh-cw-CexL11m1.js → refresh-cw-CYyWovQ1.js} +1 -1
  121. package/dist/assets/remote-C2MGMI2j.js +1 -0
  122. package/dist/assets/{requirementDiagram-TGXJPOKE-EmFph0H_.js → requirementDiagram-TGXJPOKE-D8YV0-Jz.js} +1 -1
  123. package/dist/assets/rotate-cw-BkGMTudQ.js +1 -0
  124. package/dist/assets/runtime-config-page-OS6ZT2b0.js +1 -0
  125. package/dist/assets/{sankeyDiagram-HTMAVEWB-BXWB5Ugi.js → sankeyDiagram-HTMAVEWB-C2vA4ojE.js} +1 -1
  126. package/dist/assets/{save-DtccrZld.js → save-B0V_M-kz.js} +1 -1
  127. package/dist/assets/search-config-page-IzK1hMQX.js +1 -0
  128. package/dist/assets/{search-BArc_WOX.js → search-vFYD1SOf.js} +1 -1
  129. package/dist/assets/secrets-config-page-Cm3IXYpK.js +3 -0
  130. package/dist/assets/security-config-DfzQ8txL.js +1 -0
  131. package/dist/assets/{select-C9FB0jlt.js → select-BAYdPRVG.js} +1 -1
  132. package/dist/assets/{sequenceDiagram-DBY2YBRQ-ClKnO7v0.js → sequenceDiagram-DBY2YBRQ-B9LBmpUo.js} +1 -1
  133. package/dist/assets/settings-2-QX-FHs_i.js +1 -0
  134. package/dist/assets/{sizeCapture-X5ZJPWSS-DSo-t5ou.js → sizeCapture-X5ZJPWSS-9UIfya9-.js} +1 -1
  135. package/dist/assets/skeleton-2eczb2co.js +1 -0
  136. package/dist/assets/{src-Dad2ZbCt.js → src-DKf77Yzo.js} +1 -1
  137. package/dist/assets/{stateDiagram-2N3HPSRC-BvAalBfz.js → stateDiagram-2N3HPSRC-oTW44vds.js} +1 -1
  138. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +1 -0
  139. package/dist/assets/{swimlanes-5IMT3BWC-DdVqVa1a.js → swimlanes-5IMT3BWC-Cf7lcsay.js} +2 -2
  140. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +8 -0
  141. package/dist/assets/{tabs-D_mjyOOf.js → tabs-Ddprun22.js} +1 -1
  142. package/dist/assets/{tag-chip-C011rd9q.js → tag-chip-C9SLhljq.js} +1 -1
  143. package/dist/assets/{timeline-definition-FHXFAJF6-D4ar8IQY.js → timeline-definition-FHXFAJF6-DoCuWKDJ.js} +1 -1
  144. package/dist/assets/{trash-2-bDhfsO1O.js → trash-2-CODUwc31.js} +1 -1
  145. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +1 -0
  146. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +1 -0
  147. package/dist/assets/{use-config-BNrlfEck.js → use-config-C5OKT3lk.js} +1 -1
  148. package/dist/assets/use-confirm-dialog-B_ZW-WgC.js +1 -0
  149. package/dist/assets/{use-viewport-layout-P0q6Nt_N.js → use-viewport-layout-OtzTlg5U.js} +1 -1
  150. package/dist/assets/{vennDiagram-L72KCM5P-CDXJ_i3p.js → vennDiagram-L72KCM5P-CpU0pzGe.js} +1 -1
  151. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +1 -0
  152. package/dist/assets/{wardleyDiagram-EHGQE667-D-dSuT_7.js → wardleyDiagram-EHGQE667-CkN6nn6-.js} +1 -1
  153. package/dist/assets/x-Cbfpb-Zm.js +1 -0
  154. package/dist/assets/{xychartDiagram-FW5EYKEG-C9JCzsZF.js → xychartDiagram-FW5EYKEG-DATpapm2.js} +1 -1
  155. package/dist/index.html +31 -32
  156. package/dist/runtime-icons/opencode.svg +16 -0
  157. package/package.json +10 -9
  158. package/public/runtime-icons/opencode.svg +16 -0
  159. package/src/app/components/layout/__tests__/settings-entry-page.test.tsx +6 -4
  160. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +24 -12
  161. package/src/app/components/layout/sidebar-items.tsx +11 -2
  162. package/src/app/components/layout/sidebar.tsx +33 -234
  163. package/src/app/configs/app-navigation.config.ts +10 -24
  164. package/src/app/index.tsx +0 -5
  165. package/src/features/channels/components/config/channel-form.tsx +26 -26
  166. package/src/features/channels/components/config/weixin-channel-auth-section.tsx +6 -7
  167. package/src/features/channels/pages/__tests__/channels-list-page.test.tsx +2 -2
  168. package/src/features/channels/pages/channels-list-page.tsx +33 -32
  169. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +68 -7
  170. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +27 -0
  171. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +0 -8
  172. package/src/features/chat/components/conversation/chat-conversation-content.tsx +65 -21
  173. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +36 -12
  174. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +0 -7
  175. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +0 -1
  176. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +17 -8
  177. package/src/features/chat/components/layout/chat-page-shell.tsx +2 -2
  178. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +2 -23
  179. package/src/features/chat/components/layout/chat-sidebar.tsx +3 -12
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +155 -93
  181. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +126 -93
  182. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +8 -0
  183. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +27 -13
  184. package/src/features/chat/features/message/components/__tests__/chat-inline-file-preview.test.tsx +15 -9
  185. package/src/features/chat/features/message/components/__tests__/chat-inline-panel-app-card.test.tsx +127 -49
  186. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +186 -20
  187. package/src/features/chat/features/message/components/chat-inline-content-surface.tsx +55 -0
  188. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +49 -71
  189. package/src/features/chat/features/message/components/chat-inline-panel-app-card.tsx +65 -55
  190. package/src/features/chat/features/message/components/chat-message-list.container.tsx +275 -267
  191. package/src/features/chat/features/message/hooks/__tests__/use-chat-message-virtualizer.test.tsx +163 -0
  192. package/src/features/chat/features/message/hooks/use-chat-message-virtualizer.ts +68 -0
  193. package/src/features/chat/features/message/utils/__tests__/chat-message-execution-summary.utils.test.ts +200 -0
  194. package/src/features/chat/features/message/utils/__tests__/chat-message-texts.utils.test.ts +14 -1
  195. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +33 -0
  196. package/src/features/chat/features/message/utils/chat-inline-panel-app-card.utils.ts +24 -9
  197. package/src/features/chat/features/message/utils/chat-message-execution-summary.utils.ts +173 -0
  198. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +43 -0
  199. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +194 -0
  200. package/src/features/chat/features/message/utils/chat-message.utils.ts +5 -0
  201. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +151 -13
  202. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +44 -70
  203. package/src/features/chat/features/ncp/hooks/use-ncp-session-message-history.ts +197 -0
  204. package/src/features/chat/features/session/components/__tests__/chat-sidebar-session-item.test.tsx +21 -3
  205. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +7 -7
  206. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +2 -2
  207. package/src/features/chat/features/session/components/session-context-icon.tsx +1 -1
  208. package/src/features/chat/features/session-type/components/__tests__/chat-session-type-option-item.test.tsx +5 -1
  209. package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +1 -1
  210. package/src/features/chat/features/workspace/components/__tests__/workspace-file-content-preview.test.tsx +32 -10
  211. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +5 -15
  212. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +62 -9
  213. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +6 -1
  214. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +1 -25
  215. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +190 -109
  216. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +9 -10
  217. package/src/features/chat/managers/chat-session-list.manager.ts +68 -27
  218. package/src/features/cron/components/__tests__/cron-config.test.tsx +260 -0
  219. package/src/features/cron/components/cron-config.tsx +392 -388
  220. package/src/features/cron/components/cron-job-detail-dialog.tsx +210 -0
  221. package/src/features/cron/components/cron-job-row.tsx +271 -0
  222. package/src/features/cron/components/cron-task-discovery.tsx +152 -0
  223. package/src/features/cron/hooks/use-cron-jobs.ts +3 -1
  224. package/src/features/marketplace/components/__tests__/marketplace-page.test.tsx +3 -2
  225. package/src/features/marketplace/components/marketplace-external-skill-source-action.tsx +8 -6
  226. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +26 -40
  227. package/src/features/pwa/components/pwa-install-entry.tsx +45 -58
  228. package/src/features/remote/components/remote-access-page.tsx +128 -208
  229. package/src/features/settings/components/config/provider-auth-section.tsx +32 -17
  230. package/src/features/settings/components/config/provider-form.tsx +6 -6
  231. package/src/features/settings/components/config/provider-models-section.tsx +52 -46
  232. package/src/features/settings/components/config/secrets-config-form.tsx +78 -84
  233. package/src/features/settings/index.ts +0 -1
  234. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +35 -1
  235. package/src/features/settings/pages/__tests__/model-config-page.test.tsx +18 -1
  236. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +8 -0
  237. package/src/features/settings/pages/__tests__/secrets-config-page.test.tsx +18 -17
  238. package/src/features/settings/pages/appearance-settings-page.tsx +87 -64
  239. package/src/features/settings/pages/model-config-page.tsx +75 -70
  240. package/src/features/settings/pages/providers-config-page.tsx +107 -73
  241. package/src/features/settings/pages/search-config-page.tsx +159 -152
  242. package/src/features/settings/pages/secrets-config-page.tsx +6 -1
  243. package/src/features/system-status/components/config/runtime-agent-list-card.tsx +23 -22
  244. package/src/features/system-status/components/config/runtime-binding-list-card.tsx +23 -22
  245. package/src/features/system-status/components/config/runtime-config-editor.tsx +17 -7
  246. package/src/features/system-status/components/config/runtime-config-overview.tsx +0 -3
  247. package/src/features/system-status/components/config/runtime-entry-list-card.tsx +24 -23
  248. package/src/features/system-status/components/config/runtime-settings-card.tsx +59 -52
  249. package/src/features/system-status/components/desktop-update-config.tsx +117 -112
  250. package/src/features/system-status/components/runtime-control-card.tsx +62 -59
  251. package/src/features/system-status/components/runtime-presence-card.tsx +79 -98
  252. package/src/features/system-status/components/runtime-security-card.tsx +74 -62
  253. package/src/features/system-status/components/security-config.tsx +3 -4
  254. package/src/features/system-status/pages/runtime-config-page.tsx +6 -1
  255. package/src/platforms/desktop/components/desktop-app-shell.tsx +1 -1
  256. package/src/platforms/mobile/components/mobile-settings-shell.tsx +2 -2
  257. package/src/shared/components/__tests__/config-split-page.test.tsx +15 -3
  258. package/src/shared/components/__tests__/settings-page.test.tsx +31 -0
  259. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +47 -0
  260. package/src/shared/components/actions/navigation-link.tsx +116 -0
  261. package/src/shared/components/common/__tests__/brand-header.test.tsx +5 -5
  262. package/src/shared/components/common/__tests__/searchable-model-input.test.tsx +24 -0
  263. package/src/shared/components/common/brand-header.tsx +12 -10
  264. package/src/shared/components/common/searchable-model-input.tsx +9 -10
  265. package/src/shared/components/config-split-page.tsx +70 -65
  266. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +6 -7
  267. package/src/shared/components/settings/setting-row.tsx +61 -30
  268. package/src/shared/components/settings/settings-page.tsx +34 -0
  269. package/src/shared/components/ui/actions/__tests__/icon-action-button.test.tsx +23 -2
  270. package/src/shared/components/ui/actions/icon-action-button.tsx +9 -2
  271. package/src/shared/components/ui/overlays/sheet.tsx +142 -0
  272. package/src/shared/lib/api/ncp-session.types.ts +8 -2
  273. package/src/shared/lib/api/types.ts +22 -1
  274. package/src/shared/lib/api/utils/config.utils.ts +2 -1
  275. package/src/shared/lib/api/utils/ncp-session.utils.ts +25 -11
  276. package/src/shared/lib/i18n/locales/en-US/chat.json +31 -1
  277. package/src/shared/lib/i18n/locales/en-US/core.json +3 -3
  278. package/src/shared/lib/i18n/locales/en-US/cron.json +87 -17
  279. package/src/shared/lib/i18n/locales/zh-CN/chat.json +33 -3
  280. package/src/shared/lib/i18n/locales/zh-CN/core.json +6 -6
  281. package/src/shared/lib/i18n/locales/zh-CN/cron.json +80 -10
  282. package/src/shared/lib/transport/__tests__/remote.transport.test.ts +16 -4
  283. package/dist/assets/api-D0U10BcF.js +0 -13
  284. package/dist/assets/app-presenter-provider-Dffdm_Ek.js +0 -106
  285. package/dist/assets/appearance-settings-page-sfusZc2d.js +0 -1
  286. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +0 -1
  287. package/dist/assets/channel-BEHOSFc5.js +0 -1
  288. package/dist/assets/channels-list-page-Dkfe87Zq.js +0 -8
  289. package/dist/assets/chat-page-CeapbQnG.js +0 -1
  290. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +0 -1
  291. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +0 -1
  292. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +0 -1
  293. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +0 -1
  294. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +0 -1
  295. package/dist/assets/config-split-page-CnQI8cKT.js +0 -1
  296. package/dist/assets/confirm-dialog-RZUWA_j9.js +0 -5
  297. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +0 -1
  298. package/dist/assets/desktop-update-config-CbSYQMYz.js +0 -1
  299. package/dist/assets/doc-browser-DwXHr-mm.js +0 -1
  300. package/dist/assets/doc-browser-jVTq8iY_2.js +0 -1
  301. package/dist/assets/download-BP7ZxrwI.js +0 -1
  302. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +0 -1
  303. package/dist/assets/external-link-DvHCEe3h.js +0 -1
  304. package/dist/assets/eye-C--6dxv_.js +0 -1
  305. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +0 -1
  306. package/dist/assets/folder-BfeO1zCY.js +0 -1
  307. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +0 -1
  308. package/dist/assets/index-BlKbckX4.css +0 -1
  309. package/dist/assets/index-DoZR11Nh.js +0 -2
  310. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +0 -1
  311. package/dist/assets/loader-circle-BWdgwVnn.js +0 -1
  312. package/dist/assets/mcp-marketplace-page-XnRWiC8c.js +0 -1
  313. package/dist/assets/model-config-page-CXnqSbSI.js +0 -1
  314. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +0 -1
  315. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +0 -1
  316. package/dist/assets/plus-BeApS21t.js +0 -1
  317. package/dist/assets/provider-scoped-model-input-ll_Btc9h.js +0 -1
  318. package/dist/assets/providers-config-page-Bcuhqkdl.js +0 -1
  319. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +0 -1
  320. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +0 -1
  321. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +0 -1
  322. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +0 -1
  323. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +0 -1
  324. package/dist/assets/remote-CqKpuOP9.js +0 -1
  325. package/dist/assets/rotate-cw-DHXoiqFB.js +0 -1
  326. package/dist/assets/runtime-config-page-D-Aqxizs.js +0 -1
  327. package/dist/assets/search-config-page-XTKf5rc0.js +0 -1
  328. package/dist/assets/secrets-config-page-Cx-qptcj.js +0 -3
  329. package/dist/assets/security-config-DWGFM2u9.js +0 -1
  330. package/dist/assets/setting-row-DFvioO2a.js +0 -1
  331. package/dist/assets/settings-2-RMWqf-ey.js +0 -1
  332. package/dist/assets/skeleton-DQ5WhuDG.js +0 -1
  333. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +0 -1
  334. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +0 -8
  335. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +0 -1
  336. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +0 -1
  337. package/dist/assets/use-confirm-dialog-CTNA1PC0.js +0 -1
  338. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +0 -1
  339. package/dist/assets/x-qvh05iyu.js +0 -1
  340. package/src/features/chat/components/conversation/chat-conversation-skeleton.tsx +0 -86
  341. package/src/features/settings/pages/__tests__/language-settings-page.test.tsx +0 -36
  342. package/src/features/settings/pages/language-settings-page.tsx +0 -56
  343. package/src/shared/components/actions/action-link.tsx +0 -49
  344. /package/dist/assets/{array-BqYdCIE6.js → array-CAkJT3no.js} +0 -0
  345. /package/dist/assets/{chunk-KEIR6QF5-A3jEqwzn.js → chunk-KEIR6QF5-BgdDoWEH.js} +0 -0
  346. /package/dist/assets/{chunk-Y2CYZVJY-BGrd8tLy.js → chunk-Y2CYZVJY-pTllD0P3.js} +0 -0
  347. /package/dist/assets/{config-hints-LxG80Uj5.js → config-hints-S3g-UVZb.js} +0 -0
  348. /package/dist/assets/{cytoscape.esm-CRW8q92n.js → cytoscape.esm-DmURt-5G.js} +0 -0
  349. /package/dist/assets/{defaultLocale-Cdv9x6q7.js → defaultLocale-BKy_KP3O.js} +0 -0
  350. /package/dist/assets/{dist-dw-gAfM_.js → dist-IgNNB7K9.js} +0 -0
  351. /package/dist/assets/{graphlib-BcNFQr5y.js → graphlib-xw_TBHyH.js} +0 -0
  352. /package/dist/assets/{host-capabilities-CFbAfrrA.js → host-capabilities-B-TtABsP.js} +0 -0
  353. /package/dist/assets/{init-D8TjIqfm.js → init-C9xlLIAu.js} +0 -0
  354. /package/dist/assets/{jszip.min-CP5uPNQa.js → jszip.min-Das2IAnb.js} +0 -0
  355. /package/dist/assets/{katex-wEmrEFTg.js → katex-DSy1eD2d.js} +0 -0
  356. /package/dist/assets/{middleware-CAXxl-af.js → middleware-Df-a4x-q.js} +0 -0
  357. /package/dist/assets/{path-DzBnMI1r.js → path-DB0USYHi.js} +0 -0
  358. /package/dist/assets/{provider-models-BoUhFo9R.js → provider-models-CXUgDxGR.js} +0 -0
  359. /package/dist/assets/{rough.esm-wJ1d3nQl.js → rough.esm-B1B52ZQS.js} +0 -0
  360. /package/dist/assets/{xlsx-DUqJhg5u.js → xlsx-DfjC_iml.js} +0 -0
@@ -1,4 +1,10 @@
1
- import { useCallback, useMemo } from "react";
1
+ import {
2
+ useCallback,
3
+ useMemo,
4
+ useState,
5
+ type FocusEvent,
6
+ type RefObject,
7
+ } from "react";
2
8
  import type { NcpMessage } from "@nextclaw/ncp";
3
9
  import {
4
10
  type ChatInlineDisplayViewModel,
@@ -8,10 +14,15 @@ import {
8
14
  ChatMessageList,
9
15
  } from "@nextclaw/agent-chat-ui";
10
16
  import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
11
- import { adaptChatMessage, type ChatMessageSource } from "@/features/chat/features/message/utils/chat-message.utils";
17
+ import {
18
+ adaptChatMessage,
19
+ type ChatMessageSource,
20
+ } from "@/features/chat/features/message/utils/chat-message.utils";
12
21
  import { buildChatMessageProcessSummary } from "@/features/chat/features/message/utils/chat-message-process-summary.utils";
22
+ import { buildChatMessageExecutionPresentation } from "@/features/chat/features/message/utils/chat-message-execution-summary.utils";
13
23
  import {
14
24
  buildChatMessageAdapterTexts,
25
+ buildChatMessageExecutionLabels,
15
26
  buildChatMessageTexts,
16
27
  } from "@/features/chat/features/message/utils/chat-message-texts.utils";
17
28
  import {
@@ -19,37 +30,111 @@ import {
19
30
  resolveWorkspaceReferencePath,
20
31
  } from "@/features/chat/features/input/utils/chat-inline-token.utils";
21
32
  import { adaptNcpMessageToUiMessage } from "@/features/chat/features/session/utils/ncp-session-adapter.utils";
22
- import {
23
- readContextCompactionTimeline,
24
- type ContextCompactionTimelineView,
25
- } from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
33
+ import { type ContextCompactionTimelineView } from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
26
34
  import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
27
35
  import { ChatInlineFilePreview } from "@/features/chat/features/message/components/chat-inline-file-preview";
28
36
  import { ChatInlinePanelAppCard } from "@/features/chat/features/message/components/chat-inline-panel-app-card";
29
37
  import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
30
- import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
31
38
  import { useChatMessageLayoutStore } from "@/features/chat/stores/chat-message-layout.store";
32
39
  import { useNcpChatSelectedSession } from "@/features/chat/features/ncp/hooks/use-ncp-chat-derived-state";
40
+ import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
41
+ import {
42
+ buildSessionTypeOptions,
43
+ DEFAULT_SESSION_TYPE,
44
+ normalizeSessionType,
45
+ } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
33
46
  import { useI18n } from "@/app/components/i18n-provider";
47
+ import { useChatMessageVirtualizer } from "@/features/chat/features/message/hooks/use-chat-message-virtualizer";
48
+ import {
49
+ buildChatMessageTimelineItems,
50
+ isVisibleChatMessage,
51
+ type ChatTimelineItem,
52
+ type ContextInheritanceTimelineView,
53
+ } from "@/features/chat/features/message/utils/chat-message-timeline.utils";
34
54
  import { buildServerPathContentUrl } from "@/shared/lib/api";
35
55
  import { formatDateTime, t } from "@/shared/lib/i18n";
56
+ import { cn } from "@/shared/lib/utils";
36
57
 
37
58
  type ChatMessageListContainerProps = {
38
59
  messages: readonly NcpMessage[];
39
60
  isSending: boolean;
61
+ sessionKey: string | null;
62
+ scrollRef: RefObject<HTMLDivElement | null>;
40
63
  className?: string;
41
64
  };
42
65
 
43
- const INHERITED_FROM_SESSION_METADATA_KEY = "inherited_from_session_id";
66
+ class ChatMessageViewModelAdapter {
67
+ private readonly cache = new WeakMap<
68
+ NcpMessage,
69
+ {
70
+ language: Parameters<typeof formatDateTime>[1];
71
+ processSummaryLabel: string | null;
72
+ executionPresentationKey: string | null;
73
+ viewModel: ChatMessageViewModel;
74
+ }
75
+ >();
44
76
 
45
- const messageViewModelCache = new WeakMap<
46
- NcpMessage,
47
- {
48
- language: string;
49
- processSummaryLabel: string | null;
50
- viewModel: ChatMessageViewModel;
51
- }
52
- >();
77
+ adapt = (params: {
78
+ executionLabels: ReturnType<typeof buildChatMessageExecutionLabels>;
79
+ language: Parameters<typeof formatDateTime>[1];
80
+ processedLabel: string;
81
+ rawMessages: readonly NcpMessage[];
82
+ texts: ReturnType<typeof buildChatMessageAdapterTexts>;
83
+ }): ChatMessageViewModel[] => {
84
+ const { executionLabels, language, processedLabel, rawMessages, texts } =
85
+ params;
86
+ return rawMessages.filter(isVisibleChatMessage).flatMap((message) => {
87
+ const processSummary = buildChatMessageProcessSummary({
88
+ message,
89
+ processedLabel,
90
+ });
91
+ const processSummaryLabel = processSummary?.label ?? null;
92
+ const executionPresentation = buildChatMessageExecutionPresentation({
93
+ message,
94
+ labels: executionLabels,
95
+ });
96
+ const executionPresentationKey = executionPresentation?.cacheKey ?? null;
97
+ const cached = this.cache.get(message);
98
+ if (
99
+ cached &&
100
+ cached.language === language &&
101
+ cached.processSummaryLabel === processSummaryLabel &&
102
+ cached.executionPresentationKey === executionPresentationKey
103
+ ) {
104
+ return [cached.viewModel];
105
+ }
106
+
107
+ const uiMessage = adaptNcpMessageToUiMessage(message);
108
+ const sourceMessage: ChatMessageSource = {
109
+ id: uiMessage.id,
110
+ role: uiMessage.role,
111
+ meta: {
112
+ timestamp: uiMessage.meta?.timestamp,
113
+ status: uiMessage.meta?.status,
114
+ inlineTokens: readInlineTokensFromMetadata(message.metadata),
115
+ processSummary,
116
+ executionSummaryLabel: executionPresentation?.summaryLabel,
117
+ moreActions: executionPresentation?.moreActions,
118
+ },
119
+ parts: uiMessage.parts as unknown as ChatMessageSource["parts"],
120
+ };
121
+ const viewModel = adaptChatMessage(sourceMessage, {
122
+ formatTimestamp: (value) => formatDateTime(value, language),
123
+ texts,
124
+ });
125
+
126
+ this.cache.set(message, {
127
+ language,
128
+ processSummaryLabel,
129
+ executionPresentationKey,
130
+ viewModel,
131
+ });
132
+ return [viewModel];
133
+ });
134
+ };
135
+ }
136
+
137
+ const chatMessageViewModelAdapter = new ChatMessageViewModelAdapter();
53
138
 
54
139
  function renderChatInlineDisplay(display: ChatInlineDisplayViewModel) {
55
140
  if (display.target.type !== "panel_app") {
@@ -65,43 +150,28 @@ function renderChatInlineDisplay(display: ChatInlineDisplayViewModel) {
65
150
  );
66
151
  }
67
152
 
68
- const renderChatToolAgent = (agentId: string) => <AgentIdentityAvatar agentId={agentId} className="h-4 w-4 shrink-0" />;
69
- const renderChatPanelAppCard = (panelApp: ChatPanelAppCardViewModel) => <ChatInlinePanelAppCard panelApp={panelApp} />;
153
+ const renderChatToolAgent = (agentId: string) => (
154
+ <AgentIdentityAvatar agentId={agentId} className="h-4 w-4 shrink-0" />
155
+ );
156
+ const renderChatPanelAppCard = (panelApp: ChatPanelAppCardViewModel) => (
157
+ <ChatInlinePanelAppCard panelApp={panelApp} />
158
+ );
70
159
 
71
- type ContextInheritanceTimelineView = {
72
- sourceSessionId: string;
73
- inheritedMessageCount: number;
74
- };
75
-
76
- type ContextInheritanceTimelineBoundary = ContextInheritanceTimelineView & {
77
- boundaryIndex: number;
78
- };
79
-
80
- type ChatTimelineItem =
81
- | {
82
- kind: "messages";
83
- key: string;
84
- messages: ChatMessageViewModel[];
85
- }
86
- | {
87
- kind: "compaction";
88
- key: string;
89
- checkpoint: ContextCompactionTimelineView;
90
- }
91
- | {
92
- kind: "context-inheritance";
93
- key: string;
94
- inheritance: ContextInheritanceTimelineView;
95
- };
96
-
97
- function ChatContextCompactionDivider({ checkpoint }: { checkpoint: ContextCompactionTimelineView }) {
160
+ function ChatContextCompactionDivider({
161
+ checkpoint,
162
+ }: {
163
+ checkpoint: ContextCompactionTimelineView;
164
+ }) {
98
165
  const title = [
99
166
  `${t("chatContextCompactionCoveredMessages")}: ${checkpoint.coveredSessionMessageCount}`,
100
167
  `${t("chatContextCompactionOriginalTokens")}: ${checkpoint.originalEstimatedTokens}`,
101
168
  `${t("chatContextCompactionProjectedTokens")}: ${checkpoint.projectedEstimatedTokens}`,
102
169
  ].join("\n");
103
170
  return (
104
- <div className="my-4 flex items-center gap-3 text-[11px] text-muted-foreground" title={title}>
171
+ <div
172
+ className="my-4 flex items-center gap-3 text-[11px] text-muted-foreground"
173
+ title={title}
174
+ >
105
175
  <div className="h-px flex-1 bg-border" />
106
176
  <div className="inline-flex items-center gap-2 rounded-full border border-border bg-muted px-3 py-1">
107
177
  {checkpoint.status === "compressing" ? (
@@ -120,13 +190,20 @@ function ChatContextCompactionDivider({ checkpoint }: { checkpoint: ContextCompa
120
190
  );
121
191
  }
122
192
 
123
- function ChatContextInheritanceDivider({ inheritance }: { inheritance: ContextInheritanceTimelineView }) {
193
+ function ChatContextInheritanceDivider({
194
+ inheritance,
195
+ }: {
196
+ inheritance: ContextInheritanceTimelineView;
197
+ }) {
124
198
  const title = [
125
199
  `${t("chatContextInheritanceSourceSession")}: ${inheritance.sourceSessionId}`,
126
200
  `${t("chatContextInheritanceMessages")}: ${inheritance.inheritedMessageCount}`,
127
201
  ].join("\n");
128
202
  return (
129
- <div className="my-4 flex items-center gap-3 text-[11px] text-emerald-700" title={title}>
203
+ <div
204
+ className="my-4 flex items-center gap-3 text-[11px] text-emerald-700"
205
+ title={title}
206
+ >
130
207
  <div className="h-px flex-1 bg-emerald-100" />
131
208
  <div className="inline-flex items-center gap-2 rounded-full border border-emerald-200 bg-emerald-50 px-3 py-1">
132
209
  <span className="h-1.5 w-1.5 rounded-full bg-emerald-400" />
@@ -137,155 +214,41 @@ function ChatContextInheritanceDivider({ inheritance }: { inheritance: ContextIn
137
214
  );
138
215
  }
139
216
 
140
- function resolveCompactionBoundaryIndex(params: {
141
- rawMessages: readonly NcpMessage[];
142
- visibleRawMessages: readonly NcpMessage[];
143
- rawMessageId: string;
144
- }): number {
145
- const { rawMessageId, rawMessages, visibleRawMessages } = params;
146
- const physicalIndex = rawMessages.findIndex((message) => message.id === rawMessageId);
147
- if (physicalIndex < 0) {
148
- return visibleRawMessages.length - 1;
149
- }
150
- const previousVisibleCount = rawMessages.slice(0, physicalIndex).filter(isVisibleChatMessage).length;
151
- return previousVisibleCount - 1;
152
- }
153
-
154
- function readInheritedSourceSessionId(message: NcpMessage): string | null {
155
- const value = message.metadata?.[INHERITED_FROM_SESSION_METADATA_KEY];
156
- return typeof value === "string" && value.trim() ? value.trim() : null;
157
- }
158
-
159
- function isVisibleChatMessage(message: NcpMessage): boolean {
160
- return !readContextCompactionTimeline(message) && !readInheritedSourceSessionId(message);
161
- }
162
-
163
- function resolveContextInheritanceBoundary(messages: readonly NcpMessage[]): ContextInheritanceTimelineBoundary | null {
164
- const boundaryIndex = messages.findIndex((message) => readInheritedSourceSessionId(message));
165
- if (boundaryIndex < 0) {
166
- return null;
167
- }
168
- const sourceSessionId = readInheritedSourceSessionId(messages[boundaryIndex]);
169
- if (!sourceSessionId) {
170
- return null;
171
- }
172
- return {
173
- boundaryIndex: messages.slice(0, boundaryIndex).filter(isVisibleChatMessage).length,
174
- sourceSessionId,
175
- inheritedMessageCount: messages.filter((message) => readInheritedSourceSessionId(message) === sourceSessionId)
176
- .length,
177
- };
178
- }
179
-
180
- function buildTimelineItems(params: {
181
- rawMessages: readonly NcpMessage[];
182
- messages: ChatMessageViewModel[];
183
- }): ChatTimelineItem[] {
184
- const visibleRawMessages = params.rawMessages.filter(isVisibleChatMessage);
185
- const checkpoints = params.rawMessages
186
- .map((message) => ({
187
- rawMessageId: message.id,
188
- checkpoint: readContextCompactionTimeline(message),
189
- }))
190
- .filter(
191
- (
192
- entry,
193
- ): entry is {
194
- rawMessageId: string;
195
- checkpoint: ContextCompactionTimelineView;
196
- } => Boolean(entry.checkpoint),
197
- )
198
- .map((entry) => ({
199
- key: entry.rawMessageId,
200
- checkpoint: entry.checkpoint,
201
- boundaryIndex: resolveCompactionBoundaryIndex({
202
- rawMessages: params.rawMessages,
203
- visibleRawMessages,
204
- rawMessageId: entry.rawMessageId,
205
- }),
206
- }))
207
- .sort((left, right) => left.boundaryIndex - right.boundaryIndex);
208
- const contextInheritance = resolveContextInheritanceBoundary(params.rawMessages);
209
-
210
- const items: ChatTimelineItem[] = [];
211
- let pendingMessages: ChatMessageViewModel[] = [];
212
- let checkpointCursor = 0;
213
- const flushPendingMessages = (key: string) => {
214
- if (pendingMessages.length === 0) {
215
- return;
216
- }
217
- items.push({
218
- kind: "messages",
219
- key,
220
- messages: pendingMessages,
221
- });
222
- pendingMessages = [];
223
- };
224
-
225
- visibleRawMessages.forEach((rawMessage, index) => {
226
- if (contextInheritance?.boundaryIndex === index) {
227
- flushPendingMessages("messages-before-context-inheritance");
228
- items.push({
229
- kind: "context-inheritance",
230
- key: "context-inheritance",
231
- inheritance: contextInheritance,
232
- });
233
- }
234
- const message = params.messages[index];
235
- if (message) {
236
- pendingMessages.push(message);
237
- }
238
- while (checkpointCursor < checkpoints.length && checkpoints[checkpointCursor]?.boundaryIndex <= index) {
239
- const currentCheckpoint = checkpoints[checkpointCursor];
240
- flushPendingMessages(`messages-before-${currentCheckpoint.key}`);
241
- items.push({
242
- kind: "compaction",
243
- key: currentCheckpoint.key,
244
- checkpoint: currentCheckpoint.checkpoint,
245
- });
246
- checkpointCursor += 1;
247
- }
248
- });
249
- if (contextInheritance?.boundaryIndex === visibleRawMessages.length) {
250
- flushPendingMessages("messages-before-context-inheritance");
251
- items.push({
252
- kind: "context-inheritance",
253
- key: "context-inheritance",
254
- inheritance: contextInheritance,
255
- });
256
- }
257
- while (checkpointCursor < checkpoints.length) {
258
- const currentCheckpoint = checkpoints[checkpointCursor];
259
- flushPendingMessages(`messages-before-${currentCheckpoint.key}`);
260
- items.push({
261
- kind: "compaction",
262
- key: currentCheckpoint.key,
263
- checkpoint: currentCheckpoint.checkpoint,
264
- });
265
- checkpointCursor += 1;
266
- }
267
- flushPendingMessages("messages-final");
268
- if (items.length === 0) {
269
- items.push({
270
- kind: "messages",
271
- key: "messages-empty",
272
- messages: [],
273
- });
274
- }
275
- return items;
276
- }
277
-
278
217
  export function ChatMessageListContainer({
279
218
  messages: rawMessages,
280
219
  isSending,
220
+ scrollRef,
221
+ sessionKey,
281
222
  className,
282
223
  }: ChatMessageListContainerProps) {
283
224
  const presenter = usePresenter();
284
225
  const { language } = useI18n();
285
226
  const messageLayout = useChatMessageLayoutStore((state) => state.layout);
286
- const selectedSessionKey = useChatSessionListStore((state) => state.snapshot.selectedSessionKey);
287
- const selectedSession = useNcpChatSelectedSession(selectedSessionKey);
288
- const localFileBasePath = selectedSession?.workingDir ?? selectedSession?.projectRoot ?? null;
227
+ const selectedSession = useNcpChatSelectedSession(sessionKey);
228
+ const sessionTypesData = useChatQueryStore(
229
+ (state) => state.snapshot.sessionTypesQuery?.data ?? null,
230
+ );
231
+ const activeSessionType = normalizeSessionType(
232
+ selectedSession?.sessionType ??
233
+ sessionTypesData?.defaultType ??
234
+ DEFAULT_SESSION_TYPE,
235
+ );
236
+ const sessionTypeOption = buildSessionTypeOptions(
237
+ sessionTypesData?.options ?? [],
238
+ ).find((option) => option.value === activeSessionType);
239
+ const assistantAvatarIcon = sessionTypeOption?.icon?.src?.trim() ? (
240
+ <SessionContextIconNode
241
+ icon={{
242
+ kind: "runtime-image",
243
+ src: sessionTypeOption.icon.src,
244
+ alt: sessionTypeOption.icon.alt ?? null,
245
+ name: sessionTypeOption.label,
246
+ }}
247
+ className="h-[65%] w-[65%]"
248
+ />
249
+ ) : undefined;
250
+ const localFileBasePath =
251
+ selectedSession?.workingDir ?? selectedSession?.projectRoot ?? null;
289
252
  const renderInlineDisplayWithFiles = useCallback(
290
253
  (display: ChatInlineDisplayViewModel) => {
291
254
  const panelAppDisplay = renderChatInlineDisplay(display);
@@ -296,7 +259,7 @@ export function ChatMessageListContainer({
296
259
  return (
297
260
  <ChatInlineFilePreview
298
261
  display={display}
299
- parentSessionKey={selectedSessionKey}
262
+ parentSessionKey={sessionKey}
300
263
  sessionProjectRoot={selectedSession?.projectRoot ?? null}
301
264
  sessionWorkingDir={localFileBasePath}
302
265
  onFileOpen={presenter.chatThreadManager.openFilePreview}
@@ -305,64 +268,71 @@ export function ChatMessageListContainer({
305
268
  }
306
269
  return undefined;
307
270
  },
308
- [localFileBasePath, presenter.chatThreadManager, selectedSession?.projectRoot, selectedSessionKey],
271
+ [
272
+ localFileBasePath,
273
+ presenter.chatThreadManager,
274
+ selectedSession?.projectRoot,
275
+ sessionKey,
276
+ ],
309
277
  );
310
278
  const resolveFileContentUrl = useCallback(
311
- (action: { path: string }) => buildServerPathContentUrl(action.path, localFileBasePath),
279
+ (action: { path: string }) =>
280
+ buildServerPathContentUrl(action.path, localFileBasePath),
312
281
  [localFileBasePath],
313
282
  );
314
- const texts = useMemo(() => buildChatMessageAdapterTexts(language), [language]);
315
-
316
- const messages = useMemo(() => {
317
- const visibleRawMessages = rawMessages.filter(isVisibleChatMessage);
318
- const processedLabel = t("chatProcessSummaryProcessed");
319
- return visibleRawMessages.flatMap((message) => {
320
- const processSummary = buildChatMessageProcessSummary({
321
- message,
322
- processedLabel,
323
- });
324
- const processSummaryLabel = processSummary?.label ?? null;
325
- const cached = messageViewModelCache.get(message);
326
- if (cached && cached.language === language && cached.processSummaryLabel === processSummaryLabel) {
327
- return [cached.viewModel];
328
- }
329
-
330
- const uiMessage = adaptNcpMessageToUiMessage(message);
331
- const sourceMessage: ChatMessageSource = {
332
- id: uiMessage.id,
333
- role: uiMessage.role,
334
- meta: {
335
- timestamp: uiMessage.meta?.timestamp,
336
- status: uiMessage.meta?.status,
337
- inlineTokens: readInlineTokensFromMetadata(message.metadata),
338
- processSummary,
339
- },
340
- parts: uiMessage.parts as unknown as ChatMessageSource["parts"],
341
- };
342
- const viewModel = adaptChatMessage(sourceMessage, {
343
- formatTimestamp: (value) => formatDateTime(value, language),
344
- texts,
345
- });
283
+ const texts = useMemo(
284
+ () => buildChatMessageAdapterTexts(language),
285
+ [language],
286
+ );
287
+ const executionLabels = useMemo(
288
+ () => buildChatMessageExecutionLabels(language),
289
+ [language],
290
+ );
346
291
 
347
- messageViewModelCache.set(message, {
292
+ const messages = useMemo(
293
+ () =>
294
+ chatMessageViewModelAdapter.adapt({
295
+ executionLabels,
348
296
  language,
349
- processSummaryLabel,
350
- viewModel,
351
- });
352
- return [viewModel];
353
- });
354
- }, [language, rawMessages, texts]);
297
+ processedLabel: t("chatProcessSummaryProcessed"),
298
+ rawMessages,
299
+ texts,
300
+ }),
301
+ [executionLabels, language, rawMessages, texts],
302
+ );
355
303
 
356
- const hasAssistantDraft = useMemo(
304
+ const activeAssistantMessage = messages.findLast(
305
+ (message) =>
306
+ message.role === "assistant" &&
307
+ (message.status === "streaming" || message.status === "pending"),
308
+ );
309
+ const hasAssistantDraft = Boolean(activeAssistantMessage);
310
+ const messageTexts = useMemo(
311
+ () => buildChatMessageTexts(language),
312
+ [language],
313
+ );
314
+ const timelineItems = useMemo(
315
+ () => buildChatMessageTimelineItems({ rawMessages, messages }),
316
+ [messages, rawMessages],
317
+ );
318
+ const virtualRows = useMemo<ChatTimelineItem[]>(
357
319
  () =>
358
- messages.some(
359
- (message) => message.role === "assistant" && (message.status === "streaming" || message.status === "pending"),
360
- ),
361
- [messages],
320
+ isSending && !hasAssistantDraft
321
+ ? [...timelineItems, { kind: "typing", key: "typing" }]
322
+ : timelineItems,
323
+ [hasAssistantDraft, isSending, timelineItems],
324
+ );
325
+ const [focusedRowKey, setFocusedRowKey] = useState<string | null>(null);
326
+ const activeRowKey = activeAssistantMessage ? `message:${activeAssistantMessage.id}` : null;
327
+ const { containerRef, virtualizer } = useChatMessageVirtualizer({
328
+ rows: virtualRows,
329
+ scrollRef,
330
+ activeRowKey,
331
+ focusedRowKey,
332
+ });
333
+ const sessionSkillsQuery = useChatQueryStore(
334
+ (state) => state.snapshot.sessionSkillsQuery,
362
335
  );
363
- const messageTexts = useMemo(() => buildChatMessageTexts(language), [language]);
364
- const timelineItems = useMemo(() => buildTimelineItems({ rawMessages, messages }), [messages, rawMessages]);
365
- const sessionSkillsQuery = useChatQueryStore((state) => state.snapshot.sessionSkillsQuery);
366
336
  const handleInlineTokenClick = useCallback(
367
337
  (token: ChatInlineTokenViewModel) => {
368
338
  if (token.kind === "panel_app") {
@@ -371,7 +341,10 @@ export function ChatMessageListContainer({
371
341
  });
372
342
  return;
373
343
  }
374
- if (token.kind === "workspace_file" || token.kind === "workspace_directory") {
344
+ if (
345
+ token.kind === "workspace_file" ||
346
+ token.kind === "workspace_directory"
347
+ ) {
375
348
  const path = resolveWorkspaceReferencePath({
376
349
  projectRoot: selectedSession?.projectRoot,
377
350
  relativePath: token.key,
@@ -391,7 +364,9 @@ export function ChatMessageListContainer({
391
364
  return;
392
365
  }
393
366
  const records = sessionSkillsQuery?.data?.records ?? [];
394
- const matched = records.find((record) => record.ref === skillKey || record.name === skillKey);
367
+ const matched = records.find(
368
+ (record) => record.ref === skillKey || record.name === skillKey,
369
+ );
395
370
  const skillPath = matched?.path?.trim();
396
371
  if (!skillPath) {
397
372
  return;
@@ -403,10 +378,21 @@ export function ChatMessageListContainer({
403
378
  previewViewer: "rendered",
404
379
  });
405
380
  },
406
- [presenter.chatThreadManager, presenter.chatUiManager, selectedSession?.projectRoot, sessionSkillsQuery],
381
+ [
382
+ presenter.chatThreadManager,
383
+ presenter.chatUiManager,
384
+ selectedSession?.projectRoot,
385
+ sessionSkillsQuery,
386
+ ],
407
387
  );
408
388
  const handleAttachmentOpen = useCallback(
409
- (file: { label: string; mimeType: string; dataUrl?: string; sizeBytes?: number; isImage: boolean }) => {
389
+ (file: {
390
+ label: string;
391
+ mimeType: string;
392
+ dataUrl?: string;
393
+ sizeBytes?: number;
394
+ isImage: boolean;
395
+ }) => {
410
396
  const contentUrl = file.dataUrl?.trim();
411
397
  if (!contentUrl) {
412
398
  return;
@@ -422,32 +408,54 @@ export function ChatMessageListContainer({
422
408
  },
423
409
  [presenter.chatThreadManager],
424
410
  );
411
+ const handleRowBlur = useCallback((event: FocusEvent<HTMLDivElement>) => {
412
+ if (!event.currentTarget.contains(event.relatedTarget)) {
413
+ setFocusedRowKey(null);
414
+ }
415
+ }, []);
425
416
  return (
426
- <div className={className}>
427
- {timelineItems.map((item, index) =>
428
- item.kind === "compaction" ? (
429
- <ChatContextCompactionDivider key={item.key} checkpoint={item.checkpoint} />
430
- ) : item.kind === "context-inheritance" ? (
431
- <ChatContextInheritanceDivider key={item.key} inheritance={item.inheritance} />
432
- ) : (
433
- <ChatMessageList
417
+ <div className={cn("relative", className)} ref={containerRef}>
418
+ {virtualizer.getVirtualItems().map((virtualRow) => {
419
+ const item = virtualRows[virtualRow.index];
420
+ if (!item) {
421
+ return null;
422
+ }
423
+ return (
424
+ <div
425
+ data-index={virtualRow.index}
434
426
  key={item.key}
435
- layout={messageLayout}
436
- messages={item.messages}
437
- isSending={index === timelineItems.length - 1 ? isSending : false}
438
- hasAssistantDraft={hasAssistantDraft}
439
- texts={messageTexts}
440
- onToolAction={presenter.chatThreadManager.handleToolAction}
441
- onFileOpen={presenter.chatThreadManager.openFilePreview}
442
- onAttachmentOpen={handleAttachmentOpen}
443
- onInlineTokenClick={handleInlineTokenClick}
444
- resolveFileContentUrl={resolveFileContentUrl}
445
- renderInlineDisplay={renderInlineDisplayWithFiles}
446
- renderToolAgent={renderChatToolAgent}
447
- renderPanelAppCard={renderChatPanelAppCard}
448
- />
449
- ),
450
- )}
427
+ ref={virtualizer.measureElement}
428
+ onBlurCapture={handleRowBlur}
429
+ onFocusCapture={() => setFocusedRowKey(item.key)}
430
+ className="absolute left-0 top-0 w-full"
431
+ >
432
+ {item.kind === "compaction" ? (
433
+ <ChatContextCompactionDivider checkpoint={item.checkpoint} />
434
+ ) : item.kind === "context-inheritance" ? (
435
+ <ChatContextInheritanceDivider inheritance={item.inheritance} />
436
+ ) : (
437
+ <div className={item.kind === "message" ? "pb-5" : undefined}>
438
+ <ChatMessageList
439
+ assistantAvatarIcon={assistantAvatarIcon}
440
+ layout={messageLayout}
441
+ messages={item.kind === "message" ? [item.message] : []}
442
+ isSending={item.kind === "typing"}
443
+ hasAssistantDraft={hasAssistantDraft}
444
+ texts={messageTexts}
445
+ onToolAction={presenter.chatThreadManager.handleToolAction}
446
+ onFileOpen={presenter.chatThreadManager.openFilePreview}
447
+ onAttachmentOpen={handleAttachmentOpen}
448
+ onInlineTokenClick={handleInlineTokenClick}
449
+ resolveFileContentUrl={resolveFileContentUrl}
450
+ renderInlineDisplay={renderInlineDisplayWithFiles}
451
+ renderToolAgent={renderChatToolAgent}
452
+ renderPanelAppCard={renderChatPanelAppCard}
453
+ />
454
+ </div>
455
+ )}
456
+ </div>
457
+ );
458
+ })}
451
459
  </div>
452
460
  );
453
461
  }