@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
@@ -30,6 +30,8 @@ import {
30
30
  PlusIcon,
31
31
  RefreshCwIcon,
32
32
  Repeat2Icon,
33
+ SearchIcon,
34
+ XIcon,
33
35
  SettingsIcon,
34
36
  TerminalIcon,
35
37
  Trash2Icon
@@ -58,6 +60,13 @@ import { L4PluginIcon } from '@client/l4_foundation/plugin-host/l4-plugin-icon'
58
60
  import { L4ScrollArea } from '@client/l4_foundation/ui/l4-scroll-area'
59
61
  import { useL4AppToast } from '@client/l4_foundation/ui/l4-app-toast'
60
62
  import { buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
63
+ import { Input } from '@client/l4_foundation/ui/shadcn/input'
64
+ import type { L3PiSessionSearchMatch } from '@common/l3_modules/pi/l3-pi-session-search-contract'
65
+ import type { L2WorkSessionSearch } from './hooks/l2-work-session-search'
66
+ import {
67
+ highlightL2WorkbenchSessionSearchText,
68
+ L2WorkbenchSessionSearchScopes
69
+ } from './views/l2-workbench-session-search'
61
70
  import {
62
71
  ContextMenu,
63
72
  ContextMenuContent,
@@ -110,6 +119,7 @@ export function getL2PluginSessionSidebarMode(
110
119
 
111
120
  interface L2WorkSessionSidebarProps {
112
121
  mode: L2WorkSessionSidebarMode
122
+ search: L2WorkSessionSearch
113
123
  mobile: boolean
114
124
  fileBrowser: ReactNode
115
125
  pluginTabContent: ReactNode
@@ -349,7 +359,10 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
349
359
  onDelete,
350
360
  onRename,
351
361
  syncing,
352
- onReloadChat
362
+ onReloadChat,
363
+ searchQuery,
364
+ searchMatches,
365
+ dragDisabled
353
366
  }: {
354
367
  workSession: L2WorkSessionListItem
355
368
  mobile: boolean
@@ -365,6 +378,9 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
365
378
  onRename: (workSession: L2WorkSessionListItem) => void
366
379
  syncing: boolean
367
380
  onReloadChat: (workSession: L2WorkSessionListItem) => void
381
+ searchQuery: string
382
+ searchMatches: readonly L3PiSessionSearchMatch[]
383
+ dragDisabled: boolean
368
384
  }): React.JSX.Element {
369
385
  const suppressTouchClick = useRef(false)
370
386
  const lastPointer = useRef('')
@@ -377,9 +393,10 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
377
393
  transform,
378
394
  transition,
379
395
  isDragging
380
- } = useSortable({ id: workSession.workId, disabled: actionsDisabled })
396
+ } = useSortable({ id: workSession.workId, disabled: actionsDisabled || dragDisabled })
381
397
  const toast = useL4AppToast()
382
398
  const { t } = useTranslation('workbench')
399
+ const { t: searchT } = useTranslation('workbenchPicker')
383
400
  const { locale, timeZone } = useL4Region()
384
401
  const statusLabel = t(workSessionStatusLabel(workSession))
385
402
  const sessionTitle = workSession.sessionTitle ?? t('newSession')
@@ -468,7 +485,7 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
468
485
  event.stopPropagation()
469
486
  }}
470
487
  onClick={(event) => event.stopPropagation()}
471
- disabled={actionsDisabled}
488
+ disabled={actionsDisabled || dragDisabled}
472
489
  aria-label={t('dragSession', { name: sessionTitle })}
473
490
  title={`${statusLabel} · ${t('dragOrder')}`}
474
491
  className={cn(
@@ -518,7 +535,7 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
518
535
  )}
519
536
  title={sessionTitle}
520
537
  >
521
- {sessionTitle}
538
+ {highlightL2WorkbenchSessionSearchText(sessionTitle, searchQuery)}
522
539
  </p>
523
540
  <div className={cn(styles.rowActions, 'flex shrink-0 items-center')}>
524
541
  <button
