@nextclaw/ui 0.15.22 → 0.15.24

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 (428) hide show
  1. package/CHANGELOG.md +66 -0
  2. package/dist/assets/{abnfDiagram-VRR7QNED-M4g_AyR8.js → abnfDiagram-VRR7QNED-DvTjgVMf.js} +1 -1
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-C1nfBAXR.js} +1 -1
  4. package/dist/assets/api-BxPNDp30.js +13 -0
  5. package/dist/assets/appearance-settings-page-DpVJjvkB.js +1 -0
  6. package/dist/assets/{arc-IO7WNWOB.js → arc-BvhODBXv.js} +1 -1
  7. package/dist/assets/architecture-TIHT7OUA-Bjp9jf0h.js +1 -0
  8. package/dist/assets/{architectureDiagram-ZJ3FMSHR-Dc-lvdUh.js → architectureDiagram-ZJ3FMSHR-BKO9RgNS.js} +1 -1
  9. package/dist/assets/{blockDiagram-677ZJIJ3-CcsoWpIY.js → blockDiagram-677ZJIJ3-DHpDXy7D.js} +1 -1
  10. package/dist/assets/{c4Diagram-LMCZKHZV-Bsrtg6b2.js → c4Diagram-LMCZKHZV-BG_iQnDr.js} +1 -1
  11. package/dist/assets/channel-5T3pdR-s.js +1 -0
  12. package/dist/assets/channels-list-page-DRaNWAxC.js +8 -0
  13. package/dist/assets/chat-page-BuFDmG5_.js +1 -0
  14. package/dist/assets/chunk-2Q5K7J3B-DTZ3tGfE.js +1 -0
  15. package/dist/assets/{chunk-32BRIVSS-Bt08t8oG.js → chunk-32BRIVSS-DR9M0LLx.js} +1 -1
  16. package/dist/assets/{chunk-52WLFC77-Ddx68Xzx.js → chunk-52WLFC77-698iC3BB.js} +1 -1
  17. package/dist/assets/{chunk-5FCAYU7R-B7p9_q55.js → chunk-5FCAYU7R-DikTj9Ey.js} +1 -1
  18. package/dist/assets/{chunk-5HE753X5-D52KvdTm.js → chunk-5HE753X5-ko0o7_q0.js} +1 -1
  19. package/dist/assets/{chunk-5JV3BV7I-C329h8nI.js → chunk-5JV3BV7I-CEHufhgo.js} +1 -1
  20. package/dist/assets/{chunk-5TONJI2A-BZyUBxWt.js → chunk-5TONJI2A-CGgno1cu.js} +1 -1
  21. package/dist/assets/{chunk-5VM5RSS4-BUiYXZmu.js → chunk-5VM5RSS4-DkiB4cUQ.js} +1 -1
  22. package/dist/assets/{chunk-7BUUIJ7U-B7o-mn4r.js → chunk-7BUUIJ7U-DzptcytQ.js} +1 -1
  23. package/dist/assets/{chunk-BIQX33UG-Bey9GoRP.js → chunk-BIQX33UG-DMRSQJvT.js} +1 -1
  24. package/dist/assets/{chunk-C7G6YPKG-C6tosszM.js → chunk-C7G6YPKG-Bw1BwwuY.js} +1 -1
  25. package/dist/assets/{chunk-CQNSW5MT-BNrdkgGQ.js → chunk-CQNSW5MT-C0z6eHBk.js} +1 -1
  26. package/dist/assets/{chunk-CYSBUYHQ-DHODFeyB.js → chunk-CYSBUYHQ-ByPyAouO.js} +1 -1
  27. package/dist/assets/{chunk-EMLP6XTP-BcmLjrcq.js → chunk-EMLP6XTP-CJlAXWZg.js} +1 -1
  28. package/dist/assets/{chunk-EX3LRPZG-r-_eEtJI.js → chunk-EX3LRPZG-Dqq4zKJu.js} +1 -1
  29. package/dist/assets/{chunk-FWX5IMBZ-DOObDWbf.js → chunk-FWX5IMBZ-B0SNoCmi.js} +2 -2
  30. package/dist/assets/{chunk-HOUHSVGY-Bd0FnbWZ.js → chunk-HOUHSVGY-DG_GJih_.js} +1 -1
  31. package/dist/assets/{chunk-ICXQ74PX-ntNwAm4_.js → chunk-ICXQ74PX-Drxk_6bE.js} +1 -1
  32. package/dist/assets/{chunk-JG7HCLWE-YtRpblZN.js → chunk-JG7HCLWE-DdLFPpnd.js} +1 -1
  33. package/dist/assets/{chunk-JWPE2WC7-De5-D9ta.js → chunk-JWPE2WC7-B8M4G76F.js} +1 -1
  34. package/dist/assets/{chunk-MOJQB5TN-CdvKhyp2.js → chunk-MOJQB5TN-XP6FJHY_.js} +1 -1
  35. package/dist/assets/{chunk-MOZMSUNE-DJMMN_lu.js → chunk-MOZMSUNE-C_6x18Qw.js} +1 -1
  36. package/dist/assets/{chunk-OGEWGWER-DOFWVw3O.js → chunk-OGEWGWER-N25BzCmG.js} +1 -1
  37. package/dist/assets/{chunk-OSBZ3O6U-DZkrIN9r.js → chunk-OSBZ3O6U-ASDEm7y0.js} +1 -1
  38. package/dist/assets/{chunk-PUDLZKDR-D1wma2Yf.js → chunk-PUDLZKDR-CTlvAcn7.js} +1 -1
  39. package/dist/assets/{chunk-Q4XR5HBZ-CjPy94Lz.js → chunk-Q4XR5HBZ-Da9BWfB6.js} +1 -1
  40. package/dist/assets/{chunk-QBLGF6JB-Hy1bEgS-.js → chunk-QBLGF6JB-Bi6RpTJO.js} +1 -1
  41. package/dist/assets/{chunk-R7FJI6CG-CUPIHyh-.js → chunk-R7FJI6CG-DkMsSPvK.js} +1 -1
  42. package/dist/assets/{chunk-RYQCIY6F-D5Ua4pcv.js → chunk-RYQCIY6F-DZp0eU-j.js} +1 -1
  43. package/dist/assets/{chunk-U6XO7XAA-DaUQcqb7.js → chunk-U6XO7XAA-vfinDSrj.js} +1 -1
  44. package/dist/assets/{chunk-V7JOEXUC-DX2zLfQe.js → chunk-V7JOEXUC-C9Nksccs.js} +1 -1
  45. package/dist/assets/chunk-VAUOI2AC-HLGIHQ6k.js +1 -0
  46. package/dist/assets/{chunk-VR4S4FIN-CFA-WQik.js → chunk-VR4S4FIN-Xv6zfwXs.js} +1 -1
  47. package/dist/assets/{chunk-WYO6CB5R-Bk2HeDvJ.js → chunk-WYO6CB5R-mOJNzbWl.js} +2 -2
  48. package/dist/assets/chunk-XXDRQBXY-Dl0m6Ni-.js +1 -0
  49. package/dist/assets/{chunk-YOTPTUD7-DUG4FpBN.js → chunk-YOTPTUD7-1-aKSu4o.js} +1 -1
  50. package/dist/assets/{chunk-ZGVPDNZ5-70JdIGo0.js → chunk-ZGVPDNZ5-OJ8veMpK.js} +1 -1
  51. package/dist/assets/{chunk-ZIRB5QZD-XZdgBt5L.js → chunk-ZIRB5QZD-SR_Nq_PP.js} +1 -1
  52. package/dist/assets/classDiagram-OUVF2IWQ-DcDnzGNX.js +1 -0
  53. package/dist/assets/classDiagram-v2-EOCWNBFH-BKXKMzKM.js +1 -0
  54. package/dist/assets/config-split-page-BVWVDbvG.js +1 -0
  55. package/dist/assets/confirm-dialog-mA2iVzxh.js +5 -0
  56. package/dist/assets/{cose-bilkent-JH36ORCC-B-PQoGX-.js → cose-bilkent-JH36ORCC-NjVgz2Bw.js} +1 -1
  57. package/dist/assets/cynefin-VYW2F7L2-BWg7U2UD.js +1 -0
  58. package/dist/assets/{cynefinDiagram-TSTJHNR4-BoLCPEM0.js → cynefinDiagram-TSTJHNR4-B_17LDqD.js} +1 -1
  59. package/dist/assets/{dagre-D7Ie4eXr.js → dagre-CMe101gk.js} +1 -1
  60. package/dist/assets/{dagre-VKFMJZFB-CKRrwZA-.js → dagre-VKFMJZFB-qo8nLb3B.js} +1 -1
  61. package/dist/assets/desktop-update-config-DyQb1LXr.js +1 -0
  62. package/dist/assets/{diagram-FQU43EPY-CASL1J3_.js → diagram-FQU43EPY-Ba3XIklC.js} +1 -1
  63. package/dist/assets/{diagram-G47NLZAW-CvLPBIzf.js → diagram-G47NLZAW-CG45TcUx.js} +1 -1
  64. package/dist/assets/{diagram-NH7WQ7WH-Ba6fGdYP.js → diagram-NH7WQ7WH-2F2HrwV-.js} +1 -1
  65. package/dist/assets/{diagram-OA4YK3LP-DkpfgEjM.js → diagram-OA4YK3LP-CXfWH9NZ.js} +1 -1
  66. package/dist/assets/{diagram-WEI45ONY-BP5UMVsN.js → diagram-WEI45ONY-CH_4yepc.js} +1 -1
  67. package/dist/assets/dist-B4Pf5X3Q.js +1 -0
  68. package/dist/assets/dist-DHEWkGWX.js +1 -0
  69. package/dist/assets/doc-browser-CtO_HtRy.js +1 -0
  70. package/dist/assets/doc-browser-context-CuGI6PQc.js +1 -0
  71. package/dist/assets/doc-browser-eMUagyBV.js +1 -0
  72. package/dist/assets/{docx-preview-D0tp3a9T.js → docx-preview-CoM4JK7_.js} +1 -1
  73. package/dist/assets/{ebnfDiagram-CCIWWBDH-TJKbUcmc.js → ebnfDiagram-CCIWWBDH-BwiG4Ea1.js} +1 -1
  74. package/dist/assets/{erDiagram-Q63AITRT-DzGPKkKH.js → erDiagram-Q63AITRT-BML3hpnQ.js} +1 -1
  75. package/dist/assets/eventmodeling-45OFAUF4-SB-J4tpq.js +1 -0
  76. package/dist/assets/flowDiagram-23GEKE2U-BQ_u-eLM.js +1 -0
  77. package/dist/assets/form-actions-BOfIyG3O.js +1 -0
  78. package/dist/assets/{ganttDiagram-NO4QXBWP-JhJzsGp2.js → ganttDiagram-NO4QXBWP-80enfWbD.js} +1 -1
  79. package/dist/assets/gitGraph-TEB2WS4Q-DPxNwsFd.js +1 -0
  80. package/dist/assets/{gitGraphDiagram-IHSO6WYX-DZ1CKMj8.js → gitGraphDiagram-IHSO6WYX-CVLqXr-b.js} +1 -1
  81. package/dist/assets/host-capabilities-D9C4CBK5.js +1 -0
  82. package/dist/assets/index-BjBdWHV1.js +107 -0
  83. package/dist/assets/index-DmpmjAGQ.css +1 -0
  84. package/dist/assets/info-DKCQHKI2-DNlNGH90.js +1 -0
  85. package/dist/assets/{infoDiagram-FWYZ7A6U-BNXNKfRQ.js → infoDiagram-FWYZ7A6U-Dyvs2t0i.js} +1 -1
  86. package/dist/assets/{ishikawaDiagram-FXEZZL3T-DoiEfjNw.js → ishikawaDiagram-FXEZZL3T-sSnxDKfh.js} +1 -1
  87. package/dist/assets/{journeyDiagram-5HDEW3XC-D6oK2HSy.js → journeyDiagram-5HDEW3XC-D22V71Mf.js} +1 -1
  88. package/dist/assets/{kanban-definition-HUTT4EX6-B2UnLOH-.js → kanban-definition-HUTT4EX6-D3ds0p-g.js} +1 -1
  89. package/dist/assets/{line-BM7PoxY5.js → line-CbyFs065.js} +1 -1
  90. package/dist/assets/{linear-B3DiqMjk.js → linear-4DKtnZBS.js} +1 -1
  91. package/dist/assets/loader-circle-Df8wF2Pr.js +1 -0
  92. package/dist/assets/{map-BAAdHTqR.js → map-CcZzEoz6.js} +1 -1
  93. package/dist/assets/mcp-marketplace-page-DI49IKxp.js +1 -0
  94. package/dist/assets/mcp-marketplace-page-ofDqbOIF.js +9 -0
  95. package/dist/assets/{mermaid-parser.core-BeMviefY.js → mermaid-parser.core-DPmMOjXD.js} +2 -2
  96. package/dist/assets/{mermaid.core-D0Q77r9I.js → mermaid.core-BSf-ddRa.js} +3 -3
  97. package/dist/assets/{mindmap-definition-LN4V7U3C-BQ7r7bR8.js → mindmap-definition-LN4V7U3C-BUTwWPyx.js} +1 -1
  98. package/dist/assets/model-config-page-D73Tu3jW.js +1 -0
  99. package/dist/assets/navigation-link-CAFOcXNg.js +3 -0
  100. package/dist/assets/{ordinal-Bt2N4IkA.js → ordinal-BgLYtOZE.js} +1 -1
  101. package/dist/assets/packet-7NZHBO7P-BpeiqkBZ.js +1 -0
  102. package/dist/assets/{pegDiagram-2B236MQR-B-C9-LGz.js → pegDiagram-2B236MQR-BEu_ZBjc.js} +1 -1
  103. package/dist/assets/pie-RZYD4A2V-Cy1yxsPd.js +1 -0
  104. package/dist/assets/{pieDiagram-ENE6RG2P-CtirZLtn.js → pieDiagram-ENE6RG2P-CAwAwuBd.js} +1 -1
  105. package/dist/assets/plus-DVU5RLCS.js +1 -0
  106. package/dist/assets/provider-scoped-model-input-BaJJJYMX.js +1 -0
  107. package/dist/assets/providers-config-page-BL6IRVBm.js +1 -0
  108. package/dist/assets/{quadrantDiagram-ABIIQ3AL-DiBWAzDU.js → quadrantDiagram-ABIIQ3AL-CwE6F-_2.js} +1 -1
  109. package/dist/assets/radar-I7S5WNFK-CEcPKCj8.js +1 -0
  110. package/dist/assets/railroad-3IZDKUUU-CW38eNDs.js +1 -0
  111. package/dist/assets/railroad-abnf-AHOZXSZD-ByrylcXf.js +1 -0
  112. package/dist/assets/railroad-ebnf-EBAXGLYW-CNB6GwP-.js +1 -0
  113. package/dist/assets/railroad-peg-LSFZ7HO6-x6hnqlBG.js +1 -0
  114. package/dist/assets/{railroadDiagram-RFXS5EU6-CP6b3PBI.js → railroadDiagram-RFXS5EU6-xgLekU3B.js} +1 -1
  115. package/dist/assets/react-BC_orpAj.js +8 -0
  116. package/dist/assets/{refresh-cw-CkKwPztT.js → refresh-cw-CWDp_JnN.js} +1 -1
  117. package/dist/assets/remote-T24O58lH.js +1 -0
  118. package/dist/assets/{requirementDiagram-TGXJPOKE-D8YV0-Jz.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
  119. package/dist/assets/rotate-cw-BqhwSV5p.js +1 -0
  120. package/dist/assets/runtime-config-page-CXUx017t.js +1 -0
  121. package/dist/assets/{sankeyDiagram-HTMAVEWB-C2vA4ojE.js → sankeyDiagram-HTMAVEWB-JK6bFmYB.js} +1 -1
  122. package/dist/assets/{save-BINMWate.js → save-Cnr71JON.js} +1 -1
  123. package/dist/assets/{search-7M38azRp.js → search-CCX11X2m.js} +1 -1
  124. package/dist/assets/search-config-page-BB6d5gT7.js +1 -0
  125. package/dist/assets/{secrets-config-page-2mVejXf2.js → secrets-config-page-DYsDeT9K.js} +2 -2
  126. package/dist/assets/security-config-CVPgqL7G.js +1 -0
  127. package/dist/assets/select-DvwBu99K.js +41 -0
  128. package/dist/assets/{sequenceDiagram-DBY2YBRQ-B9LBmpUo.js → sequenceDiagram-DBY2YBRQ-BR5IokXV.js} +1 -1
  129. package/dist/assets/{settings-2-DqZstREV.js → settings-2-Dmv6zMne.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-9UIfya9-.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
  131. package/dist/assets/skeleton-C5tWata3.js +1 -0
  132. package/dist/assets/{src-DKf77Yzo.js → src-vgz8E_-O.js} +1 -1
  133. package/dist/assets/{stateDiagram-2N3HPSRC-oTW44vds.js → stateDiagram-2N3HPSRC-DD4OoYhn.js} +1 -1
  134. package/dist/assets/stateDiagram-v2-6OUMAXLB-ZC6dFORv.js +1 -0
  135. package/dist/assets/{swimlanes-5IMT3BWC-Cf7lcsay.js → swimlanes-5IMT3BWC-BWkFeyCT.js} +2 -2
  136. package/dist/assets/swimlanesDiagram-G3AALYLV-CRkh51Im.js +8 -0
  137. package/dist/assets/{tag-chip-BoDaLnWt.js → tag-chip-gMPvx7n_.js} +1 -1
  138. package/dist/assets/{timeline-definition-FHXFAJF6-DoCuWKDJ.js → timeline-definition-FHXFAJF6-VTRPy6KA.js} +1 -1
  139. package/dist/assets/treeView-QDETBFTQ-Bw0DSCH7.js +1 -0
  140. package/dist/assets/treemap-6X3UGDF4-DFWBFcfh.js +1 -0
  141. package/dist/assets/use-confirm-dialog-D_3-QpZU.js +1 -0
  142. package/dist/assets/{vennDiagram-L72KCM5P-CpU0pzGe.js → vennDiagram-L72KCM5P-Bvk42Pek.js} +1 -1
  143. package/dist/assets/wardley-OPB4EBWU-CoWturk0.js +1 -0
  144. package/dist/assets/{wardleyDiagram-EHGQE667-CkN6nn6-.js → wardleyDiagram-EHGQE667-DOFjx2s3.js} +1 -1
  145. package/dist/assets/x-BEWSqeux.js +1 -0
  146. package/dist/assets/{xychartDiagram-FW5EYKEG-DATpapm2.js → xychartDiagram-FW5EYKEG-DY5yRTUc.js} +1 -1
  147. package/dist/index.html +30 -32
  148. package/index.html +8 -0
  149. package/package.json +9 -9
  150. package/src/app/components/layout/page-layout.tsx +15 -6
  151. package/src/app/styles/charcoal-theme.css +85 -0
  152. package/src/app/styles/design-system.css +11 -13
  153. package/src/features/agents/components/__tests__/agents-page.test.tsx +36 -24
  154. package/src/features/agents/components/agent-advanced-config-fields.tsx +7 -7
  155. package/src/features/agents/components/agent-details-dialog.tsx +8 -8
  156. package/src/features/agents/components/agent-dialogs.tsx +7 -7
  157. package/src/features/agents/components/agents-page.tsx +42 -62
  158. package/src/features/chat/components/conversation/__tests__/chat-conversation-content.test.tsx +27 -3
  159. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +16 -0
  160. package/src/features/chat/components/conversation/__tests__/chat-conversation-panel.test.tsx +8 -22
  161. package/src/features/chat/components/conversation/__tests__/chat-conversation-workspace-section.test.tsx +69 -3
  162. package/src/features/chat/components/conversation/chat-conversation-content.tsx +20 -6
  163. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +30 -31
  164. package/src/features/chat/components/conversation/chat-conversation-panel.tsx +1 -8
  165. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +16 -4
  166. package/src/features/chat/components/layout/__tests__/chat-page-shell.test.tsx +23 -0
  167. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +6 -0
  168. package/src/features/chat/components/layout/chat-page-shell.tsx +5 -4
  169. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +4 -4
  170. package/src/features/chat/components/providers/chat-presenter.provider.tsx +2 -0
  171. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +84 -12
  172. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +243 -5
  173. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +48 -31
  174. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +46 -37
  175. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-controller.test.tsx +128 -1
  176. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +67 -1
  177. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-slash-commands.test.tsx +4 -0
  178. package/src/features/chat/features/conversation/hooks/__tests__/use-session-provider-model-catalog.test.tsx +161 -0
  179. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +55 -0
  180. package/src/features/chat/features/conversation/hooks/use-session-conversation-composer-nodes.ts +26 -0
  181. package/src/features/chat/features/conversation/hooks/use-session-conversation-controller.ts +45 -12
  182. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-query.ts +28 -3
  183. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +91 -37
  184. package/src/features/chat/features/conversation/hooks/use-session-conversation-recovery-actions.ts +64 -0
  185. package/src/features/chat/features/conversation/hooks/use-session-conversation-slash-commands.ts +2 -0
  186. package/src/features/chat/features/conversation/hooks/use-session-provider-model-catalog.ts +159 -0
  187. package/src/features/chat/features/conversation/utils/__tests__/session-message-composer.utils.test.ts +34 -0
  188. package/src/features/chat/features/conversation/utils/session-conversation-input-toolbar.utils.ts +23 -1
  189. package/src/features/chat/features/conversation/utils/session-message-composer.utils.ts +117 -0
  190. package/src/features/chat/features/conversation/utils/session-queued-input.utils.ts +10 -98
  191. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +2 -1
  192. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +3 -1
  193. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +22 -1
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +52 -0
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +111 -0
  196. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +27 -2
  197. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +10 -0
  198. package/src/features/chat/features/input/utils/chat-conversation-excerpt-token.utils.ts +59 -0
  199. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +123 -32
  200. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +1 -0
  201. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +97 -10
  202. package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +149 -0
  203. package/src/features/chat/features/message/components/__tests__/chat-message-inline-editor.test.tsx +59 -0
  204. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +123 -90
  205. package/src/features/chat/features/message/components/__tests__/chat-message-timeline-dividers.test.tsx +43 -0
  206. package/src/features/chat/features/message/components/chat-message-inline-editor.tsx +111 -0
  207. package/src/features/chat/features/message/components/chat-message-list.container.tsx +120 -171
  208. package/src/features/chat/features/message/components/chat-message-timeline-dividers.tsx +75 -0
  209. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +125 -0
  210. package/src/features/chat/features/message/hooks/use-chat-message-actions.tsx +187 -0
  211. package/src/features/chat/features/message/utils/__tests__/chat-message-action-targets.utils.test.ts +89 -0
  212. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +194 -0
  213. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-continuation-compaction.utils.test.ts +167 -0
  214. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +175 -1
  215. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +26 -0
  216. package/src/features/chat/features/message/utils/chat-message-action-targets.utils.ts +28 -0
  217. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +38 -0
  218. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +6 -0
  219. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +201 -7
  220. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +180 -1
  221. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +184 -10
  222. package/src/features/chat/features/ncp/hooks/use-hydrated-ncp-agent.integration.test.tsx +57 -0
  223. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +90 -1
  224. package/src/features/chat/features/ncp/utils/__tests__/ncp-chat-query-derived.utils.test.ts +110 -0
  225. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +65 -3
  226. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +17 -0
  227. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +63 -54
  228. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +87 -0
  229. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +19 -0
  230. package/src/features/chat/features/session/utils/chat-context-window-indicator.utils.ts +44 -5
  231. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +10 -0
  232. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +42 -4
  233. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +17 -2
  234. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +8 -3
  235. package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
  236. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +22 -6
  237. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +13 -0
  238. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +72 -2
  239. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +21 -1
  240. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +75 -0
  241. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +109 -0
  242. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +145 -26
  243. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +42 -18
  244. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +50 -10
  245. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +48 -22
  246. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +13 -2
  247. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +45 -0
  248. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +80 -0
  249. package/src/features/chat/features/workspace/utils/__tests__/workspace-text-excerpt.utils.test.ts +54 -0
  250. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +47 -0
  251. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +16 -0
  252. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +22 -1
  253. package/src/features/chat/features/workspace/utils/workspace-text-excerpt.utils.ts +53 -0
  254. package/src/features/chat/index.ts +1 -0
  255. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +129 -0
  256. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +38 -2
  257. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +93 -1
  258. package/src/features/chat/managers/__tests__/provider-model-catalog-notice.manager.test.ts +57 -0
  259. package/src/features/chat/managers/chat-composer-intent.manager.ts +190 -0
  260. package/src/features/chat/managers/chat-session-list.manager.ts +13 -4
  261. package/src/features/chat/managers/chat-thread.manager.ts +30 -11
  262. package/src/features/chat/managers/provider-model-catalog-notice.manager.ts +121 -0
  263. package/src/features/chat/presenters/chat.presenter.ts +3 -0
  264. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +55 -0
  265. package/src/features/chat/stores/chat-composer-draft.store.ts +92 -0
  266. package/src/features/chat/stores/chat-thread.store.ts +2 -4
  267. package/src/features/chat/types/chat-input-bar.types.ts +8 -0
  268. package/src/features/chat/types/chat-input.types.ts +5 -0
  269. package/src/features/chat/types/chat-message.types.ts +5 -0
  270. package/src/features/cron/components/__tests__/cron-config.test.tsx +100 -19
  271. package/src/features/cron/components/cron-config.tsx +19 -33
  272. package/src/features/cron/components/cron-job-detail-dialog.tsx +42 -16
  273. package/src/features/cron/components/cron-job-row.tsx +18 -1
  274. package/src/features/inbox/components/inbox-delivery-content.test.tsx +8 -2
  275. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  276. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +10 -4
  277. package/src/features/inbox/components/inbox-reader-dialog.tsx +37 -61
  278. package/src/features/inbox/pages/inbox-page.tsx +90 -98
  279. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +28 -6
  280. package/src/features/notifications/components/app-notification-toast.tsx +54 -15
  281. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +2 -0
  282. package/src/features/notifications/managers/app-notification.manager.ts +3 -0
  283. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +1 -0
  284. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +9 -0
  285. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +1 -0
  286. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +5 -4
  287. package/src/features/service-apps/components/service-app-list-item.tsx +470 -0
  288. package/src/features/service-apps/components/service-apps-panel.tsx +76 -490
  289. package/src/features/settings/components/config/__tests__/provider-models-section.test.tsx +239 -0
  290. package/src/features/settings/components/config/provider-form-detail-pane.tsx +307 -0
  291. package/src/features/settings/components/config/provider-form.tsx +149 -360
  292. package/src/features/settings/components/config/provider-model-list.tsx +293 -0
  293. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +151 -0
  294. package/src/features/settings/components/config/provider-models-section.tsx +178 -149
  295. package/src/features/settings/hooks/__tests__/use-provider-connectivity.test.tsx +98 -0
  296. package/src/features/settings/hooks/__tests__/use-provider-model-suggestions.test.tsx +107 -0
  297. package/src/features/settings/hooks/use-provider-connectivity.ts +132 -0
  298. package/src/features/settings/hooks/use-provider-model-suggestions.ts +51 -0
  299. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +2 -2
  300. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +48 -0
  301. package/src/features/settings/pages/search-config-page.tsx +17 -8
  302. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +42 -0
  303. package/src/features/settings/utils/provider-form-context.utils.ts +2 -0
  304. package/src/features/settings/utils/provider-form-model.utils.ts +25 -0
  305. package/src/features/settings/utils/provider-form-support.utils.ts +21 -5
  306. package/src/features/side-dock/components/__tests__/side-dock.test.tsx +1 -0
  307. package/src/features/side-dock/configs/side-dock-built-in-items.config.ts +0 -9
  308. package/src/index.css +1 -1
  309. package/src/platforms/desktop/types/desktop-update.types.ts +5 -0
  310. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +14 -3
  311. package/src/shared/components/actions/navigation-link.tsx +35 -15
  312. package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -2
  313. package/src/shared/components/common/agent-avatar.tsx +3 -3
  314. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +66 -0
  315. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -1
  316. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -0
  317. package/src/shared/components/doc-browser/doc-browser.tsx +9 -0
  318. package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +126 -0
  319. package/src/shared/components/settings/settings-page.tsx +1 -1
  320. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +103 -0
  321. package/src/shared/components/ui/context-menu/context-menu.tsx +263 -0
  322. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +19 -6
  323. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +28 -67
  324. package/src/shared/hooks/use-config.ts +33 -1
  325. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +26 -0
  326. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +7 -10
  327. package/src/shared/lib/api/ncp-session.types.ts +6 -0
  328. package/src/shared/lib/api/types.ts +36 -1
  329. package/src/shared/lib/api/utils/config.utils.ts +14 -0
  330. package/src/shared/lib/cron/cron-job-view.utils.ts +4 -0
  331. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +26 -0
  332. package/src/shared/lib/host-capabilities/host-capability.manager.ts +28 -0
  333. package/src/shared/lib/i18n/locales/en-US/chat.json +39 -4
  334. package/src/shared/lib/i18n/locales/en-US/core.json +26 -0
  335. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
  336. package/src/shared/lib/i18n/locales/en-US/search.json +1 -0
  337. package/src/shared/lib/i18n/locales/zh-CN/chat.json +39 -4
  338. package/src/shared/lib/i18n/locales/zh-CN/core.json +26 -0
  339. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
  340. package/src/shared/lib/i18n/locales/zh-CN/search.json +1 -0
  341. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +28 -1
  342. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +23 -0
  343. package/src/shared/lib/theme/index.test.ts +11 -0
  344. package/src/shared/lib/theme/index.ts +2 -0
  345. package/dist/assets/api-B1yHQYs-.js +0 -13
  346. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
  347. package/dist/assets/app-presenter-provider-uP9waOzv.js +0 -108
  348. package/dist/assets/appearance-settings-page-C9Zq3hP9.js +0 -1
  349. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +0 -1
  350. package/dist/assets/book-open-oSmyVXea.js +0 -1
  351. package/dist/assets/card-CGvJZTZp.js +0 -1
  352. package/dist/assets/channel-BSKkTPe8.js +0 -1
  353. package/dist/assets/channels-list-page-5BTxpQHw.js +0 -8
  354. package/dist/assets/chat-page-jTvli8Jp.js +0 -1
  355. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +0 -1
  356. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +0 -1
  357. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +0 -1
  358. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +0 -1
  359. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +0 -1
  360. package/dist/assets/config-split-page-BgBsUmp1.js +0 -1
  361. package/dist/assets/confirm-dialog-h849QRWO.js +0 -5
  362. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +0 -1
  363. package/dist/assets/desktop-update-config-BGGHxClR.js +0 -1
  364. package/dist/assets/dist-CxUhurq9.js +0 -41
  365. package/dist/assets/doc-browser-0eL8AX42.js +0 -1
  366. package/dist/assets/doc-browser-DEqE9lsh.js +0 -1
  367. package/dist/assets/doc-browser-context-DkzFIUoK.js +0 -1
  368. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +0 -1
  369. package/dist/assets/eye-gOWQGt8N.js +0 -1
  370. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +0 -1
  371. package/dist/assets/form-actions-CgjYZUzM.js +0 -1
  372. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +0 -1
  373. package/dist/assets/host-capabilities-B-TtABsP.js +0 -1
  374. package/dist/assets/index-DSAUWCfl.js +0 -2
  375. package/dist/assets/index-G7cBryJ4.css +0 -1
  376. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +0 -1
  377. package/dist/assets/loader-circle-BAuycGf6.js +0 -1
  378. package/dist/assets/logo-badge-BNcsDsUM.js +0 -1
  379. package/dist/assets/mcp-marketplace-page-BX39MtPa.js +0 -9
  380. package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +0 -1
  381. package/dist/assets/model-config-page-7MIXg2fQ.js +0 -1
  382. package/dist/assets/navigation-link-B3iFOdxx.js +0 -1
  383. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +0 -1
  384. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +0 -1
  385. package/dist/assets/plus-yvhFgfal.js +0 -1
  386. package/dist/assets/popover-Cpep_rJe.js +0 -1
  387. package/dist/assets/provider-models-CUrptZ-o.js +0 -1
  388. package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +0 -1
  389. package/dist/assets/providers-config-page-A6J7Y0II.js +0 -1
  390. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +0 -1
  391. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +0 -1
  392. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +0 -1
  393. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +0 -1
  394. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +0 -1
  395. package/dist/assets/react-BCJGCjrP.js +0 -8
  396. package/dist/assets/remote-FD-ZfSIn.js +0 -1
  397. package/dist/assets/rotate-cw-DrJMfHhp.js +0 -1
  398. package/dist/assets/runtime-config-page-DwjO-Vs6.js +0 -1
  399. package/dist/assets/search-config-page-T6tMhXbP.js +0 -1
  400. package/dist/assets/security-config-DD5RrjDe.js +0 -1
  401. package/dist/assets/select-BvidP5Oa.js +0 -1
  402. package/dist/assets/skeleton-BEQW2Dva.js +0 -1
  403. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +0 -1
  404. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +0 -8
  405. package/dist/assets/tabs-BeLxVmkt.js +0 -1
  406. package/dist/assets/trash-2-CYUskMMN.js +0 -1
  407. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +0 -1
  408. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +0 -1
  409. package/dist/assets/use-config-CxKRXXCl.js +0 -1
  410. package/dist/assets/use-confirm-dialog-HASTvOP2.js +0 -1
  411. package/dist/assets/use-viewport-layout-CJxkjoRz.js +0 -1
  412. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +0 -1
  413. package/dist/assets/x-DdUwjre5.js +0 -1
  414. /package/dist/assets/{array-CAkJT3no.js → array-NADKd9aU.js} +0 -0
  415. /package/dist/assets/{chunk-KEIR6QF5-BgdDoWEH.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  416. /package/dist/assets/{chunk-Y2CYZVJY-pTllD0P3.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  417. /package/dist/assets/{config-hints-S3g-UVZb.js → config-hints-BcfVUMij.js} +0 -0
  418. /package/dist/assets/{cytoscape.esm-DmURt-5G.js → cytoscape.esm-BANTlgza.js} +0 -0
  419. /package/dist/assets/{defaultLocale-BKy_KP3O.js → defaultLocale-C4_L0aEs.js} +0 -0
  420. /package/dist/assets/{dist-IgNNB7K9.js → dist-DAPEIJbz.js} +0 -0
  421. /package/dist/assets/{graphlib-xw_TBHyH.js → graphlib-ChqXg0dI.js} +0 -0
  422. /package/dist/assets/{init-C9xlLIAu.js → init-D8wZg9xq.js} +0 -0
  423. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CkbBfwAQ.js} +0 -0
  424. /package/dist/assets/{katex-DSy1eD2d.js → katex-TZJFa0J_.js} +0 -0
  425. /package/dist/assets/{middleware-Df-a4x-q.js → middleware-B3k-TSmZ.js} +0 -0
  426. /package/dist/assets/{path-DB0USYHi.js → path-DodfxcX7.js} +0 -0
  427. /package/dist/assets/{rough.esm-B1B52ZQS.js → rough.esm-CUaa1Rbe.js} +0 -0
  428. /package/dist/assets/{xlsx-DfjC_iml.js → xlsx-CxByi7bR.js} +0 -0
@@ -153,6 +153,7 @@ describe("fixed workspace tabs", () => {
153
153
  };
154
154
  const onCloseTab = vi.fn();
155
155
  const tabs = buildWorkspaceTabsViewModel({
156
+ hasSession: true,
156
157
  resolvedChildTabs: [childTab],
157
158
  activeSideChatDraft: draft,
158
159
  closedWorkspaceTabEntries: [],
@@ -194,6 +195,7 @@ describe("fixed workspace tabs", () => {
194
195
 
195
196
  it("keeps fixed workspace tabs even when legacy closed entries exist", () => {
196
197
  const tabs = buildWorkspaceTabsViewModel({
198
+ hasSession: true,
197
199
  resolvedChildTabs: [createChildTab()],
198
200
  activeSideChatDraft: null,
199
201
  closedWorkspaceTabEntries: [
@@ -227,6 +229,7 @@ describe("fixed workspace tabs", () => {
227
229
  setLanguage("zh");
228
230
 
229
231
  const tabs = buildWorkspaceTabsViewModel({
232
+ hasSession: true,
230
233
  resolvedChildTabs: [],
231
234
  activeSideChatDraft: null,
232
235
  closedWorkspaceTabEntries: [],
@@ -264,6 +267,7 @@ describe("buildWorkspaceTabsViewModel", () => {
264
267
  const onSelectSession = vi.fn();
265
268
 
266
269
  const tabs = buildWorkspaceTabsViewModel({
270
+ hasSession: true,
267
271
  resolvedChildTabs: [childTab],
268
272
  activeSideChatDraft: null,
269
273
  closedWorkspaceTabEntries: [],
@@ -300,6 +304,7 @@ describe("buildWorkspaceTabsViewModel", () => {
300
304
  const childTab = createChildTab();
301
305
 
302
306
  const tabs = buildWorkspaceTabsViewModel({
307
+ hasSession: true,
303
308
  resolvedChildTabs: [childTab],
304
309
  activeSideChatDraft: draft,
305
310
  closedWorkspaceTabEntries: [],
@@ -353,6 +358,7 @@ describe("buildWorkspaceTabsViewModel", () => {
353
358
 
354
359
  const onOpenFileViewer = vi.fn();
355
360
  const tabs = buildWorkspaceTabsViewModel({
361
+ hasSession: true,
356
362
  resolvedChildTabs: [],
357
363
  activeSideChatDraft: null,
358
364
  closedWorkspaceTabEntries: [],
@@ -401,4 +407,78 @@ describe("buildWorkspaceTabsViewModel", () => {
401
407
  "rendered",
402
408
  );
403
409
  });
410
+
411
+ it("adds project files to chat with a normalized relative reference", () => {
412
+ const onAddFileToChat = vi.fn();
413
+ const projectFile = {
414
+ key: "project-file",
415
+ parentSessionKey: "parent-1",
416
+ path: "/workspace/docs/guide.md",
417
+ viewMode: "preview" as const,
418
+ };
419
+ const externalFile = {
420
+ key: "external-file",
421
+ parentSessionKey: "parent-1",
422
+ path: "/tmp/notes.md",
423
+ viewMode: "preview" as const,
424
+ };
425
+
426
+ const tabs = buildWorkspaceTabsViewModel({
427
+ hasSession: true,
428
+ resolvedChildTabs: [],
429
+ activeSideChatDraft: null,
430
+ closedWorkspaceTabEntries: [],
431
+ workspaceFileTabs: [projectFile, externalFile],
432
+ activeSelection: { kind: "file", file: projectFile },
433
+ optimisticReadAtBySessionKey: {},
434
+ sessionProjectRoot: "/workspace",
435
+ onAddFileToChat,
436
+ onSelectSession: vi.fn(),
437
+ onSelectFile: vi.fn(),
438
+ onOpenFileViewer: vi.fn(),
439
+ onCloseTab: vi.fn(),
440
+ onSelectOverview: vi.fn(),
441
+ onSelectChildSessions: vi.fn(),
442
+ onSelectProjectFiles: vi.fn(),
443
+ onSelectCronJobs: vi.fn(),
444
+ });
445
+
446
+ tabs.find((tab) => tab.key === "file:project-file")?.onAddToChat?.();
447
+
448
+ expect(onAddFileToChat).toHaveBeenCalledWith({
449
+ label: "guide.md",
450
+ tokenKey: "docs/guide.md",
451
+ });
452
+ expect(
453
+ tabs.find((tab) => tab.key === "file:external-file")?.onAddToChat,
454
+ ).toBeUndefined();
455
+ });
456
+
457
+ it("shows only project files before a draft session is materialized", () => {
458
+ const tabs = buildWorkspaceTabsViewModel({
459
+ hasSession: false,
460
+ resolvedChildTabs: [],
461
+ activeSideChatDraft: null,
462
+ closedWorkspaceTabEntries: [],
463
+ workspaceFileTabs: [],
464
+ activeSelection: { kind: "project-files" },
465
+ optimisticReadAtBySessionKey: {},
466
+ onSelectSession: vi.fn(),
467
+ onSelectFile: vi.fn(),
468
+ onOpenFileViewer: vi.fn(),
469
+ onCloseTab: vi.fn(),
470
+ onSelectOverview: vi.fn(),
471
+ onSelectChildSessions: vi.fn(),
472
+ onSelectProjectFiles: vi.fn(),
473
+ onSelectCronJobs: vi.fn(),
474
+ });
475
+
476
+ expect(tabs).toEqual([
477
+ expect.objectContaining({
478
+ key: "project-files",
479
+ kind: "project-files",
480
+ active: true,
481
+ }),
482
+ ]);
483
+ });
404
484
  });
@@ -0,0 +1,54 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { buildWorkspaceTextExcerpt } from "@/features/chat/features/workspace/utils/workspace-text-excerpt.utils";
3
+
4
+ describe("buildWorkspaceTextExcerpt", () => {
5
+ it("keeps the exact snapshot and maps a cross-line selection", () => {
6
+ expect(buildWorkspaceTextExcerpt({
7
+ path: "docs/guide.md",
8
+ label: "guide.md",
9
+ selectedText: "second\nthird",
10
+ sourceText: "first\nsecond\nthird\nfourth",
11
+ sourceStartLine: 10,
12
+ })).toEqual({
13
+ path: "docs/guide.md",
14
+ label: "guide.md",
15
+ excerpt: "second\nthird",
16
+ startLine: 11,
17
+ endLine: 12,
18
+ });
19
+ });
20
+
21
+ it("keeps a rendered selection without inventing a source position", () => {
22
+ expect(buildWorkspaceTextExcerpt({
23
+ path: "README.md",
24
+ label: "README.md",
25
+ selectedText: "Rendered sentence",
26
+ sourceText: "**Rendered** sentence",
27
+ })).toMatchObject({
28
+ excerpt: "Rendered sentence",
29
+ startLine: null,
30
+ endLine: null,
31
+ });
32
+ });
33
+
34
+ it("omits a line number when repeated text makes the location ambiguous", () => {
35
+ expect(buildWorkspaceTextExcerpt({
36
+ path: "src/example.ts",
37
+ label: "example.ts",
38
+ selectedText: "return value;",
39
+ sourceText: "return value;\nnext();\nreturn value;",
40
+ })).toMatchObject({
41
+ startLine: null,
42
+ endLine: null,
43
+ });
44
+ });
45
+
46
+ it("rejects whitespace-only selections", () => {
47
+ expect(buildWorkspaceTextExcerpt({
48
+ path: "README.md",
49
+ label: "README.md",
50
+ selectedText: " \n ",
51
+ sourceText: "content",
52
+ })).toBeNull();
53
+ });
54
+ });
@@ -8,6 +8,7 @@ import {
8
8
  filterNavigationHistoryEntries,
9
9
  pushNavigationHistoryEntry,
10
10
  } from "@/shared/lib/navigation-history";
11
+ import { reparentWorkspaceFileTab } from "@/features/chat/features/workspace/utils/chat-workspace-file-tab.utils";
11
12
 
12
13
  export function areWorkspaceNavigationEntriesEqual(
13
14
  current: ChatWorkspaceNavigationEntry,
@@ -206,6 +207,52 @@ export function upsertChildSessionTab(
206
207
  return nextTabs;
207
208
  }
208
209
 
210
+ export function materializeDraftWorkspaceSnapshot(
211
+ snapshot: ChatThreadSnapshot,
212
+ sessionKey: string,
213
+ ): Partial<ChatThreadSnapshot> | null {
214
+ const normalizedSessionKey = sessionKey.trim();
215
+ if (!normalizedSessionKey) {
216
+ return null;
217
+ }
218
+ const materializedFileKeys = new Map<string, string>();
219
+ const workspaceFileTabs = snapshot.workspaceFileTabs.map((tab) => {
220
+ if (tab.parentSessionKey !== null) {
221
+ return tab;
222
+ }
223
+ const materializedTab = reparentWorkspaceFileTab(tab, normalizedSessionKey);
224
+ materializedFileKeys.set(tab.key, materializedTab.key);
225
+ return materializedTab;
226
+ });
227
+ const materializeNavigationEntry = (
228
+ entry: ChatWorkspaceNavigationEntry,
229
+ ): ChatWorkspaceNavigationEntry => {
230
+ if (entry.kind !== 'file') {
231
+ return entry;
232
+ }
233
+ const materializedKey = materializedFileKeys.get(entry.key);
234
+ return materializedKey ? { ...entry, key: materializedKey } : entry;
235
+ };
236
+ return {
237
+ draftProjectRoot: null,
238
+ workspaceFileTabs,
239
+ activeWorkspaceFileKey: snapshot.activeWorkspaceFileKey
240
+ ? materializedFileKeys.get(snapshot.activeWorkspaceFileKey) ??
241
+ snapshot.activeWorkspaceFileKey
242
+ : null,
243
+ closedWorkspaceTabEntries: snapshot.closedWorkspaceTabEntries.map(
244
+ materializeNavigationEntry,
245
+ ),
246
+ workspaceNavigationHistory: snapshot.workspaceNavigationHistory.map(
247
+ materializeNavigationEntry,
248
+ ),
249
+ workspacePanelParentKey:
250
+ snapshot.workspacePanelParentKey === null && snapshot.activeWorkspacePanelKind
251
+ ? normalizedSessionKey
252
+ : snapshot.workspacePanelParentKey,
253
+ };
254
+ }
255
+
209
256
  export function materializeSideChatDraftSnapshot(params: {
210
257
  snapshot: ChatThreadSnapshot;
211
258
  draftKey: string;
@@ -65,3 +65,19 @@ export function upsertWorkspaceFileTab(
65
65
  ...tabs.slice(adjacentIndex + 1),
66
66
  ];
67
67
  }
68
+
69
+ export function reparentWorkspaceFileTab(
70
+ tab: ChatWorkspaceFileTab,
71
+ parentSessionKey: string,
72
+ ): ChatWorkspaceFileTab {
73
+ const normalizedParentSessionKey = parentSessionKey.trim();
74
+ const identitySeparatorIndex = tab.key.indexOf('::');
75
+ const tabIdentity = identitySeparatorIndex >= 0
76
+ ? tab.key.slice(identitySeparatorIndex)
77
+ : `::${tab.key}`;
78
+ return {
79
+ ...tab,
80
+ key: `${normalizedParentSessionKey}${tabIdentity}`,
81
+ parentSessionKey: normalizedParentSessionKey,
82
+ };
83
+ }
@@ -13,6 +13,7 @@ import {
13
13
  type ChatWorkspaceFileViewer,
14
14
  } from "@/features/chat/features/workspace/utils/chat-workspace-file-viewer.utils";
15
15
  import { areWorkspaceNavigationEntriesEqual } from "@/features/chat/features/workspace/utils/chat-thread-workspace-session.utils";
16
+ import { resolveWorkspaceRelativePath } from "@/shared/lib/session-project";
16
17
 
17
18
  export type WorkspaceSelection =
18
19
  | {
@@ -56,6 +57,7 @@ export type WorkspaceTabViewModel = {
56
57
  viewer: ChatWorkspaceFileViewer;
57
58
  onSelect: () => void;
58
59
  } | null;
60
+ onAddToChat?: () => void;
59
61
  onSelect: () => void;
60
62
  onClose?: () => void;
61
63
  };
@@ -154,12 +156,15 @@ export function resolveWorkspaceSelection(params: {
154
156
  }
155
157
 
156
158
  type WorkspaceTabsViewModelParams = {
159
+ hasSession: boolean;
157
160
  resolvedChildTabs: ResolvedChildSessionTab[];
158
161
  activeSideChatDraft: ChatWorkspaceSideChatDraft | null;
159
162
  closedWorkspaceTabEntries: readonly ChatWorkspaceNavigationEntry[];
160
163
  workspaceFileTabs: readonly ChatWorkspaceFileTab[];
161
164
  activeSelection: WorkspaceSelection | null;
162
165
  optimisticReadAtBySessionKey: Record<string, string>;
166
+ sessionProjectRoot?: string | null;
167
+ onAddFileToChat?: (reference: { label: string; tokenKey: string }) => void;
163
168
  onSelectSession: (sessionKey: string) => void;
164
169
  onSelectFile: (fileKey: string) => void;
165
170
  onOpenFileViewer: (fileKey: string, viewer: ChatWorkspaceFileViewer) => void;
@@ -172,6 +177,7 @@ type WorkspaceTabsViewModelParams = {
172
177
 
173
178
  function buildWorkspacePageTabs({
174
179
  activeSelection,
180
+ hasSession,
175
181
  onSelectChildSessions,
176
182
  onSelectCronJobs,
177
183
  onSelectOverview,
@@ -179,12 +185,13 @@ function buildWorkspacePageTabs({
179
185
  }: Pick<
180
186
  WorkspaceTabsViewModelParams,
181
187
  | "activeSelection"
188
+ | "hasSession"
182
189
  | "onSelectChildSessions"
183
190
  | "onSelectCronJobs"
184
191
  | "onSelectOverview"
185
192
  | "onSelectProjectFiles"
186
193
  >): WorkspaceTabViewModel[] {
187
- return [
194
+ const pageTabs: WorkspaceTabViewModel[] = [
188
195
  {
189
196
  key: "overview",
190
197
  kind: "overview",
@@ -218,18 +225,24 @@ function buildWorkspacePageTabs({
218
225
  onSelect: onSelectProjectFiles,
219
226
  },
220
227
  ];
228
+ return hasSession
229
+ ? pageTabs
230
+ : pageTabs.filter((tab) => tab.kind === "project-files");
221
231
  }
222
232
 
223
233
  export function buildWorkspaceTabsViewModel(
224
234
  params: WorkspaceTabsViewModelParams,
225
235
  ): WorkspaceTabViewModel[] {
226
236
  const {
237
+ hasSession,
227
238
  activeSideChatDraft,
228
239
  closedWorkspaceTabEntries,
229
240
  resolvedChildTabs,
230
241
  workspaceFileTabs,
231
242
  activeSelection,
232
243
  optimisticReadAtBySessionKey,
244
+ sessionProjectRoot,
245
+ onAddFileToChat,
233
246
  onSelectSession,
234
247
  onSelectFile,
235
248
  onOpenFileViewer,
@@ -242,6 +255,7 @@ export function buildWorkspaceTabsViewModel(
242
255
 
243
256
  const workspacePages = buildWorkspacePageTabs({
244
257
  activeSelection,
258
+ hasSession,
245
259
  onSelectChildSessions,
246
260
  onSelectCronJobs,
247
261
  onSelectOverview,
@@ -317,6 +331,10 @@ export function buildWorkspaceTabsViewModel(
317
331
  ? resolveAlternateWorkspaceFileViewer(file.path, file.previewViewer)
318
332
  : null;
319
333
  const fileTitle = readWorkspaceFileTitle(file);
334
+ const tokenKey = resolveWorkspaceRelativePath({
335
+ path: file.path,
336
+ sessionProjectRoot: sessionProjectRoot ?? null,
337
+ });
320
338
  return {
321
339
  key: `file:${file.key}`,
322
340
  kind: "file" as const,
@@ -336,6 +354,9 @@ export function buildWorkspaceTabsViewModel(
336
354
  onSelect: () => onOpenFileViewer(file.key, alternateViewer),
337
355
  }
338
356
  : null,
357
+ onAddToChat: tokenKey && onAddFileToChat
358
+ ? () => onAddFileToChat({ label: fileTitle, tokenKey })
359
+ : undefined,
339
360
  active:
340
361
  activeSelection?.kind === "file" && activeSelection.file.key === file.key,
341
362
  onSelect: () => onSelectFile(file.key),
@@ -0,0 +1,53 @@
1
+ export const WORKSPACE_TEXT_EXCERPT_MAX_CHARACTERS = 8_000;
2
+
3
+ export type WorkspaceTextExcerpt = {
4
+ path: string;
5
+ label: string;
6
+ excerpt: string;
7
+ startLine: number | null;
8
+ endLine: number | null;
9
+ };
10
+
11
+ function countLineBreaks(value: string): number {
12
+ return value.match(/\n/g)?.length ?? 0;
13
+ }
14
+
15
+ export function buildWorkspaceTextExcerpt(params: {
16
+ path: string;
17
+ label: string;
18
+ selectedText: string;
19
+ sourceText: string;
20
+ sourceStartLine?: number | null;
21
+ }): WorkspaceTextExcerpt | null {
22
+ const {
23
+ label: rawLabel,
24
+ path: rawPath,
25
+ selectedText,
26
+ sourceStartLine = 1,
27
+ sourceText,
28
+ } = params;
29
+ const path = rawPath.trim();
30
+ const label = rawLabel.trim();
31
+ const excerpt = selectedText.trim();
32
+ if (!path || !label || !excerpt) {
33
+ return null;
34
+ }
35
+
36
+ const sourceIndex = sourceText.indexOf(excerpt);
37
+ const hasUniqueSourceMatch = sourceIndex >= 0 && sourceIndex === sourceText.lastIndexOf(excerpt);
38
+ const baseLine = sourceStartLine ?? 1;
39
+ const startLine = hasUniqueSourceMatch
40
+ ? baseLine + countLineBreaks(sourceText.slice(0, sourceIndex))
41
+ : null;
42
+ const endLine = startLine === null
43
+ ? null
44
+ : startLine + countLineBreaks(excerpt);
45
+
46
+ return {
47
+ path,
48
+ label,
49
+ excerpt,
50
+ startLine,
51
+ endLine,
52
+ };
53
+ }
@@ -5,6 +5,7 @@ export { usePresenter } from "./components/providers/chat-presenter.provider";
5
5
  export { ChatPresenter } from "./presenters/chat.presenter";
6
6
  export { ChatDraftIntentManager } from "./managers/chat-draft-intent.manager";
7
7
  export { ChatCompletionNotificationManager } from "./managers/chat-completion-notification.manager";
8
+ export { buildSessionPath } from "./features/session/utils/chat-session-route.utils";
8
9
  export { useChatSessionListStore } from "./stores/chat-session-list.store";
9
10
  export { useChatThreadStore } from "./stores/chat-thread.store";
10
11
  export { useChatMessageLayoutStore } from "./stores/chat-message-layout.store";
@@ -0,0 +1,129 @@
1
+ import { describe, expect, it, vi } from "vitest";
2
+ import { ChatComposerIntentManager } from "@/features/chat/managers/chat-composer-intent.manager";
3
+
4
+ describe("ChatComposerIntentManager", () => {
5
+ it("delivers a file reference only to the targeted composer", () => {
6
+ const manager = new ChatComposerIntentManager();
7
+ const draftListener = vi.fn();
8
+ const sessionListener = vi.fn();
9
+ manager.subscribe(null, draftListener);
10
+ manager.subscribe("session-1", sessionListener);
11
+
12
+ manager.requestFileReference({
13
+ targetSessionKey: null,
14
+ tokenKey: "docs/guide.md",
15
+ label: "guide.md",
16
+ });
17
+
18
+ expect(draftListener).toHaveBeenCalledWith(
19
+ expect.objectContaining({
20
+ targetSessionKey: null,
21
+ tokenKey: "docs/guide.md",
22
+ label: "guide.md",
23
+ }),
24
+ );
25
+ expect(sessionListener).not.toHaveBeenCalled();
26
+ });
27
+
28
+ it("keeps an intent pending until its matching composer mounts", () => {
29
+ const manager = new ChatComposerIntentManager();
30
+ manager.requestFileReference({
31
+ targetSessionKey: " session-1 ",
32
+ tokenKey: " src/index.ts ",
33
+ label: " index.ts ",
34
+ });
35
+
36
+ expect(manager.consumePending(null)).toBeNull();
37
+ expect(manager.consumePending("session-1")).toMatchObject({
38
+ targetSessionKey: "session-1",
39
+ tokenKey: "src/index.ts",
40
+ label: "index.ts",
41
+ });
42
+ expect(manager.consumePending("session-1")).toBeNull();
43
+ });
44
+
45
+ it("ignores incomplete file references", () => {
46
+ const manager = new ChatComposerIntentManager();
47
+ const listener = vi.fn();
48
+ manager.subscribe(null, listener);
49
+
50
+ manager.requestFileReference({
51
+ targetSessionKey: null,
52
+ tokenKey: " ",
53
+ label: "README.md",
54
+ });
55
+
56
+ expect(listener).not.toHaveBeenCalled();
57
+ expect(manager.consumePending(null)).toBeNull();
58
+ });
59
+
60
+ it("publishes a source-aware excerpt with a stable content identity", () => {
61
+ const manager = new ChatComposerIntentManager();
62
+ const listener = vi.fn();
63
+ manager.subscribe("session-1", listener);
64
+
65
+ manager.requestExcerptReference({
66
+ targetSessionKey: "session-1",
67
+ path: "docs/guide.md",
68
+ label: "guide.md",
69
+ excerpt: "selected text",
70
+ startLine: 12,
71
+ endLine: 13,
72
+ });
73
+
74
+ expect(listener).toHaveBeenCalledWith(expect.objectContaining({
75
+ kind: "workspace_excerpt",
76
+ path: "docs/guide.md",
77
+ label: "guide.md",
78
+ excerpt: "selected text",
79
+ startLine: 12,
80
+ endLine: 13,
81
+ tokenKey: expect.stringMatching(/^docs\/guide\.md#excerpt-/),
82
+ }));
83
+ });
84
+
85
+ it("delivers an excerpt to the unmaterialized new-chat composer", () => {
86
+ const manager = new ChatComposerIntentManager();
87
+ const draftListener = vi.fn();
88
+ manager.subscribe(null, draftListener);
89
+
90
+ manager.requestExcerptReference({
91
+ targetSessionKey: null,
92
+ path: "package.json",
93
+ label: "package.json",
94
+ excerpt: '"name": "nextclaw"',
95
+ startLine: 2,
96
+ endLine: 2,
97
+ });
98
+
99
+ expect(draftListener).toHaveBeenCalledWith(expect.objectContaining({
100
+ targetSessionKey: null,
101
+ kind: "workspace_excerpt",
102
+ path: "package.json",
103
+ excerpt: '"name": "nextclaw"',
104
+ }));
105
+ });
106
+
107
+ it("publishes a conversation excerpt with its source message identity", () => {
108
+ const manager = new ChatComposerIntentManager();
109
+ const listener = vi.fn();
110
+ manager.subscribe("session-1", listener);
111
+
112
+ manager.requestConversationExcerptReference({
113
+ targetSessionKey: "session-1",
114
+ messageId: "assistant-message-1",
115
+ role: "assistant",
116
+ label: "AI reply",
117
+ excerpt: "Keep the visible tag concise.",
118
+ });
119
+
120
+ expect(listener).toHaveBeenCalledWith(expect.objectContaining({
121
+ kind: "conversation_excerpt",
122
+ messageId: "assistant-message-1",
123
+ role: "assistant",
124
+ label: "AI reply",
125
+ excerpt: "Keep the visible tag concise.",
126
+ tokenKey: expect.stringMatching(/^assistant-message-1#excerpt-/),
127
+ }));
128
+ });
129
+ });
@@ -55,6 +55,7 @@ function resetChatSessionListManagerState() {
55
55
  useChatThreadStore.setState({
56
56
  snapshot: {
57
57
  ...useChatThreadStore.getState().snapshot,
58
+ draftProjectRoot: null,
58
59
  workspacePanelParentKey: "session-1",
59
60
  activeWorkspacePanelKind: "child-session",
60
61
  activeChildSessionKey: "child-session-1",
@@ -121,7 +122,7 @@ describe("ChatSessionListManager draft and selection flow", () => {
121
122
  } as unknown as ConstructorParameters<typeof ChatSessionListManager>[0];
122
123
 
123
124
  const manager = new ChatSessionListManager(uiManager);
124
- manager.startAgentDraftChat("researcher", "codex");
125
+ manager.startAgentDraftChat("researcher", "codex", "Create an agent");
125
126
 
126
127
  expect(uiManager.navigateTo).toHaveBeenCalledWith("/chat/draft", {
127
128
  replace: true,
@@ -129,7 +130,7 @@ describe("ChatSessionListManager draft and selection flow", () => {
129
130
  chatDraft: {
130
131
  sessionType: "codex",
131
132
  projectRoot: null,
132
- prompt: null,
133
+ prompt: "Create an agent",
133
134
  },
134
135
  },
135
136
  });
@@ -169,6 +170,41 @@ describe("ChatSessionListManager draft and selection flow", () => {
169
170
  },
170
171
  },
171
172
  });
173
+ expect(useChatThreadStore.getState().snapshot.draftProjectRoot).toBe(
174
+ "/tmp/project-alpha",
175
+ );
176
+ });
177
+
178
+ it("drops file tabs from an abandoned draft while preserving real session tabs", () => {
179
+ useChatThreadStore.getState().setSnapshot({
180
+ workspaceFileTabs: [
181
+ {
182
+ key: "draft::preview::old.md",
183
+ parentSessionKey: null,
184
+ path: "old.md",
185
+ viewMode: "preview",
186
+ },
187
+ {
188
+ key: "session-1::preview::kept.md",
189
+ parentSessionKey: "session-1",
190
+ path: "kept.md",
191
+ viewMode: "preview",
192
+ },
193
+ ],
194
+ });
195
+ const manager = new ChatSessionListManager({
196
+ navigateTo: vi.fn(),
197
+ isAtChatRoot: vi.fn(() => true),
198
+ } as unknown as ConstructorParameters<typeof ChatSessionListManager>[0]);
199
+
200
+ manager.createSession({ projectRoot: "/tmp/new-project" });
201
+
202
+ expect(useChatThreadStore.getState().snapshot.workspaceFileTabs).toEqual([
203
+ expect.objectContaining({
204
+ key: "session-1::preview::kept.md",
205
+ parentSessionKey: "session-1",
206
+ }),
207
+ ]);
172
208
  });
173
209
 
174
210
  it("carries an initial prompt through the draft route state", () => {