@nextclaw/ui 0.26.3 → 0.26.4

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 (170) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/dist/.vite/manifest.json +329 -329
  3. package/dist/assets/{account-panel-DIdHjKG-.js → account-panel-DVi_jhkG.js} +1 -1
  4. package/dist/assets/{api-DkLJrSCd.js → api-BZvNpFOS.js} +1 -1
  5. package/dist/assets/app-presenter-provider-BcYL-VNe.js +124 -0
  6. package/dist/assets/{appearance-settings-page-DI8aDY2G.js → appearance-settings-page-AZNBzLmT.js} +1 -1
  7. package/dist/assets/{book-open-BPMkKSci.js → book-open-CmRdT3N1.js} +1 -1
  8. package/dist/assets/card-CdRBL1E0.js +1 -0
  9. package/dist/assets/{channels-list-page-CCIaTQu7.js → channels-list-page-eZN74FN3.js} +2 -2
  10. package/dist/assets/{circle-dashed-B48a0t6p.js → circle-dashed-HOgt3gIJ.js} +1 -1
  11. package/dist/assets/{client-imrVphQb.js → client-DKKbdJ6Q.js} +1 -1
  12. package/dist/assets/config-split-page-BA3V6b3Q.js +1 -0
  13. package/dist/assets/{confirm-dialog-10Qb3dpZ.js → confirm-dialog-8vqYZ0wy.js} +1 -1
  14. package/dist/assets/{context-menu-CRqH2SEk.js → context-menu-DHVVdHzN.js} +1 -1
  15. package/dist/assets/{createLucideIcon-B4xj2I2g.js → createLucideIcon-DE0sB9Nu.js} +1 -1
  16. package/dist/assets/{desktop-capabilities-page-CFcT2g8-.js → desktop-capabilities-page-B9-B9k1A.js} +1 -1
  17. package/dist/assets/{desktop-update-config-CRJd_An1.js → desktop-update-config-Cfr3IRUe.js} +1 -1
  18. package/dist/assets/{dialog-BgL3vKJQ.js → dialog-BUfWO8hr.js} +1 -1
  19. package/dist/assets/{doc-browser-context-DH4aWvkS.js → doc-browser-context-ClLgMbxd.js} +1 -1
  20. package/dist/assets/doc-browser-w8q1j0jL.js +1 -0
  21. package/dist/assets/{download-Cr337h4W.js → download-CKqS1Ksj.js} +1 -1
  22. package/dist/assets/{es2015-D-CRf3_Z.js → es2015-BiB05Sav.js} +1 -1
  23. package/dist/assets/{extensions-CsmJPz7g.js → extensions-vPD4dGqt.js} +1 -1
  24. package/dist/assets/{eye-DrQRSAfJ.js → eye-bKzylu3p.js} +1 -1
  25. package/dist/assets/{form-actions-BEeWTGUZ.js → form-actions-CCoxz33Q.js} +1 -1
  26. package/dist/assets/{keyboard-shortcuts-settings-page-DQU56SrW.js → keyboard-shortcuts-settings-page-D8rosOVh.js} +1 -1
  27. package/dist/assets/loader-circle-Clq1Gtb1.js +1 -0
  28. package/dist/assets/{login-page-sgivRYG1.js → login-page-BT3GV3Sb.js} +1 -1
  29. package/dist/assets/login-page-DKId3By8.js +1 -0
  30. package/dist/assets/{main-B-oxUq-o.js → main-bEWCGDHr.js} +2 -2
  31. package/dist/assets/main-kqMRTwOs.css +1 -0
  32. package/dist/assets/mcp-marketplace-page-CZIP6j3L.js +1 -0
  33. package/dist/assets/{model-config-page-CDQBhmVr.js → model-config-page-D21a-BO0.js} +1 -1
  34. package/dist/assets/{navigation-link-D3pzNHeA.js → navigation-link-DC-Pku0Y.js} +1 -1
  35. package/dist/assets/panelStandalone-BRg7B1EI.css +1 -0
  36. package/dist/assets/{panelStandalone-CGkYeMK5.js → panelStandalone-CX8X2hjh.js} +2 -2
  37. package/dist/assets/{privacy-settings-page-CjoAkoDt.js → privacy-settings-page-e0LFHZJm.js} +1 -1
  38. package/dist/assets/{projects-C7Zxpcoi.js → projects-Rki7Fmth.js} +1 -1
  39. package/dist/assets/provider-models-DFQgF4hS.js +1 -0
  40. package/dist/assets/provider-scoped-model-input-DaONL0j_.js +1 -0
  41. package/dist/assets/providers-config-page-CEOhQlMe.js +1 -0
  42. package/dist/assets/{puzzle--RLCeYML.js → puzzle-D_TiFXOv.js} +1 -1
  43. package/dist/assets/{pwa-DcizGowz.js → pwa-C9ac_1dg.js} +1 -1
  44. package/dist/assets/{refresh-cw-syCVj_nw.js → refresh-cw-BG8VfcdA.js} +1 -1
  45. package/dist/assets/remote-DkSx5x4R.js +1 -0
  46. package/dist/assets/{rotate-cw-BlH9El3x.js → rotate-cw-DPEegUU2.js} +1 -1
  47. package/dist/assets/{runtime-config-page-Cco9dS73.js → runtime-config-page-DDT1dVLt.js} +1 -1
  48. package/dist/assets/{save-BWynuRQ2.js → save-CPlctBjT.js} +1 -1
  49. package/dist/assets/{search-config-page-Cc74_SAU.js → search-config-page-CDwmd6eX.js} +1 -1
  50. package/dist/assets/{secrets-config-page-raYDzlFC.js → secrets-config-page-1OvgmwPv.js} +1 -1
  51. package/dist/assets/security-config-Pv4CM-kw.js +1 -0
  52. package/dist/assets/{select-6Uq4chZW.js → select-Y9IEbx9x.js} +1 -1
  53. package/dist/assets/service-apps-panel-DPrLeF3N.js +1 -0
  54. package/dist/assets/{settings-2-COvVf1XX.js → settings-2-CrGx9_De.js} +1 -1
  55. package/dist/assets/{status-dot-DZlxa5zW.js → status-dot-Cb8ewlUr.js} +1 -1
  56. package/dist/assets/{tag-chip-DXHtZ8AS.js → tag-chip-D-6aPuuh.js} +1 -1
  57. package/dist/assets/{theme-provider-C5PCDBPB.js → theme-provider-BjF1Eowc.js} +1 -1
  58. package/dist/assets/{trash-2-DazgBvmJ.js → trash-2-DER2EJKK.js} +1 -1
  59. package/dist/assets/{use-config-DRRGF9Zb.js → use-config-BM5jC65L.js} +1 -1
  60. package/dist/assets/{use-desktop-capabilities-CdfOUjdE.js → use-desktop-capabilities-ezKJwKqg.js} +1 -1
  61. package/dist/assets/{use-viewport-layout-DdxVnCYc.js → use-viewport-layout-vmbQUNT6.js} +1 -1
  62. package/dist/assets/x-DSz9cGhy.js +1 -0
  63. package/dist/index.html +33 -33
  64. package/dist/panel-standalone.html +10 -10
  65. package/package.json +4 -4
  66. package/src/app/components/layout/__tests__/sidebar.layout.test.tsx +2 -2
  67. package/src/app/components/layout/sidebar-rail.styles.ts +3 -3
  68. package/src/app/styles/charcoal-theme.css +0 -3
  69. package/src/app/styles/design-system.css +12 -6
  70. package/src/app/styles/island-theme.css +2 -15
  71. package/src/features/app-data/components/app-data-removal-choice.tsx +1 -1
  72. package/src/features/channels/pages/channels-list-page.tsx +1 -1
  73. package/src/features/chat/components/chat-sidebar-list-mode-switch.tsx +1 -1
  74. package/src/features/chat/components/conversation/__tests__/chat-conversation-header-section.test.tsx +28 -16
  75. package/src/features/chat/components/conversation/chat-conversation-content.tsx +8 -2
  76. package/src/features/chat/components/layout/__tests__/chat-sidebar-session-area.test.tsx +6 -4
  77. package/src/features/chat/components/layout/__tests__/chat-sidebar-toolbar.test.tsx +22 -1
  78. package/src/features/chat/components/layout/chat-sidebar-desktop-layout.tsx +10 -14
  79. package/src/features/chat/components/layout/chat-sidebar-toolbar.tsx +38 -7
  80. package/src/features/chat/components/layout/chat-sidebar-utility-menu.tsx +4 -4
  81. package/src/features/chat/components/layout/chat-sidebar.tsx +2 -2
  82. package/src/features/chat/features/conversation/components/session-queued-input-rows.tsx +1 -1
  83. package/src/features/chat/features/message/components/chat-message-list.container.tsx +12 -1
  84. package/src/features/chat/features/message/components/chat-tool-call-stress-harness.tsx +32 -2
  85. package/src/features/chat/features/message/hooks/__tests__/use-chat-message-virtualizer.test.tsx +7 -5
  86. package/src/features/chat/features/message/hooks/use-chat-message-virtualizer.ts +7 -3
  87. package/src/features/chat/features/session/components/__tests__/chat-sidebar-project-groups.test.tsx +1 -1
  88. package/src/features/chat/features/session/components/chat-mobile-session-row.tsx +1 -1
  89. package/src/features/chat/features/session/components/chat-sidebar-context-card.tsx +1 -1
  90. package/src/features/chat/features/session/components/chat-sidebar-project-groups.tsx +1 -1
  91. package/src/features/chat/features/session/components/chat-sidebar-session-item.tsx +7 -5
  92. package/src/features/chat/features/session/components/chat-sidebar-session-list.tsx +45 -10
  93. package/src/features/chat/features/session/components/session-header/chat-session-more-actions-menu.tsx +3 -0
  94. package/src/features/chat/features/session/components/session-header/chat-session-switcher-groups.tsx +6 -6
  95. package/src/features/chat/features/session/components/session-header/chat-session-title-switcher.tsx +5 -4
  96. package/src/features/chat/features/session-type/components/chat-session-type-option-item.tsx +1 -1
  97. package/src/features/chat/features/welcome/components/chat-welcome-agent-picker.tsx +0 -1
  98. package/src/features/chat/features/welcome/components/chat-welcome-project-picker.tsx +1 -1
  99. package/src/features/chat/features/welcome/components/chat-welcome-prompt-suggestions.tsx +1 -1
  100. package/src/features/chat/features/welcome/components/chat-welcome-session-type-picker.tsx +1 -1
  101. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumb-browser.tsx +2 -2
  102. package/src/features/chat/features/workspace/components/chat-session-workspace-file-breadcrumbs.tsx +2 -2
  103. package/src/features/chat/features/workspace/components/chat-session-workspace-panel-content.tsx +1 -1
  104. package/src/features/chat/features/workspace/components/child-sessions/chat-session-child-sessions.tsx +2 -2
  105. package/src/features/chat/features/workspace/components/file-content-preview/workspace-spreadsheet-preview.tsx +2 -2
  106. package/src/features/chat/features/workspace/components/overview/chat-session-continuous-attention.tsx +3 -3
  107. package/src/features/chat/features/workspace/components/overview/chat-session-token-usage.tsx +1 -1
  108. package/src/features/chat/features/workspace/components/project-files/workspace-directory-tree.tsx +4 -3
  109. package/src/features/chat/features/workspace/components/project-files/workspace-new-folder-tree-item.tsx +2 -1
  110. package/src/features/chat/features/workspace/components/session-cron-job-content.tsx +3 -3
  111. package/src/features/chat/features/workspace/components/workspace-file-content-preview.tsx +2 -2
  112. package/src/features/chat/features/workspace/components/workspace-markdown-outline.tsx +1 -1
  113. package/src/features/cron/components/cron-config.tsx +2 -2
  114. package/src/features/cron/components/cron-job-detail-dialog.tsx +1 -1
  115. package/src/features/cron/components/cron-job-row.tsx +3 -3
  116. package/src/features/cron/components/cron-task-discovery.tsx +1 -1
  117. package/src/features/inbox/pages/inbox-page.tsx +1 -1
  118. package/src/features/marketplace/components/curated-shelves/marketplace-curated-shelves.tsx +2 -2
  119. package/src/features/marketplace/components/curated-shelves/marketplace-shelf-card.tsx +2 -2
  120. package/src/features/marketplace/components/marketplace-list-card.tsx +4 -4
  121. package/src/features/marketplace/components/marketplace-page.tsx +2 -2
  122. package/src/features/marketplace/components/mcp/mcp-marketplace-card.tsx +2 -2
  123. package/src/features/marketplace/components/mcp/mcp-marketplace-page.tsx +3 -3
  124. package/src/features/panel-apps/components/panel-app-list-item.tsx +3 -3
  125. package/src/features/pwa/components/pwa-install-entry.tsx +1 -1
  126. package/src/features/right-panel-resources/components/__tests__/page-resource-sidebar-nav.test.tsx +9 -1
  127. package/src/features/right-panel-resources/components/page-resource-sidebar-nav.tsx +1 -1
  128. package/src/features/right-panel-resources/components/right-panel-resource-home-page.tsx +1 -1
  129. package/src/features/service-apps/components/service-action-row.tsx +3 -3
  130. package/src/features/service-apps/components/service-app-list-item.tsx +4 -4
  131. package/src/features/service-apps/components/service-apps-panel.tsx +1 -1
  132. package/src/features/settings/components/config/provider-model-list.tsx +5 -5
  133. package/src/features/settings/components/config/provider-model-suggestions-panel.tsx +4 -4
  134. package/src/features/settings/components/config/provider-pill-selector.tsx +1 -1
  135. package/src/features/settings/pages/appearance-settings-page.tsx +2 -2
  136. package/src/features/settings/pages/providers-config-page.tsx +6 -6
  137. package/src/features/side-dock/components/side-dock.tsx +4 -4
  138. package/src/index.css +4 -0
  139. package/src/platforms/desktop/components/desktop-window-chrome.tsx +1 -1
  140. package/src/platforms/mobile/components/mobile-bottom-nav.tsx +3 -3
  141. package/src/shared/components/common/brand-header.tsx +1 -1
  142. package/src/shared/components/common/searchable-model-input.tsx +2 -2
  143. package/src/shared/components/config-split-page.tsx +3 -3
  144. package/src/shared/components/doc-browser/doc-browser-tab-strip.tsx +2 -2
  145. package/src/shared/components/path-picker/server-path-picker-locations.tsx +1 -1
  146. package/src/shared/components/ui/button.tsx +2 -2
  147. package/src/shared/components/ui/card.tsx +1 -1
  148. package/src/shared/components/ui/context-menu/context-menu-items.tsx +3 -3
  149. package/src/shared/components/ui/input.tsx +7 -3
  150. package/src/shared/components/ui/overlays/sheet.tsx +1 -1
  151. package/src/shared/components/ui/popover.tsx +1 -1
  152. package/src/shared/components/ui/select.tsx +2 -2
  153. package/src/shared/components/ui/tab-strip/compact-tab-strip.tsx +3 -3
  154. package/src/shared/components/ui/tabs.tsx +1 -1
  155. package/dist/assets/app-presenter-provider-Co8uKOyW.js +0 -124
  156. package/dist/assets/card-Bqu8zWLq.js +0 -1
  157. package/dist/assets/config-split-page-Bd-bGKsw.js +0 -1
  158. package/dist/assets/doc-browser-52t2_rSF.js +0 -1
  159. package/dist/assets/loader-circle-DbOQWT9C.js +0 -1
  160. package/dist/assets/login-page-DW1AZXqX.js +0 -1
  161. package/dist/assets/main-BpL6OjwM.css +0 -1
  162. package/dist/assets/mcp-marketplace-page-DSLRpJEB.js +0 -1
  163. package/dist/assets/panelStandalone-D-mhAGNa.css +0 -1
  164. package/dist/assets/provider-models-YW7rIeOD.js +0 -1
  165. package/dist/assets/provider-scoped-model-input-axJ-pTEu.js +0 -1
  166. package/dist/assets/providers-config-page-_QhBXQ4M.js +0 -1
  167. package/dist/assets/remote-BLQU7aY5.js +0 -1
  168. package/dist/assets/security-config-aE_jOwy-.js +0 -1
  169. package/dist/assets/service-apps-panel-jBWAa4qZ.js +0 -1
  170. package/dist/assets/x-CQIp_nJT.js +0 -1
