@nextclaw/ui 0.15.22 → 0.15.23

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 (423) hide show
  1. package/CHANGELOG.md +49 -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-Glw2OGXK.js +13 -0
  5. package/dist/assets/appearance-settings-page-Bl_W_9m7.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-C7E4Sxwd.js +8 -0
  13. package/dist/assets/chat-page-BT6dNI-4.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-BgBsUmp1.js → config-split-page-D5BgtKrm.js} +1 -1
  55. package/dist/assets/confirm-dialog-Bf907Azu.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-BwuyBPJb.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-CtLyEJeO.js +1 -0
  68. package/dist/assets/dist-dv_SUR54.js +1 -0
  69. package/dist/assets/doc-browser-C23NDGh5.js +1 -0
  70. package/dist/assets/doc-browser-DlOOhBxI.js +1 -0
  71. package/dist/assets/doc-browser-context-BIBjkwfV.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-CgjYZUzM.js → form-actions-DAzi68Cn.js} +1 -1
  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-BLNg2yg0.css +1 -0
  83. package/dist/assets/index-D3EKjEyK.js +107 -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-BSg5u0r4.js +1 -0
  92. package/dist/assets/{map-BAAdHTqR.js → map-CcZzEoz6.js} +1 -1
  93. package/dist/assets/mcp-marketplace-page-ByhvQQj5.js +9 -0
  94. package/dist/assets/mcp-marketplace-page-CytXwnUs.js +1 -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-bgEx3MjQ.js +1 -0
  99. package/dist/assets/navigation-link-CB0OQbnY.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-CNjWdLhZ.js +1 -0
  106. package/dist/assets/provider-scoped-model-input-8F6JMd2n.js +1 -0
  107. package/dist/assets/providers-config-page-CxpTjR81.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-Bk2ydNTe.js +8 -0
  116. package/dist/assets/{refresh-cw-CkKwPztT.js → refresh-cw-Ba0LZou4.js} +1 -1
  117. package/dist/assets/remote-Dv38PuMV.js +1 -0
  118. package/dist/assets/{requirementDiagram-TGXJPOKE-D8YV0-Jz.js → requirementDiagram-TGXJPOKE-CyVUzEX5.js} +1 -1
  119. package/dist/assets/rotate-cw-IkYVk9JX.js +1 -0
  120. package/dist/assets/runtime-config-page-C0ByyUVr.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-DTOAvqzt.js} +1 -1
  123. package/dist/assets/{search-7M38azRp.js → search-D4X5DNMe.js} +1 -1
  124. package/dist/assets/search-config-page-Cr6254K5.js +1 -0
  125. package/dist/assets/{secrets-config-page-2mVejXf2.js → secrets-config-page-W2Hx332d.js} +2 -2
  126. package/dist/assets/security-config-1E8OF4kR.js +1 -0
  127. package/dist/assets/select-CDv0g2Pj.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-CoxzNABp.js} +1 -1
  130. package/dist/assets/{sizeCapture-X5ZJPWSS-9UIfya9-.js → sizeCapture-X5ZJPWSS-SxG_YnMA.js} +1 -1
  131. package/dist/assets/skeleton-DW018fJv.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-CjkZEoS2.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-CavtY1vJ.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-PTXYTEwz.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 +109 -0
  179. package/src/features/chat/features/conversation/hooks/use-chat-composer-reference-intent.ts +46 -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 +84 -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 +17 -0
  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 +23 -0
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +102 -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 +5 -0
  198. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +97 -32
  199. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +1 -0
  200. package/src/features/chat/features/input/utils/chat-input-toolbar.utils.ts +61 -10
  201. package/src/features/chat/features/message/components/__tests__/chat-message-assistant-header.container.test.tsx +149 -0
  202. package/src/features/chat/features/message/components/__tests__/chat-message-inline-editor.test.tsx +59 -0
  203. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +123 -90
  204. package/src/features/chat/features/message/components/__tests__/chat-message-timeline-dividers.test.tsx +43 -0
  205. package/src/features/chat/features/message/components/chat-message-inline-editor.tsx +111 -0
  206. package/src/features/chat/features/message/components/chat-message-list.container.tsx +81 -172
  207. package/src/features/chat/features/message/components/chat-message-timeline-dividers.tsx +75 -0
  208. package/src/features/chat/features/message/hooks/use-chat-inline-token-actions.ts +125 -0
  209. package/src/features/chat/features/message/hooks/use-chat-message-actions.tsx +187 -0
  210. package/src/features/chat/features/message/utils/__tests__/chat-message-action-targets.utils.test.ts +89 -0
  211. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-compaction.utils.test.ts +194 -0
  212. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline-continuation-compaction.utils.test.ts +167 -0
  213. package/src/features/chat/features/message/utils/__tests__/chat-message-timeline.utils.test.ts +175 -1
  214. package/src/features/chat/features/message/utils/__tests__/chat-message.utils.test.ts +26 -0
  215. package/src/features/chat/features/message/utils/chat-message-action-targets.utils.ts +28 -0
  216. package/src/features/chat/features/message/utils/chat-message-part.utils.ts +38 -0
  217. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +1 -0
  218. package/src/features/chat/features/message/utils/chat-message-timeline.utils.ts +201 -7
  219. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-agent-runtime.test.tsx +180 -1
  220. package/src/features/chat/features/ncp/hooks/__tests__/use-ncp-session-conversation.test.tsx +184 -10
  221. package/src/features/chat/features/ncp/hooks/use-hydrated-ncp-agent.integration.test.tsx +57 -0
  222. package/src/features/chat/features/ncp/hooks/use-ncp-session-conversation.ts +90 -1
  223. package/src/features/chat/features/ncp/utils/__tests__/ncp-chat-query-derived.utils.test.ts +110 -0
  224. package/src/features/chat/features/ncp/utils/ncp-chat-query-derived.utils.ts +65 -3
  225. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +17 -0
  226. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +63 -54
  227. package/src/features/chat/features/session/hooks/__tests__/use-selected-session-context-window-indicator.test.tsx +87 -0
  228. package/src/features/chat/features/session/utils/__tests__/ncp-session-adapter.utils.test.ts +19 -0
  229. package/src/features/chat/features/session/utils/chat-context-window-indicator.utils.ts +44 -5
  230. package/src/features/chat/features/session/utils/ncp-session-adapter.utils.ts +10 -0
  231. package/src/features/chat/features/session/utils/ncp-session-context-metadata.utils.ts +42 -4
  232. package/src/features/chat/features/welcome/components/__tests__/chat-welcome.test.tsx +17 -2
  233. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +8 -3
  234. package/src/features/chat/features/welcome/components/chat-welcome.tsx +2 -2
  235. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +22 -6
  236. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +13 -0
  237. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel-content.test.tsx +72 -2
  238. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +21 -1
  239. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-text-selection.test.tsx +75 -0
  240. package/src/features/chat/features/workspace/components/__tests__/workspace-text-selection-menu.test.tsx +102 -0
  241. package/src/features/chat/features/workspace/components/chat-session-workspace-directory-browser.tsx +145 -26
  242. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +42 -18
  243. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +50 -10
  244. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +48 -22
  245. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +13 -2
  246. package/src/features/chat/features/workspace/components/workspace-text-selection-menu.tsx +223 -0
  247. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +80 -0
  248. package/src/features/chat/features/workspace/utils/__tests__/workspace-text-excerpt.utils.test.ts +54 -0
  249. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +47 -0
  250. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +16 -0
  251. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +22 -1
  252. package/src/features/chat/features/workspace/utils/workspace-text-excerpt.utils.ts +53 -0
  253. package/src/features/chat/index.ts +1 -0
  254. package/src/features/chat/managers/__tests__/chat-composer-intent.manager.test.ts +106 -0
  255. package/src/features/chat/managers/__tests__/chat-session-list.manager.test.ts +38 -2
  256. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +93 -1
  257. package/src/features/chat/managers/__tests__/provider-model-catalog-notice.manager.test.ts +57 -0
  258. package/src/features/chat/managers/chat-composer-intent.manager.ts +145 -0
  259. package/src/features/chat/managers/chat-session-list.manager.ts +13 -4
  260. package/src/features/chat/managers/chat-thread.manager.ts +30 -11
  261. package/src/features/chat/managers/provider-model-catalog-notice.manager.ts +121 -0
  262. package/src/features/chat/presenters/chat.presenter.ts +3 -0
  263. package/src/features/chat/stores/__tests__/chat-composer-draft.store.test.ts +55 -0
  264. package/src/features/chat/stores/chat-composer-draft.store.ts +92 -0
  265. package/src/features/chat/stores/chat-thread.store.ts +2 -4
  266. package/src/features/chat/types/chat-input-bar.types.ts +5 -0
  267. package/src/features/chat/types/chat-input.types.ts +5 -0
  268. package/src/features/chat/types/chat-message.types.ts +5 -0
  269. package/src/features/cron/components/__tests__/cron-config.test.tsx +100 -19
  270. package/src/features/cron/components/cron-config.tsx +19 -33
  271. package/src/features/cron/components/cron-job-detail-dialog.tsx +42 -16
  272. package/src/features/cron/components/cron-job-row.tsx +18 -1
  273. package/src/features/inbox/components/inbox-delivery-content.test.tsx +8 -2
  274. package/src/features/inbox/components/inbox-delivery-content.tsx +1 -1
  275. package/src/features/inbox/components/inbox-reader-dialog.test.tsx +10 -4
  276. package/src/features/inbox/components/inbox-reader-dialog.tsx +37 -61
  277. package/src/features/inbox/pages/inbox-page.tsx +90 -98
  278. package/src/features/notifications/components/__tests__/app-notification-toast.test.tsx +28 -6
  279. package/src/features/notifications/components/app-notification-toast.tsx +54 -15
  280. package/src/features/notifications/managers/__tests__/app-notification.manager.test.ts +2 -0
  281. package/src/features/notifications/managers/app-notification.manager.ts +3 -0
  282. package/src/features/panel-apps/utils/panel-app-doc-browser.utils.tsx +1 -0
  283. package/src/features/pwa/managers/__tests__/pwa-shell-theme.manager.test.ts +9 -0
  284. package/src/features/pwa/managers/pwa-shell-theme.manager.ts +1 -0
  285. package/src/features/service-apps/components/__tests__/service-apps-panel.test.tsx +5 -4
  286. package/src/features/service-apps/components/service-app-list-item.tsx +470 -0
  287. package/src/features/service-apps/components/service-apps-panel.tsx +76 -490
  288. package/src/features/settings/components/config/__tests__/provider-models-section.test.tsx +239 -0
  289. package/src/features/settings/components/config/provider-form-detail-pane.tsx +307 -0
  290. package/src/features/settings/components/config/provider-form.tsx +149 -360
  291. package/src/features/settings/components/config/provider-model-list.tsx +293 -0
  292. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +151 -0
  293. package/src/features/settings/components/config/provider-models-section.tsx +178 -149
  294. package/src/features/settings/hooks/__tests__/use-provider-connectivity.test.tsx +98 -0
  295. package/src/features/settings/hooks/__tests__/use-provider-model-suggestions.test.tsx +107 -0
  296. package/src/features/settings/hooks/use-provider-connectivity.ts +132 -0
  297. package/src/features/settings/hooks/use-provider-model-suggestions.ts +51 -0
  298. package/src/features/settings/pages/__tests__/appearance-settings-page.test.tsx +2 -2
  299. package/src/features/settings/pages/__tests__/search-config-page.test.tsx +48 -0
  300. package/src/features/settings/pages/search-config-page.tsx +17 -8
  301. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +42 -0
  302. package/src/features/settings/utils/provider-form-context.utils.ts +2 -0
  303. package/src/features/settings/utils/provider-form-model.utils.ts +25 -0
  304. package/src/features/settings/utils/provider-form-support.utils.ts +21 -5
  305. package/src/index.css +1 -1
  306. package/src/platforms/desktop/types/desktop-update.types.ts +5 -0
  307. package/src/shared/components/actions/__tests__/navigation-link.test.tsx +14 -3
  308. package/src/shared/components/actions/navigation-link.tsx +35 -15
  309. package/src/shared/components/common/__tests__/agent-avatar.test.tsx +3 -2
  310. package/src/shared/components/common/agent-avatar.tsx +3 -3
  311. package/src/shared/components/doc-browser/__tests__/doc-browser.test.tsx +66 -0
  312. package/src/shared/components/doc-browser/doc-browser-panel-parts.tsx +4 -1
  313. package/src/shared/components/doc-browser/doc-browser-renderer.types.ts +1 -0
  314. package/src/shared/components/doc-browser/doc-browser.tsx +9 -0
  315. package/src/shared/components/doc-browser/hooks/use-doc-browser-scroll-restoration.ts +126 -0
  316. package/src/shared/components/settings/settings-page.tsx +1 -1
  317. package/src/shared/components/ui/context-menu/__tests__/context-menu.test.tsx +103 -0
  318. package/src/shared/components/ui/context-menu/context-menu.tsx +263 -0
  319. package/src/shared/components/ui/tab-strip/__tests__/compact-tab-strip.test.tsx +19 -6
  320. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +28 -67
  321. package/src/shared/hooks/use-config.ts +33 -1
  322. package/src/shared/lib/api/__tests__/ncp-session-query-cache.utils.test.ts +26 -0
  323. package/src/shared/lib/api/ncp-session-query-cache.utils.ts +7 -10
  324. package/src/shared/lib/api/ncp-session.types.ts +6 -0
  325. package/src/shared/lib/api/types.ts +36 -1
  326. package/src/shared/lib/api/utils/config.utils.ts +14 -0
  327. package/src/shared/lib/cron/cron-job-view.utils.ts +4 -0
  328. package/src/shared/lib/host-capabilities/__tests__/host-capability.manager.test.ts +26 -0
  329. package/src/shared/lib/host-capabilities/host-capability.manager.ts +28 -0
  330. package/src/shared/lib/i18n/locales/en-US/chat.json +33 -4
  331. package/src/shared/lib/i18n/locales/en-US/core.json +26 -0
  332. package/src/shared/lib/i18n/locales/en-US/doc-browser.json +1 -0
  333. package/src/shared/lib/i18n/locales/en-US/search.json +1 -0
  334. package/src/shared/lib/i18n/locales/zh-CN/chat.json +33 -4
  335. package/src/shared/lib/i18n/locales/zh-CN/core.json +26 -0
  336. package/src/shared/lib/i18n/locales/zh-CN/doc-browser.json +1 -0
  337. package/src/shared/lib/i18n/locales/zh-CN/search.json +1 -0
  338. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +28 -1
  339. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +23 -0
  340. package/src/shared/lib/theme/index.test.ts +11 -0
  341. package/src/shared/lib/theme/index.ts +2 -0
  342. package/dist/assets/api-B1yHQYs-.js +0 -13
  343. package/dist/assets/app-presenter-provider-D3BuuOyZ.css +0 -1
  344. package/dist/assets/app-presenter-provider-uP9waOzv.js +0 -108
  345. package/dist/assets/appearance-settings-page-C9Zq3hP9.js +0 -1
  346. package/dist/assets/architecture-TIHT7OUA-Cii4JyA2.js +0 -1
  347. package/dist/assets/book-open-oSmyVXea.js +0 -1
  348. package/dist/assets/card-CGvJZTZp.js +0 -1
  349. package/dist/assets/channel-BSKkTPe8.js +0 -1
  350. package/dist/assets/channels-list-page-5BTxpQHw.js +0 -8
  351. package/dist/assets/chat-page-jTvli8Jp.js +0 -1
  352. package/dist/assets/chunk-2Q5K7J3B-CDMOR1xZ.js +0 -1
  353. package/dist/assets/chunk-VAUOI2AC-BRvqNW76.js +0 -1
  354. package/dist/assets/chunk-XXDRQBXY-CwX_mqJ3.js +0 -1
  355. package/dist/assets/classDiagram-OUVF2IWQ-RzbGxG77.js +0 -1
  356. package/dist/assets/classDiagram-v2-EOCWNBFH-C_gWB7WT.js +0 -1
  357. package/dist/assets/confirm-dialog-h849QRWO.js +0 -5
  358. package/dist/assets/cynefin-VYW2F7L2-fSLIS0X3.js +0 -1
  359. package/dist/assets/desktop-update-config-BGGHxClR.js +0 -1
  360. package/dist/assets/dist-CxUhurq9.js +0 -41
  361. package/dist/assets/doc-browser-0eL8AX42.js +0 -1
  362. package/dist/assets/doc-browser-DEqE9lsh.js +0 -1
  363. package/dist/assets/doc-browser-context-DkzFIUoK.js +0 -1
  364. package/dist/assets/eventmodeling-45OFAUF4-XVcQGgha.js +0 -1
  365. package/dist/assets/eye-gOWQGt8N.js +0 -1
  366. package/dist/assets/flowDiagram-23GEKE2U-BiFSctqz.js +0 -1
  367. package/dist/assets/gitGraph-TEB2WS4Q-C014uXqS.js +0 -1
  368. package/dist/assets/host-capabilities-B-TtABsP.js +0 -1
  369. package/dist/assets/index-DSAUWCfl.js +0 -2
  370. package/dist/assets/index-G7cBryJ4.css +0 -1
  371. package/dist/assets/info-DKCQHKI2-CQ2qjGhU.js +0 -1
  372. package/dist/assets/loader-circle-BAuycGf6.js +0 -1
  373. package/dist/assets/logo-badge-BNcsDsUM.js +0 -1
  374. package/dist/assets/mcp-marketplace-page-BX39MtPa.js +0 -9
  375. package/dist/assets/mcp-marketplace-page-CkDtOEA4.js +0 -1
  376. package/dist/assets/model-config-page-7MIXg2fQ.js +0 -1
  377. package/dist/assets/navigation-link-B3iFOdxx.js +0 -1
  378. package/dist/assets/packet-7NZHBO7P-6IFyydij.js +0 -1
  379. package/dist/assets/pie-RZYD4A2V-spXQC_wr.js +0 -1
  380. package/dist/assets/plus-yvhFgfal.js +0 -1
  381. package/dist/assets/popover-Cpep_rJe.js +0 -1
  382. package/dist/assets/provider-models-CUrptZ-o.js +0 -1
  383. package/dist/assets/provider-scoped-model-input-CHmOzz7x.js +0 -1
  384. package/dist/assets/providers-config-page-A6J7Y0II.js +0 -1
  385. package/dist/assets/radar-I7S5WNFK-DWVA8lvY.js +0 -1
  386. package/dist/assets/railroad-3IZDKUUU-Ce51u8y1.js +0 -1
  387. package/dist/assets/railroad-abnf-AHOZXSZD-D7i7cqI_.js +0 -1
  388. package/dist/assets/railroad-ebnf-EBAXGLYW-BEg2hcPs.js +0 -1
  389. package/dist/assets/railroad-peg-LSFZ7HO6-CxROSzbi.js +0 -1
  390. package/dist/assets/react-BCJGCjrP.js +0 -8
  391. package/dist/assets/remote-FD-ZfSIn.js +0 -1
  392. package/dist/assets/rotate-cw-DrJMfHhp.js +0 -1
  393. package/dist/assets/runtime-config-page-DwjO-Vs6.js +0 -1
  394. package/dist/assets/search-config-page-T6tMhXbP.js +0 -1
  395. package/dist/assets/security-config-DD5RrjDe.js +0 -1
  396. package/dist/assets/select-BvidP5Oa.js +0 -1
  397. package/dist/assets/skeleton-BEQW2Dva.js +0 -1
  398. package/dist/assets/stateDiagram-v2-6OUMAXLB-Bui4RXQK.js +0 -1
  399. package/dist/assets/swimlanesDiagram-G3AALYLV-DTwUsCBz.js +0 -8
  400. package/dist/assets/tabs-BeLxVmkt.js +0 -1
  401. package/dist/assets/trash-2-CYUskMMN.js +0 -1
  402. package/dist/assets/treeView-QDETBFTQ-D5zsTiuL.js +0 -1
  403. package/dist/assets/treemap-6X3UGDF4-CR3dxqNs.js +0 -1
  404. package/dist/assets/use-config-CxKRXXCl.js +0 -1
  405. package/dist/assets/use-confirm-dialog-HASTvOP2.js +0 -1
  406. package/dist/assets/use-viewport-layout-CJxkjoRz.js +0 -1
  407. package/dist/assets/wardley-OPB4EBWU-LatRT_Lx.js +0 -1
  408. package/dist/assets/x-DdUwjre5.js +0 -1
  409. /package/dist/assets/{array-CAkJT3no.js → array-NADKd9aU.js} +0 -0
  410. /package/dist/assets/{chunk-KEIR6QF5-BgdDoWEH.js → chunk-KEIR6QF5-BuyuzJJL.js} +0 -0
  411. /package/dist/assets/{chunk-Y2CYZVJY-pTllD0P3.js → chunk-Y2CYZVJY-D_B2ZRNt.js} +0 -0
  412. /package/dist/assets/{config-hints-S3g-UVZb.js → config-hints-BcfVUMij.js} +0 -0
  413. /package/dist/assets/{cytoscape.esm-DmURt-5G.js → cytoscape.esm-BANTlgza.js} +0 -0
  414. /package/dist/assets/{defaultLocale-BKy_KP3O.js → defaultLocale-C4_L0aEs.js} +0 -0
  415. /package/dist/assets/{dist-IgNNB7K9.js → dist-DAPEIJbz.js} +0 -0
  416. /package/dist/assets/{graphlib-xw_TBHyH.js → graphlib-ChqXg0dI.js} +0 -0
  417. /package/dist/assets/{init-C9xlLIAu.js → init-D8wZg9xq.js} +0 -0
  418. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CkbBfwAQ.js} +0 -0
  419. /package/dist/assets/{katex-DSy1eD2d.js → katex-TZJFa0J_.js} +0 -0
  420. /package/dist/assets/{middleware-Df-a4x-q.js → middleware-B3k-TSmZ.js} +0 -0
  421. /package/dist/assets/{path-DB0USYHi.js → path-DodfxcX7.js} +0 -0
  422. /package/dist/assets/{rough.esm-B1B52ZQS.js → rough.esm-CUaa1Rbe.js} +0 -0
  423. /package/dist/assets/{xlsx-DfjC_iml.js → xlsx-CxByi7bR.js} +0 -0