@@ -558,6 +575,25 @@ const SortableWorkSessionRow = memo(function SortableWorkSessionRow({
558
575
  </button>
559
576
  </div>
560
577
  </div>
578
+ {searchMatches
579
+ .filter((match) => match.scope !== 'title')
580
+ .slice(0, 2)
581
+ .map((match) => (
582
+ <p
583
+ key={match.scope}
584
+ data-testid={`work-session-search-match-${workSession.workId}-${match.scope}`}
585
+ className="truncate text-xs leading-5 text-muted-foreground"
586
+ title={match.preview}
587
+ >
588
+ <span className="font-medium">
589
+ {match.scope === 'sessionId'
590
+ ? 'Session ID'
591
+ : searchT(match.scope === 'user' ? 'scopeUser' : 'scopeAssistant')}
592
+ {': '}
593
+ </span>
594
+ {highlightL2WorkbenchSessionSearchText(match.preview, searchQuery)}
595
+ </p>
596
+ ))}
561
597
  </div>
562
598
  </div>
563
599
  )
@@ -660,6 +696,7 @@ function WorkSessionGroupBoundary({ id, label }: { id: string; label: string }):
660
696
 
661
697
  function L2WorkSessionSidebarComponent({
662
698
  mode,
699
+ search,
663
700
  mobile,
664
701
  fileBrowser,
665
702
  pluginTabContent,
@@ -849,10 +886,22 @@ function L2WorkSessionSidebarComponent({
849
886
  () => workSessions.map((workSession) => workSession.workId),
850
887
  [workSessions]
851
888
  )
852
- const sortableIds = useMemo(
853
- () => getL2WorkSessionVisualSortableIds(workIds, pinnedCount),
854
- [pinnedCount, workIds]
889
+ const searching = search.query.trim().length > 0
890
+ const searchMatchesByWorkId = useMemo(
891
+ () => new Map(search.data?.sessions.map((session) => [session.workId, session.matches]) ?? []),
892
+ [search.data]
855
893
  )
894
+ const sortableIds = useMemo(() => {
895
+ const ids = getL2WorkSessionVisualSortableIds(workIds, pinnedCount)
896
+ if (!searching) return ids
897
+ const ordinaryMatches = workIds.slice(pinnedCount).some((id) => searchMatchesByWorkId.has(id))
898
+ const pinnedMatches = workIds.slice(0, pinnedCount).some((id) => searchMatchesByWorkId.has(id))
899
+ return ids.filter((id) =>
900
+ id === L2_WORK_SESSION_PIN_BOUNDARY_ID
901
+ ? ordinaryMatches && pinnedMatches
902
+ : searchMatchesByWorkId.has(id)
903
+ )
904
+ }, [pinnedCount, workIds, searching, searchMatchesByWorkId])
856
905
  const workSessionById = useMemo(
857
906
  () => new Map(workSessions.map((workSession) => [workSession.workId, workSession])),
858
907
  [workSessions]
@@ -879,7 +928,7 @@ function L2WorkSessionSidebarComponent({
879
928
 
880
929
  const handleDragEnd = useCallback(
881
930
  (event: DragEndEvent): void => {
882
- if (ordering || !event.over || event.active.id === event.over.id) return
931
+ if (searching || ordering || !event.over || event.active.id === event.over.id) return
883
932
 
884
933
  const activeId = String(event.active.id)
885
934
  const arrangement = moveL2WorkSessionVisual(
@@ -890,7 +939,7 @@ function L2WorkSessionSidebarComponent({
890
939
  )
891
940
  if (arrangement) onArrangementChange(arrangement, activeId)
892
941
  },
893
- [onArrangementChange, ordering, pinnedCount, workIds]
942
+ [onArrangementChange, ordering, pinnedCount, workIds, searching]
894
943
  )
895
944
 
896
945
  const rows = sortableIds.map((id) => {
@@ -917,6 +966,9 @@ function L2WorkSessionSidebarComponent({
917
966
  onRename={onRename}
918
967
  syncing={syncingWorkIds.includes(workSession.workId)}
919
968
  onReloadChat={onReloadChat}
969
+ dragDisabled={searching}
970
+ searchQuery={searching ? search.query : ''}
971
+ searchMatches={searchMatchesByWorkId.get(workSession.workId) ?? []}
920
972
  />
921
973
  )
922
974
  })
@@ -1005,12 +1057,79 @@ function L2WorkSessionSidebarComponent({
1005
1057
  return (
1006
1058
  <aside className="flex h-full w-full min-w-0 flex-col bg-sidebar text-sidebar-foreground">
1007
1059
  <SidebarHeader modeItems={modeItems} actionItems={actionItems} />
1060
+ {mode === 'sessions' ? (
1061
+ <div className="flex shrink-0 items-center gap-1 px-2 pb-1">
1062
+ <div className="relative min-w-0 flex-1">
1063
+ <SearchIcon className="pointer-events-none absolute left-2.5 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
1064
+ <Input
1065
+ data-testid="work-session-search-input"
1066
+ value={search.query}
1067
+ maxLength={120}
1068
+ aria-label={t('searchWorkSessions')}
1069
+ placeholder={t('searchWorkSessions')}
1070
+ onChange={(event) => search.setQuery(event.target.value)}
1071
+ onKeyDown={(event) => {
1072
+ if (event.key !== 'Escape' || event.nativeEvent.isComposing || !search.query) return
1073
+ event.preventDefault()
1074
+ event.stopPropagation()
1075
+ search.setQuery('')
1076
+ }}
1077
+ className="h-8 pl-8 pr-8"
1078
+ />
1079
+ {search.query ? (
1080
+ <button
1081
+ data-testid="work-session-search-clear"
1082
+ type="button"
1083
+ aria-label={t('clearSessionSearch')}
1084
+ title={t('clearSessionSearch')}
1085
+ onClick={() => search.setQuery('')}
1086
+ className="absolute right-0.5 top-1/2 flex size-7 -translate-y-1/2 items-center justify-center rounded-md text-muted-foreground hover:bg-accent focus-visible:ring-2 focus-visible:ring-ring"
1087
+ >
1088
+ <XIcon className="size-4" />
1089
+ </button>
1090
+ ) : null}
1091
+ </div>
1092
+ <L2WorkbenchSessionSearchScopes
1093
+ compact
1094
+ value={search.searchIn}
1095
+ onChange={search.setSearchIn}
1096
+ />
1097
+ </div>
1098
+ ) : null}
1008
1099
  <L4ScrollArea
1009
1100
  data-testid="work-session-list"
1010
1101
  className={cn('min-h-0 flex-1', mode !== 'sessions' && 'hidden')}
1011
1102
  viewportClassName="p-2 data-[has-overflow-y]:pr-4"
1012
1103
  >
1013
- {workSessions.length === 0 ? (
1104
+ {searching && (search.loading || search.error || sortableIds.length === 0) ? (
1105
+ <div
1106
+ data-testid="work-session-search-status"
1107
+ role="status"
1108
+ className="px-2 py-5 text-center text-sm leading-6 text-muted-foreground"
1109
+ >
1110
+ {search.loading ? (
1111
+ <p className="flex items-center justify-center gap-2">
1112
+ <LoaderCircleIcon className="size-4 animate-spin" />
1113
+ {t('searchingSessions')}
1114
+ </p>
1115
+ ) : search.error ? (
1116
+ <>
1117
+ <p role="alert" className="break-words text-destructive">
1118
+ {search.error}
1119
+ </p>
1120
+ <button
1121
+ type="button"
1122
+ onClick={search.refresh}
1123
+ className={buttonVariants({ variant: 'ghost', size: 'sm' })}
1124
+ >
1125
+ {t('retrySessionSearch')}
1126
+ </button>
1127
+ </>
1128
+ ) : (
1129
+ <p>{t('noSessionSearchMatches')}</p>
1130
+ )}
1131
+ </div>
1132
+ ) : workSessions.length === 0 ? (
1014
1133
  <p className="px-2 py-5 text-center text-sm leading-6 text-muted-foreground">
1015
1134
  {t('noSessions')}
1016
1135
  </p>
@@ -44,6 +44,12 @@ import type {
44
44
  L2SortWorkSessionsRequest,
45
45
  L2WorkSessionListResponse
46
46
  } from '@common/l2_biz/work-session/l2-work-session-contract'
47
+ import {
48
+ L2WorkSessionSearchResponseSchema,
49
+ type L2WorkSessionSearchRequest,
50
+ type L2WorkSessionSearchResponse
51
+ } from '@common/l2_biz/work-session/l2-work-session-search-contract'
52
+ import { L2PiSessionHistoryResponseSchema } from '@common/l2_biz/pi-session/l2-pi-session-contract'
47
53
  import type {
48
54
  L2WorkSessionBranchRequest,
49
55
  L2WorkSessionBranchResponse,
@@ -108,6 +114,10 @@ export interface L2WorkbenchBiz {
108
114
  setChatCapabilityMode: (input: L2ChatCapabilityModeSetRequest) => Promise<void>
109
115
  invokePluginMethod: (input: L2PluginInvokeRequest) => Promise<L2PluginInvokeResponse>
110
116
  listWorkSessions: () => Promise<L2AppBootstrapResponse>
117
+ searchWorkSessions: (
118
+ input: L2WorkSessionSearchRequest,
119
+ signal?: AbortSignal
120
+ ) => Promise<L2WorkSessionSearchResponse>
111
121
  applyAppRuntimeUpdate: (input: L3AppRuntimeApplyRequest) => Promise<void>
112
122
  sortWorkSessions: (input: L2SortWorkSessionsRequest) => Promise<L2WorkSessionListResponse>
113
123
  deleteWorkSession: (workId: string) => Promise<L2WorkSessionListResponse>
@@ -200,6 +210,11 @@ export function createL2WorkbenchBiz(
200
210
  },
201
211
  invokePluginMethod: (input) => appSocket.request(L2PluginSocketContracts.invoke, input),
202
212
  listWorkSessions: () => appSocket.request(L2WorkSessionSocketContracts.list, {}),
213
+ async searchWorkSessions(input, signal): Promise<L2WorkSessionSearchResponse> {
214
+ return L2WorkSessionSearchResponseSchema.parse(
215
+ await request<unknown>('/api/work-sessions/search', input, signal)
216
+ )
217
+ },
203
218
  async applyAppRuntimeUpdate(input): Promise<void> {
204
219
  await appSocket.request(L3AppRuntimeSocketContracts.apply, input)
205
220
  },
@@ -236,8 +251,11 @@ export function createL2WorkbenchBiz(
236
251
  saveL4PiDirectoryEntriesCache(response)
237
252
  return response
238
253
  },
239
- listSessionHistory: (input, signal) =>
240
- request<L2PiSessionHistoryResponse>('/api/pi/sessions/list', input, signal),
254
+ async listSessionHistory(input, signal): Promise<L2PiSessionHistoryResponse> {
255
+ return L2PiSessionHistoryResponseSchema.parse(
256
+ await request<unknown>('/api/pi/sessions/list', input, signal)
257
+ )
258
+ },
241
259
  listSessionUserMessages: (input, signal) =>
242
260
  request<L2PiSessionUserMessageListResponse>(
243
261
  '/api/pi/session-user-messages/list',
@@ -0,0 +1,34 @@
1
+ import type { L2WorkbenchBranchTreeFilter } from './l2-workbench-branch-tree'
2
+
3
+ const STORAGE_KEY = 'pi-desk:workbench-branch-filter'
4
+
5
+ export const L2_WORKBENCH_BRANCH_FILTER_OPTIONS: ReadonlyArray<{
6
+ value: L2WorkbenchBranchTreeFilter
7
+ label: string
8
+ }> = [
9
+ { value: 'default', label: 'filterDefault' },
10
+ { value: 'no-tools', label: 'filterNoTools' },
11
+ { value: 'user-only', label: 'filterUserOnly' },
12
+ { value: 'labeled-only', label: 'filterLabeled' },
13
+ { value: 'all', label: 'filterAll' }
14
+ ]
15
+
16
+ export function loadL2WorkbenchBranchFilter(): L2WorkbenchBranchTreeFilter {
17
+ try {
18
+ const saved = window.localStorage.getItem(STORAGE_KEY)
19
+ return (
20
+ L2_WORKBENCH_BRANCH_FILTER_OPTIONS.find((option) => option.value === saved)?.value ??
21
+ 'default'
22
+ )
23
+ } catch {
24
+ return 'default'
25
+ }
26
+ }
27
+
28
+ export function saveL2WorkbenchBranchFilter(filter: L2WorkbenchBranchTreeFilter): void {
29
+ try {
30
+ window.localStorage.setItem(STORAGE_KEY, filter)
31
+ } catch (error) {
32
+ console.warn('[Pi Desk][Workbench] 保存分支筛选失败,仅当前选择器生效', error)
33
+ }
34
+ }
@@ -1,12 +1,6 @@
1
1
  'use client'
2
2
 
3
- import {
4
- ChevronDownIcon,
5
- ChevronRightIcon,
6
- LoaderCircleIcon,
7
- SearchIcon,
8
- XIcon
9
- } from 'lucide-react'
3
+ import { CheckIcon, ChevronRightIcon, LoaderCircleIcon, SearchIcon, XIcon } from 'lucide-react'
10
4
  import { useTranslation } from 'react-i18next'
11
5
  import { motion, useIsPresent, useReducedMotion } from 'motion/react'
12
6
  import {
@@ -27,12 +21,6 @@ import type {
27
21
  import { cn } from '@client/l4_foundation/lib/l4-utils'
28
22
  import { useL4Region } from '@client/l4_foundation/locale/l4-region-provider'
29
23
  import { Button, buttonVariants } from '@client/l4_foundation/ui/shadcn/button'
30
- import {
31
- DropdownMenu,
32
- DropdownMenuContent,
33
- DropdownMenuItem,
34
- DropdownMenuTrigger
35
- } from '@client/l4_foundation/ui/shadcn/dropdown-menu'
36
24
  import { Input } from '@client/l4_foundation/ui/shadcn/input'
37
25
  import {
38
26
  buildL2WorkbenchBranchTreeRows,
@@ -40,6 +28,11 @@ import {
40
28
  type L2WorkbenchBranchTreeFilter
41
29
  } from './l2-workbench-branch-tree'
42
30
  import type { L2WorkbenchBranchIntent } from './hooks/l2-workbench-branch-picker'
31
+ import {
32
+ L2_WORKBENCH_BRANCH_FILTER_OPTIONS as FILTER_OPTIONS,
33
+ loadL2WorkbenchBranchFilter,
34
+ saveL2WorkbenchBranchFilter
35
+ } from './l2-workbench-branch-filter-preferences'
43
36
 
44
37
  export interface L2WorkbenchBranchPickerModel {
45
38
  workId: string
@@ -81,17 +74,6 @@ interface KindAppearance {
81
74
  const EMPTY_TREE_NODES: readonly L2WorkSessionTreeNode[] = []
82
75
  const OVERSCAN_ROWS = 8
83
76
 
84
- const FILTER_OPTIONS: ReadonlyArray<{
85
- value: L2WorkbenchBranchTreeFilter
86
- label: string
87
- }> = [
88
- { value: 'default', label: 'filterDefault' },
89
- { value: 'no-tools', label: 'filterNoTools' },
90
- { value: 'user-only', label: 'filterUserOnly' },
91
- { value: 'labeled-only', label: 'filterLabeled' },
92
- { value: 'all', label: 'filterAll' }
93
- ]
94
-
95
77
  const KIND_APPEARANCE: Record<L2WorkSessionTreeNodeKind, KindAppearance> = {
96
78
  user: {
97
79
  label: 'kindUser',
@@ -171,6 +153,8 @@ function intentLabel(intent: L2WorkbenchBranchIntent): string {
171
153
  switch (intent.action) {
172
154
  case 'tree':
173
155
  return 'treeIntent'
156
+ case 'resend':
157
+ return 'resendIntent'
174
158
  case 'fork':
175
159
  return 'forkIntent'
176
160
  case 'clone':
@@ -309,8 +293,7 @@ export function L2WorkbenchBranchPicker({
309
293
  [locale, timeZone]
310
294
  )
311
295
  const [query, setQuery] = useState('')
312
- const [filter, setFilter] = useState<L2WorkbenchBranchTreeFilter>('default')
313
- const [filterOpen, setFilterOpen] = useState(false)
296
+ const [filter, setFilter] = useState(loadL2WorkbenchBranchFilter)
314
297
  const [foldedEntryIds, setFoldedEntryIds] = useState<Set<string>>(() => new Set())
315
298
  const [selectedEntryId, setSelectedEntryId] = useState<string | null>(null)
316
299
  const [scrollTop, setScrollTop] = useState(0)
@@ -348,9 +331,6 @@ export function L2WorkbenchBranchPicker({
348
331
  const renderEndIndex = Math.min(rows.length, Math.max(endIndex, startIndex + OVERSCAN_ROWS))
349
332
  const visibleRows = rows.slice(startIndex, renderEndIndex)
350
333
  const selectedDetail = model.detail?.entryId === effectiveSelectedEntryId ? model.detail : null
351
- const filterLabel = t(
352
- FILTER_OPTIONS.find((option) => option.value === filter)?.label ?? 'filterDefault'
353
- )
354
334
  const confirmation = model.runningConfirmation
355
335
  const treeId = `work-session-branch-tree-${model.workId}`
356
336
  const selectedRowId = effectiveSelectedEntryId
@@ -395,12 +375,6 @@ export function L2WorkbenchBranchPicker({
395
375
  const target = event.target
396
376
  if (!(target instanceof Node)) return
397
377
  if (panelRef.current?.contains(target)) return
398
- if (
399
- target instanceof Element &&
400
- target.closest('[data-workbench-branch-picker-popup="true"]')
401
- ) {
402
- return
403
- }
404
378
  requestClose(false)
405
379
  }
406
380
  document.addEventListener('pointerdown', handlePointerDown, true)
@@ -471,6 +445,7 @@ export function L2WorkbenchBranchPicker({
471
445
  const applyFilter = useCallback(
472
446
  (nextFilter: L2WorkbenchBranchTreeFilter): void => {
473
447
  setFilter(nextFilter)
448
+ saveL2WorkbenchBranchFilter(nextFilter)
474
449
  setFoldedEntryIds(new Set())
475
450
  scrollListTo(0)
476
451
  queueMicrotask(focusPickerInput)
@@ -693,42 +668,6 @@ export function L2WorkbenchBranchPicker({
693
668
  onKeyDown={handleSearchKeyDown}
694
669
  />
695
670
  </div>
696
- <DropdownMenu
697
- open={filterOpen}
698
- onOpenChange={setFilterOpen}
699
- onOpenChangeComplete={(open) => {
700
- if (!open) queueMicrotask(focusPickerInput)
701
- }}
702
- >
703
- <DropdownMenuTrigger
704
- render={
705
- <button
706
- type="button"
707
- aria-label={t('branchFilter', { filter: filterLabel })}
708
- className={buttonVariants({ variant: 'outline' })}
709
- />
710
- }
711
- >
712
- {filterLabel}
713
- <ChevronDownIcon className="size-3.5" />
714
- </DropdownMenuTrigger>
715
- <DropdownMenuContent
716
- data-workbench-branch-picker-popup="true"
717
- align="end"
718
- side="bottom"
719
- className="w-32"
720
- finalFocus={() => (mobile ? panelRef.current : searchRef.current)}
721
- >
722
- {FILTER_OPTIONS.map((option) => (
723
- <DropdownMenuItem key={option.value} onClick={() => applyFilter(option.value)}>
724
- <span className="min-w-0 flex-1">{t(option.label)}</span>
725
- {filter === option.value ? (
726
- <span className="text-xs text-primary">●</span>
727
- ) : null}
728
- </DropdownMenuItem>
729
- ))}
730
- </DropdownMenuContent>
731
- </DropdownMenu>
732
671
  </div>
733
672
  ) : null}
734
673
  </header>
@@ -813,6 +752,30 @@ export function L2WorkbenchBranchPicker({
813
752
  )}
814
753
  </div>
815
754
 
755
+ <div
756
+ role="group"
757
+ aria-label={t('branchFilters')}
758
+ className="flex shrink-0 flex-wrap items-center gap-1 border-b bg-muted/25 px-2 py-2"
759
+ >
760
+ <span className="px-1 text-sm font-medium text-muted-foreground">
761
+ {t('branchFilters')}
762
+ </span>
763
+ {FILTER_OPTIONS.map((option) => (
764
+ <Button
765
+ key={option.value}
766
+ type="button"
767
+ size="sm"
768
+ variant="ghost"
769
+ aria-pressed={filter === option.value}
770
+ className="aria-pressed:bg-primary aria-pressed:text-primary-foreground aria-pressed:hover:bg-[color-mix(in_oklab,var(--primary),var(--primary-foreground)_8%)]"
771
+ onClick={() => applyFilter(option.value)}
772
+ >
773
+ {filter === option.value ? <CheckIcon className="size-3.5" /> : null}
774
+ {t(option.label)}
775
+ </Button>
776
+ ))}
777
+ </div>
778
+
816
779
  {model.pending && !model.runningConfirmation ? (
817
780
  <div className="flex shrink-0 items-center gap-2 border-b bg-primary/8 px-2.5 py-1.5 text-xs text-muted-foreground">
818
781
  <LoaderCircleIcon className="size-3.5 animate-spin text-primary" />
@@ -65,6 +65,7 @@ export interface L2WorkbenchChatInputRuntime {
65
65
  getState: (source: L2ChatSource) => L2WorkbenchChatInputState
66
66
  loadSource: (source: L2ChatSource) => Promise<void>
67
67
  setText: (source: L2ChatSource, text: string) => void
68
+ preserveDraftOnBranch: (source: L2ChatSource) => () => void
68
69
  dismissFeedback: (source: L2ChatSource) => void
69
70
  addFiles: (source: L2ChatSource, files: readonly File[]) => Promise<void>
70
71
  removeImage: (source: L2ChatSource, localId: string) => void
@@ -171,6 +172,7 @@ export function createL2WorkbenchChatInputRuntime(
171
172
  const persistenceFailures = new Set<string>()
172
173
  let draftWriteRevision = 0
173
174
  const loadPromises = new Map<string, Promise<void>>()
175
+ const preservedDraftSources = new Set<string>()
174
176
  const optimizeQueue: Array<() => Promise<void>> = []
175
177
  const messageImages = new Map<string, CachedMessageImage>()
176
178
  const imageLoads = new Map<string, Promise<CachedMessageImage>>()
@@ -412,7 +414,7 @@ export function createL2WorkbenchChatInputRuntime(
412
414
  replaceState(source, (state) => ({ ...state, loading: true, error: null }))
413
415
  try {
414
416
  const recovered = await repository.recoverOutbox(source)
415
- if (disposed || !sourcesByKey.has(key)) return
417
+ if (disposed || !sourcesByKey.has(key) || statesBySource.get(key)?.loaded) return
416
418
  const images = hydrateImages(recovered.input.images, workbenchText('draftImage'))
417
419
  statesBySource.set(key, {
418
420
  ...ensureState(source),
@@ -449,6 +451,12 @@ export function createL2WorkbenchChatInputRuntime(
449
451
  enqueueDraftWrite(source, storedInput(next))
450
452
  },
451
453
 
454
+ preserveDraftOnBranch(source): () => void {
455
+ const key = sourceKey(source)
456
+ preservedDraftSources.add(key)
457
+ return () => preservedDraftSources.delete(key)
458
+ },
459
+
452
460
  async addFiles(source, files): Promise<void> {
453
461
  const current = ensureState(source)
454
462
  const available = MAX_IMAGE_COUNT - current.images.length
@@ -813,6 +821,34 @@ export function createL2WorkbenchChatInputRuntime(
813
821
  )
814
822
  for (const [key, source] of [...sourcesByKey]) {
815
823
  if (valid.has(key)) continue
824
+ const state = statesBySource.get(key)
825
+ const target = preservedDraftSources.has(key)
826
+ ? workSessions.find(
827
+ (item) => item.workId === source.workId && item.sessionId === source.sessionId
828
+ )
829
+ : undefined
830
+ if (state && target) {
831
+ const nextSource = {
832
+ workId: target.workId,
833
+ sessionId: target.sessionId,
834
+ branchId: target.branchId
835
+ }
836
+ const images = hydrateImages(storedImages(state.images), workbenchText('draftImage'))
837
+ const next = ensureState(nextSource)
838
+ releaseAttachments(next.images)
839
+ statesBySource.set(sourceKey(nextSource), {
840
+ ...next,
841
+ loaded: true,
842
+ loading: false,
843
+ text: state.text,
844
+ images
845
+ })
846
+ enqueueDraftWrite(nextSource, { text: state.text, images: storedImages(images) })
847
+ for (const image of images) {
848
+ if (!image.optimized) enqueueOptimization(nextSource, image.localId)
849
+ }
850
+ }
851
+ preservedDraftSources.delete(key)
816
852
  const persistenceTail = persistenceTails.get(key) ?? Promise.resolve()
817
853
  releaseSourceMemory(source)
818
854
  void persistenceTail
@@ -839,6 +875,7 @@ export function createL2WorkbenchChatInputRuntime(
839
875
  for (const image of messageImages.values()) URL.revokeObjectURL(image.url)
840
876
  messageImages.clear()
841
877
  imageLoads.clear()
878
+ preservedDraftSources.clear()
842
879
  persistenceFailures.clear()
843
880
  listeners.clear()
844
881
  }
@@ -31,6 +31,8 @@ interface L2WorkbenchChatViewProps {
31
31
  chatRuntime: L2WorkbenchChatRuntime
32
32
  inputRuntime: L2WorkbenchChatInputRuntime
33
33
  onPreviewFile: (source: L2ChatSource, path: string) => void
34
+ onResend: (source: L2ChatSource, entryId: string) => void
35
+ resendDisabled: boolean
34
36
  }
35
37
 
36
38
  function L2WorkbenchChatViewComponent({
@@ -39,7 +41,9 @@ function L2WorkbenchChatViewComponent({
39
41
  workSession,
40
42
  chatRuntime,
41
43
  inputRuntime,
42
- onPreviewFile
44
+ onPreviewFile,
45
+ onResend,
46
+ resendDisabled
43
47
  }: L2WorkbenchChatViewProps): React.JSX.Element {
44
48
  const { t } = useTranslation('workbench')
45
49
  const source = useMemo(
@@ -104,6 +108,10 @@ function L2WorkbenchChatViewComponent({
104
108
  (path: string): void => onPreviewFile(source, path),
105
109
  [onPreviewFile, source]
106
110
  )
111
+ const resend = useCallback(
112
+ (entryId: string): void => onResend(source, entryId),
113
+ [onResend, source]
114
+ )
107
115
  const saveViewportSnapshot = useCallback(
108
116
  (snapshot: L2WorkbenchChatViewportSnapshot): void => {
109
117
  chatRuntime.saveViewportSnapshot(source, snapshot)
@@ -156,6 +164,8 @@ function L2WorkbenchChatViewComponent({
156
164
  onLoadDetail={loadDetail}
157
165
  onAcquireImage={acquireImage}
158
166
  onPreviewFile={previewFile}
167
+ onResend={resend}
168
+ resendDisabled={resendDisabled || state.syncStatus !== 'ready'}
159
169
  />
160
170
  )
161
171
  })}