package/dist/index.html CHANGED
@@ -258,47 +258,47 @@
258
258
  })();
259
259
  </script>
260
260
  <title>NextClaw</title>
261
- <script type="module" crossorigin src="/assets/main-B-oxUq-o.js"></script>
261
+ <script type="module" crossorigin src="/assets/main-bEWCGDHr.js"></script>
262
262
  <link rel="modulepreload" crossorigin href="/assets/chunk-DF37yw5_.js">
263
263
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-ca-nBW7U.js">
264
264
  <link rel="modulepreload" crossorigin href="/assets/i18n-OGmF0Ws_.js">
265
- <link rel="modulepreload" crossorigin href="/assets/api-DkLJrSCd.js">
266
- <link rel="modulepreload" crossorigin href="/assets/client-imrVphQb.js">
267
- <link rel="modulepreload" crossorigin href="/assets/es2015-D-CRf3_Z.js">
268
- <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-B4xj2I2g.js">
269
- <link rel="modulepreload" crossorigin href="/assets/select-6Uq4chZW.js">
270
- <link rel="modulepreload" crossorigin href="/assets/context-menu-CRqH2SEk.js">
271
- <link rel="modulepreload" crossorigin href="/assets/x-CQIp_nJT.js">
272
- <link rel="modulepreload" crossorigin href="/assets/dialog-BgL3vKJQ.js">
265
+ <link rel="modulepreload" crossorigin href="/assets/api-BZvNpFOS.js">
266
+ <link rel="modulepreload" crossorigin href="/assets/client-DKKbdJ6Q.js">
267
+ <link rel="modulepreload" crossorigin href="/assets/es2015-BiB05Sav.js">
268
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DE0sB9Nu.js">
269
+ <link rel="modulepreload" crossorigin href="/assets/select-Y9IEbx9x.js">
270
+ <link rel="modulepreload" crossorigin href="/assets/context-menu-DHVVdHzN.js">
271
+ <link rel="modulepreload" crossorigin href="/assets/x-DSz9cGhy.js">
272
+ <link rel="modulepreload" crossorigin href="/assets/dialog-BUfWO8hr.js">
273
273
  <link rel="modulepreload" crossorigin href="/assets/katex-CsAM73R9.js">