@@ -0,0 +1,223 @@
1
+ import {
2
+ useCallback,
3
+ useEffect,
4
+ useRef,
5
+ useState,
6
+ type ReactNode,
7
+ } from "react";
8
+ import { createPortal } from "react-dom";
9
+ import { MessageSquarePlus } from "lucide-react";
10
+ import {
11
+ buildWorkspaceTextExcerpt,
12
+ WORKSPACE_TEXT_EXCERPT_MAX_CHARACTERS,
13
+ type WorkspaceTextExcerpt,
14
+ } from "@/features/chat/features/workspace/utils/workspace-text-excerpt.utils";
15
+ import { t } from "@/shared/lib/i18n";
16
+
17
+ type SelectionMenuState = {
18
+ anchorBottom: number;
19
+ anchorCenter: number;
20
+ anchorTop: number;
21
+ excerpt: WorkspaceTextExcerpt;
22
+ tooLong: boolean;
23
+ };
24
+
25
+ type SelectionMenuPosition = {
26
+ left: number;
27
+ top: number;
28
+ };
29
+
30
+ const SELECTION_MENU_VIEWPORT_GAP = 12;
31
+ const SELECTION_MENU_ANCHOR_GAP = 10;
32
+
33
+ function containsSelection(
34
+ root: HTMLElement,
35
+ selection: Selection,
36
+ ): selection is Selection & { rangeCount: number } {
37
+ if (selection.rangeCount === 0 || selection.isCollapsed) {
38
+ return false;
39
+ }
40
+ const range = selection.getRangeAt(0);
41
+ return root.contains(range.commonAncestorContainer);
42
+ }
43
+
44
+ function getSelectionAnchorRect(range: Range): DOMRect {
45
+ const rects = typeof range.getClientRects === "function"
46
+ ? Array.from(range.getClientRects()).filter((rect) => rect.width > 0 || rect.height > 0)
47
+ : [];
48
+ return rects.at(-1) ?? range.getBoundingClientRect();
49
+ }
50
+
51
+ function clamp(value: number, minimum: number, maximum: number): number {
52
+ return Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
53
+ }
54
+
55
+ export function WorkspaceTextSelectionMenu({
56
+ children,
57
+ fileLabel,
58
+ filePath,
59
+ onAddToChat,
60
+ sourceStartLine,
61
+ sourceText,
62
+ }: {
63
+ children: ReactNode;
64
+ fileLabel: string;
65
+ filePath: string;
66
+ onAddToChat?: (excerpt: WorkspaceTextExcerpt) => void;
67
+ sourceStartLine?: number | null;
68
+ sourceText: string;
69
+ }) {
70
+ const rootRef = useRef<HTMLDivElement>(null);
71
+ const menuButtonRef = useRef<HTMLButtonElement | null>(null);
72
+ const selectionReadFrameRef = useRef<number | null>(null);
73
+ const [menu, setMenu] = useState<SelectionMenuState | null>(null);
74
+ const [menuPosition, setMenuPosition] = useState<SelectionMenuPosition | null>(null);
75
+
76
+ const readSelection = useCallback(() => {
77
+ const root = rootRef.current;
78
+ const selection = window.getSelection();
79
+ if (!root || !selection || !containsSelection(root, selection)) {
80
+ setMenu(null);
81
+ return;
82
+ }
83
+ const excerpt = buildWorkspaceTextExcerpt({
84
+ path: filePath,
85
+ label: fileLabel,
86
+ selectedText: selection.toString(),
87
+ sourceText,
88
+ sourceStartLine,
89
+ });
90
+ if (!excerpt) {
91
+ setMenu(null);
92
+ return;
93
+ }
94
+ const rect = getSelectionAnchorRect(selection.getRangeAt(0));
95
+ setMenuPosition(null);
96
+ setMenu({
97
+ anchorBottom: rect.bottom,
98
+ anchorCenter: rect.left + rect.width / 2,
99
+ anchorTop: rect.top,
100
+ excerpt,
101
+ tooLong: excerpt.excerpt.length > WORKSPACE_TEXT_EXCERPT_MAX_CHARACTERS,
102
+ });
103
+ }, [fileLabel, filePath, sourceStartLine, sourceText]);
104
+
105
+ const scheduleSelectionRead = useCallback(() => {
106
+ if (selectionReadFrameRef.current !== null) {
107
+ window.cancelAnimationFrame(selectionReadFrameRef.current);
108
+ }
109
+ selectionReadFrameRef.current = window.requestAnimationFrame(() => {
110
+ selectionReadFrameRef.current = null;
111
+ readSelection();
112
+ });
113
+ }, [readSelection]);
114
+
115
+ const captureMenuButton = useCallback((button: HTMLButtonElement | null) => {
116
+ menuButtonRef.current = button;
117
+ if (!menu || !button) {
118
+ return;
119
+ }
120
+ const buttonRect = button.getBoundingClientRect();
121
+ const left = clamp(
122
+ menu.anchorCenter - buttonRect.width / 2,
123
+ SELECTION_MENU_VIEWPORT_GAP,
124
+ window.innerWidth - SELECTION_MENU_VIEWPORT_GAP - buttonRect.width,
125
+ );
126
+ const belowTop = menu.anchorBottom + SELECTION_MENU_ANCHOR_GAP;
127
+ const aboveTop = menu.anchorTop - SELECTION_MENU_ANCHOR_GAP - buttonRect.height;
128
+ const fitsBelow = belowTop + buttonRect.height <= window.innerHeight - SELECTION_MENU_VIEWPORT_GAP;
129
+ const preferredTop = fitsBelow ? belowTop : aboveTop;
130
+ const top = clamp(
131
+ preferredTop,
132
+ SELECTION_MENU_VIEWPORT_GAP,
133
+ window.innerHeight - SELECTION_MENU_VIEWPORT_GAP - buttonRect.height,
134
+ );
135
+ setMenuPosition({ left, top });
136
+ }, [menu]);
137
+
138
+ useEffect(() => {
139
+ const handleSelectionChange = () => {
140
+ const selection = window.getSelection();
141
+ if (!selection || selection.isCollapsed) {
142
+ setMenu(null);
143
+ return;
144
+ }
145
+ scheduleSelectionRead();
146
+ };
147
+ const handlePointerUp = (event: PointerEvent) => {
148
+ if (menuButtonRef.current?.contains(event.target as Node)) {
149
+ return;
150
+ }
151
+ scheduleSelectionRead();
152
+ };
153
+ const handleKeyDown = (event: KeyboardEvent) => {
154
+ if (event.key === "Escape") {
155
+ setMenu(null);
156
+ }
157
+ };
158
+ const closeMenu = () => setMenu(null);
159
+ document.addEventListener("selectionchange", handleSelectionChange);
160
+ document.addEventListener("pointerup", handlePointerUp, true);
161
+ document.addEventListener("keydown", handleKeyDown);
162
+ document.addEventListener("scroll", closeMenu, true);
163
+ window.addEventListener("resize", closeMenu);
164
+ return () => {
165
+ document.removeEventListener("selectionchange", handleSelectionChange);
166
+ document.removeEventListener("pointerup", handlePointerUp, true);
167
+ document.removeEventListener("keydown", handleKeyDown);
168
+ document.removeEventListener("scroll", closeMenu, true);
169
+ window.removeEventListener("resize", closeMenu);
170
+ if (selectionReadFrameRef.current !== null) {
171
+ window.cancelAnimationFrame(selectionReadFrameRef.current);
172
+ }
173
+ };
174
+ }, [scheduleSelectionRead]);
175
+
176
+ const addSelectionToChat = () => {
177
+ if (!menu || menu.tooLong) {
178
+ return;
179
+ }
180
+ onAddToChat?.(menu.excerpt);
181
+ window.getSelection()?.removeAllRanges();
182
+ setMenu(null);
183
+ };
184
+
185
+ const menuNode = menu && onAddToChat
186
+ ? createPortal(
187
+ <button
188
+ ref={captureMenuButton}
189
+ type="button"
190
+ className="fixed z-[var(--z-popover,10100)] inline-flex max-w-[calc(100vw-24px)] items-center gap-1.5 overflow-hidden whitespace-nowrap rounded-xl border border-border bg-popover px-3 py-2 text-xs font-medium text-popover-foreground shadow-lg transition-colors hover:bg-[var(--interaction-hover)] hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary/30 disabled:cursor-not-allowed disabled:text-muted-foreground"
191
+ style={{
192
+ left: menuPosition?.left ?? 0,
193
+ top: menuPosition?.top ?? 0,
194
+ visibility: menuPosition ? "visible" : "hidden",
195
+ }}
196
+ disabled={menu.tooLong}
197
+ onMouseDown={(event) => event.preventDefault()}
198
+ onClick={addSelectionToChat}
199
+ >
200
+ <MessageSquarePlus aria-hidden="true" className="h-3.5 w-3.5 shrink-0" />
201
+ <span className="truncate">
202
+ {menu.tooLong
203
+ ? t("chatWorkspaceExcerptSelectionTooLong")
204
+ : t("chatWorkspaceAddToChat")}
205
+ </span>
206
+ </button>,
207
+ document.body,
208
+ )
209
+ : null;
210
+
211
+ return (
212
+ <div
213
+ ref={rootRef}
214
+ className="h-full min-h-0"
215
+ data-workspace-text-selection-root="true"
216
+ onMouseUp={scheduleSelectionRead}
217
+ onKeyUp={scheduleSelectionRead}
218
+ >
219
+ {children}
220
+ {menuNode}
221
+ </div>
222
+ );
223
+ }
@@ -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";