@nextclaw/ui 0.13.12 → 0.14.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 (430) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/dist/assets/{api-DmxQnm30.js → api-B50yVdT8.js} +7 -7
  3. package/dist/assets/app-presenter-provider-BI-ewLbO.js +3 -0
  4. package/dist/assets/arrow-left-BOCJyLbQ.js +1 -0
  5. package/dist/assets/book-open-Bd2Xbrrc.js +1 -0
  6. package/dist/assets/channels-list-page-moJkpxtU.js +8 -0
  7. package/dist/assets/chat-page-CO27cl4v.js +105 -0
  8. package/dist/assets/config-split-page-CsQ2rIdq.js +1 -0
  9. package/dist/assets/{confirm-dialog-BWRNsRgb.js → confirm-dialog-DRSelLdI.js} +2 -2
  10. package/dist/assets/desktop-update-config-qa-SynOc.js +1 -0
  11. package/dist/assets/{dist-DzM2ZEVN.js → dist-D19wiQwB.js} +1 -1
  12. package/dist/assets/{dist-CyWdaJvr.js → dist-NZRV-BxD.js} +1 -1
  13. package/dist/assets/doc-browser-2ddUJCA0.js +1 -0
  14. package/dist/assets/doc-browser-BjHUczQb.js +1 -0
  15. package/dist/assets/doc-browser-context-C8KPbj-p.js +1 -0
  16. package/dist/assets/{ellipsis-BDgTYNDX.js → ellipsis-BYy8TP0A.js} +1 -1
  17. package/dist/assets/{es2015-WqekV0OK.js → es2015-BUIIKCNh.js} +1 -1
  18. package/dist/assets/{external-link-BLjo8z2a.js → external-link-528k6MDD.js} +1 -1
  19. package/dist/assets/host-capabilities-9bB_998Z.js +1 -0
  20. package/dist/assets/index-CMNrRhnG.js +101 -0
  21. package/dist/assets/index-UTmTW1eU.css +1 -0
  22. package/dist/assets/{key-round-Du8x3sWa.js → key-round-D3vD9dj7.js} +1 -1
  23. package/dist/assets/loader-circle-DV9TSRns.js +1 -0
  24. package/dist/assets/mcp-marketplace-page-CS5Lh3oA.js +1 -0
  25. package/dist/assets/mcp-marketplace-page-DEOMcWb0.js +40 -0
  26. package/dist/assets/model-config-page-BSc4o7pb.js +1 -0
  27. package/dist/assets/{notice-card-3a7FmAz-.js → notice-card-qqlbsOoR.js} +1 -1
  28. package/dist/assets/plus-Bf9qhPDO.js +1 -0
  29. package/dist/assets/{popover-BlJLguiZ.js → popover-BnmPOXe3.js} +1 -1
  30. package/dist/assets/provider-scoped-model-input-Ca56s475.js +1 -0
  31. package/dist/assets/providers-config-page-D8Kf4U3u.js +1 -0
  32. package/dist/assets/{react-BVjl1ZaK.js → react-Dsotw5vM.js} +1 -1
  33. package/dist/assets/{refresh-cw-DFR87zei.js → refresh-cw-Bmx4wc9P.js} +1 -1
  34. package/dist/assets/remote-DNMEy_c4.js +1 -0
  35. package/dist/assets/rotate-cw-CL0sVBel.js +1 -0
  36. package/dist/assets/runtime-config-page-CUFtKr0U.js +1 -0
  37. package/dist/assets/{save-BDmIlHvN.js → save-CHDnDTok.js} +1 -1
  38. package/dist/assets/search-D7L-TCH-.js +1 -0
  39. package/dist/assets/search-config-page-CA3RVAef.js +1 -0
  40. package/dist/assets/secrets-config-page-95yaH1fa.js +3 -0
  41. package/dist/assets/{status-dot-SVPjIyG5.js → status-dot-Dm4h-bjn.js} +1 -1
  42. package/dist/assets/tabs-CJvw5Lqp.js +1 -0
  43. package/dist/assets/{tag-chip-Cdwb3JSH.js → tag-chip-Cr8GwpRE.js} +1 -1
  44. package/dist/assets/{tooltip-CKGxLGnG.js → tooltip-Bxzi6aDL.js} +1 -1
  45. package/dist/assets/{trash-2-yLA8BEcE.js → trash-2-CSfX-BvU.js} +1 -1
  46. package/dist/assets/use-config-TzwzTgg0.js +1 -0
  47. package/dist/assets/x-fkIWvi07.js +1 -0
  48. package/dist/index.html +27 -25
  49. package/package.json +9 -8
  50. package/src/app/index.tsx +8 -4
  51. package/src/features/account/components/account-panel.tsx +3 -3
  52. package/src/features/account/hooks/{use-auth.test.ts → __tests__/use-auth.test.ts} +1 -1
  53. package/src/features/account/managers/account.manager.ts +6 -5
  54. package/src/features/agents/components/{agents-page.test.tsx → __tests__/agents-page.test.tsx} +41 -2
  55. package/src/features/agents/components/agent-dialogs.tsx +2 -2
  56. package/src/features/agents/components/agents-page.tsx +1 -1
  57. package/src/features/channels/components/config/channel-form.tsx +1 -1
  58. package/src/features/channels/pages/channels-list-page.tsx +18 -15
  59. package/src/features/channels/utils/{channel-form-fields.utils.test.ts → __tests__/channel-form-fields.utils.test.ts} +1 -1
  60. package/src/features/chat/components/{chat-welcome.test.tsx → __tests__/chat-welcome.test.tsx} +1 -1
  61. package/src/features/chat/components/conversation/{chat-conversation-header.test.tsx → __tests__/chat-conversation-header.test.tsx} +25 -32
  62. package/src/features/chat/components/conversation/{chat-conversation-panel.test.tsx → __tests__/chat-conversation-panel.test.tsx} +201 -37
  63. package/src/features/chat/components/conversation/chat-conversation-alerts.tsx +37 -0
  64. package/src/features/chat/components/conversation/chat-conversation-content.tsx +91 -0
  65. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +151 -0
  66. package/src/features/chat/components/conversation/chat-conversation-header.tsx +26 -89
  67. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +13 -376
  68. package/src/features/chat/components/conversation/chat-conversation-parent-banner.tsx +16 -0
  69. package/src/features/chat/components/conversation/chat-conversation-skeleton.tsx +86 -0
  70. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +42 -0
  71. package/src/features/chat/components/layout/{chat-page-shell.test.tsx → __tests__/chat-page-shell.test.tsx} +1 -1
  72. package/src/features/chat/components/layout/__tests__/chat-sidebar-read-state.test.tsx +167 -0
  73. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +834 -0
  74. package/src/features/chat/components/layout/chat-page-shell.tsx +11 -30
  75. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +33 -53
  76. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +1 -1
  77. package/src/features/chat/components/layout/chat-sidebar.tsx +156 -202
  78. package/src/features/chat/components/providers/chat-presenter.provider.tsx +13 -44
  79. package/src/features/chat/{components/conversation → features/input/components/__tests__}/chat-attachment-upload-limit.test.ts +1 -1
  80. package/src/features/chat/{components/conversation → features/input/components}/chat-input-bar.container.tsx +133 -31
  81. package/src/features/chat/{hooks → features/input/hooks/__tests__}/use-chat-input-bar-controller.test.tsx +1 -1
  82. package/src/features/chat/{utils → features/input/utils/__tests__}/chat-composer-state.utils.test.ts +1 -1
  83. package/src/features/chat/{utils → features/input/utils/__tests__}/chat-inline-token.utils.test.ts +1 -1
  84. package/src/features/chat/{utils → features/input/utils/__tests__}/chat-input-bar.utils.test.ts +2 -2
  85. package/src/features/chat/{utils → features/input/utils/__tests__}/ncp-chat-input-availability.utils.test.ts +2 -1
  86. package/src/features/chat/{utils → features/input/utils}/ncp-chat-input-availability.utils.ts +6 -6
  87. package/src/features/chat/{components/conversation → features/message/components/__tests__}/chat-message-list.container.test.tsx +38 -3
  88. package/src/features/chat/{components/conversation → features/message/components}/chat-message-list.container.tsx +9 -13
  89. package/src/features/chat/{utils → features/message/utils/__tests__}/chat-message-invalid-tool-arguments.utils.test.ts +2 -2
  90. package/src/features/chat/{utils → features/message/utils/__tests__}/chat-message-session-spawn-tool-card.utils.test.ts +2 -2
  91. package/src/features/chat/features/message/utils/__tests__/chat-message-show-content-tool-card.utils.test.ts +54 -0
  92. package/src/features/chat/{utils → features/message/utils/__tests__}/chat-message-summary-truncation.utils.test.ts +2 -2
  93. package/src/features/chat/{utils → features/message/utils/__tests__}/chat-message-tool-agent-id.utils.test.ts +1 -1
  94. package/src/features/chat/{utils → features/message/utils/__tests__}/chat-message.utils.test.ts +2 -2
  95. package/src/features/chat/{utils → features/message/utils}/chat-message-inline-content.utils.ts +1 -1
  96. package/src/features/chat/{utils → features/message/utils}/chat-message-part.utils.ts +15 -2
  97. package/src/features/chat/{utils → features/message/utils}/chat-message-session-request-tool-card.utils.ts +1 -1
  98. package/src/features/chat/features/message/utils/chat-message-show-content-tool-card.utils.ts +165 -0
  99. package/src/features/chat/{utils → features/message/utils}/chat-message-tool-card.utils.ts +1 -1
  100. package/src/features/chat/{utils → features/message/utils}/chat-message.utils.ts +1 -1
  101. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +57 -0
  102. package/src/features/chat/{hooks → features/ncp/hooks/__tests__}/use-ncp-agent-runtime.test.tsx +2 -2
  103. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-chat-derived-state.test.tsx +22 -0
  104. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-chat-query-store-sync.test.tsx +101 -0
  105. package/src/features/chat/{hooks → features/ncp/hooks/__tests__}/use-ncp-session-conversation.test.tsx +1 -1
  106. package/src/features/chat/features/ncp/hooks/__tests__/use-ui-show-content-event.test.tsx +52 -0
  107. package/src/features/chat/features/ncp/hooks/use-ncp-chat-derived-state.ts +37 -0
  108. package/src/features/chat/features/ncp/hooks/use-ncp-chat-query-store-sync.ts +28 -0
  109. package/src/features/chat/{hooks → features/ncp/hooks}/use-ncp-child-session-tabs-view.ts +4 -4
  110. package/src/features/chat/{hooks → features/ncp/hooks}/use-ncp-session-conversation.ts +1 -1
  111. package/src/features/chat/{hooks → features/ncp/hooks}/use-ncp-session-list-view.ts +3 -3
  112. package/src/features/chat/features/ncp/hooks/use-ncp-session-queries.ts +32 -0
  113. package/src/features/chat/features/ncp/hooks/use-ui-show-content-event.ts +14 -0
  114. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +77 -0
  115. package/src/features/chat/{utils → features/runtime/utils/__tests__}/ncp-app-client-fetch.utils.test.ts +1 -1
  116. package/src/features/chat/{utils → features/runtime/utils/__tests__}/ncp-chat-runtime-availability.utils.test.ts +1 -1
  117. package/src/features/chat/{utils → features/runtime/utils}/chat-runtime.utils.ts +1 -1
  118. package/src/features/chat/{components → features/session/components}/chat-sidebar-project-groups.tsx +21 -42
  119. package/src/features/chat/{components/layout → features/session/components}/chat-sidebar-session-entry.tsx +40 -13
  120. package/src/features/chat/{components → features/session/components}/chat-sidebar-session-item.tsx +36 -44
  121. package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +78 -0
  122. package/src/features/chat/{components/session → features/session/components}/session-context-icon.tsx +1 -1
  123. package/src/features/chat/{components/conversation/session-header → features/session/components/session-header/__tests__}/chat-session-header-actions.test.tsx +3 -3
  124. package/src/features/chat/{components/conversation/session-header → features/session/components/session-header/__tests__}/chat-session-project-badge.test.tsx +3 -3
  125. package/src/features/chat/{components/conversation → features/session/components}/session-header/chat-session-header-actions.tsx +15 -27
  126. package/src/features/chat/{components/conversation → features/session/components}/session-header/chat-session-project-badge.tsx +1 -1
  127. package/src/features/chat/{hooks → features/session/hooks/__tests__}/use-chat-session-project.test.tsx +2 -2
  128. package/src/features/chat/{hooks → features/session/hooks/__tests__}/use-chat-session-update.test.tsx +1 -1
  129. package/src/features/chat/{hooks → features/session/hooks}/use-chat-session-project.ts +1 -1
  130. package/src/features/chat/{hooks → features/session/hooks}/use-chat-session-update.ts +17 -0
  131. package/src/features/chat/{hooks → features/session/hooks}/use-chat-sidebar-session-label-editor.ts +1 -1
  132. package/src/features/chat/{hooks → features/session/hooks}/use-selected-session-context-window-indicator.ts +1 -1
  133. package/src/features/chat/{utils → features/session/utils/__tests__}/chat-session-display.utils.test.ts +1 -1
  134. package/src/features/chat/{utils → features/session/utils/__tests__}/chat-session-route.utils.test.ts +1 -1
  135. package/src/features/chat/{utils → features/session/utils/__tests__}/ncp-session-adapter.utils.cancelled-tool.test.ts +2 -2
  136. package/src/features/chat/{utils → features/session/utils/__tests__}/ncp-session-adapter.utils.test.ts +2 -2
  137. package/src/features/chat/{utils → features/session/utils/__tests__}/session-context.utils.test.ts +1 -1
  138. package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +60 -0
  139. package/src/features/chat/{utils → features/session/utils}/chat-session-preference-governance.utils.ts +0 -128
  140. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +144 -0
  141. package/src/features/chat/{components → features/session-type/components/__tests__}/chat-session-type-option-item.test.tsx +1 -1
  142. package/src/features/chat/{components → features/session-type/components}/chat-session-type-option-item.tsx +7 -16
  143. package/src/features/chat/features/session-type/components/chat-sidebar-create-menu.tsx +42 -0
  144. package/src/features/chat/{hooks → features/session-type/hooks/__tests__}/use-chat-session-type-state.test.tsx +1 -1
  145. package/src/features/chat/{hooks → features/session-type/hooks}/use-chat-session-type-state.ts +23 -94
  146. package/src/features/chat/features/session-type/utils/chat-session-type.utils.ts +105 -0
  147. package/src/features/chat/{components → features/workspace/components/__tests__}/chat-session-workspace-file-preview.test.tsx +1 -1
  148. package/src/features/chat/{components → features/workspace/components}/chat-session-workspace-file-preview.tsx +2 -2
  149. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +156 -0
  150. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +84 -0
  151. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +138 -0
  152. package/src/features/chat/{components/workspace → features/workspace/components}/session-cron-job-content.tsx +1 -1
  153. package/src/features/chat/features/workspace/hooks/use-chat-conversation-workspace-state.ts +63 -0
  154. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +103 -0
  155. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +183 -0
  156. package/src/features/chat/index.ts +16 -6
  157. package/src/features/chat/managers/__tests__/chat-input.manager.test.ts +355 -0
  158. package/src/features/chat/managers/__tests__/chat-query.manager.test.ts +39 -0
  159. package/src/features/chat/managers/__tests__/chat-run.manager.test.ts +202 -0
  160. package/src/features/chat/managers/{chat-session-list.manager.test.ts → __tests__/chat-session-list.manager.test.ts} +98 -74
  161. package/src/features/chat/managers/{chat-session-preference-sync.manager.test.ts → __tests__/chat-session-preference-sync.manager.test.ts} +1 -1
  162. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +622 -0
  163. package/src/features/chat/managers/__tests__/chat-ui.manager.test.ts +126 -0
  164. package/src/features/chat/managers/{recent-selection.manager.test.ts → __tests__/recent-selection.manager.test.ts} +1 -1
  165. package/src/features/chat/managers/{ncp-chat-input.manager.ts → chat-input.manager.ts} +108 -35
  166. package/src/features/chat/managers/chat-query.manager.ts +48 -0
  167. package/src/features/chat/managers/chat-run.manager.ts +107 -0
  168. package/src/features/chat/managers/chat-session-list.manager.ts +47 -15
  169. package/src/features/chat/managers/chat-session-preference-sync.manager.ts +60 -5
  170. package/src/features/chat/managers/{ncp-chat-thread.manager.ts → chat-thread.manager.ts} +210 -37
  171. package/src/features/chat/managers/chat-ui.manager.ts +55 -9
  172. package/src/features/chat/pages/{ncp-chat-page.test.ts → __tests__/ncp-chat-page.test.ts} +49 -12
  173. package/src/features/chat/pages/ncp-chat-page.tsx +123 -399
  174. package/src/features/chat/presenters/chat.presenter.ts +39 -0
  175. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +148 -0
  176. package/src/features/chat/stores/chat-input.store.ts +5 -1
  177. package/src/features/chat/stores/chat-thread.store.ts +287 -28
  178. package/src/features/chat/stores/ncp-chat-query.store.ts +25 -0
  179. package/src/features/chat/types/chat-input.types.ts +1 -0
  180. package/src/features/chat/types/chat-message.types.ts +1 -0
  181. package/src/features/chat/types/chat-run.types.ts +41 -0
  182. package/src/{shared → features/cron}/components/cron-config.tsx +1 -1
  183. package/src/features/cron/hooks/use-cron-jobs.ts +58 -0
  184. package/src/features/cron/index.ts +7 -0
  185. package/src/features/marketplace/components/{marketplace-page.test.tsx → __tests__/marketplace-page.test.tsx} +37 -1
  186. package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +15 -61
  187. package/src/features/marketplace/components/marketplace-external-skill-source-action.tsx +14 -0
  188. package/src/features/marketplace/components/marketplace-item-list-view.tsx +167 -0
  189. package/src/features/marketplace/components/marketplace-list-card.tsx +27 -31
  190. package/src/features/marketplace/components/marketplace-page-data.ts +0 -6
  191. package/src/features/marketplace/components/marketplace-page-parts.tsx +10 -2
  192. package/src/features/marketplace/components/marketplace-page.tsx +110 -434
  193. package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +1 -1
  194. package/src/features/marketplace/components/mcp/mcp-marketplace-dialogs.tsx +1 -1
  195. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +2 -1
  196. package/src/features/marketplace/hooks/__tests__/use-marketplace.test.tsx +109 -0
  197. package/src/features/marketplace/hooks/use-marketplace-item-actions.ts +101 -0
  198. package/src/features/marketplace/hooks/use-marketplace-item-detail.ts +109 -0
  199. package/src/features/marketplace/hooks/use-marketplace-list-model.ts +160 -0
  200. package/src/features/marketplace/hooks/use-marketplace.ts +3 -5
  201. package/src/features/marketplace/utils/{marketplace-installed-cache.utils.test.ts → __tests__/marketplace-installed-cache.utils.test.ts} +1 -1
  202. package/src/features/panel-apps/components/{panel-app-list-item.test.tsx → __tests__/panel-app-list-item.test.tsx} +1 -1
  203. package/src/features/panel-apps/components/{panel-app-toolbar.test.tsx → __tests__/panel-app-toolbar.test.tsx} +1 -1
  204. package/src/features/panel-apps/utils/{panel-app-view.utils.test.ts → __tests__/panel-app-view.utils.test.ts} +1 -1
  205. package/src/features/pwa/managers/{pwa-service-worker-cache.manager.test.ts → __tests__/pwa-service-worker-cache.manager.test.ts} +1 -1
  206. package/src/features/remote/components/remote-access-page.tsx +2 -2
  207. package/src/features/remote/services/{remote-access-feedback.service.test.ts → __tests__/remote-access-feedback.service.test.ts} +1 -1
  208. package/src/{shared → features/settings}/components/config/provider-form.tsx +7 -7
  209. package/src/{shared/components → features/settings/components/config}/provider-status-badge.tsx +1 -1
  210. package/src/{shared/components/model-config.test.tsx → features/settings/pages/__tests__/model-config-page.test.tsx} +40 -5
  211. package/src/{shared/components/config/providers-list.test.tsx → features/settings/pages/__tests__/providers-config-page.test.tsx} +48 -5
  212. package/src/{shared/components/search-config.test.tsx → features/settings/pages/__tests__/search-config-page.test.tsx} +3 -3
  213. package/src/{shared/components/config/secrets-config.test.tsx → features/settings/pages/__tests__/secrets-config-page.test.tsx} +3 -3
  214. package/src/{shared/components/model-config.tsx → features/settings/pages/model-config-page.tsx} +1 -1
  215. package/src/{shared/components/config/providers-list.tsx → features/settings/pages/providers-config-page.tsx} +3 -3
  216. package/src/{shared/components/search-config.tsx → features/settings/pages/search-config-page.tsx} +1 -1
  217. package/src/{shared/components/config/secrets-config.tsx → features/settings/pages/secrets-config-page.tsx} +2 -2
  218. package/src/{shared/components/config/provider-form-support.test.ts → features/settings/utils/__tests__/provider-form-support.utils.test.ts} +1 -1
  219. package/src/features/side-dock/components/{side-dock.test.tsx → __tests__/side-dock.test.tsx} +25 -0
  220. package/src/features/side-dock/components/side-dock.tsx +13 -0
  221. package/src/features/system-status/components/{runtime-control-card.test.tsx → __tests__/runtime-control-card.test.tsx} +1 -1
  222. package/src/features/system-status/components/{runtime-presence-card.test.tsx → __tests__/runtime-presence-card.test.tsx} +1 -1
  223. package/src/features/system-status/components/desktop-update-config.tsx +2 -1
  224. package/src/features/system-status/components/runtime-presence-card.tsx +2 -2
  225. package/src/features/system-status/managers/{system-status.manager.bootstrap-polling.test.ts → __tests__/system-status.manager.bootstrap-polling.test.ts} +1 -1
  226. package/src/features/system-status/managers/{system-status.manager.test.ts → __tests__/system-status.manager.test.ts} +1 -1
  227. package/src/features/system-status/utils/{system-status.utils.test.ts → __tests__/system-status.utils.test.ts} +1 -1
  228. package/src/platforms/desktop/types/desktop-update.types.ts +9 -0
  229. package/src/shared/components/actions/action-link.tsx +49 -0
  230. package/src/shared/components/common/{agent-avatar.test.tsx → __tests__/agent-avatar.test.tsx} +1 -1
  231. package/src/shared/components/common/agent-identity/use-agent-identity.ts +3 -8
  232. package/src/shared/components/common/brand-header.tsx +1 -1
  233. package/src/shared/components/common/tag-input.tsx +1 -1
  234. package/src/shared/components/doc-browser/{doc-browser-context.test.tsx → __tests__/doc-browser-context.test.tsx} +1 -1
  235. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +64 -161
  236. package/src/shared/components/doc-browser/doc-browser.tsx +4 -4
  237. package/src/shared/components/doc-browser/managers/doc-browser.manager.ts +39 -32
  238. package/src/shared/components/resizable-right-panel/__tests__/resizable-right-panel.test.tsx +66 -0
  239. package/src/shared/components/resizable-right-panel/resizable-right-panel.tsx +11 -8
  240. package/src/shared/components/ui/actions/icon-action-button.tsx +53 -0
  241. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +52 -0
  242. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +228 -0
  243. package/src/shared/hooks/use-app-meta.ts +10 -0
  244. package/src/shared/hooks/use-config.ts +1 -148
  245. package/src/shared/lib/api/{ncp-session.test.ts → __tests__/ncp-session.test.ts} +3 -3
  246. package/src/shared/lib/api/{raw-client.test.ts → __tests__/raw-client.test.ts} +1 -1
  247. package/src/shared/lib/api/chat-session-type.types.ts +3 -0
  248. package/src/shared/lib/cadence/services/{adaptive-cadence.service.test.ts → __tests__/adaptive-cadence.service.test.ts} +1 -1
  249. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +50 -0
  250. package/src/shared/lib/host-capabilities/host-capability.manager.ts +62 -0
  251. package/src/shared/lib/host-capabilities/index.ts +1 -0
  252. package/src/shared/lib/i18n/README.md +16 -3
  253. package/src/shared/lib/i18n/index.ts +93 -426
  254. package/src/shared/lib/i18n/locales/en-US/agents.json +49 -0
  255. package/src/shared/lib/i18n/locales/en-US/channel-auth.json +51 -0
  256. package/src/shared/lib/i18n/locales/en-US/channels.json +50 -0
  257. package/src/shared/lib/i18n/locales/en-US/chat.json +183 -0
  258. package/src/shared/lib/i18n/locales/en-US/core.json +311 -0
  259. package/src/shared/lib/i18n/locales/en-US/cron.json +32 -0
  260. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +90 -0
  261. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +87 -0
  262. package/src/shared/lib/i18n/locales/en-US/marketplace.json +72 -0
  263. package/src/shared/lib/i18n/locales/en-US/path-picker.json +12 -0
  264. package/src/shared/lib/i18n/locales/en-US/pwa.json +26 -0
  265. package/src/shared/lib/i18n/locales/en-US/remote.json +168 -0
  266. package/src/shared/lib/i18n/locales/en-US/runtime-control.json +105 -0
  267. package/src/shared/lib/i18n/locales/en-US/search.json +28 -0
  268. package/src/shared/lib/i18n/locales/zh-CN/agents.json +49 -0
  269. package/src/shared/lib/i18n/locales/zh-CN/channel-auth.json +51 -0
  270. package/src/shared/lib/i18n/locales/zh-CN/channels.json +50 -0
  271. package/src/shared/lib/i18n/locales/zh-CN/chat.json +183 -0
  272. package/src/shared/lib/i18n/locales/zh-CN/core.json +311 -0
  273. package/src/shared/lib/i18n/locales/zh-CN/cron.json +32 -0
  274. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +90 -0
  275. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +87 -0
  276. package/src/shared/lib/i18n/locales/zh-CN/marketplace.json +72 -0
  277. package/src/shared/lib/i18n/locales/zh-CN/path-picker.json +12 -0
  278. package/src/shared/lib/i18n/locales/zh-CN/pwa.json +26 -0
  279. package/src/shared/lib/i18n/locales/zh-CN/remote.json +168 -0
  280. package/src/shared/lib/i18n/locales/zh-CN/runtime-control.json +105 -0
  281. package/src/shared/lib/i18n/locales/zh-CN/search.json +28 -0
  282. package/src/shared/lib/i18n/runtime/{i18n-language-owner.test.ts → __tests__/i18n-language-owner.test.ts} +2 -2
  283. package/src/shared/lib/navigation-history/__tests__/navigation-history.utils.test.ts +60 -0
  284. package/src/shared/lib/navigation-history/index.ts +1 -0
  285. package/src/shared/lib/navigation-history/navigation-history.utils.ts +105 -0
  286. package/src/shared/lib/provider-models/{index.test.ts → __tests__/index.test.ts} +1 -1
  287. package/src/shared/lib/transport/{app-client.test.ts → __tests__/app-client.test.ts} +3 -3
  288. package/src/shared/lib/transport/{remote.transport.test.ts → __tests__/remote.transport.test.ts} +1 -1
  289. package/src/shared/lib/transport/{sse-stream.test.ts → __tests__/sse-stream.test.ts} +1 -1
  290. package/tsconfig.json +2 -5
  291. package/vite.config.ts +4 -4
  292. package/vitest.config.ts +4 -0
  293. package/dist/assets/app-presenter-provider-CDSjiqfE.js +0 -3
  294. package/dist/assets/arrow-left-Dd45Mvr1.js +0 -1
  295. package/dist/assets/channels-list-page-D-WYFmcG.js +0 -8
  296. package/dist/assets/chat-page-D1X8YNTC.js +0 -105
  297. package/dist/assets/config-split-page-CdJEkWxv.js +0 -1
  298. package/dist/assets/desktop-update-config-DOi5R1fX.js +0 -1
  299. package/dist/assets/doc-browser-D5FJcAyz.js +0 -1
  300. package/dist/assets/doc-browser-DewXa6oX.js +0 -1
  301. package/dist/assets/doc-browser-context-CpL2m4Sp.js +0 -1
  302. package/dist/assets/index-Q891JiF_.css +0 -1
  303. package/dist/assets/index-ZDJP_uJc.js +0 -101
  304. package/dist/assets/loader-circle-Dz4FssYC.js +0 -1
  305. package/dist/assets/mcp-marketplace-page-LmMhKH5a.js +0 -1
  306. package/dist/assets/mcp-marketplace-page-RqISBSqO.js +0 -40
  307. package/dist/assets/model-config-JUsW1Khb.js +0 -1
  308. package/dist/assets/play-BziM-Y34.js +0 -1
  309. package/dist/assets/plus-DrpyArC8.js +0 -1
  310. package/dist/assets/provider-models-C_yOh6DE.js +0 -1
  311. package/dist/assets/provider-scoped-model-input-IMD4W0jD.js +0 -1
  312. package/dist/assets/providers-list-BsDpxNzy.js +0 -1
  313. package/dist/assets/remote-DjhVMpVd.js +0 -1
  314. package/dist/assets/rotate-cw-DMBnsYKm.js +0 -1
  315. package/dist/assets/runtime-config-page-DzbTVebD.js +0 -1
  316. package/dist/assets/search-CjHGYvTI.js +0 -1
  317. package/dist/assets/search-config-CJ6OPxAf.js +0 -1
  318. package/dist/assets/secrets-config-CYOHWL7P.js +0 -3
  319. package/dist/assets/tabs-Czd2mj6z.js +0 -1
  320. package/dist/assets/tabs-custom-Ix4nkp2h.js +0 -1
  321. package/dist/assets/x-dpDISboO.js +0 -1
  322. package/src/features/chat/components/chat-session-workspace-panel-nav.tsx +0 -232
  323. package/src/features/chat/components/chat-session-workspace-panel.tsx +0 -371
  324. package/src/features/chat/components/layout/chat-sidebar-read-state.test.tsx +0 -145
  325. package/src/features/chat/components/layout/chat-sidebar.test.tsx +0 -896
  326. package/src/features/chat/hooks/use-chat-session-label.ts +0 -18
  327. package/src/features/chat/hooks/use-hydrated-ncp-agent.test.tsx +0 -90
  328. package/src/features/chat/hooks/use-ncp-chat-derived-state.ts +0 -181
  329. package/src/features/chat/hooks/use-ncp-chat-page-data.test.tsx +0 -70
  330. package/src/features/chat/hooks/use-ncp-chat-page-data.ts +0 -260
  331. package/src/features/chat/managers/chat-stream-actions.manager.ts +0 -35
  332. package/src/features/chat/managers/ncp-chat-input.manager.test.ts +0 -232
  333. package/src/features/chat/managers/ncp-chat-presenter.manager.ts +0 -28
  334. package/src/features/chat/managers/ncp-chat-thread.manager.test.ts +0 -241
  335. package/src/features/chat/types/chat-stream.types.ts +0 -32
  336. package/src/features/chat/utils/chat-session-preference-governance.utils.test.tsx +0 -114
  337. package/src/features/marketplace/components/marketplace-catalog-grid.tsx +0 -114
  338. package/src/shared/components/resizable-right-panel/resizable-right-panel.test.tsx +0 -38
  339. package/src/shared/components/ui/action-link.tsx +0 -27
  340. package/src/shared/components/ui/config-card.tsx +0 -71
  341. package/src/shared/components/ui/highlight-card.tsx +0 -40
  342. package/src/shared/components/ui/tabs-custom.tsx +0 -49
  343. package/src/shared/lib/i18n/agents.ts +0 -85
  344. package/src/shared/lib/i18n/channel-auth.constants.ts +0 -87
  345. package/src/shared/lib/i18n/channels.ts +0 -53
  346. package/src/shared/lib/i18n/chat-labels.utils.ts +0 -206
  347. package/src/shared/lib/i18n/cron-labels.utils.ts +0 -32
  348. package/src/shared/lib/i18n/desktop-update-labels.utils.ts +0 -165
  349. package/src/shared/lib/i18n/marketplace-labels.utils.ts +0 -78
  350. package/src/shared/lib/i18n/pwa.ts +0 -62
  351. package/src/shared/lib/i18n/remote.ts +0 -273
  352. package/src/shared/lib/i18n/runtime/doc-browser-labels.utils.ts +0 -120
  353. package/src/shared/lib/i18n/runtime/i18n.path-picker.ts +0 -12
  354. package/src/shared/lib/i18n/runtime-control-labels.utils.ts +0 -180
  355. package/src/shared/lib/i18n/search.ts +0 -37
  356. /package/dist/assets/{config-hints-Ceiol9x4.js → config-hints-Cl2XAhzV.js} +0 -0
  357. /package/src/{app.test.tsx → __tests__/app.test.tsx} +0 -0
  358. /package/src/app/components/layout/{app-layout.test.tsx → __tests__/app-layout.test.tsx} +0 -0
  359. /package/src/app/components/layout/{runtime-status-entry.test.tsx → __tests__/runtime-status-entry.test.tsx} +0 -0
  360. /package/src/app/components/layout/{settings-entry-page.test.tsx → __tests__/settings-entry-page.test.tsx} +0 -0
  361. /package/src/app/components/layout/{sidebar.layout.test.tsx → __tests__/sidebar.layout.test.tsx} +0 -0
  362. /package/src/features/channels/components/config/{channel-form.test.tsx → __tests__/channel-form.test.tsx} +0 -0
  363. /package/src/features/channels/components/config/{weixin-channel-auth-section.test.tsx → __tests__/weixin-channel-auth-section.test.tsx} +0 -0
  364. /package/src/features/channels/pages/{channels-list-page.test.tsx → __tests__/channels-list-page.test.tsx} +0 -0
  365. /package/src/features/chat/{hooks → features/input/hooks}/use-chat-input-bar-controller.ts +0 -0
  366. /package/src/features/chat/{utils → features/input/utils}/chat-composer-state.utils.ts +0 -0
  367. /package/src/features/chat/{utils → features/input/utils}/chat-inline-token.utils.ts +0 -0
  368. /package/src/features/chat/{utils → features/input/utils}/chat-input-bar.utils.ts +0 -0
  369. /package/src/features/chat/{utils → features/input/utils}/chat-input-toolbar.utils.ts +0 -0
  370. /package/src/features/chat/{utils → features/message/utils}/chat-message-core.utils.ts +0 -0
  371. /package/src/features/chat/{utils → features/message/utils}/chat-message-partial-json.utils.ts +0 -0
  372. /package/src/features/chat/{utils → features/message/utils}/chat-message-tool-agent-id.utils.ts +0 -0
  373. /package/src/features/chat/{utils → features/message/utils}/file-operation/card.utils.ts +0 -0
  374. /package/src/features/chat/{utils → features/message/utils}/file-operation/diff.utils.ts +0 -0
  375. /package/src/features/chat/{utils → features/message/utils}/file-operation/line-builder.utils.ts +0 -0
  376. /package/src/features/chat/{utils → features/message/utils}/file-operation/parsed-block.utils.ts +0 -0
  377. /package/src/features/chat/{utils → features/message/utils}/file-operation/record-readers.utils.ts +0 -0
  378. /package/src/features/chat/{utils → features/runtime/utils}/ncp-app-client-fetch.utils.ts +0 -0
  379. /package/src/features/chat/{utils → features/runtime/utils}/ncp-chat-runtime-availability.utils.ts +0 -0
  380. /package/src/features/chat/{components/conversation → features/session/components}/session-header/chat-session-header-menu-item.tsx +0 -0
  381. /package/src/features/chat/{components/conversation → features/session/components}/session-header/chat-session-project-dialog.tsx +0 -0
  382. /package/src/features/chat/{components/session → features/session/components}/session-run-badge.tsx +0 -0
  383. /package/src/features/chat/{utils → features/session/utils}/chat-context-window-indicator.utils.ts +0 -0
  384. /package/src/features/chat/{utils → features/session/utils}/chat-session-display.utils.ts +0 -0
  385. /package/src/features/chat/{utils → features/session/utils}/chat-session-route.utils.ts +0 -0
  386. /package/src/features/chat/{utils → features/session/utils}/ncp-session-adapter.utils.ts +0 -0
  387. /package/src/features/chat/{utils → features/session/utils}/ncp-session-context-metadata.utils.ts +0 -0
  388. /package/src/features/chat/{utils → features/session/utils}/session-context.utils.ts +0 -0
  389. /package/src/features/chat/{hooks → features/session-type/hooks}/use-ncp-chat-session-types.ts +0 -0
  390. /package/src/features/chat/{components/workspace → features/workspace/components}/chat-session-workspace-file-breadcrumbs.tsx +0 -0
  391. /package/src/features/marketplace/components/{marketplace-page-detail.test.tsx → __tests__/marketplace-page-detail.test.tsx} +0 -0
  392. /package/src/features/marketplace/components/curated-shelves/{marketplace-curated-scene-route.test.tsx → __tests__/marketplace-curated-scene-route.test.tsx} +0 -0
  393. /package/src/features/marketplace/components/detail-doc/{marketplace-detail-doc.test.ts → __tests__/marketplace-detail-doc.test.ts} +0 -0
  394. /package/src/features/marketplace/components/mcp/{mcp-marketplace-page.test.tsx → __tests__/mcp-marketplace-page.test.tsx} +0 -0
  395. /package/src/features/panel-apps/managers/{panel-app-bridge.manager.test.ts → __tests__/panel-app-bridge.manager.test.ts} +0 -0
  396. /package/src/features/panel-apps/utils/{panel-app-doc-browser.utils.test.ts → __tests__/panel-app-doc-browser.utils.test.ts} +0 -0
  397. /package/src/features/pwa/components/{pwa-install-entry.test.tsx → __tests__/pwa-install-entry.test.tsx} +0 -0
  398. /package/src/features/pwa/managers/{pwa-install.manager.test.ts → __tests__/pwa-install.manager.test.ts} +0 -0
  399. /package/src/features/pwa/managers/{pwa-shell-theme.manager.test.ts → __tests__/pwa-shell-theme.manager.test.ts} +0 -0
  400. /package/src/features/remote/components/{remote-access-page.test.tsx → __tests__/remote-access-page.test.tsx} +0 -0
  401. /package/src/features/right-panel-resources/utils/{right-panel-resource-route-resolver.utils.test.ts → __tests__/right-panel-resource-route-resolver.utils.test.ts} +0 -0
  402. /package/src/features/right-panel-resources/utils/{right-panel-resource-uri.utils.test.ts → __tests__/right-panel-resource-uri.utils.test.ts} +0 -0
  403. /package/src/features/service-apps/components/{service-apps-panel.test.tsx → __tests__/service-apps-panel.test.tsx} +0 -0
  404. /package/src/{shared/components → features/settings/components/config}/provider-advanced-settings-section.tsx +0 -0
  405. /package/src/{shared/components → features/settings/components/config}/provider-auth-section.tsx +0 -0
  406. /package/src/{shared → features/settings}/components/config/provider-models-section.tsx +0 -0
  407. /package/src/{shared/components → features/settings/components/config}/provider-pill-selector.tsx +0 -0
  408. /package/src/{shared → features/settings}/components/config/secrets-config-form.tsx +0 -0
  409. /package/src/features/settings/pages/{language-settings-page.test.tsx → __tests__/language-settings-page.test.tsx} +0 -0
  410. /package/src/{shared/components/config/provider-form-support.ts → features/settings/utils/provider-form-support.utils.ts} +0 -0
  411. /package/src/features/side-dock/managers/{side-dock.manager.test.ts → __tests__/side-dock.manager.test.ts} +0 -0
  412. /package/src/features/side-dock/utils/{side-dock-item.utils.test.ts → __tests__/side-dock-item.utils.test.ts} +0 -0
  413. /package/src/features/system-status/components/{desktop-update-config.test.tsx → __tests__/desktop-update-config.test.tsx} +0 -0
  414. /package/src/features/system-status/pages/{runtime-config-page.test.tsx → __tests__/runtime-config-page.test.tsx} +0 -0
  415. /package/src/platforms/desktop/components/{desktop-app-shell.test.tsx → __tests__/desktop-app-shell.test.tsx} +0 -0
  416. /package/src/platforms/mobile/components/{mobile-app-shell.test.tsx → __tests__/mobile-app-shell.test.tsx} +0 -0
  417. /package/src/platforms/mobile/components/{mobile-bottom-nav.test.tsx → __tests__/mobile-bottom-nav.test.tsx} +0 -0
  418. /package/src/shared/components/common/{brand-header.test.tsx → __tests__/brand-header.test.tsx} +0 -0
  419. /package/src/shared/components/doc-browser/{doc-browser.test.tsx → __tests__/doc-browser.test.tsx} +0 -0
  420. /package/src/shared/components/{ui → feedback}/notice-card.tsx +0 -0
  421. /package/src/shared/components/path-picker/{server-path-picker-dialog.test.tsx → __tests__/server-path-picker-dialog.test.tsx} +0 -0
  422. /package/src/shared/components/{ui → settings}/setting-row.tsx +0 -0
  423. /package/src/shared/components/{ui → status}/status-dot.tsx +0 -0
  424. /package/src/shared/components/{ui → tags}/tag-chip.tsx +0 -0
  425. /package/src/shared/lib/api/{api-base.test.ts → __tests__/api-base.test.ts} +0 -0
  426. /package/src/shared/lib/api/{client.test.ts → __tests__/client.test.ts} +0 -0
  427. /package/src/shared/lib/api/{ncp-session-query-cache.utils.test.ts → __tests__/ncp-session-query-cache.utils.test.ts} +0 -0
  428. /package/src/shared/lib/app-resource-uri/{app-resource-uri.test.ts → __tests__/app-resource-uri.test.ts} +0 -0
  429. /package/src/shared/lib/resource-uri/utils/{resource-uri-resolver.utils.test.ts → __tests__/resource-uri-resolver.utils.test.ts} +0 -0
  430. /package/src/shared/lib/session-project/{workspace-file-breadcrumb.test.ts → __tests__/workspace-file-breadcrumb.test.ts} +0 -0