274
- <link rel="modulepreload" crossorigin href="/assets/puzzle--RLCeYML.js">
274
+ <link rel="modulepreload" crossorigin href="/assets/puzzle-D_TiFXOv.js">
275
275
  <link rel="modulepreload" crossorigin href="/assets/middleware-B--6mAUM.js">
276
- <link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-DdxVnCYc.js">
277
- <link rel="modulepreload" crossorigin href="/assets/book-open-BPMkKSci.js">
276
+ <link rel="modulepreload" crossorigin href="/assets/use-viewport-layout-vmbQUNT6.js">
277
+ <link rel="modulepreload" crossorigin href="/assets/book-open-CmRdT3N1.js">
278
278
  <link rel="modulepreload" crossorigin href="/assets/navigation-history-CzErJuPG.js">
279
- <link rel="modulepreload" crossorigin href="/assets/theme-provider-C5PCDBPB.js">
280
- <link rel="modulepreload" crossorigin href="/assets/confirm-dialog-10Qb3dpZ.js">
281
- <link rel="modulepreload" crossorigin href="/assets/download-Cr337h4W.js">
279
+ <link rel="modulepreload" crossorigin href="/assets/theme-provider-BjF1Eowc.js">
280
+ <link rel="modulepreload" crossorigin href="/assets/confirm-dialog-8vqYZ0wy.js">
281
+ <link rel="modulepreload" crossorigin href="/assets/download-CKqS1Ksj.js">
282
282
  <link rel="modulepreload" crossorigin href="/assets/host-capabilities-DuIFVsvM.js">
