@nextclaw/ui 0.15.4 → 0.15.6

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 (300) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/assets/abnfDiagram-VRR7QNED-BYpK0F5F.js +1 -0
  3. package/dist/assets/{aiden0z-pptx-renderer.es-Bd99vut2.js → aiden0z-pptx-renderer.es-D87ywFdC.js} +1 -1
  4. package/dist/assets/api-Dyb5rgh0.js +13 -0
  5. package/dist/assets/app-presenter-provider-7jV0nPiL.js +106 -0
  6. package/dist/assets/{appearance-settings-page-fLIJbx1C.js → appearance-settings-page-DFm3BbEb.js} +1 -1
  7. package/dist/assets/arc-DFELHWM2.js +1 -0
  8. package/dist/assets/architecture-TIHT7OUA-ClwVFq-K.js +1 -0
  9. package/dist/assets/architectureDiagram-ZJ3FMSHR-CvRhdovL.js +36 -0
  10. package/dist/assets/array-BqYdCIE6.js +1 -0
  11. package/dist/assets/blockDiagram-677ZJIJ3-DVYNkbR_.js +132 -0
  12. package/dist/assets/{book-open-DWUjS6aW.js → book-open-L6LCuqOr.js} +1 -1
  13. package/dist/assets/c4Diagram-LMCZKHZV-B1w-iwRe.js +10 -0
  14. package/dist/assets/channel-BEHOSFc5.js +1 -0
  15. package/dist/assets/{channels-list-page-CvCTJraa.js → channels-list-page-DOaPKNJA.js} +2 -2
  16. package/dist/assets/{chat-page-B42UUIFR.js → chat-page-Cw9uDhD_.js} +1 -1
  17. package/dist/assets/chunk-2Q5K7J3B-DrIhesSX.js +1 -0
  18. package/dist/assets/chunk-32BRIVSS-kG3lg0bC.js +1 -0
  19. package/dist/assets/chunk-52WLFC77-BLeB7iGk.js +10 -0
  20. package/dist/assets/chunk-5FCAYU7R-BQga1Rr7.js +1 -0
  21. package/dist/assets/chunk-5HE753X5-DU81hkcF.js +1 -0
  22. package/dist/assets/chunk-5JV3BV7I-C2bAx0m7.js +1 -0
  23. package/dist/assets/chunk-5TONJI2A-BExIIgWY.js +2 -0
  24. package/dist/assets/chunk-5VM5RSS4-C3XB05yh.js +15 -0
  25. package/dist/assets/chunk-7BUUIJ7U-JiyRtrGu.js +1 -0
  26. package/dist/assets/chunk-BIQX33UG-BZRV20pI.js +1 -0
  27. package/dist/assets/chunk-C7G6YPKG-AsyDSQ9j.js +1 -0
  28. package/dist/assets/chunk-CQNSW5MT-iN-fiXVA.js +1 -0
  29. package/dist/assets/chunk-CYSBUYHQ-DhC_TRzI.js +1 -0
  30. package/dist/assets/chunk-EMLP6XTP-QFLK8R6d.js +1 -0
  31. package/dist/assets/chunk-EX3LRPZG-DzUwhkCz.js +231 -0
  32. package/dist/assets/chunk-FWX5IMBZ-DFelpZBy.js +2 -0
  33. package/dist/assets/chunk-HOUHSVGY-BMPwFEwN.js +1 -0
  34. package/dist/assets/chunk-ICXQ74PX-DH8NJMok.js +2 -0
  35. package/dist/assets/chunk-JG7HCLWE-D-95AsO4.js +2 -0
  36. package/dist/assets/chunk-JWPE2WC7-DIaTv8kx.js +1 -0
  37. package/dist/assets/chunk-KEIR6QF5-A3jEqwzn.js +161 -0
  38. package/dist/assets/chunk-MOJQB5TN-BPFLzWgj.js +88 -0
  39. package/dist/assets/chunk-MOZMSUNE-DR_fzxG0.js +1 -0
  40. package/dist/assets/chunk-OGEWGWER-TIcEoPPH.js +1 -0
  41. package/dist/assets/chunk-OSBZ3O6U-BALNvSfG.js +1 -0
  42. package/dist/assets/chunk-PUDLZKDR-I-EXhf_k.js +156 -0
  43. package/dist/assets/chunk-Q4XR5HBZ-uvIGstBS.js +70 -0
  44. package/dist/assets/chunk-QBLGF6JB-r-TjLdxa.js +1 -0
  45. package/dist/assets/chunk-R7FJI6CG-N-1LDgvy.js +1 -0
  46. package/dist/assets/chunk-RYQCIY6F-jxNASx65.js +1 -0
  47. package/dist/assets/chunk-U6XO7XAA-Cphbuc7q.js +2 -0
  48. package/dist/assets/chunk-V7JOEXUC-BLEuBf1s.js +206 -0
  49. package/dist/assets/chunk-VAUOI2AC-Dpcz6BhT.js +1 -0
  50. package/dist/assets/chunk-VR4S4FIN-BAUbR0ke.js +1 -0
  51. package/dist/assets/chunk-WYO6CB5R-Do6SJrnY.js +127 -0
  52. package/dist/assets/chunk-XXDRQBXY-C2QjjwYX.js +1 -0
  53. package/dist/assets/chunk-Y2CYZVJY-BGrd8tLy.js +1 -0
  54. package/dist/assets/chunk-YOTPTUD7-CQqvMTCr.js +1 -0
  55. package/dist/assets/chunk-ZGVPDNZ5-B7kWWaXv.js +62 -0
  56. package/dist/assets/chunk-ZIRB5QZD-DTU0cDNh.js +32 -0
  57. package/dist/assets/classDiagram-OUVF2IWQ-DESMaY3_.js +1 -0
  58. package/dist/assets/classDiagram-v2-EOCWNBFH-DwOlByYH.js +1 -0
  59. package/dist/assets/{config-split-page-DBgt9S8b.js → config-split-page-CqlqjnCK.js} +1 -1
  60. package/dist/assets/{confirm-dialog-GSXIvoRU.js → confirm-dialog-bAp8NU93.js} +1 -1
  61. package/dist/assets/cose-bilkent-JH36ORCC-B9LrtV-q.js +1 -0
  62. package/dist/assets/cynefin-VYW2F7L2-CzjeeOq9.js +1 -0
  63. package/dist/assets/cynefinDiagram-TSTJHNR4-BfjvW4su.js +62 -0
  64. package/dist/assets/cytoscape.esm-CRW8q92n.js +321 -0
  65. package/dist/assets/dagre-DkiXZKkL.js +1 -0
  66. package/dist/assets/dagre-VKFMJZFB-B1pQKQSY.js +4 -0
  67. package/dist/assets/defaultLocale-Cdv9x6q7.js +1 -0
  68. package/dist/assets/desktop-update-config-2SjMfCMl.js +1 -0
  69. package/dist/assets/diagram-FQU43EPY-cB4EZdaP.js +3 -0
  70. package/dist/assets/diagram-G47NLZAW-CtW8im1B.js +24 -0
  71. package/dist/assets/diagram-NH7WQ7WH-BDCtrCCm.js +24 -0
  72. package/dist/assets/diagram-OA4YK3LP-DEMJ42Gt.js +30 -0
  73. package/dist/assets/diagram-WEI45ONY-CdCcxSYU.js +41 -0
  74. package/dist/assets/{dist-DC7f4TJH.js → dist-CrtbGLlh.js} +1 -1
  75. package/dist/assets/dist-dw-gAfM_.js +1 -0
  76. package/dist/assets/doc-browser-A5IRyDXQ2.js +1 -0
  77. package/dist/assets/{doc-browser-D0monHjG.js → doc-browser-BmnQa30-.js} +1 -1
  78. package/dist/assets/{doc-browser-context-CGwEOhBy.js → doc-browser-context-BlpUUFrR.js} +1 -1
  79. package/dist/assets/{docx-preview-eFH9DRsS.js → docx-preview-zis3U0Fa.js} +1 -1
  80. package/dist/assets/{download-DiP9zx-J.js → download-BhZZNXD8.js} +1 -1
  81. package/dist/assets/ebnfDiagram-CCIWWBDH-DBZETBwE.js +1 -0
  82. package/dist/assets/erDiagram-Q63AITRT-y2zDXprP.js +85 -0
  83. package/dist/assets/eventmodeling-45OFAUF4-C38BqG8w.js +1 -0
  84. package/dist/assets/{external-link-C6ZeSPef.js → external-link-B9Uoq5J7.js} +1 -1
  85. package/dist/assets/{eye-suvKmtwn.js → eye-K5knauEM.js} +1 -1
  86. package/dist/assets/flowDiagram-23GEKE2U-BoKk8RRq.js +1 -0
  87. package/dist/assets/{folder-CMELbQGG.js → folder-BIYZZWJv.js} +1 -1
  88. package/dist/assets/{form-actions-CUAzpNe_.js → form-actions-CKprWFeM.js} +1 -1
  89. package/dist/assets/ganttDiagram-NO4QXBWP-D2DP344e.js +292 -0
  90. package/dist/assets/gitGraph-TEB2WS4Q-IsQHHJuS.js +1 -0
  91. package/dist/assets/gitGraphDiagram-IHSO6WYX-D37uiwA2.js +106 -0
  92. package/dist/assets/graphlib-BcNFQr5y.js +1 -0
  93. package/dist/assets/index-BVn702m4.css +1 -0
  94. package/dist/assets/index-CFxJ1Fkg.js +2 -0
  95. package/dist/assets/info-DKCQHKI2-ofy2qlHI.js +1 -0
  96. package/dist/assets/infoDiagram-FWYZ7A6U-CG-WAIRi.js +2 -0
  97. package/dist/assets/init-D8TjIqfm.js +1 -0
  98. package/dist/assets/ishikawaDiagram-FXEZZL3T-WeCTnW0f.js +70 -0
  99. package/dist/assets/journeyDiagram-5HDEW3XC-DUFBW9Td.js +139 -0
  100. package/dist/assets/kanban-definition-HUTT4EX6-B0lxgMpg.js +89 -0
  101. package/dist/assets/katex-wEmrEFTg.js +257 -0
  102. package/dist/assets/line-B78frP_c.js +1 -0
  103. package/dist/assets/linear-C6lVEcb2.js +1 -0
  104. package/dist/assets/loader-circle-Bj2RMrCR.js +1 -0
  105. package/dist/assets/{logo-badge-Cu5UGH_4.js → logo-badge-BQ-XFZod.js} +1 -1
  106. package/dist/assets/map-Bdri5UL5.js +1 -0
  107. package/dist/assets/mcp-marketplace-page-BfA7A3n4.js +9 -0
  108. package/dist/assets/mcp-marketplace-page-CXHYO95j.js +1 -0
  109. package/dist/assets/mermaid-parser.core-Cue18vLo.js +4 -0
  110. package/dist/assets/mermaid.core-CXwWRh5i.js +11 -0
  111. package/dist/assets/mindmap-definition-LN4V7U3C-CFxz0P0-.js +96 -0
  112. package/dist/assets/{model-config-page-CWP6pyBm.js → model-config-page-BbWbW_qM.js} +1 -1
  113. package/dist/assets/ordinal-Cbd8t4LB.js +1 -0
  114. package/dist/assets/packet-7NZHBO7P-Rfopfa1o.js +1 -0
  115. package/dist/assets/path-DzBnMI1r.js +1 -0
  116. package/dist/assets/pegDiagram-2B236MQR-B_Qt9zyT.js +1 -0
  117. package/dist/assets/pie-RZYD4A2V-CJR1xKEv.js +1 -0
  118. package/dist/assets/pieDiagram-ENE6RG2P-CRQdSuwS.js +39 -0
  119. package/dist/assets/plus-TmFdwO3P.js +1 -0
  120. package/dist/assets/{popover-DImguRok.js → popover-CX9zFsuc.js} +1 -1
  121. package/dist/assets/preload-helper-D4M6sveU.js +1 -0
  122. package/dist/assets/{provider-scoped-model-input-CE7p4SbJ.js → provider-scoped-model-input-B-9_DtIP.js} +1 -1
  123. package/dist/assets/providers-config-page-xwIjGur2.js +1 -0
  124. package/dist/assets/quadrantDiagram-ABIIQ3AL-D5kPpttq.js +7 -0
  125. package/dist/assets/radar-I7S5WNFK-CDg8Z-1r.js +1 -0
  126. package/dist/assets/railroad-3IZDKUUU-BHnNmjxO.js +1 -0
  127. package/dist/assets/railroad-abnf-AHOZXSZD-DGtNcusW.js +1 -0
  128. package/dist/assets/railroad-ebnf-EBAXGLYW-BRFtsQmm.js +1 -0
  129. package/dist/assets/railroad-peg-LSFZ7HO6-Bj5IypCA.js +1 -0
  130. package/dist/assets/railroadDiagram-RFXS5EU6-DGh_TGlQ.js +1 -0
  131. package/dist/assets/{react-5sjAT_6d.js → react-DsgudwpT.js} +1 -1
  132. package/dist/assets/{refresh-cw-CHLBzWWj.js → refresh-cw-kXdvbhFf.js} +1 -1
  133. package/dist/assets/remote-B9F2uCk9.js +1 -0
  134. package/dist/assets/requirementDiagram-TGXJPOKE-EmFph0H_.js +84 -0
  135. package/dist/assets/rotate-cw-DEc6aihr.js +1 -0
  136. package/dist/assets/rough.esm-wJ1d3nQl.js +1 -0
  137. package/dist/assets/runtime-config-page-DTsi7czJ.js +1 -0
  138. package/dist/assets/sankeyDiagram-HTMAVEWB-BXWB5Ugi.js +40 -0
  139. package/dist/assets/{save-BABzyrjB.js → save-C9qpz2_2.js} +1 -1
  140. package/dist/assets/{search-BTqxG3ZG.js → search-_kEi29qo.js} +1 -1
  141. package/dist/assets/{search-config-page-BkKKmxmS.js → search-config-page-H7ug8f4D.js} +1 -1
  142. package/dist/assets/{secrets-config-page-CWJMyu-k.js → secrets-config-page-C1_6rINu.js} +2 -2
  143. package/dist/assets/security-config-Cr2ERKLu.js +1 -0
  144. package/dist/assets/{select-TYZDBNmq.js → select-MJKhKAWR.js} +1 -1
  145. package/dist/assets/sequenceDiagram-DBY2YBRQ-ClKnO7v0.js +162 -0
  146. package/dist/assets/{setting-row-DR23Z_5I.js → setting-row-DA3tJuew.js} +1 -1
  147. package/dist/assets/{settings-2-D8WWcBnw.js → settings-2-D_y7zXJz.js} +1 -1
  148. package/dist/assets/sizeCapture-X5ZJPWSS-DSo-t5ou.js +1 -0
  149. package/dist/assets/{skeleton-CloHeg0S.js → skeleton-0uTLUhm9.js} +1 -1
  150. package/dist/assets/src-Dad2ZbCt.js +1 -0
  151. package/dist/assets/stateDiagram-2N3HPSRC-BvAalBfz.js +1 -0
  152. package/dist/assets/stateDiagram-v2-6OUMAXLB-CfMU38yc.js +1 -0
  153. package/dist/assets/swimlanes-5IMT3BWC-DdVqVa1a.js +2 -0
  154. package/dist/assets/swimlanesDiagram-G3AALYLV-D9Zb9X57.js +8 -0
  155. package/dist/assets/{tabs-CX-ckWoo.js → tabs-h44XPocd.js} +1 -1
  156. package/dist/assets/{tag-chip-mCc23Rhb.js → tag-chip-bRZ04q3C.js} +1 -1
  157. package/dist/assets/timeline-definition-FHXFAJF6-D4ar8IQY.js +120 -0
  158. package/dist/assets/{trash-2-SAW1Z4Po.js → trash-2-BRpSE-Wt.js} +1 -1
  159. package/dist/assets/treeView-QDETBFTQ-Uy7M0WvH.js +1 -0
  160. package/dist/assets/treemap-6X3UGDF4-BJfGV7KX.js +1 -0
  161. package/dist/assets/use-config-CwDEAl6f.js +1 -0
  162. package/dist/assets/{use-confirm-dialog-C95F6sT1.js → use-confirm-dialog-BbnWw-8c.js} +1 -1
  163. package/dist/assets/{use-viewport-layout-C6m3Nte7.js → use-viewport-layout-CqgIMDQL.js} +1 -1
  164. package/dist/assets/vennDiagram-L72KCM5P-CDXJ_i3p.js +34 -0
  165. package/dist/assets/wardley-OPB4EBWU-q263iF3K.js +1 -0
  166. package/dist/assets/wardleyDiagram-EHGQE667-D-dSuT_7.js +78 -0
  167. package/dist/assets/x-DdEDL6ju.js +1 -0
  168. package/dist/assets/xychartDiagram-FW5EYKEG-C9JCzsZF.js +7 -0
  169. package/dist/index.html +33 -32
  170. package/package.json +8 -8
  171. package/src/features/chat/components/chat-popover-content.tsx +1 -26
  172. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +84 -1
  173. package/src/features/chat/components/conversation/chat-conversation-header-section.tsx +13 -2
  174. package/src/features/chat/components/conversation/chat-conversation-workspace-section.tsx +1 -0
  175. package/src/features/chat/components/layout/__tests__/chat-sidebar-test.utils.ts +22 -0
  176. package/src/features/chat/components/layout/__tests__/chat-sidebar.test.tsx +13 -24
  177. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +16 -4
  178. package/src/features/chat/components/layout/chat-sidebar.tsx +38 -2
  179. package/src/features/chat/features/conversation/components/__tests__/session-conversation-area.test.tsx +1 -0
  180. package/src/features/chat/features/conversation/components/__tests__/session-conversation-input.streaming.test.tsx +52 -2
  181. package/src/features/chat/features/conversation/components/session-conversation-area.tsx +22 -0
  182. package/src/features/chat/features/conversation/components/session-conversation-input.tsx +36 -20
  183. package/src/features/chat/features/conversation/hooks/__tests__/use-session-conversation-input-state.test.tsx +55 -0
  184. package/src/features/chat/features/conversation/hooks/use-session-conversation-input-state.ts +12 -11
  185. package/src/features/chat/features/conversation/hooks/use-session-conversation-preference-actions.ts +94 -0
  186. package/src/features/chat/features/input/hooks/__tests__/use-chat-input-surface-state.test.tsx +37 -0
  187. package/src/features/chat/features/input/hooks/use-chat-input-surface-state.ts +140 -63
  188. package/src/features/chat/features/input/input-surface-plugins/__tests__/context-reference-plugin.test.ts +175 -0
  189. package/src/features/chat/features/input/input-surface-plugins/__tests__/slash-command-plugin.test.ts +66 -0
  190. package/src/features/chat/features/input/input-surface-plugins/chat-input-product-plugin-adapters.types.ts +8 -1
  191. package/src/features/chat/features/input/input-surface-plugins/context-reference-plugin.utils.ts +274 -0
  192. package/src/features/chat/features/input/input-surface-plugins/slash-command-plugin.utils.ts +18 -11
  193. package/src/features/chat/features/input/utils/__tests__/chat-composer-state.utils.test.ts +26 -0
  194. package/src/features/chat/features/input/utils/__tests__/chat-inline-token.utils.test.ts +50 -4
  195. package/src/features/chat/features/input/utils/__tests__/chat-input-bar.utils.test.ts +51 -0
  196. package/src/features/chat/features/input/utils/chat-composer-state.utils.ts +4 -10
  197. package/src/features/chat/features/input/utils/chat-composer-token-protocol.utils.ts +29 -0
  198. package/src/features/chat/features/input/utils/chat-inline-token.utils.ts +66 -8
  199. package/src/features/chat/features/input/utils/chat-input-bar.utils.ts +62 -21
  200. package/src/features/chat/features/message/components/__tests__/chat-message-list.container.test.tsx +42 -10
  201. package/src/features/chat/features/message/components/chat-inline-file-preview.tsx +80 -0
  202. package/src/features/chat/features/message/components/chat-message-list.container.tsx +55 -60
  203. package/src/features/chat/features/message/utils/chat-message-texts.utils.ts +2 -0
  204. package/src/features/chat/features/ncp/hooks/__tests__/use-hydrated-ncp-agent.test.tsx +41 -3
  205. package/src/features/chat/features/project/components/__tests__/chat-project-create-dialog.test.tsx +67 -0
  206. package/src/features/chat/features/project/components/chat-project-create-dialog.tsx +203 -0
  207. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-header-actions.test.tsx +7 -7
  208. package/src/features/chat/features/session/components/session-header/__tests__/chat-session-project-dialog.test.tsx +47 -0
  209. package/src/features/chat/features/session/components/session-header/chat-session-header-actions.tsx +6 -3
  210. package/src/features/chat/features/session/components/session-header/chat-session-project-badge.tsx +3 -0
  211. package/src/features/chat/features/session/components/session-header/chat-session-project-dialog.tsx +3 -0
  212. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +245 -0
  213. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +81 -127
  214. package/src/features/chat/features/session/utils/__tests__/chat-sidebar-session-groups.utils.test.ts +22 -0
  215. package/src/features/chat/features/session/utils/chat-run-metadata.utils.ts +6 -6
  216. package/src/features/chat/features/session/utils/chat-sidebar-session-groups.utils.ts +12 -1
  217. package/src/features/chat/features/welcome/components/chat-conversation-welcome.tsx +3 -0
  218. package/src/features/chat/features/welcome/components/chat-welcome.tsx +1 -0
  219. package/src/features/chat/features/welcome/utils/__tests__/chat-welcome-project-options.utils.test.ts +20 -0
  220. package/src/features/chat/features/welcome/utils/chat-welcome-project-options.utils.ts +15 -2
  221. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-file-preview.test.tsx +22 -24
  222. package/src/features/chat/features/workspace/components/__tests__/chat-session-workspace-panel.test.tsx +2 -1
  223. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +6 -17
  224. package/src/features/chat/features/workspace/components/chat-session-workspace-file-preview.tsx +41 -19
  225. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-nav.tsx +5 -1
  226. package/src/features/chat/features/workspace/components/chat-session-workspace-panel.tsx +5 -1
  227. package/src/features/chat/features/workspace/utils/__tests__/chat-workspace-panel-view-model.utils.test.ts +104 -3
  228. package/src/features/chat/features/workspace/utils/chat-thread-workspace-session.utils.ts +83 -0
  229. package/src/features/chat/features/workspace/utils/chat-workspace-file-tab.utils.ts +4 -1
  230. package/src/features/chat/features/workspace/utils/chat-workspace-panel-view-model.utils.ts +96 -25
  231. package/src/features/chat/managers/__tests__/chat-thread-workspace.manager.test.ts +25 -0
  232. package/src/features/chat/managers/__tests__/chat-thread.manager.test.ts +5 -1
  233. package/src/features/chat/managers/chat-session-list.manager.ts +1 -0
  234. package/src/features/chat/managers/chat-thread.manager.ts +15 -37
  235. package/src/features/chat/pages/__tests__/ncp-chat-page.test.ts +23 -0
  236. package/src/features/chat/stores/__tests__/chat-thread.store.test.ts +9 -0
  237. package/src/features/chat/stores/chat-thread.store.ts +16 -36
  238. package/src/features/chat/types/chat-input-bar.types.ts +2 -0
  239. package/src/features/settings/components/config/provider-form.tsx +0 -4
  240. package/src/features/settings/utils/__tests__/provider-form-support.utils.test.ts +7 -9
  241. package/src/features/settings/utils/provider-form-model.utils.ts +0 -4
  242. package/src/features/system-status/hooks/use-current-version-release-notes-link.ts +33 -0
  243. package/src/features/system-status/index.ts +9 -0
  244. package/src/features/system-status/utils/update-release-notes.utils.ts +44 -3
  245. package/src/index.css +10 -16
  246. package/src/shared/components/common/__tests__/brand-header.test.tsx +127 -10
  247. package/src/shared/components/common/brand-header.tsx +142 -56
  248. package/src/shared/components/path-picker/__tests__/server-path-picker-dialog.test.tsx +288 -38
  249. package/src/shared/components/path-picker/server-path-picker-dialog.tsx +290 -199
  250. package/src/shared/components/path-picker/server-path-picker-directory-list.tsx +103 -0
  251. package/src/shared/components/path-picker/server-path-picker-footer.tsx +58 -0
  252. package/src/shared/components/path-picker/server-path-picker-locations.tsx +148 -0
  253. package/src/shared/components/path-picker/server-path-picker-navigation-bar.tsx +198 -0
  254. package/src/shared/components/path-picker/server-path-picker-new-folder.tsx +59 -0
  255. package/src/shared/components/path-picker/server-path-picker-toolbar.tsx +25 -0
  256. package/src/shared/hooks/use-projects.ts +28 -0
  257. package/src/shared/hooks/use-server-path-create-directory.ts +6 -0
  258. package/src/shared/hooks/use-server-path-read.ts +10 -5
  259. package/src/shared/hooks/use-server-path-search.ts +40 -0
  260. package/src/shared/lib/api/index.ts +1 -0
  261. package/src/shared/lib/api/projects/project.utils.ts +14 -0
  262. package/src/shared/lib/api/types.ts +30 -3
  263. package/src/shared/lib/api/utils/server-path.utils.ts +22 -1
  264. package/src/shared/lib/i18n/locales/en-US/chat.json +43 -2
  265. package/src/shared/lib/i18n/locales/en-US/core.json +2 -3
  266. package/src/shared/lib/i18n/locales/en-US/desktop-update.json +4 -0
  267. package/src/shared/lib/i18n/locales/en-US/path-picker.json +25 -0
  268. package/src/shared/lib/i18n/locales/zh-CN/chat.json +43 -2
  269. package/src/shared/lib/i18n/locales/zh-CN/core.json +2 -3
  270. package/src/shared/lib/i18n/locales/zh-CN/desktop-update.json +4 -0
  271. package/src/shared/lib/i18n/locales/zh-CN/path-picker.json +25 -0
  272. package/src/shared/lib/session-project/__tests__/workspace-file-breadcrumb.test.ts +1 -4
  273. package/src/shared/lib/session-project/workspace-file-breadcrumb.utils.ts +1 -18
  274. package/dist/assets/api-BOXOrpcJ.js +0 -13
  275. package/dist/assets/app-presenter-provider-CkHLbvZQ.js +0 -106
  276. package/dist/assets/desktop-update-config-7bmNeWIC.js +0 -1
  277. package/dist/assets/doc-browser-CgdoLUT92.js +0 -1
  278. package/dist/assets/index-BFeNDQ4R.css +0 -1
  279. package/dist/assets/index-D_6U-M94.js +0 -2
  280. package/dist/assets/loader-circle-tYHStefV.js +0 -1
  281. package/dist/assets/mcp-marketplace-page-BUzHDwW6.js +0 -9
  282. package/dist/assets/mcp-marketplace-page-ohUAdfqJ.js +0 -1
  283. package/dist/assets/plus-tywizkeY.js +0 -1
  284. package/dist/assets/providers-config-page-BKPo8SjH.js +0 -1
  285. package/dist/assets/remote-DXJryUxB.js +0 -1
  286. package/dist/assets/rotate-cw-BefO5brU.js +0 -1
  287. package/dist/assets/runtime-config-page-1Axv4QGu.js +0 -1
  288. package/dist/assets/security-config-DeQUIXfi.js +0 -1
  289. package/dist/assets/use-config-BucdaK23.js +0 -1
  290. package/dist/assets/x-DtCPHwCu.js +0 -1
  291. package/src/features/chat/components/__tests__/chat-popover-content.test.tsx +0 -41
  292. package/src/features/chat/components/layout/__tests__/chat-sidebar-utility-menu.test.tsx +0 -55
  293. package/src/features/chat/features/input/input-surface-plugins/__tests__/panel-app-reference-plugin.test.ts +0 -77
  294. package/src/features/chat/features/input/input-surface-plugins/panel-app-reference-plugin.utils.ts +0 -144
  295. /package/dist/assets/{config-hints-fDrYfl0l.js → config-hints-LxG80Uj5.js} +0 -0
  296. /package/dist/assets/{host-capabilities-zZvY9U63.js → host-capabilities-CFbAfrrA.js} +0 -0
  297. /package/dist/assets/{jszip.min-Das2IAnb.js → jszip.min-CP5uPNQa.js} +0 -0
  298. /package/dist/assets/{middleware-BLMHv9YF.js → middleware-CAXxl-af.js} +0 -0
  299. /package/dist/assets/{provider-models-C_yOh6DE.js → provider-models-BoUhFo9R.js} +0 -0
  300. /package/dist/assets/{xlsx-B6zVt1RK.js → xlsx-DUqJhg5u.js} +0 -0