@@ -3,22 +3,34 @@ import { ChatInputBar, type ChatInputBarHandle } from '@nextclaw/agent-chat-ui';
3
3
  import { DEFAULT_NCP_ATTACHMENT_MAX_BYTES, uploadFilesAsNcpDraftAttachments } from '@nextclaw/ncp-react';
4
4
  import { uploadNcpAssets } from '@/shared/lib/api';
5
5
  import type { SessionSkillEntryView } from '@/shared/lib/api';
6
- import { buildChatSlashItems, buildModelStateHint, buildModelToolbarSelect, buildSkillPickerModel, buildThinkingToolbarSelect, type ChatModelRecord, type ChatSkillRecord, type ChatThinkingLevel } from '@/features/chat/utils/chat-input-bar.utils';
6
+ import { buildChatSlashItems, buildModelStateHint, buildModelToolbarSelect, buildSkillPickerModel, buildThinkingToolbarSelect, type ChatModelRecord, type ChatSkillRecord, type ChatThinkingLevel } from '@/features/chat/features/input/utils/chat-input-bar.utils';
7
7
  import { usePresenter } from '@/features/chat/components/providers/chat-presenter.provider';
8
8
  import { useI18n } from '@/app/components/i18n-provider';
9
9
  import { useViewportLayout } from '@/app/hooks/use-viewport-layout';