283
- <link rel="modulepreload" crossorigin href="/assets/navigation-link-D3pzNHeA.js">
284
- <link rel="modulepreload" crossorigin href="/assets/eye-DrQRSAfJ.js">
285
- <link rel="modulepreload" crossorigin href="/assets/tag-chip-DXHtZ8AS.js">
286
- <link rel="modulepreload" crossorigin href="/assets/loader-circle-DbOQWT9C.js">
287
- <link rel="modulepreload" crossorigin href="/assets/trash-2-DazgBvmJ.js">
288
- <link rel="modulepreload" crossorigin href="/assets/refresh-cw-syCVj_nw.js">
289
- <link rel="modulepreload" crossorigin href="/assets/use-desktop-capabilities-CdfOUjdE.js">
290
- <link rel="modulepreload" crossorigin href="/assets/status-dot-DZlxa5zW.js">
291
- <link rel="modulepreload" crossorigin href="/assets/doc-browser-context-DH4aWvkS.js">
292
- <link rel="modulepreload" crossorigin href="/assets/card-Bqu8zWLq.js">
293
- <link rel="modulepreload" crossorigin href="/assets/provider-models-YW7rIeOD.js">
294
- <link rel="modulepreload" crossorigin href="/assets/use-config-DRRGF9Zb.js">
295
- <link rel="modulepreload" crossorigin href="/assets/app-presenter-provider-Co8uKOyW.js">
296
- <link rel="modulepreload" crossorigin href="/assets/provider-scoped-model-input-axJ-pTEu.js">
297
- <link rel="modulepreload" crossorigin href="/assets/settings-2-COvVf1XX.js">
298
- <link rel="modulepreload" crossorigin href="/assets/pwa-DcizGowz.js">
299
- <link rel="modulepreload" crossorigin href="/assets/login-page-sgivRYG1.js">
283
+ <link rel="modulepreload" crossorigin href="/assets/navigation-link-DC-Pku0Y.js">
284
+ <link rel="modulepreload" crossorigin href="/assets/eye-bKzylu3p.js">
285
+ <link rel="modulepreload" crossorigin href="/assets/tag-chip-D-6aPuuh.js">
286
+ <link rel="modulepreload" crossorigin href="/assets/loader-circle-Clq1Gtb1.js">
287
+ <link rel="modulepreload" crossorigin href="/assets/trash-2-DER2EJKK.js">
288
+ <link rel="modulepreload" crossorigin href="/assets/refresh-cw-BG8VfcdA.js">
289
+ <link rel="modulepreload" crossorigin href="/assets/use-desktop-capabilities-ezKJwKqg.js">
290
+ <link rel="modulepreload" crossorigin href="/assets/status-dot-Cb8ewlUr.js">
291
+ <link rel="modulepreload" crossorigin href="/assets/doc-browser-context-ClLgMbxd.js">
292
+ <link rel="modulepreload" crossorigin href="/assets/card-CdRBL1E0.js">
293
+ <link rel="modulepreload" crossorigin href="/assets/provider-models-DFQgF4hS.js">
294
+ <link rel="modulepreload" crossorigin href="/assets/use-config-BM5jC65L.js">
295
+ <link rel="modulepreload" crossorigin href="/assets/app-presenter-provider-BcYL-VNe.js">
296
+ <link rel="modulepreload" crossorigin href="/assets/provider-scoped-model-input-DaONL0j_.js">
297
+ <link rel="modulepreload" crossorigin href="/assets/settings-2-CrGx9_De.js">
298
+ <link rel="modulepreload" crossorigin href="/assets/pwa-C9ac_1dg.js">
299
+ <link rel="modulepreload" crossorigin href="/assets/login-page-BT3GV3Sb.js">
300
300
  <link rel="stylesheet" crossorigin href="/assets/app-presenter-provider-jNGlmBLe.css">
301
- <link rel="stylesheet" crossorigin href="/assets/main-BpL6OjwM.css">
301
+ <link rel="stylesheet" crossorigin href="/assets/main-kqMRTwOs.css">
302
302
  </head>
303
303
 
304
304
  <body>
@@ -5,20 +5,20 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="theme-color" content="#ffffff" />
7
7
  <title>Panel App · NextClaw</title>
8
- <script type="module" crossorigin src="/assets/panelStandalone-CGkYeMK5.js"></script>
8
+ <script type="module" crossorigin src="/assets/panelStandalone-CX8X2hjh.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/chunk-DF37yw5_.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/preload-helper-ca-nBW7U.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/i18n-OGmF0Ws_.js">
12
- <link rel="modulepreload" crossorigin href="/assets/api-DkLJrSCd.js">
13
- <link rel="modulepreload" crossorigin href="/assets/client-imrVphQb.js">
14
- <link rel="modulepreload" crossorigin href="/assets/es2015-D-CRf3_Z.js">
15
- <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-B4xj2I2g.js">
16
- <link rel="modulepreload" crossorigin href="/assets/context-menu-CRqH2SEk.js">
17
- <link rel="modulepreload" crossorigin href="/assets/x-CQIp_nJT.js">
18
- <link rel="modulepreload" crossorigin href="/assets/dialog-BgL3vKJQ.js">
12
+ <link rel="modulepreload" crossorigin href="/assets/api-BZvNpFOS.js">
13
+ <link rel="modulepreload" crossorigin href="/assets/client-DKKbdJ6Q.js">
14
+ <link rel="modulepreload" crossorigin href="/assets/es2015-BiB05Sav.js">
15
+ <link rel="modulepreload" crossorigin href="/assets/createLucideIcon-DE0sB9Nu.js">
16
+ <link rel="modulepreload" crossorigin href="/assets/context-menu-DHVVdHzN.js">
17
+ <link rel="modulepreload" crossorigin href="/assets/x-DSz9cGhy.js">
18
+ <link rel="modulepreload" crossorigin href="/assets/dialog-BUfWO8hr.js">
19
19
  <link rel="modulepreload" crossorigin href="/assets/navigation-history-CzErJuPG.js">
20
- <link rel="modulepreload" crossorigin href="/assets/theme-provider-C5PCDBPB.js">
21
- <link rel="stylesheet" crossorigin href="/assets/panelStandalone-D-mhAGNa.css">
20
+ <link rel="modulepreload" crossorigin href="/assets/theme-provider-BjF1Eowc.js">
21
+ <link rel="stylesheet" crossorigin href="/assets/panelStandalone-BRg7B1EI.css">
22
22
  </head>
