@jetcrab/pi-desk 1.1.28-dev → 1.1.30-dev

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 (362) hide show
  1. package/docs/plugins.md +2 -2
  2. package/package.json +1 -1
  3. package/src/app/api/work-sessions/search/route.ts +1 -0
  4. package/src/app/project-preview/page.tsx +1 -3
  5. package/src/client/l1_entry/pages/l1-error-page.tsx +1 -1
  6. package/src/client/l1_entry/providers/l1-app-providers.tsx +2 -0
  7. package/src/client/l2_biz/auth/l2-login.module.css +1 -1
  8. package/src/client/l2_biz/plugin-host/l2-plugin-composer-panel.tsx +1 -1
  9. package/src/client/l2_biz/plugin-host/l2-plugin-contribution-host.tsx +1 -1
  10. package/src/client/l2_biz/plugin-host/l2-plugin-session-header-layout.ts +6 -3
  11. package/src/client/l2_biz/plugin-host/l2-plugin-session-header.tsx +75 -18
  12. package/src/client/l2_biz/project-preview/hooks/l2-use-project-preview.ts +1 -14
  13. package/src/client/l2_biz/project-preview/l2-project-preview-dialog.tsx +2 -3
  14. package/src/client/l2_biz/project-preview/l2-project-preview-page.tsx +1 -2
  15. package/src/client/l2_biz/project-preview/l2-project-preview.tsx +1 -4
  16. package/src/client/l2_biz/project-preview/views/l2-project-branch-select.tsx +42 -3
  17. package/src/client/l2_biz/settings/l2-settings.tsx +1 -1
  18. package/src/client/l2_biz/settings/l2-skills-settings.tsx +1 -1
  19. package/src/client/l2_biz/task-center/l2-task-center.tsx +1 -1
  20. package/src/client/l2_biz/workbench/file-workspace/l2-plugin-standalone-file-preview.tsx +1 -1
  21. package/src/client/l2_biz/workbench/file-workspace/l2-work-session-file-preview.tsx +1 -1
  22. package/src/client/l2_biz/workbench/git-workspace/l2-git-workspace-runtime.ts +11 -10
  23. package/src/client/l2_biz/workbench/hooks/l2-work-session-search.ts +52 -0
  24. package/src/client/l2_biz/workbench/hooks/l2-workbench-branch-picker.ts +42 -7
  25. package/src/client/l2_biz/workbench/hooks/l2-workbench-session-history.ts +143 -131
  26. package/src/client/l2_biz/workbench/hooks/l2-workbench-session-search.ts +97 -0
  27. package/src/client/l2_biz/workbench/l2-work-session-columns.tsx +8 -0
  28. package/src/client/l2_biz/workbench/l2-work-session-sidebar.tsx +129 -10
  29. package/src/client/l2_biz/workbench/l2-workbench-biz.ts +20 -2
  30. package/src/client/l2_biz/workbench/l2-workbench-branch-filter-preferences.ts +34 -0
  31. package/src/client/l2_biz/workbench/l2-workbench-branch-picker.tsx +34 -71
  32. package/src/client/l2_biz/workbench/l2-workbench-chat-input-runtime.ts +38 -1
  33. package/src/client/l2_biz/workbench/l2-workbench-chat-view.tsx +11 -1
  34. package/src/client/l2_biz/workbench/l2-workbench-picker-dialog.tsx +55 -215
  35. package/src/client/l2_biz/workbench/l2-workbench.tsx +21 -39
  36. package/src/client/l2_biz/workbench/views/l2-workbench-session-search.tsx +123 -0
  37. package/src/client/l3_modules/conversation/l3-conversation-messages.tsx +43 -4
  38. package/src/client/l3_modules/project-files/l3-markdown-preview.module.css +1 -1
  39. package/src/client/l3_modules/project-files/l3-project-document-git.ts +13 -29
  40. package/src/client/l3_modules/project-files/l3-project-document-view.tsx +1 -4
  41. package/src/client/l3_modules/project-files/l3-project-file-browser.module.css +14 -18
  42. package/src/client/l3_modules/project-files/l3-project-file-browser.tsx +52 -95
  43. package/src/client/l3_modules/project-files/l3-project-file-runtime.ts +52 -21
  44. package/src/client/l3_modules/project-files/l3-project-file-tabs.tsx +41 -62
  45. package/src/client/l3_modules/project-files/l3-project-git-runtime.ts +5 -4
  46. package/src/client/l3_modules/project-files/l3-use-project-refresh.ts +36 -0
  47. package/src/client/l3_modules/project-files/views/l3-project-search-results.tsx +2 -2
  48. package/src/client/l3_modules/project-files/views/l3-project-tree-folder.tsx +86 -0
  49. package/src/client/l4_foundation/desktop-window/l4-desktop-titlebar.tsx +188 -0
  50. package/src/client/l4_foundation/desktop-window/l4-desktop-window.css +107 -0
  51. package/src/client/l4_foundation/desktop-window/l4-desktop-window.ts +20 -0
  52. package/src/client/l4_foundation/ui/file-icons/README.md +1 -1
  53. package/src/client/l4_foundation/ui/file-icons/l4-file-icon.tsx +49 -2
  54. package/src/client/l4_foundation/ui/l4-app-dialog.tsx +2 -2
  55. package/src/common/l2_biz/pi-session/l2-pi-session-contract.ts +11 -24
  56. package/src/common/l2_biz/project-preview/l2-project-preview-navigation.ts +5 -12
  57. package/src/common/l2_biz/work-session/l2-work-session-search-contract.ts +26 -0
  58. package/src/common/l2_biz/work-session/l2-work-session-tree-contract.ts +9 -1
  59. package/src/common/l2_biz/workbench/l2-workbench-locale-messages.ts +12 -0
  60. package/src/common/l2_biz/workbench/l2-workbench-picker-messages.ts +6 -2
  61. package/src/common/l3_modules/conversation/l3-conversation-locale-messages.ts +2 -0
  62. package/src/common/l3_modules/pi/l3-pi-session-search-contract.ts +26 -0
  63. package/src/common/l4_foundation/locale/l4-host-error-messages.ts +2 -0
  64. package/src/server/l1_entry/api/l1-work-session-branch-route.ts +21 -15
  65. package/src/server/l1_entry/api/l1-work-session-search-route.ts +38 -0
  66. package/src/server/l2_biz/work-session/l2-work-session-chat-runtime.ts +4 -2
  67. package/src/server/l2_biz/work-session/l2-work-session-manage.ts +46 -10
  68. package/src/server/l3_modules/work-session/l3-work-session.ts +11 -6
  69. package/src/server/l4_foundation/pi/l4-pi-chat-worker.ts +35 -2
  70. package/src/server/l4_foundation/pi/l4-pi-session-history-catalog.ts +62 -113
  71. package/src/server/l4_foundation/pi/l4-pi-session-search.ts +166 -0
  72. package/src/server/l4_foundation/pi/l4-pi-work-session-runtime.ts +66 -5
  73. package/temp/build/pi-desk/release/.next/BUILD_ID +1 -1
  74. package/temp/build/pi-desk/release/.next/app-path-routes-manifest.json +1 -0
  75. package/temp/build/pi-desk/release/.next/build-manifest.json +3 -3
  76. package/temp/build/pi-desk/release/.next/required-server-files.js +2 -2
  77. package/temp/build/pi-desk/release/.next/required-server-files.json +2 -2
  78. package/temp/build/pi-desk/release/.next/routes-manifest.json +6 -0
  79. package/temp/build/pi-desk/release/.next/server/app/_global-error/page.js +2 -2
  80. package/temp/build/pi-desk/release/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  81. package/temp/build/pi-desk/release/.next/server/app/_global-error.html +1 -1
  82. package/temp/build/pi-desk/release/.next/server/app/_global-error.rsc +3 -3
  83. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_full.segment.rsc +3 -3
  84. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +3 -3
  85. package/temp/build/pi-desk/release/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  86. package/temp/build/pi-desk/release/.next/server/app/_not-found/page.js +1 -1
  87. package/temp/build/pi-desk/release/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  88. package/temp/build/pi-desk/release/.next/server/app/api/auth/login/route.js +2 -2
  89. package/temp/build/pi-desk/release/.next/server/app/api/auth/logout/route.js +1 -1
  90. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/get/route.js +1 -1
  91. package/temp/build/pi-desk/release/.next/server/app/api/auth-settings/replace/route.js +1 -1
  92. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/get/route.js +1 -1
  93. package/temp/build/pi-desk/release/.next/server/app/api/capability-modes/replace/route.js +1 -1
  94. package/temp/build/pi-desk/release/.next/server/app/api/chat/context/get/route.js +1 -1
  95. package/temp/build/pi-desk/release/.next/server/app/api/chat/images/get/route.js +1 -1
  96. package/temp/build/pi-desk/release/.next/server/app/api/chat/messages/get/route.js +1 -1
  97. package/temp/build/pi-desk/release/.next/server/app/api/health/route.js +1 -1
  98. package/temp/build/pi-desk/release/.next/server/app/api/mcp-check/get/route.js +1 -1
  99. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/del/route.js +1 -1
  100. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/get/route.js +1 -1
  101. package/temp/build/pi-desk/release/.next/server/app/api/mcp-settings/replace/route.js +1 -1
  102. package/temp/build/pi-desk/release/.next/server/app/api/model-catalog/list/route.js +1 -1
  103. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/get/route.js +1 -1
  104. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/preview/route.js +1 -1
  105. package/temp/build/pi-desk/release/.next/server/app/api/model-settings/replace/route.js +1 -1
  106. package/temp/build/pi-desk/release/.next/server/app/api/pi/directories/list/route.js +1 -1
  107. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-entries/list/route.js +1 -1
  108. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/list/route.js +1 -1
  109. package/temp/build/pi-desk/release/.next/server/app/api/pi/directory-ignores/replace/route.js +1 -1
  110. package/temp/build/pi-desk/release/.next/server/app/api/pi/models/list/route.js +1 -1
  111. package/temp/build/pi-desk/release/.next/server/app/api/pi/session-user-messages/list/route.js +1 -1
  112. package/temp/build/pi-desk/release/.next/server/app/api/pi/sessions/list/route.js +1 -1
  113. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/get/route.js +1 -1
  114. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/search/route.js +1 -1
  115. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-get/route.js +1 -1
  116. package/temp/build/pi-desk/release/.next/server/app/api/plugin-catalog/settings-replace/route.js +1 -1
  117. package/temp/build/pi-desk/release/.next/server/app/api/plugins/add/route.js +1 -1
  118. package/temp/build/pi-desk/release/.next/server/app/api/plugins/apply/route.js +1 -1
  119. package/temp/build/pi-desk/release/.next/server/app/api/plugins/batch/route.js +1 -1
  120. package/temp/build/pi-desk/release/.next/server/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route.js +1 -1
  121. package/temp/build/pi-desk/release/.next/server/app/api/plugins/del/route.js +1 -1
  122. package/temp/build/pi-desk/release/.next/server/app/api/plugins/enabled-set/route.js +1 -1
  123. package/temp/build/pi-desk/release/.next/server/app/api/plugins/get/route.js +1 -1
  124. package/temp/build/pi-desk/release/.next/server/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route.js +1 -1
  125. package/temp/build/pi-desk/release/.next/server/app/api/plugins/list/route.js +1 -1
  126. package/temp/build/pi-desk/release/.next/server/app/api/plugins/reload/route.js +1 -1
  127. package/temp/build/pi-desk/release/.next/server/app/api/plugins/update/route.js +1 -1
  128. package/temp/build/pi-desk/release/.next/server/app/api/project-files/get/route.js +2 -2
  129. package/temp/build/pi-desk/release/.next/server/app/api/project-files/list/route.js +1 -1
  130. package/temp/build/pi-desk/release/.next/server/app/api/project-files/reveal/route.js +1 -1
  131. package/temp/build/pi-desk/release/.next/server/app/api/project-files/search/route.js +1 -1
  132. package/temp/build/pi-desk/release/.next/server/app/api/project-git/get/route.js +2 -2
  133. package/temp/build/pi-desk/release/.next/server/app/api/project-git-branches/list/route.js +2 -2
  134. package/temp/build/pi-desk/release/.next/server/app/api/project-git-changes/list/route.js +1 -1
  135. package/temp/build/pi-desk/release/.next/server/app/api/project-git-diff/get/route.js +2 -2
  136. package/temp/build/pi-desk/release/.next/server/app/api/project-git-heads/get/route.js +1 -1
  137. package/temp/build/pi-desk/release/.next/server/app/api/project-git-log/list/route.js +2 -2
  138. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/get/route.js +1 -1
  139. package/temp/build/pi-desk/release/.next/server/app/api/project-model-default/replace/route.js +1 -1
  140. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/get/route.js +1 -1
  141. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/list/route.js +1 -1
  142. package/temp/build/pi-desk/release/.next/server/app/api/skill-files/replace/route.js +1 -1
  143. package/temp/build/pi-desk/release/.next/server/app/api/skills/list/route.js +1 -1
  144. package/temp/build/pi-desk/release/.next/server/app/api/task-center/images/get/route.js +1 -1
  145. package/temp/build/pi-desk/release/.next/server/app/api/task-center/messages/get/route.js +1 -1
  146. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/add/route.js +1 -1
  147. package/temp/build/pi-desk/release/.next/server/app/api/work-session-git-branch/replace/route.js +1 -1
  148. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree/get/route.js +1 -1
  149. package/temp/build/pi-desk/release/.next/server/app/api/work-session-tree-entry/get/route.js +1 -1
  150. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/add/route.js +1 -1
  151. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/branch/route.js +1 -1
  152. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/del/route.js +1 -1
  153. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/rename/route.js +1 -1
  154. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/replace/route.js +1 -1
  155. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/search/route.js +1 -0
  156. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/search/route_client-reference-manifest.js +1 -0
  157. package/temp/build/pi-desk/release/.next/server/app/api/work-sessions/sort/route.js +1 -1
  158. package/temp/build/pi-desk/release/.next/server/app/favicon.ico/route.js +1 -1
  159. package/temp/build/pi-desk/release/.next/server/app/login/page.js +1 -1
  160. package/temp/build/pi-desk/release/.next/server/app/login/page_client-reference-manifest.js +1 -1
  161. package/temp/build/pi-desk/release/.next/server/app/page.js +138 -138
  162. package/temp/build/pi-desk/release/.next/server/app/page_client-reference-manifest.js +1 -1
  163. package/temp/build/pi-desk/release/.next/server/app/project-preview/page.js +1 -1
  164. package/temp/build/pi-desk/release/.next/server/app/project-preview/page_client-reference-manifest.js +1 -1
  165. package/temp/build/pi-desk/release/.next/server/app-paths-manifest.json +1 -0
  166. package/temp/build/pi-desk/release/.next/server/chunks/23054.js +2 -2
  167. package/temp/build/pi-desk/release/.next/server/chunks/25406.js +8 -8
  168. package/temp/build/pi-desk/release/.next/server/chunks/27344.js +3 -3
  169. package/temp/build/pi-desk/release/.next/server/chunks/3108.js +3 -3
  170. package/temp/build/pi-desk/release/.next/server/chunks/35985.js +3 -3
  171. package/temp/build/pi-desk/release/.next/server/chunks/41856.js +1 -0
  172. package/temp/build/pi-desk/release/.next/server/chunks/62900.js +235 -0
  173. package/temp/build/pi-desk/release/.next/server/chunks/6917.js +1 -1
  174. package/temp/build/pi-desk/release/.next/server/chunks/81363.js +1 -1
  175. package/temp/build/pi-desk/release/.next/server/chunks/81912.js +3 -0
  176. package/temp/build/pi-desk/release/.next/server/chunks/92386.js +2 -2
  177. package/temp/build/pi-desk/release/.next/server/middleware-build-manifest.js +1 -1
  178. package/temp/build/pi-desk/release/.next/server/pages/500.html +1 -1
  179. package/temp/build/pi-desk/release/.next/static/ErYBfYXAir5b-6rlacGvL/_buildManifest.js +1 -0
  180. package/temp/build/pi-desk/release/.next/static/chunks/10043-9d6c3735418ccaf9.js +238 -0
  181. package/temp/build/pi-desk/release/.next/static/chunks/11937-ac71fb0f7cef482a.js +1 -0
  182. package/temp/build/pi-desk/release/.next/static/chunks/30669-ffa76d39e0e25194.js +1 -0
  183. package/temp/build/pi-desk/release/.next/static/chunks/36643-f60e98278740073c.js +3 -0
  184. package/temp/build/pi-desk/release/.next/static/chunks/65232-f5b71cec787a30ec.js +15 -0
  185. package/temp/build/pi-desk/release/.next/static/chunks/{51474-8dc714cb76b2e60a.js → 65350-23bb961d87addf14.js} +3 -3
  186. package/temp/build/pi-desk/release/.next/static/chunks/app/_global-error/page-295cb63da5f7262f.js +1 -0
  187. package/temp/build/pi-desk/release/.next/static/chunks/app/_not-found/page-295cb63da5f7262f.js +1 -0
  188. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-295cb63da5f7262f.js +1 -0
  189. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-295cb63da5f7262f.js +1 -0
  190. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/get/route-295cb63da5f7262f.js +1 -0
  191. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/replace/route-295cb63da5f7262f.js +1 -0
  192. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-295cb63da5f7262f.js +1 -0
  193. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-295cb63da5f7262f.js +1 -0
  194. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-295cb63da5f7262f.js +1 -0
  195. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-295cb63da5f7262f.js +1 -0
  196. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-295cb63da5f7262f.js +1 -0
  197. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-295cb63da5f7262f.js +1 -0
  198. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-295cb63da5f7262f.js +1 -0
  199. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-295cb63da5f7262f.js +1 -0
  200. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-295cb63da5f7262f.js +1 -0
  201. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-295cb63da5f7262f.js +1 -0
  202. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-295cb63da5f7262f.js +1 -0
  203. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-295cb63da5f7262f.js +1 -0
  204. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/preview/route-295cb63da5f7262f.js +1 -0
  205. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-295cb63da5f7262f.js +1 -0
  206. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-295cb63da5f7262f.js +1 -0
  207. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-295cb63da5f7262f.js +1 -0
  208. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-295cb63da5f7262f.js +1 -0
  209. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-295cb63da5f7262f.js +1 -0
  210. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-295cb63da5f7262f.js +1 -0
  211. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-295cb63da5f7262f.js +1 -0
  212. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-295cb63da5f7262f.js +1 -0
  213. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-295cb63da5f7262f.js +1 -0
  214. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-295cb63da5f7262f.js +1 -0
  215. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-295cb63da5f7262f.js +1 -0
  216. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-295cb63da5f7262f.js +1 -0
  217. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-295cb63da5f7262f.js +1 -0
  218. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-295cb63da5f7262f.js +1 -0
  219. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-295cb63da5f7262f.js +1 -0
  220. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-295cb63da5f7262f.js +1 -0
  221. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-295cb63da5f7262f.js +1 -0
  222. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-295cb63da5f7262f.js +1 -0
  223. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-295cb63da5f7262f.js +1 -0
  224. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-295cb63da5f7262f.js +1 -0
  225. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-295cb63da5f7262f.js +1 -0
  226. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-295cb63da5f7262f.js +1 -0
  227. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-295cb63da5f7262f.js +1 -0
  228. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-295cb63da5f7262f.js +1 -0
  229. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-295cb63da5f7262f.js +1 -0
  230. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-295cb63da5f7262f.js +1 -0
  231. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-295cb63da5f7262f.js +1 -0
  232. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-295cb63da5f7262f.js +1 -0
  233. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-295cb63da5f7262f.js +1 -0
  234. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-295cb63da5f7262f.js +1 -0
  235. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-295cb63da5f7262f.js +1 -0
  236. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-295cb63da5f7262f.js +1 -0
  237. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-295cb63da5f7262f.js +1 -0
  238. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-295cb63da5f7262f.js +1 -0
  239. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-295cb63da5f7262f.js +1 -0
  240. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-295cb63da5f7262f.js +1 -0
  241. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-295cb63da5f7262f.js +1 -0
  242. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-295cb63da5f7262f.js +1 -0
  243. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-295cb63da5f7262f.js +1 -0
  244. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-295cb63da5f7262f.js +1 -0
  245. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-295cb63da5f7262f.js +1 -0
  246. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-295cb63da5f7262f.js +1 -0
  247. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-295cb63da5f7262f.js +1 -0
  248. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-295cb63da5f7262f.js +1 -0
  249. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-295cb63da5f7262f.js +1 -0
  250. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-295cb63da5f7262f.js +1 -0
  251. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-295cb63da5f7262f.js +1 -0
  252. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-295cb63da5f7262f.js +1 -0
  253. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-295cb63da5f7262f.js +1 -0
  254. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-295cb63da5f7262f.js +1 -0
  255. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/search/route-295cb63da5f7262f.js +1 -0
  256. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-295cb63da5f7262f.js +1 -0
  257. package/temp/build/pi-desk/release/.next/static/chunks/app/error-1ab62762c5f4144f.js +1 -0
  258. package/temp/build/pi-desk/release/.next/static/chunks/app/layout-ca5ef5994165eb2e.js +1 -0
  259. package/temp/build/pi-desk/release/.next/static/chunks/app/login/{page-0d3d4827802108e5.js → page-0df9df996aa68060.js} +1 -1
  260. package/temp/build/pi-desk/release/.next/static/chunks/app/not-found-b8b9b7ef5ccb7601.js +1 -0
  261. package/temp/build/pi-desk/release/.next/static/chunks/app/page-978906de76a8dcfe.js +158 -0
  262. package/temp/build/pi-desk/release/.next/static/chunks/app/project-preview/{page-038b6c14e03ed878.js → page-aaa845016a934245.js} +1 -1
  263. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-295cb63da5f7262f.js +1 -0
  264. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-295cb63da5f7262f.js +1 -0
  265. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-295cb63da5f7262f.js +1 -0
  266. package/temp/build/pi-desk/release/.next/static/chunks/{webpack-c0b805df46fcf8b3.js → webpack-a84310d111797bef.js} +1 -1
  267. package/temp/build/pi-desk/release/.next/static/css/053a8d82c09326cd.css +1 -0
  268. package/temp/build/pi-desk/release/.next/static/css/1cc92eec583aa639.css +1 -0
  269. package/temp/build/pi-desk/release/.next/static/css/{1e780a4936aa6ba3.css → 2bb393927ba8f71d.css} +1 -1
  270. package/temp/build/pi-desk/release/.next/static/css/3a07dcc86ffdf88c.css +1 -0
  271. package/temp/build/pi-desk/release/.next/static/css/3c59ac068ae404da.css +1 -0
  272. package/temp/build/pi-desk/release/.next/trace-build +1 -1
  273. package/temp/build/pi-desk/release/.next/server/chunks/35348.js +0 -3
  274. package/temp/build/pi-desk/release/.next/server/chunks/56830.js +0 -235
  275. package/temp/build/pi-desk/release/.next/server/chunks/80780.js +0 -1
  276. package/temp/build/pi-desk/release/.next/static/bj2dB_Z10YB_CqK_VNhDx/_buildManifest.js +0 -1
  277. package/temp/build/pi-desk/release/.next/static/chunks/11937-1d0a9822f2c7995b.js +0 -1
  278. package/temp/build/pi-desk/release/.next/static/chunks/14197-3d3ddd426ac06c9c.js +0 -3
  279. package/temp/build/pi-desk/release/.next/static/chunks/37313-d5970e2d0c270d22.js +0 -238
  280. package/temp/build/pi-desk/release/.next/static/chunks/65232-ef4e5a4174f62faf.js +0 -15
  281. package/temp/build/pi-desk/release/.next/static/chunks/79267-7d21d7cdbd7cfca1.js +0 -1
  282. package/temp/build/pi-desk/release/.next/static/chunks/app/_global-error/page-9969fe0baa238786.js +0 -1
  283. package/temp/build/pi-desk/release/.next/static/chunks/app/_not-found/page-9969fe0baa238786.js +0 -1
  284. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/login/route-9969fe0baa238786.js +0 -1
  285. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth/logout/route-9969fe0baa238786.js +0 -1
  286. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/get/route-9969fe0baa238786.js +0 -1
  287. package/temp/build/pi-desk/release/.next/static/chunks/app/api/auth-settings/replace/route-9969fe0baa238786.js +0 -1
  288. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/get/route-9969fe0baa238786.js +0 -1
  289. package/temp/build/pi-desk/release/.next/static/chunks/app/api/capability-modes/replace/route-9969fe0baa238786.js +0 -1
  290. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/context/get/route-9969fe0baa238786.js +0 -1
  291. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/images/get/route-9969fe0baa238786.js +0 -1
  292. package/temp/build/pi-desk/release/.next/static/chunks/app/api/chat/messages/get/route-9969fe0baa238786.js +0 -1
  293. package/temp/build/pi-desk/release/.next/static/chunks/app/api/health/route-9969fe0baa238786.js +0 -1
  294. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-check/get/route-9969fe0baa238786.js +0 -1
  295. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/del/route-9969fe0baa238786.js +0 -1
  296. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/get/route-9969fe0baa238786.js +0 -1
  297. package/temp/build/pi-desk/release/.next/static/chunks/app/api/mcp-settings/replace/route-9969fe0baa238786.js +0 -1
  298. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-catalog/list/route-9969fe0baa238786.js +0 -1
  299. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/get/route-9969fe0baa238786.js +0 -1
  300. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/preview/route-9969fe0baa238786.js +0 -1
  301. package/temp/build/pi-desk/release/.next/static/chunks/app/api/model-settings/replace/route-9969fe0baa238786.js +0 -1
  302. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directories/list/route-9969fe0baa238786.js +0 -1
  303. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-entries/list/route-9969fe0baa238786.js +0 -1
  304. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/list/route-9969fe0baa238786.js +0 -1
  305. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/directory-ignores/replace/route-9969fe0baa238786.js +0 -1
  306. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/models/list/route-9969fe0baa238786.js +0 -1
  307. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/session-user-messages/list/route-9969fe0baa238786.js +0 -1
  308. package/temp/build/pi-desk/release/.next/static/chunks/app/api/pi/sessions/list/route-9969fe0baa238786.js +0 -1
  309. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/get/route-9969fe0baa238786.js +0 -1
  310. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/search/route-9969fe0baa238786.js +0 -1
  311. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-get/route-9969fe0baa238786.js +0 -1
  312. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugin-catalog/settings-replace/route-9969fe0baa238786.js +0 -1
  313. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/add/route-9969fe0baa238786.js +0 -1
  314. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/apply/route-9969fe0baa238786.js +0 -1
  315. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/batch/route-9969fe0baa238786.js +0 -1
  316. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/browser-resources/[resourceGroup]/[...resourcePath]/route-9969fe0baa238786.js +0 -1
  317. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/del/route-9969fe0baa238786.js +0 -1
  318. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/enabled-set/route-9969fe0baa238786.js +0 -1
  319. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/get/route-9969fe0baa238786.js +0 -1
  320. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/host-runtime/[runtimeVersion]/[...resourcePath]/route-9969fe0baa238786.js +0 -1
  321. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/list/route-9969fe0baa238786.js +0 -1
  322. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/reload/route-9969fe0baa238786.js +0 -1
  323. package/temp/build/pi-desk/release/.next/static/chunks/app/api/plugins/update/route-9969fe0baa238786.js +0 -1
  324. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/get/route-9969fe0baa238786.js +0 -1
  325. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/list/route-9969fe0baa238786.js +0 -1
  326. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/reveal/route-9969fe0baa238786.js +0 -1
  327. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-files/search/route-9969fe0baa238786.js +0 -1
  328. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git/get/route-9969fe0baa238786.js +0 -1
  329. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-branches/list/route-9969fe0baa238786.js +0 -1
  330. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-changes/list/route-9969fe0baa238786.js +0 -1
  331. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-diff/get/route-9969fe0baa238786.js +0 -1
  332. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-heads/get/route-9969fe0baa238786.js +0 -1
  333. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-git-log/list/route-9969fe0baa238786.js +0 -1
  334. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/get/route-9969fe0baa238786.js +0 -1
  335. package/temp/build/pi-desk/release/.next/static/chunks/app/api/project-model-default/replace/route-9969fe0baa238786.js +0 -1
  336. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/get/route-9969fe0baa238786.js +0 -1
  337. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/list/route-9969fe0baa238786.js +0 -1
  338. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skill-files/replace/route-9969fe0baa238786.js +0 -1
  339. package/temp/build/pi-desk/release/.next/static/chunks/app/api/skills/list/route-9969fe0baa238786.js +0 -1
  340. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/images/get/route-9969fe0baa238786.js +0 -1
  341. package/temp/build/pi-desk/release/.next/static/chunks/app/api/task-center/messages/get/route-9969fe0baa238786.js +0 -1
  342. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/add/route-9969fe0baa238786.js +0 -1
  343. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-git-branch/replace/route-9969fe0baa238786.js +0 -1
  344. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree/get/route-9969fe0baa238786.js +0 -1
  345. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-session-tree-entry/get/route-9969fe0baa238786.js +0 -1
  346. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/add/route-9969fe0baa238786.js +0 -1
  347. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/branch/route-9969fe0baa238786.js +0 -1
  348. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/del/route-9969fe0baa238786.js +0 -1
  349. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/rename/route-9969fe0baa238786.js +0 -1
  350. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/replace/route-9969fe0baa238786.js +0 -1
  351. package/temp/build/pi-desk/release/.next/static/chunks/app/api/work-sessions/sort/route-9969fe0baa238786.js +0 -1
  352. package/temp/build/pi-desk/release/.next/static/chunks/app/error-216b14b5da089ed3.js +0 -1
  353. package/temp/build/pi-desk/release/.next/static/chunks/app/layout-20718f1503910291.js +0 -1
  354. package/temp/build/pi-desk/release/.next/static/chunks/app/not-found-720cdf6a606b821a.js +0 -1
  355. package/temp/build/pi-desk/release/.next/static/chunks/app/page-ce2ebf7d8ea7a330.js +0 -158
  356. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/app-error-9969fe0baa238786.js +0 -1
  357. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/forbidden-9969fe0baa238786.js +0 -1
  358. package/temp/build/pi-desk/release/.next/static/chunks/next/dist/client/components/builtin/unauthorized-9969fe0baa238786.js +0 -1
  359. package/temp/build/pi-desk/release/.next/static/css/2bdcf40eca12b794.css +0 -1
  360. package/temp/build/pi-desk/release/.next/static/css/2c3b0c0ff1f49c68.css +0 -1
  361. package/temp/build/pi-desk/release/.next/static/css/c43b04c244a64fb4.css +0 -1
  362. /package/temp/build/pi-desk/release/.next/static/{bj2dB_Z10YB_CqK_VNhDx → ErYBfYXAir5b-6rlacGvL}/_ssgManifest.js +0 -0