10
10
  import { useChatInputStore } from '@/features/chat/stores/chat-input.store';
11
+ import {
12
+ useNcpChatProviderStateResolved,
13
+ useNcpChatSelectedSession,
14
+ } from '@/features/chat/features/ncp/hooks/use-ncp-chat-derived-state';
15
+ import { useChatQueryStore } from '@/features/chat/stores/ncp-chat-query.store';
16
+ import { useChatSessionListStore } from '@/features/chat/stores/chat-session-list.store';
17
+ import {
18
+ buildNcpChatProviderModelOptions,
19
+ filterNcpChatModelOptionsBySessionType,
20
+ } from '@/features/chat/features/ncp/utils/ncp-chat-query-derived.utils';
21
+ import { useChatSessionTypeState } from '@/features/chat/features/session-type/hooks/use-chat-session-type-state';
11
22
  import { chatRecentModelsManager, CHAT_RECENT_MODELS_MIN_OPTIONS } from '@/features/chat/managers/chat-recent-models.manager';
12
23
  import { chatRecentSkillsManager, CHAT_RECENT_SKILLS_MIN_OPTIONS } from '@/features/chat/managers/chat-recent-skills.manager';
13
- import { deriveSelectedSkillsFromComposer } from '@/features/chat/utils/chat-composer-state.utils';
14
- import { hasNcpChatModelOptions, isNcpChatComposerDisabled, isNcpChatModelOptionsEmpty, isNcpChatModelOptionsLoading, isNcpChatSendDisabled } from '@/features/chat/utils/ncp-chat-input-availability.utils';
15
- import { useSelectedSessionContextWindowIndicator } from '@/features/chat/hooks/use-selected-session-context-window-indicator';
24
+ import { deriveSelectedSkillsFromComposer } from '@/features/chat/features/input/utils/chat-composer-state.utils';
25
+ import { hasNcpChatModelOptions, isNcpChatComposerDisabled, isNcpChatModelOptionsEmpty, isNcpChatModelOptionsLoading, isNcpChatSendDisabled } from '@/features/chat/features/input/utils/ncp-chat-input-availability.utils';
26
+ import { useSelectedSessionContextWindowIndicator } from '@/features/chat/features/session/hooks/use-selected-session-context-window-indicator';
16
27
  import { useSystemStatus } from '@/features/system-status';