23
23
  <body>
24
24
  <div id="root"></div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextclaw/ui",
3
- "version": "0.26.3",
3
+ "version": "0.26.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,11 +35,11 @@
35
35
  "zod": "^3.23.8",
36
36
  "zustand": "^5.0.2",
37
37
  "@nextclaw/agent-chat": "0.3.14",
38
- "@nextclaw/client-sdk": "0.12.8",
38
+ "@nextclaw/client-sdk": "0.12.9",
39
39
  "@nextclaw/ncp": "0.11.2",
40
- "@nextclaw/agent-chat-ui": "0.10.2",
41
- "@nextclaw/ncp-http-agent-client": "0.4.25",
40
+ "@nextclaw/agent-chat-ui": "0.11.0",
42
41
  "@nextclaw/ncp-react": "0.5.30",
42
+ "@nextclaw/ncp-http-agent-client": "0.4.25",
43
43
  "@nextclaw/ncp-toolkit": "0.6.27",
44
44
  "@nextclaw/shared": "0.8.2"
45
45
  },
@@ -107,7 +107,7 @@ describe("Sidebar", () => {
107
107
  expect(backLink).toBeTruthy();
108
108
  expect(header.className).not.toContain("bg-white");
109
109
  expect(header.className).not.toContain("rounded-2xl");
110
- expect(backLink.className).toContain("hover:bg-[hsl(var(--gray-200)/0.6)]");
110
+ expect(backLink.className).toContain("hover:bg-[var(--sidebar-interaction-hover)]");
111
111
  });
112
112
 