@@ -17,17 +17,14 @@ import {
17
17
  SearchIcon,
18
18
  UserIcon
19
19
  } from 'lucide-react'
20
- import { useEffect, useMemo, useRef, useState, type ReactNode } from 'react'
20
+ import { useMemo, useState } from 'react'
21
21
  import { useTranslation } from 'react-i18next'
22
22
  import type { TFunction } from 'i18next'
23
23
  import { useL4Region } from '@client/l4_foundation/locale/l4-region-provider'
24
24
  import type {
25
25
  L2PiDirectoryEntryListResponse,
26
26
  L2PiDirectoryListResponse,
27
- L2PiSessionHistoryResponse,
28
- L2PiSessionHistorySearchScope,
29
- L2PiSessionUserMessageListRequest,
30
- L2PiSessionUserMessageListResponse
27
+ L2PiSessionHistoryResponse
31
28
  } from '@common/l2_biz/pi-session/l2-pi-session-contract'
32
29
  import {
33
30
  L4AppDialogBody,
@@ -40,6 +37,14 @@ import {
40
37
  } from '@client/l4_foundation/ui/l4-app-dialog'
41
38
  import { Input } from '@client/l4_foundation/ui/shadcn/input'
42
39
  import { buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
40
+ import type { L2WorkbenchBiz } from './l2-workbench-biz'
41
+ import { useL2WorkbenchSessionHistory } from './hooks/l2-workbench-session-history'
42
+ import { L2_WORKBENCH_DEFAULT_SEARCH_IN } from './hooks/l2-workbench-session-search'
43
+ import {
44
+ highlightL2WorkbenchSessionSearchText as highlightedText,
45
+ L2WorkbenchSessionSearchScopes
46
+ } from './views/l2-workbench-session-search'
47
+ import type { L3PiSessionSearchScope } from '@common/l3_modules/pi/l3-pi-session-search-contract'
43
48
 
44
49
  export type L2WorkbenchPickerMode = 'create' | 'replace' | null
45
50
  export type L2WorkbenchPickerView = 'directories' | 'history' | 'ignored' | 'browser'
@@ -58,7 +63,6 @@ interface L2WorkbenchPickerDialogProps {
58
63
  directories: L2PiDirectoryListResponse['directories']
59
64
  ignoredDirectories: L2PiDirectoryListResponse['directories']
60
65
  directoryBrowser: L2PiDirectoryEntryListResponse | null
61
- historySessions: L2PiSessionHistoryResponse['sessions']
62
66
  replacementTarget: L2WorkbenchReplacementTarget | null
63
67
  unavailableSessionIds: readonly string[]
64
68
  error: string | null
@@ -68,12 +72,9 @@ interface L2WorkbenchPickerDialogProps {
68
72
  updatingIgnoreCwd: string | null
69
73
  onOpenChange: (open: boolean) => void
70
74
  onBack: () => void
71
- onSearchHistory: (query: string, searchIn: readonly L2PiSessionHistorySearchScope[]) => void
72
- onListSessionUserMessages: (
73
- input: L2PiSessionUserMessageListRequest,
74
- signal?: AbortSignal
75
- ) => Promise<L2PiSessionUserMessageListResponse>
76
- onRefresh: (query: string, searchIn: readonly L2PiSessionHistorySearchScope[]) => void
75
+ onListSessionHistory: L2WorkbenchBiz['listSessionHistory']
76
+ onListSessionUserMessages: L2WorkbenchBiz['listSessionUserMessages']
77
+ onRefresh: () => void
77
78
  onOpenIgnoredDirectories: () => void
78
79
  onOpenDirectoryBrowser: () => void
79
80
  onNavigateDirectory: (cwd: string | null) => void
@@ -110,20 +111,8 @@ function normalizeSearch(value: string): string {
110
111
  return value.trim().toLocaleLowerCase()
111
112
  }
112
113
 
113
- const DEFAULT_HISTORY_SEARCH_IN: readonly L2PiSessionHistorySearchScope[] = ['title', 'user']
114
-
115
- const HISTORY_SEARCH_SCOPE_OPTIONS: ReadonlyArray<{
116
- value: L2PiSessionHistorySearchScope
117
- label: string
118
- }> = [
119
- { value: 'title', label: 'scopeTitle' },
120
- { value: 'user', label: 'scopeUser' },
121
- { value: 'assistant', label: 'scopeAssistant' },
122
- { value: 'sessionId', label: 'Session ID' }
123
- ]
124
-
125
114
  function historyMatchLabel(
126
- scope: L2PiSessionHistorySearchScope,
115
+ scope: L3PiSessionSearchScope,
127
116
  count: number,
128
117
  t: TFunction<'workbenchPicker'>
129
118
  ): string {
@@ -132,7 +121,7 @@ function historyMatchLabel(
132
121
  return t(scope === 'user' ? 'scopeUserMatches' : 'scopeAssistantMatches', { count })
133
122
  }
134
123
 
135
- function historyMatchTone(scope: L2PiSessionHistorySearchScope): string {
124
+ function historyMatchTone(scope: L3PiSessionSearchScope): string {
136
125
  switch (scope) {
137
126
  case 'user':
138
127
  return 'bg-muted text-foreground'
@@ -145,7 +134,7 @@ function historyMatchTone(scope: L2PiSessionHistorySearchScope): string {
145
134
  }
146
135
  }
147
136
 
148
- function historyMatchMarkTone(scope: L2PiSessionHistorySearchScope): string {
137
+ function historyMatchMarkTone(scope: L3PiSessionSearchScope): string {
149
138
  switch (scope) {
150
139
  case 'user':
151
140
  return 'bg-accent text-accent-foreground'
@@ -162,7 +151,7 @@ function HistoryMatchIcon({
162
151
  scope,
163
152
  className
164
153
  }: {
165
- scope: L2PiSessionHistorySearchScope
154
+ scope: L3PiSessionSearchScope
166
155
  className: string
167
156
  }): React.JSX.Element {
168
157
  switch (scope) {
@@ -192,40 +181,6 @@ function isAbsoluteBrowserDirectoryPath(path: string): boolean {
192
181
  return /^(?:[A-Za-z]:[\\/]|[\\/]{2}|\/)/.test(path)
193
182
  }
194
183
 
195
- function highlightedText(
196
- text: string,
197
- query: string,
198
- markClassName = 'bg-primary/20 text-foreground'
199
- ): ReactNode {
200
- if (!query) return text
201
- const normalizedText = text.toLocaleLowerCase()
202
- const parts: ReactNode[] = []
203
- let cursor = 0
204
- let matchIndex = normalizedText.indexOf(query)
205
- while (matchIndex >= 0) {
206
- if (matchIndex > cursor) parts.push(text.slice(cursor, matchIndex))
207
- const matchEnd = matchIndex + query.length
208
- parts.push(
209
- <mark key={`${matchIndex}-${matchEnd}`} className={`rounded-sm px-px ${markClassName}`}>
210
- {text.slice(matchIndex, matchEnd)}
211
- </mark>
212
- )
213
- cursor = matchEnd
214
- matchIndex = normalizedText.indexOf(query, cursor)
215
- }
216
- if (cursor < text.length) parts.push(text.slice(cursor))
217
- return parts.length ? parts : text
218
- }
219
-
220
- interface L2ExpandedHistoryMessages {
221
- sessionId: string
222
- query: string
223
- messages: L2PiSessionUserMessageListResponse['messages']
224
- page: L2PiSessionUserMessageListResponse['page'] | null
225
- loading: boolean
226
- error: string | null
227
- }
228
-
229
184
  export function L2WorkbenchPickerDialog({
230
185
  open,
231
186
  pickerMode,
@@ -234,17 +189,16 @@ export function L2WorkbenchPickerDialog({
234
189
  directories,
235
190
  ignoredDirectories,
236
191
  directoryBrowser,
237
- historySessions,
238
192
  replacementTarget,
239
193
  unavailableSessionIds,
240
- error,
241
- loading,
194
+ error: pickerError,
195
+ loading: pickerLoading,
242
196
  creatingCwd,
243
197
  replacingSessionId,
244
198
  updatingIgnoreCwd,
245
199
  onOpenChange,
246
200
  onBack,
247
- onSearchHistory,
201
+ onListSessionHistory,
248
202
  onListSessionUserMessages,
249
203
  onRefresh,
250
204
  onOpenIgnoredDirectories,
@@ -271,14 +225,35 @@ export function L2WorkbenchPickerDialog({
271
225
  )
272
226
  const pickerTime = (timestamp: number): string => timeFormatter.format(timestamp)
273
227
  const [search, setSearch] = useState('')
274
- const [searchIn, setSearchIn] =
275
- useState<readonly L2PiSessionHistorySearchScope[]>(DEFAULT_HISTORY_SEARCH_IN)
228
+ const [searchIn, setSearchIn] = useState<readonly L3PiSessionSearchScope[]>(
229
+ L2_WORKBENCH_DEFAULT_SEARCH_IN
230
+ )
276
231
  const [browserPathInput, setBrowserPathInput] = useState('')
277
- const [expandedHistory, setExpandedHistory] = useState<L2ExpandedHistoryMessages | null>(null)
278
- const lastRequestedHistorySearchRef = useRef('')
279
- const userMessageRequestIdRef = useRef(0)
280
- const userMessageAbortControllerRef = useRef<AbortController | null>(null)
232
+ const searchContext = JSON.stringify([open, pickerMode, pickerCwd, pickerView])
233
+ const [previousSearchContext, setPreviousSearchContext] = useState(searchContext)
234
+ if (previousSearchContext !== searchContext) {
235
+ setPreviousSearchContext(searchContext)
236
+ setSearch('')
237
+ setSearchIn(L2_WORKBENCH_DEFAULT_SEARCH_IN)
238
+ setBrowserPathInput('')
239
+ }
281
240
  const working = creatingCwd !== null || replacingSessionId !== null || updatingIgnoreCwd !== null
241
+ const history = useL2WorkbenchSessionHistory({
242
+ active: open && pickerView === 'history' && !working,
243
+ cwd: pickerCwd,
244
+ query: search,
245
+ searchIn,
246
+ listSessionHistory: onListSessionHistory,
247
+ listSessionUserMessages: onListSessionUserMessages
248
+ })
249
+ const {
250
+ sessions: historySessions,
251
+ expandedHistory,
252
+ toggleUserMessages,
253
+ loadUserMessages
254
+ } = history
255
+ const loading = pickerView === 'history' ? history.loading : pickerLoading
256
+ const error = pickerError ?? (pickerView === 'history' ? history.error : null)
282
257
  const unavailableSessionIdSet = useMemo(
283
258
  () => new Set(unavailableSessionIds),
284
259
  [unavailableSessionIds]
@@ -297,127 +272,6 @@ export function L2WorkbenchPickerDialog({
297
272
  return normalizeSearch(`${directory.name} ${directory.cwd}`).includes(query)
298
273
  })
299
274
 
300
- useEffect(() => {
301
- setSearch('')
302
- setSearchIn(DEFAULT_HISTORY_SEARCH_IN)
303
- setBrowserPathInput('')
304
- lastRequestedHistorySearchRef.current = ''
305
- }, [open, pickerMode, pickerCwd, pickerView])
306
-
307
- const historySearchKey = `${query}\u0000${searchIn.join('\u0000')}`
308
- useEffect(() => {
309
- if (
310
- pickerView !== 'history' ||
311
- !query ||
312
- historySearchKey === lastRequestedHistorySearchRef.current
313
- ) {
314
- return
315
- }
316
- const timer = window.setTimeout(() => {
317
- lastRequestedHistorySearchRef.current = historySearchKey
318
- onSearchHistory(query, searchIn)
319
- }, 300)
320
- return () => window.clearTimeout(timer)
321
- }, [historySearchKey, onSearchHistory, pickerView, query, searchIn])
322
-
323
- useEffect(() => {
324
- userMessageRequestIdRef.current += 1
325
- userMessageAbortControllerRef.current?.abort()
326
- userMessageAbortControllerRef.current = null
327
- setExpandedHistory(null)
328
- }, [open, pickerCwd, pickerView, query])
329
-
330
- useEffect(
331
- () => () => {
332
- userMessageAbortControllerRef.current?.abort()
333
- },
334
- []
335
- )
336
-
337
- const loadUserMessages = async (
338
- sessionId: string,
339
- messageQuery: string,
340
- pageIndex: number,
341
- append: boolean
342
- ): Promise<void> => {
343
- if (!pickerCwd) return
344
- const requestId = ++userMessageRequestIdRef.current
345
- userMessageAbortControllerRef.current?.abort()
346
- const controller = new AbortController()
347
- userMessageAbortControllerRef.current = controller
348
- setExpandedHistory((current) => ({
349
- sessionId,
350
- query: messageQuery,
351
- messages: append && current?.sessionId === sessionId ? current.messages : [],
352
- page: append && current?.sessionId === sessionId ? current.page : null,
353
- loading: true,
354
- error: null
355
- }))
356
- try {
357
- const response = await onListSessionUserMessages(
358
- {
359
- cwd: pickerCwd,
360
- sessionId,
361
- query: messageQuery,
362
- page: { index: pageIndex, size: 20 }
363
- },
364
- controller.signal
365
- )
366
- if (userMessageRequestIdRef.current !== requestId) return
367
- setExpandedHistory((current) => ({
368
- sessionId,
369
- query: messageQuery,
370
- messages:
371
- append && current?.sessionId === sessionId
372
- ? [...current.messages, ...response.messages]
373
- : response.messages,
374
- page: response.page,
375
- loading: false,
376
- error: null
377
- }))
378
- } catch (cause) {
379
- if (
380
- userMessageRequestIdRef.current !== requestId ||
381
- (cause instanceof Error && cause.name === 'AbortError')
382
- ) {
383
- return
384
- }
385
- setExpandedHistory((current) => ({
386
- sessionId,
387
- query: messageQuery,
388
- messages: current?.sessionId === sessionId ? current.messages : [],
389
- page: current?.sessionId === sessionId ? current.page : null,
390
- loading: false,
391
- error: cause instanceof Error ? cause.message : t('loadUserFailed')
392
- }))
393
- } finally {
394
- if (userMessageAbortControllerRef.current === controller) {
395
- userMessageAbortControllerRef.current = null
396
- }
397
- }
398
- }
399
-
400
- const toggleSearchScope = (scope: L2PiSessionHistorySearchScope): void => {
401
- setSearchIn((current) => {
402
- if (current.includes(scope)) {
403
- return current.length === 1 ? current : current.filter((item) => item !== scope)
404
- }
405
- return [...current, scope]
406
- })
407
- }
408
-
409
- const toggleUserMessages = (session: L2PiSessionHistoryResponse['sessions'][number]): void => {
410
- if (expandedHistory?.sessionId === session.sessionId) {
411
- userMessageRequestIdRef.current += 1
412
- userMessageAbortControllerRef.current?.abort()
413
- userMessageAbortControllerRef.current = null
414
- setExpandedHistory(null)
415
- return
416
- }
417
- const messageQuery = session.matches?.some((match) => match.scope === 'user') ? query : ''
418
- void loadUserMessages(session.sessionId, messageQuery, 1, false)
419
- }
420
-
421
275
  const title =
422
276
  pickerView === 'ignored'
423
277
  ? t('ignoredRestoreTitle')
@@ -565,7 +419,9 @@ export function L2WorkbenchPickerDialog({
565
419
  <div className="relative">
566
420
  <SearchIcon className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground" />
567
421
  <Input
422
+ data-testid={pickerView === 'history' ? 'workbench-history-search-input' : undefined}
568
423
  value={search}
424
+ maxLength={pickerView === 'history' ? 120 : undefined}
569
425
  onChange={(event) => setSearch(event.target.value)}
570
426
  placeholder={searchPlaceholder}
571
427
  aria-label={searchPlaceholder}
@@ -574,27 +430,11 @@ export function L2WorkbenchPickerDialog({
574
430
  />
575
431
  </div>
576
432
  {pickerView === 'history' ? (
577
- <fieldset className="mt-2 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5 px-0.5">
578
- <legend className="mr-1 text-xs text-muted-foreground">{t('searchScopes')}</legend>
579
- {HISTORY_SEARCH_SCOPE_OPTIONS.map((option) => {
580
- const checked = searchIn.includes(option.value)
581
- return (
582
- <label
583
- key={option.value}
584
- className="flex min-h-7 cursor-pointer items-center gap-2 text-sm text-muted-foreground has-[:focus-visible]:rounded-sm has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring"
585
- >
586
- <input
587
- type="checkbox"
588
- checked={checked}
589
- disabled={working || (checked && searchIn.length === 1)}
590
- onChange={() => toggleSearchScope(option.value)}
591
- className="size-4 accent-primary"
592
- />
593
- <span>{t(option.label)}</span>
594
- </label>
595
- )
596
- })}
597
- </fieldset>
433
+ <L2WorkbenchSessionSearchScopes
434
+ value={searchIn}
435
+ onChange={setSearchIn}
436
+ disabled={working}
437
+ />
598
438
  ) : null}
599
439
  </div>
600
440
 
@@ -1130,7 +970,7 @@ export function L2WorkbenchPickerDialog({
1130
970
  data-testid="workbench-picker-refresh"
1131
971
  type="button"
1132
972
  disabled={loading || working}
1133
- onClick={() => onRefresh(query, searchIn)}
973
+ onClick={pickerView === 'history' ? history.refresh : onRefresh}
1134
974
  aria-label={t('refreshList')}
1135
975
  title={t('refreshList')}
1136
976
  className={buttonVariants({ variant: 'outline', size: 'icon' })}
@@ -116,7 +116,7 @@ import { useL2WorkSessionRename } from './hooks/l2-work-session-rename'
116
116
  import { useL2WorkbenchChatReload } from './hooks/l2-workbench-chat-reload'
117
117
  import { L2WorkSessionRenameDialog } from './views/l2-work-session-rename-dialog'
118
118
  import { useL2WorkbenchPluginHealth } from './hooks/l2-workbench-plugin-health'
119
- import { useL2WorkbenchSessionHistory } from './hooks/l2-workbench-session-history'
119
+ import { useL2WorkSessionSearch } from './hooks/l2-work-session-search'
120
120
  import {
121
121
  getL2PluginSessionSidebarMode,
122
122
  L2WorkSessionSidebar,
@@ -356,13 +356,6 @@ export function L2Workbench({
356
356
  const [updatingIgnoreCwd, setUpdatingIgnoreCwd] = useState<string | null>(null)
357
357
  const [replacingWithEmptyWorkId, setReplacingWithEmptyWorkId] = useState<string | null>(null)
358
358
  const [pickerError, setPickerError] = useState<string | null>(null)
359
- const {
360
- sessions: historySessions,
361
- loading: historyLoading,
362
- load: loadSessionHistory,
363
- cancel: cancelSessionHistory,
364
- showCached: showCachedSessionHistory
365
- } = useL2WorkbenchSessionHistory(workbenchBiz, setPickerError)
366
359
  const [error, setError] = useState<string | null>(null)
367
360
  const [connectionAttempt, setConnectionAttempt] = useState(0)
368
361
  const reloadPromiseRef = useRef<Promise<void> | null>(null)
@@ -528,13 +521,11 @@ export function L2Workbench({
528
521
  }, [activePluginSessionTab, activePluginSessionTabDescriptor, activeWorkSession, sidebarMode])
529
522
 
530
523
  const pickerLoading =
531
- pickerView === 'history'
532
- ? historyLoading
533
- : pickerView === 'ignored'
534
- ? ignoredDirectoriesLoading
535
- : pickerView === 'browser'
536
- ? directoryBrowserLoading
537
- : directoriesLoading
524
+ pickerView === 'ignored'
525
+ ? ignoredDirectoriesLoading
526
+ : pickerView === 'browser'
527
+ ? directoryBrowserLoading
528
+ : directoriesLoading
538
529
  const replacementTarget = useMemo<L2WorkbenchReplacementTarget | null>(() => {
539
530
  const target = workSessions.find((workSession) => workSession.workId === replacementWorkId)
540
531
  if (!target) return null
@@ -1231,7 +1222,6 @@ export function L2Workbench({
1231
1222
  setIgnoredDirectoriesLoading(false)
1232
1223
  directoryBrowserRequestIdRef.current += 1
1233
1224
  setDirectoryBrowserLoading(false)
1234
- cancelSessionHistory()
1235
1225
  setPickerCwd(null)
1236
1226
  setDirectoryDialogOpen(true)
1237
1227
  void loadDirectories().then((loadedDirectories) => {
@@ -1240,7 +1230,7 @@ export function L2Workbench({
1240
1230
  setDirectoryBrowser(readCachedDirectoryEntries(readL4PiDirectoryEntriesCache(null)))
1241
1231
  void loadDirectoryEntries(null)
1242
1232
  })
1243
- }, [cancelSessionHistory, loadDirectories, loadDirectoryEntries, loading])
1233
+ }, [loadDirectories, loadDirectoryEntries, loading])
1244
1234
 
1245
1235
  const openReplacePicker = useCallback((): void => {
1246
1236
  if (loading) return
@@ -1258,10 +1248,8 @@ export function L2Workbench({
1258
1248
  setPickerView('history')
1259
1249
  setReplacementWorkId(target.workId)
1260
1250
  setPickerCwd(target.cwd)
1261
- showCachedSessionHistory(target.cwd)
1262
1251
  setDirectoryDialogOpen(true)
1263
- void loadSessionHistory(target.cwd)
1264
- }, [activeWorkId, loadSessionHistory, loading, showCachedSessionHistory, workSessions])
1252
+ }, [activeWorkId, loading, workSessions])
1265
1253
 
1266
1254
  const returnToDirectoryList = (): void => {
1267
1255
  setPickerError(null)
@@ -1269,7 +1257,6 @@ export function L2Workbench({
1269
1257
  setIgnoredDirectoriesLoading(false)
1270
1258
  directoryBrowserRequestIdRef.current += 1
1271
1259
  setDirectoryBrowserLoading(false)
1272
- cancelSessionHistory()
1273
1260
  const cachedDirectories = readCachedDirectoryList(readL4PiDirectoriesCache())
1274
1261
  if (cachedDirectories) setDirectories(cachedDirectories)
1275
1262
  setPickerView('directories')
@@ -1371,6 +1358,7 @@ export function L2Workbench({
1371
1358
  selectEntry: selectBranchEntry,
1372
1359
  requestAction: requestBranchAction,
1373
1360
  clone: cloneWorkSession,
1361
+ resend: resendMessage,
1374
1362
  confirmRunning: confirmRunningBranch,
1375
1363
  cancelRunning: cancelRunningBranch,
1376
1364
  pendingWorkId: branchPendingWorkId
@@ -1497,7 +1485,6 @@ export function L2Workbench({
1497
1485
  }
1498
1486
 
1499
1487
  const workId = replacementWorkId
1500
- cancelSessionHistory()
1501
1488
  localBindingOperationsRef.current.add(workId)
1502
1489
  setReplacingSessionId(sessionId)
1503
1490
  setPickerError(null)
@@ -1832,6 +1819,11 @@ export function L2Workbench({
1832
1819
  )
1833
1820
 
1834
1821
  const sidebarOpen = mobile ? mobileDrawerOpen : desktopSidebarOpen
1822
+ const sessionSearch = useL2WorkSessionSearch({
1823
+ active: sidebarOpen && sidebarMode === 'sessions' && !loading,
1824
+ workSessions,
1825
+ searchWorkSessions: workbenchBiz.searchWorkSessions
1826
+ })
1835
1827
  const validGitBranchPickerTarget =
1836
1828
  gitBranchPickerTarget &&
1837
1829
  workSessions.some(
@@ -1891,6 +1883,7 @@ export function L2Workbench({
1891
1883
  const sidebar = (
1892
1884
  <L2WorkSessionSidebar
1893
1885
  mode={sidebarMode}
1886
+ search={sessionSearch}
1894
1887
  mobile={mobile}
1895
1888
  fileBrowser={fileBrowser}
1896
1889
  pluginTabContent={pluginTabContent}
@@ -1994,6 +1987,7 @@ export function L2Workbench({
1994
1987
  setChatCapabilityMode={workbenchBiz.setChatCapabilityMode}
1995
1988
  branchPicker={branchPicker}
1996
1989
  branchPendingWorkId={branchPendingWorkId}
1990
+ onResendMessage={resendMessage}
1997
1991
  fileRuntime={fileRuntime}
1998
1992
  onOpenFileSidebar={openFileSidebar}
1999
1993
  onPreviewFile={previewConversationFile}
@@ -2046,7 +2040,7 @@ export function L2Workbench({
2046
2040
  )
2047
2041
 
2048
2042
  return (
2049
- <main className="pi-desk-workbench flex h-dvh overflow-hidden bg-background">
2043
+ <main className="pi-desk-workbench flex h-[var(--pi-desk-content-height)] overflow-hidden bg-background">
2050
2044
  <L2WorkbenchSidebarLayout
2051
2045
  mobile={mobile}
2052
2046
  open={sidebarOpen}
@@ -2127,7 +2121,6 @@ export function L2Workbench({
2127
2121
  directories={directories}
2128
2122
  ignoredDirectories={ignoredDirectories}
2129
2123
  directoryBrowser={directoryBrowser}
2130
- historySessions={historySessions}
2131
2124
  replacementTarget={pickerMode === 'create' ? null : replacementTarget}
2132
2125
  unavailableSessionIds={unavailableSessionIds}
2133
2126
  error={pickerError}
@@ -2144,25 +2137,16 @@ export function L2Workbench({
2144
2137
  setIgnoredDirectoriesLoading(false)
2145
2138
  directoryBrowserRequestIdRef.current += 1
2146
2139
  setDirectoryBrowserLoading(false)
2147
- cancelSessionHistory()
2148
2140
  setPickerMode(null)
2149
2141
  setPickerView('directories')
2150
2142
  setPickerError(null)
2151
2143
  }
2152
2144
  }}
2153
2145
  onBack={returnToDirectoryList}
2154
- onSearchHistory={(query, searchIn) => {
2155
- if (pickerView === 'history' && pickerCwd) {
2156
- void loadSessionHistory(pickerCwd, query, searchIn)
2157
- }
2158
- }}
2159
- onListSessionUserMessages={(input, signal) =>
2160
- workbenchBiz.listSessionUserMessages(input, signal)
2161
- }
2162
- onRefresh={(query, searchIn) => {
2163
- if (pickerView === 'history' && pickerCwd) {
2164
- void loadSessionHistory(pickerCwd, query, searchIn, true)
2165
- } else if (pickerView === 'ignored') void loadIgnoredDirectories(true)
2146
+ onListSessionHistory={workbenchBiz.listSessionHistory}
2147
+ onListSessionUserMessages={workbenchBiz.listSessionUserMessages}
2148
+ onRefresh={() => {
2149
+ if (pickerView === 'ignored') void loadIgnoredDirectories(true)
2166
2150
  else if (pickerView === 'browser') {
2167
2151
  void loadDirectoryEntries(directoryBrowser?.cwd ?? null)
2168
2152
  } else void loadDirectories(true)
@@ -2178,8 +2162,6 @@ export function L2Workbench({
2178
2162
  setDirectoriesLoading(false)
2179
2163
  setPickerView('history')
2180
2164
  setPickerCwd(cwd)
2181
- showCachedSessionHistory(cwd)
2182
- void loadSessionHistory(cwd)
2183
2165
  } else {
2184
2166
  void createWorkSession(cwd)
2185
2167
  }
@@ -0,0 +1,123 @@
1
+ 'use client'
2
+
3
+ import type { ReactNode } from 'react'
4
+ import { useTranslation } from 'react-i18next'
5
+ import { SlidersHorizontalIcon } from 'lucide-react'
6
+ import type { L3PiSessionSearchScope } from '@common/l3_modules/pi/l3-pi-session-search-contract'
7
+ import { buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
8
+ import {
9
+ DropdownMenu,
10
+ DropdownMenuCheckboxItem,
11
+ DropdownMenuContent,
12
+ DropdownMenuTrigger
13
+ } from '@client/l4_foundation/ui/shadcn/dropdown-menu'
14
+
15
+ const SEARCH_SCOPE_OPTIONS: ReadonlyArray<{ value: L3PiSessionSearchScope; label: string }> = [
16
+ { value: 'title', label: 'scopeTitle' },
17
+ { value: 'user', label: 'scopeUser' },
18
+ { value: 'assistant', label: 'scopeAssistant' },
19
+ { value: 'sessionId', label: 'Session ID' }
20
+ ]
21
+
22
+ export function L2WorkbenchSessionSearchScopes({
23
+ value,
24
+ onChange,
25
+ disabled = false,
26
+ compact = false
27
+ }: {
28
+ value: readonly L3PiSessionSearchScope[]
29
+ onChange: (value: readonly L3PiSessionSearchScope[]) => void
30
+ disabled?: boolean
31
+ compact?: boolean
32
+ }): React.JSX.Element {
33
+ const { t } = useTranslation('workbenchPicker')
34
+ const toggle = (scope: L3PiSessionSearchScope): void => {
35
+ if (value.includes(scope)) {
36
+ if (value.length > 1) onChange(value.filter((item) => item !== scope))
37
+ } else {
38
+ onChange([...value, scope])
39
+ }
40
+ }
41
+ if (compact) {
42
+ return (
43
+ <DropdownMenu>
44
+ <DropdownMenuTrigger
45
+ render={
46
+ <button
47
+ data-testid="work-session-search-scopes"
48
+ type="button"
49
+ disabled={disabled}
50
+ aria-label={t('searchScopes')}
51
+ title={t('searchScopes')}
52
+ className={buttonVariants({ variant: 'ghost', size: 'icon' })}
53
+ >
54
+ <SlidersHorizontalIcon className="size-4" />
55
+ </button>
56
+ }
57
+ />
58
+ <DropdownMenuContent align="end">
59
+ {SEARCH_SCOPE_OPTIONS.map((option) => (
60
+ <DropdownMenuCheckboxItem
61
+ key={option.value}
62
+ checked={value.includes(option.value)}
63
+ disabled={disabled || (value.length === 1 && value.includes(option.value))}
64
+ closeOnClick={false}
65
+ onCheckedChange={() => toggle(option.value)}
66
+ >
67
+ {t(option.label)}
68
+ </DropdownMenuCheckboxItem>
69
+ ))}
70
+ </DropdownMenuContent>
71
+ </DropdownMenu>
72
+ )
73
+ }
74
+ return (
75
+ <fieldset className="mt-2 flex min-w-0 flex-wrap items-center gap-x-3 gap-y-1.5 px-0.5">
76
+ <legend className="mr-1 text-xs text-muted-foreground">{t('searchScopes')}</legend>
77
+ {SEARCH_SCOPE_OPTIONS.map((option) => {
78
+ const checked = value.includes(option.value)
79
+ return (
80
+ <label
81
+ key={option.value}
82
+ className="flex min-h-7 cursor-pointer items-center gap-2 text-sm text-muted-foreground has-[:focus-visible]:rounded-sm has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring"
83
+ >
84
+ <input
85
+ type="checkbox"
86
+ checked={checked}
87
+ disabled={disabled || (checked && value.length === 1)}
88
+ onChange={() => toggle(option.value)}
89
+ className="size-4 accent-primary"
90
+ />
91
+ <span>{t(option.label)}</span>
92
+ </label>
93
+ )
94
+ })}
95
+ </fieldset>
96
+ )
97
+ }
98
+
99
+ export function highlightL2WorkbenchSessionSearchText(
100
+ text: string,
101
+ query: string,
102
+ markClassName = 'bg-accent text-accent-foreground'
103
+ ): ReactNode {
104
+ const normalizedQuery = query.trim().toLocaleLowerCase()
105
+ if (!normalizedQuery) return text
106
+ const normalizedText = text.toLocaleLowerCase()
107
+ const parts: ReactNode[] = []
108
+ let cursor = 0
109
+ let matchIndex = normalizedText.indexOf(normalizedQuery)
110
+ while (matchIndex >= 0) {
111
+ if (matchIndex > cursor) parts.push(text.slice(cursor, matchIndex))
112
+ const matchEnd = matchIndex + normalizedQuery.length
113
+ parts.push(
114
+ <mark key={`${matchIndex}-${matchEnd}`} className={`rounded-sm px-px ${markClassName}`}>
115
+ {text.slice(matchIndex, matchEnd)}
116
+ </mark>
117
+ )
118
+ cursor = matchEnd
119
+ matchIndex = normalizedText.indexOf(normalizedQuery, cursor)
120
+ }
121
+ if (cursor < text.length) parts.push(text.slice(cursor))
122
+ return parts.length ? parts : text
123
+ }