@@ -15,7 +15,8 @@ type ChatSessionHeaderActionsProps = {
15
15
  sessionKey: string;
16
16
  canDeleteSession: boolean;
17
17
  isDeletePending: boolean;
18
- projectRoot?: string | null;
18
+ currentPath?: string | null;
19
+ defaultWorkspacePath?: string | null;
19
20
  metadata?: Record<string, unknown> | null;
20
21
  isWorkspaceOpen: boolean;
21
22
  onToggleWorkspace: () => void;
@@ -26,7 +27,8 @@ export function ChatSessionHeaderActions({
26
27
  sessionKey,
27
28
  canDeleteSession,
28
29
  isDeletePending,
29
- projectRoot,
30
+ currentPath,
31
+ defaultWorkspacePath,
30
32
  metadata,
31
33
  isWorkspaceOpen,
32
34
  onToggleWorkspace,
@@ -119,7 +121,8 @@ export function ChatSessionHeaderActions({
119
121
 
120
122
  <ChatSessionProjectDialog
121
123
  open={isDialogOpen}
122
- currentProjectRoot={projectRoot}
124
+ currentProjectRoot={currentPath}
125
+ defaultWorkspacePath={defaultWorkspacePath}
123
126
  isSaving={isProjectPending}
124
127
  onOpenChange={setIsDialogOpen}
125
128
  onSave={runProjectUpdate}
@@ -15,6 +15,7 @@ type ChatSessionProjectBadgeProps = {
15
15
  sessionKey: string;
16
16
  projectName: string;
17
17
  projectRoot?: string | null;
18
+ defaultWorkspacePath?: string | null;
18
19
  persistToServer: boolean;
19
20
  };
20
21
 
@@ -26,6 +27,7 @@ export function ChatSessionProjectBadge({
26
27
  sessionKey,
27
28
  projectName,
28
29
  projectRoot,
30
+ defaultWorkspacePath,
29
31
  persistToServer,
30
32
  }: ChatSessionProjectBadgeProps) {
31
33
  const updateSessionProject = useChatSessionProject();
@@ -106,6 +108,7 @@ export function ChatSessionProjectBadge({
106
108
  <ChatSessionProjectDialog
107
109
  open={isDialogOpen}
108
110
  currentProjectRoot={projectRoot}
111
+ defaultWorkspacePath={defaultWorkspacePath}
109
112
  isSaving={isProjectPending}
110
113
  onOpenChange={setIsDialogOpen}
111
114
  onSave={runProjectUpdate}
@@ -4,6 +4,7 @@ import { t } from '@/shared/lib/i18n';
4
4
  type ChatSessionProjectDialogProps = {
5
5
  open: boolean;
6
6
  currentProjectRoot?: string | null;
7
+ defaultWorkspacePath?: string | null;
7
8
  isSaving: boolean;
8
9
  onOpenChange: (open: boolean) => void;
9
10
  onSave: (projectRoot: string) => Promise<void> | void;
@@ -12,6 +13,7 @@ type ChatSessionProjectDialogProps = {
12
13
  export function ChatSessionProjectDialog({
13
14
  open,
14
15
  currentProjectRoot,
16
+ defaultWorkspacePath,
15
17
  isSaving,
16
18
  onOpenChange,
17
19
  onSave,
@@ -20,6 +22,7 @@ export function ChatSessionProjectDialog({
20
22
  <ServerPathPickerDialog
21
23
  open={open}
22
24
  currentPath={currentProjectRoot}
25
+ defaultWorkspacePath={defaultWorkspacePath}
23
26
  isSaving={isSaving}
24
27
  onOpenChange={onOpenChange}
25
28
  onConfirm={onSave}
@@ -0,0 +1,245 @@
1
+ import { useId } from "react";
2
+ import { Check, ChevronDown, ChevronRight, Folder } from "lucide-react";
3
+ import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
4
+ import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
5
+ import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
6
+ import type {
7
+ ChatSidebarDateGroup,
8
+ ChatSidebarProjectGroup,
9
+ } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
10
+ import {
11
+ formatSessionListTime,
12
+ sessionActivityPreviewText,
13
+ sessionDisplayName,
14
+ } from "@/features/chat/features/session/utils/chat-session-display.utils";
15
+ import { resolveSessionContextView } from "@/features/chat/features/session/utils/session-context.utils";
16
+ import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
17
+ import { shouldShowUnreadSessionIndicator } from "@/features/chat/stores/chat-session-list.store";
18
+ import { t } from "@/shared/lib/i18n";
19
+ import { cn } from "@/shared/lib/utils";
20
+
21
+ type SessionSwitchItemsProps = {
22
+ items: NcpSessionListItemView[];
23
+ selectedSessionKey: string | null;
24
+ optimisticReadAtBySessionKey: Record<string, string>;
25
+ sessionTypeOptions: ChatSessionTypeOption[];
26
+ onSelect: (sessionKey: string) => void;
27
+ className?: string;
28
+ };
29
+
30
+ function ChatSessionSwitchItem({
31
+ item,
32
+ selectedSessionKey,
33
+ optimisticReadAtBySessionKey,
34
+ sessionTypeOptions,
35
+ onSelect,
36
+ }: Omit<SessionSwitchItemsProps, "items" | "className"> & {
37
+ item: NcpSessionListItemView;
38
+ }) {
39
+ const { session, runStatus } = item;
40
+ const active = selectedSessionKey === session.key;
41
+ const optimisticReadAt = optimisticReadAtBySessionKey[session.key];
42
+ const effectiveReadAt =
43
+ optimisticReadAt && session.readAt
44
+ ? optimisticReadAt.localeCompare(session.readAt) > 0
45
+ ? optimisticReadAt
46
+ : session.readAt
47
+ : (optimisticReadAt ?? session.readAt);
48
+ const shouldShowUnread = shouldShowUnreadSessionIndicator({
49
+ active,
50
+ lastMessageAt: session.lastMessageAt,
51
+ readAt: effectiveReadAt,
52
+ runStatus,
53
+ });
54
+ const previewText =
55
+ sessionActivityPreviewText(session) ??
56
+ session.projectName ??
57
+ session.projectRoot ??
58
+ `${session.messageCount}`;
59
+ const trailingText = formatSessionListTime(
60
+ session.lastMessageAt ?? session.createdAt,
61
+ );
62
+ const sessionContext = resolveSessionContextView(session, sessionTypeOptions);
63
+
64
+ return (
65
+ <button
66
+ type="button"
67
+ aria-current={active ? "page" : undefined}
68
+ className={cn(
69
+ "flex w-full min-w-0 items-start gap-2 rounded-lg px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
70
+ active
71
+ ? "bg-accent text-accent-foreground"
72
+ : "text-foreground hover:bg-accent/65 hover:text-accent-foreground",
73
+ )}
74
+ onClick={() => onSelect(session.key)}
75
+ >
76
+ <span className="relative mt-1 flex h-2.5 w-2.5 shrink-0 items-center justify-center">
77
+ {shouldShowUnread ? (
78
+ <span
79
+ aria-label={t("chatSidebarUnread")}
80
+ className="h-1.5 w-1.5 rounded-full bg-primary"
81
+ />
82
+ ) : active ? (
83
+ <Check className="h-3 w-3 text-primary" />
84
+ ) : (
85
+ <span className="h-1 w-1 rounded-full bg-muted-foreground/45" />
86
+ )}
87
+ </span>
88
+ <span className="min-w-0 flex-1">
89
+ <span className="flex min-w-0 items-center gap-2">
90
+ <span className="min-w-0 flex-1 truncate text-xs font-medium">
91
+ {sessionDisplayName(session)}
92
+ </span>
93
+ {sessionContext.label ? (
94
+ <span className="shrink-0 rounded-full border border-border/70 px-1.5 py-0.5 text-[10px] font-semibold leading-none text-muted-foreground">
95
+ {sessionContext.label}
96
+ </span>
97
+ ) : null}
98
+ {sessionContext.icon ? (
99
+ <span className="inline-flex h-[1.125rem] w-[1.125rem] shrink-0 items-center justify-center text-muted-foreground">
100
+ <SessionContextIconNode icon={sessionContext.icon} />
101
+ </span>
102
+ ) : null}
103
+ </span>
104
+ <span className="mt-0.5 flex min-w-0 items-center gap-2 text-[11px] text-muted-foreground">
105
+ <span className="min-w-0 truncate">{previewText}</span>
106
+ <span className="shrink-0">{trailingText}</span>
107
+ </span>
108
+ </span>
109
+ {runStatus ? (
110
+ <span className="mt-0.5 shrink-0">
111
+ <SessionRunBadge status={runStatus} />
112
+ </span>
113
+ ) : null}
114
+ </button>
115
+ );
116
+ }
117
+
118
+ function ChatSessionSwitchItems({
119
+ items,
120
+ selectedSessionKey,
121
+ optimisticReadAtBySessionKey,
122
+ sessionTypeOptions,
123
+ onSelect,
124
+ className,
125
+ }: SessionSwitchItemsProps) {
126
+ return (
127
+ <div className={cn("space-y-1", className)}>
128
+ {items.map((item) => (
129
+ <ChatSessionSwitchItem
130
+ key={item.session.key}
131
+ item={item}
132
+ selectedSessionKey={selectedSessionKey}
133
+ optimisticReadAtBySessionKey={optimisticReadAtBySessionKey}
134
+ sessionTypeOptions={sessionTypeOptions}
135
+ onSelect={onSelect}
136
+ />
137
+ ))}
138
+ </div>
139
+ );
140
+ }
141
+
142
+ function ChatSessionSwitchDateGroup({
143
+ group,
144
+ ...itemsProps
145
+ }: Omit<SessionSwitchItemsProps, "items"> & {
146
+ group: ChatSidebarDateGroup;
147
+ }) {
148
+ return (
149
+ <section aria-label={group.label}>
150
+ <div className="px-2 py-1 text-[11px] font-medium uppercase tracking-wider text-muted-foreground/70">
151
+ {group.label}
152
+ </div>
153
+ <ChatSessionSwitchItems items={group.items} {...itemsProps} />
154
+ </section>
155
+ );
156
+ }
157
+
158
+ function ChatSessionSwitchProjectGroup({
159
+ group,
160
+ isCollapsed,
161
+ onToggleCollapsed,
162
+ ...itemsProps
163
+ }: Omit<SessionSwitchItemsProps, "items"> & {
164
+ group: ChatSidebarProjectGroup;
165
+ isCollapsed: boolean;
166
+ onToggleCollapsed: (projectRoot: string) => void;
167
+ }) {
168
+ const contentId = useId();
169
+ const actionLabel = `${t(
170
+ isCollapsed ? "chatSidebarExpandProject" : "chatSidebarCollapseProject",
171
+ )} · ${group.projectName}`;
172
+
173
+ return (
174
+ <section aria-label={group.projectName}>
175
+ <button
176
+ type="button"
177
+ aria-controls={contentId}
178
+ aria-expanded={!isCollapsed}
179
+ aria-label={actionLabel}
180
+ className="group flex h-10 w-full min-w-0 items-center gap-1.5 rounded-lg px-2 text-left text-muted-foreground transition-colors hover:bg-gray-200/60 hover:text-gray-900 focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border"
181
+ onClick={() => onToggleCollapsed(group.projectRoot)}
182
+ >
183
+ <Folder className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
184
+ <span
185
+ className="min-w-0 flex-1 truncate text-[11px] font-medium uppercase tracking-wider"
186
+ title={group.projectRoot}
187
+ >
188
+ {group.projectName}
189
+ </span>
190
+ <span className="shrink-0 text-[10px] text-muted-foreground/70">
191
+ {group.items.length}
192
+ </span>
193
+ {isCollapsed ? (
194
+ <ChevronRight className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
195
+ ) : (
196
+ <ChevronDown className="h-3.5 w-3.5 shrink-0" aria-hidden="true" />
197
+ )}
198
+ </button>
199
+ {isCollapsed ? null : (
200
+ <ChatSessionSwitchItems
201
+ items={group.items}
202
+ className="mt-0.5"
203
+ {...itemsProps}
204
+ />
205
+ )}
206
+ </section>
207
+ );
208
+ }
209
+
210
+ export function ChatSessionSwitcherGroups({
211
+ collapsedProjectRoots,
212
+ dateGroups,
213
+ isProjectFirstView,
214
+ onToggleProjectCollapsed,
215
+ projectGroups,
216
+ ...itemsProps
217
+ }: Omit<SessionSwitchItemsProps, "items"> & {
218
+ collapsedProjectRoots: string[];
219
+ dateGroups: ChatSidebarDateGroup[];
220
+ isProjectFirstView: boolean;
221
+ onToggleProjectCollapsed: (projectRoot: string) => void;
222
+ projectGroups: ChatSidebarProjectGroup[];
223
+ }) {
224
+ return (
225
+ <div className="space-y-3">
226
+ {isProjectFirstView
227
+ ? projectGroups.map((group) => (
228
+ <ChatSessionSwitchProjectGroup
229
+ key={group.projectRoot}
230
+ group={group}
231
+ isCollapsed={collapsedProjectRoots.includes(group.projectRoot)}
232
+ onToggleCollapsed={onToggleProjectCollapsed}
233
+ {...itemsProps}
234
+ />
235
+ ))
236
+ : dateGroups.map((group) => (
237
+ <ChatSessionSwitchDateGroup
238
+ key={group.label}
239
+ group={group}
240
+ {...itemsProps}
241
+ />
242
+ ))}
243
+ </div>
244
+ );
245
+ }
@@ -1,33 +1,21 @@
1
1
  import { useMemo, useState } from "react";
2
2
  import {
3
- Check,
4
3
  ChevronDown,
5
4
  Loader2,
6
5
  MessageSquareText,
7
6
  Search,
8
7
  } from "lucide-react";
9
8
  import { usePresenter } from "@/features/chat/components/providers/chat-presenter.provider";
9
+ import { useNcpSessionListView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
10
10
  import {
11
- useNcpSessionListView,
12
- type NcpSessionListItemView,
13
- } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
14
- import { SessionContextIconNode } from "@/features/chat/features/session/components/session-context-icon";
15
- import { SessionRunBadge } from "@/features/chat/features/session/components/session-run-badge";
16
- import {
17
- formatSessionListTime,
18
- sessionActivityPreviewText,
19
- sessionDisplayName,
20
- } from "@/features/chat/features/session/utils/chat-session-display.utils";
21
- import { sortSessionItemsByActivityAtDesc } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
22
- import { resolveSessionContextView } from "@/features/chat/features/session/utils/session-context.utils";
23
- import {
24
- buildSessionTypeOptions,
25
- type ChatSessionTypeOption,
26
- } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
27
- import {
28
- shouldShowUnreadSessionIndicator,
29
- useChatSessionListStore,
30
- } from "@/features/chat/stores/chat-session-list.store";
11
+ groupSessionsByDate,
12
+ groupSessionsByProject,
13
+ sortSessionItemsByActivityAtDesc,
14
+ } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
15
+ import { ChatSidebarListModeSwitch } from "@/features/chat/components/chat-sidebar-list-mode-switch";
16
+ import { buildSessionTypeOptions } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
17
+ import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
18
+ import { ChatSessionSwitcherGroups } from "@/features/chat/features/session/components/session-header/chat-session-switcher-groups";
31
19
  import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
32
20
  import { useViewportLayoutStore } from "@/app/stores/viewport-layout.store";
33
21
  import {
@@ -38,6 +26,7 @@ import { ChatPopoverContent } from "@/features/chat/components/chat-popover-cont
38
26
  import { Input } from "@/shared/components/ui/input";
39
27
  import { t } from "@/shared/lib/i18n";
40
28
  import { cn } from "@/shared/lib/utils";
29
+ import { useProjects } from "@/shared/hooks/use-projects";
41
30
 
42
31
  const SWITCHER_TRIGGER_CLASS =
43
32
  "group inline-flex min-w-0 max-w-full items-center gap-1.5 rounded-md px-1.5 py-1 text-sm font-medium text-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border";
@@ -68,96 +57,6 @@ function ChatSessionTitleSwitcherLoadingState() {
68
57
  );
69
58
  }
70
59
 
71
- function ChatSessionSwitchItem({
72
- item,
73
- selectedSessionKey,
74
- optimisticReadAtBySessionKey,
75
- sessionTypeOptions,
76
- onSelect,
77
- }: {
78
- item: NcpSessionListItemView;
79
- selectedSessionKey: string | null;
80
- optimisticReadAtBySessionKey: Record<string, string>;
81
- sessionTypeOptions: ChatSessionTypeOption[];
82
- onSelect: (sessionKey: string) => void;
83
- }) {
84
- const { session, runStatus } = item;
85
- const active = selectedSessionKey === session.key;
86
- const optimisticReadAt = optimisticReadAtBySessionKey[session.key];
87
- const effectiveReadAt =
88
- optimisticReadAt && session.readAt
89
- ? optimisticReadAt.localeCompare(session.readAt) > 0
90
- ? optimisticReadAt
91
- : session.readAt
92
- : (optimisticReadAt ?? session.readAt);
93
- const shouldShowUnread = shouldShowUnreadSessionIndicator({
94
- active,
95
- lastMessageAt: session.lastMessageAt,
96
- readAt: effectiveReadAt,
97
- runStatus,
98
- });
99
- const previewText =
100
- sessionActivityPreviewText(session) ??
101
- session.projectName ??
102
- session.projectRoot ??
103
- `${session.messageCount}`;
104
- const trailingText = formatSessionListTime(
105
- session.lastMessageAt ?? session.createdAt,
106
- );
107
- const sessionContext = resolveSessionContextView(session, sessionTypeOptions);
108
-
109
- return (
110
- <button
111
- type="button"
112
- aria-current={active ? "page" : undefined}
113
- className={cn(
114
- "flex w-full min-w-0 items-start gap-2 rounded-lg px-2.5 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border",
115
- active
116
- ? "bg-accent text-accent-foreground"
117
- : "text-popover-foreground hover:bg-accent/70",
118
- )}
119
- onClick={() => onSelect(session.key)}
120
- >
121
- <span className="mt-0.5 flex h-4 w-4 shrink-0 items-center justify-center">
122
- {active ? (
123
- <Check className="h-3.5 w-3.5" />
124
- ) : shouldShowUnread ? (
125
- <span
126
- aria-label={t("chatSessionUnread")}
127
- className="h-2 w-2 rounded-full bg-primary"
128
- />
129
- ) : null}
130
- </span>
131
- <span className="min-w-0 flex-1">
132
- <span className="flex min-w-0 items-center gap-1.5">
133
- <span className="min-w-0 truncate text-[13px] font-medium">
134
- {sessionDisplayName(session)}
135
- </span>
136
- {sessionContext.label ? (
137
- <span className="shrink-0 rounded-full border border-border/70 px-1.5 py-0.5 text-[10px] font-semibold leading-none text-muted-foreground">
138
- {sessionContext.label}
139
- </span>
140
- ) : null}
141
- {sessionContext.icon ? (
142
- <span className="inline-flex h-[1.125rem] w-[1.125rem] shrink-0 items-center justify-center text-muted-foreground">
143
- <SessionContextIconNode icon={sessionContext.icon} />
144
- </span>
145
- ) : null}
146
- </span>
147
- <span className="mt-0.5 flex min-w-0 items-center gap-2 text-[11px] text-muted-foreground">
148
- <span className="min-w-0 truncate">{previewText}</span>
149
- <span className="shrink-0">{trailingText}</span>
150
- </span>
151
- </span>
152
- {runStatus ? (
153
- <span className="mt-0.5 shrink-0">
154
- <SessionRunBadge status={runStatus} />
155
- </span>
156
- ) : null}
157
- </button>
158
- );
159
- }
160
-
161
60
  function ChatSessionTitleSwitcherPopover({
162
61
  selectedSessionKey,
163
62
  title,
@@ -171,14 +70,51 @@ function ChatSessionTitleSwitcherPopover({
171
70
  const optimisticReadAtBySessionKey = useChatSessionListStore(
172
71
  (state) => state.optimisticReadAtBySessionKey,
173
72
  );
73
+ const pinnedSessionKeys = useChatSessionListStore(
74
+ (state) => state.snapshot.pinnedSessionKeys,
75
+ );
76
+ const pinnedProjectRoots = useChatSessionListStore(
77
+ (state) => state.snapshot.pinnedProjectRoots,
78
+ );
79
+ const collapsedProjectRoots = useChatSessionListStore(
80
+ (state) => state.snapshot.collapsedProjectRoots,
81
+ );
82
+ const listMode = useChatSessionListStore(
83
+ (state) => state.snapshot.listMode,
84
+ );
174
85
  const sessionTypesData = useChatQueryStore(
175
86
  (state) => state.snapshot.sessionTypesQuery?.data ?? null,
176
87
  );
177
88
  const { isLoading, items } = useNcpSessionListView({ query: searchQuery });
89
+ const projectsQuery = useProjects();
90
+ const visibleProjects = useMemo(() => {
91
+ const query = searchQuery.trim().toLowerCase();
92
+ const projects = projectsQuery.data?.projects ?? [];
93
+ if (!query) {
94
+ return projects;
95
+ }
96
+ return projects.filter((project) =>
97
+ project.name.toLowerCase().includes(query) ||
98
+ project.rootPath.toLowerCase().includes(query)
99
+ );
100
+ }, [projectsQuery.data?.projects, searchQuery]);
178
101
  const sortedItems = useMemo(
179
102
  () => sortSessionItemsByActivityAtDesc(items),
180
103
  [items],
181
104
  );
105
+ const dateGroups = useMemo(
106
+ () => groupSessionsByDate(sortedItems, new Set(pinnedSessionKeys)),
107
+ [pinnedSessionKeys, sortedItems],
108
+ );
109
+ const projectGroups = useMemo(
110
+ () => groupSessionsByProject(
111
+ sortedItems,
112
+ new Set(pinnedSessionKeys),
113
+ new Set(pinnedProjectRoots),
114
+ visibleProjects,
115
+ ),
116
+ [pinnedProjectRoots, pinnedSessionKeys, sortedItems, visibleProjects],
117
+ );
182
118
  const sessionTypeOptions = useMemo(
183
119
  () => buildSessionTypeOptions(sessionTypesData?.options ?? []),
184
120
  [sessionTypesData?.options],
@@ -198,6 +134,16 @@ function ChatSessionTitleSwitcherPopover({
198
134
  setSearchQuery("");
199
135
  }
200
136
  };
137
+ const sessionListProps = {
138
+ selectedSessionKey,
139
+ optimisticReadAtBySessionKey,
140
+ sessionTypeOptions,
141
+ onSelect: selectSession,
142
+ };
143
+ const isProjectFirstView = listMode === "project-first";
144
+ const hasVisibleGroups = isProjectFirstView
145
+ ? projectGroups.length > 0
146
+ : dateGroups.length > 0;
201
147
 
202
148
  return (
203
149
  <Popover open={isOpen} onOpenChange={updateOpen}>
@@ -222,8 +168,14 @@ function ChatSessionTitleSwitcherPopover({
222
168
  className="w-[22rem] max-w-[calc(100vw-2rem)] p-0"
223
169
  >
224
170
  <div className="space-y-2 border-b border-border px-3 py-2">
225
- <div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground/75">
226
- {t("chatSessionSwitcherTitle")}
171
+ <div className="flex items-center justify-between gap-3">
172
+ <div className="text-[11px] font-medium uppercase tracking-wider text-muted-foreground/75">
173
+ {t("chatSessionSwitcherTitle")}
174
+ </div>
175
+ <ChatSidebarListModeSwitch
176
+ isProjectFirstView={isProjectFirstView}
177
+ onSelectMode={presenter.chatSessionListManager.setListMode}
178
+ />
227
179
  </div>
228
180
  <div className="relative">
229
181
  <Search className="pointer-events-none absolute left-2.5 top-1/2 h-3.5 w-3.5 -translate-y-1/2 text-muted-foreground/65" />
@@ -240,25 +192,27 @@ function ChatSessionTitleSwitcherPopover({
240
192
  <div className="max-h-80 overflow-y-auto p-1.5">
241
193
  {isLoading ? (
242
194
  <ChatSessionTitleSwitcherLoadingState />
243
- ) : sortedItems.length > 0 ? (
244
- <div className="space-y-1">
245
- {sortedItems.map((item) => (
246
- <ChatSessionSwitchItem
247
- key={item.session.key}
248
- item={item}
249
- selectedSessionKey={selectedSessionKey}
250
- optimisticReadAtBySessionKey={optimisticReadAtBySessionKey}
251
- sessionTypeOptions={sessionTypeOptions}
252
- onSelect={selectSession}
253
- />
254
- ))}
255
- </div>
195
+ ) : hasVisibleGroups ? (
196
+ <ChatSessionSwitcherGroups
197
+ collapsedProjectRoots={collapsedProjectRoots}
198
+ dateGroups={dateGroups}
199
+ isProjectFirstView={isProjectFirstView}
200
+ onToggleProjectCollapsed={
201
+ presenter.chatSessionListManager.toggleProjectCollapsed
202
+ }
203
+ projectGroups={projectGroups}
204
+ {...sessionListProps}
205
+ />
256
206
  ) : (
257
207
  <ChatSessionTitleSwitcherEmptyState
258
208
  label={
259
209
  searchQuery.trim()
260
210
  ? t("chatSessionSwitcherNoResults")
261
- : t("sessionsEmpty")
211
+ : t(
212
+ isProjectFirstView
213
+ ? "chatSidebarProjectViewEmpty"
214
+ : "sessionsEmpty",
215
+ )
262
216
  }
263
217
  />
264
218
  )}
@@ -0,0 +1,22 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { groupSessionsByProject } from "@/features/chat/features/session/utils/chat-sidebar-session-groups.utils";
3
+
4
+ describe("groupSessionsByProject", () => {
5
+ it("uses registered projects as the project-first baseline", () => {
6
+ const groups = groupSessionsByProject([], new Set(), new Set(), [{
7
+ name: "Knowledge",
8
+ rootPath: "/tmp/knowledge",
9
+ template: "knowledge-base",
10
+ createdAt: "2026-07-15T00:00:00.000Z",
11
+ updatedAt: "2026-07-15T00:00:00.000Z",
12
+ }]);
13
+
14
+ expect(groups).toEqual([{
15
+ projectRoot: "/tmp/knowledge",
16
+ projectName: "Knowledge",
17
+ items: [],
18
+ latestUpdatedAt: new Date("2026-07-15T00:00:00.000Z").getTime(),
19
+ isPinned: false,
20
+ }]);
21
+ });
22
+ });
@@ -2,7 +2,7 @@ import {
2
2
  CHAT_SESSION_MATERIALIZATION_METADATA_KEY,
3
3
  type AgentRunSessionMaterializationMetadata,
4
4
  } from "@nextclaw/shared";
5
- import { buildInlineSkillTokensFromComposer, CHAT_UI_INLINE_TOKENS_METADATA_KEY } from "@/features/chat/features/input/utils/chat-inline-token.utils";
5
+ import { buildInlineTokensFromComposer, CHAT_INLINE_TOKENS_METADATA_KEY } from "@/features/chat/features/input/utils/chat-inline-token.utils";
6
6
  import { normalizeRequestedSkills } from "@/features/chat/features/runtime/utils/chat-runtime.utils";
7
7
  import { normalizeSessionProjectRootValue } from "@/shared/lib/session-project";
8
8
 
@@ -20,7 +20,7 @@ export function buildChatRunMetadata(payload: {
20
20
  sessionType?: string;
21
21
  projectRoot?: string | null;
22
22
  requestedSkills?: string[];
23
- composerNodes?: Parameters<typeof buildInlineSkillTokensFromComposer>[0];
23
+ composerNodes?: Parameters<typeof buildInlineTokensFromComposer>[0];
24
24
  sessionMaterialization?: AgentRunSessionMaterializationMetadata | null;
25
25
  }): Record<string, unknown> {
26
26
  const projectRoot = normalizeSessionProjectRootValue(payload.projectRoot);
@@ -35,11 +35,11 @@ export function buildChatRunMetadata(payload: {
35
35
  if (requestedSkills.length > 0) {
36
36
  metadata.requested_skill_refs = requestedSkills;
37
37
  }
38
- const inlineSkillTokens = payload.composerNodes
39
- ? buildInlineSkillTokensFromComposer(payload.composerNodes)
38
+ const inlineTokens = payload.composerNodes
39
+ ? buildInlineTokensFromComposer(payload.composerNodes)
40
40
  : [];
41
- if (inlineSkillTokens.length > 0) {
42
- metadata[CHAT_UI_INLINE_TOKENS_METADATA_KEY] = inlineSkillTokens;
41
+ if (inlineTokens.length > 0) {
42
+ metadata[CHAT_INLINE_TOKENS_METADATA_KEY] = inlineTokens;
43
43
  }
44
44
  if (payload.sessionMaterialization) {
45
45
  metadata[CHAT_SESSION_MATERIALIZATION_METADATA_KEY] = payload.sessionMaterialization;
@@ -1,4 +1,4 @@
1
- import type { SessionEntryView } from "@/shared/lib/api";
1
+ import type { ProjectView, SessionEntryView } from "@/shared/lib/api";
2
2
  import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
3
3
  import { getSessionProjectName } from "@/shared/lib/session-project";
4
4
  import { t } from "@/shared/lib/i18n";
@@ -96,9 +96,20 @@ export function groupSessionsByProject(
96
96
  items: NcpSessionListItemView[],
97
97
  pinnedSessionKeys: ReadonlySet<string> = new Set(),
98
98
  pinnedProjectRoots: ReadonlySet<string> = new Set(),
99
+ projects: readonly ProjectView[] = [],
99
100
  ): ChatSidebarProjectGroup[] {
100
101
  const grouped = new Map<string, ChatSidebarProjectGroup>();
101
102
 
103
+ for (const project of projects) {
104
+ grouped.set(project.rootPath, {
105
+ projectRoot: project.rootPath,
106
+ projectName: project.name,
107
+ items: [],
108
+ latestUpdatedAt: new Date(project.updatedAt).getTime(),
109
+ isPinned: pinnedProjectRoots.has(project.rootPath),
110
+ });
111
+ }
112
+
102
113
  for (const item of items) {
103
114
  const projectRoot = item.session.projectRoot?.trim();
104
115
  if (!projectRoot) {