113
113
  it("keeps the settings navigation in the expected product order", () => {
@@ -207,7 +207,7 @@ describe("Sidebar", () => {
207
207
  expect(screen.queryByRole("combobox", { name: "Language" })).toBeNull();
208
208
  expect(accountEntry.className).toContain("py-2");
209
209
  expect(accountEntry.className).toContain("text-muted-foreground");
210
- expect(accountEntry.className).toContain("hover:bg-[hsl(var(--gray-200)/0.6)]");
210
+ expect(accountEntry.className).toContain("hover:bg-[var(--sidebar-interaction-hover)]");
211
211
  expect(navigationList?.className).toContain("space-y-0.5");
212
212
  expect(footer?.className).toContain("space-y-0.5");
213
213
  expect(footer?.firstElementChild?.textContent).toContain("Help Docs");
@@ -10,13 +10,13 @@ export const SIDEBAR_RAIL_ITEM_GAP_CLASS = "gap-1";
10
10
  // Use theme neutral tokens directly so legacy gray utility overrides cannot
11
11
  // turn sidebar feedback into a content selection or accent surface.
12
12
  export const SIDEBAR_ITEM_SURFACE_CLASS =
13
- "rounded-xl text-muted-foreground transition-colors duration-base hover:bg-[hsl(var(--gray-200)/0.6)] hover:text-foreground focus-within:bg-[hsl(var(--gray-200)/0.6)] has-[[data-context-menu-open]]:bg-[hsl(var(--gray-200)/0.6)]";
13
+ "rounded-xl text-muted-foreground transition-colors duration-base hover:bg-[var(--sidebar-interaction-hover)] hover:text-foreground focus-within:bg-[var(--sidebar-interaction-hover)] has-[[data-context-menu-open]]:bg-[var(--sidebar-interaction-hover)]";
14
14
 
15
15
  export const SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS =
16
- "bg-[hsl(var(--gray-200)/0.8)] text-foreground hover:bg-[hsl(var(--gray-200)/0.8)] focus-within:bg-[hsl(var(--gray-200)/0.8)] has-[[data-context-menu-open]]:bg-[hsl(var(--gray-200)/0.8)]";
16
+ "bg-[var(--sidebar-interaction-selection)] text-foreground hover:bg-[var(--sidebar-interaction-selection)] focus-within:bg-[var(--sidebar-interaction-selection)] has-[[data-context-menu-open]]:bg-[var(--sidebar-interaction-selection)]";
17
17
 
18
18
  export const SIDEBAR_RAIL_SURFACE_CLASS =
19
- `${SIDEBAR_ITEM_SURFACE_CLASS} active:bg-[hsl(var(--gray-200)/0.8)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border`;
19
+ `${SIDEBAR_ITEM_SURFACE_CLASS} active:bg-[var(--sidebar-interaction-selection)] focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border`;
20
20
 
21
21
  export const SIDEBAR_RAIL_ACTIVE_SURFACE_CLASS = SIDEBAR_ITEM_ACTIVE_SURFACE_CLASS;
22
22
 
@@ -42,9 +42,6 @@
42
42
  --secondary-foreground: var(--gray-800);
43
43
  --accent: 0 0% 19%;
44
44
  --accent-foreground: var(--gray-900);
45
- --interaction-hover: hsl(var(--muted));
46
- --interaction-selection: hsl(202 38% 27%);
47
- --interaction-selection-border: hsl(202 48% 40%);
48
45
  --muted: var(--gray-100);
49
46
  --muted-foreground: var(--gray-500);
50
47
  --destructive: 0 72% 60%;
@@ -12,6 +12,7 @@
12
12
  }
13
13
 
14
14
  :root {
15
+ --shadow-popover: 0 12px 36px -8px rgb(0 0 0 / 0.18), 0 3px 10px -2px rgb(0 0 0 / 0.08);
15
16
  color-scheme: light;
16
17
 
17
18
  /* Mobile density: hit target, visual feedback, and chrome are separate. */
@@ -86,9 +87,16 @@
86
87
  /* Accent */
87
88
  --accent: var(--brand-50);
88
89
  --accent-foreground: var(--brand-700);
89
- --interaction-hover: hsl(var(--muted));
90
- --interaction-selection: hsl(211 100% 92%);
91
- --interaction-selection-border: hsl(211 75% 84%);
90
+ --interaction-hover: hsl(var(--foreground) / 0.04);
91
+ --interaction-selection: hsl(var(--foreground) / 0.07);
92
+ --interaction-selection-border: hsl(var(--foreground) / 0.12);
93
+ /* Desktop sidebar feedback matches the verified VPS v0.54.2 baseline. */
94
+ --sidebar-interaction-hover: hsl(var(--gray-200) / 0.6);
95
+ --sidebar-interaction-selection: hsl(var(--gray-200) / 0.8);
96
+ --sidebar-segment-selection: hsl(var(--gray-200) / 0.7);
97
+ /* Mobile conversation rows retain their separate paper surface. */
98
+ --session-interaction-hover: hsl(var(--background) / 0.65);
99
+ --session-interaction-selection: hsl(var(--background) / 0.9);
92
100
 
93
101
  /* Muted */
94
102
  --muted: var(--gray-100);
@@ -723,9 +731,6 @@
723
731
  --secondary-foreground: var(--gray-800);
724
732
  --accent: 151 32% 18%;
725
733
  --accent-foreground: 148 34% 84%;
726
- --interaction-hover: hsl(var(--muted));
727
- --interaction-selection: hsl(211 48% 30%);
728
- --interaction-selection-border: hsl(211 48% 42%);
729
734
  --muted: var(--gray-100);
730
735
  --muted-foreground: var(--gray-500);
731
736
  --destructive: 0 70% 52%;
@@ -834,6 +839,7 @@
834
839
 
835
840
  @layer base {
836
841
  :root[data-theme-appearance="dark"] {
842
+ --shadow-popover: 0 8px 32px -8px rgb(0 0 0 / 0.4), 0 2px 8px -2px rgb(0 0 0 / 0.24);
837
843
  --link-foreground: 213 94% 68%;
838
844
  --link-foreground-hover: 213 97% 79%;
839
845
  }
@@ -44,9 +44,6 @@
44
44
  --secondary-foreground: var(--gray-800);
45
45
  --accent: 151 28% 88%;
46
46
  --accent-foreground: var(--brand-800);
47
- --interaction-hover: hsl(var(--muted));
48
- --interaction-selection: hsl(153 35% 80% / 0.82);
49
- --interaction-selection-border: hsl(157 37% 58% / 0.88);
50
47
  --muted: var(--gray-100);
51
48
  --muted-foreground: var(--gray-500);
52
49
  --destructive: 5 61% 47%;
@@ -323,8 +320,8 @@
323
320
  -webkit-backdrop-filter: blur(18px) saturate(0.9);
324
321
  backdrop-filter: blur(18px) saturate(0.9);
325
322
  background-color: hsl(var(--popover) / 0.9);
326
- border-color: hsl(var(--border) / 0.7);
327
- box-shadow: 0 18px 42px -24px hsl(158 27% 20% / 0.46);
323
+ border-color: hsl(var(--border) / 0.4);
324
+ box-shadow: var(--shadow-popover);
328
325
  }
329
326
 
330
327
  :root[data-theme="island"] [data-theme-overlay="tooltip"] {
@@ -446,16 +443,6 @@
446
443
  border-radius: 0;
447
444
  }
448
445
 
449
- :root[data-theme="island"]
450
- [data-theme-surface="navigation"]
451
- :is([aria-current="page"], [aria-selected="true"], [data-state="active"]),
452
- :root[data-theme="island"]
453
- [data-theme-surface="utility-rail"]
454
- :is([aria-pressed="true"], [data-state="active"]) {
455
- background-color: hsl(151 27% 84% / 0.68);
456
- color: hsl(var(--brand-800));
457
- }
458
-
459
446
  :root[data-theme="island"]
460
447
  :is(
461
448
  .custom-scrollbar,
@@ -25,7 +25,7 @@ export function AppDataRemovalChoice({
25
25
  onClick={onClick}
26
26
  className={cn(
27
27
  'flex items-start gap-3 rounded-xl border px-3 py-3 text-left transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-border disabled:cursor-not-allowed disabled:opacity-50',
28
- checked ? 'border-foreground/25 bg-muted/55' : 'border-border/60 hover:bg-muted/35',
28
+ checked ? 'border-[var(--interaction-selection-border)] bg-[var(--interaction-selection)]' : 'border-border/60 hover:bg-[var(--interaction-hover)]',
29
29
  )}
30
30
  >
31
31
  <span className={cn(
@@ -138,7 +138,7 @@ export function ChannelsList() {
138
138
  <a
139
139
  href={tutorialUrl}
140
140
  onClick={(event) => event.stopPropagation()}
141
- className='inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground/45 transition-colors hover:bg-muted hover:text-foreground'
141
+ className='inline-flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground/45 transition-colors hover:bg-[var(--interaction-hover)] hover:text-foreground'
142
142
  title={t('channelsGuideTitle')}
143
143
  >
144
144
  <ExternalLink className='h-3.5 w-3.5' />
@@ -14,7 +14,7 @@ export function ChatSidebarListModeSwitch({ isProjectFirstView, onSelectMode }:
14
14
  <span
15
15
  aria-hidden="true"
16
16
  className={cn(
17
- 'pointer-events-none absolute bottom-0.5 left-0.5 top-0.5 w-[calc(50%-2px)] rounded-full bg-gray-200/70 transition-transform duration-200 ease-out motion-reduce:transition-none',
17
+ 'pointer-events-none absolute bottom-0.5 left-0.5 top-0.5 w-[calc(50%-2px)] rounded-full bg-[var(--sidebar-segment-selection)] transition-transform duration-200 ease-out motion-reduce:transition-none',
18
18
  isProjectFirstView && 'translate-x-full',
19
19
  )}
20
20
  />
@@ -8,6 +8,7 @@ import { useChatQueryStore } from "@/features/chat/stores/ncp-chat-query.store";
8
8
  import { useChatSessionListStore } from "@/features/chat/stores/chat-session-list.store";
9
9
  import { useChatThreadStore } from "@/features/chat/stores/chat-thread.store";
10
10
  import { viewportLayoutManager } from "@/app/managers/viewport-layout.manager";
11
+ import { ChatSessionSwitcherGroups } from "@/features/chat/features/session/components/session-header/chat-session-switcher-groups";
11
12
  import type { NcpSessionListItemView } from "@/features/chat/features/ncp/hooks/use-ncp-session-list-view";
12
13
  import type { NcpSessionSummaryView, SessionEntryView } from "@/shared/lib/api";
13
14
 
@@ -312,42 +313,51 @@ describe("ChatConversationHeaderSection", () => {
312
313
  ).toBeTruthy();
313
314
  });
314
315
 
315
- it("switches to collapsible project groups inside the header popover", async () => {
316
+ it("renders compact collapsible project groups inside the header popover", async () => {
316
317
  const user = userEvent.setup();
317
- viewportLayoutManager.setSidebarCollapsed(true);
318
- mocks.sessionItems = [
319
- createSessionListItem({
318
+ const alphaItem = createSessionListItem({
320
319
  key: "parent-session-1",
321
320
  label: "Parent Task",
322
321
  projectName: "Alpha",
323
322
  projectRoot: "/workspace/alpha",
324
- }),
325
- createSessionListItem({
323
+ });
324
+ const betaItem = createSessionListItem({
326
325
  key: "session:ncp-2",
327
326
  label: "Background Task",
328
327
  projectName: "Beta",
329
328
  projectRoot: "/workspace/beta",
330
- }),
331
- ];
329
+ });
332
330
 
333
- renderHeaderSection();
334
- await user.click(screen.getByRole("button", { name: /Switch session/ }));
335
- await user.click(screen.getByRole("button", { name: "Project" }));
331
+ render(
332
+ <ChatSessionSwitcherGroups
333
+ collapsedProjectRoots={[]}
334
+ dateGroups={[]}
335
+ isProjectFirstView
336
+ onSelect={mocks.selectSession}
337
+ onToggleProjectCollapsed={mocks.toggleProjectCollapsed}
338
+ optimisticReadAtBySessionKey={{}}
339
+ projectGroups={[
340
+ { projectRoot: "/workspace/alpha", projectName: "Alpha", items: [alphaItem], latestUpdatedAt: 1, isPinned: false },
341
+ { projectRoot: "/workspace/beta", projectName: "Beta", items: [betaItem], latestUpdatedAt: 1, isPinned: false },
342
+ ]}
343
+ selectedSessionKey="parent-session-1"
344
+ sessionTypeOptions={[]}
345
+ />,
346
+ );
336
347
 
337
- expect(mocks.setListMode).toHaveBeenCalledWith("project-first");
338
- const alphaGroup = screen.getByRole("region", { name: "Alpha" });
348
+ const alphaGroup = await screen.findByRole("region", { name: "Alpha" });
339
349
  const alphaToggle = within(alphaGroup).getByRole("button", {
340
350
  name: "Collapse project · Alpha",
341
351
  });
342
352
  expect(alphaToggle.getAttribute("aria-expanded")).toBe("true");
353
+ expect(alphaToggle.className).toContain("h-8");
343
354
  expect(alphaToggle.className).not.toContain("bg-muted");
355
+ expect(alphaToggle.querySelector('[title="/workspace/alpha"]')?.getAttribute("class")).toContain("text-[13px]");
356
+ expect(alphaGroup.parentElement?.className).toContain("space-y-0.5");
344
357
  expect(within(alphaGroup).getByText("Parent Task")).toBeTruthy();
345
358
 
346
359
  await user.click(alphaToggle);
347
360
  expect(mocks.toggleProjectCollapsed).toHaveBeenCalledWith("/workspace/alpha");
348
- expect(
349
- within(screen.getByRole("region", { name: "Alpha" })).queryByText("Parent Task"),
350
- ).toBeNull();
351
361
  });
352
362
 
353
363
  it("keeps the desktop title plain while the sidebar is expanded", () => {
@@ -447,6 +457,8 @@ describe("ChatConversationHeaderSection", () => {
447
457
  const searchInput = screen.getByRole("textbox", {
448
458
  name: "Search session key / label",
449
459
  });
460
+ expect(searchInput.className).toContain("max-md:text-[13px]");
461
+ expect(searchInput.className).toContain("max-md:min-h-0");
450
462
  await user.type(searchInput, "Background");
451
463
  await user.click(screen.getByRole("button", { name: /Background Task/ }));
452
464
 
@@ -95,6 +95,9 @@ export function ChatConversationContent({
95
95
  }: ChatConversationContentProps) {
96
96
  const threadRef = useRef<HTMLDivElement>(null);
97
97
  const contentRef = useRef<HTMLDivElement>(null);
98
+ const turnAnchorRef = useRef<HTMLDivElement>(null);
99
+ const turnFrameRef = useRef<HTMLDivElement>(null);
100
+ const latestUserMessageId = messages.findLast((message) => message.role === "user")?.id ?? null;
98
101
  const hasConversationContent = messages.length > 0 || isSending;
99
102
  const readingKey = createConversationScrollRestorationKey(sessionKey, showWelcome);
100
103
  const initialReadingPosition = useMemo(() => readingKey ? scrollRestorationManager.read(readingKey) : null, [readingKey]);
@@ -112,6 +115,8 @@ export function ChatConversationContent({
112
115
  isLoading: isHistoryLoading,
113
116
  hasContent: hasConversationContent,
114
117
  contentVersion: messages[messages.length - 1] ?? isSending,
118
+ stickyThresholdPx: 80,
119
+ turnSpace: { key: latestUserMessageId, anchorRef: turnAnchorRef, frameRef: turnFrameRef },
115
120
  });
116
121
  const hasMessages = messages.length > 0;
117
122
  const handleScroll = useCallback(
@@ -148,7 +153,7 @@ export function ChatConversationContent({
148
153
  {showWelcome ? (
149
154
  (welcomeSlot ?? null)
150
155
  ) : (
151
- <div ref={contentRef} className="pb-7">
156
+ <div ref={turnFrameRef}><div ref={contentRef} className="pb-7">
152
157
  {hasConversationContent ? (
153
158
  <ChatConversationTrack className="relative py-4 sm:py-5">
154
159
  <ConversationHistoryStatus historyError={historyError} isLoadingPreviousMessages={isLoadingPreviousMessages} onLoadPreviousMessages={onLoadPreviousMessages} />
@@ -163,6 +168,7 @@ export function ChatConversationContent({
163
168
  onLoadMessageDetails={onLoadMessageDetails}
164
169
  scrollRef={threadRef}
165
170
  sessionKey={sessionKey}
171
+ turnAnchorRef={turnAnchorRef}
166
172
  />
167
173
  {isContextCompacting ? <ChatContextCompactionDivider /> : null}
168
174
  </ChatConversationTrack>
@@ -172,7 +178,7 @@ export function ChatConversationContent({
172
178
  {bottomSlot}
173
179
  </ChatConversationTrack>
174
180
  ) : null}
175
- </div>
181
+ </div></div>
176
182
  )}
177
183
  </div>
178
184
  {hasMessages && !showWelcome && !isAtBottom ? (
@@ -8,6 +8,8 @@ function renderSessionArea(isProjectFirstView: boolean, onScrollNearEnd = vi.fn(
8
8
 
9
9
  render(
10
10
  <ChatSidebarSessionArea
11
+ query=""
12
+ onQueryChange={vi.fn()}
11
13
  defaultSessionType="native"
12
14
  groups={[]}
13
15
  isCollapsed={false}
@@ -50,9 +52,9 @@ describe("ChatSidebarSessionArea", () => {
50
52
  expect(projectButton.className).toContain("rounded-full");
51
53
  expect(timeButton.querySelector("svg")).not.toBeNull();
52
54
  expect(projectButton.querySelector("svg")).not.toBeNull();
53
- expect(modeGroup.parentElement?.className).toContain("justify-end");
55
+ expect(modeGroup.parentElement?.className).toContain("justify-between");
54
56
  expect(modeGroup.parentElement?.className).toContain("h-8");
55
- expect(modeGroup.parentElement?.lastElementChild).toBe(modeGroup);
57
+ expect(modeGroup.parentElement?.firstElementChild).toBe(modeGroup);
56
58
 
57
59
  fireEvent.click(projectButton);
58
60
 
@@ -73,7 +75,7 @@ describe("ChatSidebarSessionArea", () => {
73
75
  .querySelector("svg")
74
76
  ?.classList.contains("lucide-folder-plus"),
75
77
  ).toBe(true);
76
- expect(addProjectButton.className).toContain("hover:bg-gray-200/60");
78
+ expect(addProjectButton.className).toContain("hover:before:bg-[var(--interaction-hover)]");
77
79
  expect(modeIndicator?.className).toContain("translate-x-full");
78
80
  expect(modeIndicator?.className).toContain("motion-reduce:transition-none");
79
81
  expect(
@@ -81,7 +83,7 @@ describe("ChatSidebarSessionArea", () => {
81
83
  .getByRole("button", { name: "Project" })
82
84
  .getAttribute("aria-pressed"),
83
85
  ).toBe("true");
84
- expect(modeIndicator?.parentElement?.previousElementSibling).toBe(
86
+ expect(screen.getByRole("button", { name: "Search conversations..." }).previousElementSibling).toBe(
85
87
  addProjectButton,
86
88
  );
87
89
  fireEvent.click(addProjectButton);
@@ -3,6 +3,7 @@ import userEvent from '@testing-library/user-event';
3
3
  import { describe, expect, it, vi } from "vitest";
4
4
  import {
5
5
  ChatSidebarDesktopToolbar,
6
+ ChatSidebarListToolbar,
6
7
  ChatSidebarMobileToolbar,
7
8
  } from "@/features/chat/components/layout/chat-sidebar-toolbar";
8
9
  import type { ChatSessionTypeOption } from "@/features/chat/features/session-type/utils/chat-session-type.utils";
@@ -34,8 +35,18 @@ function searchIconClassName() {
34
35
  }
35
36
 
36
37
  describe("ChatSidebarToolbar", () => {
37
- it("keeps desktop search icon transparent to pointer input", () => {
38
+ it("does not reserve a search row in the desktop header", () => {
38
39
  render(<ChatSidebarDesktopToolbar {...toolbarProps} />);
40
+ expect(screen.queryByRole("textbox")).toBeNull();
41
+ });
42
+ it("reveals desktop search below the list controls and clears it on close", async () => {
43
+ const user = userEvent.setup();
44
+ const onQueryChange = vi.fn();
45
+ render(<ChatSidebarListToolbar {...toolbarProps} onQueryChange={onQueryChange} isProjectFirstView={false} onSelectMode={vi.fn()} onAddProject={vi.fn()} />);
46
+ expect(screen.queryByRole("textbox")).toBeNull();
47
+ const trigger = screen.getByRole("button", { name: "Search conversations..." });
48
+ await user.click(trigger);
49
+ expect(document.activeElement).toBe(screen.getByRole("textbox"));
39
50
 
40
51
  expect(searchIconClassName()).toContain("pointer-events-none");
41
52
  expect(
@@ -46,6 +57,16 @@ describe("ChatSidebarToolbar", () => {
46
57
  .getByPlaceholderText("Search conversations...")
47
58
  .getAttribute("data-theme-control"),
48
59
  ).toBe("chat-search");
60
+ await user.type(screen.getByRole("textbox"), "a");
61
+ expect(onQueryChange).toHaveBeenLastCalledWith("a");
62
+ await user.click(screen.getByRole("button", { name: "Close search" }));
63
+ expect(onQueryChange).toHaveBeenLastCalledWith("");
64
+ expect(screen.queryByRole("textbox")).toBeNull();
65
+ expect(document.activeElement).toBe(trigger);
66
+ await user.click(trigger);
67
+ await user.keyboard("{Escape}");
68
+ expect(screen.queryByRole("textbox")).toBeNull();
69
+ expect(document.activeElement).toBe(trigger);
49
70
  });
50
71
 
51
72
  it('reveals mobile search on demand and clears the filter when closed', async () => {
@@ -1,6 +1,6 @@
1
1
  import { BrandHeader } from "@/shared/components/common/brand-header";
2
2
  import { StatusBadge } from "@/shared/components/common/status-badge";
3
- import { ChatSidebarListModeSwitch } from "@/features/chat/components/chat-sidebar-list-mode-switch";
3
+ import { ChatSidebarListToolbar } from "@/features/chat/components/layout/chat-sidebar-toolbar";
4
4
  import type {
5
5
  groupSessionsByDate,
6
6
  groupSessionsByProject,
@@ -20,7 +20,6 @@ import {
20
20
  Inbox,
21
21
  PanelLeftClose,
22
22
  PanelLeftOpen,
23
- FolderPlus,
24
23
  } from "lucide-react";
25
24
  import { useInboxUnreadCount } from "@/features/inbox";
26
25
  import { ChatSidebarSessionList } from "@/features/chat/features/session/components/chat-sidebar-session-list";
@@ -148,6 +147,8 @@ export function ChatSidebarDesktopNav({
148
147
  }
149
148
 
150
149
  export function ChatSidebarSessionArea({
150
+ query,
151
+ onQueryChange,
151
152
  variant = 'desktop',
152
153
  defaultSessionType,
153
154
  groups,
@@ -162,6 +163,8 @@ export function ChatSidebarSessionArea({
162
163
  renderSessionItem,
163
164
  sessionTypeOptions,
164
165
  }: {
166
+ query: string;
167
+ onQueryChange: (query: string) => void;
165
168
  variant?: 'desktop' | 'mobile';
166
169
  defaultSessionType: string;
167
170
  groups: ReturnType<typeof groupSessionsByDate>;
@@ -189,20 +192,13 @@ export function ChatSidebarSessionArea({
189
192
 
190
193
  return (
191
194
  <>
192
- <div className={variant === 'mobile' ? 'hidden' : 'flex h-8 items-center justify-end gap-1 px-3'}>
193
- {isProjectFirstView ? (
194
- <IconActionButton
195
- icon={<FolderPlus className="h-3.5 w-3.5" />}
196
- label={t("chatProjectAdd")}
197
- tone="surface"
198
- onClick={onAddProject}
199
- />
200
- ) : null}
201
- <ChatSidebarListModeSwitch
195
+ {variant === 'desktop' ? <ChatSidebarListToolbar
196
+ query={query}
197
+ onQueryChange={onQueryChange}
202
198
  isProjectFirstView={isProjectFirstView}
203
199
  onSelectMode={onSelectMode}
204
- />
205
- </div>
200
+ onAddProject={onAddProject}
201
+ /> : null}
206
202
 
207
203
  <div
208
204
  ref={scrollRef}