17
- import { isNcpChatRuntimeBlocked } from '@/features/chat/utils/ncp-chat-runtime-availability.utils';
28
+ import { isNcpChatRuntimeBlocked } from '@/features/chat/features/runtime/utils/ncp-chat-runtime-availability.utils';
18
29
  import { t } from '@/shared/lib/i18n';
19
30
  import { toast } from 'sonner';
20
31
 
21
- type ChatInputStoreSnapshot = ReturnType<typeof useChatInputStore.getState>['snapshot']; type ChatPresenter = ReturnType<typeof usePresenter>;
32
+ type ChatInputStoreSnapshot = ReturnType<typeof useChatInputStore.getState>['snapshot'];
33
+ const EMPTY_SESSION_SKILL_RECORDS: SessionSkillEntryView[] = [];
22
34
 
23
35
  function buildThinkingLabels(): Record<ChatThinkingLevel, string> {
24
36
  return {
@@ -92,12 +104,16 @@ function useChatInputBarLabels(language: string) {
92
104
  allSkillsLabel: t('chatPickerAllSkills')
93
105
  };
94
106
  }
95
- function useChatInputBarCollections(snapshot: ChatInputStoreSnapshot, skillScopeLabels: Record<'builtin' | 'project' | 'workspace', string>) {
107
+ function useChatInputBarCollections(params: {
108
+ modelOptions: ChatInputStoreSnapshot['modelOptions'];
109
+ skillRecords: SessionSkillEntryView[];
110
+ skillScopeLabels: Record<'builtin' | 'project' | 'workspace', string>;
111
+ }) {
96
112
  const skillRecords = useMemo(
97
- () => toSkillRecords(snapshot.skillRecords, skillScopeLabels),
98
- [snapshot.skillRecords, skillScopeLabels]
113
+ () => toSkillRecords(params.skillRecords, params.skillScopeLabels),
114
+ [params.skillRecords, params.skillScopeLabels]
99
115
  );
100
- const modelRecords = useMemo(() => toModelRecords(snapshot.modelOptions), [snapshot.modelOptions]);
116
+ const modelRecords = useMemo(() => toModelRecords(params.modelOptions), [params.modelOptions]);
101
117
  return {
102
118
  skillRecords,
103
119
  modelRecords,
@@ -115,8 +131,9 @@ function useChatInputBarCollections(snapshot: ChatInputStoreSnapshot, skillScope
115
131
  })
116
132
  };
117
133
  }
118
- function useChatInputBarAttachments(params: { attachmentSupported: boolean; inputBarRef: RefObject<ChatInputBarHandle | null>; presenter: ChatPresenter; }) {
119
- const { attachmentSupported, inputBarRef, presenter } = params;
134
+ function useChatInputBarAttachments(params: { attachmentSupported: boolean; inputBarRef: RefObject<ChatInputBarHandle | null>; }) {
135
+ const { attachmentSupported, inputBarRef } = params;
136
+ const presenter = usePresenter();
120
137
  const showAttachmentError = useCallback((reason: 'unsupported-type' | 'too-large' | 'read-failed') => {
121
138
  if (reason === 'unsupported-type') {
122
139
  toast.error(t('chatInputAttachmentUnsupported'));
@@ -156,8 +173,8 @@ function useChatInputBarAttachments(params: { attachmentSupported: boolean; inpu
156
173
  }, [handleFilesAdd])
157
174
  };
158
175
  }
159
- function buildToolbarSelects(params: { allModelsLabel: string; hasModelOptions: boolean; isModelOptionsLoading: boolean; modelRecords: ChatModelRecord[]; presenter: ChatPresenter; recentModelValues: string[]; recentModelsLabel: string; selectedModel: string; selectedThinkingLevel: ChatThinkingLevel | null; thinkingSupportedLevels: ChatThinkingLevel[]; thinkingDefaultLevel: ChatThinkingLevel | null; }) {
160
- const { allModelsLabel, hasModelOptions, isModelOptionsLoading, modelRecords, presenter, recentModelValues, recentModelsLabel, selectedModel, selectedThinkingLevel, thinkingSupportedLevels, thinkingDefaultLevel } = params;
176
+ function buildToolbarSelects(params: { allModelsLabel: string; hasModelOptions: boolean; isModelOptionsLoading: boolean; modelRecords: ChatModelRecord[]; onModelChange: (value: string) => void; onThinkingChange: (value: ChatThinkingLevel | null) => void; recentModelValues: string[]; recentModelsLabel: string; selectedModel: string; selectedThinkingLevel: ChatThinkingLevel | null; thinkingSupportedLevels: ChatThinkingLevel[]; thinkingDefaultLevel: ChatThinkingLevel | null; }) {
177
+ const { allModelsLabel, hasModelOptions, isModelOptionsLoading, modelRecords, onModelChange, onThinkingChange, recentModelValues, recentModelsLabel, selectedModel, selectedThinkingLevel, thinkingSupportedLevels, thinkingDefaultLevel } = params;
161
178
  return [
162
179
  buildModelToolbarSelect({
163
180
  modelOptions: modelRecords,
@@ -165,7 +182,7 @@ function buildToolbarSelects(params: { allModelsLabel: string; hasModelOptions:
165
182
  selectedModel,
166
183
  isModelOptionsLoading,
167
184
  hasModelOptions,
168
- onValueChange: presenter.chatInputManager.selectModel,
185
+ onValueChange: onModelChange,
169
186
  texts: {
170
187
  modelSelectPlaceholder: t('chatSelectModel'),
171
188
  modelNoOptionsLabel: t('chatModelNoOptions'),
@@ -177,22 +194,22 @@ function buildToolbarSelects(params: { allModelsLabel: string; hasModelOptions:
177
194
  supportedLevels: thinkingSupportedLevels,
178
195
  selectedThinkingLevel,
179
196
  defaultThinkingLevel: thinkingDefaultLevel,
180
- onValueChange: (value) => presenter.chatInputManager.selectThinkingLevel(value),
197
+ onValueChange: onThinkingChange,
181
198
  texts: {
182
199
  thinkingLabels: buildThinkingLabels()
183
200
  }
184
201
  })
185
202
  ].filter((item): item is NonNullable<typeof item> => item !== null);
186
203
  }
187
- function buildSkillPicker(params: { allSkillsLabel: string; presenter: ChatPresenter; recentSkillGroupValues: string[]; recentSkillValues: string[]; recentSkillsLabel: string; skillRecords: ChatSkillRecord[]; snapshot: ChatInputStoreSnapshot; }) {
188
- const { allSkillsLabel, presenter, recentSkillGroupValues, recentSkillValues, recentSkillsLabel, skillRecords, snapshot } = params;
204
+ function buildSkillPicker(params: { allSkillsLabel: string; isSkillsLoading: boolean; onSelectedKeysChange: (keys: string[]) => void; recentSkillGroupValues: string[]; recentSkillValues: string[]; recentSkillsLabel: string; skillRecords: ChatSkillRecord[]; snapshot: ChatInputStoreSnapshot; }) {
205
+ const { allSkillsLabel, isSkillsLoading, onSelectedKeysChange, recentSkillGroupValues, recentSkillValues, recentSkillsLabel, skillRecords, snapshot } = params;
189
206
  return buildSkillPickerModel({
190
207
  skillRecords,
191
208
  recentSkillValues,
192
209
  groupedRecentSkillValues: recentSkillGroupValues,
193
210
  selectedSkills: snapshot.selectedSkills,
194
- isLoading: snapshot.isSkillsLoading,
195
- onSelectedKeysChange: presenter.chatInputManager.selectSkills,
211
+ isLoading: isSkillsLoading,
212
+ onSelectedKeysChange,
196
213
  texts: {
197
214
  title: t('chatSkillsPickerTitle'),
198
215
  searchPlaceholder: t('chatSkillsPickerSearchPlaceholder'),
@@ -204,21 +221,100 @@ function buildSkillPicker(params: { allSkillsLabel: string; presenter: ChatPrese
204
221
  }
205
222
  });
206
223
  }
224
+
225
+ function useChatInputBarQueryState(snapshot: ChatInputStoreSnapshot) {
226
+ const presenter = usePresenter();
227
+ const selectedSessionKey = useChatSessionListStore(
228
+ (state) => state.snapshot.selectedSessionKey,
229
+ );
230
+ const selectedSession = useNcpChatSelectedSession(selectedSessionKey);
231
+ const isProviderStateResolved = useNcpChatProviderStateResolved();
232
+ const config = useChatQueryStore(
233
+ (state) => state.snapshot.configQuery?.data ?? null,
234
+ );
235
+ const providersView = useChatQueryStore(
236
+ (state) => state.snapshot.providersQuery?.data ?? null,
237
+ );
238
+ const templatesView = useChatQueryStore(
239
+ (state) => state.snapshot.providerTemplatesQuery?.data ?? null,
240
+ );
241
+ const sessionTypesData = useChatQueryStore(
242
+ (state) => state.snapshot.sessionTypesQuery?.data ?? null,
243
+ );
244
+ const skillRecords = useChatQueryStore(
245
+ (state) =>
246
+ state.snapshot.sessionSkillsQuery?.data?.records ?? EMPTY_SESSION_SKILL_RECORDS,
247
+ );
248
+ const isSkillsLoading = useChatQueryStore(
249
+ (state) =>
250
+ Boolean(
251
+ state.snapshot.sessionSkillsQuery?.isLoading ||
252
+ state.snapshot.sessionSkillsQuery?.isFetching,
253
+ ),
254
+ );
255
+ const sessionTypeState = useChatSessionTypeState({
256
+ selectedSession,
257
+ pendingSessionType: snapshot.pendingSessionType,
258
+ setPendingSessionType: presenter.chatInputManager.setPendingSessionType,
259
+ sessionTypesData,
260
+ });
261
+ const providerModelOptions = useMemo(
262
+ () =>
263
+ buildNcpChatProviderModelOptions({
264
+ config,
265
+ providersView,
266
+ templatesView,
267
+ }),
268
+ [config, providersView, templatesView],
269
+ );
270
+ const modelOptions = useMemo(
271
+ () =>
272
+ filterNcpChatModelOptionsBySessionType({
273
+ modelOptions: providerModelOptions,
274
+ modelSelectionMode: sessionTypeState.selectedSessionTypeOption?.modelSelectionMode,
275
+ runtimeDefaultModelLabel: t('chatRuntimeDefaultModel'),
276
+ supportedModels: sessionTypeState.selectedSessionTypeOption?.supportedModels,
277
+ }),
278
+ [
279
+ providerModelOptions,
280
+ sessionTypeState.selectedSessionTypeOption?.modelSelectionMode,
281
+ sessionTypeState.selectedSessionTypeOption?.supportedModels,
282
+ ],
283
+ );
284
+
285
+ return {
286
+ isProviderStateResolved,
287
+ isSkillsLoading,
288
+ modelOptions,
289
+ skillRecords,
290
+ };
291
+ }
292
+
207
293
  export function ChatInputBarContainer() {
208
294
  const presenter = usePresenter();
209
295
  const { language } = useI18n();
210
296
  const { isMobile } = useViewportLayout();
211
297
  const snapshot = useChatInputStore((state) => state.snapshot);
298
+ const inputQueryState = useChatInputBarQueryState(snapshot);
212
299
  const isRuntimeBlocked = isNcpChatRuntimeBlocked(useSystemStatus());
213
300
  const [slashQuery, setSlashQuery] = useState<string | null>(null);
214
301
  const inputBarRef = useRef<ChatInputBarHandle | null>(null);
215
302
  const fileInputRef = useRef<HTMLInputElement | null>(null);
216
303
  const labels = useChatInputBarLabels(language);
217
- const { skillRecords, modelRecords, recentModelValues, recentSkillValues, recentSkillGroupValues } = useChatInputBarCollections(snapshot, labels.skillScopeLabels);
218
- const hasModelOptions = hasNcpChatModelOptions(snapshot);
219
- const isModelOptionsLoading = isNcpChatModelOptionsLoading(snapshot);
220
- const isModelOptionsEmpty = isNcpChatModelOptionsEmpty(snapshot);
221
- const inputDisabled = isNcpChatComposerDisabled(snapshot);
304
+ const { skillRecords, modelRecords, recentModelValues, recentSkillValues, recentSkillGroupValues } = useChatInputBarCollections({
305
+ modelOptions: inputQueryState.modelOptions,
306
+ skillRecords: inputQueryState.skillRecords,
307
+ skillScopeLabels: labels.skillScopeLabels
308
+ });
309
+ const availabilitySnapshot = {
310
+ ...snapshot,
311
+ isProviderStateResolved: inputQueryState.isProviderStateResolved,
312
+ modelOptions: inputQueryState.modelOptions
313
+ };
314
+ const hasModelOptions = hasNcpChatModelOptions(availabilitySnapshot);
315
+ const isModelOptionsLoading = isNcpChatModelOptionsLoading(availabilitySnapshot);
316
+ const isModelOptionsEmpty = isNcpChatModelOptionsEmpty(availabilitySnapshot);
317
+ const inputDisabled = isNcpChatComposerDisabled(availabilitySnapshot);
222
318
  const attachmentSupported = typeof presenter.chatInputManager.addAttachments === 'function';
223
319
  const textareaPlaceholder = isModelOptionsEmpty
224
320
  ? t('chatModelNoOptions')
@@ -233,7 +329,7 @@ export function ChatInputBarContainer() {
233
329
  const resolvedStopHint = snapshot.stopDisabledReason === '__preparing__'
234
330
  ? t('chatStopPreparing')
235
331
  : snapshot.stopDisabledReason?.trim() || t('chatStopUnavailable');
236
- const { handleFilesAdd, handleFileInputChange } = useChatInputBarAttachments({ attachmentSupported, inputBarRef, presenter });
332
+ const { handleFilesAdd, handleFileInputChange } = useChatInputBarAttachments({ attachmentSupported, inputBarRef });
237
333
  useEffect(() => {
238
334
  const request = snapshot.composerFocusRequest;
239
335
  if (!request) {
@@ -249,7 +345,12 @@ export function ChatInputBarContainer() {
249
345
  hasModelOptions,
250
346
  isModelOptionsLoading,
251
347
  modelRecords,
252
- presenter,
348
+ onModelChange: presenter.chatInputManager.selectModel,
349
+ onThinkingChange: (value) => {
350
+ if (value) {
351
+ presenter.chatInputManager.selectThinkingLevel(value);
352
+ }
353
+ },
253
354
  recentModelValues,
254
355
  recentModelsLabel: labels.recentModelsLabel,
255
356
  selectedModel: snapshot.selectedModel,
@@ -259,10 +360,11 @@ export function ChatInputBarContainer() {
259
360
  });
260
361
  const skillPicker = buildSkillPicker({
261
362
  allSkillsLabel: labels.allSkillsLabel,
262
- presenter,
363
+ onSelectedKeysChange: presenter.chatInputManager.selectSkills,
263
364
  recentSkillGroupValues,
264
365
  recentSkillValues,
265
366
  recentSkillsLabel: labels.recentSkillsLabel,
367
+ isSkillsLoading: inputQueryState.isSkillsLoading,
266
368
  skillRecords,
267
369
  snapshot
268
370
  });
@@ -281,7 +383,7 @@ export function ChatInputBarContainer() {
281
383
  onSlashQueryChange: setSlashQuery
282
384
  }}
283
385
  slashMenu={{
284
- isLoading: snapshot.isSkillsLoading,
386
+ isLoading: inputQueryState.isSkillsLoading,
285
387
  items: slashItems,
286
388
  onSelectItem: (item: { value?: string }) => {
287
389
  if (item.value) {
@@ -299,7 +401,7 @@ export function ChatInputBarContainer() {
299
401
  hint={buildModelStateHint({
300
402
  isModelOptionsLoading,
301
403
  isModelOptionsEmpty,
302
- onGoToProviders: presenter.chatInputManager.goToProviders,
404
+ onGoToProviders: presenter.chatUiManager.goToProviders,
303
405
  texts: {
304
406
  noModelOptionsLabel: t('chatModelNoOptions'),
305
407
  configureProviderLabel: t('chatGoConfigureProvider')
@@ -329,7 +431,7 @@ export function ChatInputBarContainer() {
329
431
  isSending: snapshot.isSending,
330
432
  canStopGeneration: snapshot.canStopGeneration,
331
433
  sendDisabled: isNcpChatSendDisabled({
332
- snapshot,
434
+ snapshot: availabilitySnapshot,
333
435
  hasSendableDraft,
334
436
  isRuntimeBlocked
335
437
  }),
@@ -1,6 +1,6 @@
1
1
  import type { KeyboardEvent as ReactKeyboardEvent } from 'react';
2
2
  import { renderHook, act } from '@testing-library/react';
3
- import { useChatInputBarController } from '@/features/chat/hooks/use-chat-input-bar-controller';
3
+ import { useChatInputBarController } from '@/features/chat/features/input/hooks/use-chat-input-bar-controller';
4
4
 
5
5
  function createKeyEvent(
6
6
  key: string,
@@ -1,5 +1,5 @@
1
1
  import { createChatComposerTextNode, createChatComposerTokenNode } from '@nextclaw/agent-chat-ui';
2
- import { deriveNcpMessagePartsFromComposer } from './chat-composer-state.utils';
2
+ import { deriveNcpMessagePartsFromComposer } from '../chat-composer-state.utils';
3
3
 
4
4
  describe('deriveNcpMessagePartsFromComposer', () => {
5
5
  it('preserves interleaved text and image token order while serializing skill tokens inline', () => {
@@ -4,7 +4,7 @@ import {
4
4
  CHAT_UI_INLINE_TOKENS_METADATA_KEY,
5
5
  readInlineTokensFromMetadata,
6
6
  splitTextByInlineTokens
7
- } from './chat-inline-token.utils';
7
+ } from '../chat-inline-token.utils';
8
8
 
9
9
  describe('chat-inline-token utils', () => {
10
10
  it('builds ordered inline skill tokens from composer nodes', () => {
@@ -3,8 +3,8 @@ import {
3
3
  buildModelToolbarSelect,
4
4
  buildSelectedSkillItems,
5
5
  buildSkillPickerModel
6
- } from './chat-input-bar.utils';
7
- import type { ChatSkillRecord } from './chat-input-bar.utils';
6
+ } from '../chat-input-bar.utils';
7
+ import type { ChatSkillRecord } from '../chat-input-bar.utils';
8
8
 
9
9
  function createSkillRecord(partial: Partial<ChatSkillRecord>): ChatSkillRecord {
10
10
  return {
@@ -5,7 +5,7 @@ import {
5
5
  isNcpChatModelOptionsEmpty,
6
6
  isNcpChatModelOptionsLoading,
7
7
  isNcpChatSendDisabled,
8
- } from '@/features/chat/utils/ncp-chat-input-availability.utils';
8
+ } from '@/features/chat/features/input/utils/ncp-chat-input-availability.utils';
9
9
  import type { ChatInputSnapshot } from '@/features/chat/stores/chat-input.store';
10
10
 
11
11
  function createSnapshot(
@@ -33,6 +33,7 @@ function createSnapshot(
33
33
  stopReason: undefined,
34
34
  canEditSessionType: true,
35
35
  sessionTypeUnavailable: false,
36
+ sessionTypeUnavailableMessage: null,
36
37
  skillRecords: [],
37
38
  isSkillsLoading: false,
38
39
  selectedSkills: [],
@@ -1,37 +1,37 @@
1
1
  import type { ChatInputSnapshot } from '@/features/chat/stores/chat-input.store';
2
2
 
3
- type NcpChatInputAvailabilitySnapshot = Pick<
3
+ type ChatInputAvailabilitySnapshot = Pick<
4
4
  ChatInputSnapshot,
5
5
  'isProviderStateResolved' | 'modelOptions' | 'sessionTypeUnavailable'
6
6
  >;
7
7
 
8
8
  export function hasNcpChatModelOptions(
9
- snapshot: NcpChatInputAvailabilitySnapshot
9
+ snapshot: ChatInputAvailabilitySnapshot
10
10
  ): boolean {
11
11
  return snapshot.modelOptions.length > 0;
12
12
  }
13
13
 
14
14
  export function isNcpChatModelOptionsLoading(
15
- snapshot: NcpChatInputAvailabilitySnapshot
15
+ snapshot: ChatInputAvailabilitySnapshot
16
16
  ): boolean {
17
17
  return !snapshot.isProviderStateResolved && !hasNcpChatModelOptions(snapshot);
18
18
  }
19
19
 
20
20
  export function isNcpChatModelOptionsEmpty(
21
- snapshot: NcpChatInputAvailabilitySnapshot
21
+ snapshot: ChatInputAvailabilitySnapshot
22
22
  ): boolean {
23
23
  return snapshot.isProviderStateResolved && !hasNcpChatModelOptions(snapshot);
24
24
  }
25
25
 
26
26
  export function isNcpChatComposerDisabled(
27
- _snapshot: NcpChatInputAvailabilitySnapshot
27
+ _snapshot: ChatInputAvailabilitySnapshot
28
28
  ): boolean {
29
29
  return false;
30
30
  }
31
31
 
32
32
  export function isNcpChatSendDisabled(params: {
33
33
  hasSendableDraft: boolean;
34
- snapshot: NcpChatInputAvailabilitySnapshot;
34
+ snapshot: ChatInputAvailabilitySnapshot;
35
35
  isRuntimeBlocked: boolean;
36
36
  }): boolean {
37
37
  const { hasSendableDraft, isRuntimeBlocked } = params;
@@ -1,24 +1,43 @@
1
1
  import { render } from "@testing-library/react";
2
2
  import type { NcpMessage } from "@nextclaw/ncp";
3
3
  import { beforeEach, expect, it, vi } from "vitest";
4
- import { ChatMessageListContainer } from "./chat-message-list.container";
4
+ import { ChatMessageListContainer } from "../chat-message-list.container";
5
5
 
6
6
  const captures = vi.hoisted(() => ({
7
- renders: [] as Array<{ messages: unknown[]; texts?: Record<string, unknown> }>,
7
+ renders: [] as Array<{
8
+ messages: unknown[];
9
+ onToolAction?: (action: unknown) => void;
10
+ texts?: Record<string, unknown>;
11
+ }>,
8
12
  language: "en",
13
+ openFilePreview: vi.fn(),
14
+ handleToolAction: vi.fn(),
9
15
  }));
10
16
 
11
17
  vi.mock("@nextclaw/agent-chat-ui", async (importOriginal) => {
12
18
  const actual = await importOriginal();
13
19
  return {
14
20
  ...(actual as object),
15
- ChatMessageList: (props: { messages: unknown[]; texts?: Record<string, unknown> }) => {
21
+ ChatMessageList: (props: {
22
+ messages: unknown[];
23
+ onToolAction?: (action: unknown) => void;
24
+ texts?: Record<string, unknown>;
25
+ }) => {
16
26
  captures.renders.push(props);
17
27
  return <div data-testid="chat-message-list" />;
18
28
  },
19
29
  };
20
30
  });
21
31
 
32
+ vi.mock("@/features/chat/components/providers/chat-presenter.provider", () => ({
33
+ usePresenter: () => ({
34
+ chatThreadManager: {
35
+ openFilePreview: captures.openFilePreview,
36
+ handleToolAction: captures.handleToolAction,
37
+ },
38
+ }),
39
+ }));
40
+
22
41
  vi.mock("@/app/components/i18n-provider", () => ({
23
42
  useI18n: () => ({ language: captures.language }),
24
43
  }));
@@ -31,6 +50,8 @@ vi.mock("@/shared/lib/i18n", () => ({
31
50
  beforeEach(() => {
32
51
  captures.renders = [];
33
52
  captures.language = "en";
53
+ captures.openFilePreview.mockReset();
54
+ captures.handleToolAction.mockReset();
34
55
  });
35
56
 
36
57
  it("reuses adapted message references when the source message object is unchanged", () => {
@@ -216,6 +237,20 @@ it("passes localized attachment card texts to the shared chat UI", () => {
216
237
  });
217
238
  });
218
239
 
240
+ it("delegates tool actions to the chat thread manager owner", () => {
241
+ const toolAction = {
242
+ kind: "open-session",
243
+ sessionId: "child-session-1",
244
+ sessionKind: "child",
245
+ };
246
+
247
+ render(<ChatMessageListContainer messages={[]} isSending={false} />);
248
+
249
+ captures.renders[captures.renders.length - 1]?.onToolAction?.(toolAction);
250
+
251
+ expect(captures.handleToolAction).toHaveBeenCalledWith(toolAction);
252
+ });
253
+
219
254
  it("renders context compaction as an in-flow divider instead of a chat message", () => {
220
255
  const beforeMessage = {
221
256
  id: "message-before",
@@ -1,22 +1,21 @@
1
1
  import { useMemo } from "react";
2
2
  import type { NcpMessage } from "@nextclaw/ncp";
3
3
  import {
4
- type ChatFileOpenActionViewModel,
5
- type ChatToolActionViewModel,
6
4
  type ChatMessageViewModel,
7
5
  ChatMessageList,
8
6
  } from "@nextclaw/agent-chat-ui";
7
+ import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
9
8
  import {
10
9
  adaptChatMessage,
11
10
  type ChatMessageAdapterTexts,
12
11
  type ChatMessageSource,
13
- } from "@/features/chat/utils/chat-message.utils";
14
- import { readInlineTokensFromMetadata } from "@/features/chat/utils/chat-inline-token.utils";
15
- import { adaptNcpMessageToUiMessage } from "@/features/chat/utils/ncp-session-adapter.utils";
12
+ } from "@/features/chat/features/message/utils/chat-message.utils";
13
+ import { readInlineTokensFromMetadata } from "@/features/chat/features/input/utils/chat-inline-token.utils";
14
+ import { adaptNcpMessageToUiMessage } from "@/features/chat/features/session/utils/ncp-session-adapter.utils";
16
15
  import {
17
16
  readContextCompactionTimeline,
18
17
  type ContextCompactionTimelineView,
19
- } from "@/features/chat/utils/ncp-session-context-metadata.utils";
18
+ } from "@/features/chat/features/session/utils/ncp-session-context-metadata.utils";
20
19
  import { AgentIdentityAvatar } from "@/shared/components/common/agent-identity";
21
20
  import { useI18n } from "@/app/components/i18n-provider";
22
21
  import { formatDateTime, t } from "@/shared/lib/i18n";
@@ -25,8 +24,6 @@ type ChatMessageListContainerProps = {
25
24
  messages: readonly NcpMessage[];
26
25
  isSending: boolean;
27
26
  className?: string;
28
- onToolAction?: (action: ChatToolActionViewModel) => void;
29
- onFileOpen?: (action: ChatFileOpenActionViewModel) => void;
30
27
  };
31
28
 
32
29
  const messageViewModelCache = new WeakMap<
@@ -69,6 +66,7 @@ function buildChatMessageAdapterTexts(
69
66
  toolStatusCompletedLabel: t("chatToolStatusCompleted"),
70
67
  toolStatusFailedLabel: t("chatToolStatusFailed"),
71
68
  toolStatusCancelledLabel: t("chatToolStatusCancelled"),
69
+ showContentActionLabel: t("chatShowContentAction"),
72
70
  imageAttachmentLabel: t("chatImageAttachment"),
73
71
  fileAttachmentLabel: t("chatFileAttachment"),
74
72
  unknownPartLabel: t("chatUnknownPart"),
@@ -235,9 +233,8 @@ export function ChatMessageListContainer({
235
233
  messages: rawMessages,
236
234
  isSending,
237
235
  className,
238
- onToolAction,
239
- onFileOpen,
240
236
  }: ChatMessageListContainerProps) {
237
+ const presenter = usePresenter();
241
238
  const { language } = useI18n();
242
239
  const texts = useMemo<ChatMessageAdapterTexts>(
243
240
  () => buildChatMessageAdapterTexts(language),
@@ -292,7 +289,6 @@ export function ChatMessageListContainer({
292
289
  () => buildTimelineItems({ rawMessages, messages }),
293
290
  [messages, rawMessages],
294
291
  );
295
-
296
292
  return (
297
293
  <div className={className}>
298
294
  {timelineItems.map((item, index) =>
@@ -305,8 +301,8 @@ export function ChatMessageListContainer({
305
301
  isSending={index === timelineItems.length - 1 ? isSending : false}
306
302
  hasAssistantDraft={hasAssistantDraft}
307
303
  texts={messageTexts}
308
- onToolAction={onToolAction}
309
- onFileOpen={onFileOpen}
304
+ onToolAction={presenter.chatThreadManager.handleToolAction}
305
+ onFileOpen={presenter.chatThreadManager.openFilePreview}
310
306
  renderToolAgent={(agentId) => (
311
307
  <AgentIdentityAvatar
312
308
  agentId={agentId}
@@ -1,6 +1,6 @@
1
1
  import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages } from "./chat-message.utils";
3
- import type { ChatMessageSource } from "./chat-message.utils";
2
+ import { adaptChatMessages } from "../chat-message.utils";
3
+ import type { ChatMessageSource } from "../chat-message.utils";
4
4
 
5
5
  const defaultTexts = {
6
6
  roleLabels: {
@@ -1,6 +1,6 @@
1
1
  import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages } from "./chat-message.utils";
3
- import type { ChatMessageSource } from "./chat-message.utils";
2
+ import { adaptChatMessages } from "../chat-message.utils";
3
+ import type { ChatMessageSource } from "../chat-message.utils";
4
4
 
5
5
  const defaultTexts = {
6
6
  roleLabels: {
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildShowContentToolCard } from "../chat-message-show-content-tool-card.utils";
3
+
4
+ describe("buildShowContentToolCard", () => {
5
+ it("builds a show-content action from a normalized show_content result", () => {
6
+ const card = buildShowContentToolCard({
7
+ invocation: {
8
+ status: "result",
9
+ toolCallId: "call-show-content",
10
+ toolName: "show_content",
11
+ result: {
12
+ ok: true,
13
+ action: "showContent",
14
+ request: {
15
+ target: {
16
+ type: "file",
17
+ payload: {
18
+ path: "docs/example.md",
19
+ line: 2,
20
+ },
21
+ },
22
+ title: "Example",
23
+ purpose: "read",
24
+ },
25
+ },
26
+ },
27
+ actionLabel: "Show content",
28
+ statusLabel: "Completed",
29
+ });
30
+
31
+ expect(card).toMatchObject({
32
+ kind: "result",
33
+ name: "show_content",
34
+ detail: "Example",
35
+ statusTone: "success",
36
+ statusLabel: "Completed",
37
+ action: {
38
+ kind: "show-content",
39
+ label: "Show content",
40
+ request: {
41
+ target: {
42
+ type: "file",
43
+ payload: {
44
+ path: "docs/example.md",
45
+ line: 2,
46
+ },
47
+ },
48
+ title: "Example",
49
+ purpose: "read",
50
+ },
51
+ },
52
+ });
53
+ });
54
+ });
@@ -1,6 +1,6 @@
1
1
  import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages } from "./chat-message.utils";
3
- import type { ChatMessageSource } from "./chat-message.utils";
2
+ import { adaptChatMessages } from "../chat-message.utils";
3
+ import type { ChatMessageSource } from "../chat-message.utils";
4
4
  import type { ChatMessagePartViewModel } from "@nextclaw/agent-chat-ui";
5
5
 
6
6
  const defaultTexts = {
@@ -1,5 +1,5 @@
1
1
  import { ToolInvocationStatus } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages, type ChatMessageSource } from "./chat-message.utils";
2
+ import { adaptChatMessages, type ChatMessageSource } from "../chat-message.utils";
3
3
 
4
4
  const defaultTexts = {
5
5
  roleLabels: {
@@ -1,6 +1,6 @@
1
1
  import { ToolInvocationStatus, type UiMessage } from "@nextclaw/agent-chat";
2
- import { adaptChatMessages } from "./chat-message.utils";
3
- import type { ChatMessageSource } from "./chat-message.utils";
2
+ import { adaptChatMessages } from "../chat-message.utils";
3
+ import type { ChatMessageSource } from "../chat-message.utils";
4
4
 
5
5
  function toSource(uiMessages: UiMessage[]): ChatMessageSource[] {
6
6
  return uiMessages as unknown as ChatMessageSource[];
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  splitTextByInlineTokens,
3
3
  type ChatInlineTokenSource,
4
- } from "@/features/chat/utils/chat-inline-token.utils";
4
+ } from "@/features/chat/features/input/utils/chat-inline-token.utils";
5
5
  import type {
6
6
  ChatInlineContentSegmentViewModel,
7
7
  ChatMessagePartViewModel,